[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