Browse Source

Merge remote-tracking branch 'origin/master'

master
lizw-2015 7 months ago
parent
commit
7b6bcd9a53
  1. 10
      src/components/Model3DView.vue
  2. 5
      src/core/base/BaseRenderer.ts
  3. 34
      src/core/controls/SelectInspect.ts
  4. 169
      src/core/manager/EntityManager.ts
  5. 63
      src/modules/gstore/GstoreRenderer.ts
  6. 1
      src/modules/measure/MeasureRenderer.ts
  7. 5
      src/modules/rack/RackEntity.ts
  8. 22
      src/modules/rack/RackInteraction.ts
  9. 14
      src/modules/rack/RackMeta.ts
  10. 105
      src/modules/rack/RackRenderer.ts
  11. 15
      src/modules/rack/index.ts

10
src/components/Model3DView.vue

@ -17,6 +17,7 @@
</el-upload>
<el-button @click="addConveyor">添加输送线</el-button>
<el-button @click="createShelf">添加货架</el-button>
<el-button @click="createGroundStore">添加地堆</el-button>
<div class="demo-color-block">
<span class="demonstration">材质颜色</span>
<el-color-picker v-model="restate.targetColor" />
@ -378,6 +379,15 @@ function createShelf(){//创建货架
})
}
function createGroundStore() {
const planeGeometry = new THREE.PlaneGeometry(1, 1);
const material = new THREE.MeshBasicMaterial({
color: 0x00ff00,
side: THREE.DoubleSide // :ml-citation{ref="5,8" data="citationList"}
});
const planeMesh = new THREE.Mesh(planeGeometry, material);
scene.add(planeMesh);
}
function initThree() {
const viewerDom = canvasContainer.value

5
src/core/base/BaseRenderer.ts

@ -116,9 +116,8 @@ export default abstract class BaseRenderer {
console.warn('No active viewport to append objects to.')
return
}
const dragObjects = objects.filter(obj => !!obj.userData.draggable)
this.tempViewport.dragControl.setDragObjects(dragObjects, 'push')
// const dragObjects = objects.filter(obj => !!obj.userData.draggable)
// this.tempViewport.dragControl.setDragObjects(dragObjects, 'push')
this.tempViewport.scene.add(...objects)
}

34
src/core/controls/SelectInspect.ts

@ -5,6 +5,9 @@ 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 EventBus from '@/runtime/EventBus'
import { markRaw } from 'vue'
import { getMeta } from '@/core/manager/ModuleManager.ts'
import MouseMoveInspect from '@/core/controls/MouseMoveInspect.ts'
let pdFn, pmFn, puFn
@ -52,6 +55,8 @@ export default class SelectInspect implements IControls {
*/
selectionId: string
clickTime: number | null = null
constructor() {
}
@ -196,6 +201,9 @@ export default class SelectInspect implements IControls {
// 记录鼠标按下位置
this.recStartPos = this.viewport.getClosestIntersection(event)
this.createRectangle()
} else {
// 为 click 事件添加处理逻辑
this.clickTime = Date.now()
}
}
@ -221,5 +229,31 @@ export default class SelectInspect implements IControls {
onMouseUp(event: MouseEvent) {
this.disposeRect()
const clickTime = this.clickTime
this.clickTime = null
if (Date.now() - clickTime < 200) {
// 如果是点击事件,触发选中逻辑
const objects: THREE.Object3D[] = this.viewport.entityManager.getObjectByCanvasMouse(event)
if (objects.length > 0) {
const object = objects[0]
const entityId = object.userData.entityId
const item = this.viewport.entityManager.findItemById(entityId)
const itemTypeName = object.userData.t
if (item.dt.protected !== true) {
this.viewport.state.selectedObject = markRaw(object)
this.viewport.state.selectedItem = markRaw(item)
this.viewport.state.selectedEntityId = entityId
this.viewport.state.selectedObjectMeta = getMeta(itemTypeName)
EventBus.dispatch('selectedObjectChanged', {
viewport: markRaw(this.viewport),
selectedObject: this.viewport.state.selectedObject,
selectedItem: this.viewport.state.selectedItem,
selectedEntityId: this.viewport.state.selectedEntityId,
selectedObjectMeta: this.viewport.state.selectedObjectMeta
})
}
}
}
}
}

169
src/core/manager/EntityManager.ts

