4

I'm developing a macOS app that generates rectangles. I can separately move rectangles' points with left mouse button LMB. The principle of operation: when I drag 1 point with a LMB, the other 3 points will follow it at the same time with the same offset.

How can I move all four points of a rectangle simultaneously?

enter image description here

The code for constructing elements:

enum RectPoint {
    case point1(point: CGPoint)
    case point2(point: CGPoint)
    case point3(point: CGPoint)
    case point4(point: CGPoint)
 
    func pointCoord() -> [CGPoint] {    
        switch self {  
            case .point1(let point): return [point]
            case .point2(let point): return [point]
            case .point3(let point): return [point]
            case .point4(let point): return [point]
        }
    }
}
class SpecialView: NSView {
    var array: [RectPoint] = [] 
    private var trackVertex: RectPoint?
    private var trackVertexIndex: Int?
    private var trackElementIndex: Int?

    private func updateDragging(point: CGPoint) {
        guard let trackVertex = self.trackVertex,
              let trackVertexIndex = self.trackVertexIndex,
              let trackElementIndex = self.trackElementIndex   
        else { return }
  
        let newVertex = trackVertex.debugReleaseChecking(point, 
                                    atElementIndex: trackElementIndex)
        array[trackVertexIndex] = newVertex     
        self.needsDisplay = true
    }
}

Here's a method for dragging points:

func mouseDragged(event: NSEvent) {        
   var point = self.convertPoint(event.locationInWindow, fromView: nil)
   updateDragging(point) 
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • What kind of constraint are you imagining? There are all sorts of different ways of thinking about how the other points would move in relation to the one you're dragging. (That's the case you are thinking about, right? Versus just move-all-points-in-same-way-at-the-same-time?) – Ben Zotto Aug 09 '16 at 14:50
  • Yes, I'd like 3 other points move in relation to the point I'm dragging. – Andy Jazz Aug 09 '16 at 15:01
  • OK. You should clarify in what way they should move. In lockstep? As if the lines between the points are connected by elastic? As if the other points have mass? There are actually many effects you could be looking to achieve (from simple to complex physics), and you probably are imagining one specific effect, but you'll need to describe in detail what that effect is before anyone can help you figure out how to achieve it. – Ben Zotto Aug 09 '16 at 17:02
  • If I move one point of a rectangle, then the other three follow it synchronously. It's just the same effect as if I select a rectangle in Adobe Illustrator and drag it – all its components (points and lines between them) follow mouse pointer with the same offset at the same time. So If I offset one rectangle's point (x: +50, y: +200) using a mouse, the other three move with the same offset. I do not need an effect of elastic lines as if points have a mass. I do not need any dynamics. – Andy Jazz Aug 10 '16 at 11:02
  • Ah understood. Then why not simply update all of the geometry with the same offset? If the cursor moves, say (+20, -35), you can apply that offset to all of the geometry you wish to move. I don't know what your data structures look like, so you could either trivially loop over all relevant objects to update them. But most drawing programs will have an object hierarchy such that a "rectangle"'s points are all managed by a single shape object with its own "location"; updating the offset of the shape's location would then update all of its edges and points. – Ben Zotto Aug 10 '16 at 15:14
  • That would be hard to do in this case because you're showing only an excerpt of the relevant code here (why does a `RectPoint` have four cases and who calls `pointCoord`? what does `debugReleaseChecking` do?) , and to be candid it looks like you're going about storing your polygon in a bit of a nonstandard way. If you can post the complete code for this view with relevant object definitions, that would enable someone to look at it and provide you with both (a) a specific intervention to help and (b) possibly some feedback on a better structure. – Ben Zotto Oct 06 '16 at 13:07

1 Answers1

1

Basically, you need to calculate the offset that is being applied to the tracked point and apply that offset to all the other points. It's easiest to do this by separating the calculation and the update, so the update always runs on all points.

To calculate the offset just subtract the current point x and y from the new point x and y.

Something along the lines of:

func mouseDragged(event: NSEvent) {
    let eventPoint = self.convertPoint(event.locationInWindow, fromView: nil)
    let trackingPoint = XXX

    let xOffset = eventPoint.x - trackingPoint.x
    let yOffset = eventPoint.y - trackingPoint.y

    for point in allPoints {
        point.x += xOffset
        point.y += yOffset
    }
}
Wain
  • 118,658
  • 15
  • 128
  • 151
  • You know the current point being tracked, so you know its position, and you know the mouse position. – Wain Oct 04 '16 at 23:19
  • Thanx a lot. I've got two questions: 1)...What a trackingPoint is? and 2)...allPoints array is suitable for 4 points but what if I've got many rectangles, to say 100? – Andy Jazz Oct 06 '16 at 17:02
  • the point the touched to start dragging, i imagine it's related to self.trackVertex - if they don't have to start with a point then when they start you need to choose one and calculate the initial offset and apply that to the calculation in my answer too. you can also iterate over many rectangles, but if you're doing that you don't need an initial point, you're just tracking the mouse movement amount in between each update... – Wain Oct 07 '16 at 08:28
  • XXX is to be filled in with whatever the point you're tracking is - if you're moving all rectangles it could be the pervious mouse point... – Wain Oct 07 '16 at 08:46
  • you'd just set it when you get an update about a drag the first time (which you can tell because you don't already have one - because you also set it back to nil when the dragging is done) – Wain Oct 07 '16 at 09:01