Skip to main content

Deformer Module

Introduction​

The following are only available to the JavaScript Deformer. Everything in this module is in the def namespace and so methods need prefixing with def..

Member Functions​

Hierarchy-Based Access​

The hierarchy-based methods provide access to the complete mesh tree. This is the preferred approach as it correctly handles nested groups and complex hierarchies.

getRootMesh() → cavalry.Mesh​

Returns the root cavalry.Mesh of the shape being deformed. Use this to access the complete mesh hierarchy for modification.

var root = def.getRootMesh();
// Traverse and modify the mesh hierarchy
def.setRootMesh(root);

setRootMesh(mesh:cavalry.Mesh)​

Sets the root mesh back after modification. This must be called to apply changes made to the mesh hierarchy.

// Helper function to recursively visit all meshes
function visitAllMeshes(mesh, func) {
for (var i = 0; i < mesh.childMeshCount(); i++) {
var child = mesh.getChildMeshAtIndex(i);
visitAllMeshes(child, func);
mesh.setChildMeshAtIndex(i, child);
}
func(mesh);
}

function deformMesh(mesh) {
var pathCount = mesh.count();
for (var p = 0; p < pathCount; p++) {
var path = mesh.getPathAtIndex(p);
var pd = path.pathData();

for (var idx = 0; idx < pd.length; idx++) {
// Move each point along its normal
pd[idx].point.x += pd[idx].normal.x * amount;
pd[idx].point.y += pd[idx].normal.y * amount;
}

path.setPathData(pd);
mesh.setPathAtIndex(p, path);
}
}

var root = def.getRootMesh();
visitAllMeshes(root, deformMesh);
def.setRootMesh(root);

Falloffs​

getFalloffAtPoint(x:number, y:number) → number​

Returns the combined falloff value (0-1) at the specified position. This includes the deformer's Strength setting and any connected falloff shapes. Coordinates should be in local mesh space.

function deformMesh(mesh) {
var pathCount = mesh.count();
for (var p = 0; p < pathCount; p++) {
var path = mesh.getPathAtIndex(p);
var pd = path.pathData();

for (var idx = 0; idx < pd.length; idx++) {
var pt = pd[idx].point;
var normal = pd[idx].normal;

// Get falloff at this point (includes Strength)
var falloff = def.getFalloffAtPoint(pt.x, pt.y);
if (falloff <= 0) continue;

pd[idx].point.x += normal.x * amount * falloff;
pd[idx].point.y += normal.y * amount * falloff;
}

path.setPathData(pd);
mesh.setPathAtIndex(p, path);
}
}

Bounding Box and Points​

getBoundingBox() → {x:number, y:number, width:number, height:number, centre:{x:number, y:number}, left:number, right:number, top:number, bottom:number}​

Return an object containing details about the selection's bounding box.

getPoints() → array[points]​

Return an array of positions for each point of a Shape. These can then be looped through these to manipulate them.

// Connect the Deformer to a Shape's Deformers attribute and hit 'Return'.
var points = def.getPoints();
console.log(JSON.stringify(points));

setPoints(array[points])​

Set the position for each point of a Shape.

// A simple sine wave deformer
var points = def.getPoints();
var bbox = def.getBoundingBox();
var maxX = bbox.x+bbox.width;
var frequency = 10;
var amplitude = 50;

for (let pt of points) {
let normX = cavalry.norm(pt.x, bbox.x, maxX);
pt.y += Math.sin((normX+n0*.1)*frequency)*amplitude;
}

def.setPoints(points);

Depth-Based Access​

The depth-based methods access meshes at specific levels of the hierarchy.

meshDepth​

The depth of the mesh tree (the highest number reported in the Mesh Explorer). This is a property/variable, not a method.

meshCountAtDepth(depth:int) → int​

Return the number of meshes at a given depth.

getMeshesAtDepth(depth:int) → [cavalry.Mesh]​

Return an array of cavalry.Mesh objects.

setMeshesAtDepth(depth:int, [cavalry.Mesh])​

Replace the meshes at the given depth with a cavalry.Mesh. This will not remove any un-replaced meshes.

