3D Applications

Simple 3D Application

For applications that needs to preview a product and offer basic modifications in 3D, a light weight version of the 3D machinery can be used. The model view

Cx3D.createPreview(domId,docId);

This function will give you back a Cx3D.Preview object that derives from Cx3D.ModelView and holds a Cx3D.ProductionTexture internally to make things easier on the dev side. This kind of application doesn’t use a Cx.Canvas at all, improving the performance in case many previews are used on the same page.

A demo can be found here.

<!DOCTYPE html>
<html>
<head> <meta http-equiv='Content-type' content='text/html; charset=utf-8'>
<title>Cx Canvas</title>
<script type="text/javascript" src="http://cadxcdn.blob.core.windows.net/core-edge/Cx-include.js"></script>
<script type="text/javascript" src="http://cadxcdn.blob.core.windows.net/canvas-edge/Canvas-include.js"></script>
<script type="text/javascript">
Cx.Config.three = true;
Cx.js( Cx.Core( { base: 'http://cadxcdn.blob.core.windows.net/core-edge' } ),
Cx.Canvas( { base: 'http://cadxcdn.blob.core.windows.net/canvas-edge' } ) );
</script>
</head>
<body>
<div id="preview" style="width:500px;height:500px;"></div>
<script type="text/javascript">
Cx.ready(function(){
Cx3D.createPreview('preview','8c4642b0-4669-4da9-a22c-b1e058f7ec2f');
});
</script>
</body>
</html>

Document Filter

If the app wants to show a modified version of the template, to avoid flickering, the document needs to be modified before any rendering. This is done using a document filter function:

Cx3D.createPreview( domId, docId, function(doc){
// modify what you want, no need to commit anything
// if you need any metrics, return the promises so we can wait for them
});

For example, modifying the first Text in the document is done with:

var preview = Cx3D.createPreview('preview','8c4642b0-4669-4da9-a22c-b1e058f7ec2f',function(doc){
var figure = Cx.findByType( doc.allPagesFigures(), Cx.Text );
if( figure )
figure.text('New text');
});

Modifying the document with user input

If the apps wants to show the original template, and later allow the user to change the text.

preview.ready$() .then(function(doc){
// Ok, document is loaded, you can enable your text box or buttons
})

Change actions over the document are written as usual:

// this shouldn't be called if the preview is not ready...
onTextChanged: function(value){
var doc = preview.document();
var figure = Cx.findByType( doc.allPagesFigures(), Cx.Text );
if( figure ){
figure.text(value);
doc.commit$();
}
}

Full Canvas Application

Later…

Debugging

If you add the cadx.debug=true switch in your App URL, Canvas will load debugging tools to aid in the development process.

Debugging Tools

CDL Editor

CDL Editor

Code Editor

Code Editor

Auto Selection

Whenever you add a figure to the active layer of a document, that figure
will get automatically selected for you in the Canvas when you commit$ your
transaction.

doc.add( figures );
doc.commit$(); // Triggers canvas.selectedFigures( figures );

If at any point of the transaction you change the selection by hand,
then auto selection will not kick in for newly selected figures. The
following transaction will leave Canvas without any selected figures.

doc.add([ figure, border ]);
doc.selectedFigures( figure );
doc.commit$(); // Manual selection, border is not selected

With this simple scheme, figure auto selection will do what is expected
for almost every transaction you will ever write. There are some corner
cases where you will not want want automatic selection to kick in for a
given transaction. If you find yourself in that case just call:

canvas.keepSelection();
doc.add(figure);
doc.commit$(); // Auto selection disabled for this transaction

You can call this function at any point before the transaction commit$.

If you want to disable the feature altogether for all your transactions
you can use:

canvas.autoSelection(false);

These is not recommended, because the app will need to select the newly
added figures by hand in each transaction.

Annotations

Figure annotations lets Apps highlight a figure including some text to give the user feedback. Annotations are Canvas dependent, you may spawn multiple Canvas over the same document showing different annotations in each of them.

The basic API is the following:

// Annotate figures with a given message and a highlight them with a box
canvas.annotate( , { text: 'Message', pen: pen });

This will annotate each of the figures with the text message over the highlight rectangle draw using the given pen.

