mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-04-25 10:08:34 +02:00
feat: add any stroke wrapper
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; };
|
EC0D14262BF7A8C9009BFE5F /* PenObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14252BF7A8C9009BFE5F /* PenObject.swift */; };
|
||||||
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */; };
|
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */; };
|
||||||
EC1B783D2BFA0AC9005A34E2 /* Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1B783C2BFA0AC9005A34E2 /* Toolbar.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 */; };
|
EC2BEBF42C0F5FF7005DB0AF /* RTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */; };
|
||||||
EC2BEBF62C0F600D005DB0AF /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF52C0F600D005DB0AF /* Box.swift */; };
|
EC2BEBF62C0F600D005DB0AF /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF52C0F600D005DB0AF /* Box.swift */; };
|
||||||
EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC2BEBF72C0F601A005DB0AF /* Node.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 = "<group>"; };
|
EC0D14252BF7A8C9009BFE5F /* PenObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenObject.swift; sourceTree = "<group>"; };
|
||||||
EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = "<group>"; };
|
EC0D14272BF7BF20009BFE5F /* ContextMenuViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenuViewModifier.swift; sourceTree = "<group>"; };
|
||||||
EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
|
EC1B783C2BFA0AC9005A34E2 /* Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toolbar.swift; sourceTree = "<group>"; };
|
||||||
|
EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyStroke.swift; sourceTree = "<group>"; };
|
||||||
EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTree.swift; sourceTree = "<group>"; };
|
EC2BEBF32C0F5FF7005DB0AF /* RTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTree.swift; sourceTree = "<group>"; };
|
||||||
EC2BEBF52C0F600D005DB0AF /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = "<group>"; };
|
EC2BEBF52C0F600D005DB0AF /* Box.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = "<group>"; };
|
||||||
EC2BEBF72C0F601A005DB0AF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = "<group>"; };
|
EC2BEBF72C0F601A005DB0AF /* Node.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Node.swift; sourceTree = "<group>"; };
|
||||||
@@ -653,6 +655,7 @@
|
|||||||
ECE883B92C009DCA0045C53D /* Core */ = {
|
ECE883B92C009DCA0045C53D /* Core */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
EC2106AC2C10C2A700FBE27C /* AnyStroke.swift */,
|
||||||
ECE883BE2C00AB440045C53D /* Stroke.swift */,
|
ECE883BE2C00AB440045C53D /* Stroke.swift */,
|
||||||
ECA738D32BE60F9100A4542E /* StrokeGenerator.swift */,
|
ECA738D32BE60F9100A4542E /* StrokeGenerator.swift */,
|
||||||
ECE883C02C00C9CB0045C53D /* StrokeStyle.swift */,
|
ECE883C02C00C9CB0045C53D /* StrokeStyle.swift */,
|
||||||
@@ -834,6 +837,7 @@
|
|||||||
EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */,
|
EC2BEBF82C0F601A005DB0AF /* Node.swift in Sources */,
|
||||||
ECA738A02BE601E400A4542E /* ViewPortVertex.swift in Sources */,
|
ECA738A02BE601E400A4542E /* ViewPortVertex.swift in Sources */,
|
||||||
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */,
|
EC0D14282BF7BF20009BFE5F /* ContextMenuViewModifier.swift in Sources */,
|
||||||
|
EC2106AD2C10C2A700FBE27C /* AnyStroke.swift in Sources */,
|
||||||
ECA738BC2BE60E0300A4542E /* Tool.swift in Sources */,
|
ECA738BC2BE60E0300A4542E /* Tool.swift in Sources */,
|
||||||
ECA738972BE6014200A4542E /* Graphic.metal in Sources */,
|
ECA738972BE6014200A4542E /* Graphic.metal in Sources */,
|
||||||
ECA7388A2BE6006A00A4542E /* PipelineStates.swift in Sources */,
|
ECA7388A2BE6006A00A4542E /* PipelineStates.swift in Sources */,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import CoreData
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
final class GraphicContext: @unchecked Sendable {
|
final class GraphicContext: @unchecked Sendable {
|
||||||
var tree: RTree = RTree<PenStroke>(maxEntries: 8)
|
var tree: RTree = RTree<AnyStroke>(maxEntries: 8)
|
||||||
var object: GraphicContextObject?
|
var object: GraphicContextObject?
|
||||||
|
|
||||||
var currentStroke: (any Stroke)?
|
var currentStroke: (any Stroke)?
|
||||||
@@ -41,7 +41,7 @@ final class GraphicContext: @unchecked Sendable {
|
|||||||
switch event {
|
switch event {
|
||||||
case .stroke(let stroke):
|
case .stroke(let stroke):
|
||||||
guard let _stroke = stroke.stroke(as: PenStroke.self) else { return }
|
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
|
withPersistence(\.backgroundContext) { [stroke = deletedStroke] context in
|
||||||
stroke?.stroke(as: PenStroke.self)?.object?.graphicContext = nil
|
stroke?.stroke(as: PenStroke.self)?.object?.graphicContext = nil
|
||||||
try context.saveIfNeeded()
|
try context.saveIfNeeded()
|
||||||
@@ -55,7 +55,7 @@ final class GraphicContext: @unchecked Sendable {
|
|||||||
switch event {
|
switch event {
|
||||||
case .stroke(let stroke):
|
case .stroke(let stroke):
|
||||||
if let stroke = stroke.stroke(as: PenStroke.self) {
|
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
|
withPersistence(\.backgroundContext) { [weak self, stroke] context in
|
||||||
stroke.stroke(as: PenStroke.self)?.object?.graphicContext = self?.object
|
stroke.stroke(as: PenStroke.self)?.object?.graphicContext = self?.object
|
||||||
@@ -74,7 +74,7 @@ extension GraphicContext {
|
|||||||
object.strokes.forEach { stroke in
|
object.strokes.forEach { stroke in
|
||||||
guard let stroke = stroke as? StrokeObject else { return }
|
guard let stroke = stroke as? StrokeObject else { return }
|
||||||
let _stroke = PenStroke(object: stroke)
|
let _stroke = PenStroke(object: stroke)
|
||||||
tree.insert(_stroke, in: _stroke.strokeBox)
|
tree.insert(_stroke.anyStroke, in: _stroke.strokeBox)
|
||||||
if _stroke.isVisible(in: bounds) {
|
if _stroke.isVisible(in: bounds) {
|
||||||
let id = stroke.objectID
|
let id = stroke.objectID
|
||||||
queue.addOperation {
|
queue.addOperation {
|
||||||
@@ -97,9 +97,9 @@ extension GraphicContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadQuads(_ bounds: CGRect) {
|
func loadQuads(_ bounds: CGRect) {
|
||||||
for stroke in self.tree.search(box: bounds.box) {
|
for _stroke in self.tree.search(box: bounds.box) {
|
||||||
guard stroke.isEmpty else { continue }
|
guard let stroke = _stroke.stroke(as: PenStroke.self), stroke.isEmpty else { continue }
|
||||||
stroke.stroke(as: PenStroke.self)?.loadQuads()
|
stroke.loadQuads()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ extension GraphicContext {
|
|||||||
func endStroke(at point: CGPoint) {
|
func endStroke(at point: CGPoint) {
|
||||||
guard currentPoint != nil, let currentStroke = currentStroke?.stroke(as: PenStroke.self) else { return }
|
guard currentPoint != nil, let currentStroke = currentStroke?.stroke(as: PenStroke.self) else { return }
|
||||||
currentStroke.finish(at: point)
|
currentStroke.finish(at: point)
|
||||||
tree.insert(currentStroke, in: currentStroke.strokeBox)
|
tree.insert(currentStroke.anyStroke, in: currentStroke.strokeBox)
|
||||||
withPersistence(\.backgroundContext) { [currentStroke] context in
|
withPersistence(\.backgroundContext) { [currentStroke] context in
|
||||||
guard let stroke = currentStroke.stroke(as: PenStroke.self) else { return }
|
guard let stroke = currentStroke.stroke(as: PenStroke.self) else { return }
|
||||||
stroke.object?.bounds = stroke.bounds
|
stroke.object?.bounds = stroke.bounds
|
||||||
@@ -175,7 +175,7 @@ extension GraphicContext {
|
|||||||
|
|
||||||
func cancelStroke() {
|
func cancelStroke() {
|
||||||
if !tree.isEmpty, let stroke = currentStroke?.stroke(as: PenStroke.self) {
|
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
|
withPersistence(\.backgroundContext) { [graphicContext = object, _stroke] context in
|
||||||
if let stroke = _stroke?.stroke(as: PenStroke.self)?.object {
|
if let stroke = _stroke?.stroke(as: PenStroke.self)?.object {
|
||||||
graphicContext?.strokes.remove(stroke)
|
graphicContext?.strokes.remove(stroke)
|
||||||
|
|||||||
28
Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift
Normal file
28
Memola/Canvas/Geometries/Stroke/Core/AnyStroke.swift
Normal file
@@ -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<S: Stroke>(as type: S.Type) -> S? {
|
||||||
|
value.stroke(as: type)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,4 +121,8 @@ extension Stroke {
|
|||||||
func stroke<S: Stroke>(as type: S.Type) -> S? {
|
func stroke<S: Stroke>(as type: S.Type) -> S? {
|
||||||
self as? S
|
self as? S
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var anyStroke: AnyStroke {
|
||||||
|
AnyStroke(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,9 +45,8 @@ class GraphicRenderPass: RenderPass {
|
|||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
if renderer.redrawsGraphicRender {
|
if renderer.redrawsGraphicRender {
|
||||||
canvas.setGraphicRenderType(.finished)
|
canvas.setGraphicRenderType(.finished)
|
||||||
let strokes = graphicContext.tree.search(box: canvas.bounds.box)
|
for _stroke in graphicContext.tree.search(box: canvas.bounds.box) {
|
||||||
print(strokes.count)
|
let stroke = _stroke.value
|
||||||
for stroke in strokes {
|
|
||||||
if graphicContext.previousStroke === stroke || graphicContext.currentStroke === stroke {
|
if graphicContext.previousStroke === stroke || graphicContext.currentStroke === stroke {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user