Browse Source

线条变成 Mesh

master
修宁 7 months ago
parent
commit
9365a6a35a
  1. 3
      package.json
  2. 12
      pnpm-lock.yaml
  3. 2
      src/designer/model2DEditor/EsDragControls.ts
  4. 12
      src/designer/model2DEditor/tools/MouseMoveInspect.ts
  5. 80
      src/model/ModelUtils.ts
  6. 3
      src/model/itemType/ItemType.ts
  7. 13
      src/model/itemType/ItemTypeLine.ts
  8. 4
      src/model/itemType/ToolboxLine.ts
  9. 39
      src/model/itemType/measure/Measure.ts
  10. 2
      src/model/itemType/measure/MeasureToolbox.ts

3
package.json

@ -56,6 +56,7 @@
"vite-plugin-vue-devtools": "^7.7.2", "vite-plugin-vue-devtools": "^7.7.2",
"vue-tsc": "^2.2.8", "vue-tsc": "^2.2.8",
"three": "^0.176.0", "three": "^0.176.0",
"camera-controls": "2.10.1" "camera-controls": "2.10.1",
"three-mesh-bvh": "^0.9.0"
} }
} }

12
pnpm-lock.yaml

@ -126,6 +126,9 @@ importers:
three: three:
specifier: ^0.176.0 specifier: ^0.176.0
version: 0.176.0 version: 0.176.0
three-mesh-bvh:
specifier: ^0.9.0
version: 0.9.0(three@0.176.0)
typescript: typescript:
specifier: ~5.8.0 specifier: ~5.8.0
version: 5.8.3 version: 5.8.3
@ -1490,6 +1493,11 @@ packages:
resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==}
engines: {node: '>=16'} engines: {node: '>=16'}
three-mesh-bvh@0.9.0:
resolution: {integrity: sha512-xAwZj0hZknpwVsdK5BBJTIAZDjDPZCRzURY1o+z/JHBON/jc2UetK1CzPeQZiiOVSfI4jV2z7sXnnGtgsgnjaA==}
peerDependencies:
three: '>= 0.159.0'
three@0.176.0: three@0.176.0:
resolution: {integrity: sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==} resolution: {integrity: sha512-PWRKYWQo23ojf9oZSlRGH8K09q7nRSWx6LY/HF/UUrMdYgN9i1e2OwJYHoQjwc6HF/4lvvYLC5YC1X8UJL2ZpA==}
@ -2978,6 +2986,10 @@ snapshots:
dependencies: dependencies:
copy-anything: 3.0.5 copy-anything: 3.0.5
three-mesh-bvh@0.9.0(three@0.176.0):
dependencies:
three: 0.176.0
three@0.176.0: {} three@0.176.0: {}
tinyglobby@0.2.13: tinyglobby@0.2.13:

2
src/designer/model2DEditor/EsDragControls.ts

