3

I want to create a canvas that draws diagrams.

For that I've created custom class derived from NSView where I've drown two rectangles and connected them with two lines.

I am using NSView.addTrackingArea functionality in order to move and resize rectangles.

The issue I am facing with, How to create tracking areas to track mouse events for lines? (NSTrackingArea accepts NSRect shapes.)

The second question is more about approach. How do you think, this approach is okay to manipulate with graphic objects?

enter image description here

Taras Shchybovyk
  • 416
  • 4
  • 17
  • If you really would like to use tracking areas and not some computer graphics algorithms for that, I think you should approximate your line with little rectangles. So you would potentially have for example 3x3 points areas where center of each square lands on the line. – Eugene Mankovski Aug 16 '16 at 12:42
  • What is the another approach? You mentioned graphics algorithms. – Taras Shchybovyk Aug 16 '16 at 12:58
  • We studied some of them as University what was long ago... But you can try to google something. I just found something on stack overflow: http://stackoverflow.com/questions/17692922/check-is-a-point-x-y-is-between-two-points-drawn-on-a-straight-line – Eugene Mankovski Aug 16 '16 at 14:15
  • this could be an answer to my question http://stackoverflow.com/questions/6482362/hit-detection-when-drawing-lines-in-ios – Taras Shchybovyk Aug 16 '16 at 16:22

1 Answers1

3

I don't think the approach you outline in your question is appropriate. As you've seen already tracking areas can't represent non-rectangular shapes, and if you're creating a graphics app you're going to need to represent a variety of shapes that aren't rectangular, not just lines. Fortunately help is at hand from Apple itself in the form Sketch, a sample drawing app that answers some fundamental questions about the basic setup of any program that wants to draw lots of different shapes to the screen.

enter image description here

Here's a quick overview of how they get shapes onto the canvas and how they hit-test those shapes:

  • The canvas is a single NSView with a single array containing Graphic objects.
  • Graphic is an abstract base class that inherits from NSObject. It exposes a number of empty methods:

    Graphic: NSObject
        - isContentUnderPoint(point: CGPoint) -> Bool
        - frame: CGRect
        - drawInRect(rect: CGPoint)
    
  • There are a number of concrete Graphic subclasses (Line, Rectangle, Circle, Square, etc), each of which implements the above methods, as required.

  • Getting the shapes to the screen occurs in the canvas's drawRect:

    // In MyCanvas (an NSView subclass)
    func drawRect(dirtyRect: CGRect) {
        for shape in self.graphics {
            shape.drawInRect(dirtyRect) 
        }
    }
    
  • Hit testing is much the same principle...

    // in MyCanvas (an NSView subclass)
    func mouseDown(theEvent: NSEvent {
        let canvasPoint = convertPoint(theEvent.locationInWindow, fromView: nil)
        for shape in self.graphics {
            if shape.isContentUnderPoint(canvasPoint) {
                // do something!
            }
        }
    }
    

In the case of a Line shape, a isContentUnderPoint implementation would maybe calculate the equation of the line (using its start- and end- point) and then plug in the mouse-down point to see if it fits the equation. All in all it would take no more than a handful of lines of code, and it completely avoids any Cocoa drawing gymnastics.

Paul Patterson
  • 6,840
  • 3
  • 42
  • 56
  • Looks like this is the answer to my second question. As for line detection, its not enough, I think. In order to hit the line you need to hit into exact point that will fit the line equation, no matter how fat the line is. I found the solution, is to build invisible NSBezierPath around the line, which is thicker than target line. And use NSBezierPath.containsPoint for hit detection. – Taras Shchybovyk Aug 19 '16 at 09:53