[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