Browse Source

gstore 拖拽

master
修宁 6 months ago
parent
commit
0da7a5e88b
  1. 828
      src/components/ThreePerfView2.vue
  2. 3
      src/core/Constract.ts
  3. 130
      src/core/IfxUtils.ts
  4. 70
      src/core/base/BaseRenderer.ts
  5. 33
      src/core/engine/Viewport.ts
  6. 36
      src/core/manager/EntityManager.ts
  7. 16
      src/editor/menus/Model3DView.ts
  8. 20
      src/example/example1.js
  9. 18
      src/modules/gstore/GstorePropertySetter.ts
  10. 164
      src/modules/gstore/GstoreRenderer.ts
  11. 122
      src/modules/measure/MeasureRenderer.ts
  12. 18
      src/types/ModelTypes.ts

828
src/components/ThreePerfView2.vue

@ -0,0 +1,828 @@
<template>
<div class="model3d-view">
<el-space :gutter="10" class="toolbar">
<el-button type="primary" @click="test1">测试1</el-button>
<el-button type="primary" @click="test2">测试2</el-button>
<el-button type="primary" @click="test3">测试3</el-button>
<div class="demo-color-block">
<span class="demonstration">物体数:<el-text type="danger">{{ restate.objects }}</el-text></span>
<span class="demonstration"> 顶点数:<el-text type="danger">{{ restate.vertices }}</el-text></span>
<span class="demonstration"> 三角形:<el-text type="danger">{{ restate.faces }}</el-text></span>
<span class="demonstration"> 标签:<el-text type="danger">{{ restate.viewLabelCount }}</el-text></span>
</div>
</el-space>
<div class="main-content">
<div class="canvas-container" ref="canvasContainer" />
</div>
</div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import { getCurrentInstance, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module'
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry'
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import MeasureRenderer from '@/modules/measure/MeasureRenderer.ts'
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry'
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2'
import { Text } from 'troika-three-text'
import SimSunTTF from '@/assets/fonts/simsunb.ttf'
const canvasContainer = ref(null)
let resizeObserver: ResizeObserver | null = null
let scene: THREE.Scene | null = null
let renderer: THREE.WebGLRenderer | null = null
let viewerDom: HTMLElement | null = null
let camera: THREE.PerspectiveCamera | THREE.OrthographicCamera | null = null
let controls: OrbitControls | null = null
let axesHelper: THREE.AxesHelper | null = null
let gridHelper: THREE.GridHelper | null = null
let statsControls: Stats | null = null
let animationFrameId: number | null = null
let modelGroup = new THREE.Group()
const pointMaterial = new THREE.SpriteMaterial({
color: 0xFFFF99, // 0x303133,
transparent: true,
side: THREE.DoubleSide,
opacity: 1,
sizeAttenuation: true
})
// const lineMaterial = new LineMaterial({
// color: 0xFF8C00,
// linewidth: 5,
// vertexColors: false,
// dashed: false
// })
const lineMaterial = new LineMaterial({
color: 0xFF8C00,
linewidth: 5
})
/**
* 测试1创建平面标识
*/
function test1() {
// cleanupThree()
const [x, y, z] = [5, 0, 5]
//
const width = 1.5
//
const depth = 2.0
// 线
const strokeWidth = 0.1
//
const sectionCount = 1
const material = new THREE.MeshBasicMaterial({
color: 0xd8dad0,
transparent: true,
opacity: 1,
side: THREE.BackSide
})
const mesh = new THREE.Mesh(
createGroundMarking(strokeWidth, width, depth, sectionCount, x, y, z),
material)
scene.add(mesh)
refreshCount()
}
/**
* 创建平面标识
*/
function createGroundMarking(weight: number, width: number, depth: number, sectionCount: number, x: number, y: number, z: number, anchor?: THREE.Vector3) {
const sectionDepth = (depth - (sectionCount + 1) * weight) / sectionCount
const shape = buildRectangle(width, depth)
for (let i = 0; i < sectionCount; i++) {
shape.holes.push(buildRectangle(width - 2 * weight, sectionDepth, 0, weight, weight + i * (weight + sectionDepth)))
}
return createExtrudeItem(shape, 0.1, BasePlane.BOTTOM, x, y, -z, null, anchor)
}
/**
* 根据自定义的 Shape通过放样得到一个实体默认实体的样式是在 front 面放样的
*
* @param {*} shape 自定义的型状
* @param {*} depth 放样深度放样后的物体厚度
* @param {*} basePlane 放样的基准面
* @param {*} x 目标定位x
* @param {*} y 目标定位y
* @param {*} z 目标定位z
* @param {*} euler 旋转到目标位
* @param {*} anchor 锚点
* @returns THREE.ExtrudeGeometry
*/
function createExtrudeItem(shape, depth, basePlane, x = 0, y = 0, z = 0, euler = null, anchor = new THREE.Vector3(0, 0, 0)) {
const geometry = new THREE.ExtrudeGeometry(shape, {
steps: 1,
depth: -depth,
bevelEnabled: false,
bevelThickness: 0,
bevelSize: 0,
bevelOffset: 0,
bevelSegments: 0
})
geometry.center()
const size = geometry.boundingBox.getSize(new THREE.Vector3())
let dx, dy, dz
switch (basePlane) {
case BasePlane.LEFT:
geometry.rotateY(Math.PI / 2)
dx = size.z / 2
dy = size.y / 2
dz = size.x / 2
break
case BasePlane.RIGHT:
geometry.rotateY(-Math.PI / 2)
dx = size.z / 2
dy = size.y / 2
dz = size.x / 2
break
case BasePlane.BEHIND:
geometry.translate(0, 0, size.z)
dx = size.x / 2
dy = size.y / 2
dz = size.z / 2
break
case BasePlane.TOP:
geometry.rotateZ(Math.PI)
geometry.rotateX(Math.PI / 2)
geometry.translate(size.x, -size.z, 0)
dx = size.x / 2
dy = size.z / 2
dz = size.y / 2
break
case BasePlane.BOTTOM:
geometry.rotateX(-Math.PI / 2)
dx = size.x / 2
dy = size.z / 2
dz = size.y / 2
break
default:
//BasePlane.FRONT:
dx = size.x / 2
dy = size.y / 2
dz = size.z / 2
break
}
if (euler != null && euler.isEuler) {
//
geometry.rotateX(euler.x)
geometry.rotateY(euler.y)
geometry.rotateZ(euler.z)
}
geometry.translate(dx + x + anchor.x, dy + y + anchor.y, -dz + z + anchor.z)
return geometry
}
/**
* 创建一个矩形的 Shape
*/
function buildRectangle(width: number, height: number, padding: number[] | number = 0, offsetX = 0, offsetY = 0) {
let pl = 0,
pt = 0,
pr = 0,
pb = 0
if (typeof padding == 'number') {
pl = padding
pt = padding
pr = padding
pb = padding
} else if (Array.isArray(padding)) {
if (padding.length == 1) {
pl = padding[0]
pt = padding[0]
pr = padding[0]
pb = padding[0]
} else if (padding.length > 1 && padding.length < 4) {
pl = padding[0]
pt = padding[1]
pr = padding[0]
pb = padding[1]
} else if (padding.length >= 4) {
pl = padding[0]
pt = padding[1]
pr = padding[2]
pb = padding[3]
}
}
const shape = new THREE.Shape()
shape.moveTo(0 + pl + offsetX, 0 + pb + offsetY)
shape.lineTo(width - pr + offsetX, 0 + pb + offsetY)
shape.lineTo(width - pr + offsetX, height - pt + offsetY)
shape.lineTo(0 + pl + offsetX, height - pt + offsetY)
shape.closePath()
return shape
}
/**
* 平面产量
*/
const BasePlane = {
LEFT: 0b000010,
RIGHT: 0b000001,
FRONT: 0b001000,
BEHIND: 0b000100,
TOP: 0b100000,
BOTTOM: 0b010000,
TRANSVERSE: 0b10000000,
LONGITUDINAL: 0b01000000,
THROW: 0b00100000,
toArray: () => {
return [BasePlane.LEFT, BasePlane.BEHIND, BasePlane.RIGHT, BasePlane.FRONT, BasePlane.BOTTOM, BasePlane.TOP, BasePlane.TRANSVERSE, BasePlane.LONGITUDINAL, BasePlane.THROW]
}
}
/**
* LineSegments2
*/
function test2() {
const count = 10000 //
const material = new THREE.MeshBasicMaterial({
color: '#038217',
transparent: true,
opacity: 1,
side: THREE.BackSide
})
// InstancedMesh
const instancedMesh = new THREE.InstancedMesh(groundMarkingGeometry, material, count)
// , 10 0.3,
for (let i = 0; i < count; i++) {
const x = (i % 10) * 3
const y = 0
const z = Math.floor(i / 10) * 3
console.log(`Setting instance [${i}]=(${x}, ${z})`)
const matrix = new THREE.Matrix4()
matrix.setPosition(x, y, z) // /
instancedMesh.setMatrixAt(i, matrix)
}
scene.add(instancedMesh)
refreshCount()
}
const groundMarkingGeometry = createGroundMarkingGeometry() //
function createGroundMarkingGeometry() {
const weight = 0.1
const width = 1.5
const depth = 2.0
const sectionCount = 1
const sectionDepth = (depth - (sectionCount + 1) * weight) / sectionCount
const shape = buildRectangle(width, depth)
for (let i = 0; i < sectionCount; i++) {
shape.holes.push(
buildRectangle(
width - 2 * weight,
sectionDepth,
0,
weight,
weight + i * (weight + sectionDepth)
)
)
}
return createExtrudeItem(shape, 0.1, BasePlane.BOTTOM, 0, 0, 0)
}
function isLabelInView(label, frustum) {
const pos = new THREE.Vector3()
label.getWorldPosition(pos)
//
if (frustum.containsPoint(pos)) {
//
if (shouldShowLabel(label)) {
return true
}
}
return false
}
const labels: Text[] = []
function getLabelPixelSize(fontSize, cameraZoom) {
const pixelRatio = renderer.getPixelRatio()
const referenceZoom = 1
const referenceFontSize = 0.2
const referencePixelSize = 16 // fontSize=0.2, zoom=1 16px
const scale = (fontSize / referenceFontSize) * (cameraZoom / referenceZoom)
return referencePixelSize * scale * pixelRatio
}
function shouldShowLabel(label, minPixelSize = 700) {
const pixelSize = getLabelPixelSize(label.fontSize, camera.zoom)
return pixelSize >= minPixelSize
}
/**
* InstanceMesh(Point) + BufferGeometry + Label
*/
function test3() {
cleanupThree() //
const xcount = 300
const zcount = 100
const dist = 1.25
const spacing = dist
const y = 0.1
const noShaderMaterial = new THREE.MeshBasicMaterial({
color: 0xFFFF99,
transparent: true,
depthWrite: false,
side: THREE.DoubleSide
})
const planeGeometry = new THREE.PlaneGeometry(0.25, 0.25)
// 使 InstancedMesh
const instancedMesh = new THREE.InstancedMesh(planeGeometry, noShaderMaterial, zcount * xcount)
instancedMesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage)
const dummy = new THREE.Object3D()
const points = []
//
for (let z = 0; z < zcount; z++) {
for (let x = 0; x < xcount; x++) {
const px = x * dist
const pz = z * dist
dummy.position.set(px, y, pz)
dummy.rotation.set(-Math.PI / 2, 0, 0)
dummy.updateMatrix()
instancedMesh.setMatrixAt(z * xcount + x, dummy.matrix)
points.push(new THREE.Vector3(px, y, pz))
}
}
scene.add(instancedMesh)
const positions = []
labels.length = 0
function createTextLabel(text, position): Text {
const label = new Text()
label.text = text
label.font = SimSunTTF
label.fontSize = 0.2
label.color = '#333333'
label.opacity = 0.8
label.padding = 0.2
label.anchorX = 'center'
label.anchorY = 'middle'
label.depthOffset = 1
label.backgroundColor = '#000000'
label.backgroundOpacity = 0.6
label.material.depthTest = false
label.position.copy(position)
label.name = MeasureRenderer.LABEL_NAME
label.position.set(position.x, position.y + 0.3, position.z - 0.2)
label.quaternion.copy(camera.quaternion)
label.visible = false
// label.sync()
return label
}
//
for (let z = 0; z < zcount; z++) {
for (let x = 0; x < xcount - 1; x++) {
const x1 = x * spacing
const z1 = z * spacing
const x2 = (x + 1) * spacing
const z2 = z * spacing
positions.push(x1, y, z1, x2, y, z2)
//
const midPoint = new THREE.Vector3((x1 + x2) / 2, y + 0.5, (z1 + z2) / 2)
const length = Math.hypot(x2 - x1, y - y, z2 - z1)
const label = createTextLabel(length.toFixed(2) + 'm', midPoint)
labels.push(label)
}
}
//
for (let z = 0; z < zcount - 1; z++) {
for (let x = 0; x < xcount; x++) {
const x1 = x * spacing
const z1 = z * spacing
const x2 = x * spacing
const z2 = (z + 1) * spacing
positions.push(x1, y, z1, x2, y, z2)
//
const midPoint = new THREE.Vector3((x1 + x2) / 2, y + 0.5, (z1 + z2) / 2)
const length = Math.hypot(x2 - x1, y - y, z2 - z1)
const label = createTextLabel(length.toFixed(2) + 'm', midPoint)
labels.push(label)
}
}
const positionNums = new Float32Array(positions)
const geometry = new THREE.BufferGeometry()
geometry.setAttribute('position', new THREE.BufferAttribute(positionNums, 3))
const material = new THREE.LineBasicMaterial({ color: 0xFF8C00 })
const lineSegments = new THREE.LineSegments(geometry, material)
lineSegments.name = 'grid-lines'
scene.add(lineSegments)
labels.forEach(label => scene.add(label))
//
refreshCount()
}
const restate = reactive({
targetColor: '#ff0000',
loadScale: 1,
viewLabelCount: 0,
mode: 'translate',
objects: 0,
vertices: 0,
faces: 0
})
//
const state = {
showAxesHelper: true,
showGridHelper: true,
camera: {
position: { x: 0, y: 5, z: 10 },
rotation: { x: 0, y: 0, z: 0 }
}
}
onMounted(() => {
nextTick(() => {
initThree()
const viewerDom = canvasContainer.value
if (resizeObserver) {
resizeObserver.unobserve(viewerDom)
}
resizeObserver = new ResizeObserver(handleResize)
resizeObserver.observe(viewerDom)
window['cp'] = getCurrentInstance()
})
})
onBeforeUnmount(() => {
if (animationFrameId !== null) {
cancelAnimationFrame(animationFrameId)
animationFrameId = null
}
cleanupThree()
const viewerDom = canvasContainer.value
if (resizeObserver) {
resizeObserver.unobserve(viewerDom)
}
window['cp'] = null
})
function initThree() {
viewerDom = canvasContainer.value
if (!viewerDom) {
console.error('Viewer DOM element not found')
return
}
//
scene = new THREE.Scene()
scene.background = new THREE.Color(0xeeeeee)
//
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
powerPreference: 'high-performance'
})
renderer.clearDepth()
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(viewerDom.clientWidth, viewerDom.clientHeight)
viewerDom.appendChild(renderer.domElement)
//
// initMode3DCamera()
initMode2DCamera()
// 线
axesHelper = new THREE.AxesHelper(5)
scene.add(axesHelper)
gridHelper = new THREE.GridHelper(1000, 1000)
scene.add(gridHelper)
gridHelper.visible = false
//
const ambientLight = new THREE.AmbientLight(0xffffff, 1.5)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5)
directionalLight.position.set(5, 5, 5).multiplyScalar(3)
directionalLight.castShadow = true
scene.add(directionalLight)
const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 1)
scene.add(hemisphereLight)
//
statsControls = new Stats()
statsControls.showPanel(0)
statsControls.dom.style.right = '0'
statsControls.dom.style.left = 'auto'
viewerDom.appendChild(statsControls.dom)
renderer.setAnimationLoop(animate)
renderer.setClearColor(0x000000, 0)
// animate()
}
//
let frameCount = 0
function animate() {
// animationFrameId = requestAnimationFrame(animate)
renderView()
if (frameCount++ % 60 === 0) { // 60
const frustum = new THREE.Frustum()
const cameraCopy = camera.clone()
//
cameraCopy.updateMatrixWorld()
//
const projScreenMatrix = new THREE.Matrix4().multiplyMatrices(
cameraCopy.projectionMatrix,
cameraCopy.matrixWorldInverse
)
//
frustum.setFromProjectionMatrix(projScreenMatrix)
let viewLabelCount = 0
labels.forEach((label: Text) => {
// label.quaternion.copy(camera.quaternion) // billboard
const isvis = isLabelInView(label, frustum, camera.position)
if (isvis) {
viewLabelCount++
}
if (isvis && label.visible === false) {
label.visible = true
label.sync()
} else if (!isvis && label.visible === true) {
label.visible = false
label.sync()
}
})
restate.viewLabelCount = viewLabelCount
}
}
function handleResize(entries) {
for (let entry of entries) {
const width = entry.contentRect.width
const height = entry.contentRect.height
if (camera instanceof THREE.PerspectiveCamera) {
camera.aspect = width / height
camera.updateProjectionMatrix()
} else if (camera instanceof THREE.OrthographicCamera) {
camera.left = width / -2
camera.right = width / 2
camera.top = height / 2
camera.bottom = height / -2
camera.updateProjectionMatrix()
}
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
break
}
}
/**
* 初始化2D相机
*/
function initMode2DCamera() {
if (camera) {
scene.remove(camera)
}
// ============================
const cameraNew = new THREE.OrthographicCamera(
viewerDom.clientWidth / -2,
viewerDom.clientWidth / 2,
viewerDom.clientHeight / 2,
viewerDom.clientHeight / -2,
1,
500
)
cameraNew.position.set(0, 60, 0)
cameraNew.lookAt(0, 0, 0)
cameraNew.zoom = 60
camera = cameraNew
scene.add(camera)
// ============================
const controlsNew = new OrbitControls(
camera,
renderer.domElement
)
controlsNew.enableDamping = false
controlsNew.enableZoom = true
controlsNew.enableRotate = false
controlsNew.mouseButtons = { LEFT: THREE.MOUSE.PAN, RIGHT: THREE.MOUSE.PAN } //
controlsNew.screenSpacePanning = false //
controlsNew.listenToKeyEvents(viewerDom) //
controlsNew.keys = { LEFT: 'KeyA', UP: 'KeyW', RIGHT: 'KeyD', BOTTOM: 'KeyS' }
controlsNew.panSpeed = 1
controlsNew.keyPanSpeed = 20 // normal 7
controlsNew.minDistance = 0.1
controlsNew.maxDistance = 1000
controls = controlsNew
cameraNew.updateProjectionMatrix()
}
function initMode3DCamera() {
if (camera) {
scene.remove(camera)
}
const viewerDom = canvasContainer.value
// ============================
const cameraNew = new THREE.PerspectiveCamera(25, viewerDom.clientWidth / viewerDom.clientHeight, 0.1, 2000)
cameraNew.position.set(5, 5, 5)
cameraNew.lookAt(0, 0, 0)
camera = cameraNew
scene.add(camera)
const controlsNew = new OrbitControls(camera, viewerDom)
controlsNew.mouseButtons = { LEFT: THREE.MOUSE.PAN, RIGHT: THREE.MOUSE.ROTATE } //
controlsNew.enableDamping = false
controlsNew.screenSpacePanning = false //
controlsNew.minDistance = 2
controlsNew.addEventListener('change', syncCameraState)
controls = controlsNew
controls.update()
camera.updateProjectionMatrix()
syncCameraState()
}
/**
* 重新加载相机状态到全局状态
*/
function syncCameraState() {
if (camera) {
state.camera.position.x = camera.position.x
state.camera.position.y = camera.position.y
state.camera.position.z = camera.position.z
state.camera.rotation.x = camera.rotation.x
state.camera.rotation.y = camera.rotation.y
state.camera.rotation.z = camera.rotation.z
}
}
function renderView() {
statsControls?.update()
renderer?.render(scene, camera)
}
function refreshCount() {
//
let totalObjects = 0
let totalVertices = 0
let totalFaces = 0
scene.traverse(function(child) {
if (child.isMesh) {
totalObjects++
//
const geometry = child.geometry
// BufferGeometry
if (geometry.isBufferGeometry) {
//
if (geometry.attributes.position) {
totalVertices += geometry.attributes.position.count
}
//
if (geometry.index) {
totalFaces += geometry.index.count / 3
} else if (geometry.attributes.position) {
//
totalFaces += geometry.attributes.position.count / 3
}
}
// Geometry 使
else if (geometry.isGeometry) {
//
totalVertices += geometry.vertices.length
//
totalFaces += geometry.faces.length
}
}
})
restate.objects = totalObjects
restate.vertices = totalVertices
restate.faces = totalFaces
}
function cleanupThree() {
//
if (scene) {
scene.traverse((obj: THREE.Mesh) => {
//
if (obj.geometry) {
obj.geometry.dispose()
}
//
if (obj.material) {
if (Array.isArray(obj.material)) {
obj.material.forEach(m => m.dispose())
} else {
obj.material.dispose()
}
}
//
if (obj.texture) {
obj.texture.dispose()
}
})
//
scene.children = []
modelGroup = null
}
}
</script>
<style scoped lang="less">
.model3d-view {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: hidden;
.main-content {
display: flex;
flex: 1;
overflow: hidden;
.model3d-content {
height: 100%;
display: flex;
flex-direction: row;
}
}
.canvas-container {
width: 100%;
height: 100%;
position: relative;
}
}
</style>

