Quick Start
Quick Start
This chapter will guide you through getting started with WebCAD development, from project creation to drawing your first shape.
VJCAD Official MCP Address
VJCAD provides an official MCP (Model Context Protocol) server that can be used with AI editors such as Cursor:
- MCP URL: https://vjmap.com/server/aicad/mcp
"vjcad": {
"url": "https://vjmap.com/server/aicad/mcp"
}Online Examples
Before you begin, try the online examples:
| Example | Description | Link |
|---|---|---|
| Initialize Engine | MainView initialization and configuration options | Live Demo{target="_blank"} |
| Wait for Load | onLoad async waiting example | Live Demo{target="_blank"} |
| Viewer Mode | Display drawing area only | Live Demo{target="_blank"} |
| Internationalization (i18n) | Force a specific UI language or auto-detect | Live Demo{target="_blank"} |
Requirements
- Node.js: 16.0 or higher
- Package Manager: npm, yarn, or pnpm
- Browser: A modern browser with WebGL support
Create a Project
Option 1: Create a New Project with Vite
# Create a Vite + TypeScript project
npm create vite@latest my-webcad-app -- --template vanilla-ts
# Navigate to the project directory
cd my-webcad-app
# Install the WebCAD library
npm install vjcad
# Start the development server
npm run devOption 2: Add to an Existing Project
# Using npm
npm install vjcad
# Using yarn
yarn add vjcad
# Using pnpm
pnpm add vjcadProject Structure
Recommended project structure:
my-webcad-app/
├── public/
│ └── fonts/ # Font files directory
│ ├── simkai.woff # TrueType font
│ └── _default.shx # SHX font
├── src/
│ ├── main.ts # Entry file
│ ├── commands/ # Custom commands
│ └── plugins/ # Custom plugins
├── index.html
├── package.json
└── tsconfig.jsonInitialize the Engine
Basic Initialization
Add a container in index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebCAD App</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; }
#cad-app { width: 100%; height: 100%; }
</style>
</head>
<body>
<div id="cad-app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>Initialize in main.ts:
import { MainView, initCadContainer } from 'vjcad';
// Create a MainView instance
const cadView = new MainView({
appname: "My CAD App",
version: "v1.0.0"
});
// Mount the CAD view to the container
initCadContainer("cad-app", cadView);MainView Configuration Options
The MainView constructor accepts a configuration object with the following options:
interface MainViewOptions {
/** Application name, displayed in the title bar */
appname?: string;
/** Application version number */
version?: string;
/** WASM service URL for loading WebAssembly modules */
wasmServiceUrl?: string;
/** Backend service URL for file operations and other APIs */
serviceUrl?: string;
/** Access token for authentication */
accessToken?: string;
/** Access key for drawing encryption */
accessKey?: string;
/** Plugin marketplace configuration file URL */
pluginMarketUrl?: string;
/**
* Sidebar style
* - "both": Show on both sides
* - "left": Show left side only
* - "right": Show right side only (default)
* - "none": Hide sidebar
*/
sidebarStyle?: "both" | "left" | "right" | "none";
/**
* UI locale (optional).
* - 'zh-CN': Chinese (default)
* - 'en-US': English
* When omitted, the locale is auto-detected in priority order:
* URL ?lang= > localStorage > navigator.language > zh-CN
*/
locale?: 'zh-CN' | 'en-US';
/** Font configuration list */
fonts?: FontConfig[];
}
interface FontConfig {
/** Font file path */
path: string;
/** Font name (optional, auto-inferred from filename) */
name?: string;
/** Font type: 'woff' | 'ttf' | 'shx' */
kind?: string;
}Full Configuration Example
import { MainView, initCadContainer } from 'vjcad';
const cadView = new MainView({
appname: "WebCAD",
version: "v1.0.0",
serviceUrl: "http://127.0.0.1:27660/api/v1",
accessToken: "xxxx",
accessKey: "",
sidebarStyle: "none",
pluginMarketUrl: './plugins/plugins.json',
fonts: [
{
path: "./fonts/simkai.woff",
name: "simkai",
kind: "woff"
},
{
path: "./fonts/_default.shx",
name: "_default",
kind: "shx"
}
]
});
initCadContainer("cad-app", cadView);Waiting for Initialization
WebCAD initialization is asynchronous, including canvas creation, WASM loading, plugin loading, etc. Use the onLoad() method to wait for initialization before executing operations:
import { MainView, initCadContainer, Engine, LineEnt } from 'vjcad';
const cadView = new MainView({
appname: "WebCAD",
version: "v1.0.0",
serviceUrl: "http://127.0.0.1:27660/api/v1",
});
initCadContainer("cad-app", cadView);
// Wait for initialization to complete
await cadView.onLoad();
// Now you can safely use the Engine API
// Using shorthand: arrays instead of Point2D objects
const line = new LineEnt([0, 0], [100, 100]);
line.setDefaults();
Engine.addEntities(line);
// Zoom to fit
Engine.zoomExtents();Note
onLoad() returns a Promise that resolves after the following initialization steps:
- PixiJS canvas creation
- WASM module loading
- Font loading
- Plugin system initialization
- URL parameter processing (auto-open drawings, activate sidebar panels, etc.)
Supported URL parameters include vcad_mapid (drawing ID), vcad_panel (active panel), etc. See URL Parameters for details.
First Example: Drawing a Line
import { Engine, LineEnt } from 'vjcad';
/**
* Draw a line from (0,0) to (100,100)
*/
function drawLine() {
// 1. Create a line entity
// Two syntax options:
// - Shorthand: new LineEnt([x1, y1], [x2, y2])
// - Standard: new LineEnt(new Point2D(x1, y1), new Point2D(x2, y2))
const line = new LineEnt([0, 0], [100, 100]);
// 2. Apply system default properties
// This sets the current layer, color, linetype, etc.
line.setDefaults();
// 3. Add to canvas (recommended approach)
// Engine.addEntities automatically handles addition and undo recording
Engine.addEntities(line);
// 4. Zoom to fit all content
Engine.zoomExtents();
console.log('Line created, length:', line.Length);
}Shorthand Syntax
WebCAD supports using arrays [x, y] instead of new Point2D(x, y) for cleaner code. Most methods that accept coordinate parameters support this shorthand.
Drawing Basic Shapes
Drawing a Circle
import { Engine, CircleEnt } from 'vjcad';
/**
* Draw a circle
* @param center Center coordinates [x, y]
* @param radius Radius
*/
function drawCircle(center: [number, number], radius: number) {
const circle = new CircleEnt(center, radius);
circle.setDefaults();
Engine.addEntities(circle);
console.log('Center:', circle.center);
console.log('Radius:', circle.radius);
}
drawCircle([50, 50], 30);Drawing an Arc
import { Engine, ArcEnt } from 'vjcad';
/**
* Draw an arc by center, radius and angles
*/
function drawArc() {
// Create arc: center (100, 100), radius 50, from 0 to 90 degrees
const arc = new ArcEnt(
[100, 100], // Center (shorthand)
50, // Radius
0, // Start angle (radians)
Math.PI / 2 // End angle (radians, 90 degrees)
);
arc.setDefaults();
Engine.addEntities(arc);
console.log('Arc start point:', arc.startPoint);
console.log('Arc end point:', arc.endPoint);
console.log('Arc length:', arc.length);
}
/**
* Draw an arc through three points
*/
function drawArcBy3Points() {
const arc = new ArcEnt();
arc.initBy3Pt([0, 0], [50, 50], [100, 0]);
arc.setDefaults();
Engine.addEntities(arc);
}Drawing a Polyline
import { Engine, PolylineEnt, BulgePoints, BulgePoint } from 'vjcad';
/**
* Draw a rectangle (closed polyline)
*/
function drawRectangle(x: number, y: number, width: number, height: number) {
const points = new BulgePoints();
// Add four vertices (bulge of 0 means straight line segment)
points.add(new BulgePoint([x, y], 0)); // Bottom-left
points.add(new BulgePoint([x + width, y], 0)); // Bottom-right
points.add(new BulgePoint([x + width, y + height], 0)); // Top-right
points.add(new BulgePoint([x, y + height], 0)); // Top-left
const pline = new PolylineEnt(points);
pline.isClosed = true;
pline.setDefaults();
Engine.addEntities(pline);
console.log('Polyline length:', pline.length);
console.log('Polyline area:', pline.area);
}
/**
* Draw a polyline with arc segments
* Bulge represents the curvature of the arc segment:
* - 0: Straight line segment
* - Positive: Counter-clockwise arc
* - Negative: Clockwise arc
* - 1: Semicircle
*/
function drawPolylineWithArc() {
const points = new BulgePoints();
points.add(new BulgePoint([0, 0], 0)); // Start, straight to next
points.add(new BulgePoint([100, 0], 0.5)); // Arc start, bulge 0.5
points.add(new BulgePoint([150, 50], 0)); // Arc end, straight to next
points.add(new BulgePoint([150, 100], 0)); // End point
const pline = new PolylineEnt(points);
pline.setDefaults();
Engine.addEntities(pline);
}
drawRectangle(0, 0, 200, 100);Drawing Text
import { Engine, TextEnt, MTextEnt } from 'vjcad';
/**
* Draw single-line text
*/
function drawText(position: [number, number], content: string, height: number = 10) {
const text = new TextEnt();
text.position = position;
text.text = content;
text.height = height;
text.rotation = 0; // Rotation angle (radians)
text.setDefaults();
Engine.addEntities(text);
}
/**
* Draw multi-line text
*/
function drawMText(position: [number, number], content: string, width: number = 100) {
const mtext = new MTextEnt();
mtext.position = position;
mtext.text = content;
mtext.width = width;
mtext.height = 10;
mtext.setDefaults();
Engine.addEntities(mtext);
}
drawText([0, 120], "WebCAD Sample Text", 15);
drawMText([0, 150], "This is a\nmulti-line\ntext example", 80);Setting Entity Properties
Color
import { Engine, LineEnt } from 'vjcad';
// WebCAD uses AutoCAD Color Index (ACI)
// Common color indices:
// 1 = Red 4 = Cyan 7 = White
// 2 = Yellow 5 = Blue 256 = ByLayer
// 3 = Green 6 = Magenta 0 = ByBlock
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
// Set to red
line.color = 1;
// Set to ByLayer color
line.color = 256;
// Use Engine's current color
line.color = Engine.CECOLOR;Layer
import { Engine, LineEnt } from 'vjcad';
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
// Set layer name
line.layer = "Annotation";
// Get current layer
const currentLayer = Engine.CLAYER;
line.layer = currentLayer;
// Create a new layer (if it doesn't exist)
const doc = Engine.currentDoc;
if (!doc.layers.has("MyLayer")) {
doc.layers.add("MyLayer", {
color: 3, // Green
lineType: "CONTINUOUS",
layerOn: true,
frozen: false
});
}
line.layer = "MyLayer";Linetype
import { Engine, LineEnt } from 'vjcad';
const line = new LineEnt([0, 0], [100, 0]);
line.setDefaults();
// Set linetype name
// Common linetypes: CONTINUOUS (solid), HIDDEN (dashed), CENTER (center line)
line.lineType = "HIDDEN";
// Set linetype scale
line.lineTypeScale = 2.0;
// Use current linetype scale
line.lineTypeScale = Engine.CELTSCALE;Getting User Input
Getting a Point
import { Engine, PointInputOptions, InputStatusEnum, LineEnt, Point2D } from 'vjcad';
/**
* Interactive line drawing
*/
async function drawLineInteractive() {
const opt1 = new PointInputOptions("Specify start point:");
const result1 = await Engine.getPoint(opt1);
if (result1.status !== InputStatusEnum.OK) {
console.log('User cancelled');
return;
}
const startPoint = result1.value as Point2D;
const opt2 = new PointInputOptions("Specify end point:");
opt2.basePoint = startPoint;
opt2.useBasePoint = true; // Enable rubber band line
const result2 = await Engine.getPoint(opt2);
if (result2.status !== InputStatusEnum.OK) {
console.log('User cancelled');
return;
}
const endPoint = result2.value as Point2D;
const line = new LineEnt(startPoint, endPoint);
line.setDefaults();
Engine.addEntities(line);
}Getting a Selection Set
import {
Engine,
SelectionSetInputOptions,
InputStatusEnum,
EntityBase
} from 'vjcad';
/**
* Select entities and change their color
*/
async function changeEntityColor(newColor: number) {
const options = new SelectionSetInputOptions("Select entities to change color:");
const result = await Engine.getSelections(options);
if (result.status !== InputStatusEnum.OK) {
console.log('No entities selected');
return;
}
const entities = result.value as EntityBase[];
console.log(`Selected ${entities.length} entities`);
entities.forEach(entity => {
entity.color = newColor;
});
Engine.regen();
}Getting Numeric Input
import { Engine, RealInputOptions, IntegerInputOptions, InputStatusEnum } from 'vjcad';
/**
* Get a real number input
*/
async function getRealValue(): Promise<number | null> {
const options = new RealInputOptions("Enter radius value:");
options.defaultValue = 10;
const result = await Engine.getReal(options);
if (result.status === InputStatusEnum.OK) {
return result.value as number;
}
return null;
}
/**
* Get an integer input
*/
async function getIntegerValue(): Promise<number | null> {
const options = new IntegerInputOptions("Enter number of sides:");
options.defaultValue = 6;
const result = await Engine.getInteger(options);
if (result.status === InputStatusEnum.OK) {
return result.value as number;
}
return null;
}Input with Keywords
import { Engine, PointInputOptions, InputStatusEnum } from 'vjcad';
/**
* Point input with keyword options
*/
async function getPointWithKeywords() {
const options = new PointInputOptions("Specify point or [Settings(S)/Exit(E)]:");
options.keywords = ["S", "E"];
const result = await Engine.getPoint(options);
switch (result.status) {
case InputStatusEnum.OK:
console.log('User entered a point:', result.value);
break;
case InputStatusEnum.KEYWORD:
console.log('User entered a keyword:', result.stringResult);
if (result.stringResult === "S") {
// Handle settings option
} else if (result.stringResult === "E") {
// Handle exit option
}
break;
case InputStatusEnum.CANCEL:
console.log('User cancelled the operation');
break;
}
}View Operations
import { Engine } from 'vjcad';
// Zoom to fit all content (most commonly used)
Engine.zoomExtents();
// Zoom to selected entities
Engine.zoomToEntities([entity1, entity2]);
// Refresh display
Engine.regen();Adding Entities with Engine Helper Methods
import { Engine, LineEnt, CircleEnt } from 'vjcad';
// Method 1: Using Engine.addEntities (recommended)
// Call setDefaults() first to set default properties
const line = new LineEnt([0, 0], [100, 100]);
line.setDefaults();
Engine.addEntities(line); // Single entity
// Add multiple entities
const circle = new CircleEnt([50, 50], 25);
circle.setDefaults();
Engine.addEntities([line, circle]);
// Method 2: With custom properties
const ent = new LineEnt([0, 0], [50, 50]);
ent.setDefaults();
ent.color = 1; // Set to red
Engine.addEntities(ent);
// Zoom to fit
Engine.zoomExtents();Internationalization (i18n)
WebCAD includes a lightweight built-in i18n module that supports Chinese (zh-CN) and English (en-US) UI languages.
Locale Detection Priority
When no locale is explicitly set, the system detects the language in the following order:
localeoption in theMainViewconstructor (highest priority)- URL parameter
?lang= vjcad-localevalue saved in localStorage- Browser language
navigator.language - Default:
zh-CN
URL parameter examples:
# English UI
https://example.com/app?lang=en-US
https://example.com/app?lang=en
# Chinese UI
https://example.com/app?lang=zh-CN
https://example.com/app?lang=zhFuzzy matching is supported: en, en-US, and EN all resolve to 'en-US'; zh, zh-CN, and zh-TW all resolve to 'zh-CN'.
Forcing a Specific Language
Option 1: Via the MainView locale config (recommended)
import { MainView, initCadContainer } from 'vjcad';
const cadView = new MainView({
appname: "WebCAD",
version: "v1.0.0",
locale: 'en-US', // Force English; use 'zh-CN' to force Chinese
});
initCadContainer("cad-app", cadView);Option 2: Via initLocale() (must be called before MainView is instantiated)
import { MainView, initCadContainer, initLocale } from 'vjcad';
// Call before creating MainView
initLocale('en-US'); // Force English
const cadView = new MainView({ appname: "WebCAD" });
initCadContainer("cad-app", cadView);Auto-detecting Language (Default Behavior)
Simply omit the locale option to let the system detect it automatically:
import { MainView, initCadContainer } from 'vjcad';
// No locale set — auto-detected by priority order
const cadView = new MainView({
appname: "WebCAD",
version: "v1.0.0",
});
initCadContainer("cad-app", cadView);Users can also switch language by appending ?lang=en to the URL, or by clicking the language button in the top-right corner of the UI.
Switching Language at Runtime
import { setLocale, getLocale } from 'vjcad';
// Get the current locale
console.log(getLocale()); // 'zh-CN' or 'en-US'
// Switch to English (saves to localStorage and reloads the page)
setLocale('en-US');
// Switch to Chinese
setLocale('zh-CN');Registering Custom Translations
Use registerMessages to add application-level or plugin-level translations, then retrieve them with t():
import { registerMessages, t } from 'vjcad';
registerMessages({
'zh-CN': {
'app.title': '我的 CAD 应用',
'app.ready': '引擎初始化完成',
},
'en-US': {
'app.title': 'My CAD App',
'app.ready': 'Engine initialized',
},
});
// Returns text for the current locale automatically
console.log(t('app.title')); // 'My CAD App' (when locale is en-US)
console.log(t('app.ready')); // 'Engine initialized'Live Example
i18n Demo{target="_blank"} — Force English UI and demonstrate runtime language switching.
Next Steps
Now that you've learned the basics of WebCAD, continue with:
- Architecture Overview - Learn about the system architecture
- Engine System - Dive into the Engine API
- Entity System - Learn about various entity types in detail
- Command System - Learn how to create custom commands
- Plugin System - Develop WebCAD plugins