Command System
About 2 min
Command System
Creating, registering, and executing custom commands.
Basic Command Definition
const {
CommandDefinition,
CommandRegistry,
CommandOptions,
writeMessage,
Engine
} = vjcad;
// Define command class
class HelloCommand {
async main() {
writeMessage("<br/>Hello WebCAD!");
Engine.zoomExtents();
}
}
// Create command definition
const options = new CommandOptions();
options.useAutoComplete = true; // Enable auto-complete
const cmdDef = new CommandDefinition(
'HELLO', // Command name (uppercase)
'Simple example command', // Description
HelloCommand, // Command class
options // Options (optional)
);
// Register command
CommandRegistry.regist(cmdDef);
// Execute command
await Engine.editor.executerWithOp('HELLO');Command with User Input
const {
CommandDefinition, CommandRegistry,
PointInputOptions, InputStatusEnum,
getPoint, writeMessage,
LineEnt, Engine
} = vjcad;
class DrawLineCommand {
async main() {
// Start undo group
Engine.undoManager.start_undoMark();
try {
// Get start point
const opt1 = new PointInputOptions("Specify start point:");
const result1 = await getPoint(opt1);
if (result1.status !== InputStatusEnum.OK) return;
// Get end point (with rubber band line)
const opt2 = new PointInputOptions("Specify end point:");
opt2.useBasePoint = true;
opt2.basePoint = result1.value;
const result2 = await getPoint(opt2);
if (result2.status !== InputStatusEnum.OK) return;
// Create line
const line = new LineEnt(result1.value, result2.value);
line.setDefaults();
Engine.addEntities(line);
writeMessage("<br/>Line created");
} finally {
Engine.undoManager.end_undoMark();
}
}
}
const cmdDef = new CommandDefinition("DRAWLINE", "Draw Line", DrawLineCommand);
CommandRegistry.regist(cmdDef);Preview Drawing
Use callback to implement real-time preview during mouse movement.
const {
PointInputOptions, InputStatusEnum,
getPoint, CircleEnt, Engine
} = vjcad;
class DrawCircleWithPreviewCommand {
constructor() {
this.center = null;
}
async main() {
Engine.undoManager.start_undoMark();
try {
// Get center
const opt1 = new PointInputOptions("Specify center:");
const result1 = await getPoint(opt1);
if (result1.status !== InputStatusEnum.OK) return;
this.center = result1.value;
// Get radius (with preview)
await this.getRadiusWithPreview();
} finally {
Engine.undoManager.end_undoMark();
Engine.clearPreview(); // Clear preview
}
}
async getRadiusWithPreview() {
const options = new PointInputOptions("Specify radius:");
options.useBasePoint = true;
options.basePoint = this.center;
// Preview callback
options.callback = (canvasPoint) => {
const worldPoint = Engine.CanvasToWcs(canvasPoint);
const radius = Math.sqrt(
Math.pow(worldPoint.x - this.center.x, 2) +
Math.pow(worldPoint.y - this.center.y, 2)
);
if (radius > 0) {
const previewCircle = new CircleEnt(
[this.center.x, this.center.y],
radius
);
previewCircle.setDefaults();
Engine.clearPreview();
Engine.drawPreviewEntity(previewCircle);
}
};
const result = await getPoint(options);
Engine.clearPreview();
if (result.status === InputStatusEnum.OK) {
const endPoint = result.value;
const radius = Math.sqrt(
Math.pow(endPoint.x - this.center.x, 2) +
Math.pow(endPoint.y - this.center.y, 2)
);
const circle = new CircleEnt([this.center.x, this.center.y], radius);
circle.setDefaults();
Engine.addEntities(circle);
}
}
}Preview-related APIs:
Engine.CanvasToWcs(canvasPoint)- Convert canvas coordinates to world coordinatesEngine.clearPreview()- Clear previewEngine.drawPreviewEntity(entity)- Draw a single preview entityEngine.drawPreviewEntities(entities)- Draw multiple preview entities
State Machine Command Pattern
Handle complex multi-step commands.
const {
PointInputOptions, InputStatusEnum,
getPoint, PolylineEnt, Engine, writeMessage
} = vjcad;
class DrawPolylineCommand {
constructor() {
this.step = 1; // Current state
this.points = []; // Vertex collection
this.isClosed = false;
}
async main() {
Engine.undoManager.start_undoMark();
try {
// State machine loop
while (this.step > 0) {
switch (this.step) {
case 1:
await this.step1_getFirstPoint();
break;
case 2:
await this.step2_getNextPoints();
break;
case 3:
this.step3_finish();
break;
}
}
} finally {
Engine.undoManager.end_undoMark();
Engine.clearPreview();
}
}
async step1_getFirstPoint() {
const options = new PointInputOptions("Specify start point:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
this.points.push(result.value);
this.step = 2;
} else {
this.step = 0; // Exit
}
}
async step2_getNextPoints() {
const options = new PointInputOptions(
"Specify next point [Close(C)/Undo(U)] <Done>:"
);
options.keywords = ["C", "U"]; // Support keywords
if (this.points.length > 0) {
options.useBasePoint = true;
options.basePoint = this.points[this.points.length - 1];
}
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// User clicked a point
this.points.push(result.value);
} else if (result.status === InputStatusEnum.Keyword) {
// User entered a keyword
const keyword = result.stringResult.toUpperCase();
if (keyword === "C") {
this.isClosed = true;
this.step = 3;
} else if (keyword === "U") {
if (this.points.length > 1) {
this.points.pop();
writeMessage("<br/>Last point undone");
}
}
} else if (result.status === InputStatusEnum.EnterOrSpace) {
// User pressed Enter/Space, finish
this.step = 3;
} else {
// Cancel
this.step = 0;
}
}
step3_finish() {
if (this.points.length >= 2) {
const pline = new PolylineEnt();
pline.setPoints(this.points.map(p => [p.x, p.y]));
pline.isClosed = this.isClosed;
pline.setDefaults();
Engine.addEntities(pline);
writeMessage(`<br/>Polyline created, ${this.points.length} vertices`);
}
this.step = 0; // End
}
}State machine key points:
step > 0continues loop,step = 0exitsInputStatusEnum.Keywordhandles keyword inputresult.stringResultgets keyword stringInputStatusEnum.EnterOrSpacehandles Enter/Space
Command Registration & Management
// Batch register commands
const commands = [
{ name: 'CMD1', desc: 'Command 1', cls: Cmd1Class },
{ name: 'CMD2', desc: 'Command 2', cls: Cmd2Class },
];
commands.forEach(cmd => {
const options = new CommandOptions();
options.useAutoComplete = true;
CommandRegistry.regist(
new CommandDefinition(cmd.name, cmd.desc, cmd.cls, options)
);
});
// Query registered commands
const cmdInfo = CommandRegistry.item('CMD1');
if (cmdInfo) {
console.log("Command name:", cmdInfo.name);
console.log("Description:", cmdInfo.description);
}
// Unregister command
CommandRegistry.unregist('CMD1');Standard Command Class Template
class MyCommand {
constructor() {
// Initialize state
}
async main() {
// Start undo group
Engine.undoManager.start_undoMark();
try {
// Command logic
await this.doWork();
} catch (error) {
writeMessage(`<br/>Error: ${error.message}`);
} finally {
// End undo group
Engine.undoManager.end_undoMark();
// Clear preview
Engine.clearPreview();
}
}
async doWork() {
// Actual command logic
}
}