import * as THREE from 'three' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemJson } from '@/model/WorldModelType.ts' import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts' import type Viewport from '@/designer/Viewport.ts' import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh' import { Vector2 } from 'three/src/math/Vector2' import type Toolbox from '@/model/itemType/Toolbox.ts' export function quickCopyByMouse() { // 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport const currentMouseInfo = window['CurrentMouseInfo'] if (!currentMouseInfo?.viewport || !currentMouseInfo.x || !currentMouseInfo.z) { system.msg('无法获取鼠标位置') return } const x = currentMouseInfo.x const z = currentMouseInfo.z const viewport: Viewport = currentMouseInfo.viewport // const point: THREE.Vector2 = currentMouseInfo.mouse // // const ray = new THREE.Raycaster() // ray.setFromCamera(point, viewport.camera) // const intersections = ray.intersectObjects(viewport.dragControl._dragObjects, true) // // if (intersections.length === 0) { // system.msg('没有找到可复制的对象') // return // } // console.log('intersections:', intersections) // 如果不在线上,查找0.2米内的有效点 Object3D, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连 findObject3DByCondition(viewport.scene, object => { // 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象 if (object instanceof THREE.Object3D && object.visible && object.userData.type && viewport.toolbox[object.userData.type]) { const toolbox: Toolbox = viewport.toolbox[object.userData.type] // 检查是否在 0.2 米内 const distance = object.position.distanceTo(new THREE.Vector3(x, 0, z)) if (distance < 0.2) { // 找到一个有效点,执行复制操作 viewport.toolStartObject = object viewport.state.cursorMode = object.userData.type // toolbox.start(object) system.msg('连线成功') return true } } return false }) } // // /** // * 查找射线周围指定半径内的对象 // */ // export function findObjectsInRadius(viewport: Viewport, // point: THREE.Vector2, // radius: number, // lines: { object: THREE.Object3D, distance: number }[], // points: { object: THREE.Object3D, distance: number }[] // ): void { // const ray = new THREE.Raycaster() // ray.setFromCamera(point, viewport.camera) // // viewport.dragControl._dragObjects.forEach(obj => { // if (obj instanceof THREE.Points) { // // 处理点云:遍历每个点 // const distance = distanceToRay(ray, point) // if (distance <= radius) { // points.push({ object: obj, distance }) // } // // } else if (obj instanceof THREE.Line) { // // 处理线段:计算线段到射线的最近距离 // const distance = getLineDistanceToRay(ray, obj) // if (distance <= radius) { // lines.push({ object: obj, distance }) // } // } // }) // } // // /** // * 计算点到射线的最短距离 // */ // function distanceToRay(ray: THREE.Raycaster, point: THREE.Vector2) { // const closestPoint = new THREE.Vector3() // ray.closestPointToPoint(point, closestPoint) // return point.distanceTo(closestPoint) // } // // /** // * 计算线段到射线的最短距离 // */ // function getLineDistanceToRay(ray: THREE.Raycaster, line: THREE.Line) { // const lineStart = new THREE.Vector3() // const lineEnd = new THREE.Vector3() // line.geometry.attributes.position.getXYZ(0, lineStart) // line.geometry.attributes.position.getXYZ(1, lineEnd) // line.localToWorld(lineStart) // line.localToWorld(lineEnd) // // const lineSegment = new THREE.Line3(lineStart, lineEnd) // const closestOnRay = new THREE.Vector3() // const closestOnLine = new THREE.Vector3() // THREE.Line3.prototype.closestPointsRayLine ??= function(ray, line, closestOnRay, closestOnLine) { // // 实现射线与线段最近点计算(需自定义或使用数学库) // } // // lineSegment.closestPointsRayLine(ray, true, closestOnRay, closestOnLine) // return closestOnRay.distanceTo(closestOnLine) // } /** * 考虑吸附的情况下计算鼠标事件位置 */ export function calcPositionUseSnap(e: MouseEvent, point: THREE.Vector3) { // 按下 ctrl 键,不启用吸附,其他情况启用吸附 const gridOption = worldModel.gridOption if (!e.ctrlKey && !e.metaKey) { if (gridOption.snapEnabled && gridOption.snapDistance > 0) { // 启用吸附, 针对 point 的 x 和 z 坐标进行吸附, 吸附距离为 gridOption.snapDistance const snapDistance = gridOption.snapDistance const newPoint = new THREE.Vector3(point.x, point.y, point.z) newPoint.x = Math.round(newPoint.x / snapDistance) * snapDistance newPoint.z = Math.round(newPoint.z / snapDistance) * snapDistance return newPoint } } return point } /** * 在给定的场景中查找具有指定 uuid 的 Object3D 对象 */ export function findObject3DById(scene: THREE.Object3D, uuid: string): THREE.Object3D | undefined { const rets = findObject3DByCondition(scene, object => object.uuid === uuid) if (rets.length > 0) { return rets[0] } return undefined } /** * 在给定场景中查找满足特定条件的 Object3D 对象集合 */ export function findObject3DByCondition(scene: THREE.Object3D, condition: (object: THREE.Object3D) => boolean): THREE.Object3D[] { const foundObjects: THREE.Object3D[] = [] // 定义一个内部递归函数来遍历每个节点及其子节点 function traverse(obj: THREE.Object3D) { if (condition(obj)) { foundObjects.push(obj) } // 遍历当前对象的所有子对象 for (let i = 0; i < obj.children.length; i++) { traverse(obj.children[i]) } } // 开始从场景根节点进行遍历 traverse(scene) return foundObjects } export function loadSceneFromJson(viewport: Viewport, scene: THREE.Scene, items: ItemJson[]) { console.time('loadSceneFromJson') const object3ds: THREE.Object3D[] = [] // beforeLoad 通知所有加载的对象, 模型加载开始 getAllItemTypes().forEach((itemType: ItemTypeDefineOption) => { const ret = itemType.clazz.beforeLoad() Array.isArray(ret) && object3ds.push(...ret) }) const loads = loadObject3DFromJson(items) Array.isArray(loads) && object3ds.push(...loads) // afterLoadComplete 通知所有加载的对象, 模型加载完成 getAllItemTypes().forEach((itemType: ItemTypeDefineOption) => { const ret = itemType.clazz.afterLoadComplete(object3ds) Array.isArray(ret) && object3ds.push(...ret) }) scene.add(...object3ds) // afterAddScene 通知所有加载的对象, 模型加载完成 getAllItemTypes().forEach(itemType => { itemType.clazz.afterAddScene(viewport, scene, object3ds) }) console.log('loadSceneFromJson:', items.length, 'items,', object3ds.length, 'objects') console.timeEnd('loadSceneFromJson') } function loadObject3DFromJson(items: ItemJson[]): THREE.Object3D[] { const result: THREE.Object3D[] = [] for (const item of items) { if (!item || !item.t) { console.error('unkown item:', item) continue } const object3D: THREE.Object3D | undefined = getItemTypeByName(item.t)?.clazz.loadFromJson(item) if (object3D === undefined) { continue } if (_.isArray(item.items)) { // 如果有子元素,递归处理 const children = loadObject3DFromJson(item.items) children.forEach(child => object3D.add(child)) } result.push(object3D) } return result }