[Drawkit] DrawKit b6 now available

Graham Cox graham.cox at bigpond.com
Tue Jul 14 19:47:11 PDT 2009


Finally, I got around to updating the site with the latest source.

http://apptree.net/drawkitmain.htm

Note that this version has quite a large number of changes (due to the  
long time between updates - sorry about that) but by and large should  
require very few changes to your client code. Do let me know if you  
have any problems either with the downloads or with building.

Particular areas that you'll need to test are text and object metadata  
- they are the parts that have changed most significantly.

The release notes are copied below.

--Graham

Release 1.0 beta 6
July 15th, 2009

1. Objects that can be passed to a renderer must now implement the  
DKRenderable formal protocol. DKDrawableObject does, so no change is  
required. However it allows other kinds of objects to be passed to a  
style/renderer for special purposes if necessary while reducing the  
likelihood of introducing bugs by doing so. The previous informal  
protocol for this purpose is deprecated. The protocol is defined in  
DKRasterizerProtocol.h

2. Style scripting support, which has been deprecated for several  
releases now, has gone.

3. Additional objects storage class, DKBSPDirectObjectStorage, added.  
This uses a similar algorithm to DKBSPObjectStorage, but instead of  
indexing it stores the objects directly. This seems to yield  
significant speed advantages, though more profiling is needed to  
quantify this. The storage itself is not archived as part of the  
drawing - only its objects are, and the currently set preferred class  
for the storage is honoured on dearchiving. Files saved with Beta 5  
did archive the storage and they will be automatically converted when  
read. Re-saving the file will update to the newer approach.

4. Code that draws and iterates over drawables is gradually being  
updated to take advantage of the benefits of the storage model. In  
most cases this means that fewer objects need to be iterated over for  
many operations, yielding noticable performance benefits. In addition,  
time-critical code that uses NSEnumerator is replaced by the much  
faster CFArrayApplyFunction methodology. This works on 10.4 and  
earlier, unlike ObjC-2.0 fast enumeration. Upshot: Teh Snappy™. Maybe.

5. Locked layers are no longer auto-activated by default. This  
reflects the typical usage patterns for locked layers as background  
content or for tracing.

6. DKLayerGroup now correctly propagates hit-testing recursively down  
through a layer hierarchy.

7. Select/Edit tool now implements an alternative dragging methodology  
in an effort to boost performance substantially when dragging a large  
number of objects at once. This method uses a single drag image as a  
proxy for the objects being dragged, then repositions the real objects  
on mouse up. This approach can be turned off (thus using the old  
method), turned on for all objects or used when a certain threshold  
object count is exceeded. This is done by calling [DKSelectAndEditTool  
setDragProxyThreshold:] with values of 0, 1 and the required threshold  
respectively. The default in this release is to use the alternative  
method for 50 or more objects. The main downside of this approach is  
that the interleaving of the dragged objects with non-dragged objects  
during the drag cannot be preserved. The Z-order is maintained and  
restored at the end of the drag. In practice the performance benefit  
will probably outweigh this minor inconvenience. Currently the drag  
image will noticeably pixellate at higher zooms also.

8. Select/Edit tool now treats locked objects slightly differently. A  
drag that starts in a locked object selects it as usual, but now  
continues in the selection rect dragging mode. This gives more useful  
behaviour: objects overlaid on some locked background object can now  
be drag-selected as normal even if the drag started in the background  
object.

9. DKLayerGroup and DKViewController add methods to permit the  
implementation of a "Hide Others" and "Show All" command for operating  
on layers if desired.

10. Added some additional properties to DKHatching: line roughness and  
"wobblyness" which together can be used to create some additional  
effects.

11. Bugfix: objects resulting from combinatorial operations now  
preserve any attached user data from the source object.

12. Metadata keys in both DKLayer and DKDrawableObject are now case- 
insensitive. Existing files will have their keys converted if  
necessary, at the possible risk of data loss if you have keys that  
differ only in case. The case insensitivity is achieved by always  
converting all key strings to lowercase. If you set a key in the user  
info dictionary directly, rather than using the metadata methods, you  
need to ensure this otherwise your data won't be findable.

13. Drawable Objects now support the concept of ghosting, which draws  
the object using a light grey thin outline rather than its attached  
style. Otherwise the object behaves as a normal visible object.  
Ghosting is a good alternative to hiding objects in many applications.  
DKObjectDrawingLayer supports high-level commands for ghosting and  
unghosting items in the selection. Some renderers, such as  
DKTextAdornment, are ghosting aware, though most don't need to be as  
ghosting typically bypasses the style altogether. The grey ghosting  
colour is settable as a global preference using +[DKDrawableObject  
setGhostColour:]; You can also override an object's - 
drawGhostedContent method to customise the ghosted appearance.

