Home
Blog
Envision Training
Generating Assembly-Step Pages with evCreate: Automating 3D Model Breakdown

Generating Assembly-Step Pages with evCreate: Automating 3D Model Breakdown

Learn how to quickly create digital work instructions in Canvas Envision with the evCreate API by using the structure of a 3D model to generate step-by-step pages. It highlights how automation can save time and improve the accuracy of assembly documentation.

By
Trent Wanders
July 16, 2025

In our previous blog post, we showed how to deploy custom evCreate functionality as a button within Envision. Now that you know how to deploy custom code, it is time to explore examples of what is possible with evCreate API. This blog post dives deeper into an example of automation — showing you how to use evCreate to auto-generate step-by-step instruction pages from a 3D model’s assembly structure.

A 3D model’s assembly tree structure (EBOM) often closely resembles the actual intended manufacturing assembly order (MBOM). We can leverage this information by scripting instruction steps to be generated based on that structure. This saves significant time in the document creation process.

By exploring the evCreate API functions that are used in this example, it lays the groundwork for future customization and tailoring to your company’s specific needs.

What We’re Building

We’ll create a script that:

  • Automatically reads the selected 3D model's assembly structure.
  • Creates a step for each subassembly using a bottom-up order.
  • Creates one new page for each step.
  • Hides and shows only the relevant parts for each step.

Prerequisites

To follow along, you’ll need:

  • Admin user role in Canvas Envision.
  • A document containing a 3D model (not just a 2D illustration or image).
  • Familiarity with Canvas Envision’s evCreate API.
  • Understanding of JavaScript basics.

How It Works (Step by Step)

1. Wait for evCreate to Load

A best practice for starting your code is to always ensure your script waits for the evCreate API to become available:

(async function () {
  async function waitForEvCreateApiReady() {
    const until = (predicateFn, intervalMsec) => new Promise(resolve => {
      const poll = () => predicateFn() ? resolve() : setTimeout(poll, 
intervalMsec);
      poll();
    });
    await until(() => window.evCreate !== undefined, 500);
    await window.evCreate.config.WhenApiReady();
  }

2. Validate 3D Model Selection

To avoid errors and guide the end user of the custom button in Envision, we can check that the user has selected a 3D object:

  async function addModelToNewPages() {
    await waitForEvCreateApiReady();

    const selected = await window.evCreate.object.selection.Get();
    if (!selected.length) return alert("Please select a 3D model.");
    const selectedAttr = await 
window.evCreate.object.GetAttributes(selected[0]);
    if (!selectedAttr || selectedAttr.type !== "model3D") return alert("Selected 
object is not a 3D model.");

    const modelInfo = await 
window.evCreate.object.model.GetModelInfo(selected[0], { recursive: true });

3. Label Subassemblies Using Hierarchical Paths

We recursively walk the model tree and assign labels like “1”, “1.2”, or “2.1.3” to subassemblies to denote parent/child relationships:

    // Label assemblies with hierarchical step numbers
    const labeledNodes = [];
    function labelAssemblies(node, path = [], levelIndex = {}) {
      if (node.isPart === false && typeof node.childrenCount === "number" &&
node.childrenCount > 1) {
        const level = path.length;
        levelIndex[level] = (levelIndex[level] || 0) + 1;
        const newPath = [...path.slice(0, level), levelIndex[level]];
        const label = newPath.join(".");
        const childObjectIds = (node.children || []).map(child => 
child.objectId);
        labeledNodes.push({ label, name: node.name, childrenCount: 
node.childrenCount, childObjectIds });
        Object.keys(levelIndex).map(Number).filter(lvl => lvl > 
level).forEach(lvl => delete levelIndex[lvl]);
        for (const child of node.children || []) labelAssemblies(child, newPath, 
levelIndex);
      } else {
        for (const child of node.children || []) labelAssemblies(child, path, 
levelIndex);
      }
    }

    labelAssemblies(modelInfo);

4. Ordering Assembly Steps Bottom to Top

Now that we have the assembly steps labeled, we can order them based on the numbering to ensure we are building up our assemblies in a logical fashion:

    // Sort deepest assemblies first, based on label depth and value
    labeledNodes.sort((a, b) => {
      const aParts = a.label.split('.').map(Number);
      const bParts = b.label.split('.').map(Number);
      const len = Math.min(aParts.length, bParts.length);
      for (let i = 0; i < len; i++) {
        if (aParts[i] !== bParts[i]) return aParts[i] - bParts[i];
      }
      return bParts.length - aParts.length;
    });

5. Create New Pages and Show/Hide Parts

For each step:

  • Add a page
  • Duplicate the model to new page
  • Hide everything
  • Show only the relevant parts
    const selectedModel = selected[0];

    for (const node of labeledNodes) {
      const newPage = await window.evCreate.document.AddPage();
      await window.evCreate.document.SetCurrentPage(newPage);
      const [firstLayer] = await window.evCreate.page.GetLayers(newPage);
      await window.evCreate.page.SetCurrentLayer(firstLayer);

      const [dupedModel] = await 
window.evCreate.object.Duplicate([selectedModel], 0, 0);
      await window.evCreate.object.SetZOrder(dupedModel, { layer: firstLayer, 
index: 0 });

      await window.evCreate.edit3D.EditModel(dupedModel);
      const fullModelInfo = 
await window.evCreate.object.model.GetModelInfo(dupedModel, { recursive: true });

      const partIdsToHide = [];
      (function collectParts(node) {
        if (node.isPart) partIdsToHide.push({ partId: node.objectId });
        for (const child of node.children || []) collectParts(child);
      })(fullModelInfo);

      await window.evCreate.edit3D.SetPartVisible(partIdsToHide, false);
      const partIdsToShow = node.childObjectIds.map(id => ({ partId: id }));
      await window.evCreate.edit3D.SetPartVisible(partIdsToShow, true);
      await window.evCreate.edit3D.FitCameraTo();
      await window.evCreate.edit3D.Save();
    }
  }

  addModelToNewPages();
})();

Best Practices

  • Checks and prompts like ensuring the selected object is a 3D object can help guide the end user on how to use your custom tool.
  • Having a well-structured EBOM in the 3D model makes scripting easier, reduces manual processing, and leads to higher quality outputs.
  • Envision and the evCreate API require entering 3D Edit mode before making changes to the 3D Model.
  • Modularize your code — helper functions improve reusability and clarity.

What’s Next?

This technique automates a tedious part of 3D documentation. Future enhancements might include:

  • Auto-naming the newly generated pages.
  • Setting master pages for the new generated pages.
  • Using an external MBOM to define the steps rather than EBOM from the model.
  • Scripting Exploded Views of the 3D model.

Start your Canvas Envision free trial today →

Resources

About the author
Trent Wanders
Solutions Engineer
Follow

Continue your reading with these value-packed posts

Back to Blog

Canvas Envision: Connected Knowledge for the New Manufacturing Workforce

Cut errors, reduce costs, improve time to market and retain the best workforce. Talk to us today.

SaaS or self-hosted

Fully customizable

Integrate and embed

CTA Background Image

Get the job done right, every time, with Canvas Envision

© Canvas GFX 2024
35 Village Road. Suite 100.
Middleton, MA, 01949

cookie consent

We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.
Cookies Preferences