PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, July 27, 2022

[FIXED] How to bound the drawn shape to square as mouse dragged

 July 27, 2022     crop, ios, macos, nsimage, swift     No comments   

Issue

I am drawing a shape as mouse down event starts and that animation is drawn on mouse dragged events. Below is the code used:

override func mouseDown(with event: NSEvent) {

    self.startPoint = self.convert(event.locationInWindow, from: nil)
    if self.shapeLayer != nil {
        self.shapeLayer.removeFromSuperlayer()
        self.shapeLayer = nil
    }
    var pixelColor: NSColor = NSReadPixel(startPoint) ?? NSColor()
    shapeLayer = CAShapeLayer()
    shapeLayer.lineWidth = 1.0
    shapeLayer.fillColor = NSColor.clear.cgColor
    if pixelColor == NSColor.black {
        pixelColor = NSColor.color_white
    } else {
        pixelColor = NSColor.black
    }
    shapeLayer.strokeColor = pixelColor.cgColor
    shapeLayer.lineDashPattern = [1]
    self.layer?.addSublayer(shapeLayer)

    var dashAnimation = CABasicAnimation()
    dashAnimation = CABasicAnimation(keyPath: "lineDashPhase")
    dashAnimation.duration = 0.75
    dashAnimation.fromValue = 0.0
    dashAnimation.toValue = 15.0
    dashAnimation.repeatCount = 0.0
    shapeLayer.add(dashAnimation, forKey: "linePhase")
}

override func mouseDragged(with event: NSEvent) {
    let point: NSPoint = self.convert(event.locationInWindow, from: nil)
    let path = CGMutablePath()
    path.move(to: self.startPoint)
    path.addLine(to: NSPoint(x: self.startPoint.x, y: point.y))
    path.addLine(to: point)
    path.addLine(to: NSPoint(x: point.x, y: self.startPoint.y))
    path.closeSubpath()
    self.shapeLayer.path = path
}

Currently it will draw rectangle like this as well:

enter image description here

Can we bound it to square shape so as user starts dragging mouse it will always follow along square shape?


Solution

On mouseDragged:

  • get the distance from startPoint.x to point.x
  • get the distance from startPoint.y to point.y
  • use either the MAX or MIN (abs() values) of those two as both the Width and Height of the outline shape

Using MAX will draw a larger square, and it will "flip" when you cross an axis. Using MIN will draw a square that shrinks to the center as you approach an axis. Easiest way to see the difference is to try it out:

var startPoint: CGPoint = .zero

override func mouseDown(with event: NSEvent) {
    self.startPoint = self.convert(event.locationInWindow, from: nil)
}

override func mouseDragged(with event: NSEvent) {
    let point: NSPoint = self.convert(event.locationInWindow, from: nil)

    var newPoint: CGPoint = self.startPoint
    
    let xDiff = point.x - self.startPoint.x
    let yDiff = point.y - self.startPoint.y
    
    // using min() will cause the square to "shrink"
    //  as you get closer to an axis
    let dist = min(abs(xDiff), abs(yDiff))
    
    // using max() will cause the square to "flip"
    //  when crossing an axis
    //let dist = max(abs(xDiff), abs(yDiff))
    
    newPoint.x += xDiff > 0 ? dist : -dist
    newPoint.y += yDiff > 0 ? dist : -dist
    
    let path = CGMutablePath()
    path.move(to: self.startPoint)
    path.addLine(to: NSPoint(x: self.startPoint.x, y: newPoint.y))
    path.addLine(to: newPoint)
    path.addLine(to: NSPoint(x: newPoint.x, y: self.startPoint.y))
    path.closeSubpath()
    self.shapeLayer.path = path
}

in case someone's looking for a similar feature in iOS:

var startPoint: CGPoint = .zero

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        startPoint = touch.location(in: self)
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        let point = touch.location(in: self)
        
        var newPoint: CGPoint = startPoint
        
        let xDiff = point.x - startPoint.x
        let yDiff = point.y - startPoint.y

        // using min() will cause the square to "shrink"
        //  as you get closer to an axis
        //let dist = min(abs(xDiff), abs(yDiff))
        
        // using max() will cause the square to "flip"
        //  when crossing an axis
        let dist = max(abs(xDiff), abs(yDiff))

        newPoint.x += xDiff > 0 ? dist : -dist
        newPoint.y += yDiff > 0 ? dist : -dist

        let path = CGMutablePath()
        path.move(to: startPoint)
        path.addLine(to: CGPoint(x: startPoint.x, y: newPoint.y))
        path.addLine(to: newPoint)
        path.addLine(to: CGPoint(x: newPoint.x, y: startPoint.y))
        path.closeSubpath()
        shapeLayer.path = path
    }
}


Answered By - DonMag
Answer Checked By - Cary Denson (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing