Browse Source

方向指示器

master
修宁 6 months ago
parent
commit
5934b86a2d
  1. BIN
      src/assets/images/conveyor/shapes/triangle2.png
  2. 82
      src/core/ModelUtils.ts
  3. 12
      src/core/base/BaseRenderer.ts
  4. 33
      src/core/base/IMeta.ts
  5. 7
      src/core/engine/Viewport.ts
  6. 4
      src/core/manager/EntityManager.ts
  7. 15
      src/core/manager/InstanceMeshManager.ts
  8. 2
      src/example/ExampleUtil.js
  9. 233
      src/example/example1.js
  10. 47942
      src/example/flash.js
  11. 77
      src/model/itemType/ItemType.ts
  12. 106
      src/model/itemType/ItemTypeDefine.ts
  13. 256
      src/model/itemType/ItemTypeLine.ts
  14. 323
      src/model/itemType/Toolbox.ts
  15. 149
      src/model/itemType/ToolboxLine.ts
  16. 7
      src/model/itemType/line/LineMeta.ts
  17. 124
      src/model/itemType/line/conveyor/Conveyor.ts
  18. 9
      src/model/itemType/line/conveyor/ConveyorMeta.ts
  19. 16
      src/model/itemType/line/conveyor/ConveyorToolbox.ts
  20. 202
      src/model/itemType/measure/Measure.ts
  21. 36
      src/model/itemType/measure/MeasureMeta.ts
  22. 94
      src/model/itemType/measure/MeasureToolbox.ts
  23. 7
      src/model/itemType/point/PointMeta.ts
  24. 7
      src/model/itemType/store/QueueMeta.ts
  25. 5
      src/modules/measure/MeasureRenderer.ts
  26. 260
      src/modules/way/WayRenderer.ts
  27. 7
      src/types/ModelTypes.ts
  28. 1
      src/types/Types.d.ts

BIN
src/assets/images/conveyor/shapes/triangle2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

82
src/core/ModelUtils.ts