3
src/core/Constract.ts

@ -33,6 +33,7 @@ const Constract = Object.freeze({
HEIGHT_WAY_LABEL: 0.03,
HEIGHT_WAY_LINE: 0.02,
MAX_MEASURE_INSTANCES: 20000,
MAX_MEASURE_INSTANCES: 1000,
MAX_GSTORE_INSTANCES: 500,
})
export default Constract

130
src/core/IfxUtils.ts

@ -0,0 +1,130 @@
import * as THREE from 'three'
import { BasePlane } from '@/types/ModelTypes.ts'
/**
* Shape
*/
export function buildRectangle(width: number, height: number, padding: number[] | number = 0, offsetX = 0, offsetY = 0): THREE.Shape {
let pl = 0,
pt = 0,
pr = 0,
pb = 0
if (typeof padding == 'number') {
pl = padding
pt = padding
pr = padding
pb = padding
} else if (Array.isArray(padding)) {
if (padding.length == 1) {
pl = padding[0]
pt = padding[0]
pr = padding[0]
pb = padding[0]
} else if (padding.length > 1 && padding.length < 4) {
pl = padding[0]
pt = padding[1]
pr = padding[0]
pb = padding[1]
} else if (padding.length >= 4) {
pl = padding[0]
pt = padding[1]
pr = padding[2]
pb = padding[3]
}
}
const shape = new THREE.Shape()
shape.moveTo(0 + pl + offsetX, 0 + pb + offsetY)
shape.lineTo(width - pr + offsetX, 0 + pb + offsetY)
shape.lineTo(width - pr + offsetX, height - pt + offsetY)
shape.lineTo(0 + pl + offsetX, height - pt + offsetY)
shape.closePath()
return shape
}
/**
* Shape front
*
* @param {*} shape
* @param {*} depth
* @param {*} basePlane
* @param {*} x x
* @param {*} y y
* @param {*} z z
* @param {*} euler
* @param {*} anchor
* @returns THREE.ExtrudeGeometry
*/
export function createExtrudeItem(shape: THREE.Shape, depth: number, basePlane: number,
x = 0, y = 0, z = 0, euler = null,
anchor = new THREE.Vector3(0, 0, 0)) {
const geometry = new THREE.ExtrudeGeometry(shape, {
steps: 1,
depth: -depth,
bevelEnabled: false,
bevelThickness: 0,
bevelSize: 0,
bevelOffset: 0,
bevelSegments: 0
})
geometry.center()
const size = geometry.boundingBox.getSize(new THREE.Vector3())
let dx, dy, dz
switch (basePlane) {
case BasePlane.LEFT:
geometry.rotateY(Math.PI / 2)
dx = size.z / 2
dy = size.y / 2
dz = size.x / 2
break
case BasePlane.RIGHT:
geometry.rotateY(-Math.PI / 2)
dx = size.z / 2
dy = size.y / 2
dz = size.x / 2
break
case BasePlane.BEHIND:
geometry.translate(0, 0, size.z)
dx = size.x / 2
dy = size.y / 2
dz = size.z / 2
break
case BasePlane.TOP:
geometry.rotateZ(Math.PI)
geometry.rotateX(Math.PI / 2)
geometry.translate(size.x, -size.z, 0)
dx = size.x / 2
dy = size.z / 2
dz = size.y / 2
break
case BasePlane.BOTTOM:
geometry.rotateX(-Math.PI / 2)
dx = size.x / 2
dy = size.z / 2
dz = size.y / 2
break
default:
//BasePlane.FRONT:
dx = size.x / 2
dy = size.y / 2
dz = size.z / 2
break
}
if (euler != null && euler.isEuler) {
// 注意,需要先旋转,再平移。
geometry.rotateX(euler.x)
geometry.rotateY(euler.y)
geometry.rotateZ(euler.z)
}
geometry.translate(dx + x + anchor.x, dy + y + anchor.y, -dz + z + anchor.z)
geometry.center()
return geometry
}