@ -9,7 +9,7 @@ import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
let dragStartFn, dragFn, dragEndFn, clickblankFn let dragStartFn, dragFn, dragEndFn, clickblankFn
export default class EsDragControls { export default class EsDragControls {
protected _dragObjects: THREE.Object3D[] = [] // 拖拽对象 _dragObjects: THREE.Object3D[] = [] // 拖拽对象
dragControls: any dragControls: any
private onDownPosition: { x: number; y: number } = { x: -1, y: -1 } private onDownPosition: { x: number; y: number } = { x: -1, y: -1 }

12
src/designer/model2DEditor/tools/MouseMoveInspect.ts

@ -1,5 +1,6 @@
import type Viewport from '@/designer/Viewport.ts' import type Viewport from '@/designer/Viewport.ts'
import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts' import type { ITool } from '@/designer/model2DEditor/tools/ITool.ts'
import * as THREE from 'three'
let pmFn, otFn, lvFn let pmFn, otFn, lvFn
@ -44,6 +45,14 @@ export default class MouseMoveInspect implements ITool {
} }
mouseMove = _.throttle(function(this: MouseMoveInspect, event: MouseEvent) { mouseMove = _.throttle(function(this: MouseMoveInspect, event: MouseEvent) {
const pointv = new THREE.Vector2()
pointv.x = event.offsetX / this.viewport.renderer.domElement.offsetWidth
pointv.y = event.offsetY / this.viewport.renderer.domElement.offsetHeight
const mouse = new THREE.Vector2()
mouse.set((pointv.x * 2) - 1, -(pointv.y * 2) + 1)
// 当前鼠标所在的点 // 当前鼠标所在的点
const point = this.viewport.getClosestIntersection(event) const point = this.viewport.getClosestIntersection(event)
if (!point) { if (!point) {
@ -56,7 +65,8 @@ export default class MouseMoveInspect implements ITool {
window['CurrentMouseInfo'] = { window['CurrentMouseInfo'] = {
viewport: this.viewport, viewport: this.viewport,
x: point.x, x: point.x,
z: point.z z: point.z,
mouse: mouse
} }
}, 1) }, 1)

80
src/model/ModelUtils.ts

@ -3,7 +3,8 @@ import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemJson } from '@/model/WorldModelType.ts' import type { ItemJson } from '@/model/WorldModelType.ts'
import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts' import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts'
import type Viewport from '@/designer/Viewport.ts' import type Viewport from '@/designer/Viewport.ts'
import type Toolbox from '@/model/itemType/Toolbox.ts' import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh'
import { Vector2 } from 'three/src/math/Vector2'
export function quickCopyByMouse() { export function quickCopyByMouse() {
// 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport // 获取鼠标位置,查看鼠标是否在某个 viewport 的画布上,并取得该 viewport
@ -13,13 +14,20 @@ export function quickCopyByMouse() {
return return
} }
// 从当前 mouse.x, mouse.z 获取地图坐标
const viewport: Viewport = currentMouseInfo.viewport const viewport: Viewport = currentMouseInfo.viewport
const x = currentMouseInfo.x const point: THREE.Vector2 = currentMouseInfo.mouse
const z = currentMouseInfo.z
// 如果当前 x,y 命中在某条线上,以当前鼠标为点,插入一个这个线段的起点的相似点,连接两头的点。 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, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连 // 如果不在线上,查找0.2米内的有效点 Object3D, 如果有,则以这个点为起点, 延伸同类型的点,并让他们相连
findObject3DByCondition(viewport.scene, object => { findObject3DByCondition(viewport.scene, object => {
// 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象 // 判断 object 是否是有效的 Object3D, 并且是当前 viewport 的对象
@ -39,6 +47,68 @@ export function quickCopyByMouse() {
} }
return false 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)
} }
/** /**

3
src/model/itemType/ItemType.ts

@ -1,6 +1,7 @@
import * as THREE from 'three' import * as THREE from 'three'
import type WorldModel from '@/model/WorldModel.ts' import type WorldModel from '@/model/WorldModel.ts'
import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
import type Viewport from '@/designer/Viewport.ts' import type Viewport from '@/designer/Viewport.ts'
import type Toolbox from '@/model/itemType/Toolbox.ts' import type Toolbox from '@/model/itemType/Toolbox.ts'

13
src/model/itemType/ItemTypeLine.ts

@ -1,9 +1,10 @@
import * as THREE from 'three' import * as THREE from 'three'
import ItemType from '@/model/itemType/ItemType.ts' import ItemType from '@/model/itemType/ItemType.ts'
import type { ItemJson } from '@/model/itemType/ItemTypeDefine.ts' import type { ItemJson } from '@/model/WorldModelType.ts'
import type WorldModel from '@/model/WorldModel.ts' import type WorldModel from '@/model/WorldModel.ts'
import type Viewport from '@/designer/Viewport.ts' import type Viewport from '@/designer/Viewport.ts'
import { findObject3DByCondition, findObject3DById } from '@/model/ModelUtils.ts' import { findObject3DByCondition, findObject3DById } from '@/model/ModelUtils.ts'
import type { Line2 } from 'three/examples/jsm/lines/Line2.js'
let pmFn let pmFn
@ -18,20 +19,20 @@ export default abstract class ItemTypeLine extends ItemType {
abstract createPointBasic(position: THREE.Vector3): THREE.Object3D abstract createPointBasic(position: THREE.Vector3): THREE.Object3D
abstract createLineBasic(): THREE.Line abstract createLineBasic(): THREE.Mesh
public init(worldModel: WorldModel) { public init(worldModel: WorldModel) {
return super.init(worldModel).then(() => { return super.init(worldModel).then(() => {
}) })
} }
afterCreateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void { afterCreateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
} }
afterUpdateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void { afterUpdateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
} }
createLine(scene: THREE.Scene, startPoint: THREE.Object3D, endPoint: THREE.Object3D): THREE.Line { createLine(scene: THREE.Scene, startPoint: THREE.Object3D, endPoint: THREE.Object3D): THREE.Mesh {
const line = this.createLineBasic() const line = this.createLineBasic()
const geom = line.geometry const geom = line.geometry
geom.setFromPoints([startPoint.position, endPoint.position]) geom.setFromPoints([startPoint.position, endPoint.position])
@ -207,7 +208,7 @@ export default abstract class ItemTypeLine extends ItemType {
// 找到 startPoint 与 this.dragPoint 之间的线段 // 找到 startPoint 与 this.dragPoint 之间的线段
const line = findObject3DByCondition(this.dragViewport.scene, (obj) => { const line = findObject3DByCondition(this.dragViewport.scene, (obj) => {
return obj.userData.lineStartId === startPoint.uuid && obj.userData.lineEndId === endPoint.uuid return obj.userData.lineStartId === startPoint.uuid && obj.userData.lineEndId === endPoint.uuid
})[0] as THREE.Line })[0] as THREE.Mesh
if (!line) { if (!line) {
// line = this.createLine(this.dragViewport.scene, startPoint, endPoint) // line = this.createLine(this.dragViewport.scene, startPoint, endPoint)

4
src/model/itemType/ToolboxLine.ts

@ -9,7 +9,7 @@ export default class ToolboxLine extends Toolbox {
/** /**
* 线 * 线
*/ */
tempLine?: THREE.Line tempLine?: THREE.Mesh
get itemType(): ItemTypeLine { get itemType(): ItemTypeLine {
return this._itemType return this._itemType
@ -19,7 +19,7 @@ export default class ToolboxLine extends Toolbox {
return '_measure_temp_point' return '_measure_temp_point'
} }
afterMoveTemplateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) { afterMoveTemplateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
} }
stop() { stop() {

39
src/model/itemType/measure/Measure.ts

@ -9,6 +9,9 @@ import Viewport from '@/designer/Viewport.ts'
import ToolboxLine from '@/model/itemType/ToolboxLine.ts' import ToolboxLine from '@/model/itemType/ToolboxLine.ts'
import MeasureToolbox from '@/model/itemType/measure/MeasureToolbox.ts' import MeasureToolbox from '@/model/itemType/measure/MeasureToolbox.ts'
import Toolbox from '@/model/itemType/Toolbox.ts' import Toolbox from '@/model/itemType/Toolbox.ts'
import { Line2 } from 'three/examples/jsm/lines/Line2.js'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js'
export default class Measure extends ItemTypeLine { export default class Measure extends ItemTypeLine {
/** /**
@ -21,7 +24,7 @@ export default class Measure extends ItemTypeLine {
pointMaterial!: Material pointMaterial!: Material
lineMaterial!: Material lineMaterial!: LineMaterial
static GROUP_NAME = 'measure-group' static GROUP_NAME = 'measure-group'
static LABEL_NAME = 'measure_label' static LABEL_NAME = 'measure_label'
@ -31,14 +34,21 @@ export default class Measure extends ItemTypeLine {
override init(worldModel: WorldModel): Promise<void> { override init(worldModel: WorldModel): Promise<void> {
super.init(worldModel) super.init(worldModel)
this.lineMaterial = new THREE.LineBasicMaterial({ // this.lineMaterial = new THREE.LineBasicMaterial({
color: 0xE63C17, // color: 0xE63C17,
linewidth: 2, // linewidth: 2,
opacity: 0.9, // opacity: 0.9,
transparent: true, // transparent: true,
side: THREE.DoubleSide, // side: THREE.DoubleSide,
depthWrite: false, // depthWrite: false,
depthTest: false // depthTest: false
// })
this.lineMaterial = new LineMaterial({
color: 0xE63C17, // 主颜色
linewidth: 2, // 实际可用的线宽
vertexColors: true, // 启用顶点颜色
dashed: false,
alphaToCoverage: true
}) })
this.pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 }) this.pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 })
@ -62,6 +72,7 @@ export default class Measure extends ItemTypeLine {
createToolbox(viewport: Viewport): ToolboxLine { createToolbox(viewport: Viewport): ToolboxLine {
const toolbox = new MeasureToolbox(this.group) const toolbox = new MeasureToolbox(this.group)
toolbox.init(viewport, this) toolbox.init(viewport, this)
//@ts-ignore
return toolbox return toolbox
} }
@ -105,9 +116,9 @@ export default class Measure extends ItemTypeLine {
/** /**
* 线 * 线
*/ */
createLineBasic(): THREE.Line { createLineBasic(): Line2 {
const geom = new THREE.BufferGeometry() const geom = new LineGeometry()
const obj = new THREE.Line(geom, this.lineMaterial) const obj = new Line2(geom, this.lineMaterial)
obj.frustumCulled = false obj.frustumCulled = false
obj.name = Measure.LINE_NAME obj.name = Measure.LINE_NAME
obj.uuid = THREE.MathUtils.generateUUID() obj.uuid = THREE.MathUtils.generateUUID()
@ -115,7 +126,7 @@ export default class Measure extends ItemTypeLine {
} }
// 创建完线之后,创建 label // 创建完线之后,创建 label
afterCreateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) { afterCreateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterCreateLine(line, startPoint, endPoint) super.afterCreateLine(line, startPoint, endPoint)
const p0 = startPoint.position const p0 = startPoint.position
@ -134,7 +145,7 @@ export default class Measure extends ItemTypeLine {
this.group.add(labelObj) this.group.add(labelObj)
} }
afterUpdateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) { afterUpdateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterUpdateLine(line, startPoint, endPoint) super.afterUpdateLine(line, startPoint, endPoint)
const p0 = startPoint.position const p0 = startPoint.position

2
src/model/itemType/measure/MeasureToolbox.ts

@ -50,7 +50,7 @@ export default class MeasureToolbox extends ToolboxLine {
this.measure.group.add(object) this.measure.group.add(object)
} }
afterMoveTemplateLine(line: THREE.Line, startPoint: THREE.Object3D, endPoint: THREE.Object3D) { afterMoveTemplateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterMoveTemplateLine(line, startPoint, endPoint) super.afterMoveTemplateLine(line, startPoint, endPoint)
const p0 = startPoint.position const p0 = startPoint.position

Loading…
Cancel
Save