[Drawkit] DKShapeGroups and DXF blocks
Graham Cox
graham.cox at bigpond.com
Mon Apr 13 19:51:39 PDT 2009
On 14/04/2009, at 7:47 AM, Allan Daly wrote:
> I'm getting ready to tackle drawing DXF "blocks" in DK and I wonder
> if I could ask you for some conceptual help.
>
> A "block" is a collection of DXF entities with a unique name that
> can be placed in a drawing multiple times. Inside the block all of
> the entities are positioned with respect to a base point. The entire
> block can then be placed in a drawing by "inserting" it at a
> location. The base point of the block is placed at the "insertion"
> point. Block translations and rotations then are referenced to the
> insertion point.
>
> The DKShapeGroup clearly is a close match to much of this behavior.
>
> If I have a list of entities that need to go into the DKShapeGroup,
> what is the best way to create the group? Do I need to add them to a
> drawing on a layer, then group them all together? Or is there a way
> to add a new DKShape (or whatever) into an existing group one at a
> time? How do I get the DKShapes into the right position relative to
> the "base point?" How does DK handle the base point? The
> documentation of the DKShapeGroup methods suggests that the base
> point becomes the center of the bounding rectangle -- but I need to
> be able to set the basepoint to match the block definition (could be
> anywhere) so when I insert it into the drawing later it will be in
> the right place.
>
> Also -- can I create a DKShapeGroup and not draw it? A block is
> really just a definition of a group of entities. It is not drawn
> until it is inserted into the drawing. Can I create a non-drawn
> DKShapeGroup then copy it when I need to insert it? is there an easy
> way to copy DKShapeGroups? Or even better, is there a way to
> reference the DKShapeGroup so that if I change the DKShapeGroup
> definition then all instances where that group is placed in the
> drawing would be automatically updated?
>
> I'm sure I can dive into this and get something working -- but if
> you wouldn't mind giving me some high level advice as to how you
> think it might be best to proceed I think that would save me a lot
> of time.
>
> Thanks for any help you can send my way.
This may need a bit of back-and-forth to get fully worked out, because
I'm not familiar with DXF. But yes, it sounds like DKShapeGroup is
what you want here.
You can create a DKShapeGroup (a.k.a "group") from a bunch of objects
in advance, then add the group to the layer. In fact I would recommend
this approach, rather than adding the objects to the layer then
grouping them. DKShapeGroup has a convenience method
+groupWithObjects: which is intended for this.
Currently it's not so easy to add objects one at a time to a group,
though if you end up needing to do that, it can probably be worked out.
Where things get slightly tricky is with managing the coordinate
systems within the groups. When an object is in a group, its location
is modified so that it's relative to the group. Other attributes such
as angle and size are unaffected, but special transforms that apply
group attributes are constructed for rendering. The location is
automatically adjusted when the object is grouped, so for example when
using +groupWithObjects:, the objects you pass here would have their
original locations, and be modified automatically. The group itself
would be initially located according to the locations of the original
objects. This reflects the typical usage of groups where you select a
bunch of existing objects and group them.
As long as the original object's locations are all set correctly
relative to each other, things will work out fine. Even if they are
stored in the DXF file such that the location is group-relative, it
should work because it's only the relative positions of the objects
that matters. The group needs to figure out the bounding rect of the
objects it contains, and that is used to set the initial bounds and
location of the group as a whole. You can then position the entire
group where you need it overall.
A group inherits from a shape, mostly to get the same interactive
behaviour. As a result, its location is initially set as the centre of
the bounding rect, but as with any shape, you can displace this
elsewhere using the -setOffset: method. This would be the way to set
your "basepoint" to anywhere within the bounding rect of the group.
Somehow there will be a way to determine the position of the basepoint
relative to the bounding rect of the group from the DXF data. For
example each object's position might be relative to the basepoint, so
after determining the union of the bounds of each individual object,
the basepoint will be relative to this bounding rect. Remember that
setOffset: requires a value that is proportional to the unit rect, so
a little further calculation will be needed. Note that if the
basepoint is outside of the bounding rect (not sure from your
description if this is possible), then that might work, but this is
currently untested since in DrawKit you can't interactively move the
location outside the bounds. If it turns out to be necessary it
shouldn't be a problem to modify the code as needed to allow you to do
this.
Once you've created the group you can add it to the layer as with any
object. If you simply retain it somewhere it won't be drawn if it's
not added to a layer, and it can be copied as with any object. The
group's contents are "deep copied" so you get a completely unique copy
including all of the content each time. Each copy can be positioned
and added to a layer as a separate object - once copied it retains no
connection to the original.
One further thing to mention about groups. When a group is drawn,
there are two ways that can be handled. One way is to build a
transform that is used to transform each contained object's path, then
draw the path using its style as normal. The other way is to build a
transform that affects the overall graphics context, then draw the
path as normal. The outcome is geometrically identical, but may differ
in appearance. In the first case, a stroke width for example isn't
scaled - 2 points is always 2 points, even if the object being stroked
is inside a group that has been scaled to some other size. In the
second case, the stroke width would scale according to any group
scaling, and so on. Which to use depends on your requirements. The
first case is often the one to go with for CAD-type apps and is the
default. The second might apply to more artistic type apps, and would
match what is usually done for SVG graphics. To use the second
approach, the group must be set to transform its contents visually by
setting the ivar m_transformVisually to YES. (Note that in the current
beta there's no accessor for this - there should be one, and I've
added it for the next update).
Hope this helps, but of course if you need more info or clarification,
just ask.
--Graham
More information about the Drawkit
mailing list