Create Command
About 2 min
Create Command
This chapter demonstrates how to create custom commands through complete examples.
Complete Command Example: Draw Rectangle
import {
Engine,
Point2D,
PolylineEnt,
PointInputOptions,
RealInputOptions,
InputStatusEnum,
ssSetFirst,
writeMessage
} from 'vjcad';
export class RectangleCommand {
private cornerPoint1: Point2D | null = null;
private width: number = 0;
private height: number = 0;
async main(): Promise<void> {
ssSetFirst([]); // Clear selection set
// Start undo mark group
Engine.undoManager.start_undoMark();
try {
// Step 1: Get first corner point
const p1Result = await this.getFirstCorner();
if (!p1Result) return;
// Step 2: Get second corner point or dimensions
const p2Result = await this.getSecondCorner();
if (!p2Result) return;
// Step 3: Create rectangle
this.createRectangle();
} finally {
// End undo mark group
Engine.undoManager.end_undoMark();
Engine.clearPreview();
}
}
private async getFirstCorner(): Promise<boolean> {
const options = new PointInputOptions("Specify first corner point:");
const result = await Engine.getPoint(options);
if (result.status === InputStatusEnum.OK) {
this.cornerPoint1 = result.value;
return true;
}
return false;
}
private async getSecondCorner(): Promise<boolean> {
const options = new PointInputOptions("Specify other corner point [Dimensions(D)]:");
options.keywords = ["D"];
options.useBasePoint = true;
options.basePoint = this.cornerPoint1!;
// Set preview callback
options.onMouseMove = (currentPoint: Point2D) => {
this.drawPreview(currentPoint);
};
const result = await Engine.getPoint(options);
if (result.status === InputStatusEnum.OK) {
const p2 = result.value;
this.width = Math.abs(p2.x - this.cornerPoint1!.x);
this.height = Math.abs(p2.y - this.cornerPoint1!.y);
return true;
} else if (result.stringResult === "D") {
// User chose to input dimensions
return await this.getDimensions();
}
return false;
}
private async getDimensions(): Promise<boolean> {
// Get width
const widthOpt = new RealInputOptions("Specify rectangle width:");
const widthResult = await Engine.getReal(widthOpt);
if (widthResult.status !== InputStatusEnum.OK) return false;
this.width = widthResult.value;
// Get height
const heightOpt = new RealInputOptions("Specify rectangle height:");
const heightResult = await Engine.getReal(heightOpt);
if (heightResult.status !== InputStatusEnum.OK) return false;
this.height = heightResult.value;
return true;
}
private drawPreview(currentPoint: Point2D): void {
if (!this.cornerPoint1) return;
const rect = this.buildRectangle(this.cornerPoint1, currentPoint);
Engine.drawPreviewEntity(rect);
}
private buildRectangle(p1: Point2D, p2: Point2D): PolylineEnt {
const pline = new PolylineEnt();
// Use simplified interface to set vertices
pline.setPoints([
[p1.x, p1.y],
[p2.x, p1.y],
[p2.x, p2.y],
[p1.x, p2.y]
]);
pline.isClosed = true;
pline.setDefaults();
return pline;
}
private createRectangle(): void {
const p2 = new Point2D(
this.cornerPoint1!.x + this.width,
this.cornerPoint1!.y + this.height
);
const rect = this.buildRectangle(this.cornerPoint1!, p2);
// Add to canvas
Engine.pcanvas.addEntity(rect);
Engine.undoManager.added_undoMark([rect]);
// Refresh display
Engine.pcanvas.regen();
writeMessage(`<br/>Rectangle created, width: ${this.width}, height: ${this.height}`);
}
}Complex Command with State Machine
For more complex commands, you can use the state machine pattern:
export class MultiStepCommand {
private step: number = 1;
private points: Point2D[] = [];
async main(): Promise<void> {
Engine.undoManager.start_undoMark();
while (this.step > 0) {
switch (this.step) {
case 1:
await this.step1_getFirstPoint();
break;
case 2:
await this.step2_getNextPoint();
break;
case 3:
await this.step3_finish();
break;
}
}
Engine.undoManager.end_undoMark();
}
private async step1_getFirstPoint(): Promise<void> {
const result = await Engine.getPoint(new PointInputOptions("First point:"));
if (result.status === InputStatusEnum.OK) {
this.points.push(result.value);
this.step = 2;
} else {
this.step = 0; // Exit
}
}
private async step2_getNextPoint(): Promise<void> {
const options = new PointInputOptions("Next point [Undo(U)/Finish(F)]:");
options.keywords = ["U", "F"];
options.useBasePoint = true;
options.basePoint = this.points[this.points.length - 1];
const result = await Engine.getPoint(options);
if (result.status === InputStatusEnum.OK) {
this.points.push(result.value);
} else if (result.stringResult === "U") {
if (this.points.length > 1) {
this.points.pop();
} else {
this.step = 1;
}
} else if (result.stringResult === "F" || result.status === InputStatusEnum.EnterOrSpace) {
this.step = 3;
} else {
this.step = 0;
}
}
private async step3_finish(): Promise<void> {
if (this.points.length >= 2) {
// Create entity...
const pline = new PolylineEnt();
// Use bulgePoints.add() to add vertices
for (const p of this.points) {
pline.bulgePoints.add(new BulgePoint(new Point2D(p.x, p.y), 0));
}
pline.setDefaults();
Engine.pcanvas.addEntity(pline);
Engine.undoManager.added_undoMark([pline]);
}
this.step = 0;
}
}Register Command
import { CommandRegistry, CommandDefinition, CommandOptions } from 'vjcad';
// Register rectangle command
CommandRegistry.regist(new CommandDefinition(
'RECTANGLE',
'Draw rectangle',
RectangleCommand,
new CommandOptions()
));
// Register multi-step command
CommandRegistry.regist(new CommandDefinition(
'MULTISTEP',
'Multi-step command',
MultiStepCommand,
new CommandOptions()
));Command Development Key Points
Undo Support
// Always use undo mark groups
Engine.undoManager.start_undoMark();
try {
// Execute operations
} finally {
Engine.undoManager.end_undoMark();
}Preview Drawing
// Draw preview on mouse move
options.onMouseMove = (point: Point2D) => {
Engine.clearPreview();
const previewEntity = buildEntity(point);
Engine.drawPreviewEntity(previewEntity);
};Cleanup
// Clean up when command ends
finally {
Engine.clearPreview();
Engine.undoManager.end_undoMark();
}Keyword Handling
if (result.stringResult === "U") {
// Undo last step
undoLastStep();
} else if (result.stringResult === "C") {
// Close
closeShape();
}Next Steps
- Command Architecture - Deep dive into command registration and lifecycle
- Point Picking Guide - Detailed point picking tutorial
- Entity Selection Guide - Detailed entity selection tutorial
- Undo/Redo Mechanism - Learn undo support
- Preview Drawing - Learn entity preview
- Command Design Patterns - Learn different command design patterns
- Best Practices - Command development best practices
- API Reference - Complete API reference
- Plugin System - Register commands in plugins