DrawingTools

This module provides functions to manipulate the layers of a Drawing. All the operations are executed upon call. When calling any of the methods, the indices of the layers and the strokes in a layer might end up modified.

Methods

(static) createLayers(arg)

This function is used to create a layer in a specified drawing
Examples

This example creates a one field rectangular contour on the currently selected Element module at the current frame if there is a drawing exposed at this frame.
If not, the API to create a drawing should be called first.

 var n = selection.selectedNode(0); // First selected node
 DrawingTools.createLayers( {
   label : "My Modify layer Example", 
   drawing : {node: n, frame: frame.current()},
   art: 2, // Line art
   masks : [
      {
         polygon: true,
         path: [ {x: 60, y: 60}, 
                  {x: 70, y: 60}, 
                  {x: 70, y: 70}, 
                  {x: 60, y: 70},
                  {x: 60, y: 60}],
         holes : [
            [ {x: 65, y: 65}, 
            {x: 65, y: 68}, 
            {x: 68, y: 68}, 
            {x: 68, y: 65},
            {x: 65, y: 65}]
         ]    
      }
   ],
   layers : [
      {
         shaders : [
            { colorId : "0000000000000003" }  // This is the default vectorization color
         ],
         under : false,
         referenceLayer: 0,
         strokes : [ 
         {
            shaderLeft: 0,
         stroke : true,
         pencilColorId : "0000000000000003", // This is the default vectorization color
         thickness: 1,
         polygon: true,
         path: [ {x: 0, y: 0}, 
                  {x: 2500/12, y: 0}, 
                  {x: 2500/12, y: 1875/12}, 
                  {x: 0, y: 1875/12},
                  {x: 0, y: 0}]
         }
      ] }
   ]
   });

This example will generate a spiral of circles at the current frame for the selected drawing. If there is no drawing for the current selected element, it will try to create one.

    function createLayer(selectedElement, atFrame, path)
    {
       DrawingTools.createLayers( {
          label : "unused",  // This label will not be visible because the caller was wrapped in an undo/redo accumulator.
          drawing : {node: selectedElement, frame: atFrame},
              art: 2,
              layers : [
                 {
                   shaders : [
                      { colorId : "0000000000000003" }
                    ],
                    layerType: 10001,
                    strokes : [ 
                    {
                      shaderLeft: 0,
                      stroke : true,
                      pencilColorId : "08aeff1cd4c30c01",
                      thickness: 1,
                      path: path
                     }
                 ] }
              ]
         });
    
    }
    
    function generateSpiral(selectedElement, atFrame)
    {
       var angle = 0;
       var r = 0;
       while (angle < 3600)
       {
          var path = Drawing.geometry.createCircle({
             x : r * Math.cos(angle * Math.PI / 180.0),
             y : r * Math.sin(angle * Math.PI / 180.0),
             radius : 40 + 20 * Math.random()
          });
          createLayer(selectedElement, atFrame, path);
          r += 5;
          angle += 10 + Math.random() * 5;
       }
    }

    scene.beginUndoRedoAccum("My nice spiral example");

    var n = selection.selectedNode(0);
    var f = frame.current();
    // Wrap the generate spiral in an undo/redo accum to avoid having a very long undo list.
    var settings = Tools.getToolSettings();
    if (!settings.currentDrawing) {
      settings = Tools.createDrawing();
      if (!settings)
      {
        scene.cancelUndoRedoAccum();
        return;
      }
    }
    generateSpiral(n, f);
    scene.endUndoRedoAccum();
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Create Layers" The undo command label.
layers Array.<Object> <optional>
A list of layers definitions
Properties
Name Type Attributes Default Description
referenceLayer int <optional>
-1 Create the layer above or under this Layer index. Is not specified or -1, the layer willbe created on top or under the whole drawing.
under boolean <optional>
false Create the layer under a specific layer (layerReference) or under all layers.
shaders Array.<Object> <optional>
[] List of shader definition to be used when inserting strokes having colors on each of their sides.
type int <optional>
0 The type of layer to create. Harmony layers are created with layer type 0-9 depending on the tool used. Imported drawings are created with layer type 100-104. Applications dcan use bigger values for their own purposes.
contours Array.<Object> <optional>
[] List of contours to create in the layer.
Properties
Name Type Attributes Default Description
polygon boolean <optional>
false If true, the contour is a polygon. If false the contour is a Bezier path.
stroke boolean <optional>
false If true, the contour is created like if it was created with the Stroke Tool.
thickness double <optional>
0 If thickness is > 0, the contour are to be created using a Pencil line.
pencilColorId String <optional>
3 The pencil color id used to trace the contour's pencil line.
colorId String <optional>
"" The color id used to fill the contour.
path Array.<BezierPoint> <optional>
The color id used to fill the contour.
strokes Array.<Object> <optional>
[] List of strokes to insert in the layer.
masks Array.<BezierContour> <optional>
A list of masks that to be applied to the newly created layers.

