Invalidation

Figure measurements and the processing needed to obtain its output representation are asynchronous and possibly expensive operations. When you change a property of the figure, for performance reasons, Canvas will try hard to avoid performing the whole computation again by updating its internal measurements and output representation locally whenever possible. Expensive updating operations will not be triggered every time the model changes. The figure remembers its invalidation state waiting for the next update$. Model changes work in batch:

figure.pin( canvas.center() );
figure.matrix22( Mx.scale(2,2) );
figure.brush( Cx.Brush({ rgb: 'FF0000' }) );
// Now the figure is in an invalid state, measurements and the output representation
// that is rendered in Canvas are outdated
figure.update$() .then(function(){
// Now the figure measurements and its output representation are up to date,
// but the Canvas has not been touched
});

The model will automatically invalidate itself when using the public API so all these optimizations will work behind the scene and apps shouldn’t need to worry about them. But direct manipulation of primitive types needs a manual call to invalidate:

figure.pin().x = 20;
figure.matrix22().s00 = 2.0;
// figure is in an undefined invalidation state now
figure.invalidate();
// good, everything will work now

Avoid at all cost this pattern, apps should not need any .invalidate call at all if they treat primitive types as whole values always. You have to think that a point is actually equivalent to a number or a string in JavaScript. We can not expect that the model will work if you change a character in an array like this:

figure.name()[3] = 'a'; // this should strike you as the same as figure.pin().x = 20

So every time you change a point, a matrix or other primitives types in the model, just use them as immutable objects passing around:

var center = canvas.center();
figure.pin( center );

The list of primitives types in Canvas API is: number, integer, angle, magnitude, string, point, matrix, frame and bounds. They are easy to recognize because their members are plain JavaScript variables (.x and .y, .p0, .s00, etc).

Owned mechanics

Cx objects are generally owned by other objects in the document model. For example, the figures in a layer are owned by it and the brush in a contour belongs to that process. These objects implements a owned object protocol to avoid aliasing problems while modifying the document.

When a new object is created, it doesn’t belongs to any other object. So the following snippet will create a free brush:

var brush = Cx.Brush( { rgb: 'FF0000', alpha: 0.5 } );
Cx.assert( brush.owner() == null );

When a free object is used to set another object property, the free object is adopted by the parent object without making any copy:

contour.brush( brush );
// brush is no longer free, contour owns it
Cx.assert( brush.owner() == contour );

If an owned object is used to set a property, the object will be cloned to avoid duplicated references:

contour.brush( figure.brush() );
// contour's brush and figure's brush are different objects, owned by different parents.
Cx.assert( contour.brush() != figure.brush() );

The protocol was selected for being a good compromise between simplicity and efficiency (a possible option could be to use copy-on-write for efficiency or copy-on-set for simplicity). Free or owned objects shouldn’t be something you are thinking on while using the API. It just works, and you can relax about when to clone and object… you never need to do it, the model knows better than us how to avoid getting into troubles. The only caveat is that the following snippet could be confusing:

var brush = figure.brush();
// ...
contour.brush( brush );
brush.color( color ); // Wrong!
// contour's brush and brush are different objects, owned by different parents.
Cx.assert( contour.brush() != brush );

The way to avoid these patterns is:

contour.brush( figure.brush() );
var brush = contour.brush();
brush.color( color ); // Good...

But if you can not use the above pattern, you can add the clone explicitly:

var brush = figure.brush().clone();
Cx.assert( brush.owner() == null );
// ...
contour.brush( brush );
brush.color( color ); // Good...