From cf126134393264686c278f10b7ec5c1dd1fa9916 Mon Sep 17 00:00:00 2001 From: dscyrescotti Date: Wed, 5 Jun 2024 23:11:38 +0700 Subject: [PATCH] feat: add any stroke wrapper --- Memola.xcodeproj/project.pbxproj | 4 +++ Memola/Canvas/Contexts/GraphicContext.swift | 18 ++++++------ .../Geometries/Stroke/Core/AnyStroke.swift | 28 +++++++++++++++++++ .../Geometries/Stroke/Core/Stroke.swift | 4 +++ .../RenderPasses/GraphicRenderPass.swift | 5 ++-- 5 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift diff --git a/Memola.xcodeproj/project.pbxproj b/Memola.xcodeproj/project.pbxproj index 56d2e82..921a58b 100644 --- a/Memola.xcodeproj/project.pbxproj +++ b/Memola.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; }; EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */; }; EC1B783D2BFA0AC9005A34E2 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */; }; + EC2106AD2C10C2A700FBE27C /* AnyStroke.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */; }; EC2BEBF42C0F5FF7005DB0AF /* RTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */; }; EC2BEBF62C0F600D005DB0AF /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF52C0F600D005DB0AF /* Box.swift */; }; EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF72C0F601A005DB0AF /* Node.swift */; }; @@ -98,6 +99,7 @@ EC0D14252BF7A8C9009BFE5F /* PenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenObject.swift; sourceTree = ""; }; EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = ""; }; EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = ""; }; + EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyStroke.swift; sourceTree = ""; }; EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTree.swift; sourceTree = ""; }; EC2BEBF52C0F600D005DB0AF /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = ""; }; EC2BEBF72C0F601A005DB0AF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = ""; }; @@ -653,6 +655,7 @@ ECE883B92C009DCA0045C53D /* Core */ = { isa = PBXGroup; children = ( + EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */, ECE883BE2C00AB440045C53D /* Stroke.swift */, ECA738D32BE60F9100A4542E /* StrokeGenerator.swift */, ECE883C02C00C9CB0045C53D /* StrokeStyle.swift */, @@ -834,6 +837,7 @@ EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */, ECA738A02BE601E400A4542E /* ViewPortVertex.swift in Sources */, EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */, + EC2106AD2C10C2A700FBE27C /* AnyStroke.swift in Sources */, ECA738BC2BE60E0300A4542E /* Tool.swift in Sources */, ECA738972BE6014200A4542E /* Graphic.metal in Sources */, ECA7388A2BE6006A00A4542E /* PipelineStates.swift in Sources */, diff --git a/Memola/Canvas/Contexts/GraphicContext.swift b/Memola/Canvas/Contexts/GraphicContext.swift index 10fca2c..8b4bb61 100644 --- a/Memola/Canvas/Contexts/GraphicContext.swift +++ b/Memola/Canvas/Contexts/GraphicContext.swift @@ -11,7 +11,7 @@ import CoreData import Foundation final class GraphicContext: @unchecked Sendable { - var tree: RTree = RTree(maxEntries: 8) + var tree: RTree = RTree(maxEntries: 8) var object: GraphicContextObject? var currentStroke: (any Stroke)? @@ -41,7 +41,7 @@ final class GraphicContext: @unchecked Sendable { switch event { case .stroke(let stroke): guard let _stroke = stroke.stroke(as: PenStroke.self) else { return } - let deletedStroke = tree.remove(_stroke, in: _stroke.strokeBox) + let deletedStroke = tree.remove(_stroke.anyStroke, in: _stroke.strokeBox) withPersistence(\.backgroundContext) { [stroke = deletedStroke] context in stroke?.stroke(as: PenStroke.self)?.object?.graphicContext = nil try context.saveIfNeeded() @@ -55,7 +55,7 @@ final class GraphicContext: @unchecked Sendable { switch event { case .stroke(let stroke): if let stroke = stroke.stroke(as: PenStroke.self) { - tree.insert(stroke, in: stroke.strokeBox) + tree.insert(stroke.anyStroke, in: stroke.strokeBox) } withPersistence(\.backgroundContext) { [weak self, stroke] context in stroke.stroke(as: PenStroke.self)?.object?.graphicContext = self?.object @@ -74,7 +74,7 @@ extension GraphicContext { object.strokes.forEach { stroke in guard let stroke = stroke as? StrokeObject else { return } let _stroke = PenStroke(object: stroke) - tree.insert(_stroke, in: _stroke.strokeBox) + tree.insert(_stroke.anyStroke, in: _stroke.strokeBox) if _stroke.isVisible(in: bounds) { let id = stroke.objectID queue.addOperation { @@ -97,9 +97,9 @@ extension GraphicContext { } func loadQuads(_ bounds: CGRect) { - for stroke in self.tree.search(box: bounds.box) { - guard stroke.isEmpty else { continue } - stroke.stroke(as: PenStroke.self)?.loadQuads() + for _stroke in self.tree.search(box: bounds.box) { + guard let stroke = _stroke.stroke(as: PenStroke.self), stroke.isEmpty else { continue } + stroke.loadQuads() } } } @@ -159,7 +159,7 @@ extension GraphicContext { func endStroke(at point: CGPoint) { guard currentPoint != nil, let currentStroke = currentStroke?.stroke(as: PenStroke.self) else { return } currentStroke.finish(at: point) - tree.insert(currentStroke, in: currentStroke.strokeBox) + tree.insert(currentStroke.anyStroke, in: currentStroke.strokeBox) withPersistence(\.backgroundContext) { [currentStroke] context in guard let stroke = currentStroke.stroke(as: PenStroke.self) else { return } stroke.object?.bounds = stroke.bounds @@ -175,7 +175,7 @@ extension GraphicContext { func cancelStroke() { if !tree.isEmpty, let stroke = currentStroke?.stroke(as: PenStroke.self) { - let _stroke = tree.remove(stroke, in: stroke.strokeBox) + let _stroke = tree.remove(stroke.anyStroke, in: stroke.strokeBox) withPersistence(\.backgroundContext) { [graphicContext = object, _stroke] context in if let stroke = _stroke?.stroke(as: PenStroke.self)?.object { graphicContext?.strokes.remove(stroke) diff --git a/Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift b/Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift new file mode 100644 index 0000000..c80810a --- /dev/null +++ b/Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift @@ -0,0 +1,28 @@ +// +// AnyStroke.swift +// Memola +// +// Created by Dscyre Scotti on 6/5/24. +// + +import Foundation + +struct AnyStroke: Equatable, Comparable { + var value: any Stroke + + init(_ value: any Stroke) { + self.value = value + } + + static func == (lhs: AnyStroke, rhs: AnyStroke) -> Bool { + lhs.value.id == rhs.value.id + } + + static func < (lhs: AnyStroke, rhs: AnyStroke) -> Bool { + lhs.value.createdAt < rhs.value.createdAt + } + + func stroke(as type: S.Type) -> S? { + value.stroke(as: type) + } +} diff --git a/Memola/Canvas/Geometries/Stroke/Core/Stroke.swift b/Memola/Canvas/Geometries/Stroke/Core/Stroke.swift index f1d959e..6eb7526 100644 --- a/Memola/Canvas/Geometries/Stroke/Core/Stroke.swift +++ b/Memola/Canvas/Geometries/Stroke/Core/Stroke.swift @@ -121,4 +121,8 @@ extension Stroke { func stroke(as type: S.Type) -> S? { self as? S } + + var anyStroke: AnyStroke { + AnyStroke(self) + } } diff --git a/Memola/Canvas/RenderPasses/GraphicRenderPass.swift b/Memola/Canvas/RenderPasses/GraphicRenderPass.swift index 6e34684..8e5bf1e 100644 --- a/Memola/Canvas/RenderPasses/GraphicRenderPass.swift +++ b/Memola/Canvas/RenderPasses/GraphicRenderPass.swift @@ -45,9 +45,8 @@ class GraphicRenderPass: RenderPass { let graphicContext = canvas.graphicContext if renderer.redrawsGraphicRender { canvas.setGraphicRenderType(.finished) - let strokes = graphicContext.tree.search(box: canvas.bounds.box) - print(strokes.count) - for stroke in strokes { + for _stroke in graphicContext.tree.search(box: canvas.bounds.box) { + let stroke = _stroke.value if graphicContext.previousStroke === stroke || graphicContext.currentStroke === stroke { continue }