(static) deleteLayers(arg)

This function is used to delete layers in a specified drawing
Example

This will delete the bottom layer of the first selected node drawing's line art at the current frame.

 var config = {
    label : "Delete bottom layer",
    drawing  : { node : selection.selectedNode(), frame : frame.current() },
    art : 2, // 0 = underlay, 1 = color art, 2 = line art, 3 = overlay 
    layers : [ 0 ] // bottom layer
 };
 DrawingTools.deleteLayers(config);
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Delete Layers" The undo command label.
layers Array.<int> <optional>
A list of layer indices.

(static) deleteStrokes(arg)

This function is used to delete strokes in a layer of a specified drawing. If a stroke is part of a painted contour, this will have the effect of unpainting the contour.
Example
var config = {
    label : "Delete one stroke",
    drawing  : { node : "Top/Drawing", frame : 1 },
    art : 2, // 0 = underlay, 1 = color art, 2 = line art, 3 = overlay 
    strokes :
    [
       {
         layer: 0,
         strokeIndex : 16
       }
    ]  
 };
 DrawingTools.deleteStrokes(config);
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Paint at" The undo command label.
strokes Array.<Object> <optional>
[] The list of strokes to delete.
Properties
Name Type Description
layer int The layer index of the stroke.
strokeIndex int The stroke index of the stroke.

(static) eraseLayers(arg)

This function is used to insert eraser contours in layers of a specified drawing. Please note, that this is not the same as deleteLayers. The deleteLayers function completely erases layers whereas eraseLayers is like using the Eraser Tool on a list of layers.
Example
var config = {
    label : "Erase bottom layer",
    drawing  : { node : "Top/Drawing", frame : 1 },
    art : 2, // 0 = underlay, 1 = color art, 2 = line art, 3 = overlay 
    layers : [ 0  ] ,
    masks : [
      {
         polygon: true,
         path: [ {x: 60, y: 60}, 
                  {x: 70, y: 60}, 
                  {x: 70, y: 70}, 
                  {x: 60, y: 70},
                  {x: 60, y: 60}],
         holes : [
            [ {x: 65, y: 65}, 
            {x: 65, y: 68}, 
            {x: 68, y: 68}, 
            {x: 68, y: 65},
            {x: 65, y: 65}]
         ]    
      }
   ]
 };
 DrawingTools.eraseLayers(config);
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Erase Layers" The undo command label.
layers Array.<int> <optional>
The list of layer indices to be modified. If empty or unspecified, all the layers of the drawing are erased.
masks Array.<BezierContour> <optional>
A list of masks that to be applied to the newly created layers.

(static) modifyLayers(arg)

This function is used to modify a layer in a specified drawing by replacing all of its strokes by other strokes. This method works exaclty as the createLayers method with the exception that a layer index must be specified by each object. One could replace the call to modifyLayers by a call to deleteLayers followed by a call to createLayers. The only problem with this approach is the index computation because the layer indices of the drawing change after the deletion. The use of modifyLayers prevent the user from having to compute the new indices of the layer for the createLayers.
Example
var config = {
    label : "This will be the undo/redo label",
    drawing  : { node : "Top/Drawing", frame : 1 },
    art : 2, // 0 = underlay, 1 = color art, 2 = line art, 3 = overlay 
    layers : [  ] // Definition of the layer strokes or contours
 };
 DrawingTools.modifyLayers(config);
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Modify Layers" The undo command label.
layers Array.<Object> <optional>
A list of layers definitions
Properties
Name Type Attributes Default Description
shaders Array.<Object> <optional>
[] List of shader definition to be used when inserting strokes having colors on each of their sides.
layer int Modify this layer index.
type int <optional>
0 The type of layer to create. Harmony layers are created with layer type 0-9 depending on the tool used. Imported drawings are created with layer type 100-104. Applications dcan use bigger values for their own purposes.
contours Array.<Object> <optional>
[] List of contours to create in the layer.
Properties
Name Type Attributes Default Description
polygon boolean <optional>
false If true, the contour is a polygon. If false the contour is a Bezier path.
stroke boolean <optional>
false If true, the contour is created like if it was created with the Stroke Tool.
stroke boolean <optional>
If true, the contour is created like if it was created with the Stroke Tool.
thickness double <optional>
0 If thickness is > 0, the contour is created using a Pencil line.
pencilColorId String <optional>
3 The pencil color id used to trace the contour's pencil line.
colorId String <optional>
"" The color id used to fill the contour.
path Array.<BezierPoint> <optional>
The actual bezier path of the contour.
strokes Array.<Object> <optional>
[] List of strokes to insert in the layer.
masks Array.<BezierContour> <optional>
A list of masks that to be applied to the newly created layers.

