唯杰地图 logo唯杰地图
VJMAP
EN

grip

vjmapext/references/drawing/grip.md 8.0 KB140 行
原文

本文回答:夹点编辑 —— 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)。

详见 entity/custom-entity。

七、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。

九、常见坑

  1. gripEdit 里不改几何/不调 changed():画面不刷新,感觉夹点失效。
  2. position 必须在合理位置(如 Arc 中点要在弧上):位置不合理看起来会乱跑。
  3. getGripPoints 返回空数组:合法,不显示夹点。
  4. browse 模式下不出现夹点:getActiveGrips() 直接返回 []——夹点不显示先查当前模式。
  5. onMove 越界:gripEdit 里自己做 clamp(如 innerR < outerR - eps)。

相关