getMeshAtDepthAtIndex(depth:int, index:int) → cavalry.Mesh​

Return a mesh at the given depth and index.

setMeshAtDepthAtIndex(depth:int, index:int, mesh:cavalry.Mesh)​

Replace a mesh at the given depth and index.

setTransformAtDepthAtIndex(depth:int, index:int, position:object, rotation:double, scale:object)​

Set the position of a mesh at the given depth and index. Position and Scale are optional objects with x and y values (e.g. {"x":20, "y": 5}).

clearTransformAtDepthAtIndex(depth:int, index:int)​

Reset the transform of the mesh at a given depth and index.

centrePivotAtDepthAtIndex(depth:int, index:int)​

Centres the pivot of the mesh at a given depth and index.

setMaterialAtDepthAtIndex(depth:int, index:int, material:cavalry.Material)​

Sets the Material object for the mesh at a given depth and index.

highestDepthWithPath() → int​

Returns the highest depth within a Mesh that contains a Path.

Examples​

Replace a child-mesh with another mesh.​

  1. Create a Text Shape.
  2. In the Attribute Editor, click the + button on the Text Shape's Deformers attribute and choose JavaScript Deformer.
  3. Click the + button at the bottom of the JavaScript Deformer's UI and choose 'Add Layer'.
  4. Create a Star Shape.
  5. Set the Star's Radius to 30.
  6. Connect starShape.id→javaScriptDeformer.array.1 - this is the new n1 array Id created in step 3.
  7. Replace the JavaScript Deformer's Expression with:
    var meshes = def.getMeshesAtDepth(3);
    var replaceIndex = n0;
    if (meshes.length > replaceIndex) {
    let currentMesh = def.getMeshAtDepthAtIndex(3, replaceIndex);
    let bbox = currentMesh.getBoundingBox();
    let path = n1.path;
    def.setMeshAtDepthAtIndex(3,replaceIndex, path);
    def.setTransformAtDepthAtIndex(3,replaceIndex, bbox.centre);
    }
  8. Change the JavaScript Deformer's n0 value.

Replace a child-mesh with a custom Path.​

  1. Create a Text Shape.
  2. In the Attribute Editor, click the + button on the Text Shape's Deformers attribute and choose JavaScript Deformer.
  3. Replace the JavaScript Deformer's Expression with:
    var meshes = def.getMeshesAtDepth(3);
    var replaceIndex = n0;
    if (meshes.length > replaceIndex) {
    var path = new cavalry.Path()
    var newMesh = new cavalry.Mesh();
    var material = new cavalry.Material();
    material.fillColor = "#ff24e0";
    material.stroke = true;
    material.strokeColor = "#000000";
    material.strokeWidth = 2;
    let bbox = meshes[replaceIndex].getBoundingBox();
    path.addEllipse(bbox.centre.x,bbox.centre.y,30,80);
    newMesh.addPath(path, material);
    def.setMeshAtDepthAtIndex(3,replaceIndex,newMesh);
    }
  4. Change the JavaScript Deformer's n0 value.

Modify a child-mesh at a depth and index.​

  1. Create a Text Shape.
  2. In the Attribute Editor, click the + button on the Text Shape's Deformers attribute and choose JavaScript Deformer.
  3. Click the + button at the bottom of the JavaScript Deformer and choose 'Add Int'.
  4. Replace the JavaScript Deformer's Expression with:
    var meshes = def.getMeshesAtDepth(3);
    var replaceIndex = n1;
    if (meshes.length > 3) {
    var material = new cavalry.Material();
    material.fillColor = "#ff24e0";
    material.stroke = true;
    material.strokeColor = "#000000";
    material.strokeWidth = 2;
    def.setMaterialAtDepthAtIndex(3,replaceIndex,material);
    def.centrePivotAtDepthAtIndex(3,replaceIndex)
    def.setTransformAtDepthAtIndex(3,replaceIndex, {}, n0, {"x": 0.8, "y": 0.8});
    }
  5. Change the JavaScript Deformer's n0 and n1 values.