(static) modifyStrokes(arg)

This function is used to modify strokes in a layer of a specified drawing
Examples

This example will insert 2 points in the first stroke of the first layer of the drawing.

 var config = {
    label : "Modify one stroke",
    drawing  : { node : "Top/Drawing", frame : 1 },
    art : 2, // 0 = underlay, 1 = color art, 2 = line art, 3 = overlay 
    strokes : [ {
       layer: 0, strokeIndex: 0, insertPoints : [ 0.5, 1.5 ] // Will insert 2 points in the bezier path
    } ] 
 };
 DrawingTools.modifyStrokes(config);

 

This is a more complex example that will take the selected strokes of a drawing and connect their end point if they are not connected to their closest point in the drawing. When making this, we have to make sure to insert a bezier anchor at the closest point. In the example, the main function is connectPointsFromSelection().

 // Retrieve a layer by its index in the drawing layers
function findLayer(layers, index)
{
   for(var i=0 ; i<layers.length ; ++i)
     if (layers[i].index == index)
       return layers[i];
   return null;    
}

// Will look for joints in the selection that are not connected to any other strokes.
function findUnconnectedStrokePoints(strokes, drawingData)
{
   var ret = [];
   for(var i=0 ; i<strokes.selectedStrokes.length ; ++i)
   {
      var s = strokes.selectedStrokes[i];
      if (!s.stroke) continue;
      var layer = findLayer(drawingData.layers, s.layer);
      if (!layer) continue;
      var joints = layer.joints;
      for(var j=0 ; j<joints.length ; ++j)
      {
         // To be even more robust, we should make sure there are no other joint in other layers 
         // that are located at the same position...
         if (joints[j].strokes.length == 1 && joints[j].strokes[0].strokeIndex == s.strokeIndex)
         {
            ret.push({ layer : s.layer, strokeIndex : s.strokeIndex, vertex : joints[j].strokes[0].vertex,
              x : joints[j].x, y : joints[j].y});
         }
      }
   }
   return ret;
}

function findClosestPoints(strokes, drawingData, dmax)
{
   var consideredStrokes = [];

   // Build unique ids for the selected strokes to prevent connecting to them
   for(var si =0 ; si < strokes.length ; ++si)
   {
      var s = strokes[si];
      s.uniqueId = s.strokeIndex + "-" + s.layer;
      consideredStrokes.push(s.uniqueId);
   }

   for(var si =0 ; si < strokes.length ; ++si)
   {
      var s = strokes[si];
      var closest = null;
      for(var i=0 ; i < drawingData.layers.length ; ++i)
      {
         var layer = drawingData.layers[i];
         for(var j=0 ; j<layer.strokes.length ; ++j)
         {
            // Do not consider selected strokes for connection
            if (consideredStrokes.indexOf(j + "-" + layer.index) != -1) continue;
            var arg = {
               path : layer.strokes[j].path, 
               points : [ s ]
            }
            if (closest)
              arg.maxDistanceSq = closest.distanceSq;
            var currentClosest = Drawing.geometry.getClosestPoint(arg);
            if (currentClosest && currentClosest.length && currentClosest[0].closestPoint && (!closest || currentClosest[0].closestPoint.distanceSq < closest.distanceSq))
            {
               closest = currentClosest[0].closestPoint;
               closest.layer = layer.index;
               closest.strokeIndex = s.strokeIndex;
               closest.distanceSq = currentClosest[0].closestPoint.distanceSq;
            }
         }
      }
      s.closestPoint = closest;
   }
   return strokes;
}