70
src/core/base/BaseRenderer.ts

@ -31,48 +31,6 @@ export default abstract class BaseRenderer {
return Promise.resolve()
}
get pointManager(): InstancePointManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set. Please call beginRendererUpdate first.')
}
let pointManager = this.tempViewport.pointManagerMap.get(this.itemTypeName)
if (!pointManager) {
pointManager = this.createPointManager()
if (pointManager) {
this.tempViewport.pointManagerMap.set(this.itemTypeName, pointManager)
}
}
return pointManager
}
get lineSegmentManager(): LineSegmentManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set. Please call beginRendererUpdate first.')
}
let lineSegmentManager = this.tempViewport.lineSegmentManagerMap.get(this.itemTypeName)
if (!lineSegmentManager) {
lineSegmentManager = this.createLineSegmentManager()
if (lineSegmentManager) {
this.tempViewport.lineSegmentManagerMap.set(this.itemTypeName, lineSegmentManager)
}
}
return lineSegmentManager
}
/**
*
*/
createPointManager(): InstancePointManager {
return null
}
/**
* 线
*/
createLineSegmentManager(): LineSegmentManager {
return null
}
/**
*
* @param viewport
@ -153,7 +111,6 @@ export default abstract class BaseRenderer {
createPointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike {
const point = this.createPoint(item, option)
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
setUserDataForItem(item, point)
@ -202,12 +159,21 @@ export default abstract class BaseRenderer {
}
updatePointForEntity(item: ItemJson, option?: RendererCudOption): Object3DLike {
const point = this.updatePoint(item, option)
point.visible = ((typeof item.v !== 'undefined') ? item.v : true)
const originObject = this.tempViewport.entityManager.findObjectById(item.id)
const newObject = this.updatePoint(item, originObject, option)
newObject.visible = ((typeof item.v !== 'undefined') ? item.v : true)
setUserDataForItem(item, point)
this.afterCreateOrUpdatePoint(item, option, point)
return point
if (originObject !== newObject) {
// 如果更新后的对象和原来的对象不同, 则替换掉
setUserDataForItem(item, newObject)
this.removeFromScene(originObject as THREE.Object3D)
this.appendToScene(newObject as THREE.Object3D)
this.tempViewport.entityManager.replaceObject(item.id, newObject)
}
this.afterCreateOrUpdatePoint(item, option, newObject)
return newObject
}
/**
@ -215,13 +181,7 @@ export default abstract class BaseRenderer {
* @param item
* @param option
*/
updatePoint(item: ItemJson, option?: RendererCudOption): Object3DLike {
const object = this.tempViewport.entityManager.findObjectById(item.id)
if (!object) {
console.warn(`Point with ID "${item.id}" does not exist.`)
return
}
updatePoint(item: ItemJson, object: Object3DLike, option?: RendererCudOption): Object3DLike {
const point = object
point.name = item.name || point.name

33
src/core/engine/Viewport.ts

@ -109,6 +109,38 @@ export default class Viewport {
}
/**
*
* @param typeName
* @param createFn
*/
getOrCreatePointManager(typeName: string, createFn: () => InstancePointManager): InstancePointManager {
let pointManager = this.pointManagerMap.get(typeName)
if (!pointManager) {
pointManager = createFn()
if (pointManager) {
this.pointManagerMap.set(typeName, pointManager)
}
}
return pointManager
}
/**
* 线
* @param typeName 线
* @param createFn 线
*/
getOrCreateLineManager(typeName: string, createFn: () => LineSegmentManager): LineSegmentManager {
let lineSegmentManager = this.lineSegmentManagerMap.get(typeName)
if (!lineSegmentManager) {
lineSegmentManager = createFn()
if (lineSegmentManager) {
this.lineSegmentManagerMap.set(typeName, lineSegmentManager)
}
}
return lineSegmentManager
}
/**
* THREE
*/
async initThree(option: InitThreeOption) {
@ -179,6 +211,7 @@ export default class Viewport {
statsControls.dom.style.position = 'absolute'
statsControls.dom.style.top = '0'
statsControls.dom.style.left = '0'
statsControls.dom.style.zIndex = '1'
viewerDom.parentElement.parentElement.appendChild(statsControls.dom)
$(statsControls.dom).children().css('height', '28px')

36
src/core/manager/EntityManager.ts

@ -96,11 +96,13 @@ export default class EntityManager {
/**
* , center[] / in[] / out[] ,
*/
createOrUpdateEntity(entityRaw: ItemJson, option: EntityCudOption = {}): void {
if (!entityRaw?.id) {
createOrUpdateEntity(entity: ItemJson, option: EntityCudOption = {}): void {
// if (!entityRaw?.id) {
if (!entity?.id) {
throw new Error('Entity must have an id')
}
const entity = _.cloneDeep(entityRaw) as ItemJson
// 改成非深拷贝, 直接使用原始数据, 因为 renderer 可能会修改这个数据, 后续也要能生效
// const entity = _.cloneDeep(entityRaw) as ItemJson
const originEntity = this.___entityMap.get(entity.id)
// 找到这个数据的渲染器
@ -418,6 +420,34 @@ export default class EntityManager {
return this.__objectMap.get(id)
}
replaceObject(id: string, newObject: Object3DLike) {
if (newObject.userData.entityId !== id) {
throw new Error(`New object must have userData.entityId set to ${id}`)
}
const originalObject = this.__objectMap.get(id)
_.remove(this._selectableObjects, obj => obj.userData?.entityId === id)
_.remove(this._draggableObjects, obj => obj.userData?.entityId === id)
this.__objectMap.set(id, newObject)
// 如果是可选中对象,添加到 _selectableObjects 中
if (newObject instanceof THREE.Object3D && newObject.userData.selectable !== false) {
this._selectableObjects.push(newObject)
}
if (newObject instanceof THREE.Object3D && newObject.userData.draggable) {
this._draggableObjects.push(newObject)
}
if (this.viewport.state.selectedEntityId === id) {
// 如果当前选中的对象被替换了,更新选中状态
this.viewport.selectInspect.selectById(id)
}
if (_.includes(this.viewport.state.multiSelectedEntityIds, id)) {
// 如果当前多选的对象被替换了,更新多选状态
this.viewport.selectInspect.multiSelectByIds(this.viewport.state.multiSelectedEntityIds)
}
}
appendObject(id: string, object: Object3DLike) {
this.__objectMap.set(id, object)
// 如果是可选中对象,添加到 _selectableObjects 中

16
src/editor/menus/Model3DView.ts

@ -1,6 +1,7 @@
import { defineMenu } from '@/runtime/DefineMenu.ts'
import Model3DView from '@/components/Model3DView.vue'
import ThreePerfView from '@/components/ThreePerfView.vue'
import ThreePerfView2 from '@/components/ThreePerfView2.vue'
export default defineMenu((menus) => {
menus.insertChildren('tool',
@ -37,6 +38,21 @@ export default defineMenu((menus) => {
dialogClass: 'model-3d-view-wrap'
})
}
},
{
name: 'threePerfView2', label: '性能测试2', order: 3,
click: () => {
system.showDialog(ThreePerfView2, {
title: '性能测试2',
width: 950,
height: 400,
showClose: true,
showMax: true,
showCancelButton: false,
showOkButton: false,
dialogClass: 'model-3d-view-wrap'
})
}
}
]
)

20
src/example/example1.js

@ -323,6 +323,25 @@ export default {
}]
},
{
catalogCode: 'f3', t: 'floor',
items: [
{
id: 'gstore3',
t: 'gstore',
v: true,
tf: [[0, 0.1, 0], [0, 0, 0], [2.0, 0.1, 1.0]],
dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 }
},
{
id: 'gstore4',
t: 'gstore',
v: true,
tf: [[5, 0.1, 0], [0, 0, 0], [2.0, 0.1, 1.0]],
dt: { in: [], out: [], center: [], storeWidth: 1.4, storeDepth: 1.4 }
}
]
},
{
catalogCode: '__f1', t: 'floor',
items: buildAgvPerformanceData(100, 100)
}
@ -337,6 +356,7 @@ export default {
{ catalogCode: '__f1', label: '地下室 (-f1)' }, // 目录项
{ catalogCode: 'f1', label: '一楼 (f1)' },
{ catalogCode: 'f2', label: '二楼 (f2)' },
{ catalogCode: 'f3', label: '三楼 (f3)' },
{ catalogCode: 'OUT', label: '外场 (OUT)' },
{ catalogCode: 'fe', label: '楼层电梯 (fe)' }
]

18
src/modules/gstore/GstorePropertySetter.ts

@ -1,12 +1,20 @@
import type { PropertySetter } from "@/core/base/PropertyTypes.ts";
import { basicFieldsSetter } from "@/editor/widgets/property/PropertyPanelConstant.ts";
import type { PropertySetter } from '@/core/base/PropertyTypes.ts'
import { basicFieldsSetter } from '@/editor/widgets/property/PropertyPanelConstant.ts'
const propertySetter: PropertySetter = {
flatten: {
fields: [
...basicFieldsSetter,
],
{
dataPath: 'dt.strokeColor', label: '边线颜色', input: 'ColorPicker',
inputProps: {}
},
};
{
dataPath: 'dt.strokeWidth', label: '边线宽度', input: 'InputNumber',
inputProps: {}
}
]
}
}
export default propertySetter;
export default propertySetter

164
src/modules/gstore/GstoreRenderer.ts

@ -1,9 +1,12 @@
import * as THREE from 'three'
import * as IfxUtils from '@/core/IfxUtils.ts'
import BaseRenderer from '@/core/base/BaseRenderer.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'
import Constract from '@/core/Constract.ts'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import { BasePlane, type Object3DLike } from '@/types/ModelTypes.ts'
/**
*
@ -20,95 +23,120 @@ export default class GstoreRenderer extends BaseRenderer {
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(1.5, 1.2, 0.1)
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0)
readonly defaultLineWidth: number = 0.05
readonly defaultPointOption = {
weight: 0.1,
width: 1.5,
depth: 1.3,
strokeColor: 0x038217,
strokeWidth: 0.15
}
centerMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.5
})
constructor(itemTypeName: string) {
super(itemTypeName)
getColorOfMaterial(strokeColor: number): THREE.MeshBasicMaterial {
return new THREE.MeshBasicMaterial({
color: strokeColor,
transparent: true,
opacity: 0.9
})
}
/**
* 使 storeWidth/storeDepth, TF无效
*/
override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, object: THREE.Object3D) {
super.afterCreateOrUpdatePoint(item, option, object)
edgeMaterial = this.getColorOfMaterial(this.defaultPointOption.strokeColor)
createPoint(item: ItemJson, option?: RendererCudOption): Object3DLike {
const point = this.createMesh(item.tf[2][0], item.tf[2][2], item.dt.strokeWidth, item.dt.strokeColor)
point.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
const group = object
group.position.y = this.defulePositionY
group.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth)
group.rotation.set(
point.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
// const planeMesh = group.children[0] as THREE.Mesh
// planeMesh.geometry.dispose()
//
// const newGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth)
// planeMesh.geometry = newGeometry
return point
}
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D {
throw new Error('not allow store line.')
}
updatePoint(item: ItemJson, object: THREE.Group, option?: RendererCudOption): Object3DLike {
// 如果 object.userData.strokeWidth 没有变化,并且 scale.x/z 没有变化, 则只平移位置
if (!object ||
(object.userData.strokeWidth !== item.dt.strokeWidth && item.dt.strokeWidth) ||
(object.userData.strokeColor !== item.dt.strokeColor && item.dt.strokeColor) ||
(object.userData.width !== item.tf[2][0] || object.userData.depth !== item.tf[2][2])) {
updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
throw new Error('not allow store line.')
// 线宽度 / 大小 / 颜色发生变化. 重新创建一个新的点
object = this.createPoint(item) as THREE.Group
}
createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D {
throw new Error('not allow createPointBasic.')
object.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
object.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
return object
}
createPoint(item: ItemJson, option?: RendererCudOption): THREE.Object3D {
// 创建平面几何体
if (!item.dt.storeWidth || !item.dt.storeDepth) {
system.showErrorDialog('地堆货位缺少 storeWidth 或 storeDepth 属性')
return null
}
const group = new THREE.Group()
group.name = GstoreRenderer.POINT_NAME
// 绘制背景矩形框
const planeGeometry = new THREE.PlaneGeometry(1, 1)
planeGeometry.rotateX(Math.PI / 2)
const planeMaterial = new THREE.MeshBasicMaterial({
color: '#dee8ee',
transparent: true, // 启用透明
opacity: 0.5, // 50%透明度
depthWrite: false, // 防止深度冲突
side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"}
createMesh(totalWidth: number, totalDepth: number, strokeWidth = 0.15, strokeColor = this.defaultPointOption.strokeColor): Object3DLike {
// === 边框部分(使用 Shape + Hole 构建)===
const shape = new THREE.Shape()
shape.moveTo(-totalWidth / 2, -totalDepth / 2)
shape.lineTo(totalWidth / 2, -totalDepth / 2)
shape.lineTo(totalWidth / 2, totalDepth / 2)
shape.lineTo(-totalWidth / 2, totalDepth / 2)
shape.closePath()
const hole = new THREE.Path()
hole.moveTo(-totalWidth / 2 + strokeWidth, -totalDepth / 2 + strokeWidth)
hole.lineTo(totalWidth / 2 - strokeWidth, -totalDepth / 2 + strokeWidth)
hole.lineTo(totalWidth / 2 - strokeWidth, totalDepth / 2 - strokeWidth)
hole.lineTo(-totalWidth / 2 + strokeWidth, totalDepth / 2 - strokeWidth)
hole.closePath()
shape.holes.push(hole)
// ExtrudeGeometry 默认是沿 Z 轴拉伸 → 我们需要让它朝 Y 轴拉伸
const edgeGeometry = new THREE.ExtrudeGeometry(shape, {
depth: 0.02,
bevelEnabled: false
})
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial)
group.add(planeMesh)
// 绘制边框
const lineXLen = item.dt.storeWidth - this.defaultLineWidth
const lineYLen = item.dt.storeDepth - this.defaultLineWidth
const lineGeometry = new LineGeometry().setPositions([
-(lineXLen / 2), 0, -(lineYLen / 2),
lineXLen / 2, 0, -(lineYLen / 2),
lineXLen / 2, 0, lineYLen / 2,
-(lineXLen / 2), 0, lineYLen / 2,
-(lineXLen / 2), 0, -(lineYLen / 2)
])
const lineMaterial = new LineMaterial({
color: '#038217',
linewidth: this.defaultLineWidth,
worldUnits: true,
resolution: new THREE.Vector2(window.innerWidth, window.innerHeight),
side: THREE.DoubleSide
})
//
const line = new Line2(lineGeometry, lineMaterial)
group.add(line as THREE.Object3D)
edgeGeometry.rotateX(-Math.PI / 2)
edgeGeometry.translate(0, 0.01, 0)
const centerGeometry = new THREE.PlaneGeometry(totalWidth, totalDepth)
centerGeometry.rotateX(-Math.PI / 2)
// 设置位置
group.position.set(item.tf[0][0], item.tf[0][1], item.tf[0][2])
// === 网格组合 ===
const edgeMesh = new THREE.Mesh(edgeGeometry,
(this.defaultPointOption.strokeColor === strokeColor) ? this.edgeMaterial : this.getColorOfMaterial(strokeColor)
)
const centerMesh = new THREE.Mesh(centerGeometry, this.centerMaterial)
const group = new THREE.Group()
group.add(edgeMesh)
if (this.defaultPointOption.strokeColor !== strokeColor) {
// this.centerMaterial.color.set(strokeColor)
edgeMesh.material.color.set(strokeColor)
}
group.add(centerMesh)
group.userData.strokeWidth = strokeWidth
group.userData.strokeColor = strokeColor
group.userData.width = totalWidth
group.userData.depth = totalDepth
return group
}
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D {
throw new Error('not allow store line.')
}
updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
throw new Error('not allow store line.')
}
dispose() {
super.dispose()
this.pointMaterial?.dispose()

122
src/modules/measure/MeasureRenderer.ts

@ -11,15 +11,8 @@ import type { Object3DLike } from '@/types/ModelTypes.ts'
*
*/
export default class MeasureRenderer extends BaseRenderer {
/**
* , 线. 线
*/
group: THREE.Group
static GROUP_NAME = 'measure_group'
static LABEL_NAME = 'measure_label'
static POINT_NAME = 'measure_point'
static LINE_NAME = 'measure_line'
public useHtmlLabel = false
@ -40,12 +33,28 @@ export default class MeasureRenderer extends BaseRenderer {
readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1)
readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0)
createPointManager(): InstancePointManager {
return InstancePointManager.create(this.itemTypeName, this.tempViewport, this.pointGeometry, this.pointMaterial, Constract.MAX_MEASURE_INSTANCES)
get pointManager(): InstancePointManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set.')
}
return this.tempViewport.getOrCreatePointManager(this.itemTypeName, () =>
InstancePointManager.create(this.itemTypeName,
this.tempViewport,
this.pointGeometry,
this.pointMaterial,
Constract.MAX_MEASURE_INSTANCES)
)
}
createLineSegmentManager(): LineSegmentManager {
return LineSegmentManager.create(this.itemTypeName, this.tempViewport, this.lineMaterial)
get lineSegmentManager(): LineSegmentManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set.')
}
return this.tempViewport.getOrCreateLineManager(this.itemTypeName, () =>
LineSegmentManager.create(this.itemTypeName,
this.tempViewport,
this.lineMaterial)
)
}
/**
@ -64,31 +73,6 @@ export default class MeasureRenderer extends BaseRenderer {
)
}
// createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D {
// const geom = new LineGeometry()
// const obj = new Line2(geom, this.lineMaterial)
// obj.frustumCulled = false
// obj.name = MeasureRenderer.LINE_NAME
// obj.uuid = getLineId(start.id, end.id, type)
//
// return obj
// }
//
// createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D {
// // const tt = new THREE.BoxGeometry(1, 1, 1)
// // const obj = new THREE.Mesh(tt, this.movelinePoint)
// // obj.name = MeasureRenderer.POINT_NAME
// // obj.uuid = item.id
// //
// // return [obj]
//
// // 创建平面几何体
// // const obj = new THREE.Mesh(this.pointGeometry,this.pointMaterial)
// // obj.name = MeasureRenderer.POINT_NAME
// // return obj
// }
createPointBasic(item: ItemJson, option?: RendererCudOption): Object3DLike {
// 不允许改变高度/角度/大小
item.tf = [
@ -104,33 +88,6 @@ export default class MeasureRenderer extends BaseRenderer {
return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color)
}
// createLine(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike {
// const lineId = getLineId(start.id, end.id, type)
// return this.lineSegmentManager.createLine(lineId, start.tf[0], end.tf[0], this.lineMaterial.color, {
// lineId: lineId,
// startId: start.id,
// endId: end.id
// })
// }
// appendToScene(...objects: THREE.Object3D[]) {
// if (!this.group || this.group.parent !== this.tempViewport.scene.scene) {
// if (this.group && this.group.parent !== this.tempViewport.scene.scene) {
// // 幻影加载问题
// this.group.parent.removeFromParent()
// }
//
// this.group = new THREE.Group()
// this.group.name = MeasureRenderer.GROUP_NAME
// this.tempViewport?.scene.add(this.group)
// }
//
// const dragObjects = objects.filter(obj => !!obj.userData.draggable)
// //this.tempViewport.dragControl.setDragObjects(dragObjects, 'push')
//
// this.group.add(...objects)
// }
afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
super.afterCreateOrUpdateLine(start, end, type, option, object)
@ -142,54 +99,15 @@ export default class MeasureRenderer extends BaseRenderer {
fontSize: 0.4,
color: '#333333'
})
// const p0 = startPoint.position
// const p1 = endPoint.position
//
// const dist = p0.distanceTo(p1)
// const label = numberToString(dist) + ' m'
//
// const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5)
// let labelObj: Text | CSS2DObject | undefined = object.userData.labelObj
// if (!labelObj || !labelObj.parent) {
// labelObj = this.createLabel(label)
// this.appendToScene(labelObj)
// object.userData.labelObj = labelObj
// }
//
// labelObj.position.set(position.x, position.y + 0.3, position.z - 0.2)
//
// if (this.useHtmlLabel) {
// labelObj.element.innerHTML = label
//
// } else {
// // 让文本朝向摄像机
// labelObj.quaternion.copy(this.tempViewport.camera.quaternion)
// labelObj.text = label
// labelObj.sync()
// }
}
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
super.afterDeleteLine(start, end, type, option, object)
this.tempViewport.labelManager.removeLabel(object)
// 删除标签
// const labelObj = object.userData?.labelObj
// if (labelObj && labelObj.parent) {
// labelObj.parent.remove(labelObj)
// }
}
dispose() {
super.dispose()
if (this.group && this.group.parent) {
this.group.parent.remove(this.group)
}
this.group = undefined
this.pointMaterial?.dispose()
this.lineMaterial?.dispose()
}

18
src/types/ModelTypes.ts

@ -38,6 +38,24 @@ import * as THREE from 'three'
// end: THREE.Vector3;
// }
/**
*
*/
export const BasePlane = {
LEFT: 0b000010,
RIGHT: 0b000001,
FRONT: 0b001000,
BEHIND: 0b000100,
TOP: 0b100000,
BOTTOM: 0b010000,
TRANSVERSE: 0b10000000,
LONGITUDINAL: 0b01000000,
THROW: 0b00100000,
toArray: () => {
return [BasePlane.LEFT, BasePlane.BEHIND, BasePlane.RIGHT, BasePlane.FRONT, BasePlane.BOTTOM, BasePlane.TOP, BasePlane.TRANSVERSE, BasePlane.LONGITUDINAL, BasePlane.THROW]
}
}
export interface LineManageReference {
manager: LineSegmentManager
id: string

Loading…
Cancel
Save