Point Input Guide
About 5 min
Point Input Guide
Point picking is the most basic and important user interaction in CAD commands. This chapter explains in detail how to use getPoint() to obtain point coordinates from the user.
Basic Usage
Simple Point Pick
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
async function getOnePoint(): Promise<void> {
const options = new PointInputOptions("Specify point:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
const point = result.value;
console.log(`Point: (${point.x}, ${point.y})`);
}
}Full PointInputOptions Properties
PointInputOptions provides many configuration options:
const options = new PointInputOptions("Prompt message");
// Basic properties
options.message = "Specify point [Option(A)/Option(B)]:"; // Prompt message
options.keywords = ["A", "B"]; // Keyword list
// Base point and rubber-band line
options.useBasePoint = true; // Enable base point
options.basePoint = new Point2D(0, 0); // Base point
// Object snap
options.useOsnap = true; // Enable object snap
options.forceOsmode = 128; // Force snap mode
options.useForceOsmode = false; // Whether to use forced mode
// Ortho and polar
options.useOrthoMode = true; // Use ortho mode
options.forceOrthomode = false; // Force ortho
options.ignoreOrthomode = false; // Ignore ortho
options.ignorePolarMode = false; // Ignore polar
// Angle control
options.useforceAngle = false; // Use forced angle
options.forceAngle = 0; // Forced angle value
// Input control
options.allowNumberResult = false; // Allow number input
options.defaultKeyword = undefined; // Default keyword
// Callback
options.callback = (canvasPoint, originalPoint) => {
// Called while mouse moves, useful for preview
};
// Display control
options.hideRubberBand = false; // Hide rubber-band line
options.hideCrossHairCursor = false; // Hide crosshair cursor
// Automation
options.isAutomation = false; // Automation mode
options.automateStr = ""; // Automated input string
// Multi-point tracking
options.isMtpMode = false; // Multi-point tracking modeCommon Properties
| Property | Type | Default | Description |
|---|---|---|---|
message | string | "Specify point:" | Command-line prompt |
keywords | string[] | [] | Available keywords such as ["U", "C"] |
useBasePoint | boolean | false | Whether to show a rubber-band line from the base point |
basePoint | Point2D | (0,0) | Start point of the rubber-band line |
useOsnap | boolean | true | Whether to enable object snap |
callback | function | undefined | Mouse-move callback |
allowNumberResult | boolean | false | Whether users can enter a number directly |
InputStatusEnum
The status field in the getPoint() result indicates the user action:
const result = await getPoint(options);
switch (result.status) {
case InputStatusEnum.OK:
// User successfully input a point
const point = result.value;
break;
case InputStatusEnum.Cancel:
// User pressed ESC or right-clicked to cancel
break;
case InputStatusEnum.Keyword:
// User entered a keyword
const keyword = result.stringResult;
break;
case InputStatusEnum.EnterOrSpace:
// User pressed Enter or Space
break;
case InputStatusEnum.IsNumber:
// User entered a number (requires allowNumberResult = true)
const number = result.numberValue;
break;
case InputStatusEnum.None:
// No input
break;
case InputStatusEnum.Error:
// Error occurred
break;
}| Status | Value | Description |
|---|---|---|
OK | 5100 | Point coordinate acquired successfully |
Cancel | -5002 | User canceled the operation |
Keyword | -5005 | User entered a keyword |
EnterOrSpace | 3 | User pressed Enter or Space |
IsNumber | 4 | User entered a number |
None | 5000 | No input |
Error | -5001 | An error occurred |
Point-Picking Scenarios
Scenario 1: Pick a Single Point
The simplest point pick, used to obtain one coordinate:
import {
getPoint,
PointInputOptions,
InputStatusEnum,
writeMessage
} from 'vjcad';
async function pickSinglePoint(): Promise<Point2D | null> {
const options = new PointInputOptions("Specify point:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
writeMessage(`<br/>Point: (${result.value.x.toFixed(2)}, ${result.value.y.toFixed(2)})`);
return result.value;
}
return null;
}Scenario 2: Continuous Point Picking with Base Point
When drawing lines, a rubber-band line should be shown from the previous point to the cursor:
import { Point2D } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
class LineDrawer {
private points: Point2D[] = [];
async drawLines(): Promise<void> {
// Get first point
const firstOptions = new PointInputOptions("Specify first point:");
const firstResult = await getPoint(firstOptions);
if (firstResult.status !== InputStatusEnum.OK) return;
this.points.push(firstResult.value);
// Continuously get next points
while (true) {
const lastPoint = this.points[this.points.length - 1];
const nextOptions = new PointInputOptions("Specify next point [Undo(U)]:");
nextOptions.keywords = ["U"];
nextOptions.useBasePoint = true;
nextOptions.basePoint = lastPoint;
const nextResult = await getPoint(nextOptions);
if (nextResult.status === InputStatusEnum.OK) {
this.points.push(nextResult.value);
// Create line segment...
} else if (nextResult.stringResult === "U") {
// Undo previous point
if (this.points.length > 1) {
this.points.pop();
}
} else {
// User canceled or pressed Enter to finish
break;
}
}
}
}Scenario 3: Point Picking with Keyword Options
Support different user options:
async function getPointWithOptions(): Promise<void> {
const options = new PointInputOptions("Specify point [Undo(U)/Close(C)/Finish(F)]:");
options.keywords = ["U", "C", "F"];
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// User clicked a point
console.log('Point:', result.value);
} else if (result.status === InputStatusEnum.Keyword) {
switch (result.stringResult.toUpperCase()) {
case "U":
console.log('Undo');
break;
case "C":
console.log('Close');
break;
case "F":
console.log('Finish');
break;
}
} else if (result.status === InputStatusEnum.EnterOrSpace) {
console.log('Use default');
}
}Scenario 4: Point Picking with Preview Callback
When drawing a circle, preview the radius in real time:
import { Point2D, CircleEnt, Engine } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
class CircleDrawer {
private center: Point2D = new Point2D();
async drawCircle(): Promise<void> {
// Get center
const centerOptions = new PointInputOptions("Specify center:");
const centerResult = await getPoint(centerOptions);
if (centerResult.status !== InputStatusEnum.OK) return;
this.center = centerResult.value;
// Get radius point with preview
const radiusOptions = new PointInputOptions("Specify radius:");
radiusOptions.useBasePoint = true;
radiusOptions.basePoint = this.center;
// Preview callback
radiusOptions.callback = (canvasPoint: Point2D) => {
const worldPoint = Engine.trans.CanvasToWcs(canvasPoint);
const radius = Math.sqrt(
Math.pow(worldPoint.x - this.center.x, 2) +
Math.pow(worldPoint.y - this.center.y, 2)
);
const previewCircle = new CircleEnt(this.center, radius);
previewCircle.setDefaults();
Engine.pcanvas.drawControl.previewGraphics.clear();
Engine.pcanvas.drawControl.drawPreviewEntity(previewCircle);
};
const radiusResult = await getPoint(radiusOptions);
// Clear preview
Engine.pcanvas.drawControl.previewGraphics.clear();
if (radiusResult.status === InputStatusEnum.OK) {
const radiusPoint = radiusResult.value;
const radius = Math.sqrt(
Math.pow(radiusPoint.x - this.center.x, 2) +
Math.pow(radiusPoint.y - this.center.y, 2)
);
const circle = new CircleEnt(this.center, radius);
circle.setDefaults();
Engine.pcanvas.addEntity(circle);
}
}
}Scenario 5: Allow Number Input
In some cases the user may input a number directly instead of clicking:
import { Engine } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum, writeMessage } from 'vjcad';
async function getDistanceOrPoint(): Promise<void> {
const options = new PointInputOptions("Enter offset distance or specify first point:");
options.allowNumberResult = true;
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
writeMessage(`<br/>First point: (${result.value.x}, ${result.value.y})`);
} else if (result.status === InputStatusEnum.IsNumber) {
const distance = result.numberValue;
writeMessage(`<br/>Offset distance: ${distance}`);
Engine.OFFSETDIST = distance;
}
}Scenario 6: Relative and Polar Coordinate Input
WebCAD automatically supports relative and polar coordinate formats:
// The user can enter in the command line:
// @100,50 - offset from previous point (dx=100, dy=50)
// @100<45 - distance 100 at angle 45 degrees from previous point
const options = new PointInputOptions("Specify next point or @x,y or @distance<angle:");
options.useBasePoint = true;
options.basePoint = lastPoint; // Reference point for relative input
const result = await getPoint(options);
// result.value is already the calculated absolute coordinateComplete Command Example: Drawing a Polyline
The following command combines multiple point-picking techniques:
import { Point2D, PolylineEnt, BulgePoint, Engine } from 'vjcad';
import {
getPoint,
PointInputOptions,
InputStatusEnum,
ssSetFirst,
writeMessage
} from 'vjcad';
export class PlineCommand {
private points: Point2D[] = [];
private stepNumber: number = 1;
private isClosed: boolean = false;
async main(): Promise<void> {
ssSetFirst([]);
Engine.undoManager.start_undoMark();
try {
while (this.stepNumber > 0) {
switch (this.stepNumber) {
case 1:
await this.getFirstPoint();
break;
case 2:
await this.getNextPoints();
break;
}
}
// Create polyline
if (this.points.length >= 2) {
this.createPolyline();
}
} finally {
Engine.undoManager.end_undoMark();
Engine.clearPreview();
}
}
private async getFirstPoint(): Promise<void> {
const options = new PointInputOptions("Specify start point:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
this.points.push(result.value);
this.stepNumber = 2;
} else {
this.stepNumber = 0;
}
}
private async getNextPoints(): Promise<void> {
const lastPoint = this.points[this.points.length - 1];
const options = new PointInputOptions(
`Specify next point [Close(C)/Undo(U)] <Finish>:`
);
options.keywords = ["C", "U"];
options.useBasePoint = true;
options.basePoint = lastPoint;
// Preview polyline
options.callback = (canvasPoint: Point2D) => {
const worldPoint = Engine.trans.CanvasToWcs(canvasPoint);
this.drawPreview(worldPoint);
};
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
this.points.push(result.value);
} else if (result.stringResult === "C") {
if (this.points.length >= 3) {
this.isClosed = true;
this.stepNumber = 0;
} else {
writeMessage("<br/>At least 3 points are required to close.");
}
} else if (result.stringResult === "U") {
if (this.points.length > 1) {
this.points.pop();
} else {
this.stepNumber = 1;
}
} else if (result.status === InputStatusEnum.EnterOrSpace) {
this.stepNumber = 0;
} else {
this.stepNumber = 0;
}
}
private drawPreview(currentPoint: Point2D): void {
if (this.points.length === 0) return;
const previewPoints = [...this.points, currentPoint];
const pline = new PolylineEnt();
for (const pt of previewPoints) {
pline.bulgePoints.add(new BulgePoint(new Point2D(pt.x, pt.y), 0));
}
pline.setDefaults();
Engine.pcanvas.drawControl.previewGraphics.clear();
Engine.pcanvas.drawControl.drawPreviewEntity(pline);
}
private createPolyline(): void {
const pline = new PolylineEnt();
for (const pt of this.points) {
pline.bulgePoints.add(new BulgePoint(new Point2D(pt.x, pt.y), 0));
}
pline.isClosed = this.isClosed;
pline.setDefaults();
Engine.pcanvas.addEntity(pline);
Engine.undoManager.added_undoMark([pline]);
writeMessage(`<br/>Created ${this.isClosed ? 'closed ' : ''}polyline with ${this.points.length} vertices.`);
}
}Best Practices
1. Always Check Return Status
// Good
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// Process point
} else if (result.status === InputStatusEnum.Cancel) {
return;
}
// Bad
const result = await getPoint(options);
const point = result.value; // Dangerous, may be invalid2. Clean Up Preview
try {
options.callback = (pt) => { /* draw preview */ };
const result = await getPoint(options);
} finally {
Engine.pcanvas.drawControl.previewGraphics.clear();
}3. Provide Meaningful Prompts
// Good
"Specify center [3P/2P/TTR]:"
// Bad
"Input:"4. Use Base Point for Rubber-Band Interaction
options.useBasePoint = true;
options.basePoint = previousPoint;Next Steps
- Entity Selection Guide - learn how to select entities
- Preview Drawing - understand the preview mechanism
- Command Patterns - learn common command design patterns