14. Bugfix:  Layer Bring Forward/Bring To Front/Send Backward/Send To  
Back commands now operate without ambiguity within their immediate  
containing group only (which for the case of a flat layer list is the  
drawing, as before). The earlier implementation was buggy when  
additional layer groups were present.

15. Intersection operation now preserves the style and metadata from  
the lower object rather than the upper. This better reflects real- 
world usage of this operation to extract a shape from a larger one  
using an overlaid "cookie cutter".

16. Bugfix: -hexString in NSColor category makes a more accurate job  
of rounding the colour component values when determining the hex  
value. This prevents light greys for example incorrectly coming out as  
white.

17. "Break Apart" operation now available for shapes without  
converting to a path first. Metadata is preserved as is the true class  
of the object.

18. DKTextShape and DKTextPath modified in behaviour such that  
deliberate changes to the text made by the user "stick" properly when  
saving the file. The change involves mutating the existing style, if  
it's a registered style, into an identical one but lacking text  
attributes. This allows the text to be set both by the user directly,  
and also by applying a style having text attributes, but without any  
user-made changes getting reverted when a file containing such objects  
is reloaded. A memory leak and a KVO error bug was also fixed in the  
DKTextShape object.

19. Text on a path now lays out the full attributed string exactly as  
it would be drawn by the standard text system, so it is possible to  
vary attributes such as font, underlining, superscripting, colour and  
shadow within the string.

20. DKDrawableObject now supports a global table used to map classes  
when performing interconversions from one type of object to another  
(e.g. path -> shape). By customising this table, your app can  
substitute a subclass to be used when performing an interconversion  
that normally would make an object of the base class. All conversions  
done by DK now go through this table, but by default no substitution  
is performed (and in fact the table is nil).

21. The NSBezierPath+Geometry category has been split into a new  
category NSBezierPath+Text which contains all the text-on-path and  
text-wrap-in-shape methods.

22. DKTextPath now has a contextual menu that mirrors the one used by  
DKTextShape. Both have been cleaned up.

23. DKDrawablePath creation allows modal creation loops to be  
interrupted (ended) by hitting any key on the keyboard as well as the  
previously supported methods. This makes it easier to "get out of" a  
modal loop if you were not expecting to be in one. Also some  
additional feedback via the floating info window was added for several  
of the path loops that previously didn't have anything.

24. Text on a path now supports underline and strikethrough  
attributes. Currently these are based on the metrics returned by  
NSFont and some tweaking - the positioning differs very slightly from  
text laid out by the system and the discrepancy varies with the font.  
It's unclear why the system is not using the metrics it is given.  
Currently, the NSUnderlineByWordMask flag is not supported.

25. Text on a path now renders any text shadow the same way that  
NSTextView etc do. This is to give consistency beyween text blocks  
laid out by the system and text on paths. However this way of drawing  
shadows is a bit dumb - it changes according to the view zoom scale.  
You can use the earlier behaviour for text on a path by setting  
DK_TEXT_SHADOW_COMPATIBLE to 1 and recompiling. There's no way to  
change how the system draws text shadows though.

26. Locked path objects no longer show the control knobs and bars  
since they can't be moved anyway. On-path knobs are shown as before  
using the locked style. This reduces visual clutter.

27. DKGuideLayer now manages its cursor to provide useful feedback  
when dragging, deleting and placing. When creating a new guide by  
dragging from a ruler, the guide layer is now made active which brings  
its cursors into play. While dragging a guide, the up/down or left/ 
right curosr is shown. When not dragging, the open hand cursor is  
shown. When a guide is dragged into the "delete" zone (the margins,  
typically), the disappearing item cursor is shown and when a guide is  
deleted the 'poof' animation is shown. If locked the layer shows the  
arrow. Guide layers also now maintain the deletion area independently  
of the drawing's interior. By default this is set to be the same as  
the drawing's interior but you can set it to some other rect if you  
want (the most likely alternative is the entire drawing).

28. DKDrawing no longer calls DKStyleRegistry's loadDefaults from its  
own loadDefaults. This was assuming too much about what an app's needs  
are here. If you are relying on this, you need to call the registry's  
loadDefaults yourself from the application delegate's - 
applicationDidFinishLaunching: method. The same applies to the call to  
the registry's saveDefaults.

