I just thought I'd throw my 2ยข worth in here, since I've been down this very long road in great detail while building DrawKit, so I've learned a thing or two about this.

On 5 Feb 2009, at 5:54 am, Joseph Crawford wrote:

You make some very good points. Let me first start out by saying I was doing ALL the path work in drawRect: based on someone telling me that was the best practice, I then had a few people including you tell me that is not the case.

Absolutely wrong. -drawRect: is solely and unequivocally for drawing *only*. If you find yourself adding or removing points to a path in there, you've made an error in design.

This is all just messing around trying to replicate the capabilities of the curve tool in many drawing applications, I already saw how to replicate the drawing of a line and that would be a different tool to choose from.

Some apps don't do this all that well actually. For my money, the approach that Inkscape (and DrawKit) takes is the one I've found the more intuitive. This uses a mouse down to set the on-path point, then the immediately following drag to set the off-path (control) point for the two control points that are either side of the just-placed on-path point. These two control points and the on-path point always form a straight line, thus the curve that passes through the on-path point is tangent to this line. This ensures that the join between elements of the curve is smooth. If you don't want that you can go back and change the points later, but for initially laying down a curve, this is rapid and graceful. The next on-path point along then requires a new mouse- down, which implies that during the intervening mouse-up period, you need to attach the current end-point of the curve to the mouse and use mouse-moved events. Because of that, using a view's mouseDown/ mouseDragged/mouseUp methods is terribly unwieldy, so I found that making your own tracking loop and entering it from the mouseDown only, and staying in it until you've finished the entire curve works much better. This forms a short-term temporary mode where you are constructing your curved path. The next problem is how do you get out of this mode? I do it in several ways. If you double-click at the end of the path it finishes, or if you click on the first point of the path, forming a closed path, it finishes, or if you hit Escape, it finishes. Having the modal loop here allows you to make use of four mouse gestures (down, drag, up, move) instead of the usual three. It also allows you to use a different modal loop for others kinds of drawing tool - lines and polygons require a different series of gestures to create them.

There are other niceties too, like allowing modifier keys to constrain drags in various ways, such as forcing the angles between a control point and the on-path point to be whole intervals, or temporarily toggling "snap to grid" plus a few others. Real drawing apps need these sorts of things, so while you will probably feel that's overkill at this stage, it may be worth thinking about how you'd incorporate those into your design.

What would you suggest I look into for wanting to modify the points?

Essentially all that Cocoa provides is the - elementAtIndex:associatedPoints: method and its inverse. That's about as minimalistic as it gets - all of the other interactive stuff needed you'll have to write. In DrawKit I tackle this in several stages. First, I have a category on NSBezierPath that extends the low-level tools available for basic path manipulation. This provides methods for identifying the different points on a path relative to one another (for example if dragging a curve element control point you need to also modify its mate, bearing in mind that if the next or previous element isn't a curve it doesn't exist, etc). The highest level method in this category is the method:

- (void) moveControlPointPartcode:(int) pc toPoint:(NSPoint) p colinear:(BOOL) colin coradial:(BOOL) corad constrainAngle:(BOOL) acon;

Which wraps up all the nasty tricky stuff into one method which basically implements moving of any point on any path, so you'd call this in a loop (or from mouseDragged:) when dragging.

I have other classes that call this when they are responding to user edits in their paths, but basically this one does the grunt work. By the way you could use this if you want, DrawKit is free under a BSD license.

I had this working perfectly with a tempPath for the dragging and actually had it grey in color and when it was drawn it was done so in black (the visual queue) but was told that I could do it with only one path which is why I was looking into modifying the path elements. When I have one point the dragging operation always added more and more points which led to the line being drawn with every drag and that was not the wanted solution.

NSBezierPath is not rich enough as a class on its own to form the basis for the data model of a drawing app. You need to create a class of your own that "has a" path plus other attributes such as its stroke and fill colours, etc. This object is stored in your data model and draws its path on demand applying the visual attributes as it does so. In that way you can have paths with different colours. NSBezierPath on its own has no "colour" property, reflecting the fact that it's not exclusively used for drawing anyway - it can be used to represent curves in other situations, or for clipping.

A drawing app also needs essential methods such as "find the object hit by the mouse", "find the objects intersecting this rect" and many others. These sound trivial but turn out to have some subtle kinks - given arbitrary drawn effects on a path for example, how do you reliably detect a hit? (n.b. -containsPoint: is naive). It also needs to maintain a coherent drawing order, layers maybe, a grid, and probably a means of maintaining a selection and indicating what objects are selected to the user, and a way to apply operations to the selection, either using the mouse or through commands, and undo. Since the common approaches for doing these have become very standardised across a variety of apps over the years, it's possible to create a framework that provides all of this functionality in a generic and flexible way. Hence DrawKit. Other toolkits such as Qt provide similar things as well.

I'd say that if you're just doing this as a learning exercise, go for it. But if your aim is to actually create a drawing app, it's well worth considering not reinventing the wheel since it turns out it's a pretty big one! Anyway, feel free to use DK (or cannibalise it as you wish). http:apptree.net/drawkit.htm

You can download a very bare-bones demo app that also shows how DK implements curve creation/editing among others here: http://apptree.net/code/dk/Binaries/DKMiniDemo_app_1.2.zip

good luck!

--Graham











_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to