wip: fix grid line

This commit is contained in:
dscyrescotti
2024-07-06 20:44:19 +07:00
parent 59b8f18a61
commit cbd2e4c484
9 changed files with 184 additions and 20 deletions

View File

@@ -119,6 +119,7 @@
ECF7B2E02C39169C004D2C57 /* Image++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2CD2C39169C004D2C57 /* Image++.swift */; };
ECF7B2E12C39169C004D2C57 /* View++.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2CE2C39169C004D2C57 /* View++.swift */; };
ECF7B2E42C39174D004D2C57 /* Platform.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2E32C39174D004D2C57 /* Platform.swift */; };
ECF7B2E72C39544E004D2C57 /* CenterClipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */; };
ECFA15202BEF21EF00455818 /* MemoObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA151F2BEF21EF00455818 /* MemoObject.swift */; };
ECFA15222BEF21F500455818 /* CanvasObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15212BEF21F500455818 /* CanvasObject.swift */; };
ECFA15242BEF223300455818 /* GraphicContextObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECFA15232BEF223300455818 /* GraphicContextObject.swift */; };
@@ -242,6 +243,8 @@
ECF7B2CD2C39169C004D2C57 /* Image++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Image++.swift"; sourceTree = "<group>"; };
ECF7B2CE2C39169C004D2C57 /* View++.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "View++.swift"; sourceTree = "<group>"; };
ECF7B2E32C39174D004D2C57 /* Platform.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Platform.swift; sourceTree = "<group>"; };
ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CenterClipView.swift; sourceTree = "<group>"; };
ECF7B2E82C395A8E004D2C57 /* Memola.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Memola.entitlements; sourceTree = "<group>"; };
ECFA151F2BEF21EF00455818 /* MemoObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoObject.swift; sourceTree = "<group>"; };
ECFA15212BEF21F500455818 /* CanvasObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanvasObject.swift; sourceTree = "<group>"; };
ECFA15232BEF223300455818 /* GraphicContextObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicContextObject.swift; sourceTree = "<group>"; };
@@ -320,6 +323,7 @@
isa = PBXGroup;
children = (
ECA738AC2BE60CC600A4542E /* DrawingView.swift */,
ECF7B2E62C39544E004D2C57 /* CenterClipView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -455,6 +459,7 @@
EC7F6BEA2BE5E6E300A34A7B /* Memola */ = {
isa = PBXGroup;
children = (
ECF7B2E82C395A8E004D2C57 /* Memola.entitlements */,
ECA738762BE5EE4E00A4542E /* App */,
ECA7387E2BE5FE4200A4542E /* Canvas */,
EC50500A2BF6672000B4D86E /* Components */,
@@ -1053,6 +1058,7 @@
ECA738E22BE610D000A4542E /* GraphicRenderPass.swift in Sources */,
ECE883BF2C00AB440045C53D /* Stroke.swift in Sources */,
ECA738DC2BE6108D00A4542E /* StrokeRenderPass.swift in Sources */,
ECF7B2E72C39544E004D2C57 /* CenterClipView.swift in Sources */,
ECF7B2D22C39169C004D2C57 /* CGFloat++.swift in Sources */,
EC4538892BEBCAE000A86FEC /* Quad.swift in Sources */,
ECE883BD2C00AA170045C53D /* EraserStroke.swift in Sources */,
@@ -1235,6 +1241,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";
@@ -1271,6 +1278,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = Memola/Memola.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"Memola/Preview Content\"";

View File

@@ -10,7 +10,11 @@ import Foundation
struct PointGridVertex {
var position: vector_float4
#if os(macOS)
var pointSize: Float = 256
#else
var pointSize: Float = 10
#endif
}
extension PointGridVertex {

View File

@@ -86,7 +86,7 @@ extension Canvas {
state = .closing
let previewImage = renderer?.drawPreview(on: self)
#if os(macOS)
#warning("TODO: implement for macos")
memoObject.preview = previewImage?.tiffRepresentation
#else
memoObject.preview = previewImage?.jpegData(compressionQuality: 0.8)
#endif
@@ -109,7 +109,12 @@ extension Canvas {
func updateTransform(on drawingView: DrawingView) {
let bounds = CGRect(origin: .zero, size: size)
let renderView = drawingView.renderView
#if os(macOS)
let drawingViewBounds = CGRect(origin: .zero, size: drawingView.bounds.size.multiply(by: zoomScale))
let targetRect = drawingView.convert(drawingViewBounds, to: renderView)
#else
let targetRect = drawingView.convert(drawingView.bounds, to: renderView)
#endif
let transform1 = bounds.transform(to: targetRect)
let transform2 = renderView.bounds.transform(to: CGRect(x: -1.0, y: -1.0, width: 2.0, height: 2.0))
let transform3 = CGAffineTransform.identity.translatedBy(x: 0, y: 1).scaledBy(x: 1, y: -1).translatedBy(x: 0, y: 1)

View File

@@ -125,8 +125,7 @@ final class Renderer {
return nil
}
#if os(macOS)
#warning("TODO: implement here")
return nil
return NSImage(cgImage: cgImage, size: .init(width: CGFloat(cgImage.width), height: CGFloat(cgImage.height))).flipped(flipVertically: true)
#else
return UIImage(cgImage: cgImage, scale: 1.0, orientation: .downMirrored)
#endif

View File

@@ -148,7 +148,12 @@ public class Tool: NSObject, ObservableObject {
let rect = CGRect(origin: .zero, size: targetSize)
#if os(macOS)
return (image, dimension)
let newImage = NSImage(size: rect.size, flipped: false) { destRect in
NSGraphicsContext.current?.imageInterpolation = .high
image.draw(in: destRect, from: NSZeroRect, operation: .copy, fraction: 1)
return true
}
return (newImage, dimension)
#else
UIGraphicsBeginImageContextWithOptions(targetSize, true, 1.0)
image.draw(in: rect)
@@ -163,8 +168,7 @@ public class Tool: NSObject, ObservableObject {
private func bookmarkPhoto(of image: Platform.Image, and previewImage: Platform.Image, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
#if os(macOS)
#warning("TODO: implement for macos")
let data = Data()
guard let data = image.tiffRepresentation else { return nil }
#else
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
#endif

View File

@@ -48,10 +48,14 @@ class CanvasViewController: Platform.ViewController {
}
#if os(macOS)
override func viewWillAppear() {
super.viewWillAppear()
override func viewWillLayout() {
super.viewWillLayout()
resizeDocumentView()
updateDocumentBounds()
}
override func viewWillAppear() {
super.viewWillAppear()
loadMemo()
}
@@ -95,9 +99,11 @@ class CanvasViewController: Platform.ViewController {
extension CanvasViewController {
func configureViews() {
#if os(macOS)
#warning("TODO: implement for macos")
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.white.cgColor
#else
view.backgroundColor = .white
#endif
renderView.autoResizeDrawable = false
renderView.enableSetNeedsDisplay = true
@@ -111,6 +117,15 @@ extension CanvasViewController {
renderView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
#if os(macOS)
scrollView.maxMagnification = canvas.maximumZoomScale
scrollView.minMagnification = canvas.minimumZoomScale
scrollView.hasVerticalScroller = true
scrollView.hasHorizontalScroller = true
scrollView.allowsMagnification = true
scrollView.drawsBackground = false
scrollView.scrollerKnobStyle = .dark
#else
scrollView.maximumZoomScale = canvas.maximumZoomScale
scrollView.minimumZoomScale = canvas.minimumZoomScale
scrollView.contentInsetAdjustmentBehavior = .never
@@ -119,6 +134,7 @@ extension CanvasViewController {
scrollView.showsHorizontalScrollIndicator = true
scrollView.delegate = self
scrollView.backgroundColor = .clear
#endif
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
@@ -129,6 +145,11 @@ extension CanvasViewController {
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor)
])
#if os(macOS)
scrollView.contentView = CenterClipView()
scrollView.contentView.drawsBackground = false
scrollView.documentView = drawingView
#else
scrollView.addSubview(drawingView)
drawingView.backgroundColor = .clear
drawingView.isUserInteractionEnabled = false
@@ -137,10 +158,10 @@ extension CanvasViewController {
func resizeDocumentView(to newSize: CGSize? = nil) {
#if os(macOS)
#warning("TODO: implement for macos")
scrollView.layoutSubtreeIfNeeded()
#else
scrollView.layoutIfNeeded()
#endif
let size = canvas.size
let widthScale = (newSize?.width ?? view.frame.width) / size.width
let heightScale = (newSize?.height ?? view.frame.height) / size.height
@@ -151,36 +172,42 @@ extension CanvasViewController {
let newFrame = CGRect(x: 0, y: 0, width: width, height: height)
drawingView.frame = newFrame
#if os(macOS)
let rect = scrollView.documentVisibleRect
let center = NSPoint(x: rect.midX, y: rect.midY)
scrollView.setMagnification(canvas.defaultZoomScale, centeredAt: center)
#else
scrollView.setZoomScale(canvas.defaultZoomScale, animated: true)
centerDocumentView(to: newSize)
#endif
#if os(iOS)
let offsetX = (newFrame.width * canvas.defaultZoomScale - view.frame.width) / 2
let offsetY = (newFrame.height * canvas.defaultZoomScale - view.frame.height) / 2
let point = CGPoint(x: offsetX, y: offsetY)
scrollView.setContentOffset(point, animated: true)
drawingView.updateDrawableSize(with: view.frame.size)
#endif
drawingView.updateDrawableSize(with: view.frame.size)
NSLog("[Memola] - drawingView: \(drawingView.frame.size.multiply(by: scrollView.magnification)), scrollView: \(scrollView.frame), renderView: \(renderView.frame)")
}
#if os(iOS)
func centerDocumentView(to newSize: CGSize? = nil) {
#if os(macOS)
#warning("TODO: implement for macos")
#else
let documentViewSize = drawingView.frame.size
let scrollViewSize = newSize ?? view.frame.size
let verticalPadding = documentViewSize.height < scrollViewSize.height ? (scrollViewSize.height - documentViewSize.height) / 2 : 0
let horizontalPadding = documentViewSize.width < scrollViewSize.width ? (scrollViewSize.width - documentViewSize.width) / 2 : 0
self.scrollView.contentInset = UIEdgeInsets(top: verticalPadding, left: horizontalPadding, bottom: verticalPadding, right: horizontalPadding)
#endif
}
#endif
func updateDocumentBounds() {
#if os(macOS)
#warning("TODO: implement for macos")
var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.magnification)
#else
var bounds = scrollView.bounds.muliply(by: drawingView.ratio / scrollView.zoomScale)
#endif
let xDelta = bounds.minX * 0.0
let yDelta = bounds.minY * 0.0
bounds.origin.x -= xDelta
@@ -191,12 +218,33 @@ extension CanvasViewController {
if canvas.state == .loaded {
canvas.loadStrokes(bounds)
}
#endif
}
}
extension CanvasViewController {
func configureListeners() {
#if os(macOS)
NotificationCenter.default.publisher(for: NSScrollView.didEndLiveMagnifyNotification, object: scrollView)
.sink { [weak self] _ in
self?.magnificationEnded()
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: NSScrollView.willStartLiveMagnifyNotification, object: scrollView)
.sink { [weak self] _ in
self?.magnificationStarted()
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: NSScrollView.willStartLiveScrollNotification, object: scrollView)
.sink { [weak self] _ in
self?.draggingStarted()
}
.store(in: &cancellables)
NotificationCenter.default.publisher(for: NSScrollView.didEndLiveScrollNotification, object: scrollView)
.sink { [weak self] _ in
self?.draggingEnded()
}
.store(in: &cancellables)
#endif
canvas.$state
.sink { [weak self] state in
self?.canvasStateChanged(state)
@@ -344,6 +392,9 @@ extension CanvasViewController: UIScrollViewDelegate {
extension CanvasViewController {
func magnificationStarted() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
guard !renderer.updatesViewPort else { return }
drawingView.touchCancelled()
canvas.updateClipBounds(scrollView, on: drawingView)
@@ -352,6 +403,9 @@ extension CanvasViewController {
}
func magnificationEnded() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
renderer.setUpdatesViewPort(false)
renderer.setRedrawsGraphicRender()
renderView.draw()
@@ -359,6 +413,9 @@ extension CanvasViewController {
}
func draggingStarted() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
guard !renderer.updatesViewPort else { return }
canvas.updateClipBounds(scrollView, on: drawingView)
drawingView.disableUserInteraction()
@@ -366,6 +423,9 @@ extension CanvasViewController {
}
func draggingEnded() {
#if os(macOS)
canvas.setZoomScale(scrollView.magnification)
#endif
renderer.setUpdatesViewPort(false)
renderer.setRedrawsGraphicRender()
renderView.draw()
@@ -413,7 +473,8 @@ extension CanvasViewController {
extension CanvasViewController {
func zoomChanged(_ zoomScale: CGFloat) {
#if os(macOS)
#warning("TODO: implement for macos")
let rect = scrollView.documentVisibleRect
scrollView.setMagnification(zoomScale, centeredAt: CGPoint(x: rect.midX, y: rect.midY))
#else
scrollView.setZoomScale(zoomScale, animated: true)
#endif

View File

@@ -0,0 +1,25 @@
//
// CenterClipView.swift
// Memola
//
// Created by Dscyre Scotti on 7/6/24.
//
#if canImport(AppKit)
import AppKit
class CenterClipView: NSClipView {
override func constrainBoundsRect(_ proposedBounds: NSRect) -> NSRect {
var rect = super.constrainBoundsRect(proposedBounds)
if let containerView = self.documentView {
if (rect.size.width > containerView.frame.size.width) {
rect.origin.x = (containerView.frame.width - rect.width) / 2
}
if(rect.size.height > containerView.frame.size.height) {
rect.origin.y = (containerView.frame.height - rect.height) / 2
}
}
return rect
}
}
#endif

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@@ -18,6 +18,59 @@ extension Image {
}
}
#if os(macOS)
extension NSImage {
func upsideDownMirrored() -> NSImage {
let degrees: CGFloat = 180
let sinDegrees = abs(sin(degrees * CGFloat.pi / 180.0))
let cosDegrees = abs(cos(degrees * CGFloat.pi / 180.0))
let newSize = CGSize(
width: size.height * sinDegrees + size.width * cosDegrees,
height: size.width * sinDegrees + size.height * cosDegrees
)
let imageBounds = NSRect(
x: (newSize.width - size.width) / 2,
y: (newSize.height - size.height) / 2,
width: size.width,
height: size.height
)
let otherTransform = NSAffineTransform()
otherTransform.translateX(by: newSize.width / 2, yBy: newSize.height / 2)
otherTransform.rotate(byDegrees: degrees)
otherTransform.translateX(by: -newSize.width / 2, yBy: -newSize.height / 2)
let rotatedImage = NSImage(size: newSize)
rotatedImage.lockFocus()
otherTransform.concat()
draw(in: imageBounds, from: CGRect.zero, operation: NSCompositingOperation.copy, fraction: 1.0)
rotatedImage.unlockFocus()
return rotatedImage
}
func flipped(flipHorizontally: Bool = false, flipVertically: Bool = false) -> NSImage {
let flippedImage = NSImage(size: size)
flippedImage.lockFocus()
NSGraphicsContext.current?.imageInterpolation = .high
let transform = NSAffineTransform()
transform.translateX(by: flipHorizontally ? size.width : 0, yBy: flipVertically ? size.height : 0)
transform.scaleX(by: flipHorizontally ? -1 : 1, yBy: flipVertically ? -1 : 1)
transform.concat()
draw(at: .zero, from: NSRect(origin: .zero, size: size), operation: .sourceOver, fraction: 1)
flippedImage.unlockFocus()
return flippedImage
}
}
#endif
#if os(iOS)
extension UIImage {
func imageWithUpOrientation() -> UIImage? {