Files
cadSet/text-to-cad/packages/cadjs/docs/render-pipeline.md
T
2026-07-22 19:38:36 +08:00

10 KiB

Render Pipeline

cadjs exposes a staged render pipeline for shared viewer, docs, and generated snapshot browser-runtime work:

const source = await loadSource(input, sourceOptions);
const model = buildModel(THREE, source, modelOptions);
const viewport = renderModel(THREE, model, viewportOptions);
const result = await captureModel(viewport, captureOptions);

The stages keep ownership narrow:

  • loadSource owns source and sidecar loading plus file-kind validation.
  • buildModel owns the CAD object graph, records, selection, clipping, materials, topology/display edges, and STEP parameter effects.
  • renderModel owns renderer, scene, camera, lighting, background, floor, framing, resizing, and render loop concerns.
  • captureModel owns deterministic snapshot outputs without filesystem writes.

The CAD skill's Python snapshot CLI remains responsible for job parsing, path resolution, Playwright routing, and writing returned outputs to disk.

Modules

common/source.js

import {
  loadSource,
  stepParameterFrameRuntime
} from "cadjs/common/source.js";

loadSource(input, options) returns a normalized render source:

{
  kind,
  meshData,
  selectorRuntime,
  displayEdgeRuntime,
  stepParameterSource,
  resolved,
  url,
  glbUrl,
  cadPath
}

Accepted input fields:

  • kind: step, stp, glb, stl, 3mf, or inferred from a URL.
  • meshData: already-loaded mesh data. If present, no mesh URL fetch is needed.
  • url: source URL for non-STEP GLB loading.
  • glbUrl or resolved.glbUrl: STEP/STP hidden GLB sidecar URL.
  • cadPath or resolved.inputPath: CAD path used by STEP selectors.
  • selectorRuntime and displayEdgeRuntime: preloaded runtimes when a caller already owns sidecar loading.
  • stepParameters: raw STEP render values or animation envelope.
  • stepParameterUrl or resolved.stepParameterUrl: .step.js/.stp.js parameter sidecar URL.

STEP-only options are rejected for non-STEP sources. The old shared params field is rejected; use stepParameters.

Use stepParameterFrameRuntime(stepParameterSource, frameIndex) to turn the loaded STEP parameter source into the runtime object accepted by buildModel for a still or animation frame.

common/cadScene.js

import {
  buildModel,
  fitCameraToModel
} from "cadjs/common/cadScene.js";

buildModel(THREE, source, settings) returns a model API:

{
  source,
  meshData,
  root,
  modelGroup,
  edgesGroup,
  displayRecords,
  records,
  bounds,
  radius,
  runtime,
  update(nextSettings),
  dispose()
}

source can be a loadSource() result or raw mesh data. The model owns the Three.js object graph and its mutable state.

Common settings:

  • theme: normalized or raw theme settings.
  • displayMode: solid, rendered, transparent, hidden_edges, hidden_lines_removed, unshaded, or wireframe.
  • scale/sceneScale: CAD or robot scene scale.
  • selection: internal selection/filtering state. focus, refs, and hide filter rendered parts before records are built. Viewer-only fields such as selectedPartIds, hiddenPartIds, and showEdges affect visual state.
  • clip: normalized clip-plane settings.
  • stepParameters: STEP parameter runtime object, usually from stepParameterFrameRuntime().
  • parameterSetup: set false to skip sidecar setup lifecycle calls.
  • renderPartsIndividually: build per-part records instead of a whole mesh.
  • edgeRendering: declarative edge rendering configuration.

Declarative screen-space edge rendering:

buildModel(THREE, source, {
  edgeRendering: {
    mode: "screen-space",
    Line2,
    LineGeometry,
    LineSegments2,
    LineSegmentsGeometry,
    LineMaterial,
    wireframeEdgeColor: "#111827"
  }
});

The model keeps screen-space line material bookkeeping internal through runtime.screenSpaceLineMaterials and runtime.syncScreenSpaceLineMaterials(). Callers should not provide callbacks that create edge objects.

model.update(nextSettings) merges mutable settings, rebuilds geometry only when needed, reapplies material/selection/clip/STEP parameter state, and returns the same model API. model.dispose() releases model-owned scene objects and STEP parameter cleanup hooks.

fitCameraToModel(THREE, camera, bounds, options) is the shared orthographic camera framing helper used by interactive rendering.

common/renderModel.js

Use this module for interactive browser canvases, including the docs hero.

import { renderModel } from "cadjs/common/renderModel.js";

renderModel(THREE, model, options) returns an interactive viewport API:

{
  THREE,
  model,
  renderer,
  scene,
  camera,
  ready,
  resize(),
  render(),
  start(),
  stop(),
  capturePng(),
  dispose()
}

