唯杰地图 logo唯杰地图
VJMAP
EN

transform

vjmapext/references/entity/transform.md 5.9 KB202 行
原文

本文回答:实体变换 —— move / rotate / scale / mirror + 组合用法。

一、四个方法(参数都是点对象,不是散坐标)

方法 签名 说明
move(from, to) move(Point2DLike, Point2DLike) 位移 = to − from
rotate(center, angle) rotate(Point2DLike, number) 绕点旋转,弧度,逆时针正
scale(center, factor) scale(Point2DLike, number) 以点为中心缩放
mirror(p1, p2) mirror(Point2DLike, Point2DLike) 沿 p1→p2 直线镜像

Point2DLike = Point2D | [number, number]——传 [x,y] 数组即可。

这四个方法内部已自动调 changed()(标记脏 + 通知 store),不需要手动再调。手动 changed() 只在直接改属性时需要。

line.move([0, 0], [200, 100]);   // 平移向量 (200,100);内部自动 changed()

二、平移

const line = new LineEnt([0, 0], [100, 0]);
mapcadLayer.addEntity(line);

line.move([0, 0], [200, 100]);   // → (200,100) → (300,100)

批量:

const dx = 200, dy = 100, origin = [0, 0];
selected.forEach(e => e.move(origin, [origin[0] + dx, origin[1] + dy]));
// 或更直观:e.move([0,0],[dx,dy])

三、旋转(弧度!)

entity.rotate([cx, cy], angleRad);   // 弧度,逆时针为正
  • 单位是弧度(内部 Math.cos(angle))——传 90 会转出 90 弧度而非 90°
  • Math.PI / 2 = 90°;deg * Math.PI / 180 换算
const rect = new PolylineEnt([[-50,-50],[50,-50],[50,50],[-50,50]], true);
mapcadLayer.addEntity(rect);

rect.rotate([0, 0], Math.PI / 4);    // 绕原点逆时针 45°

绕自身中心:

const bb = rect.boundingBox();       // [x1, y1, x2, y2]
const c = [(bb[0] + bb[2]) / 2, (bb[1] + bb[3]) / 2];
rect.rotate(c, 30 * Math.PI / 180);

四、缩放

entity.scale([cx, cy], factor);
  • factor > 1 放大;< 1 缩小;负值会把坐标镜像到中心另一侧(scaleFrom 不取 abs;仅 radius/height 等标量内部取 Math.abs)
  • 同时改变相对缩放中心的距离
circle.scale([0, 0], 2);     // 半径 ×2,圆心坐标也 ×2

只改大小不改位置:

const bb = circle.boundingBox();
circle.scale([(bb[0]+bb[2])/2, (bb[1]+bb[3])/2], 1.5);

五、镜像

entity.mirror([p1x, p1y], [p2x, p2y]);

沿两点的直线翻转。

轴 参数
X 轴(上下翻) mirror([0,0], [100,0])
Y 轴(左右翻) mirror([0,0], [0,100])
45° 对角线 mirror([0,0], [100,100])
const tri = new PolylineEnt([[0,0],[100,0],[50,80]], true);
mapcadLayer.addEntity(tri);

const mirrored = tri.clone();
mirrored.mirror([0, 0], [0, 100]);   // 沿 Y 轴镜像
mirrored.color = 0x4488ff;
mirrored.changed();                  // clone 后改属性,手动 changed
mapcadLayer.addEntity(mirrored);

六、组合变换

不满足交换律,顺序重要:

rect.rotate([0, 0], Math.PI / 6);
rect.move([0, 0], [200, 150]);
rect.scale([200, 150], 1.5);

七、克隆 + 变换 模式

保留原始实体:

const base = new CircleEnt([0, 0], 100);
mapcadLayer.addEntity(base);

for (let i = 1; i <= 4; i++) {
  const copy = base.clone();
  copy.move([0, 0], [200 * i, 0]);
  copy.color = 0xff0000 + i * 0x001000;
  copy.changed();
  mapcadLayer.addEntity(copy);
}

八、环形阵列

const base = new LineEnt([50, 0], [150, 0]);
base.color = 0x00ffff;

const N = 12;
for (let i = 0; i < N; i++) {
  const c = base.clone();
  c.rotate([0, 0], (2 * Math.PI / N) * i);
  mapcadLayer.addEntity(c);
}

九、矩阵阵列

function arrayPattern(src, rows, cols, dx, dy) {
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      if (r === 0 && c === 0) { mapcadLayer.addEntity(src); continue; }
      const cpy = src.clone();
      cpy.move([0, 0], [c * dx, r * dy]);
      mapcadLayer.addEntity(cpy);
    }
  }
}

十、角度单位速查(容易混)

API 单位
entity.rotate(center, angle) 弧度
ArcEnt 构造的 startAngle/endAngle 弧度
EllipseEnt 的 startAngle/endAngle/rotation 弧度
BlockRefEnt.rotation 弧度
TextEnt.rotation / MTextEnt.rotation 属性 度(仅这两个例外;rotate() 内部会 ×180/π 累加进去)

十一、变换撤销

⚠️ changed() 不会把变换记入撤销栈——它只标脏(isDirty=true)并通知 store 重渲染(entityChanged)。um.clear() 后裸调 entity.move(...),undoManager.canUndo 仍为 false。要撤销须改前先手动快照:

mapcadLayer.undoManager.recordModify(entity);  // 先快照
entity.move([0,0],[100,0]);                    // 再改
mapcadLayer.undoManager.undo();                // 撤销(快照还原,实体被替换为新对象——旧引用失效)

多个变换当一次记录(注意 recordModify 也要在事务内逐个调用):

mapcadLayer.undoManager.beginTransaction();
selected.forEach(e => { mapcadLayer.undoManager.recordModify(e); e.rotate([cx, cy], Math.PI / 2); });
mapcadLayer.undoManager.endTransaction();

十二、常见坑

  1. rotate 传了角度数 → 90° 写成 90 会转 90 弧度(≈5156°);记住 deg * Math.PI/180。
  2. move 传 (dx, dy) → 签名是 move(from, to) 两个点,位移 = to − from;平移向量写法 move([0,0],[dx,dy])。
  3. scale 中心选错 → 实体跑到屏幕外;选自身 boundingBox 中心。
  4. mirror 两点太近 → 轴线方向不稳定;拉开点距。
  5. 在 store 迭代中 move 选中项 → 先 .slice() 一份数组再遍历。

相关

  • 交互式编辑命令 → drawing/builtin-edit(无独立 ROTATE 命令,但 SCALE 命令带 [旋转(R)] 分支可交互旋转;程序化旋转用实体方法)
  • Undo / Redo → drawing/undo-redo