The first parameter is actually quite flexible because there are different kind of scenarios where this feature will be useful.

// Annotate a single figure
canvas.annotate( figure, { ... } );
// Annotate multiple figures
canvas.annotate( figures, { ... } );
// You can also tell the system to only draw one annotation for the figures with
canvas.annotate( figures, { grouped: true, ... } );

These versions are useful, but normally applications will have some rules that defines when a figure should be annotated. The app could work the sync of the annotations after each commit but Canvas gives a tool for this common case that will simplify the work:

// Annotate every figure conditionally
canvas.annotate( function(figure){ ... return true if it should be annotated ... }, { ... } );
// Example, apply an annotation to each Text
canvas.annotate( function(figure){ return figure instanceof Cx.Text; }, { ... } );

You basically pass a filter function that will be used to dynamically find to which figures this annotation needs to be drawn. There is one shortcut in place if you just want to annotate every figure (also dynamically):

// Annotate all figures, same as function(figure){ return true; }
canvas.annotate( 'all', { ... } );

The basic parameters that you give to the annotation are:

Param Type Definition
text string Message to include next to the figure
pen Pen Pen used to stroke the highlight box
brush Brush Brush used to fill the highlight box, alpha is supported
color Color Message color if you want it to be different from the box color

Lets use a dynamic annotation to give feedback to the user about a probable date issue in its template:

// Annotate text that looks like date with "Not 2014?"
canvas.annotate( function(figure){
if( figure instanceof Cx.Text ){
var year = parseInt(figure.text(),10);
if( 2000 < year && year < 2014 )
return true;
}
return false;
},{
text: 'Not 2014?',
pen: Cx.Pen({color:Cx.Color({rgb:'F04111'})})
});
canvas.render$();

Here is what the user will see:

In this case the message is the same for all figures that pass the condition. But if you use dynamic annotations, you will normally want to set an individual message for each figure. Each of the annotation parameters accepts a function that takes a figure and returns the processed value. A good example, lets show the bounds of each figure to the user as an annotation:

// Annotate all figures with their bounds extents
canvas.annotate( 'all', {
pen: Cx.Pen({color:{rgb:'9A8CDB'}}),
text: function(figure){
var bounds = figure.bounds_();
return bounds.width().toFixed(0) + ' x ' +
bounds.height().toFixed(0) + ' mm';
}
});
canvas.render$();

Important The functions passed to annotate can safely use the sync version of the figure metrics ( figure.bounds_() ). Canvas will only produce the annotation when the figure has been properly updated. This will simplify a lot of use cases that involves metrics, like annotate to small or to big figures.

We can play with the box style, use dashed lines. Or only show the text. For example, if you do not pass any pen. The result will look like:

This looks really nice in this example, but it will be confusing if there are many overlapped figures. But I like the idea of having less noise so we can show annotations without the fear of covering everything with lines.

You have the option to use a different box style, using the boxStyle parameter. Only the default and ‘Clean’ is supported for the moment. The ‘Clean’ style only draws the corners of the box, so it looks like:

It is also possible to use overlapping annotations, the system will just place the messages on top of each other. But it is a good idea to avoid using to many annotations at the same time, and give the user some way to choose what he want to see.

If you want to remove the annotation, keep a reference to it and pass that to the Canvas unannotate function like:

var ann = canvas.annotate( figures, { ... } );
// later...
canvas.unannotate( ann );

You can also just clear all the annotations using:

canvas.clearAnnotations();

Conditions

Applications needs to be able to define the conditions under wich figures are able to be modified. These conditions are bussiness logic that is App dependent and can not be abstracted directly in Canvas. The Figure Conditions API fill this need providing a way for App to express rules that Canvas will use internally. At the same time, these conditions can be used by Apps to define their own logic. A condition query DSL is provided that makes it easy to define if a list of figures complies with a set of conditions.

App level conditions

Cx.Figure.condition defines or extends a condition:

Cx.Figure.condition(name,function(figure){ return boolean });
// Rasters will not be scalable for this App
Cx.Figure.condition('scalable',function(figure){
return ! ( figure instanceof Cx.Raster );
});

Once a condition is defined, it can be used to check if a list of figures fullfills the rule.