Common options:

  • canvas: existing canvas for the renderer.
  • hostElement/container: element used for responsive sizing.
  • renderer: caller-owned renderer. If omitted, one is created.
  • scene and camera: caller-owned scene/camera. If omitted, defaults are created.
  • theme/themeSettings: background and lighting settings.
  • alpha, antialias, powerPreference, preserveDrawingBuffer, logarithmicDepthBuffer, shadows: renderer controls.
  • direction, up, padding, scale/sceneScale: framing controls.
  • pixelRatio, maxPixelRatio: output density controls.
  • autoResize: set false to disable ResizeObserver.
  • autoStart: set true to start an animation loop.
  • autoRender: set false to prevent the initial render.
  • beforeRender({ deltaSeconds, viewport }): per-frame hook for animation.
  • disposeModel: set false when the caller will dispose the model.

dispose() stops animation, disconnects resize observation, removes the model root from the scene, and disposes the created renderer/model unless ownership was explicitly retained by options.

common/renderMeshScene.js

Use this module for deterministic headless snapshot rendering.

import {
  renderJobContext,
  modelOptionsForRenderJob,
  renderModel,
  captureModel,
  renderMeshJob
} from "cadjs/common/renderMeshScene.js";

renderJobContext(meshData, job) normalizes snapshot-owned render policy: appearance, display, scene scale, outputs, STEP topology edge visibility, and warnings.

modelOptionsForRenderJob(context, job) converts that policy into buildModel() settings.

Snapshot renderModel(THREE, model, { job, context }) returns a headless viewport:

{
  THREE,
  model,
  scene,
  renderer,
  orthographicCamera,
  perspectiveCamera,
  context,
  sceneBuildStarted,
  ready,
  dispose()
}

This renderModel is intentionally separate from common/renderModel.js. It uses snapshot sizing, theme environment, floor, orthographic/perspective camera presets, and deterministic renderer settings.

captureModel(viewport, { job }) returns data only:

  • mode: "view": PNG data URLs in outputs.
  • mode: "orbit": the caller composes frames into GIF output.
  • mode: "section": PNG data URLs or SVG text in outputs.
  • mode: "list": part list and bounds.

It does not write files. The CAD skill snapshot CLI writes the returned data to disk. Source checkouts use packages/cadjs; generated snapshot browser assets bundle this entrypoint into skills/cad/scripts/snapshot/runtime.

renderMeshJob(meshData, job) is a compatibility wrapper that builds a context, builds a model, renders/captures it, and disposes owned resources.

STEP Parameters

Shared render APIs use the name stepParameters.

Raw render values can be direct parameter values:

{
  "drive": 180,
  "ringVisible": false
}

Or an animation envelope:

{
  "values": {
    "ringVisible": true
  },
  "animate": {
    "drive": { "from": 0, "to": 1260 }
  },
  "durationSeconds": 6,
  "fps": 18,
  "loop": true
}

common/stepParameters.js validates these values against the loaded STEP parameter sidecar schema, normalizes defaults, and computes per-frame values. loadSource() uses it to populate source.stepParameterSource; callers can then use stepParameterFrameRuntime() when passing parameters into buildModel().

Examples

Interactive viewer/docs usage:

import * as THREE from "three";
import { loadSource, stepParameterFrameRuntime } from "cadjs/common/source.js";
import { buildModel } from "cadjs/common/cadScene.js";
import { renderModel } from "cadjs/common/renderModel.js";

const source = await loadSource({
  kind: "step",
  glbUrl: "/models/.part.step.glb",
  stepParameterUrl: "/models/.part.step.js",
  cadPath: "models/part.step",
  stepParameters: { drive: 180 }
});

const model = buildModel(THREE, source, {
  theme,
  displayMode: "solid",
  stepParameters: stepParameterFrameRuntime(source.stepParameterSource, 0)
});

const viewport = renderModel(THREE, model, {
  canvas,
  hostElement: canvas.parentElement,
  theme,
  autoStart: true
});

Headless snapshot usage:

import * as THREE from "three";
import { loadSource } from "cadjs/common/source.js";
import { buildModel } from "cadjs/common/cadScene.js";
import {
  captureModel,
  modelOptionsForRenderJob,
  renderJobContext,
  renderModel
} from "cadjs/common/renderMeshScene.js";

const source = await loadSource(job);
const context = renderJobContext(source.meshData, job);
const model = buildModel(THREE, source, modelOptionsForRenderJob(context, job));
const viewport = renderModel(THREE, model, { job, context });

try {
  const result = await captureModel(viewport, { job });
  // Write result.outputs in the CAD skill snapshot CLI or another caller-owned layer.
} finally {
  viewport.dispose();
}

Orbit GIF jobs default to 6 fps over 12 seconds for a calmer review spin without increasing default render frame count. Override with orbit.fps and orbit.durationSeconds when a shorter or denser render is needed.

Ownership Rules

  • Do not write files from shared render APIs. Return data to the owning CLI or application layer.
  • Do not expose object-construction callbacks for edges. Use declarative edgeRendering.
  • Keep STEP-only options explicitly STEP-named and reject them for non-STEP sources.
  • Dispose viewports and models that you create.
  • Prefer loadSource -> buildModel -> renderModel -> captureModel for new shared render code instead of loading assets or constructing render scenes inline.