diff --git a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift index 22ff0f2..7e518f6 100644 --- a/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift +++ b/Examples/ForceDirectedGraphExample/ForceDirectedGraphExample/MermaidVisualization.swift @@ -84,13 +84,9 @@ struct MermaidVisualization: View { let parsedGraph = model.parsedGraph ForceDirectedGraph { Series(parsedGraph.0) { node in - NodeMark(id: node) - .symbol(.circle) - .symbolSize(radius: 16) - .foregroundStyle(Color(white: 1.0, opacity: 0.0)) - .annotation(node, alignment: .center, offset: .zero) { - getLabel(node) - } + AnnotationNodeMark(id: node, radius: 16) { + getLabel(node) + } } Series(parsedGraph.1) { link in LinkMark(from: link.0, to: link.1) @@ -115,11 +111,11 @@ struct MermaidVisualization: View { } }) .ignoresSafeArea() - #if !os(visionOS) +#if !os(visionOS) .inspector(isPresented: .constant(true)) { MermaidInspector(model: model) } - #endif +#endif } } @@ -180,7 +176,7 @@ let mermaidLinkRegex = Regex { "<-" "→" } - + OneOrMore(.whitespace) singleNodeRegex } diff --git a/Sources/Grape/Contents/AnyGraphContent.swift b/Sources/Grape/Contents/AnyGraphContent.swift index 433c493..6a56b30 100644 --- a/Sources/Grape/Contents/AnyGraphContent.swift +++ b/Sources/Grape/Contents/AnyGraphContent.swift @@ -9,6 +9,11 @@ public struct AnyGraphContent: GraphContent { self.storage = storage } + @inlinable + public var body: _IdentifiableNever { + fatalError() + } + @inlinable public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { storage._attachToGraphRenderingContext(&context) diff --git a/Sources/Grape/Contents/CompositedGraphContent.swift b/Sources/Grape/Contents/CompositedGraphContent.swift index 83bd982..0209d21 100644 --- a/Sources/Grape/Contents/CompositedGraphContent.swift +++ b/Sources/Grape/Contents/CompositedGraphContent.swift @@ -1,7 +1,5 @@ -public protocol GraphComponent: GraphContent { +public protocol GraphComponent: GraphContent where Body: GraphContent { - associatedtype Body: GraphContent - @inlinable @GraphContentBuilder var body: Body { get } diff --git a/Sources/Grape/Contents/GraphContent.swift b/Sources/Grape/Contents/GraphContent.swift index d752511..e2ab945 100644 --- a/Sources/Grape/Contents/GraphContent.swift +++ b/Sources/Grape/Contents/GraphContent.swift @@ -3,7 +3,20 @@ import SwiftUI public protocol GraphContent { associatedtype NodeID: Hashable + associatedtype Body: GraphContent @inlinable func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) + + @inlinable + var body: Body { get } } + + +extension GraphContent { + + @inlinable + public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { + body._attachToGraphRenderingContext(&context) + } +} \ No newline at end of file diff --git a/Sources/Grape/Contents/LinkMark.swift b/Sources/Grape/Contents/LinkMark.swift index 90b0cfb..af9aeda 100644 --- a/Sources/Grape/Contents/LinkMark.swift +++ b/Sources/Grape/Contents/LinkMark.swift @@ -3,6 +3,10 @@ import SwiftUI public struct LinkMark: GraphContent & Identifiable { + @inlinable + public var body: _IdentifiableNever { + fatalError() + } // public enum LabelDisplayStrategy { // case auto // case specified(Bool) diff --git a/Sources/Grape/Contents/ModifiedGraphContent.swift b/Sources/Grape/Contents/ModifiedGraphContent.swift index 9f60469..ab89931 100644 --- a/Sources/Grape/Contents/ModifiedGraphContent.swift +++ b/Sources/Grape/Contents/ModifiedGraphContent.swift @@ -49,4 +49,9 @@ extension ModifiedGraphContent: GraphContent { content._attachToGraphRenderingContext(&context) modifier._exit(&context) } + + @inlinable + public var body: _IdentifiableNever { + fatalError() + } } diff --git a/Sources/Grape/Contents/NodeMark.swift b/Sources/Grape/Contents/NodeMark.swift index 588cb5e..b0f75f7 100644 --- a/Sources/Grape/Contents/NodeMark.swift +++ b/Sources/Grape/Contents/NodeMark.swift @@ -1,5 +1,6 @@ import SwiftUI import simd +import Charts public struct NodeMark: GraphContent, Identifiable, Equatable { @@ -12,6 +13,11 @@ public struct NodeMark: GraphContent, Identifiable, Equatable self.id = id } + @inlinable + public var body: _IdentifiableNever { + fatalError() + } + @inlinable public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { context.nodeOperations.append( @@ -28,9 +34,31 @@ public struct NodeMark: GraphContent, Identifiable, Equatable } } -extension NodeMark: CustomDebugStringConvertible { +public struct AnnotationNodeMark: GraphContent, Identifiable { + + public var id: NodeID + + @usableFromInline + var radius: CGFloat + + @usableFromInline + var annotation: AnyView + + @inlinable + public init(id: NodeID, radius: CGFloat, @ViewBuilder annnotation: () -> some View) { + self.id = id + self.radius = radius + self.annotation = AnyView(annnotation()) + } + @inlinable - public var debugDescription: String { - return "Node(id: \(id))" + public var body: some GraphContent { + NodeMark(id: id) + .symbolSize(radius: radius) + .foregroundStyle(.clear) + .annotation("\(id)", alignment: .center, offset: .zero) { + annotation + } } -} \ No newline at end of file + +} diff --git a/Sources/Grape/Contents/Series.swift b/Sources/Grape/Contents/Series.swift index 389a288..f1ec596 100644 --- a/Sources/Grape/Contents/Series.swift +++ b/Sources/Grape/Contents/Series.swift @@ -15,6 +15,11 @@ where Data: RandomAccessCollection, Content: GraphContent, NodeID: Hasha self.data = data self.content = graphContent } + + @inlinable + public var body: _IdentifiableNever { + fatalError() + } } extension Series: GraphContent { diff --git a/Sources/Grape/Contents/_ArrayGraphContent.swift b/Sources/Grape/Contents/_ArrayGraphContent.swift index dd8476d..bf18f8a 100644 --- a/Sources/Grape/Contents/_ArrayGraphContent.swift +++ b/Sources/Grape/Contents/_ArrayGraphContent.swift @@ -13,6 +13,11 @@ where C: GraphContent { self.storage = storage } + @inlinable + public var body: _IdentifiableNever { + fatalError() + } + @inlinable public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { for content in storage { diff --git a/Sources/Grape/Contents/_ConditionalGraphContent.swift b/Sources/Grape/Contents/_ConditionalGraphContent.swift index 67c0670..487cb84 100644 --- a/Sources/Grape/Contents/_ConditionalGraphContent.swift +++ b/Sources/Grape/Contents/_ConditionalGraphContent.swift @@ -18,6 +18,11 @@ where C1: GraphContent, C2: GraphContent, C1.NodeID == C2.NodeID { self.storage = storage } + @inlinable + public var body: _IdentifiableNever { + fatalError() + } + @inlinable public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { switch storage { diff --git a/Sources/Grape/Contents/_EmptyGraphContent.swift b/Sources/Grape/Contents/_EmptyGraphContent.swift index 8985451..4fd35ab 100644 --- a/Sources/Grape/Contents/_EmptyGraphContent.swift +++ b/Sources/Grape/Contents/_EmptyGraphContent.swift @@ -7,5 +7,10 @@ struct _EmptyGraphContent: GraphContent { @inlinable public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { + } + + @inlinable + public var body: _IdentifiableNever { + fatalError() } } \ No newline at end of file diff --git a/Sources/Grape/Contents/_ForEach+GraphContent.swift b/Sources/Grape/Contents/_ForEach+GraphContent.swift deleted file mode 100644 index ae4aa7e..0000000 --- a/Sources/Grape/Contents/_ForEach+GraphContent.swift +++ /dev/null @@ -1,132 +0,0 @@ -import SwiftUI - -public struct _GraphContentWrapper: GraphContent -where InnerGraphContent: GraphContent { - public typealias NodeID = InnerGraphContent.NodeID - - public let storage: InnerGraphContent - - @inlinable - init(_ content: InnerGraphContent) { - self.storage = content - } - - @inlinable - public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { - storage._attachToGraphRenderingContext(&context) - } -} - -public protocol _GraphContentWrappingView: View { - associatedtype InnerGraphContent: GraphContent - @inlinable - var storage: InnerGraphContent { get } -} - -extension _GraphContentWrapper: _GraphContentWrappingView { - @inlinable - public var body: some View { - EmptyView() - } - - @inlinable - internal static func pullback(_ content: @escaping (T) -> InnerGraphContent) -> (T) -> Self { - return { element in - return .init(content(element)) - } - } - - @inlinable - internal static func pullback( - id: KeyPath, _ content: @escaping (ID) -> InnerGraphContent - ) - -> (T) -> Self where ID: Hashable - { - return { element in - return .init(content(element[keyPath: id])) - } - } -} - -extension ForEach: GraphContent where Content: GraphContent { - public typealias NodeID = Content.NodeID - - @inlinable - public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { - self.data.forEach { element in - self.content(element)._attachToGraphRenderingContext(&context) - } - } -} - -extension ForEach -where ID == Data.Element.ID, Content: _GraphContentWrappingView, Data.Element: Identifiable { - @inlinable - // @_disfavoredOverload - public init( - _ data: Data, - @GraphContentBuilder graphContent: @escaping (Data.Element) -> IG - ) - where - IG: GraphContent, - NodeID: Hashable, - Content == _GraphContentWrapper - { - let pb = _GraphContentWrapper.pullback(graphContent) - self.init(data, content: pb) - } -} - -extension ForEach where Content: _GraphContentWrappingView { - @inlinable - public init( - _ data: Data, - id: KeyPath, - @GraphContentBuilder graphContent: @escaping (ID) -> IG - ) - where - IG: GraphContent, - NodeID: Hashable, - Content == _GraphContentWrapper, - ID: Hashable - { - let pb = _GraphContentWrapper.pullback(id: id, graphContent) - self.init(data, id: id, content: pb) - } -} - -extension ForEach { - @inlinable - public init( - _ range: Range, - @GraphContentBuilder content: @escaping (Int) -> IG - ) - where - Data == Swift.Range, ID == Swift.Int, IG: GraphContent, NodeID: Hashable, - Content == _GraphContentWrapper - { - self.init(range, id: \.self, content: _GraphContentWrapper.pullback(content)) - } -} - -// extension ForEach -// where -// Data == Swift.Range, ID == Swift.Int, Content: _GraphContentWrappingView -// { -// @inlinable -// public init( -// _ data: RawData, -// @GraphContentBuilder graphContent: @escaping (RawData.Element) -> IG -// ) -// where -// IG: GraphContent, NodeID: Hashable, -// RawData: RandomAccessCollection, -// RawData.Indices == Swift.Range, -// Content == _GraphContentWrapper -// { - -// self.init(data.indices, id: \.self) { index in -// graphContent(data[index]) -// } -// } -// } diff --git a/Sources/Grape/Contents/_IdentifiableNever.swift b/Sources/Grape/Contents/_IdentifiableNever.swift new file mode 100644 index 0000000..4dfd5f2 --- /dev/null +++ b/Sources/Grape/Contents/_IdentifiableNever.swift @@ -0,0 +1,13 @@ +public enum _IdentifiableNever { } + +extension _IdentifiableNever: GraphContent { + public typealias NodeID = ID + + public var body: Self { + fatalError() + } + + public func _attachToGraphRenderingContext(_ context: inout _GraphRenderingContext) { + fatalError() + } +} \ No newline at end of file diff --git a/Sources/Grape/Contents/_OptionalGraphContent.swift b/Sources/Grape/Contents/_OptionalGraphContent.swift index 21f4643..d62bfcc 100644 --- a/Sources/Grape/Contents/_OptionalGraphContent.swift +++ b/Sources/Grape/Contents/_OptionalGraphContent.swift @@ -23,4 +23,9 @@ where C: GraphContent { content._attachToGraphRenderingContext(&context) } } + + @inlinable + public var body: _IdentifiableNever { + fatalError() + } } \ No newline at end of file diff --git a/Sources/Grape/Contents/_PairedGraphContent.swift b/Sources/Grape/Contents/_PairedGraphContent.swift index 782c372..303be68 100644 --- a/Sources/Grape/Contents/_PairedGraphContent.swift +++ b/Sources/Grape/Contents/_PairedGraphContent.swift @@ -19,4 +19,10 @@ where C1: GraphContent, C2: GraphContent, NodeID: Hashable, C1.NodeID == NodeID, first._attachToGraphRenderingContext(&context) second._attachToGraphRenderingContext(&context) } -} \ No newline at end of file + + + @inlinable + public var body: _IdentifiableNever { + fatalError() + } +} diff --git a/Sources/Grape/Views/ForceDirectedGraph+View.swift b/Sources/Grape/Views/ForceDirectedGraph+View.swift index a87586a..0db3166 100644 --- a/Sources/Grape/Views/ForceDirectedGraph+View.swift +++ b/Sources/Grape/Views/ForceDirectedGraph+View.swift @@ -31,29 +31,29 @@ extension ForceDirectedGraph: View { // #if DEBUG - @ViewBuilder - @inlinable - var debugView: some View { - VStack(alignment: .leading, spacing: 8.0) { - Text("Elapsed Time: \(model.currentFrame)") - Divider() - Text(self.model.changeMessage) - Divider() - Button { - // self.clickCount += 1 - } label: { - Text("Click") - } + // @ViewBuilder + // @inlinable + // var debugView: some View { + // VStack(alignment: .leading, spacing: 8.0) { + // Text("Elapsed Time: \(model.currentFrame)") + // Divider() + // Text(self.model.changeMessage) + // Divider() + // Button { + // // self.clickCount += 1 + // } label: { + // Text("Click") + // } - ScrollView { - ForEach(self.model.graphRenderingContext.nodes, id: \.id) { node in - Text("\(node.debugDescription)") - } - }.frame(maxWidth: .infinity) + // ScrollView { + // ForEach(self.model.graphRenderingContext.nodes, id: \.id) { node in + // Text("\(node.debugDescription)") + // } + // }.frame(maxWidth: .infinity) - } - .frame(width: 200.0) - } + // } + // .frame(width: 200.0) + // } // #endif