[Drawkit] Questions on control knobs
Graham Cox
graham.cox at bigpond.com
Tue Jun 24 23:07:26 PDT 2008
On 25 Jun 2008, at 12:52 pm, Brad Larson wrote:
> I'm not quite sure how or where to set up and use the DKKnobs in my
> custom subclass. Attached is my fumbling attempt at creating an arc
> subclass that has three control points: center, starting point, and
> ending point. The idea is that you could drag around the center
> point to set the radius (it would need to be constrained to be
> equidistant from the starting and ending points), and the starting
> and ending points to set the starting and ending angle. After
> dragging one of the points, the internal Bezier path would be
> regenerated to fit the new values. centerPoint, startingPoint, and
> endingPoint are NSPoint properties, and startingPointKnobPartCode
> and endingPointKnobPartCode are integer properties.
>
> I can display my custom control points at the right place by
> overriding drawControlPointsOfPath:usingKnobs: , draw the shape
> properly using the arcCreateLoop: method (called from a custom
> drawing tool), and adjust the control point properties using
> moveByX:byY: on the movement of the whole object. However, I can't
> figure out how to assign a partcode to my custom center point and
> retrieve the existing partcodes within the Bezier path for the
> control points present at the beginning and ending of the path. I
> tried using hitPart: using the starting and ending point coordinates
> at the end of the arcCreateLoop: method, but that returns -1. If I
> had those partcodes, I believe that I could change the control point
> properties in mouseDraggedAtPoint: inPart:event: in response to
> dragging on the control points.
>
> Am I going about this the right way in my subclass or are there
> other methods that I should be overriding to achieve this behavior?
I'll be the first to admit, getting this right is not that easy,
especially without any docs. I think you've done pretty well
considering! However your code is probably overcomplicating things a
bit, though definitely on the right track.
I've had a stab at an arc object, which is still a bit rough, but is
getting there. It's quite similar to your code. I think one of the
things that has made it complicated for you is your choice of using
points to represent the parameters of the arc. Instead, I found it
more productive to declare these in a more abstract way - just float
values for the radius and the two angles. Then you can calculate the
location of the points on the fly relative to the object's location
and you therefore don't need to worry about intercepting positioning
calls so much. In my case I also keep track of the arc's centre point
which I define as the logical location of the object (even though it
falls outside of the object's actual bounds).
As you found, the most complicated thing is probably hit-testing the
special partcodes. This is somewhat convoluted, because DK has to
allow for grid/guide snapping and snapping to other objects, and
because of that the code is factored in such a way that it's quite
hard to follow. However, it boils down to the method:
- (int) hitSelectedPart:(NSPoint) pt forSnapDetection:(BOOL) snap;
which is overridden to detect the private partcodes you define. All it
needs to do is test the point passed to it against the knobs centred
on the calculated points derived from the arc parameters, then return
some number other than 0 or -1 which it can later use to tell which
knob was dragged.
Currently you have to override the mouseDragged: ... method to then
implement the dragging. Having gone through this exercise just now I
think there's a good case for refactoring that method a bit so that
it's easier to intercept the drag for private partcodes. The dragging
code needs to work back from the points given to calculate the radius,
start and end angles, which in turn recompute the path.
Regarding the DKKnob class, the thing there is to realise that all it
does is *draw* the knob at some given point. It has no knowledge of
partcodes or any other abstract meaning that might be attached to the
knob by a client object. Once you've worked out where the knob is, you
can use DKKnob to draw it for you. As such drawable objects don't have
instances of DKKnob private to themselves, they just get the instance
from the layer which is shared by all objects. By overriding:
- (void) drawControlPointsOfPath:(NSBezierPath*) path usingKnobs:
(DKKnob*) knobs
You are given the DKKnob instance to use, so you can just go ahead and
call it.
I'll attach my attempt below. It is pretty unpolished, but works by
and large. It should help you understand what needs to be overridden
(at this stage slightly more than would be ideal). Since I've done
this now I may add it to DK as a native object in future once all the
bugs have been worked out. (One that is a nuisance right now is that
the radius knob, which is drawn halfway between the two end points,
jumps 180° out of phase at certain positions. My brain must suffering
from coffee withdrawal or something because I couldn't quite put my
finger on a fix for that immediately). Also, as with yours, the knobs
sometimes don't get refreshed if they go outside the bounds, but
that's a relatively minor glitch - returning a bounds that always
includes all control points will fix that.
To test this, you can modify the standard registered Arc tool to use a
DKArcPath instance as its prototype.
Hope this helps - do let me know if you need any clarification, etc.
cheers, Graham
-------------- next part --------------
A non-text attachment was scrubbed...
Name: DKArcPath.h
Type: application/octet-stream
Size: 815 bytes
Desc: not available
URL: <http://lists.apptree.net/pipermail/drawkit-apptree.net/attachments/20080625/b6238bcf/attachment-0002.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: DKArcPath.m
Type: application/octet-stream
Size: 9832 bytes
Desc: not available
URL: <http://lists.apptree.net/pipermail/drawkit-apptree.net/attachments/20080625/b6238bcf/attachment-0003.obj>
More information about the Drawkit
mailing list