grip
本文回答:夹点编辑 ——
GripManager/GripPointDef/GripType/ 实体的getGripPoints+gripEdit(全部已对照vjmapext.d.ts与运行时核实)。
一、夹点是什么
选中实体后,实体上显示的可拖拽小图形。拖它 = 修改实体几何(AutoCAD 的 Grip)。
◆
────────◆────▲────◆────▲────
(起点) (中点) (终点) (中点)
◆
▲(三角形)= mid 边中点,◆/■ = 端点/顶点,● = 圆心。按住拖拽即修改。
二、工作方式(与文档旧版不同的真实模型)
- 挂在
mapcadLayer.gripManager(GripManager)。输入状态机命中夹点(hitTestGrip)后进入grip_drag:拖动过程实时改实体(走GripManager.gripEdit(entityId, cadPos, gripKey)),松开时包进一次 undo 事务。 - 夹点只在默认模式下出现:
getActiveGrips()在browse模式或输入非idle态时返回[];editable === false的实体也被跳过。没有enabled开关属性——要抑制夹点,把图层切到browse模式(见 mode)、不选中实体,或设editable = false。 - 拖拽期间没有对象捕捉、也没有预览实体:每次鼠标移动直接用原始光标的 CAD 坐标调
gripEdit改真实实体(捕捉只在getPoint的点输入状态生效),松开时再gripEdit一次并previewManager.clear()。要精确落点请用命令(MOVE/STRETCH 等走getPoint,带捕捉)。
三、GripPointDef / GripProvider(自定义实体按这个实现)
interface GripPointDef {
key: string; // 夹点身份串(自定义实体自取,如 "start"/"mid_0"/"base")
position: Point2D; // 位置(CAD 坐标)
type: GripType; // 视觉形状提示
}
interface GripProvider {
getGripPoints(entity: EntityBase): GripPointDef[];
gripEdit(entity: EntityBase, newPos: Point2DLike, gripKey: string): void;
}
模型是「key 分发」不是「每夹点一个回调」:getGripPoints() 只声明有哪些夹点(key+位置+形状);拖到新位置时框架回调实体自身的 gripEdit(newPos, gripKey)(EntityBase.gripEdit),由它按 gripKey 决定改哪段几何。改完内部 changed()。
四、GripType → GripShape(渲染形状由 gripTypeToShape 映射)
| GripType | 形状 | 典型用途 |
|---|---|---|
vertex |
■ square | 顶点(移动单点) |
endpoint |
■ square | 端点 |
mid |
▲ triangle | 边中点(拖动=整边平移/插点) |
center |
● circle | 圆心(拖动=整体移动) |
quad |
◆ diamond | 象限点(缩放/半径类控制) |
insertion |
◆ diamond | 插入点(文字/块参照) |
rotation |
● circle | 旋转控制 |
scale |
◆ diamond | 缩放控制 |
注意是小写字符串联合,不存在 VERTEX/MIDPOINT/RADIUS/CONTROL 这类枚举成员。
五、内置实体的夹点(key 约定)
| 实体 | 夹点 key | 行为 |
|---|---|---|
PointEnt |
base |
移动点位置 |
LineEnt |
start / end / mid |
动端点 / 中点拖=整体平移 |
PolylineEnt |
'0'…'N'(顶点,纯索引字符串)+ mid_0…(边中点) |
顶点动点、中点整边平移 |
SplineEnt |
'0'…'N' + mid_0…(作用于 activePoints) |
同上 |
CircleEnt |
center / quad0…3 |
圆心平移;象限点拖=改半径 |
ArcEnt |
start / end / center / mid |
起终点、圆心平移、弧中点 |
EllipseEnt |
center / major / minor |
圆心、长轴端、短轴端 |
TextEnt/MTextEnt |
ins |
插入点 |
BlockRefEnt |
insertion / rotation / scale |
插入点、旋转角、缩放 |
MLeaderEnt |
arrow / vtx{i} / landing |
箭头点、折线顶点、落点 |
LinearDimEnt/AlignedDimEnt |
start/end/dimLine |
两界线原点 + 尺寸线位置 |
AngleDimEnt |
vertex/start/end/arc |
顶点、两边端点、弧位置 |
RadialDimEnt |
center/chord |
圆心、弦点 |
HatchEnt |
${loop}_${i} + center |
各环顶点 + 整体中心 |
六、自定义实体加夹点(正确写法)
class StarEnt extends vjmapext.EntityBase {
constructor(center, outerR, innerR) {
super();
this.center = center.clone();
this.outerR = outerR;
this.innerR = innerR;
}
// ① 声明夹点:key + position + type
getGripPoints() {
return [
{ key: 'center', position: this.center, type: 'center' },
{ key: 'outer', position: new vjmapext.Point2D(this.center.x + this.outerR, this.center.y), type: 'quad' },
{ key: 'inner', position: new vjmapext.Point2D(this.center.x + this.innerR, this.center.y), type: 'quad' },
];
}
// ② 按 key 应用新几何(框架自动包 undo 事务 + changed())
gripEdit(newPos, gripKey) {
const p = new vjmapext.Point2D(newPos.x ?? newPos[0], newPos.y ?? newPos[1]);
if (gripKey === 'center') this.center = p;
else if (gripKey === 'outer') this.outerR = p.distanceTo(this.center);
else if (gripKey === 'inner') this.innerR = Math.min(p.distanceTo(this.center), this.outerR - 0.1);
this.changed();
}
}
⚠️
GripPointDef的字段是position(不是point)、key(不是回调onMove);gripEdit的 newPos 是Point2DLike(Point2D实例或[x,y]数组——不能传{x,y}字面量)。toPoint2D只认instanceof Point2D和元组,传{x,y}会取p[0]/p[1]=undefined 再被Point2D构造的x=0默认值兜底——静默得到 (0,0),夹点直接跳到原点(不是 NaN)。
七、GripManager 直接可用 API
gripManager.getActiveGrips(): GripPoint[] // 当前选集的全部夹点 {key,position,type,entityId}
gripManager.getGripFeatures(): GeoJSON.Feature[] // 渲染用 feature(框架内部用)
gripManager.gripEdit(entityId, newCadPos, gripKey) // 程序化拖一个夹点(→ entity.gripEdit)
gripManager.hitTestGrip(point:{x,y}, map): GripPoint | undefined // 屏幕点命中测试
⚠️
hitTestGrip不是几何距离判定——它是对已渲染的 grip 图层做queryRenderedFeatures(±10px 包围盒,再校验_meta===GRIP)。所以夹点图层还没渲染完成 / 坐标偏离超过 10px 时都返回undefined;自动化脚本里要先确认选集夹点已上屏再测。
程序化触发夹点编辑(不走鼠标)可直接 gripManager.gripEdit(ent.id, [x, y], 'center')——注意必须传 [x,y] 元组或 Point2D,不能传 {x,y} 字面量(见下坑位)。
⚠️ 程序化
gripManager.gripEdit不落 undo——undo 记录发生在InputManager拖拽路径(拖动开始时recordModify),直接调gripEdit只改几何+changed(),undoStack不变,之后undo()也不会回退这次修改。要可撤销就自己包undoManager.recordModify(entity)+endTransaction(),或改用 transform 命令。
八、连续夹点模式
AutoCAD「选中夹点→选动作」两步法在 vjmapext 不做——夹点只做几何修改(移动/改半径/动顶点)。要移动/旋转/缩放整体用 entity/transform 的 move/rotate/scale/mirror。
九、常见坑
- gripEdit 里不改几何/不调 changed():画面不刷新,感觉夹点失效。
- position 必须在合理位置(如 Arc 中点要在弧上):位置不合理看起来会乱跑。
getGripPoints返回空数组:合法,不显示夹点。- browse 模式下不出现夹点:
getActiveGrips()直接返回[]——夹点不显示先查当前模式。 - onMove 越界:
gripEdit里自己做 clamp(如innerR < outerR - eps)。
相关
- 命令与输入 → custom-command
- 变换(move/rotate/scale/mirror) → entity/transform
- 自定义实体 → entity/custom-entity