@ -1,6 +1,4 @@
import * as THREE from 'three'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import { getAllItemTypes, getItemTypeByName } from '@/model/itemType/ItemTypeDefine.ts'
import type Viewport from '@/core/engine/Viewport'
import { Vector2 } from 'three/src/math/Vector2'
import EventBus from '@/runtime/EventBus.ts'
@ -12,6 +10,64 @@ import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'
import { TDSLoader } from 'three/examples/jsm/loaders/TDSLoader'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
/**
* 线id
*/
export function getCargoLineId(linkName: string, startId: string, endId: string): string {
// 无序线条的id格式为 cargo$endId$startId
return `${linkName}$${[startId, endId].sort().join('$')}`
}
/**
*
* console.log('Guideway: type=' + type + ', start=' + start.id, ', end=' + end.id)
*
*
* Guideway: type=in, start=way11 , end=way12
* Guideway: type=out, start=way11 , end=way12
* Guideway: type=in, start=way12 , end=way11
* Guideway: type=out, start=way12 , end=way11
* Guideway: type=out, start=way21 , end=way22
* Guideway: type=in, start=way22 , end=way21
* Guideway: type=in, start=way31 , end=way32
* Guideway: type=out, start=way32 , end=way31
*
*
* way11 <-> way12
* way21 -> way22
* way31 <- way32
*/
export function getLinkDirection(viewport: Viewport, linkName: string, start: ItemJson, end: ItemJson): { lineId: string, direction: LinkDirection } {
const lineId = getCargoLineId(linkName, start.id, end.id)
if (viewport.entityManager.findLineObjectById(lineId)) {
// 已经处理过这一对
return { lineId, direction: '' }
}
const startOut = start.dt?.out || []
const endIn = end.dt?.in || []
const endOut = end.dt?.out || []
const startIn = start.dt?.in || []
// 判断方向
const forward = startOut.includes(end.id) && endIn.includes(start.id)
const backward = endOut.includes(start.id) && startIn.includes(end.id)
if (forward && backward) {
return { lineId, direction: '<->' }
} else if (forward) {
return { lineId, direction: '->' }
} else if (backward) {
return { lineId, direction: '<-' }
}
throw new Error(`无法判断 ${start.id}${end.id} 之间的连接关系`)
}
export function setUserDataForItem(item: ItemJson, object: Object3DLike) {
if (!object.name && item.name) {
object.name = item.name
@ -366,19 +422,6 @@ export function calcPositionUseSnap(e: MouseEvent, point: THREE.Vector3) {
return point
}
export function getAllControlPoints(): THREE.Object3D[] {
const allPoints: THREE.Object3D[] = []
getAllItemTypes().forEach((itemType: ItemTypeDefineOption) => {
if (itemType.clazz && itemType.clazz.pointArray) {
// 将每个 ItemType 的点添加到结果数组中
allPoints.push(...itemType.clazz.pointArray)
}
})
return allPoints
}
/**
* uuid Object3D
*/
@ -496,11 +539,14 @@ export async function loadByUrl(url): Promise<any> {
}
/**
* , z轴为平面长度, x轴为平面宽度
* , z轴为平面长度, x轴为平面宽度 linkPlaneByPoint
* @param startPosition
* @param endPosition
* @param direction
* @param width
*
*/
export function linkPlaneByPoint(startPosition: THREE.Vector3, endPosition: THREE.Vector3, width: number): THREE.Matrix4 {
export function createLinkPlaneMatrix4(startPosition: THREE.Vector3, endPosition: THREE.Vector3, width: number, direction: LinkDirection): THREE.Matrix4 {
const dir = new THREE.Vector3().subVectors(endPosition, startPosition)
const length = dir.length() // 平面长度 = 两点距离
dir.normalize()
@ -510,7 +556,7 @@ export function linkPlaneByPoint(startPosition: THREE.Vector3, endPosition: THRE
const dummy = new THREE.Object3D()
dummy.position.copy(center)
dummy.lookAt(endPosition);
dummy.lookAt(endPosition)
// dummy.quaternion.setFromRotationMatrix(new THREE.Matrix4().lookAt(startPosition, endPosition, new THREE.Vector3(0, 1, 0)))
dummy.scale.set(width, 1, length) // Z轴缩放为长度
dummy.updateMatrix()

12
src/core/base/BaseRenderer.ts

@ -1,6 +1,6 @@
import type Viewport from '@/core/engine/Viewport'
import * as THREE from 'three'
import { getLineId, linkPlaneByPoint, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts'
import { getLineId, setUserDataForItem, setUserDataForLine } from '@/core/ModelUtils.ts'
import { Line2 } from 'three/examples/jsm/lines/Line2'
import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts'
import Constract from '@/core/Constract.ts'
@ -83,7 +83,7 @@ export default abstract class BaseRenderer {
/**
* 线
*/
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption) {
}
/**
@ -231,7 +231,7 @@ export default abstract class BaseRenderer {
this.afterCreateOrUpdateLine(start, end, type, option, line)
}
updateLineEntity(start: ItemJson, end: ItemJson, type: LinkType, option: any = {}) {
updateLineForEntity(start: ItemJson, end: ItemJson, type: LinkType, option: any = {}) {
const lineId = getLineId(start.id, end.id, type)
const line = this.tempViewport.entityManager.findLineObjectById(lineId)
@ -263,6 +263,11 @@ export default abstract class BaseRenderer {
}
}
deleteLineForEntity(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
this.deleteLine(start, end, type, option)
this.afterDeleteLine(start, end, type, option)
}
/**
* 线
* @param start
@ -274,7 +279,6 @@ export default abstract class BaseRenderer {
const line = this.tempViewport.entityManager.findLineObjectById(lineId)
this.tempViewport.entityManager.deleteLineObjectOnly(lineId)
this.afterDeleteLine(start, end, type, option, line)
}
dispose() {

33
src/core/base/IMeta.ts

@ -1,33 +0,0 @@
import type { ItemTypeMeta } from '@/model/itemType/ItemTypeDefine.ts'
/**
* "点"
*/
export const BASIC_META_OF_POINT: ItemTypeMeta = [
{ field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true },
{ field: 'name', editor: 'TextInput', label: '名称' },
{ field: 'userData.label', editor: 'TextInput', label: '标签' },
{ editor: 'Transform' },
{ field: 'color', editor: 'Color', label: '颜色' },
{ editor: '-' },
{ editor: 'IN_OUT_CENTER' }
]
/**
* "物流运输单元",
*/
export const BASIC_META_OF_POINT2: ItemTypeMeta = [
{ field: 'userData.selectable', editor: 'Switch', label: '可选中' },
{ field: 'userData.protected', editor: 'Switch', label: '受保护' },
{ field: 'visible', editor: 'Switch', label: '可见' }
]
/**
* "线"
*/
export const BASIC_META_OF_LINE: ItemTypeMeta = []
/**
* "线",
*/
export const BASIC_META_OF_LINE2: ItemTypeMeta = []

7
src/core/engine/Viewport.ts

@ -8,8 +8,6 @@ import { markRaw, reactive, toRaw, watch } from 'vue'
import type IControls from '../controls/IControls'
import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer'
import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import { getAllItemTypes } from '@/model/itemType/ItemTypeDefine.ts'
import SceneHelp from './SceneHelp'
import SelectInspect from '../controls/SelectInspect'
@ -269,11 +267,6 @@ export default class Viewport {
}
}, { immediate: true }))
// 触发所有物品类型的 afterAddViewport 方法
_.forEach(getAllItemTypes(), (itemType: ItemTypeDefineOption) => {
itemType.clazz.afterAddViewport(this)
})
this.animate()
try {

4
src/core/manager/EntityManager.ts

@ -121,7 +121,7 @@ export default class EntityManager {
const relation = this.__relationIndex.get(entity.id)
if (relation) {
for (const type of (['center', 'in', 'out'] as LinkType[])) {
for (const type of (['center', 'input', 'output'] as LinkType[])) {
const relatedIds = relation[type]
if (!relatedIds) continue
@ -226,7 +226,7 @@ export default class EntityManager {
// 只通知起点对应的渲染器
continue
}
renderer.updateLineEntity(start, end, lineDiffItem.type)
renderer.updateLineForEntity(start, end, lineDiffItem.type)
}
// "线"删除

15
src/core/manager/InstanceMeshManager.ts

@ -43,7 +43,7 @@ export default class InstanceMeshManager {
return this.__uuidMap.get(uuid)
}
create(entityId: string): InstanceMeshWrap {
create(entityId: string, userData?: any): InstanceMeshWrap {
let meshIndex = -1
let blockIndex = -1
for (const block of this.blocks) {
@ -64,17 +64,15 @@ export default class InstanceMeshManager {
return null
}
return new InstanceMeshWrap(entityId, this, blockIndex, meshIndex)
return new InstanceMeshWrap(entityId, this, blockIndex, meshIndex, userData)
}
delete(wrapOrId: InstanceMeshWrap | string) {
let wrap: InstanceMeshWrap
if (typeof wrapOrId === 'string') {
wrap = this.__uuidMap.get(wrapOrId)
if (!wrap) {
console.warn(`InstanceMeshManager: Wrap with UUID ${wrapOrId} not found`)
return
}
if (!wrap) return
} else {
wrap = wrapOrId
if (!wrap || !this.__uuidMap.has(wrap.uuid)) {
@ -144,18 +142,21 @@ export class InstanceMeshWrap {
readonly manager: InstanceMeshManager
readonly blockIndex: number
readonly meshIndex: number
//@ts-ignore
userData: UserData = {}
uuid: string
parent: THREE.Object3D | null = null
name: string
visible: boolean
constructor(entityId: string, manager: InstanceMeshManager, blockIndex: number, meshIndex: number) {
constructor(entityId: string, manager: InstanceMeshManager, blockIndex: number, meshIndex: number, userData?: any) {
this.uuid = system.createUUID()
this.entityId = entityId
this.manager = manager
this.meshIndex = meshIndex
this.blockIndex = blockIndex
this.userData = userData
}
setMatrix4(matrix: THREE.Matrix4): InstanceMeshWrap {

2
src/example/ExampleUtil.js

@ -106,7 +106,7 @@ export function buildPointPerformanceData(t, rows, cols) {
node.tf[0][1] = 0.01
node.tf[0][2] = col * spacingZ
node.tf[1] = [0, 0, 0]
node.tf[2] = [1, 0.4, 1]
node.tf[2] = [1, 1.5, 1]
data.set(node.id, node)
}
}

233
src/example/example1.js

@ -331,20 +331,58 @@ export default {
v: true,
tf: [[0, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: [], out: [], center: [] }
}
]
},
{
catalogCode: 'f4', t: 'floor',
items: [
// 双向连接路径
{
id: 'way11',
t: 'way',
v: true,
tf: [[2, 0.1, 1], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way12'], out: ['way12'], center: [] }
},
{
id: 'way12',
t: 'way',
v: true,
tf: [[5, 0.1, 1], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way11'], out: ['way11'], center: [] }
},
// 21->22 单向连接路径
{
id: 'way21',
t: 'way',
v: true,
tf: [[2, 0.1, 3], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: [], out: ['way22'], center: [] }
},
{
id: 'way1',
id: 'way22',
t: 'way',
v: true,
tf: [[2, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way2'], out: ['way2'], center: [] }
tf: [[5, 0.1, 3], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way21'], out: [], center: [] }
},
// 32->31 单向连接路径
{
id: 'way31',
t: 'way',
v: true,
tf: [[2, 0.1, 5], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way32'], out: [], center: [] }
},
{
id: 'way2',
id: 'way32',
t: 'way',
v: true,
tf: [[5, 0.1, 0], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: ['way1'], out: ['way1'], center: [] }
tf: [[5, 0.1, 5], [0, 0, 0], [1.0, 1.0, 1.0]],
dt: { in: [], out: ['way31'], center: [] }
}
]
},
@ -354,11 +392,157 @@ export default {
},
{
catalogCode: '__f2', t: 'floor',
items: buildPointPerformanceData('carton', 200, 500)
items: buildPointPerformanceData('pallet', 200, 500)
},
{
catalogCode: '__f3', t: 'floor',
items: buildRackPerformanceData(10, 200)
},
{
catalogCode: 'asrs1', t: 'floor',
items: [
{
id: 'asrs1_rack',
t: 'asrs_rack',
dt: {
bayCount: 15, // 列数
aisleCount: 2, // 巷道数
hideFloor: false, // 隐藏底板
bays: [ // 每列的配置
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 }
],
aisles: [ // 巷道的配置
{
rail: { width: 0.8 }, // 轨道, 主要是轨道宽度
railLeft: { // 轨道左侧
stores: [ // 轨道左侧货位
{ depth: 1.1 }, // 深位1的存储深度
{ depth: 1.1 } // 深位2的存储深度
]
},
railRight: { // 轨道左侧
stores: [ // 轨道左侧货位
{ depth: 1.1 }, // 深位1的存储深度
{ depth: 1.1 } // 深位2的存储深度
]
}
},
{
rail: { width: 0.8 },
railLeft: {
stores: [
{ depth: 1.1 },
{ depth: 1.1 }
]
},
railRight: {
stores: [
{ depth: 1.1 },
{ depth: 1.1 }
]
}
}
]
}
}
]
},
{
catalogCode: 'shuttle1', t: 'floor',
items: [
{
t: 'shuttle_rack', // 多穿库货架
dt: {
bayCount: 15, // 列数
aisleCount: 2, // 巷道数
hideFloor: false, // 隐藏底板
bayRail: [ // 横行巷道
{
afterBay: 2, // 横行巷道所在位置
railWidth: 1.4 // 横行巷道宽度
},
{ afterBay: 11, railWidth: 1.4 }
],
bays: [ // 每列的配置
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 },
{ bayWidth: 1.6 }
],
aisles: [ // 巷道的配置
{
rail: { railWidth: 0.8 }, // 轨道, 主要是轨道宽度
railLeft: { // 轨道左侧
stores: [ // 轨道左侧货位
{ depth: 1.1 }, // 深位1的存储深度
{ depth: 1.1 } // 深位2的存储深度
]
},
railRight: { // 轨道左侧
stores: [ // 轨道左侧货位
{ depth: 1.1 }, // 深位1的存储深度
{ depth: 1.1 } // 深位2的存储深度
]
}
},
{
rail: { railWidth: 0.8 },
railLeft: {
stores: [
{ depth: 1.1 },
{ depth: 1.1 }
]
},
railRight: {
stores: [
{ depth: 1.1 },
{ depth: 1.1 }
]
}
}
]
}
}
]
},
{
catalogCode: 'flash1', t: 'floor',
items: [
{
id: 'flash_rack1',
t: 'flash_rack',
dt: async () => import('./flash.js')
}
]
}
],
elevator: [], // 电梯
@ -374,37 +558,38 @@ export default {
{ catalogCode: 'f1', label: '一楼 (f1)' },
{ catalogCode: 'f2', label: '二楼 (f2)' },
{ catalogCode: 'f3', label: '三楼 (f3)' },
{ catalogCode: 'f4', label: 'AGV测试区 (f4)' },
{ catalogCode: 'OUT', label: '外场 (OUT)' },
{ catalogCode: 'fe', label: '楼层电梯 (fe)' }
]
},
{
label: '密集库区域',
label: '立体库',
items: [
{ catalogCode: 'm1', label: 'M1 (m1)' },
{ catalogCode: 'm2', label: 'M2 (m2)' },
{ catalogCode: 'm3', label: 'M3 (m3)' },
{ catalogCode: 'm4', label: 'M4 (m4)' },
{ catalogCode: 'me', label: '提升机 (me)' }
{ catalogCode: 'asrs1', label: 'D1 (asrs1)' },
{ catalogCode: 'asrs2', label: 'D2 (asrs2)' },
{ catalogCode: 'asrs3', label: 'D3 (asrs3)' },
{ catalogCode: 'asrs4', label: 'D4 (asrs4)' },
{ catalogCode: 'de1', label: '提升机 (de1)' }
]
},
{
label: '多穿库A',
label: '密集库区域',
items: [
{ catalogCode: 'd1', label: 'D1 (d1)' },
{ catalogCode: 'd2', label: 'D2 (d2)' },
{ catalogCode: 'd3', label: 'D3 (d3)' },
{ catalogCode: 'd4', label: 'D4 (d4)' },
{ catalogCode: 'de1', label: '提升机 (de1)' }
{ catalogCode: 'flash1', label: '1层 (flash1)' },
{ catalogCode: 'flash2', label: '2层 (flash2)' },
{ catalogCode: 'flash3', label: '3层 (flash3)' },
{ catalogCode: 'flash4', label: '4层 (flash4)' },
{ catalogCode: 'me', label: '提升机 (me)' }
]
},
{
label: '多穿库B',
label: '多穿库',
items: [
{ catalogCode: 'e1', label: 'E1 (e1)' },
{ catalogCode: 'e2', label: 'E2 (e2)' },
{ catalogCode: 'e3', label: 'E3 (e3)' },
{ catalogCode: 'e4', label: 'E4 (e4)' },
{ catalogCode: 'shuttle1', label: 'E1 (shuttle1)' },
{ catalogCode: 'shuttle2', label: 'E2 (shuttle2)' },
{ catalogCode: 'shuttle3', label: 'E3 (shuttle3)' },
{ catalogCode: 'shuttle4', label: 'E4 (shuttle4)' },
{ catalogCode: 'ee1', label: '提升机 (ee1)' }
]
}

47942
src/example/flash.js

File diff suppressed because it is too large

77
src/model/itemType/ItemType.ts

@ -1,77 +0,0 @@
import * as THREE from 'three'
import type WorldModel from '@/model/WorldModel.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 Toolbox from '@/model/itemType/Toolbox.ts'
export default abstract class ItemType {
/**
*
*/
pointArray: THREE.Object3D[] = []
name: string
option: ItemTypeDefineOption
worldModel: WorldModel
public init(worldModel: WorldModel) {
this.worldModel = worldModel
// 初始化方法,子类可以重写
return Promise.resolve()
}
abstract loadFromJson(item: ItemJson): undefined | THREE.Object3D
abstract createToolbox(viewport: Viewport): Toolbox
abstract getDefaultScale(): THREE.Vector3
abstract getDefaultRotation(): THREE.Vector3
abstract createPoint(position: THREE.Vector3, itemJson: ItemJson): THREE.Object3D
beforeLoad(): THREE.Object3D[] {
return []
}
afterLoadPoint(point: THREE.Object3D): void {
}
afterUpdatePoint(point: THREE.Object3D, updatedLines: THREE.Object3D[]): void {
}
afterLoadGroup(group: THREE.Group): void {
}
afterLoadComplete(objects: THREE.Object3D[]): THREE.Object3D[] {
return []
}
/**
* scene
*/
afterAddScene(viewport: Viewport, scene: THREE.Scene, objects: THREE.Object3D[]): void {
}
/**
* viewport
*/
afterAddViewport(viewport: Viewport): void {
//viewport.dragControl.setDragObjects(this.pointArray, 'push')
const toolbox = this.createToolbox(viewport)
viewport.toolbox[this.name] = toolbox
}
/**
* EsDragControls
*/
abstract dragPointStart(viewport: Viewport, point: THREE.Mesh)
/**
* EsDragControls
*/
abstract dragPointComplete(viewport: Viewport)
}

106
src/model/itemType/ItemTypeDefine.ts

@ -1,106 +0,0 @@
import _ from 'lodash'
import type ItemType from '@/model/itemType/ItemType.ts'
import * as THREE from 'three'
const itemTypes: Record<string, ItemTypeDefineOption> = {}
window['itemTypes'] = itemTypes
/**
*
*/
export function defineItemType(option: ItemTypeDefineOption) {
itemTypes[option.name] = option
option.clazz.name = option.name
option.clazz.option = option
return option
}
export function getItemTypeByName(type: string): ItemTypeDefineOption {
const itemType = _.get(itemTypes, type)
if (!itemType) {
console.warn(`未找到物流单元类型定义: ${type}`)
return
}
return itemType
}
export function getAllItemTypes(): ItemTypeDefineOption[] {
return Object.values(itemTypes)
}
/**
* "点"
*/
export const BASIC_META_OF_POINT: ItemTypeMeta = [
{ field: 'uuid', editor: 'UUID', label: 'uuid', readonly: true },
{ field: 'name', editor: 'TextInput', label: '名称' },
{ field: 'userData.label', editor: 'TextInput', label: '标签' },
{ editor: 'Transform' },
{ field: 'color', editor: 'Color', label: '颜色' },
{ editor: '-' },
{ editor: 'IN_OUT_CENTER' }
]
/**
* "物流运输单元",
*/
export const BASIC_META_OF_POINT2: ItemTypeMeta = [
{ field: 'userData.selectable', editor: 'Switch', label: '可选中' },
{ field: 'userData.protected', editor: 'Switch', label: '受保护' },
{ field: 'visible', editor: 'Switch', label: '可见' }
]
/**
* "线"
*/
export const BASIC_META_OF_LINE: ItemTypeMeta = []
/**
* "线",
*/
export const BASIC_META_OF_LINE2: ItemTypeMeta = []
export type ActionType =
/**
* 线
*/
'ln' |
/**
*
*/
'pt' |
/**
*
*/
'fl' |
/**
*
*/
'gp'
export interface ItemTypeDefineOption {
name: string
label: string
actionType: ActionType
clazz: ItemType
getMeta(object: THREE.Object3D): ItemTypeMeta
}
/**
*
*/
export type ItemTypeMeta = ItemTypeMetaItem[]
export type ItemTypeMetaItem = {
field?: string
label?: string
labelWidth?: number
readonly?: boolean
editor: '-' |
'TextInput' | 'Number' | 'Switch' | 'Select' | 'ButtonGroup' |
'UUID' | 'Color' | 'Transform' | 'IN_OUT_CENTER'
}

256
src/model/itemType/ItemTypeLine.ts

@ -1,256 +0,0 @@
import * as THREE from 'three'
import ItemType from '@/model/itemType/ItemType.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
import type WorldModel from '@/model/WorldModel.ts'
import type Viewport from '@/designer/Viewport.ts'
import { findObject3DByCondition, findObject3DById } from '@/model/ModelUtils.ts'
let pmFn
/**
* ILineType 线
* 线, ,
*/
export default abstract class ItemTypeLine extends ItemType {
private relationPoints: THREE.Mesh[] = []
private dragViewport: Viewport | undefined
private dragPoint: THREE.Mesh | undefined
abstract createPointBasic(position: THREE.Vector3): THREE.Object3D
abstract createLineBasic(isTemplate?: boolean): THREE.Mesh
public init(worldModel: WorldModel) {
return super.init(worldModel).then(() => {
})
}
afterCreateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
}
afterUpdateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
}
createLine(viewport: Viewport, scene: THREE.Scene, startPoint: THREE.Object3D, endPoint: THREE.Object3D): THREE.Mesh {
const line = this.createLineBasic()
const geom = line.geometry
geom.setFromPoints([startPoint.position, endPoint.position])
if (!line.userData) {
line.userData = {}
}
line.userData.lineStartId = startPoint.uuid
line.userData.lineEndId = endPoint.uuid
if (startPoint.parent) {
startPoint.parent.add(line)
} else {
scene.add(line)
}
if (!startPoint.userData.lines) {
startPoint.userData.lines = []
}
startPoint.userData.lines.push(line.uuid)
if (!endPoint.userData.lines) {
endPoint.userData.lines = []
}
endPoint.userData.lines.push(line.uuid)
this.afterCreateLine(line, startPoint, endPoint)
//@ts-ignore
if (typeof line.computeLineDistances === 'function') {
// const canvas = viewport.renderer.domElement
//@ts-ignore
// this.lineMaterial.resolution.set(canvas.width, canvas.height)
//@ts-ignore
line.computeLineDistances()
}
return line
}
/**
* center 线
*/
afterAddScene(viewport: Viewport, scene: THREE.Scene, objects: THREE.Object3D[]) {
super.afterAddScene(viewport, scene, objects)
// 为所有的 pointArray 连接线
for (let i = 0; i < this.pointArray.length; i++) {
const startPoint = this.pointArray[i]
// 找到这个元素的 userData.center 数组
const linkArray: string[] = startPoint.userData.center || []
for (let j = 0; j < linkArray.length; j++) {
const linkId = linkArray[j]
// 在 pointArray 中查找对应的点
const endPoint = findObject3DById(scene, linkId)
if (!endPoint) {
console.warn('not found link point uuid=${}', linkId)
continue
}
const line = this.createLine(viewport, scene, startPoint, endPoint)
}
}
}
/**
* ,
*/
createPoint(position: THREE.Vector3, item: ItemJson): THREE.Object3D {
const point = this.createPointBasic(position)
if (item.name) {
point.name = item.name
}
point.uuid = item.id || THREE.MathUtils.generateUUID()
point.userData = _.cloneDeep(item.dt) || {}
_.extend(point.userData, {
type: item.t,
actionType: item.a,
label: item.l,
color: item.c,
selectable: true,
protected: false
})
point.rotation.set(
THREE.MathUtils.degToRad(item.tf[1][0]),
THREE.MathUtils.degToRad(item.tf[1][1]),
THREE.MathUtils.degToRad(item.tf[1][2])
)
point.scale.set(item.tf[2][0], item.tf[2][1], item.tf[2][2])
this.pointArray.push(point)
this.afterLoadPoint(point)
return point
}
/**
* Json item
*/
override loadFromJson(item: ItemJson): undefined | THREE.Object3D {
if (item.a === 'gp') {
// gp 是为了分组而存在的
const group = new THREE.Group()
group.name = item.name
group.uuid = item.id || THREE.MathUtils.generateUUID()
group.userData = _.cloneDeep(item.dt) || {}
group.userData.type = item.t
group.userData.actionType = item.a
group.userData.label = item.l
group.userData.color = item.c
this.afterLoadGroup(group)
return group
}
// 其他情况都是 ln
else if (item.a === 'ln') {
const position = new THREE.Vector3(
item.tf[0][0],
item.tf[0][1],
item.tf[0][2]
)
return this.createPoint(position, item)
}
console.error('ItemTypeLineBase.loadFromJson: Unsupported', item)
}
/**
* DragControls
*/
dragPointStart(viewport: Viewport, point: THREE.Mesh) {
console.log('dragPoint:', point)
this.dragViewport = viewport
this.dragPoint = point
const canvas = viewport.renderer.domElement
// 收集相关联的线段和标签
const relationPoints = new Set<THREE.Object3D>()
// 找到与 point 有关联的节点, 无论是 a->b 还是 b->a 都需要收集
findObject3DByCondition(viewport.scene, (targetObj: THREE.Object3D) => {
if (targetObj.uuid === point.uuid) {
return false
}
// 无论 a->b 还是 b->a 都需要收集
if (_.includes(targetObj.userData?.center, point.uuid) || _.includes(point.userData?.center, targetObj.uuid)) {
relationPoints.add(targetObj)
}
})
//@ts-ignore
this.relationPoints = Array.from(relationPoints)
// 监听move事件
pmFn = this.redrawMousemove.bind(this)
canvas.addEventListener('pointermove', pmFn)
}
/**
* DragControls
*/
redrawMousemove(e: MouseEvent) {
if (!this.dragViewport || !this.dragPoint) return
const updateLines: THREE.Object3D[] = []
// 更新所有相关线段
_.forEach(this.relationPoints, (targetPoint, idx) => {
if (targetPoint.uuid === this.dragPoint.uuid) {
return
}
// 判断谁是起点,谁是终点
let startPoint: THREE.Object3D
let endPoint: THREE.Object3D
if (_.includes(targetPoint.userData?.center, this.dragPoint.uuid)) {
startPoint = targetPoint
endPoint = this.dragPoint
} else {
startPoint = this.dragPoint
endPoint = targetPoint
}
// 找到 startPoint 与 this.dragPoint 之间的线段
const line = findObject3DByCondition(this.dragViewport.scene, (obj) => {
return obj.userData.lineStartId === startPoint.uuid && obj.userData.lineEndId === endPoint.uuid
})[0] as THREE.Mesh
if (!line) {
// line = this.createLine(this.dragViewport.scene, startPoint, endPoint)
console.warn('Line not found between points:', startPoint.uuid, endPoint.uuid)
debugger
}
// 更新线段几何体
const geometry = line.geometry as THREE.BufferGeometry
geometry.setFromPoints([startPoint.position, endPoint.position])
geometry.attributes.position.needsUpdate = true
this.afterUpdateLine(line, startPoint, endPoint)
updateLines.push(line)
})
this.afterUpdatePoint(this.dragPoint, updateLines)
}
/**
* DragControls
*/
dragPointComplete(viewport: Viewport) {
// 删除鼠标事件监听
viewport.renderer.domElement.removeEventListener('pointermove', pmFn)
pmFn = undefined
}
}

323
src/model/itemType/Toolbox.ts

@ -1,323 +0,0 @@
import * as THREE from 'three'
import type Viewport from '@/designer/Viewport.ts'
import type ItemType from '@/model/itemType/ItemType.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
let pdFn, pmFn, puFn
/**
*
*/
export default abstract class Toolbox {
/**
* Three.js .
* :
* - viewport.scene
* - viewport.renderer
* - viewport.controls
* - viewport.camera
* - viewport.raycaster 线
* - viewport.dragControl
* - viewport.measure
*/
viewport: Viewport
/**
*
*/
isCompleted = false
/**
*
*/
mouseMoved = false
/**
* , viewport.renderer.domElement
*/
canvas: HTMLCanvasElement
/**
*
*/
tempPointMarker?: THREE.Mesh
/**
*
*/
startPoint?: THREE.Object3D = undefined
/**
* 便
* @protected
*/
lastClickTime: number = 0
/**
*
*/
lastMovePosition: THREE.Vector3 | undefined = undefined
_itemType: any
get itemType(): ItemType {
return this._itemType
}
mode: string
static TMP_TYPE = '_TMP'
/**
*
*/
abstract getTempPointName(): string
addToScene(object: THREE.Object3D) {
this.viewport.scene.add(object)
}
deletePoint(point: THREE.Object3D): void {
const deletedPoints = _.remove(this.itemType.pointArray, (p) => p.uuid === point.uuid)
if (!deletedPoints || deletedPoints.length !== 1) {
console.warn('没有找到要删除的点:', point.uuid)
return
}
// 删除点
this.removeFromScene(point)
// 如果是起始点,则清除起始点
if (this.startPoint === point) {
this.startPoint = undefined
}
}
removeFromScene(object: THREE.Object3D) {
if (object?.parent) {
object.parent.remove(object)
} else {
this.viewport.scene.remove(object)
}
}
/**
*
*/
init(viewport: any, itemType: ItemType): void {
this.viewport = viewport
this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement
this._itemType = itemType
}
/**
* , ,
*/
start(startPoint?: THREE.Object3D) {
pdFn = this.mousedown.bind(this)
this.canvas.addEventListener('pointerdown', pdFn)
pmFn = this.mousemove.bind(this)
this.canvas.addEventListener('pointermove', pmFn)
puFn = this.mouseup.bind(this)
this.canvas.addEventListener('pointerup', puFn)
this.isCompleted = false
this.viewport.viewerDom.style.cursor = 'crosshair'
this.mode = this.viewport.state.cursorMode
this.startPoint = startPoint
system.msg('新建 [' + this.itemType.name + '] 模式')
}
/**
* , 线.
*/
stop(): void {
system.msg('退出新建模式')
const viewerDom = this.viewport.viewerDom
this.isCompleted = true
viewerDom.style.cursor = ''
this.canvas.removeEventListener('pointerdown', pdFn)
pdFn = undefined
this.canvas.removeEventListener('pointermove', pmFn)
pmFn = undefined
this.canvas.removeEventListener('pointerup', puFn)
puFn = undefined
// 清空所有临时点
this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker)
this.tempPointMarker = undefined
}
/**
*
*/
createTempPointMarker(position?: THREE.Vector3): THREE.Mesh {
const p = position
const scale = this.itemType.getDefaultScale()
const rotation = this.itemType.getDefaultRotation()
const tt = new THREE.BoxGeometry(1, 1, 1)
const t2 = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 })
const obj = new THREE.Mesh(tt, t2)
obj.scale.set(scale.x, scale.y, scale.x)
obj.rotation.set(
THREE.MathUtils.degToRad(rotation.x),
THREE.MathUtils.degToRad(rotation.y),
THREE.MathUtils.degToRad(rotation.z)
)
if (p) {
obj.position.set(p.x, p.y, p.z)
}
obj.name = this.getTempPointName()
obj.userData = {
mode: this.mode,
type: Toolbox.TMP_TYPE
}
return obj
}
/**
*
*/
mousedown() {
this.mouseMoved = false
}
/**
* 线
*/
mousemove(e: MouseEvent): THREE.Vector3 | undefined {
if (this.isCompleted) return
this.mouseMoved = true
// 当前鼠标所在的点
const point = this.viewport.getClosestIntersection(e)
if (!point) {
return
}
// 如果按下了 shift 键,则 point 的位置必须位于 startPoint 正上下方,或者正左右方
if (this.startPoint && e.shiftKey) {
const startPos = this.startPoint.position
const dx = Math.abs(point.x - startPos.x)
const dz = Math.abs(point.z - startPos.z)
if (dx > dz) {
point.z = startPos.z
} else {
point.x = startPos.x
}
}
this.lastMovePosition = point
// 在鼠标移动时绘制临时点
if (this.tempPointMarker) {
this.tempPointMarker.position.set(point.x, point.y, point.z)
} else {
this.tempPointMarker = this.createTempPointMarker(point)
this.viewport.scene.add(this.tempPointMarker)
}
// this.viewport.dispatchSignal('sceneGraphChanged')
return point
}
/**
*
*/
mouseup(e: MouseEvent) {
// 如果mouseMoved是true,那么它可能在移动,而不是点击
if (!this.mouseMoved) {
if (e.button === 2) {
// 右键点击, 完成绘图操作
this.viewport.state.cursorMode = 'normal'
} else if (e.button === 0) {
// 左键点击, 添加点
this.onMouseClicked(e)
}
}
}
onMouseClicked(e: MouseEvent): THREE.Vector3 | undefined {
if (this.isCompleted) {
return
}
// 获取鼠标点击位置的三维坐标
const point = this.lastMovePosition
if (!point) {
return
}
// 双击触发两次点击事件,我们需要避免这里的第二次点击
const now = Date.now()
if (this.lastClickTime && (now - this.lastClickTime < 50)) {
return
}
this.lastClickTime = now
const defaultScale = this.itemType.getDefaultScale()
const defaultRotation = this.itemType.getDefaultRotation()
// 添加正式点
const itemJson = {
t: this.itemType.name,
a: 'ln',
tf: [
[point.x, point.y, point.z],
[defaultRotation.x, defaultRotation.y, defaultRotation.z],
[defaultScale.x, defaultScale.y, defaultScale.z]
],
dt: {
in: [] as string[],
out: [] as string[],
center: [] as string[]
}
} as ItemJson
const marker = this.itemType.createPoint(point, itemJson)
this.addToScene(marker)
// 把点加入拖拽控制器
//this.viewport.dragControl.setDragObjects([marker], 'push')
if (this.startPoint) {
this.afterAddPoint(this.startPoint, marker)
}
// 更新起始点为新添加的点
this.startPoint = marker
// 删除临时线
this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker)
this.tempPointMarker = undefined
return point
}
/**
*
*
*/
afterAddPoint(startPoint: THREE.Object3D, endPoint: THREE.Object3D): void {
// 默认实现不做任何操作
// 子类可以重写此方法来处理添加点后的逻辑
}
/**
* ,
*/
destory() {
}
}

149
src/model/itemType/ToolboxLine.ts

@ -1,149 +0,0 @@
import * as THREE from 'three'
import Toolbox from '@/model/itemType/Toolbox.ts'
import type ItemTypeLine from '@/model/itemType/ItemTypeLine.ts'
import { findObject3DById, getAllControlPoints } from '@/model/ModelUtils.ts'
import EventBus from '@/runtime/EventBus'
/**
* 线
*/
export default class ToolboxLine extends Toolbox {
/**
* 线
*/
tempLine?: THREE.Mesh
get itemType(): ItemTypeLine {
return this._itemType
}
getTempPointName(): string {
return '_measure_temp_point'
}
afterMoveTemplateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
}
afterDeleteLine(line: THREE.Object3D, point: THREE.Object3D) {
}
stop() {
super.stop()
this.tempLine && this.removeFromScene(this.tempLine)
this.tempLine = undefined
}
afterAddPoint(startPoint: THREE.Object3D, point: THREE.Object3D) {
// 如果起始点存在,则将新点添加到起始点的链接中
startPoint.userData.center.push(point.uuid)
this.itemType.createLine(this.viewport, this.viewport.scene, this.startPoint, point)
}
/**
*
*/
deletePoint(point: THREE.Object3D) {
const allPoints = getAllControlPoints()
const deletedPoints = _.remove(getAllControlPoints(), (p) => p.uuid === point.uuid)
if (!deletedPoints || deletedPoints.length !== 1) {
console.warn('没有找到要删除的点:', point.uuid)
return
}
if (this.viewport.state.selectedObject === point) {
// 如果当前选中的对象是要删除的点,则清除选中状态
this.viewport.state.selectedObject = undefined
EventBus.dispatch('objectChanged', {
viewport: this,
object: null
})
}
// 找出与这个点相关的其他点
allPoints.forEach(p => {
if (p.userData.center) {
_.remove(p.userData.center, i => i === point.uuid)
_.remove(p.userData.in, i => i === point.uuid)
_.remove(p.userData.out, i => i === point.uuid)
}
})
// 找出与点相关的所有线
_.forEach(point.userData.lines, (line) => {
const lineObject = findObject3DById(this.viewport.scene, line)
this.removeFromScene(lineObject)
this.afterDeleteLine(lineObject, point)
})
// 从场景中删除点
this.removeFromScene(point)
// 如果是起始点,则清除起始点
if (this.startPoint === point) {
this.startPoint = undefined
}
}
mousemove(e: MouseEvent): THREE.Vector3 | undefined {
const point = super.mousemove(e)
if (!point) {
return
}
// 移动时绘制临时线
if (this.startPoint) {
// 获取最后一个点
if (!this.tempLine) {
this.tempLine = this.itemType.createLineBasic(true)
this.viewport.scene.add(this.tempLine)
}
const p0 = this.startPoint.position
const line = this.tempLine
const geom = line.geometry
geom.setFromPoints([p0, point])
this.afterMoveTemplateLine(line, this.startPoint, this.tempPointMarker)
}
}
onMouseClicked(e: MouseEvent): THREE.Vector3 | undefined {
const point = this.lastMovePosition
// 如果正式的点命中到同类型的节点上,则不添加新的点,只牵线到该点
if (point) {
let catchPoint = null
const vv = this.itemType.pointArray.some(p => {
if (p.position.x === point.x && p.position.z === point.z) {
catchPoint = p
return true
}
})
if (catchPoint) {
if (this.startPoint === catchPoint) {
// 自己连接自己,忽略
return
}
// 如果捕获到点,则将线条连接到该点
if (this.startPoint) {
this.afterAddPoint(this.startPoint, catchPoint)
}
this.tempLine && this.removeFromScene(this.tempLine)
this.tempLine = undefined
return
}
}
const r = super.onMouseClicked(e)
if (!r) {
return
}
this.tempLine && this.removeFromScene(this.tempLine)
this.tempLine = undefined
return r
}
}

7
src/model/itemType/line/LineMeta.ts

@ -1,7 +0,0 @@
import { defineItem } from '@/runtime/DefineItem.ts'
export default defineItem({
name: 'line',
label: '辅助线',
category: 'line'
})

124
src/model/itemType/line/conveyor/Conveyor.ts

@ -1,124 +0,0 @@
import * as THREE from 'three'
import ItemTypeLine from '@/model/itemType/ItemTypeLine.ts'
import WorldModel from '@/model/WorldModel.ts'
import Viewport from '@/designer/Viewport.ts'
import ToolboxLine from '@/model/itemType/ToolboxLine.ts'
import ConveyorToolbox from './ConveyorToolbox.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 _ from 'lodash'
export default class Conveyor extends ItemTypeLine {
defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.1, 0.25)
defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0)
pointMaterial!: THREE.Material
lineMaterial!: LineMaterial
lineMaterialTemplate!: LineMaterial
lineMaterialOutline!: LineMaterial
static POINT_NAME = 'conveyor_point'
static LINE_NAME = 'conveyor_line'
async init(worldModel: WorldModel): Promise<void> {
await super.init(worldModel)
try {
this.pointMaterial = new THREE.MeshBasicMaterial({ color: 0x303133, transparent: true, opacity: 0.9 })
this.lineMaterial = new LineMaterial({
alphaToCoverage: true,
side: THREE.DoubleSide,
color: 0x0088ff,
dashed: true,
resolution: new THREE.Vector2(1, 1), // 需要在afterAddScene中设置
dashOffset: 0,
linewidth: 0.8,
dashScale: 1,
dashSize: 0.2,
gapSize: 0.2,
worldUnits: true
// linewidth: 10,
// worldUnits: false,
// dashSize: 0.2,
// gapSize: 0.2,
// dashScale: 1
})
this.lineMaterialTemplate = new LineMaterial({
color: 0x0088ff,
linewidth: 0.8,
worldUnits: true,
opacity: 0.5,
transparent: true,
alphaToCoverage: true,
depthWrite: false, // 避免深度冲突
blending: THREE.NormalBlending
})
window['lineMaterial'] = this.lineMaterial // 方便调试查看
} catch (error) {
system.showErrorDialog('Texture loading failed:' + error)
}
}
afterAddScene(viewport: Viewport, scene: THREE.Scene, objects: THREE.Object3D[]) {
super.afterAddScene(viewport, scene, objects)
_.defer(() => {
const canvas = viewport.renderer.domElement
// this.lineMaterial.resolution.set(canvas.width, canvas.height)
})
}
getDefaultScale(): THREE.Vector3 {
return this.defaultScale
}
getDefaultRotation(): THREE.Vector3 {
return this.defaultRotation
}
createToolbox(viewport: Viewport): ToolboxLine {
const toolbox = new ConveyorToolbox()
toolbox.init(viewport, this)
//@ts-ignore
return toolbox
}
/**
*
*/
createPointBasic(position?: THREE.Vector3): THREE.Object3D {
const p = position
const scale = 0.25
const tt = new THREE.BoxGeometry(1, 1, 1)
const obj = new THREE.Mesh(tt, this.pointMaterial)
obj.scale.set(scale, 0.1, scale)
if (p) {
obj.position.set(p.x, p.y, p.z)
}
obj.name = Conveyor.POINT_NAME
return obj
}
/**
* 线
*/
createLineBasic(isTemplate?: boolean): THREE.Mesh {
const geom = new LineGeometry()
let obj: THREE.Mesh
if (isTemplate) {
obj = new Line2(geom, this.lineMaterialTemplate)
} else {
obj = new Line2(geom, this.lineMaterial)
}
obj.frustumCulled = false
obj.name = Conveyor.LINE_NAME
obj.uuid = THREE.MathUtils.generateUUID()
return obj
}
}

9
src/model/itemType/line/conveyor/ConveyorMeta.ts

@ -1,9 +0,0 @@
import { defineItemType } from '@/model/itemType/ItemTypeDefine.ts'
import Conveyor from './Conveyor.ts'
export default defineItemType({
name: 'conveyor',
label: '输送线',
actionType: 'ln',
clazz: new Conveyor()
})

16
src/model/itemType/line/conveyor/ConveyorToolbox.ts

@ -1,16 +0,0 @@
import ToolboxLine from '@/model/itemType/ToolboxLine.ts'
import type Conveyor from './Conveyor.ts'
/**
*
*/
export default class ConveyorToolbox extends ToolboxLine {
constructor() {
super()
}
get conveyor(): Conveyor {
return this._itemType
}
}

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

@ -1,202 +0,0 @@
import * as THREE from 'three'
import { Material } from 'three'
import ItemTypeLine from '@/model/itemType/ItemTypeLine.ts'
import WorldModel from '@/model/WorldModel.ts'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { numberToString } from '@/utils/webutils.ts'
import { findObject3DById } from '@/model/ModelUtils.ts'
import Viewport from '@/designer/Viewport.ts'
import ToolboxLine from '@/model/itemType/ToolboxLine.ts'
import MeasureToolbox from '@/model/itemType/measure/MeasureToolbox.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 {
/**
* , 线. 线
*/
group: THREE.Group
defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.1, 0.25)
defaultRotation: THREE.Vector3 = new THREE.Vector3(0, 0, 0)
pointMaterial!: Material
lineMaterial!: LineMaterial
static GROUP_NAME = 'measure-group'
static LABEL_NAME = 'measure_label'
static POINT_NAME = 'measure_point'
static LINE_NAME = 'measure_line'
override init(worldModel: WorldModel): Promise<void> {
super.init(worldModel)
// this.lineMaterial = new THREE.LineBasicMaterial({
// color: 0xE63C17,
// linewidth: 2,
// opacity: 0.9,
// transparent: true,
// side: THREE.DoubleSide,
// depthWrite: 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 })
return Promise.resolve()
}
getDefaultScale(): THREE.Vector3 {
return this.defaultScale
}
getDefaultRotation(): THREE.Vector3 {
return this.defaultRotation
}
beforeLoad(): THREE.Object3D[] {
this.group = null
return []
}
createToolbox(viewport: Viewport): ToolboxLine {
const toolbox = new MeasureToolbox(this.group)
toolbox.init(viewport, this)
//@ts-ignore
return toolbox
}
afterLoadGroup(group: THREE.Group) {
super.afterLoadGroup(group)
if (this.group) {
// 如果已经有 group,则忽略
return
}
this.group = group
}
afterLoadComplete(objects: THREE.Object3D[]): THREE.Object3D[] {
// 如果没有 group,则创建一个新的 group
if (!this.group) {
this.group = new THREE.Group()
this.group.name = Measure.GROUP_NAME
}
return [this.group]
}
/**
*
*/
createPointBasic(position?: THREE.Vector3): THREE.Object3D {
const p = position
const scale = 0.25
const tt = new THREE.BoxGeometry(1, 1, 1)
const obj = new THREE.Mesh(tt, this.pointMaterial)
obj.scale.set(scale, 0.1, scale)
if (p) {
obj.position.set(p.x, p.y, p.z)
}
obj.name = Measure.POINT_NAME
return obj
}
/**
* 线
*/
createLineBasic(): Line2 {
const geom = new LineGeometry()
const obj = new Line2(geom, this.lineMaterial)
obj.frustumCulled = false
obj.name = Measure.LINE_NAME
obj.uuid = THREE.MathUtils.generateUUID()
return obj
}
// 创建完线之后,创建 label
afterCreateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterCreateLine(line, startPoint, endPoint)
if (!startPoint.userData.center) {
startPoint.userData.center = []
}
if (!startPoint.userData.center.includes(endPoint.uuid)) {
startPoint.userData.center.push(endPoint.uuid)
}
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)
const labelObj = this.createLabel(label)
labelObj.name = Measure.LABEL_NAME
labelObj.position.set(position.x, position.y, position.z)
labelObj.element.innerHTML = label
line.userData.labelId = labelObj.uuid
this.group.add(labelObj)
}
afterUpdateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterUpdateLine(line, startPoint, endPoint)
const p0 = startPoint.position
const p1 = endPoint.position
const dist = p0.distanceTo(p1)
const label = `${numberToString(dist)}`
const position = new THREE.Vector3().addVectors(p0, p1).multiplyScalar(0.5)
const labelObj: CSS2DObject = findObject3DById(this.group, line.userData.labelId) as CSS2DObject
if (labelObj) {
labelObj.position.set(position.x, position.y, position.z)
labelObj.element.innerHTML = label
} else {
// 如果没有找到,则创建新的标签
const newLabelObj = this.createLabel(label)
newLabelObj.position.set(position.x, position.y, position.z)
newLabelObj.element.innerHTML = label
line.userData.labelId = labelObj.uuid
this.group.add(newLabelObj)
}
}
/**
*
*/
createLabel(text: string): CSS2DObject {
const div = document.createElement('div')
div.className = 'css2dObjectLabel'
div.innerHTML = text
div.style.padding = '5px 8px'
div.style.color = '#fff'
div.style.fontSize = '14px'
div.style.position = 'absolute'
div.style.backgroundColor = 'rgba(25, 25, 25, 0.3)'
div.style.borderRadius = '12px'
div.style.top = '0px'
div.style.left = '0px'
// div.style.pointerEvents = 'none' //避免HTML元素影响场景的鼠标事件
const obj = new CSS2DObject(div)
obj.name = MeasureToolbox.TMP_LABEL_NAME
obj.userData = {
type: Toolbox.TMP_TYPE
}
return obj
}
}

36
src/model/itemType/measure/MeasureMeta.ts

@ -1,36 +0,0 @@
import * as THREE from 'three'
import Measure from '@/model/itemType/measure/Measure.ts'
import {
defineItemType,
BASIC_META_OF_POINT,
BASIC_META_OF_POINT2,
BASIC_META_OF_LINE,
BASIC_META_OF_LINE2,
type ItemTypeMeta
} from '@/model/itemType/ItemTypeDefine.ts'
export default defineItemType({
name: 'measure',
label: '测量距离',
actionType: 'ln',
clazz: new Measure(),
/**
*
*/
getMeta(object: THREE.Object3D): ItemTypeMeta {
if (object.name === Measure.LINE_NAME) {
return [
...BASIC_META_OF_LINE,
...BASIC_META_OF_LINE2
]
} else if (object.name === Measure.POINT_NAME) {
return [
...BASIC_META_OF_POINT,
...BASIC_META_OF_POINT2
]
}
return []
}
})

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

@ -1,94 +0,0 @@
import * as THREE from 'three'
import ToolboxLine from '@/model/itemType/ToolboxLine.ts'
import { numberToString } from '@/utils/webutils.ts'
import type Measure from './Measure.ts'
import type { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import Toolbox from '@/model/itemType/Toolbox.ts'
import { findObject3DById } from '@/model/ModelUtils.ts'
/**
*
*/
export default class MeasureToolbox extends ToolboxLine {
group: THREE.Group
/**
* ,
*/
tempLabel?: CSS2DObject
static TMP_LABEL_NAME = '_measure_temp_label'
constructor(group: THREE.Group) {
super()
this.group = group
}
stop() {
super.stop()
// 清除临时标签
this.tempLabel && this.removeFromScene(this.tempLabel)
this.tempLabel = undefined
}
onMouseClicked(e: MouseEvent): THREE.Vector3 | undefined {
const r = super.onMouseClicked(e)
if (!r) {
return
}
this.tempLabel && this.removeFromScene(this.tempLabel)
this.tempLabel = undefined
}
get measure(): Measure {
return this._itemType
}
addToScene(object: THREE.Object3D) {
this.measure.group.add(object)
}
afterMoveTemplateLine(line: THREE.Mesh, startPoint: THREE.Object3D, endPoint: THREE.Object3D) {
super.afterMoveTemplateLine(line, startPoint, endPoint)
const p0 = startPoint.position
const point = endPoint.position
const dist = p0.distanceTo(point)
const label = `${numberToString(dist)} m`
const position = new THREE.Vector3().addVectors(p0, point).multiplyScalar(0.5)
this.addOrUpdateTempLabel(line, label, position)
}
afterDeleteLine(line: THREE.Object3D, point: THREE.Object3D) {
super.afterDeleteLine(line, point)
// 删除临时标签
if (line?.userData?.labelId) {
const label = findObject3DById(this.measure.group, line.userData.labelId)
this.removeFromScene(label)
}
}
/**
*
*/
addOrUpdateTempLabel(line: THREE.Mesh, label: string, position: THREE.Vector3) {
if (!this.tempLabel) {
this.tempLabel = this.measure.createLabel(label)
this.tempLabel.name = MeasureToolbox.TMP_LABEL_NAME
this.tempLabel.uuid = THREE.MathUtils.generateUUID()
this.tempLabel.userData = {
mode: this.mode,
type: Toolbox.TMP_TYPE
}
line.userData.labelId = this.tempLabel.uuid
this.addToScene(this.tempLabel)
}
this.tempLabel.position.set(position.x, position.y, position.z)
this.tempLabel.element.innerHTML = label
}
}

7
src/model/itemType/point/PointMeta.ts

@ -1,7 +0,0 @@
import { defineItem } from '@/runtime/DefineItem.ts'
export default defineItem({
name: 'point',
label: '辅助点',
category: 'point'
})

7
src/model/itemType/store/QueueMeta.ts

@ -1,7 +0,0 @@
import { defineItem } from '@/runtime/DefineItem.ts'
export default defineItem({
name: 'queue',
label: '暂存区',
category: 'store'
})

5
src/modules/measure/MeasureRenderer.ts

@ -78,8 +78,9 @@ export default class MeasureRenderer extends BaseRenderer {
})
}
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
super.afterDeleteLine(start, end, type, option, object)
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption) {
const lineId = getLineId(start.id, end.id, type)
const object = this.tempViewport.entityManager.findLineObjectById(lineId)
this.tempViewport.labelManager.removeLabel(object)
}

