Entity Base Class
Entity Base Class
EntityBase is the base class for all CAD entities, defining common properties and basic behaviors. All concrete graphic entities (such as lines, circles, arcs, etc.) inherit from this base class.
Overview
Entity Lifecycle
Basic Properties
Identification Properties
| Property | Type | Description |
|---|---|---|
type | string | Entity type identifier (e.g., "LINE", "CIRCLE") |
entityType | string | Entity type (alias for type) |
id | number | Entity index ID (used internally by WebCAD) |
objectId | string | DWG object handle (preserved when imported from DWG) |
handle | string | DWG handle (alias for objectId) |
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
console.log('Entity type:', line.type); // "LINE"
console.log('Entity ID:', line.id); // 0 (assigned after adding to document)
console.log('DWG handle:', line.objectId); // "" (empty for newly created entities)State Properties
| Property | Type | Description |
|---|---|---|
isAlive | boolean | Whether the entity is alive (false means deleted) |
isDirty | boolean | Whether it needs redrawing (dirty flag) |
block | BlockDefinition | Owning block (model space or block definition) |
doc | CadDocument | Owning document |
import { Engine, EntityBase } from 'vjcad';
const entity: EntityBase = /* ... */;
// Check entity state
console.log('Is alive:', entity.isAlive);
console.log('Needs redraw:', entity.isDirty);
console.log('Owning document:', entity.doc?.name);
console.log('Owning block:', entity.block?.name);Appearance Properties
Color
WebCAD supports two color representation methods: ACI (AutoCAD Color Index) and RGB True Color.
Color Storage Format
| Value Range | Meaning |
|---|---|
| 0 | ByBlock |
| 1-255 | CAD standard color index (ACI) |
| 256 | ByLayer |
| >= 16777216 (0x1000000) | RGB true color |
RGB colors are distinguished from index colors using the marker bit 0x1000000:
- RGB color value =
0x1000000 + 0xRRGGBB - For example: Red =
0x1000000 + 0xFF0000 = 0x1FF0000 = 33488896
Using Index Colors
Note the Calling Order
You must call setDefaults() first, then set the color. Otherwise, setDefaults() will overwrite your color value with the system default color.
// Correct order
line.setDefaults(); // Apply default properties first
line.color = 1; // Then set the color
// Wrong order (color will be overwritten)
line.color = 1; // Set the color
line.setDefaults(); // Default properties overwrite the color!import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults(); // Apply default properties first
// Then set the color index
line.color = 1; // Red
line.color = 2; // Yellow
line.color = 3; // Green
line.color = 256; // ByLayer
line.color = 0; // ByBlockColor Index Table
| Index | Color | Description |
|---|---|---|
| 0 | ByBlock | Inherits the block's color |
| 1 | Red | Red |
| 2 | Yellow | Yellow |
| 3 | Green | Green |
| 4 | Cyan | Cyan |
| 5 | Blue | Blue |
| 6 | Magenta | Magenta |
| 7 | White/Black | White (displayed as white on dark backgrounds) |
| 8-255 | Extended colors | AutoCAD extended colors |
| 256 | ByLayer | Inherits the layer's color |
Using RGB True Color
WebCAD provides the ColorConverter utility class to simplify RGB color operations:
import { LineEnt, Point2D, ColorConverter } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults(); // Apply default properties first
// Method 1: Direct calculation (RGB marker bit + RGB value)
line.color = 0x1000000 + 0xFF8000; // Orange
// Method 2: Using createRgbColorIndex (pass RGB integer value)
line.color = ColorConverter.createRgbColorIndex(0xFF8000); // Orange
// Method 3: Using createRgbColorIndexFromComponents (pass R, G, B components)
line.color = ColorConverter.createRgbColorIndexFromComponents(255, 128, 0); // Orange
// Method 4: Using createRgbColorIndexFromHexStr (pass hex string)
line.color = ColorConverter.createRgbColorIndexFromHexStr("#FF8000"); // Orange
line.color = ColorConverter.createRgbColorIndexFromHexStr("FF8000"); // Without # also worksColorConverter Utility Class
| Method | Description | Example |
|---|---|---|
isRgbColor(color) | Check if it's an RGB color | ColorConverter.isRgbColor(0x1FF0000) → true |
extractRgbValue(color) | Extract RGB value from color | ColorConverter.extractRgbValue(0x1FF0000) → 0xFF0000 |
createRgbColorIndex(rgb) | Create color from RGB integer | ColorConverter.createRgbColorIndex(0xFF0000) → 0x1FF0000 |
createRgbColorIndexFromComponents(r, g, b) | Create color from RGB components | ColorConverter.createRgbColorIndexFromComponents(255, 0, 0) |
createRgbColorIndexFromHexStr(hex) | Create color from hex string | ColorConverter.createRgbColorIndexFromHexStr("#FF0000") |
getRgbComponents(color) | Get RGB components | ColorConverter.getRgbComponents(color) → {r, g, b} |
GetColorHexStr(color) | Get hex string | ColorConverter.GetColorHexStr(color) → "FF0000" |
GetColorCssStr(color) | Get CSS color format | ColorConverter.GetColorCssStr(color) → "#FF0000" |
Color Name Conversion
Use colorNameToIndex and colorIndexToName to convert between color names and indices:
import { colorNameToIndex, colorIndexToName } from 'vjcad';
// Color name → index
colorNameToIndex("red"); // 1
colorNameToIndex("ByLayer"); // 256
colorNameToIndex("#FF8000"); // 0x1FF8000 (RGB orange)
// Index → color name
colorIndexToName(1); // "Red"
colorIndexToName(256); // "ByLayer"
colorIndexToName(0x1FF8000); // "#FF8000"Layer
import { Engine, LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Set by layer name
line.layer = 'Annotation';
// Get layer name
console.log('Layer:', line.layer);
// Set by layer ID (internal use)
line.layerId = '1'; // Layer ID
// Get layer ID
console.log('Layer ID:', line.layerId);Line Type
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Set line type
line.lineType = 'CONTINUOUS'; // Solid line
line.lineType = 'HIDDEN'; // Dashed line
line.lineType = 'CENTER'; // Center line
line.lineType = 'PHANTOM'; // Phantom line
line.lineType = 'ByLayer'; // ByLayer
// Set line type scale
line.lineTypeScale = 2.0; // Increase dash spacingBuilt-in Line Types
| Line Type | Aliases | Description |
|---|---|---|
CONTINUOUS | - | Continuous solid line |
HIDDEN | DASHED, DASHED1, DASHED2 | Dashed line |
CENTER | CENTER1, CENTER2 | Center line |
PHANTOM | PHANTOM1, PHANTOM2 | Phantom line |
ByLayer | - | Inherits layer line type |
ByBlock | - | Inherits block line type |
Line Weight
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Set line weight (unit: 0.01mm)
line.lineWeight = 25; // 0.25mm
line.lineWeight = 50; // 0.50mm
line.lineWeight = -1; // ByLayer
line.lineWeight = -2; // ByBlockTransparency
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Set transparency (0-100)
line.transpMgr.setTp100(50); // 50% transparent
// ByLayer transparency
line.transpMgr.setValue(-1); // ByLayer
// Get transparency
const alpha = line.transpMgr.alpha; // Range 0-1Visibility
Entity-level show/hide control, corresponding to DXF Group Code 60. Defaults to true (visible).
| Value | Meaning |
|---|---|
true | Visible (default) |
false | Invisible (DXF 60 = 1) |
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults();
// Hide entity
line.visible = false;
// Show entity
line.visible = true;
// Read visibility
console.log(line.visible); // trueBehavior of Invisible Entities
- Rendering: Not drawn to canvas
- Pick/Window selection: Cannot be selected
- Object snap: Does not provide snap points
- Ctrl+A select all: Can be selected (consistent with AutoCAD)
- Grips: Grips are not displayed after select-all
- Zoom extents: Does not affect zoomExtents calculation
- Data preservation: Saved during serialization, DXF 60 flag correctly restored when exporting to DWG
Difference from Layer Visibility
- Layer visibility (
layer.layerOn): Controls the display of all entities on the entire layer - Entity visibility (
entity.visible): Controls only a single entity's display; other entities on the same layer are not affected
They work independently: if an entity has visible = true but its layer is turned off, the entity will not be displayed.
Extended Data (XData)
Extended Data (XData) allows attaching custom data to entities. This data is saved and loaded along with the DWG file. It is suitable for storing business properties, sensor data, device information, etc.
Data Format
Extended data is stored as key-value pairs, internally serialized as JSON:
// Internal storage format (follows DXF standard)
{
"1001": "APP_NAME", // Application name (identifies data source)
"1000": "{\"key\":\"value\"}" // JSON-serialized data string
}DWG File Limitations
- Single string: Serialized data cannot exceed 255 bytes
- Total xdata per entity: Cannot exceed 16KB
It is recommended to store only essential data and avoid storing large amounts of text or complex structures.
Copying Entities Preserves Extended Data
When using the clone() method or copy commands to copy entities, extended data is fully copied to the new entity without manual handling.
Setting Extended Data
Use the setExtData(appName, data) method:
import { LineEnt, Engine } from 'vjcad';
// Create an entity
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
// Set extended data
line.setExtData("SENSOR_DATA", {
name: "Temperature Sensor",
value: 25.5,
unit: "℃",
isActive: true,
lastUpdate: "2026-01-29"
});
Engine.addEntities(line);| Parameter | Type | Description |
|---|---|---|
appName | string | Application name, used to identify data source in DWG |
data | object | Data object to store (will be serialized to JSON) |
Reading Extended Data
// Check if extended data exists
if (line.hasExtData()) {
// Get application name
console.log('AppName:', line.extDataAppName); // "SENSOR_DATA"
// Get extended data (JSON automatically parsed)
const data = line.extData;
console.log('Sensor name:', data.name); // "Temperature Sensor"
console.log('Sensor value:', data.value); // 25.5
console.log('Is active:', data.isActive); // true
}Modifying Extended Data
// Method 1: Full replacement
line.setExtData("SENSOR_DATA", {
name: "Temperature Sensor",
value: 30.2, // Update temperature value
unit: "℃",
isActive: true,
alarm: true // Add alarm field
});
// Method 2: Modify based on existing data
const currentData = line.extData;
if (currentData) {
currentData.value = 30.2; // Update temperature value
currentData.lastCheck = "2026-01-28"; // Add check date
line.setExtData("SENSOR_DATA", currentData);
}Clearing Extended Data
line.clearExtData();
console.log(line.hasExtData()); // falseQuerying Entities with Extended Data
// Get all entities
const allEntities = Engine.getEntities();
// Filter entities with extended data
const entitiesWithXData = allEntities.filter(ent => ent.hasExtData?.());
// Categorize by AppName
const byAppName = {};
entitiesWithXData.forEach(ent => {
const appName = ent.extDataAppName;
if (!byAppName[appName]) byAppName[appName] = [];
byAppName[appName].push(ent);
});Getting Raw xdata (Advanced Usage)
// Get raw format xdata
const rawXData = line.xdataRaw;
// { "1001": "SENSOR_DATA", "1000": "{\"name\":\"Temperature Sensor\",...}" }
// Set raw xdata directly (usually not necessary)
line.xdataRaw = {
"1001": "MY_APP",
"1000": JSON.stringify({ custom: "data" })
};Extended Data API Summary
| Method/Property | Type | Description |
|---|---|---|
setExtData(appName, data) | method | Set extended data |
extData | getter | Get parsed extended data object |
extDataAppName | getter | Get application name |
hasExtData() | method | Check if extended data exists |
clearExtData() | method | Clear extended data |
xdataRaw | getter/setter | Get/set raw xdata object |
Property Panel Editing
After selecting an entity, you can view and edit extended data in the right-side property panel:
- The property panel displays an "Extended Data" row showing the current AppName and field count
- Click the button on the right to open the detailed editing dialog
- Supports adding, editing, and deleting key-value pairs
- Automatically recognizes data types (string, number, boolean)
Complete Example
import { LineEnt, CircleEnt, Engine, message } from 'vjcad';
// Create a line with sensor data
const sensorLine = new LineEnt([0, 0], [100, 0]);
sensorLine.setDefaults();
sensorLine.color = 1;
sensorLine.setExtData("SENSOR_DATA", {
name: "Temperature Sensor",
value: 25.5,
unit: "℃",
isActive: true
});
// Create a circle with device information
const deviceCircle = new CircleEnt([150, 0], 20);
deviceCircle.setDefaults();
deviceCircle.color = 3;
deviceCircle.setExtData("DEVICE_INFO", {
deviceId: "DEV-001",
type: "Valve",
status: "open",
pressure: 1.2
});
// Add to canvas
Engine.addEntities([sensorLine, deviceCircle]);
// Read and display extended data
const sensorData = sensorLine.extData;
message.info(`Sensor: ${sensorData.name}, Value: ${sensorData.value}${sensorData.unit}`);
const deviceData = deviceCircle.extData;
message.info(`Device: ${deviceData.deviceId}, Status: ${deviceData.status}`);
// Update data
sensorData.value = 30.2;
sensorData.alarm = true;
sensorLine.setExtData("SENSOR_DATA", sensorData);
Engine.zoomExtents();
// Tip: Extended data is preserved after exporting to DWG and reopeningProperty Summary Table
| Property | Type | Default | Description |
|---|---|---|---|
type | string | "" | Entity type identifier |
id | number | 0 | Entity index ID |
objectId | string | "" | DWG object handle |
color | number | 256 | Color index (256=ByLayer) |
layer | string | "0" | Layer name |
layerId | string | "0" | Layer ID |
lineType | string | "ByLayer" | Line type name |
lineTypeScale | number | 1 | Line type scale |
lineWeight | number | -1 | Line weight (-1=ByLayer) |
visible | boolean | true | Entity visibility (false=invisible, DXF 60) |
isAlive | boolean | true | Whether the entity is alive |
isDirty | boolean | false | Whether it needs redrawing |
extData | object | undefined | Extended data (parsed JSON object) |
extDataAppName | string | undefined | Application name of extended data |
xdataRaw | object | undefined | Raw extended data format |
Core Methods
setDefaults()
Applies system default properties (current layer, color, line type, etc.). This method should be called after creating a new entity.
import { Engine, LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
// Apply system defaults
line.setDefaults();
// Equivalent to:
// line.layer = Engine.currentDoc.CLAYER;
// line.color = Engine.CECOLOR;
// line.lineType = Engine.CELTYPE;
// line.lineTypeScale = Engine.CELTSCALE;setModified()
Marks the entity as modified (sets isDirty = true).
Note: setModified() Does Not Trigger Redraw
setModified() only marks the entity as "dirty", it does not automatically trigger a screen redraw.
To see the modification effects, you must call Engine.regen() at the appropriate time.
// Internal implementation of setModified():
setModified(): void {
this.isDirty = true; // Mark as dirty
this.cachedBoundingBox = undefined; // Clear bounding box cache
// Note: No rendering methods are called!
}import { LineEnt, Point2D, Engine } from 'vjcad';
const line: LineEnt = /* ... */;
// Modifying properties via setters automatically calls setModified()
line.color = 1; // Automatically calls setModified()
line.startPoint.x = 50; // ObservablePoint2D automatically calls setModified()
// But you must manually call regen() to see the effects
Engine.regen();Automatic Invocation Scenarios
The following operations automatically call setModified(), no manual call needed:
- Property setters:
entity.color,entity.radius,entity.layer, etc. - ObservablePoint2D:
line.startPoint.x,circle.center.y, etc. - Transform methods:
move(),rotate(),scale(),mirror(), etc.
But you still need to manually call Engine.regen() to see the modification effects!
Scenarios Requiring Manual Invocation
When directly manipulating collections or internal data, you need to call it manually:
// Directly manipulating bulgePoints collection
pline.bulgePoints.items[0].bulge = 0.5;
pline.bulgePoints.removeLast();
pline.setModified(); // Manual call requiredclone()
Creates a deep copy of the entity.
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults();
line.color = 1;
// Clone the entity
const lineCopy = line.clone();
// The cloned entity is independent
lineCopy.color = 3; // Does not affect the original entity
console.log(line.color); // 1
console.log(lineCopy.color); // 3dispose()
Releases resources occupied by the entity. Usually called by the system when deleting an entity.
// Manual release (usually not needed)
entity.dispose();
// Automatically released when entity is deleted
Engine.eraseEntities(entity); // Internally calls dispose()Geometric Transforms
All entities support the following geometric transform methods. The point parameters of these methods support the PointInput type, which accepts either Point2D objects or [x, y] array format.
Important: Deferred Rendering Mechanism
WebCAD uses a deferred rendering design. Geometric transform methods (move, rotate, scale, mirror) will only:
- Modify the entity's geometric data
- Call
setModified()to markisDirty = true
They do not automatically trigger a redraw! You must manually call Engine.regen() to see the changes.
// Wrong: No visible effect after moving
circle.move([0, 0], [50, 30]);
// The screen is not updated...
// Correct: Call regen() after moving
circle.move([0, 0], [50, 30]);
Engine.regen(); // Trigger redraw to see the move effectDesign rationale: This allows batch modifications of multiple entities followed by a single redraw, avoiding GPU rendering for every small change and improving performance.
// Batch modify then redraw once (recommended)
entities.forEach(ent => ent.move([0, 0], [100, 0]));
Engine.regen(); // Single redraw, optimal performancemove() - Move
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// Shorthand (recommended)
entity.move([0, 0], [50, 50]);
// Point2D syntax
entity.move(new Point2D(0, 0), new Point2D(50, 50));
// Offset is to - from = (50, 50)
// Effectively translates the entire entity by (50, 50)rotate() - Rotate
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// Shorthand (recommended)
entity.rotate([50, 50], Math.PI / 4); // Rotate 45 degrees around point (50,50)
// Point2D syntax
entity.rotate(new Point2D(50, 50), Math.PI / 4);scale() - Scale
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// Shorthand (recommended)
entity.scale([50, 50], 2); // Scale by 2x with base point (50,50)
entity.scale([50, 50], 0.5); // Scale down to 50%
// Point2D syntax
entity.scale(new Point2D(50, 50), 2);mirror() - Mirror
import { EntityBase, Point2D } from 'vjcad';
const entity: EntityBase = /* ... */;
// Shorthand (recommended)
entity.mirror([0, 0], [100, 0]); // Mirror along horizontal line (Y=0)
entity.mirror([50, 0], [50, 100]); // Mirror along vertical line (X=50)
entity.mirror([0, 0], [100, 100]); // Mirror along arbitrary line
// Point2D syntax
entity.mirror(new Point2D(0, 0), new Point2D(100, 0));PointInput Type
All geometric transform methods support the PointInput type parameter, which accepts:
Point2Dobject:new Point2D(x, y)- 2D array:
[x, y](recommended, more concise) - 3D array:
[x, y, z]
See the Points and Vectors section for details.
Bounding Box
Get the entity's bounding box.
import { EntityBase, BoundingBox } from 'vjcad';
const entity: EntityBase = /* ... */;
// Get bounding box
const bbox: BoundingBox = entity.boundingBox();
// Access bounding box properties
console.log('Min point:', bbox.pt1);
console.log('Max point:', bbox.pt2);
console.log('Width:', bbox.width);
console.log('Height:', bbox.height);
console.log('Center:', bbox.center);Serialization
Entities support serialization to JSON format and restoration from JSON.
Export (toDb)
import { LineEnt, Point2D } from 'vjcad';
const line = new LineEnt(new Point2D(0, 0), new Point2D(100, 100));
line.setDefaults();
// Serialize to database format
const dbData = line.toDb();
// Example dbData structure:
// {
// type: "LINE",
// layerId: "0",
// color: 256,
// lineType: "ByLayer",
// lineTypeScale: 1,
// startPoint: [0, 0], // 2D coordinate format
// endPoint: [100, 100]
// }
// With Z coordinate: startPoint: [0, 0, 10], endPoint: [100, 100, 20]Import (fromDb)
import { LineEnt } from 'vjcad';
// Restore entity from JSON data (supports both new array format and old object format)
const dbData = {
type: "LINE",
layerId: "0",
color: 1,
startPoint: [0, 0], // New format: [x, y] or [x, y, z]
endPoint: [100, 100, 50] // With Z coordinate
};
const line = new LineEnt();
line.fromDb(dbData);
console.log('Start point:', line.startPoint); // Point2D(0, 0)
console.log('End point:', line.endPoint); // Point2D(100, 100)Complete Example
import { Engine, LineEnt, CircleEnt, Point2D } from 'vjcad';
// Create entities (using shorthand)
const line = new LineEnt([0, 0], [100, 100]);
const circle = new CircleEnt([50, 50], 30);
// Apply system default properties
line.setDefaults();
circle.setDefaults();
// Set custom properties
line.color = 1; // Red
line.layer = 'Layer1';
line.lineType = 'HIDDEN'; // Dashed line
line.lineTypeScale = 2.0;
circle.color = 3; // Green
circle.transpMgr.setTp100(30); // 30% transparent
// Geometric transforms (using shorthand)
line.move([0, 0], [50, 0]);
circle.scale([50, 50], 1.5);
// Add to canvas
Engine.addEntities([line, circle]);
// [Important] Must call regen() after modifications to see effects
Engine.regen();
// Get bounding boxes
const lineBBox = line.boundingBox();
const circleBBox = circle.boundingBox();
console.log('Line bounds:', lineBBox.pt1, lineBBox.pt2);
console.log('Circle bounds:', circleBBox.pt1, circleBBox.pt2);
// Clone an entity
const lineCopy = line.clone();
lineCopy.color = 5; // Blue
lineCopy.move([0, 0], [0, 50]);
Engine.addEntities(lineCopy);Next Steps
- Line Entity (LineEnt) - Line entity details
- Circle Entity (CircleEnt) - Circle entity details
- Arc Entity (ArcEnt) - Arc entity details
- Polyline Entity (PolylineEnt) - Polyline entity details
- Custom Entities - Creating custom entities
Related Examples
- Extended Data Example:
webcad-playground/public/static/demo/map/example/property/07xdata.js - Visibility Example:
webcad-playground/public/static/demo/map/example/property/08visibility.js