点拾取指南
大约 7 分钟
点拾取指南
点拾取是 CAD 命令中最基础也是最重要的用户交互方式。本章详细介绍如何使用 getPoint() 函数获取用户输入的坐标点。
基本用法
简单点拾取
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
async function getOnePoint(): Promise<void> {
const options = new PointInputOptions("指定点:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
const point = result.value;
console.log(`点坐标: (${point.x}, ${point.y})`);
}
}PointInputOptions 完整属性
PointInputOptions 类提供丰富的配置选项:
const options = new PointInputOptions("提示消息");
// 基本属性
options.message = "指定点 [选项(A)/选项(B)]:"; // 提示消息
options.keywords = ["A", "B"]; // 关键字列表
// 基点与橡皮筋线
options.useBasePoint = true; // 启用基点
options.basePoint = new Point2D(0, 0); // 基点坐标
// 对象捕捉
options.useOsnap = true; // 启用对象捕捉
options.forceOsmode = 128; // 强制捕捉模式
options.useForceOsmode = false; // 是否使用强制模式
// 正交与极坐标
options.useOrthoMode = true; // 使用正交模式
options.forceOrthomode = false; // 强制正交
options.ignoreOrthomode = false; // 忽略正交
options.ignorePolarMode = false; // 忽略极坐标
// 角度控制
options.useforceAngle = false; // 使用强制角度
options.forceAngle = 0; // 强制角度值
// 输入控制
options.allowNumberResult = false; // 允许数字输入
options.defaultKeyword = undefined; // 默认关键字
// 回调函数
options.callback = (canvasPoint, originalPoint) => {
// 鼠标移动时调用,用于预览
};
// 显示控制
options.hideRubberBand = false; // 隐藏橡皮筋线
options.hideCrossHairCursor = false; // 隐藏十字光标
// 自动化
options.isAutomation = false; // 自动化模式
options.automateStr = ""; // 自动化输入字符串
// 多点追踪
options.isMtpMode = false; // 多点追踪模式常用属性详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
message | string | "指定点:" | 命令行提示消息 |
keywords | string[] | [] | 可用关键字列表,如 ["U", "C"] |
useBasePoint | boolean | false | 是否显示从基点到光标的橡皮筋线 |
basePoint | Point2D | (0,0) | 橡皮筋线的起始点 |
useOsnap | boolean | true | 是否启用对象捕捉 |
callback | function | undefined | 鼠标移动时的回调函数 |
allowNumberResult | boolean | false | 是否允许用户直接输入数字 |
InputStatusEnum 状态枚举
getPoint() 返回的结果中,status 字段表示用户操作的类型:
const result = await getPoint(options);
switch (result.status) {
case InputStatusEnum.OK:
// 用户成功输入了一个点
const point = result.value;
break;
case InputStatusEnum.Cancel:
// 用户按 ESC 或右键取消
break;
case InputStatusEnum.Keyword:
// 用户输入了关键字
const keyword = result.stringResult;
break;
case InputStatusEnum.EnterOrSpace:
// 用户按 Enter 或空格(通常表示使用默认值或结束)
break;
case InputStatusEnum.IsNumber:
// 用户输入了数字(需设置 allowNumberResult = true)
const number = result.numberValue;
break;
case InputStatusEnum.None:
// 无输入
break;
case InputStatusEnum.Error:
// 发生错误
break;
}| 状态 | 值 | 说明 |
|---|---|---|
OK | 5100 | 成功获取点坐标 |
Cancel | -5002 | 用户取消操作 |
Keyword | -5005 | 用户输入了关键字 |
EnterOrSpace | 3 | 用户按回车或空格 |
IsNumber | 4 | 用户输入了数字 |
None | 5000 | 无输入 |
Error | -5001 | 发生错误 |
点拾取场景示例
场景1:单点拾取
最简单的点拾取,获取一个坐标:
import {
getPoint,
PointInputOptions,
InputStatusEnum,
writeMessage
} from 'vjcad';
async function pickSinglePoint(): Promise<Point2D | null> {
const options = new PointInputOptions("指定点:");
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
writeMessage(`<br/>点: (${result.value.x.toFixed(2)}, ${result.value.y.toFixed(2)})`);
return result.value;
}
return null;
}场景2:带基点的连续点拾取(橡皮筋线)
绘制直线时,需要显示从上一点到当前光标的橡皮筋线:
import { Point2D } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
class LineDrawer {
private points: Point2D[] = [];
async drawLines(): Promise<void> {
// 获取第一点
const firstOptions = new PointInputOptions("指定第一点:");
const firstResult = await getPoint(firstOptions);
if (firstResult.status !== InputStatusEnum.OK) return;
this.points.push(firstResult.value);
// 连续获取后续点
while (true) {
const lastPoint = this.points[this.points.length - 1];
const nextOptions = new PointInputOptions("指定下一点 [撤销(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);
// 创建线段...
} else if (nextResult.stringResult === "U") {
// 撤销上一点
if (this.points.length > 1) {
this.points.pop();
}
} else {
// 用户取消或按回车结束
break;
}
}
}
}场景3:带关键字选项的点拾取
支持用户选择不同选项:
async function getPointWithOptions(): Promise<void> {
const options = new PointInputOptions("指定点 [撤销(U)/闭合(C)/完成(F)]:");
options.keywords = ["U", "C", "F"];
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// 用户点击了一个点
console.log('点:', result.value);
} else if (result.status === InputStatusEnum.Keyword) {
// 用户输入了关键字
switch (result.stringResult.toUpperCase()) {
case "U":
console.log('撤销');
break;
case "C":
console.log('闭合');
break;
case "F":
console.log('完成');
break;
}
} else if (result.status === InputStatusEnum.EnterOrSpace) {
// 用户按回车,可能使用默认选项
console.log('使用默认');
}
}场景4:带预览回调的点拾取
绘制圆时,实时预览圆的大小:
import { Point2D, CircleEnt, Engine } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum } from 'vjcad';
class CircleDrawer {
private center: Point2D = new Point2D();
async drawCircle(): Promise<void> {
// 获取圆心
const centerOptions = new PointInputOptions("指定圆心:");
const centerResult = await getPoint(centerOptions);
if (centerResult.status !== InputStatusEnum.OK) return;
this.center = centerResult.value;
// 获取半径点(带预览)
const radiusOptions = new PointInputOptions("指定半径:");
radiusOptions.useBasePoint = true;
radiusOptions.basePoint = this.center;
// 设置预览回调
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);
// 清除预览
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);
}
}
}场景5:允许数字输入
某些情况下,用户可以直接输入数值而非点击:
import { Engine } from 'vjcad';
import { getPoint, PointInputOptions, InputStatusEnum, writeMessage } from 'vjcad';
async function getDistanceOrPoint(): Promise<void> {
const options = new PointInputOptions("输入偏移距离 或 指定第一点:");
options.allowNumberResult = true; // 允许数字输入
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// 用户点击了一个点
writeMessage(`<br/>第一点: (${result.value.x}, ${result.value.y})`);
} else if (result.status === InputStatusEnum.IsNumber) {
// 用户输入了数字
const distance = result.numberValue;
writeMessage(`<br/>偏移距离: ${distance}`);
Engine.OFFSETDIST = distance; // 保存到系统变量
}
}场景6:相对坐标和极坐标输入
WebCAD 自动支持相对坐标和极坐标输入格式:
// 用户可以在命令行输入:
// @100,50 - 相对于上一点的偏移 (dx=100, dy=50)
// @100<45 - 相对于上一点,距离100,角度45度
const options = new PointInputOptions("指定下一点 或 @x,y 或 @距离<角度:");
options.useBasePoint = true;
options.basePoint = lastPoint; // 相对坐标的参考点
const result = await getPoint(options);
// result.value 已经是计算后的绝对坐标完整命令示例:多段线绘制
以下是一个综合运用各种点拾取技术的多段线命令:
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;
}
}
// 创建多段线
if (this.points.length >= 2) {
this.createPolyline();
}
} finally {
Engine.undoManager.end_undoMark();
Engine.clearPreview();
}
}
private async getFirstPoint(): Promise<void> {
const options = new PointInputOptions("指定起点:");
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(
`指定下一点 [闭合(C)/撤销(U)] <完成>:`
);
options.keywords = ["C", "U"];
options.useBasePoint = true;
options.basePoint = lastPoint;
// 预览多段线
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/>至少需要3个点才能闭合。");
}
} 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/>已创建${this.isClosed ? '闭合' : ''}多段线,${this.points.length}个顶点。`);
}
}最佳实践
1. 始终检查返回状态
// 好的写法
const result = await getPoint(options);
if (result.status === InputStatusEnum.OK) {
// 处理点
} else if (result.status === InputStatusEnum.Cancel) {
return; // 优雅退出
}
// 不好的写法
const result = await getPoint(options);
const point = result.value; // 危险!可能是无效数据2. 清理预览
try {
// 带预览的点拾取
options.callback = (pt) => { /* 绘制预览 */ };
const result = await getPoint(options);
} finally {
// 确保清理预览
Engine.pcanvas.drawControl.previewGraphics.clear();
}3. 提供有意义的提示
// 好的提示
"指定圆心 [三点(3P)/两点(2P)/切点切点半径(T)]:"
// 不好的提示
"输入:"4. 使用基点实现橡皮筋线
// 绘制连续图形时必须设置基点
options.useBasePoint = true;
options.basePoint = previousPoint;