260
src/modules/way/WayRenderer.ts

@ -1,11 +1,12 @@
import * as THREE from 'three'
import BaseRenderer from '@/core/base/BaseRenderer.ts'
import MoveLinePointPng from '@/assets/images/moveline_point.png'
import { getLineId, linkPlaneByPoint } from '@/core/ModelUtils.ts'
import { createLinkPlaneMatrix4, getCargoLineId, getLinkDirection } from '@/core/ModelUtils.ts'
import Constract from '@/core/Constract.ts'
import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts'
import InstancePointManager from '@/core/manager/InstancePointManager.ts'
import type { Object3DLike } from '@/types/ModelTypes.ts'
import TriangleUrl from '@/assets/images/conveyor/shapes/triangle.png'
import Triangle2Url from '@/assets/images/conveyor/shapes/triangle2.png'
import InstanceMeshManager from '@/core/manager/InstanceMeshManager.ts'
/**
@ -15,6 +16,7 @@ export default class WayRenderer extends BaseRenderer {
static LABEL_NAME = 'way_label'
static POINT_NAME = 'way_point'
static LINE_NAME = 'way_line'
static GUIDEWAY_LINE_NAME = 'guideway'
pointGeometry: THREE.BufferGeometry
pointMaterial: THREE.Material
@ -35,6 +37,7 @@ export default class WayRenderer extends BaseRenderer {
})
dirGeometry: THREE.PlaneGeometry
dirMaterial: THREE.Material
dir2Material: THREE.Material
/**
* ,
@ -50,9 +53,10 @@ export default class WayRenderer extends BaseRenderer {
return Promise.all([
super.init(),
new THREE.TextureLoader().loadAsync(MoveLinePointPng),
new THREE.TextureLoader().loadAsync(TriangleUrl)
new THREE.TextureLoader().loadAsync(TriangleUrl),
new THREE.TextureLoader().loadAsync(Triangle2Url)
]).then(([_, texture, dirTexture]) => {
]).then(([_, texture, dirTexture, dir2Texture]) => {
texture.flipY = false
this.pointGeometry = new THREE.PlaneGeometry(1, 1).rotateX(-Math.PI / 2)
@ -68,9 +72,8 @@ export default class WayRenderer extends BaseRenderer {
this.dirGeometry = new THREE.PlaneGeometry(1, 1)
this.dirGeometry.rotateX(-Math.PI / 2)
.rotateY(Math.PI / 2)
.rotateY(-Math.PI / 2)
this.dirGeometry.center()
this.dirMaterial = new THREE.MeshBasicMaterial({
map: dirTexture,
transparent: true,
@ -78,6 +81,14 @@ export default class WayRenderer extends BaseRenderer {
depthWrite: false,
side: THREE.DoubleSide
})
this.dir2Material = new THREE.MeshBasicMaterial({
map: dir2Texture,
transparent: true,
opacity: 1,
depthWrite: false,
side: THREE.DoubleSide
})
})
}
@ -91,53 +102,39 @@ export default class WayRenderer extends BaseRenderer {
return this.pointManager.createPoint(item)
}
createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike {
const lineId = getLineId(start.id, end.id, type)
// 用 PlaneGeometry 替代线段
const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2])
const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2])
const wrap = this.lineManager.createPointSimple(lineId)
const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth)
wrap.applyMatrix4(matrix)
return wrap
createLine(start: ItemJson, end: ItemJson, type: LinkType): Object3DLike {
if (start.t === this.itemTypeName && end.t === this.itemTypeName) {
return this._createOrUpdateGuideway(start, end, type)
} else {
throw new Error('目前只支持二维码站点之间的连接')
}
}
updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
const lineId = getLineId(start.id, end.id, type)
const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2])
const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2])
if (start.t === this.itemTypeName && end.t === this.itemTypeName) {
this.deleteLine(start, end, type)
this._createOrUpdateGuideway(start, end, type)
const wrap = this.tempViewport.entityManager.findLineObjectById(lineId) as PointManageWrap
const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth)
wrap.applyMatrix4(matrix)
} else {
throw new Error('目前只支持二维码站点之间的连接')
}
}
afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
// 如果起点和终点类型, 都是 二维码站点, 就画 Guideway
private _createOrUpdateGuideway(start: ItemJson, end: ItemJson, type: LinkType) {
const { lineId, direction } = getLinkDirection(this.tempViewport, WayRenderer.GUIDEWAY_LINE_NAME, start, end)
if (!direction) {
// 忽略创建
return
}
const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2])
const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2])
const wrap = this.guidewayManager.create(lineId, {})
wrap.uuid = lineId
const matrix = createLinkPlaneMatrix4(startPosition, endPosition, this.rendererOption.lineWidth, direction)
wrap.setMatrix4(matrix)
// =============== 下面这一段不要删除,用来显示数字标签 =============================
// this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, {
// useHtmlLabel: false,
// fontSize: 0.2,
// color: '#333333',
// format: (distance) => {
// return (distance * 1000).toFixed(0)
// }
// })
// ==========================================================================
const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth)
if (object.userData.dirWraps) {
// 清空之前的箭头
object.userData.dirWraps.forEach((uuid: string) => {
this.dirPointManager.delete(uuid)
})
}
const length = startPosition.distanceTo(endPosition)
if (length < 0.1) {
@ -146,49 +143,160 @@ export default class WayRenderer extends BaseRenderer {
} else if (length < 3) {
// 如果两点距离小于 3m,则在中间添加一个方向指示器
const dirWrap = this.dirPointManager.create(object.uuid + '_dir')
let dirWrap
if (direction === '<->') {
dirWrap = this.dir2PointManager.create(lineId + '_dir')
} else {
dirWrap = this.dirPointManager.create(lineId + '_dir')
}
const dummy = new THREE.Object3D()
dummy.position.setFromMatrixPosition(matrix)
dummy.rotation.setFromRotationMatrix(matrix)
// dummy.rotation.setFromRotationMatrix(matrix)
if (direction === '<->') {
dummy.lookAt(endPosition)
} else if (direction === '->') {
dummy.lookAt(endPosition)
} else if (direction === '<-') {
dummy.lookAt(startPosition)
}
dummy.scale.set(0.4, 0.01, 0.2)
dummy.updateMatrix()
dirWrap.setMatrix4(dummy.matrix)
object.userData.dirWraps = [dirWrap.uuid]
wrap.userData.dirWraps = [dirWrap.uuid]
} else {
// 否则每隔 3m 添加一个方向指示器
for (let i = 0; i < length; i += 3) {
const dirWrap = this.dirPointManager.create(object.uuid + '_dir_' + i)
for (let i = 1; i < length - 0.1; i += 3) {
let dirWrap
if (direction === '<->') {
dirWrap = this.dir2PointManager.create(lineId + '_dir_' + i)
} else {
dirWrap = this.dirPointManager.create(lineId + '_dir_' + i)
}
const position = startPosition.clone().lerp(endPosition, i / length)
const dummy = new THREE.Object3D()
dummy.position.copy(position)
dummy.lookAt(endPosition)
if (direction === '<->') {
dummy.lookAt(endPosition)
} else if (direction === '->') {
dummy.lookAt(endPosition)
} else if (direction === '<-') {
dummy.lookAt(startPosition)
}
dummy.scale.set(0.4, 0.01, 0.2)
dummy.updateMatrix()
dirWrap.setMatrix4(dummy.matrix)
if (!object.userData.dirWraps) {
object.userData.dirWraps = []
if (!wrap.userData.dirWraps) {
wrap.userData.dirWraps = []
}
object.userData.dirWraps.push(dirWrap.uuid)
wrap.userData.dirWraps.push(dirWrap.uuid)
}
}
/**
*
* way11 <-> way12
* way21 -> way22
* way31 <- way32
*/
this.tempViewport.entityManager.appendLineObject(lineId, wrap)
// console.log(start.id + direction + end.id)
return wrap
}
afterDeleteLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
deleteLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) {
if (start.t === this.itemTypeName && end.t === this.itemTypeName) {
// 二维码站点之间的连接
const lineId = getCargoLineId(WayRenderer.GUIDEWAY_LINE_NAME, start.id, end.id)
const object = this.tempViewport.entityManager.findLineObjectById(lineId)
// 删除箭头
if (object?.userData.dirWraps) {
object.userData.dirWraps.forEach((uuid: string) => {
this.dirPointManager.delete(uuid)
this.dir2PointManager.delete(uuid)
})
}
// 删除这条线
this.guidewayManager.delete(lineId)
this.tempViewport.entityManager.deleteLineObjectOnly(lineId)
} else {
throw new Error('目前只支持二维码站点之间的连接')
}
}
afterCreateOrUpdateLine(start: ItemJson, end: ItemJson, type: LinkType, option: RendererCudOption, object: Object3DLike) {
const startPosition = new THREE.Vector3(start.tf[0][0], this.defulePositionY, start.tf[0][2])
const endPosition = new THREE.Vector3(end.tf[0][0], this.defulePositionY, end.tf[0][2])
// =============== 下面这一段不要删除,用来显示数字标签 =============================
// super.afterDeleteLine(start, end, type, option, object)
// this.tempViewport.labelManager.removeLabel(object)
// this.tempViewport.labelManager.createOrUpdateLabelByDistance(object, startPoint.position, endPoint.position, {
// useHtmlLabel: false,
// fontSize: 0.2,
// color: '#333333',
// format: (distance) => {
// return (distance * 1000).toFixed(0)
// }
// })
// ==========================================================================
if (object.userData.dirWraps) {
// 清空之前的箭头
object.userData.dirWraps.forEach((uuid: string) => {
this.dirPointManager.delete(uuid)
})
}
// const matrix = linkPlaneByPoint(startPosition, endPosition, this.rendererOption.lineWidth)
//
// if (object.userData.dirWraps) {
// // 清空之前的箭头
// object.userData.dirWraps.forEach((uuid: string) => {
// this.dirPointManager.delete(uuid)
// })
// }
//
// const length = startPosition.distanceTo(endPosition)
// if (length < 0.1) {
// // 如果两点距离小于 0.1m,则不添加方向指示器
// return
//
// } else if (length < 3) {
// // 如果两点距离小于 3m,则在中间添加一个方向指示器
// const dirWrap = this.dirPointManager.create(object.uuid + '_dir')
//
// const dummy = new THREE.Object3D()
// dummy.position.setFromMatrixPosition(matrix)
// dummy.rotation.setFromRotationMatrix(matrix)
// dummy.scale.set(0.4, 0.01, 0.2)
// dummy.updateMatrix()
// dirWrap.setMatrix4(dummy.matrix)
// object.userData.dirWraps = [dirWrap.uuid]
//
// } else {
// // 否则每隔 3m 添加一个方向指示器
// for (let i = 0; i < length; i += 3) {
// const dirWrap = this.dirPointManager.create(object.uuid + '_dir_' + i)
// const position = startPosition.clone().lerp(endPosition, i / length)
// const dummy = new THREE.Object3D()
// dummy.position.copy(position)
// dummy.lookAt(endPosition)
// dummy.scale.set(0.4, 0.01, 0.2)
// dummy.updateMatrix()
//
// dirWrap.setMatrix4(dummy.matrix)
//
// if (!object.userData.dirWraps) {
// object.userData.dirWraps = []
// }
// object.userData.dirWraps.push(dirWrap.uuid)
// }
// }
}
dispose() {
@ -197,18 +305,18 @@ export default class WayRenderer extends BaseRenderer {
this.lineMaterial?.dispose()
}
get lineManager(): InstancePointManager {
get guidewayManager(): InstanceMeshManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set.')
}
return this.tempViewport.getOrCreatePointManager(this.itemTypeName + '_line', () =>
const name = WayRenderer.GUIDEWAY_LINE_NAME
return this.tempViewport.getOrCreateMeshManager(name, () => {
// 构建 LineSegment.points 代理对象
InstancePointManager.create(this.itemTypeName + '_line',
this.tempViewport,
this.lineGeometry,
this.lineMaterial,
return new InstanceMeshManager(name, this.tempViewport, this.lineGeometry, this.lineMaterial,
false, false)
)
})
}
get pointManager(): InstancePointManager {
@ -238,4 +346,18 @@ export default class WayRenderer extends BaseRenderer {
false, false)
)
}
get dir2PointManager(): InstanceMeshManager {
if (!this.tempViewport) {
throw new Error('tempViewport is not set.')
}
return this.tempViewport.getOrCreateMeshManager(this.itemTypeName + '_dir2', () =>
// 构建 InstanceMesh 代理对象
new InstanceMeshManager(this.itemTypeName + '_dir2',
this.tempViewport,
this.dirGeometry,
this.dir2Material,
false, false)
)
}
}

7
src/types/ModelTypes.ts

@ -2,6 +2,7 @@ import { Object3D } from 'three'
import LineSegmentManager, { LineManageWrap } from '@/core/manager/LineSegmentManager.ts'
import InstancePointManager, { PointManageWrap } from '@/core/manager/InstancePointManager.ts'
import * as THREE from 'three'
import { InstanceMeshWrap } from '@/core/manager/InstanceMeshManager.ts'
//
// /**
// * 点数据接口, 用于平衡 Object3D 一致的取数方式
@ -66,7 +67,7 @@ export interface PointManagerReference {
id: string
}
export type Object3DLike = Object3D | LineManageWrap | PointManageWrap
export type Object3DLike = Object3D | LineManageWrap | PointManageWrap | InstanceMeshWrap
/**
* , THREE.Vector3
@ -79,5 +80,5 @@ export type Vector3Like = THREE.Vector3 | number[]
export const MaterialQualityEnum = {
High: 0,
Middle: 1,
Low: 2,
};
Low: 2
}

1
src/types/Types.d.ts

@ -1,3 +1,4 @@
type LinkDirection = '<-' | '->' | '<->' | ''
type CursorMode =
'normal'
| 'ALink'

Loading…
Cancel
Save