rm-wall-snap-3d
v1.1.47
Published
3D物体墙体吸附与位置修正引擎 - 支持家具、电源等自动适配墙体
Maintainers
Readme
rm-wall-snap-3d
3D物体墙体吸附与位置修正引擎 - 支持家具、电源等自动适配墙体
安装
npm install rm-wall-snap-3d使用
基础用法
import { snapToWalls } from 'rm-wall-snap-3d';
// 只需要传入 items 和 walls
const response = snapToWalls({
items: [
{
obj_id: 'socket_001',
category: 'socket',
box: {
isBox3: true,
min: { x: 1.0, y: 2.0, z: 1.4 },
max: { x: 1.085, y: 2.085, z: 1.485 }
},
position: { x: 1.0425, y: 2.0425, z: 1.4425 },
size: { x: 0.085, y: 0.085, z: 0.085 },
contour: [...], // 轮廓点数组
direction: { x: 0, y: 1, z: 0 }
}
],
walls: [
{
center: { x: 0, y: 2.1, z: 1.5 },
normal: { x: 0, y: -1, z: 0 },
innerSurface: { x: 0, y: 2.0, z: 1.5 },
outerSurface: { x: 0, y: 2.2, z: 1.5 }
}
]
});
// 处理结果
if (response.success) {
console.log(response.message);
response.data.results.forEach(result => {
if (result.snapped) {
console.log(`✅ ${result.obj_id} 吸附成功`);
console.log('新位置:', result.position); // 吸附后的中心位置
console.log('新轮廓:', result.contour); // 吸附后的轮廓点
console.log('新朝向:', result.direction); // 墙面内侧方向
}
});
}返回数据格式
{
success: true,
message: "吸附完成: 3/3 个物品成功吸附",
data: {
results: [
{
obj_id: "socket_001", // 物体的唯一标识(保持不变)
category: "socket", // 物体类型(保持不变)
snapped: true, // 是否成功吸附
wallIndex: 0, // 吸附到的墙面索引
distance: 0.015, // 与墙面的距离(米)
// 以下字段保持原始值不变
box: {
isBox3: true,
min: { x: 1.0, y: 2.0, z: 1.4 },
max: { x: 1.085, y: 2.085, z: 1.485 }
},
size: { x: 0.085, y: 0.085, z: 0.085 },
// 以下字段根据吸附结果更新
position: { x: 1.0425, y: 2.015, z: 1.4425 }, // 吸附后的新位置
contour: [...], // 吸附后的新轮廓点数组
direction: { x: 0, y: -1, z: 0 } // 墙面内侧方向
}
]
}
}数据格式
Item(物品)
interface IItemInfo {
obj_id: string; // 唯一标识
category: string; // 类型:socket, switch, cabinet, air conditioner
box: {
isBox3: boolean;
min: { x: number, y: number, z: number }; // 最小点
max: { x: number, y: number, z: number }; // 最大点
};
position: { x: number, y: number, z: number }; // 中心位置
size: { x: number, y: number, z: number }; // 尺寸
contour: { x: number, y: number, z: number }[]; // 轮廓点数组
direction: { x: number, y: number, z: number }; // 朝向
}Wall(墙体)
interface IWallInfo {
center: { x: number, y: number, z: number }; // 墙面中心点
normal: { x: number, y: number, z: number }; // 墙面法向量(朝向)
innerSurface: { x: number, y: number, z: number }; // 内表面位置
outerSurface: { x: number, y: number, z: number }; // 外表面位置
}Result(结果)
interface ISnapResult {
obj_id: string; // 物体唯一标识(不变)
category: string; // 物体类型(不变)
snapped: boolean; // 是否成功吸附
wallIndex: number; // 吸附到的墙面索引
distance: number; // 与墙面的距离
// 保持不变
box: { isBox3: boolean; min: IVector3; max: IVector3 };
size: { x: number; y: number; z: number };
// 根据吸附更新
position: { x: number; y: number; z: number }; // 新位置
contour: { x: number; y: number; z: number }[]; // 新轮廓
direction: { x: number; y: number; z: number }; // 新朝向
}支持的物品类型
| 类型 | 行为 | 偏移量 | |------|------|--------| | socket(电源) | 贴墙 | 3mm | | switch(开关) | 贴墙 | 3mm | | air conditioner(空调) | 贴墙 | 3mm | | cabinet(柜子) | 贴墙 | 2cm |
字段说明
输入字段
| 字段 | 说明 | 示例 |
|------|------|------|
| obj_id | 物体的唯一标识符 | "socket_001" |
| category | 物体类型 | "socket", "switch", "cabinet" |
| box | 包围盒,用于碰撞检测 | { isBox3: true, min: {...}, max: {...} } |
| position | 物体中心点坐标 | { x: 1.0, y: 2.0, z: 1.4 } |
| size | 物体的长宽高 | { x: 0.085, y: 0.085, z: 0.085 } |
| contour | 底面轮廓点数组 | [{x, y, z}, ...] |
| direction | 物体的朝向向量 | { x: 0, y: 1, z: 0 } |
输出字段
| 字段 | 变化 | 说明 |
|------|------|------|
| obj_id | ❌ 不变 | 物体的唯一标识符 |
| category | ❌ 不变 | 物体类型 |
| box | ❌ 不变 | 原始包围盒 |
| size | ❌ 不变 | 原始尺寸 |
| position | ✅ 更新 | 吸附后的新位置 |
| contour | ✅ 更新 | 吸附后的新轮廓点 |
| direction | ✅ 更新 | 墙面内侧方向 |
| snapped | ✅ 新增 | 是否成功吸附 |
| wallIndex | ✅ 新增 | 吸附到的墙面索引 |
| distance | ✅ 新增 | 与墙面的距离 |
前端使用示例
import { snapToWalls } from 'rm-wall-snap-3d';
// 准备数据
const items = [
{
obj_id: 'socket_001',
category: 'socket',
box: { isBox3: true, min: {...}, max: {...} },
position: { x: 1.0, y: 2.0, z: 1.4 },
size: { x: 0.085, y: 0.085, z: 0.085 },
contour: [...],
direction: { x: 0, y: 1, z: 0 }
}
];
const walls = [...]; // 墙体数据
// 调用吸附
const result = snapToWalls({ items, walls });
// 更新前端物体
result.data.results.forEach(item => {
if (item.snapped) {
// 更新位置
object.position.set(item.position.x, item.position.y, item.position.z);
// 更新朝向
object.rotation.z = Math.atan2(item.direction.y, item.direction.x);
// 使用 contour 绘制 2D 轮廓
drawContour(item.contour);
}
});注意事项
- box 和 size 保持不变:用于碰撞检测和显示尺寸
- position 更新:吸附后的新中心位置
- contour 更新:吸附后的新轮廓点,用于 2D 绘制
- direction 更新:指向墙面内侧的方向向量
- 坐标系:使用右手坐标系,Z轴向上
License
MIT