function getStrokePath(drawingData, layerIndex, strokeIndex)
{
   for(var i=0 ; i < drawingData.layers.length ; ++i)
   {
      var layer = drawingData.layers[i];
      if (layer.index != layerIndex) continue;
      return layer.strokes[strokeIndex].path;
   }
   return null;
}

function connectPointsFromSelection()
{
   var settings = Tools.getToolSettings();

   var n = selection.selectedNode(0);
   var descriptor = {
      drawing : {node: n, frame: frame.current()},
      art: settings.activeArt
   };

   var sel = Drawing.selection.get(descriptor);
   
   var request = {
      drawing : descriptor.drawing,
      art : descriptor.art,
   }
   var data = Drawing.query.getStrokes(request);

   // This will iterate on the selected strokes and find the 
   // strokes that have unconnected end points.
   var toProcess = findUnconnectedStrokePoints(sel, data);

   // This will find the closest point on the drawing for each
   // point that was found in findUnconnectedStrokePoints
   toProcess = findClosestPoints(toProcess, data);

   var insertedPoints = {};
   var modifiedStrokes = {};
   
   for(var i=0 ; i<toProcess.length ; ++i)
   {
      var current = toProcess[i];
      if (!current.closestPoint) continue;
      if (!modifiedStrokes[current.uniqueId])
      {
         modifiedStrokes[current.uniqueId] = { layer: current.layer, strokeIndex : current.strokeIndex, path: getStrokePath(data, current.layer, current.strokeIndex) };
      }
      // This should not happen, but it does not hurt to make sure...
      if (!modifiedStrokes[current.uniqueId].path ) continue;

      modifiedStrokes[current.uniqueId].path[current.vertex] = { x : current.closestPoint.x, y: current.closestPoint.y, onCurve: true};

      var uniqueId = current.closestPoint.strokeIndex + "-" + current.closestPoint.layer;
      if (!insertedPoints[uniqueId])
      {
         insertedPoints[uniqueId] = { layer : current.closestPoint.layer, strokeIndex : current.closestPoint.strokeIndex, insertPoints : []};
      }
      insertedPoints[uniqueId].insertPoints.push(current.closestPoint.t);
   }

   var modifyStrokeCommand = {
      drawing : {node: n, frame: frame.current()},
      art: settings.activeArt,
      strokes : []
   }

   for(var i in insertedPoints)
   {
      if (!insertedPoints.hasOwnProperty(i)) continue;
      modifyStrokeCommand.strokes.push(insertedPoints[i]);
   }
   for(var i in modifiedStrokes)
   {
      if (!modifiedStrokes.hasOwnProperty(i)) continue;
      modifyStrokeCommand.strokes.push(modifiedStrokes[i]);
   }

   DrawingTools.modifyStrokes(modifyStrokeCommand);
}

connectPointsFromSelection();
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Paint at" The undo command label.
strokes Array.<Object> <optional>
[] The list of strokes to modify.
Properties
Name Type Attributes Description
layer int The layer index of the stroke.
strokeIndex int The index of the stroke in the layer.
path Array.<BezierPoint> <optional>
The new bezier path of the stroke.
closed boolean <optional>
If true, the path is closed.
polygon boolean <optional>
If true, the path is a polygon.
insertPoints Array.<double> <optional>
If no path is specified and an array of double is passed in this member, the stroke will have points inserted at each parameter in this array.

(static) paintAt(arg)

This function is used to paint at a specific point in a drawing like using the paint tool.
Example

This example will paint at coordinates (0,0) in the line art opf the first selected Element at the current frame using the vectorization color.

 var n = selection.selectedNode(0);
 var f = frame.current();
 DrawingTools.paintAt(
    {
       label : "Paint at (0,0)", 
       drawing : {node: n, frame: f},
           art: 2,
       points :
          [
             {
                x: 0, y: 0, colorId : "0000000000000003"
             }
          ] 
    }

 );
Parameters:
Name Type Description
arg Object A dictionary of options
Properties
Name Type Attributes Default Description
drawing Object Drawing descriptor
art Object The drawing's art index. Must be 0, 1, 2 or 3.
label String <optional>
"Paint at" The undo command label.
points Array.<Object> <optional>
[] The list of points and colors to using when painting.
Properties
Name Type Description
x double The x coordinate of the point.
y double The x coordinate of the point.
colorId String The color ID to use.