29. Layers can get directly notified when they, or a group containing  
them, was added to the root drawing. The intention is to perform  
additional setup with knowledge of the drawing such as its size. The  
method is -wasAddedToDrawing:

30. The persistence mechanism for DKDrawingTool made more general.  
Instead of -saveDefaults assuming that we are interested in the style,  
we leave it up to the tool object, using the new methods - 
persistentData and -shouldLoadPersistentData: DKObjectCreationTool now  
handles the style as before using this mechanism, but other tool  
classes can do something different. The change is fully backward  
compatible. In addition DKObjectCreationTool now correctly attempts to  
remerge the style with the registry as it should to ensure that two  
copies of the same style don't end up in the system (and then  
transferred to drawings). While a bug that was leaking into saved  
documents, it's self-correcting in that such documents will fix  
themselves up when they do their own remerge on being opened.

31. Removed misguided attempt at caching point hit-testing on  
drawables - led to problems with hit-testing reliability.

32. DKStroke drops the unimplemented properties pathScaleFactor and  
strokePosition, but gains lateralOffset, which is a better and more  
general solution (and one that is actually implemented).

33. DKDrawing now allows a delegate. Currently the only delegate  
methods posible are to hook into before and after drawing the content.

34. Ruler visibility is now persistent - new instances of  
DKDrawingView initially set the rulers according to the most recent  
state seen. If never set, the default is now hidden, not shown.

35. [NSBezierPath bezierPathByInterpolatingPath:] is now functional.

36. GCZoomView now implements all of the scrollwheel control flags as  
class methods, so they are applied to all instances in a given  
application. This makes more sense and allows them to be easily linked  
with user defaults. DKDrawingView defines some standard user defaults  
keys for enabling and setting the sense of scroll wheel operations.  
These are loaded as needed but never set - your app can expose these  
settings in its preferences if it wants to.

37. Menu items for "Hide/Show Grid" and "Hide/Show Guides" now use the  
actual set name of the layers in question, rather than assuming "Grid"  
and "Guides".

38. Default layer cache settings for DKObjectOwnerLayer are now  
settable. Saved cache settings are ignored. The default is no caching.  
Note that in real-world testing, using a PDF-based cache was found to  
be often slower than drawing the objects directly, especially with  
partitioned storage. CGLayer cacheing is still faster, but suffers  
from pixellation effects.

39. There is now a formal protocol - DKDrawableContainer - that  
defines the methods a container must implement if it claims to "own"  
or "contain" any drawable. Existing objects that were acting as  
containers, namely DKObjectOwnerLayer and DKShapeGroup, now formally  
adopt this protocol. There is no functional difference but the formal  
protocol both ensures correctness for containers and makes clear what  
methods a container needs to support.

40. Archiving of DKImageShape and interaction with image data manager  
reworked to give a much more robust and straightforward  
implementation. As before, wherever possible original image data is  
stored in a document archive and used to recreate image shapes as  
needed. Only one copy of the data is saved no matter how many image  
shapes share it, keeping file sizes down, and since the images are  
recreated from the original data when dearchived, quality is kept as  
high as possible. The difference now is that the image shape also  
retains a reference to the data and makes sure that this gets copied  
to new image data managers as they are needed. It can also create its  
own image even if no manager is available, and will update it when the  
opportunity arises. The result is far less chance of an image shape  
being unable to instantiate itself even when archived in a non- 
standard way or from within a custom container. Note that older  
archives will remain operational and will update to the revised  
archive scheme if saved.

41. Hit-testing of very narrow or offset paths made much more reliable  
using a temporary stroke attribute that isn't visible to the user.  
Allows tools such as insert/remove path point to work even on offset  
paths, or paths with very thin strokes that can be hard to hit.  
Additionally, hatches are treated as 'solid' for the purpose of hit- 
testing

42. New algorithm for generating the offset or parallel path, more  
accurate, also allows for differing treatments of corners (mitre,  
round or bevel). The use of paralleloidPathWithOffset2 now calls this  
new code using the path's current line join style.