if( canvas.are(figures,'scalable') ){
// Good to go, allow this action...

Several conditions can be combined in single query

if( canvas.are(figures,'rotatable','scalable') ){
// Ok, transform the figures...

Because the figures will usually be the canvas.selectedFigures() list, you can just pass the target figures directly to the query:

if( canvas.are('selectedFigures','groupable') ){
// Green light, enable the group action buttons

All valid targets are accepted: 'figures', 'contextFigures', 'allFigures'.

Canvas driven by the App

Canvas uses conditions query checks internally in the Select Tool, the 3D Tool interaction, the HUD tools and actions and generally in every place that requires a decision that is better defined by the App.

The following conditions are checked: 'selectable', 'movable', 'scalable', 'rotatable', skewable', 'groupable', 'nodeEditable', 'zorderable'. For example, in 3D mode texture islands transformations are restricted and they are only movable and rotatable.

If the application extends any of these conditions, Canvas will correctly follow their rules.

// App defined decoration will not be selectable
Cx.Figure.condition('selectable',function(figure){
return figure.xType() != 'App.Decoration';
});

Helper conditions

Canvas pre-defines the following conditions: 'text', 'shape', 'raster', 'group', 'proxyGroup'.
Fine grained: 'simpleText', 'multiPartText', 'typesetText', 'fullColorRaster', 'monochromeRaster'.
For 3D mode: 'textureIsland'

These conditions can be used to simplify the condition definitions.

// Only text can be moved in this App
Cx.Figure.condition('movable','text');

The ! char can be used to negate a condition.

// Proxy Groups are not groupable in this App
Cx.Figure.condition('groupable','!proxyGroup');

Async Conditions

Async conditions can be specified post fixing the name with the $ char

// Defines what figures are small for this App
Cx.Figure.condition('small$',function(figure){
return figure.bounds$() .then(function(bounds){
return bounds.width() < 100 && bounds.height() < 100;
});
};

To check async conditions, the async version of the query is used

Cx.if$( canvas.are$( figures, '!small$' ), function(){
// Figures are big enough for this action to proceed...

Array Conditions

There are conditions that defines rules over the list of figures as a whole.

Cx.Figure.arrayCondition(name,function(figures){ return boolean });
// A list of figures is Groupable if there are at least two figures
Cx.Figure.arrayCondition('groupable',function(figures){
return figures.length >= 2;
});

Array conditions are combined with figure conditions when querying.

Cx.Figure.condition('groupable','!raster');
Cx.assert( canvas.are([ raster, text ],'groupable') == false );
// It passes the array condition, but fails because there is a raster

Canvas defines array conditions for 'groupable' and 'zorderable'.
It also defines helper conditions to check the number of figures: '1', '1+', '2', '2+'.

Cx.Figure.arrayCondition('groupable','2+');

Canvas Instance Conditions

It is advisable to use as much as possible App level figure conditions to define the rules of the application. If specific logic is needed for a given Canvas instance, the canvas version of the condition and arrayCondition functions can be used. This may be used to implement an interactive preview where figures can only be moved.

// None of the figures will be scalable in this Canvas
canvas.condition('scalable',function(figure){ return false; });
// Disallow z ordering if there is more than one figure in the list
canvas.arrayCondition('zorderable','1');

Canvas queries

Query to check if a list of figures fullfills a set of requirements

canvas.are(figures,conditions...);
canvas.are$(figures,conditions...);

Query to check if a single figure fullfills a set of requirements

canvas.is(figure,condition...);
canvas.is$(figure,conditions...);

Query to filter the figures that fullfills a set of requirements

canvas.only(figures,conditions...);
canvas.only$(figures,conditions...);

Save and Restore conditions

If the App can spawn several modes, each with its own bussines rules, common global conditions can be defined and then each mode can save at start up the current state of the conditions and restore them when the mode is changed (in the same way that HTML5 Canvas context.save() and canvas.restore() works).

Cx.Figure.saveConditions();
Cx.Figure.restoreConditions();

The same can be done with Canvas conditions, although normally it will be more convenient to instanciate a new Canvas for each mode.

canvas.saveConditions();
canvas.restoreConditions();