Engine System
Engine System
Engine is the core static class of WebCAD, serving as the global entry point for the entire system and providing access to core functionality such as documents, canvas, undo management, and coordinate transformation.
Overview
System Variables
Color and Style Variables
| Variable Name | Type | Default | Description |
|---|---|---|---|
CECOLOR | number | 256 | Current entity color (256=ByLayer, 0=ByBlock) |
CEALPHA | number | 1 | Current entity transparency (0-1) |
CELTYPE | string | "ByLayer" | Current entity line type |
CELTSCALE | number | 1 | Current entity line type scale |
import { Engine } from 'vjcad';
// Color index reference:
// 1=red, 2=yellow, 3=green, 4=cyan, 5=blue, 6=magenta, 7=white
// 256=ByLayer, 0=ByBlock
// Set current drawing color to red
Engine.CECOLOR = 1;
// Set line type to dashed
Engine.CELTYPE = "HIDDEN";
// Set line type scale
Engine.CELTSCALE = 2.0;Snap and Drawing Mode Variables
| Variable Name | Type | Default | Description |
|---|---|---|---|
OSMODE | number | 255 | Object snap mode (bitmask) |
ORTHOMODE | number | 0 | Ortho mode (0=off, 1=on) |
POLARMODE | number | 0 | Polar mode (0=off, 1=on) |
POLARANG | number | π/12 | Polar angle increment |
GRIDMODE | number | 0 | Grid display mode |
Object Snap Mode Bitmask
// OSMODE bitmask definitions
const OSNAP = {
ENDPOINT: 1, // Endpoint
MIDPOINT: 2, // Midpoint
CENTER: 4, // Center
NODE: 8, // Node
QUADRANT: 16, // Quadrant
INTERSECTION: 32, // Intersection
INSERT: 64, // Insert
PERPENDICULAR: 128, // Perpendicular
TANGENT: 256, // Tangent
NEAREST: 512, // Nearest
APPARENT: 1024, // Apparent intersection
EXTENSION: 2048, // Extension
PARALLEL: 4096, // Parallel
};
// Enable all snaps
Engine.OSMODE = 255;
// Enable only endpoint and midpoint snaps
Engine.OSMODE = OSNAP.ENDPOINT | OSNAP.MIDPOINT;
// Disable all snaps
Engine.OSMODE = 0;
// Check if endpoint snap is enabled
const hasEndpoint = (Engine.OSMODE & OSNAP.ENDPOINT) !== 0;Ortho and Polar Mode
// Enable ortho mode (restricts cursor movement to horizontal or vertical)
Engine.ORTHOMODE = 1;
// Enable polar mode
Engine.POLARMODE = 1;
// Set polar angle increment (15 degrees)
Engine.POLARANG = Math.PI / 12;
// Note: Ortho and polar modes are mutually exclusive; enabling one automatically disables the otherDisplay and Interface Variables
| Variable Name | Type | Default | Description |
|---|---|---|---|
APERTURE | number | 5 | Snap aperture size (pixels) |
PICKBOX | number | 5 | Pick box size (pixels) |
GRIPSIZE | number | 4 | Grip size (pixels) |
CURSORSIZE | number | 64 | Crosshair cursor size (0=full screen) |
ZOOMFACTOR | number | 75 | Zoom factor |
// Adjust snap aperture size
Engine.APERTURE = 10;
// Adjust pick box size
Engine.PICKBOX = 8;
// Set crosshair cursor to full screen
Engine.CURSORSIZE = 0;
// Set zoom factor (affects wheel zoom speed)
Engine.ZOOMFACTOR = 100;Edit-Related Variables
| Variable Name | Type | Default | Description |
|---|---|---|---|
FILLETRAD | number | 0 | Fillet radius |
OFFSETDIST | number | 10 | Offset distance |
PLINEWID | number | 0 | Polyline width |
Theme and Highlight Variables
| Variable Name | Type | Default | Description |
|---|---|---|---|
THEME_MODE | number | 0 | Theme mode (0=dark, 1=light) |
HLCOLOR | number | 172 | Highlight color index |
HLALPHA | number | 0.5 | Highlight transparency |
// Switch to light theme
Engine.THEME_MODE = 1;
// Switch to dark theme
Engine.THEME_MODE = 0;
// Check current theme
if (Engine.isDarkTheme()) {
console.log('Current theme is dark');
}
// Get current theme background color
const bgColor = Engine.getThemeBackground();Core Component Access
Document and Space
import { Engine, CadDocument, BlockDefinition } from 'vjcad';
// Get current document
const doc: CadDocument = Engine.currentDoc;
console.log('Document name:', doc.name);
console.log('Document path:', doc.path);
// Get current space (model space or layout space)
const space: BlockDefinition = Engine.currentSpace;
console.log('Entity count in current space:', space.items.length);
// Access layer collection
const layers = doc.layers;
console.log('Layer count:', layers.items.length);
// Access block collection
const blocks = doc.blocks;Canvas Controller
import { Engine, CanvasController } from 'vjcad';
// Get canvas controller (low-level access)
const canvas: CanvasController = Engine.pcanvas;
// Common canvas operations are encapsulated in Engine static methods; use them directly:
Engine.zoomExtents(); // Zoom to extents
Engine.regen(); // Regenerate
Engine.redraw(); // Redraw
Engine.setCenter(point); // Set view center
Engine.setBgc(0); // Set background color
Engine.resize(800, 600); // Resize
Engine.clearGrip(); // Clear grips
Engine.clearOsnap(); // Clear snap markers
Engine.clearHighLight(); // Clear highlight
Engine.getScreenBoundsWcs(); // Get screen bounds
Engine.getScreenEntities(); // Get entities on screen
// Get view zoom level
const zoom = Engine.currentSpace.zoom;Undo Manager
import { Engine, UndoManager } from 'vjcad';
// Get undo manager
const undoMgr: UndoManager = Engine.undoManager;
// Perform undo
undoMgr.undo();
// Perform redo
undoMgr.redo();
// Check if undo is available
if (undoMgr.canUndo()) {
undoMgr.undo();
}Coordinate Transform
import { Engine, CoordinateTransform, Point2D } from 'vjcad';
// Get coordinate transform
const trans: CoordinateTransform = Engine.trans;
// World coordinates to user coordinates
const ucsPoint = trans.WcsToUcs(new Point2D(100, 100));
// World coordinates to display coordinates
const dcsPoint = trans.WcsToDcs(new Point2D(100, 100));Other Components
| Property | Type | Description |
|---|---|---|
Engine.editor | Editor | Editor controller, handles user input |
Engine.commandLine | CommandLine | Command line component |
Engine.view | MainView | Main view component |
Engine.eventManager | CadEventManager | Event manager, subscribe/publish CAD events |
Engine.reactorManager | EntityReactorManager | Entity reactor manager |
Engine.sidePalettes | SidePalettes | Side palette container |
Engine.propertyPalette | PropertyPalette | Property palette |
Engine.service | Service | Backend service interface |
Entity Operations
Adding Entities
import { Engine, LineEnt, CircleEnt, Point2D } from 'vjcad';
// Create entities
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
const circle = new CircleEnt(new Point2D(50, 50), 30);
// Apply default properties (layer, color, etc.)
line.setDefaults();
circle.setDefaults();
// Method 1: Use Engine.addEntities (recommended)
// Automatically records undo and refreshes selectable entity list
Engine.addEntities([line, circle]);
// Method 2: With options
Engine.addEntities(line, { recordUndo: false });
// Method 3: Manual operation (not recommended, requires manual refresh of selectable entity list)
Engine.pcanvas.addEntity(line);
Engine.undoManager.added_undoMark([line]);
Engine.editor?.refreshRemain(); // Ensure new entities can be selectedEntity Selectability
Engine.addEntities() automatically calls Engine.editor.refreshRemain() to refresh the selectable entity list, ensuring newly added entities can be selected immediately by mouse click. If you add entities by other means (e.g., directly calling pcanvas.addEntity()), you must manually call Engine.editor.refreshRemain() for the new entities to be selectable.
Erasing Entities
import { Engine, EntityBase } from 'vjcad';
// Erase single entity (automatically refreshes selectable entity list)
Engine.eraseEntities(entity);
// Erase multiple entities
Engine.eraseEntities([entity1, entity2]);
// Without recording undo
Engine.eraseEntities(entity, { recordUndo: false });Tips
Engine.eraseEntities() automatically calls Engine.editor.refreshRemain() to refresh the selectable entity list, ensuring erased entities are removed from the selectable list.
Getting Entities
import { Engine, EntityBase } from 'vjcad';
// Get all entities
const allEntities: EntityBase[] = Engine.getEntities();
// Get entities by type
const lines = Engine.getEntitiesByType('LINE');
const circles = Engine.getEntitiesByType('CIRCLE');
const plines = Engine.getEntitiesByType('PLINE');
// Get entities with filter function
const redEntities = Engine.getEntities(ent => ent.color === 1);
const layer1Entities = Engine.getEntities(ent => ent.layer === 'Layer1');
// Get currently selected entities
const selected = Engine.getSelectedEntities();Modifying Entity Properties
import { Engine, EntityBase } from 'vjcad';
// Batch set color (automatically records undo)
Engine.setEntityColor(entities, 3); // Green
// Batch set layer
Engine.setEntityLayer(entities, 'Dimension');
// Batch set line type
Engine.setEntityLineType(entities, 'HIDDEN');
// Batch set line type scale
Engine.setEntityLineTypeScale(entities, 2.0);
// Batch set transparency (0-100)
Engine.setEntityTransparency(entities, 50);
// When modifying entities manually, record undo first
Engine.markModified(entity);
entity.color = 1; // setter automatically calls setModified()Layer Operations
import { Engine, Layer } from 'vjcad';
// Get all layers
const layers: Layer[] = Engine.getLayers();
// Get layer by name
const layer = Engine.getLayerByName('Dimension');
// Get current layer name
const currentLayer: string = Engine.getCurrentLayer();
// Set current layer
Engine.setCurrentLayer('Layer1');
// Create new layer
const newLayer = Engine.createLayer('My Layer', {
color: 3, // Green
lineType: 'HIDDEN', // Dashed
layerOn: true // On
});
// Create layer with auto-generated name
const autoLayer = Engine.createLayer(); // Auto-named as "Layer1", "Layer2", etc.User Input
Getting a Point
import { Engine, PointInputOptions, InputStatusEnum, Point2D } from 'vjcad';
// Basic usage
const options = new PointInputOptions("Specify point:");
const result = await Engine.getPoint(options);
if (result.status === InputStatusEnum.OK) {
const point = result.value as Point2D;
console.log(`Point coordinates: (${point.x}, ${point.y})`);
}
// With base point rubber band
const options2 = new PointInputOptions("Specify next point:");
options2.basePoint = new Point2D(0, 0);
options2.useBasePoint = true;
// With keywords
const options3 = new PointInputOptions("Specify point or [Close(C)/Undo(U)]:");
options3.keywords = ["C", "U"];
const result3 = await Engine.getPoint(options3);
if (result3.status === InputStatusEnum.KEYWORD) {
console.log('User entered keyword:', result3.stringResult);
}Getting Selection Set
import {
Engine,
SelectionSetInputOptions,
InputStatusEnum,
EntityBase
} from 'vjcad';
// Get selection set
const options = new SelectionSetInputOptions("Select objects:");
const result = await Engine.getSelections(options);
if (result.status === InputStatusEnum.OK) {
const entities = result.value as EntityBase[];
console.log(`Selected ${entities.length} entities`);
for (const entity of entities) {
console.log(`- ${entity.type}: ${entity.id}`);
}
}Getting Numeric Values
import { Engine, RealInputOptions, IntegerInputOptions, InputStatusEnum } from 'vjcad';
// Get real number
const realOptions = new RealInputOptions("Enter radius:");
realOptions.defaultValue = 10;
const realResult = await Engine.getReal(realOptions);
if (realResult.status === InputStatusEnum.OK) {
const radius = realResult.value as number;
}
// Get integer
const intOptions = new IntegerInputOptions("Enter number of sides:");
intOptions.defaultValue = 6;
const intResult = await Engine.getInteger(intOptions);
if (intResult.status === InputStatusEnum.OK) {
const sides = intResult.value as number;
}Getting String
import { Engine, StringInputOptions, InputStatusEnum } from 'vjcad';
const options = new StringInputOptions("Enter text content:");
options.defaultValue = "Default text";
const result = await Engine.getString(options);
if (result.status === InputStatusEnum.OK) {
const text = result.value as string;
}Getting Corner (Window Selection)
import { Engine, CornerInputOptions, InputStatusEnum, Point2D } from 'vjcad';
const options = new CornerInputOptions("Specify opposite corner:");
options.basePoint = new Point2D(0, 0);
const result = await Engine.getCorner(options);
if (result.status === InputStatusEnum.OK) {
const corner = result.value as Point2D;
// Forms rectangular region from (0,0) to corner
}View Control
Zoom
import { Engine, Point2D } from 'vjcad';
// Zoom to extents (zoom to all entities)
Engine.zoomExtents();
// Zoom to extents (specified entities)
Engine.zoomExtents([entity1, entity2]);
// Zoom to extents with options
Engine.zoomExtents(entities, {
padding: { top: 20, bottom: 20, left: 20, right: 20 },
autoSetRenderOrigin: true // Auto set render origin
});
// Zoom to specified entities (same as zoomExtents)
Engine.zoomToEntities([entity1, entity2]);
// Zoom to fit specified entity bounds
Engine.zoomToFit(entities, {
top: 20, bottom: 20, left: 20, right: 20
});
// Get entity bounding box
const bounds = Engine.getBoundsByEntities(entities, "WCS");
// Get screen bounds (world coordinates)
const screenBounds = Engine.getScreenBoundsWcs();
// Get entities on screen
const screenEntities = Engine.getScreenEntities();View Center
import { Engine, Point2D } from 'vjcad';
// Set view center to specified point
Engine.setCenter(new Point2D(100, 200));
// Set view center without redraw
Engine.setCenter(new Point2D(100, 200), false);
// Set view center with padding
Engine.setCenter(new Point2D(100, 200), true, {
top: 50, bottom: 0, left: 0, right: 0
});Redraw and Regen
import { Engine } from 'vjcad';
// render: Lowest-level rendering, only performs GPU drawing
Engine.render();
// redraw: Updates view transform (position/zoom/rotation), then calls render
Engine.redraw();
// regen: Partial update, only re-renders entities that have been setModified (fast)
Engine.regen();
// regen(true): Full redraw, clears and redraws all graphics (slow)
Engine.regen(true);Method Hierarchy
regen(true) → Fully clear and redraw all graphics
↓
regen() → Only redraw modified entities (dirty bucket) → calls regenPartial()
↓
redraw() → Update view transform (position/zoom/rotation) → calls render()
↓
render() → Calls PIXI.renderer.render() for GPU drawingCorrect Usage
After modifying entity properties, the setter automatically calls setModified() to mark as modified. Just call regen() for partial refresh:
circle.radius = 45; // setter automatically calls setModified()
Engine.regen(); // Partial update (only redraws dirty entities)Background and Canvas
import { Engine } from 'vjcad';
// Set background color (grayscale: 0=black, 255=white)
Engine.setBgc(0); // Black background
Engine.setBgc(255); // White background
// Resize canvas
Engine.resize(800, 600);Clear Operations
import { Engine } from 'vjcad';
// Clear grip display
Engine.clearGrip();
// Clear object snap markers
Engine.clearOsnap();
// Clear highlight
Engine.clearHighLight();Preview and Highlight
import { Engine, LineEnt, Point2D } from 'vjcad';
// Draw preview entity
const previewLine = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
Engine.drawPreviewEntity(previewLine);
// Batch preview
Engine.drawPreviewEntities([entity1, entity2]);
// Set preview position
Engine.setPreviewPosition(new Point2D(50, 50));
// Set preview rotation
Engine.setPreviewRotation(Math.PI / 4);
// Clear preview
Engine.clearPreview();
// Highlight entities
Engine.highLightEntities([entity1, entity2]);
// Clear highlight
Engine.clearHighLight();Coordinate Transformation
WebCAD uses three coordinate systems:
- WCS (World Coordinate System): World coordinate system, absolute reference
- UCS (User Coordinate System): User coordinate system, customizable work plane
- DCS (Display Coordinate System): Display coordinate system, screen pixel coordinates
import { Engine, Point2D } from 'vjcad';
// Canvas coordinates to world coordinates (commonly used for mouse events)
const wcsPoint = Engine.canvasToWcs(new Point2D(event.clientX, event.clientY));
// World coordinates to user coordinates
const ucsPoint = Engine.wcsToUcs(wcsPoint);
// User coordinates to world coordinates
const wcsPoint2 = Engine.ucsToWcs(ucsPoint);
// World coordinates to display coordinates
const dcsPoint = Engine.wcsToDcs(wcsPoint);
// Use trans object for more conversions
const trans = Engine.trans;
const ucsPoint2 = trans.WcsToUcs(wcsPoint);
const dcsPoint2 = trans.WcsToDcs(wcsPoint);
const wcsFromDcs = trans.DcsToWcs(dcsPoint);Undo Recording
Basic Undo Operations
import { Engine } from 'vjcad';
// Record undo for added entities
Engine.addedUndoMark([entity1, entity2]);
// Record undo for erased entities
Engine.erasedUndoMark([entity1]);
// Record undo for move operation
Engine.movedUndoMark(entities, fromPoint, toPoint);
// Record undo for rotate operation
Engine.rotateUndoMark(entities, basePoint, angle);
// Record undo for scale operation
Engine.scaledUndoMark(entities, basePoint, scale);
// Record undo for mirror operation
Engine.mirroredUndoMark(entities, point1, point2);Undo Record Group
import { Engine } from 'vjcad';
// Start undo record group (merge multiple operations into one undo step)
Engine.startUndoRecord();
try {
// Execute multiple operations
Engine.addEntities(entity1);
Engine.setEntityColor([entity1], 1);
Engine.setEntityLayer([entity1], 'Layer1');
} finally {
// End undo record group
Engine.endUndoRecord();
}
// Supports nested calls
Engine.startUndoRecord();
Engine.startUndoRecord(); // Nested
Engine.endUndoRecord();
Engine.endUndoRecord(); // Only the outermost creates the actual undo groupMessage Output
import { Engine } from 'vjcad';
// Output message to command line
Engine.writeMessage("Operation complete");
Engine.writeMessage(`Selected ${count} entities`);Selection Set Operations
import { Engine, EntityBase } from 'vjcad';
// Set selection set
Engine.ssSetFirst([entity1, entity2]);
// Clear selection set
Engine.ssSetFirst([]);
// Get current selection set
const selected: EntityBase[] = Engine.ssGetFirst();Line Segment Detection
The detectLineAtPoint() method detects line segments near a specified point, supporting line entities (LINE) and line segments within polyline entities (PLINE). This feature is commonly used for associative dimensions, smart snap, and similar scenarios.
LineSegmentInfo Interface
interface LineSegmentInfo {
/** Start point of the line segment */
startPoint: Point2D;
/** End point of the line segment */
endPoint: Point2D;
/** Original entity (LINE or PLINE) */
entity: EntityBase;
/** Segment index (for segments in a polyline, indicates position within the polyline) */
segmentIndex: number;
/** Type of pick point on the line segment */
pointType: 'start' | 'end' | 'midpoint' | 'nearest';
/** Distance from pick point to the line segment */
distance: number;
/**
* Linear parameter t, indicating relative position of pick point on the segment (0-1)
* - t = 0 indicates start point
* - t = 1 indicates end point
* - t = 0.5 indicates midpoint
*/
parameterT: number;
}Basic Usage
import { Engine, Point2D, LineSegmentInfo } from 'vjcad';
// Detect line segments near a point
const lineInfo: LineSegmentInfo | null = Engine.detectLineAtPoint(worldPoint);
if (lineInfo) {
console.log('Line segment detected');
console.log('Start point:', lineInfo.startPoint);
console.log('End point:', lineInfo.endPoint);
console.log('Original entity:', lineInfo.entity.type); // "LINE" or "PLINE"
console.log('Segment index:', lineInfo.segmentIndex); // Always 0 for LINE
console.log('Point type:', lineInfo.pointType); // 'start' | 'end' | 'midpoint' | 'nearest'
console.log('Distance:', lineInfo.distance);
console.log('Parameter t:', lineInfo.parameterT); // 0=start, 1=end, 0.5=midpoint
}
// Specify custom tolerance (in world coordinate units)
const lineInfo2 = Engine.detectLineAtPoint(worldPoint, 20);pointType Type Reference
| Type | Description |
|---|---|
'start' | Pick point is near the line segment start point |
'end' | Pick point is near the line segment end point |
'midpoint' | Pick point is near the line segment midpoint |
'nearest' | Pick point is the nearest point on the segment (not a special point) |
Application Example: Associative Dimension
import { Engine, Point2D, LineSegmentInfo, AlignedDimensionEnt } from 'vjcad';
async function createAssociatedDimension(pt1: Point2D, pt2: Point2D) {
// Detect line segment near first point
const firstLine = Engine.detectLineAtPoint(pt1);
// Detect line segment near second point
const secondLine = Engine.detectLineAtPoint(pt2);
// Create dimension
const dim = new AlignedDimensionEnt();
dim.setDefaults();
// Set dimension points
dim.firstPoint = pt1;
dim.secondPoint = pt2;
// If line segment detected, establish association
if (firstLine) {
// Highlight associated entity
Engine.highLightEntities([firstLine.entity]);
// Use pointType to establish association ('nearest' is not a valid association type)
if (firstLine.pointType !== 'nearest') {
console.log(`First point associated to line segment ${firstLine.pointType}`);
}
}
if (secondLine) {
if (secondLine.pointType !== 'nearest') {
console.log(`Second point associated to line segment ${secondLine.pointType}`);
}
}
Engine.addEntities(dim);
Engine.clearHighLight();
}Application Example: Smart Leader
import { Engine, Point2D, LineSegmentInfo } from 'vjcad';
async function createSmartLeader(arrowPoint: Point2D) {
// Detect line segment near arrow position
const snapInfo = Engine.detectLineAtPoint(arrowPoint);
if (snapInfo) {
// Highlight target entity
Engine.pcanvas.highLightEntities([snapInfo.entity]);
// Adjust arrow position to precise location based on pointType
let preciseArrowPoint: Point2D;
switch (snapInfo.pointType) {
case 'start':
preciseArrowPoint = snapInfo.startPoint.clone();
break;
case 'end':
preciseArrowPoint = snapInfo.endPoint.clone();
break;
case 'midpoint':
preciseArrowPoint = snapInfo.startPoint.clone();
preciseArrowPoint.x = (snapInfo.startPoint.x + snapInfo.endPoint.x) / 2;
preciseArrowPoint.y = (snapInfo.startPoint.y + snapInfo.endPoint.y) / 2;
break;
default:
// Use parameterT to calculate nearest point
preciseArrowPoint = new Point2D(
snapInfo.startPoint.x + snapInfo.parameterT * (snapInfo.endPoint.x - snapInfo.startPoint.x),
snapInfo.startPoint.y + snapInfo.parameterT * (snapInfo.endPoint.y - snapInfo.startPoint.y)
);
}
return preciseArrowPoint;
}
return arrowPoint;
}Detection Scope
- Default tolerance is
Engine.APERTURE / Engine.currentSpace.zoom, i.e., automatically adjusted based on current zoom level - Supports detection of LINE (line) entities
- Supports detection of line segments within PLINE (polyline) entities (arc segments not yet supported)
- Uses spatial index to optimize search performance, suitable for scenarios with many entities
Global Configuration
import { Engine } from 'vjcad';
// Show file loading progress bar
Engine.SHOWLOADPROGRESS = true;
// Use WASM for hatch pattern generation
Engine.USE_WASM_HATCH = true;
// Performance priority mode (disables performance-intensive features)
Engine.performancePriorityMode = true;
// Lazy load closed layers
Engine.LAZYLOADCLOSEDLAYERS = true;
// Enable geometry cache
Engine.ENABLE_GEOMETRY_CACHE = true;
// Dimension entity open mode
Engine.OPEN_DIMENSION_AS_GROUP = false; // false=editableNext Steps
- Document Model - Learn about CadDocument
- Coordinate System - Learn about coordinate transformation
- Undo/Redo - Learn about undo mechanism
- Entity Base Class - Learn about EntityBase