transform
本文回答:实体变换 ——
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();
十二、常见坑
- rotate 传了角度数 → 90° 写成 90 会转 90 弧度(≈5156°);记住
deg * Math.PI/180。 - move 传
(dx, dy)→ 签名是move(from, to)两个点,位移 =to − from;平移向量写法move([0,0],[dx,dy])。 - scale 中心选错 → 实体跑到屏幕外;选自身 boundingBox 中心。
- mirror 两点太近 → 轴线方向不稳定;拉开点距。
- 在 store 迭代中 move 选中项 → 先
.slice()一份数组再遍历。
相关
- 交互式编辑命令 → drawing/builtin-edit(无独立
ROTATE命令,但SCALE命令带[旋转(R)]分支可交互旋转;程序化旋转用实体方法) - Undo / Redo → drawing/undo-redo