@ -3,6 +3,7 @@ import type Viewport from '@/core/engine/Viewport'
import type BaseRenderer from '@/core/base/BaseRenderer'
import { getRenderer } from './ModuleManager'
import { getLineId, parseLineId } from '@/core/ModelUtils'
import { Vector2 } from 'three'
/**
*
@ -24,6 +25,9 @@ export default class EntityManager {
// 所有 THREEJS "点"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组
private readonly objects = new Map<string, THREE.Object3D[]>()
// 所有 THREEJS "可选中"对象, 检索值是"点实体"的 id, 值是 THREE.Object3D 数组
private readonly _selectableObjects: THREE.Object3D[] = []
// 所有 THREEJS "线"对象, 检索值是"线实体"的 id, 取值方式是 {type}${startId}${endId}, 值是 THREE.Object3D 数组
private readonly lines = new Map<string, THREE.Object3D[]>()
@ -404,70 +408,70 @@ export default class EntityManager {
}
/**
*
* , .
*/
renamePoint(newId: string, originId: string) {
if (this.isUpdating) {
throw new Error('Cannot rename point during update')
}
const entity = this.entities.get(originId)
if (!entity) {
throw new Error(`Entity with id ${originId} does not exist`)
}
if (this.entities.has(newId)) {
throw new Error(`Entity with id ${newId} already exists`)
}
entity.id = newId
this.entities.set(newId, entity)
this.entities.delete(originId)
this.objects.set(newId, this.objects.get(originId) || [])
this.objects.delete(originId)
// 更新关系索引
const relations = this.relationIndex.get(originId)
if (relations) {
this.relationIndex.delete(originId)
// 更新所有关系中的 id
relations.center.forEach((relatedId) => {
const rev = this.relationIndex.get(relatedId)
if (rev && rev.delete('center', originId)) {
rev.add('center', newId)
}
})
relations.input.forEach((relatedId) => {
const rev = this.relationIndex.get(relatedId)
if (rev && rev.delete('out', originId)) {
rev.add('out', newId)
}
})
relations.output.forEach((relatedId) => {
const rev = this.relationIndex.get(relatedId)
if (rev && rev.delete('in', originId)) {
rev.add('in', newId)
}
})
this.relationIndex.set(newId, relations)
}
// 更新所有线段数据
for (const [lineId, lineObjects] of this.lines.entries()) {
const [type, startId, endId] = parseLineId(lineId)
if (startId === originId) {
const newLineId = getLineId(newId, endId, type)
this.lines.set(newLineId, lineObjects)
this.lines.delete(lineId)
} else if (endId === originId) {
const newLineId = getLineId(startId, newId, type)
this.lines.set(newLineId, lineObjects)
this.lines.delete(lineId)
}
}
}
// /**
// * 重命名一个点
// * 注意, 不能在更新时刻改名. 所有的关系节点都应该改名
// */
// renamePoint(newId: string, originId: string) {
// if (this.isUpdating) {
// throw new Error('Cannot rename point during update')
// }
// const entity = this.entities.get(originId)
// if (!entity) {
// throw new Error(`Entity with id ${originId} does not exist`)
// }
// if (this.entities.has(newId)) {
// throw new Error(`Entity with id ${newId} already exists`)
// }
// entity.id = newId
// this.entities.set(newId, entity)
// this.entities.delete(originId)
// this.objects.set(newId, this.objects.get(originId) || [])
// this.objects.delete(originId)
//
// // 更新关系索引
// const relations = this.relationIndex.get(originId)
// if (relations) {
// this.relationIndex.delete(originId)
//
// // 更新所有关系中的 id
// relations.center.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('center', originId)) {
// rev.add('center', newId)
// }
// })
// relations.input.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('out', originId)) {
// rev.add('out', newId)
// }
// })
// relations.output.forEach((relatedId) => {
// const rev = this.relationIndex.get(relatedId)
// if (rev && rev.delete('in', originId)) {
// rev.add('in', newId)
// }
// })
//
// this.relationIndex.set(newId, relations)
// }
//
// // 更新所有线段数据
// for (const [lineId, lineObjects] of this.lines.entries()) {
// const [type, startId, endId] = parseLineId(lineId)
// if (startId === originId) {
// const newLineId = getLineId(newId, endId, type)
// this.lines.set(newLineId, lineObjects)
// this.lines.delete(lineId)
//
// } else if (endId === originId) {
// const newLineId = getLineId(startId, newId, type)
// this.lines.set(newLineId, lineObjects)
// this.lines.delete(lineId)
// }
// }
// }
deleteEntityOnly(id: string) {
return this.entities.delete(id)
@ -478,15 +482,28 @@ export default class EntityManager {
}
deleteObjectsOnly(id: string) {
// 删除对象时,也需要从 _selectableObjects 中移除
const rel = this.objects.get(id)
if (rel) {
_.remove(this._selectableObjects, obj => !rel.includes(obj))
}
return this.objects.delete(id)
}
appendObject(id: string, points: THREE.Object3D[]) {
this.objects.set(id, points)
// 如果是可选中对象,添加到 _selectableObjects 中
if (points.some(obj => obj.userData.selectable !== false)) {
this._selectableObjects.push(...points)
}
}
appendLineObject(id: string, lines: THREE.Object3D[]) {
this.lines.set(id, lines)
// 如果是可选中对象,添加到 _selectableObjects 中
if (lines.some(obj => obj.userData.selectable !== false)) {
this._selectableObjects.push(...lines)
}
}
findLineObjectsById(lineId: string): THREE.Object3D[] {
@ -494,6 +511,11 @@ export default class EntityManager {
}
deleteLineObjectOnly(id: string) {
// 删除线对象时,也需要从 _selectableObjects 中移除
const rel = this.lines.get(id)
if (rel) {
_.remove(this._selectableObjects, obj => !rel.includes(obj))
}
return this.lines.delete(id)
}
@ -507,8 +529,23 @@ export default class EntityManager {
return this.entities.get(linkStartPointId)
}
getEntityMap() {
return this.entities
getObjectByCanvasMouse(event: MouseEvent): THREE.Object3D[] {
const _domElement = this.viewport.renderer.domElement
const rect = _domElement.getBoundingClientRect()
const _pointer = new Vector2()
_pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1
_pointer.y = -(event.clientY - rect.top) / rect.height * 2 + 1
this.viewport.raycaster.setFromCamera(_pointer, this.viewport.camera)
const _intersections = this.viewport.raycaster.intersectObjects(this._selectableObjects, true)
if (!_intersections || _intersections.length === 0) {
return []
}
// 根据距离排序射线命中的对象集
return _.map(
_intersections.sort((a, b) => a.distance - b.distance),
r => r.object
)
}
}

63
src/modules/gstore/GstoreRenderer.ts

@ -1,15 +1,14 @@
import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import { Text } from 'troika-three-text'
import MoveLinePointPng from '@/assets/images/moveline_point.png'
import SimSunTTF from '@/assets/fonts/simsunb.ttf'
import { getLineId } from '@/core/ModelUtils.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 GstoreRenderer extends BaseRenderer {
static POINT_NAME = 'way_point'
static POINT_NAME = 'ground_store'
pointMaterial: THREE.Material
@ -19,6 +18,7 @@ export default class GstoreRenderer extends BaseRenderer {
readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合
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
constructor(itemTypeName: string) {
super(itemTypeName)
@ -50,13 +50,56 @@ export default class GstoreRenderer extends BaseRenderer {
}
createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] {
const obj = new THREE.Sprite(this.pointMaterial as THREE.SpriteMaterial)
obj.name = GstoreRenderer.POINT_NAME
return [obj]
// 创建平面几何体
const group = new THREE.Group()
group.name = GstoreRenderer.POINT_NAME
// 绘制背景矩形框
const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth);
planeGeometry.rotateX(-Math.PI / 2)
const planeMaterial = new THREE.MeshBasicMaterial({
color: 'white',
transparent: true, // 启用透明
opacity: 0.2, // 50%透明度
depthWrite: false, // 防止深度冲突
side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"}
});
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
group.add(planeMesh)
if (!item.dt.storeWidth || !item.dt.storeDepth) {
return [group]
}
// 绘制边框
const lineXLen = item.dt.storeWidth - this.defaultLineWidth
const lineYLen = item.dt.storeDepth - this.defaultLineWidth
const lineGeometry = new LineGeometry().setPositions([
-(lineXLen/2),-(lineYLen/2),0,
lineXLen/2,-(lineYLen/2),0,
lineXLen/2,lineYLen/2,0,
-(lineXLen/2),lineYLen/2,0,
-(lineXLen/2),-(lineYLen/2),0
]);
lineGeometry.rotateX(-Math.PI / 2)
const lineMaterial = new LineMaterial({
color: 0x00ff00,
linewidth: 0.05,
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)
return [group]
}
dispose() {
super.dispose()
this.pointMaterial.dispose()
}
}
}

1
src/modules/measure/MeasureRenderer.ts

@ -110,7 +110,6 @@ export default class MeasureRenderer extends BaseRenderer {
const dragObjects = objects.filter(obj => !!obj.userData.draggable)
this.tempViewport.dragControl.setDragObjects(dragObjects, 'push')
// this.tempViewport.dragControl.setDragObjects(objects, 'remove')
this.group.add(...objects)
}

5
src/modules/rack/RackEntity.ts

@ -0,0 +1,5 @@
import BaseEntity from '@/core/base/BaseItemEntity.ts'
export default class RackEntity extends BaseEntity {
}

22
src/modules/rack/RackInteraction.ts

@ -0,0 +1,22 @@
import BaseInteraction from '@/core/base/BaseInteraction.ts'
import * as THREE from 'three'
export default class RackInteraction extends BaseInteraction {
get isSinglePointMode(): boolean {
return true
}
constructor(itemTypeName: string) {
super(itemTypeName)
}
createPointOfItem(item: ItemJson, point: THREE.Vector3): ItemJson {
item = super.createPointOfItem(item, point)
// 创建一个地堆货架
item.dt.storeWidth = 1.2 // 宽度
item.dt.storeDepth = 1.2 // 深度
return item
}
}

14
src/modules/rack/RackMeta.ts

@ -0,0 +1,14 @@
import type { IMeta } from '@/core/base/IMeta.ts'
export default [
{ field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true, category: 'basic' },
{ field: 'name', editor: 'TextInput', label: '名称', category: 'basic' },
{ field: 'dt.label', editor: 'TextInput', label: '标签', category: 'basic' },
{ editor: 'TransformEditor', category: 'basic' },
{ field: 'dt.color', editor: 'Color', label: '颜色', category: 'basic' },
{ editor: '-', category: 'basic' },
{ field: 'tf', editor: 'InOutCenterEditor', category: 'basic' },
{ field: 'dt.selectable', editor: 'Switch', label: '可选中', category: 'basic' },
{ field: 'dt.protected', editor: 'Switch', label: '受保护', category: 'basic' },
{ field: 'visible', editor: 'Switch', label: '可见', category: 'basic' }
] as IMeta

105
src/modules/rack/RackRenderer.ts

@ -0,0 +1,105 @@
import * as THREE from 'three'
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'
/**
*
*/
export default class RackRenderer extends BaseRenderer {
static POINT_NAME = 'ground_store'
pointMaterial: THREE.Material
/**
* ,
*/
readonly defulePositionY: number = 0.5 // 默认点的高度, 0.01, 防止和地面重合
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
constructor(itemTypeName: string) {
super(itemTypeName)
}
/**
* 使 storeWidth/storeDepth, TF无效
*/
override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) {
super.afterCreateOrUpdatePoint(item, option, objects)
const point = objects[0]
point.position.y = this.defulePositionY
point.scale.set(item.dt.storeWidth, this.defaultScale.y, item.dt.storeDepth)
point.rotation.set(
THREE.MathUtils.degToRad(this.defaultRotation.x),
THREE.MathUtils.degToRad(this.defaultRotation.y),
THREE.MathUtils.degToRad(this.defaultRotation.z)
)
}
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.')
}
createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] {
// 创建平面几何体
const group = new THREE.Group()
group.name = RackRenderer.POINT_NAME
// 绘制背景矩形框
const planeGeometry = new THREE.PlaneGeometry(item.dt.storeWidth, item.dt.storeDepth);
planeGeometry.rotateX(-Math.PI / 2)
const planeMaterial = new THREE.MeshBasicMaterial({
color: 'white',
transparent: true, // 启用透明
opacity: 0.2, // 50%透明度
depthWrite: false, // 防止深度冲突
side: THREE.DoubleSide // 双面渲染:ml-citation{ref="5,8" data="citationList"}
});
const planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
group.add(planeMesh)
if (!item.dt.storeWidth || !item.dt.storeDepth) {
return [group]
}
// 绘制边框
const lineXLen = item.dt.storeWidth - this.defaultLineWidth
const lineYLen = item.dt.storeDepth - this.defaultLineWidth
const lineGeometry = new LineGeometry().setPositions([
-(lineXLen/2),-(lineYLen/2),0,
lineXLen/2,-(lineYLen/2),0,
lineXLen/2,lineYLen/2,0,
-(lineXLen/2),lineYLen/2,0,
-(lineXLen/2),-(lineYLen/2),0
]);
lineGeometry.rotateX(-Math.PI / 2)
const lineMaterial = new LineMaterial({
color: 0x00ff00,
linewidth: 0.05,
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)
return [group]
}
dispose() {
super.dispose()
this.pointMaterial.dispose()
}
}

15
src/modules/rack/index.ts

@ -0,0 +1,15 @@
import { defineModule } from '@/core/manager/ModuleManager.ts'
import RackRenderer from './RackRenderer.ts'
import RackEntity from './RackEntity.ts'
import RackMeta from './RackMeta.ts'
import RackInteraction from './RackInteraction.ts'
export const ITEM_TYPE_NAME = 'rack'
export default defineModule({
name: ITEM_TYPE_NAME,
renderer: new RackRenderer(ITEM_TYPE_NAME),
interaction: new RackInteraction(ITEM_TYPE_NAME),
meta: RackMeta,
entity: RackEntity
})
Loading…
Cancel
Save