43. An enhancement to the message forwarding performed by  
DKObjectDrawingLayer allows multiple selections to be handled  
automatically where the actual action is implemented by the object,  
rather than the layer. Previously, forwarding from the layer to an  
object was only done if exactly one selected object could respond to  
the message. This meant that any actions pertaining to a set of  
selected objects had to be implemented strictly by the layer. For  
certain operations, such as conversions, this was inconvenient, and  
disallowed such operations t be performed on more than one object at a  
time. Now this can be handled for multiple selected objects  
automatically, even when the action is handled only by the object. For  
backward compatibility, this feature is turned off by default, and is  
not saved in an archive. It can be enabled using - 
setMultipleSelectionAutoForwarding:YES. The layer will temporarily  
buffer selection changes when invoking multiple commands in this way  
such that the user will "see" the same selection behaviour as if the  
message had been handled by the layer itself.

44. The DK project now includes a unit test target. Over time more  
tests will be added; right now only a test for the BSP storage sub- 
system is included. However, the test is fairly thorough and as a  
result a couple of subtle bugs were found and corrected in the tested  
classes.

45. DKDrawkitInspectorBase was changed so that it passes nil to - 
redisplayContentForSelection: when the document resigned main.  
Previously it passed the "current" selection but this is stale when a  
document is closing.

46. The select/edit tool now posts a mouse up event if appropriate  
when an object is dragged out of a drawing. This is necessary because  
the normal drag handling swallows the mouse up, but without one the  
tool controller can be left in an unbalanced state. This fixes the  
issue of the undo manager going on strike following this kind of drag.

47. DKShapeGroup can now optionally clip its contents to the group's  
path. By default this is off, but easily enabled using a new  
contextual menu item. When enabled, the selection highlight reveals  
the path.

48. When interactively creating bezier paths and irregular polygons,  
the delete key can now be used to "back up" by one element (in other  
words, deleting the current point being positioned).

49. DKDrawingDocument factors the creation of a print drawing view  
into an overridable method allowing easy customisation of the print  
view.

50. DKStyleRegistry no longer enables style notifications by default.  
A new method, +setStyleNotificationsEnabled:, allows you to turn this  
on or off, but it's now up to your app to do this. It's only needed if  
you're using the built-in menu handling, otherwise leaving it off  
speeds up various operations. Also, DKCategoryManager uses a different  
approach in -replaceContentsWithData: which greatly improves its speed.

51. Double-click detection changed to be slightly earlier so that it  
is invoked even when a handle or other partcode of an object is hit.  
Previously it was only called when the "entire object" partcode was  
hit, so looking for particular partcodes in the double-click method  
didn't work.

52. A new vertical alignment constant for text-on-a-path automatically  
centres the text on the path based on the cap height of the font the  
text is drawn in. This visually centres the text on the path rather  
than placing its baseline there. DKTextAdornment implements this for  
text on path layouts which is therefore inherited by DKTextPath and  
DKTextShape.

53. DKStyle has some additional class methods for pasteboard  
operations: +canInitWithPasteboard: and +stylePasteboardTypes

54. DKRasterizers now broadcast notifications before and after every  
property change, so that non-style clients can easily get notified  
when any property of the reasterizer changes. This is used by  
DKTextPath, DKTextShape for example, which uses an internal adornment  
without a style.

55. DKTextADornment now uses a much more versatile method for handling  
substitution of metadata values into the text stream. Now, the  
adornment uses a DKTextSubstitutor (new object) to store the "master"  
string which replaces the earlier label. The user is able to simply  
embed metadata keys into this string by using a special delimiter (%%  
in the default implementation). When the string is displayed the  
metadata value is substituted for the embedded key. The property - 
identifier is deprecated and older adornment objects automatically  
convert themselves to the new method in a manner that preserves their  
original layout. DKTextShape and DKTextPath have been modified  
slightly so that when editing the text, it is the master string that  
is passed to the editor. Existing methods of DKTextAdornment  
automatically deal with the new design - for example -setLabel: passes  
the text as the master string. The main advantages of this change are  
that a) values can be substitued anywhere within the text label, not  
just appended, and b) that any number of embedded keys can be used at  
once. Note that the ablity to introspect object properties by keypath  
remains - just prefix the key with a '$' as before, e.g. %% 
$object.key.path

56. DKTextAdornment now supports a "knockout" text effect which places  
a filled and/or stroked outline of the text behind the glyphs.

57. DKTextAdornment now supports capitalization operations when  
displaying text.

58. DKTextADornment now adds a number of its special effects  
properties as attributes to the normal -textAttributes dictionary, but  
using keys which are unique to DK, so they can be placed alongside  
standard Cocoa text attributes. These attributes include the knockout  
parameters, the vertical alignment parameters and the capitalization  
settings. These attributes are thus cut-and-pastable as part of a  
style between objects that are aware of them including DKTextShape and  
DKTextPath.

