28 changed files with 48433 additions and 1583 deletions
|
After Width: | Height: | Size: 2.5 KiB |
@ -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 = [] |
|||
File diff suppressed because it is too large
@ -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) |
|||
} |
|||
@ -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' |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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() { |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
import { defineItem } from '@/runtime/DefineItem.ts' |
|||
|
|||
export default defineItem({ |
|||
name: 'line', |
|||
label: '辅助线', |
|||
category: 'line' |
|||
}) |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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() |
|||
}) |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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 [] |
|||
} |
|||
}) |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -1,7 +0,0 @@ |
|||
import { defineItem } from '@/runtime/DefineItem.ts' |
|||
|
|||
export default defineItem({ |
|||
name: 'point', |
|||
label: '辅助点', |
|||
category: 'point' |
|||
}) |
|||
@ -1,7 +0,0 @@ |
|||
import { defineItem } from '@/runtime/DefineItem.ts' |
|||
|
|||
export default defineItem({ |
|||
name: 'queue', |
|||
label: '暂存区', |
|||
category: 'store' |
|||
}) |
|||
Loading…
Reference in new issue