Skip to content

Commit

Permalink
Update mermaid example
Browse files Browse the repository at this point in the history
  • Loading branch information
li3zhen1 committed Jan 7, 2024
1 parent 7bbbc84 commit 4bc462f
Show file tree
Hide file tree
Showing 18 changed files with 382 additions and 83 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ struct Lattice: View {
var body: some View {
ForceDirectedGraph($isRunning) {

Repeated(0..<(width*width)) { i in
Series(0..<(width*width)) { i in
let _i = Double(i / width) / Double(width)
let _j = Double(i % width) / Double(width)
NodeMark(id: i, radius: 3.0)
.foregroundStyle(Color(red: 1, green: _i, blue: _j))
.stroke()
}

Repeated(edge) {
Series(edge) {
LinkMark(from: $0.0, to: $0.1)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ let mermaidLinkRegex = Regex {
"<—"
"->"
"<-"
""
}

OneOrMore(.whitespace)
Expand Down Expand Up @@ -73,31 +74,79 @@ func getInitialPosition(id: String, r: Double) -> SIMD2<Double> {
struct MermaidVisualization: View {

@State private var text: String = """
Alice --> Bob
Bob --> Cindy
Alice --> Dan
Alice --> Cindy
Tom --> Bob
Tom --> Kate
Kate --> Cindy
Alice → Bob
Bob → Cindy
Cindy → David
David → Emily
Emily → Frank
Frank → Grace
Grace → Henry
Henry → Isabella
Isabella → Jack
Jack → Karen
Karen → Liam
Liam → Monica
Monica → Nathan
Nathan → Olivia
Olivia → Peter
Peter → Quinn
Quinn → Rachel
Rachel → Steve
Steve → Tiffany
Tiffany → Umar
Umar → Violet
Violet → William
William → Xavier
Xavier → Yolanda
Yolanda → Zack
Zack → Alice
Jack -> Rachel
Xavier -> José
José -> アキラ
アキラ -> Liam
"""

@State private var tappedNode: String? = nil

@ViewBuilder
func getLabel(_ text: String) -> some View {

let accentColor = colors[Int(UInt(truncatingIfNeeded: text.hashValue) % UInt(colors.count))]

Text(text)
.font(.caption)
.foregroundStyle(.foreground)
.padding(.vertical, 4.0)
.padding(.horizontal, 8.0)
.background(alignment: .center) {
ZStack {
RoundedRectangle(cornerSize: .init(width: 12, height: 12))
.fill(.background)
.shadow(radius: 1.5, y: 1.0)
RoundedRectangle(cornerSize: .init(width: 12, height: 12))
.stroke(accentColor, style: .init(lineWidth: 2.0))
}
}
.padding()
}

var parsedGraph: ([String], [(String, String)]) {
parseMermaid(text)
}

var body: some View {
ForceDirectedGraph {
Repeated(parsedGraph.0) { node in
Series(parsedGraph.0) { node in

NodeMark(id: node)
.symbol(RoundedRectangle(cornerSize: CGSize(width: 3, height: 3)))
.symbolSize(radius: 6)
.label(alignment: .bottom, offset: [0, 4]) {
Text(node)
.symbol(.circle)
.symbolSize(radius: 8)
.foregroundStyle(Color(white: 1.0, opacity: 0.0))
.richLabel(node, alignment: .center, offset: .zero) {
getLabel(node)
}
}
Repeated(parsedGraph.1) { link in
Series(parsedGraph.1) { link in
LinkMark(from: link.0, to: link.1)
}
} force: {
Expand All @@ -107,13 +156,20 @@ struct MermaidVisualization: View {
} emittingNewNodesWithStates: { id in
KineticState(position: getInitialPosition(id: id, r: 100))
}
.onNodeTapped {
tappedNode = $0
}
.inspector(isPresented: .constant(true)) {
VStack {
Text("Tapped: \(tappedNode ?? "nil")")
.font(.title2)
Divider()

Text("Edit the mermaid syntaxes to update the graph")
.font(.title)
.font(.title2)
TextEditor(text: $text)
.fontDesign(.monospaced)

}.padding(.top)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,30 @@ struct MiserableGraph: View {
@State private var opacity: Double = 0
@State private var inspectorPresented = false

@State private var modelTransform: ViewportTransform = .identity.scale(by: 2.0)

@ViewBuilder
func getLabel(_ text: String) -> some View {
Text(text)
.font(.caption2)
.padding(.vertical, 2.0)
.padding(.horizontal, 6.0)
.background(alignment: .center) {
RoundedRectangle(cornerSize: .init(width: 12, height: 12))
.fill(.white)
.shadow(radius: 1.5, y: 1.0)
}
.padding()
}

var body: some View {

ForceDirectedGraph($isRunning) {
ForceDirectedGraph(
$isRunning,
$modelTransform
) {

Repeated(graphData.nodes) { node in
Series(graphData.nodes) { node in
NodeMark(id: node.id)
.symbol(.asterisk)
.symbolSize(radius: 9.0)
Expand All @@ -39,23 +58,22 @@ struct MiserableGraph: View {
)
)
)
.label(offset: [0.0, 12.0]) {
Text(node.id)
.font(.caption2)
.richLabel(node.id, offset: .zero) {
self.getLabel(node.id)
}
}

Repeated(graphData.links) { l in
Series(graphData.links) { l in
LinkMark(from: l.source, to: l.target)
}
//
} force: {
ManyBodyForce(strength: -20)
CenterForce()
LinkForce(
originalLength: .constant(35.0),
stiffness: .weightedByDegree(k: { _, _ in 1.0})
)
CenterForce()
}
.onNodeTapped { node in
inspectorPresented = true
Expand All @@ -67,6 +85,7 @@ struct MiserableGraph: View {
}

.toolbar {
Text("\(modelTransform.scale)")
Button {
isRunning.toggle()
if opacity < 1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct MyRing: View {


ForceDirectedGraph($isRunning) {
Repeated(0..<20) { i in
Series(0..<20) { i in
NodeMark(id: 3 * i + 0)
.symbol(.circle)
.symbolSize(radius:4.0)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ struct MyGraph: View {
NodeMark(id: 1).foregroundStyle(.blue)
NodeMark(id: 2).foregroundStyle(.yellow)

Repeated(0..<2) { i in
Series(0..<2) { i in
LinkMark(from: i, to: i+1)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/Grape/Contents/GraphContentBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public struct GraphContentBuilder<NodeID: Hashable> {
/// MyComponent(i)
/// }
/// }
@available(*, deprecated, message: "Use `Repeated` instead. ")
@available(*, deprecated, message: "Use `Series` instead. ")
@inlinable
public static func buildArray<T>(_ components: [T]) -> some Content
where T: Content, T.NodeID == NodeID {
Expand Down
4 changes: 2 additions & 2 deletions Sources/Grape/Contents/Repeated.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
public struct Repeated<NodeID, Data, Content>
public struct Series<NodeID, Data, Content>
where Data: RandomAccessCollection, Content: GraphContent<NodeID>, NodeID: Hashable {

@usableFromInline
Expand All @@ -17,7 +17,7 @@ where Data: RandomAccessCollection, Content: GraphContent<NodeID>, NodeID: Hasha
}
}

extension Repeated: GraphContent {
extension Series: GraphContent {
@inlinable
public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext<NodeID>) {
self.data.forEach { element in
Expand Down
3 changes: 1 addition & 2 deletions Sources/Grape/Grape.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ If you’re looking for a more detailed control of force-directed layouts, pleas
* ``GraphContent``
* ``NodeMark``
* ``LinkMark``
* ``Repeated``
* ``GraphContent/foregroundStyle(_:)``
* ``Series``

### Handling gestures and events

Expand Down
27 changes: 24 additions & 3 deletions Sources/Grape/Modifiers/Effects/GrapeEffect.Label.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import SwiftUI


extension GraphContentEffect {
@usableFromInline
internal struct Label {
Expand Down Expand Up @@ -31,6 +32,9 @@ extension GraphContentEffect {
@usableFromInline
let view: AnyView

@usableFromInline
let tag: String

@usableFromInline
let alignment: Alignment

Expand All @@ -39,15 +43,18 @@ extension GraphContentEffect {

@inlinable
public init(
_ tag: String,
_ view: some View,
alignment: Alignment = .bottom,
offset: CGVector = .zero
) {
self.tag = tag
self.view = .init(erasing: view)
self.alignment = alignment
self.offset = offset
}
}

}

extension GraphContentEffect.Label: GraphContentModifier {
Expand Down Expand Up @@ -83,7 +90,6 @@ extension GraphContentEffect.Label: GraphContentModifier {
}
}


extension GraphContentEffect.RichLabel: GraphContentModifier {
@inlinable
public func _into<NodeID>(
Expand All @@ -96,7 +102,22 @@ extension GraphContentEffect.RichLabel: GraphContentModifier {
@MainActor
public func _exit<NodeID>(_ context: inout _GraphRenderingContext<NodeID>)
where NodeID: Hashable {

if let currentID = context.states.currentID {

context.resolvedViews[currentID] = .pending(self.view)

switch currentID {
case .node(_):
if let currentSymbolSize = context.states.currentSymbolSize {
let anchorOffset = alignment.anchorOffset(for: currentSymbolSize)
context.textOffsets[currentID] = (alignment, offset.simd + anchorOffset)
} else {
context.textOffsets[currentID] = (alignment, offset.simd)
}
case .link(_, _):
context.textOffsets[currentID] = (alignment, offset.simd)
}
}
}
}

Expand All @@ -113,7 +134,7 @@ extension Alignment {
return SIMD2(Double(size.width) / 2, 0)
case .trailing:
return SIMD2(-Double(size.width) / 2, 0)
default:
default:
return .zero
}
case .bottom:
Expand Down
14 changes: 9 additions & 5 deletions Sources/Grape/Modifiers/GraphContent+GraphContentModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,20 +107,24 @@ extension GraphContent {

@inlinable
public func richLabel(
_ alignment: Alignment = .bottom, offset: CGVector = .zero,
_ tag: String,
_ alignment: Alignment = .bottom,
offset: CGVector = .zero,
@ViewBuilder _ content: () -> some View
) -> some GraphContent<NodeID> {

return ModifiedGraphContent(
self, GraphContentEffect.RichLabel(content(), alignment: alignment, offset: offset))
self, GraphContentEffect.RichLabel(tag, content(), alignment: alignment, offset: offset)
)
}

@inlinable
public func richLabel(
alignment: Alignment = .bottom, offset: SIMD2<Double> = .zero,
_ tag: String,
alignment: Alignment = .bottom,
offset: SIMD2<Double> = .zero,
@ViewBuilder _ content: () -> some View
) -> some GraphContent<NodeID> {
return richLabel(alignment, offset: offset.cgVector, content)
return richLabel(tag, alignment, offset: offset.cgVector, content)
}

/// Sets the stroke style for this graph content.
Expand Down
Loading

0 comments on commit 4bc462f

Please sign in to comment.