59. DKShapeGroup now checks an object's validity when ungrouping. If  
the ungrouping causes the object to become invalid, it is discarded  
rather than be allowed to cause problems. An object is defined to be  
invalid if it contributes nothing visible to the drawing, so this has  
no effect on what you see but allows groups to work more robustly. To  
support this, DKDrawableShape now disallows an invalid path when  
ungrouping, and deliberately forces itself
into an invalid state if this occurs. This was a rare occurrence and  
won't affect the average user.

60. DKDrawing adds +dearchivingHelper and +setDearchivingHelper: to  
augment or replace the standard dearchiving delegate. This allows  
applications using DK to deal with obsolete or replacement classes of  
their own when dearchiving a drawing. In general it's a good idea to  
subclass DKUnarchivingHelper if you plan to do this, since that  
already deals with earlier Drawkit object evolution.

61. DKCategoryManager also now supports a dearchiving helper.  
DKUnarchivingHelper has moved to its own source file from DKDrawing,  
and the internal -changeCount state it was holding has been removed  
(it was not used anywhere) so the same helper instance can be reused  
in multiple places if required.

62. Hit-testing of text objects much improved in terms of accuracy and  
performance. When hit-testing, the real text isn't drawn but "greeked"  
text is instead, which doesn't mind about the heavy scaling and bitmap  
destination context used, and draws faster. The actual greeking effect  
is implemented by DKGreekingLayoutManager, a new class, used as  
necessary by DKTextAdornment. Greeked text can also be drawn by  
DKTextAdornment in normal use also, but since there's very little need  
for this it currently isn't a persistent or observable property.

63. DKDrawableObject now explicitly declares the userInfo property as  
a mutable dictionary. Other parts of DK were already assuming this to  
be the case. If you are storing any other object type here, you need  
to revise your code to store the object in the userInfo dictionary  
using a suitable key. The metadata API that built on this has been  
revised so that the metadata is stored as an additional dictionary  
within the userInfo dicitonary, rather than as individual items. Code  
may need to be revised that was accessing the userInfo directly as the  
metadata dictionary - a new method, -metadata, returns the appropriate  
dictionary. This change permits more flexibility when extending a  
class without compromising the metadata.

64. DKDrawableObject can now accept a delegate object. DK doesn't use  
it except to send it messages if it responds to the  
DKDrawableObjectDelegate informal protocol. This is basically reserved  
for the use of applications.

65. Memory leak fixed in DKDrawing export methods.

66. Bugs fixed with computational geometry on paths. Note that the  
optional OMNI methods have been found to exhibit an accumulative  
rounding error which may make them unsuitable when dealing with long  
paths. Thus in this version they are disabled by default, and the  
slightly slower, but more accurate DK methods are used.

67. When copying native objects on the pasteboard, an info object is  
also stored which can be used to get information about the objects on  
the clipboard without having to dearchive them. The data type is  
kDKDrawableObjectInfoPasteboardType. Currently the only info provided  
is a count of the objects and a breakdown of the classes and the  
numbers of each. The use of a special helper info object allows this  
to be expanded simply in the future as needed. Since the count is  
frequently used to update menus, etc, this is a lot more efficient.

68. When a DKDrawingView automatically creates the drawing "back end",  
it now also loads the DKDrawing defaults, which puts in place the  
standard registered tools. This is an added convenience further  
reducing the necessary setup for a working DK installation. Even with  
no UI at all, tool keyboard equivalents function for selecting  
standard tools.

69. "Smart" repeated pasting and duplication of objects now works as  
it was always meant to, by 'predicting' the appropriate offset for the  
paste.

70. A drawing's image manager is no longer archived. It's not  
necessary because each object using image data archives the data, and  
archiving ensures only a single copy of the same data is placed in the  
file. The image manager is still needed to handle multiple copies of  
the same image, but it is noe rebuilt on the fly. As an added bugfix,  
this change prevents data from images that have been deleted  
altogether from remaining in the image manager and subsequently  
getting archived, which was just adding unused content to the file  
unnecessarily.

71. Clean-up of a number of memory leaks and incorrectly named methods  
based on Clang static analyser (thanks to Brad for doing this). Some  
methods were renamed as a result so they fall into line with proper  
conventions for memory management - i.e. those including 'create' or  
'new' return retained objects or else renamed so that they do not  
imply this. Some minor code revision of apps using these methods may  
be needed.



More information about the Drawkit mailing list