13 changed files with 394 additions and 616 deletions
@ -1,211 +1,214 @@ |
|||
import { |
|||
EventDispatcher, |
|||
Matrix4, |
|||
Plane, |
|||
Raycaster, |
|||
Vector2, |
|||
Vector3 |
|||
} from 'three'; |
|||
|
|||
const _plane = new Plane(); |
|||
const _raycaster = new Raycaster(); |
|||
|
|||
const _pointer = new Vector2(); |
|||
const _offset = new Vector3(); |
|||
const _intersection = new Vector3(); |
|||
const _worldPosition = new Vector3(); |
|||
const _inverseMatrix = new Matrix4(); |
|||
EventDispatcher, |
|||
Matrix4, |
|||
Plane, |
|||
Raycaster, |
|||
Vector2, |
|||
Vector3 |
|||
} from 'three' |
|||
import { calcPositionUseSnap } from '@/model/ModelUtils.js' |
|||
|
|||
const _plane = new Plane() |
|||
const _raycaster = new Raycaster() |
|||
|
|||
const _pointer = new Vector2() |
|||
const _offset = new Vector3() |
|||
const _intersection = new Vector3() |
|||
const _worldPosition = new Vector3() |
|||
const _inverseMatrix = new Matrix4() |
|||
|
|||
class DragControls extends EventDispatcher { |
|||
constructor( _objects, _camera, _domElement ) { |
|||
super(); |
|||
constructor(_objects, _camera, _domElement) { |
|||
super() |
|||
|
|||
_domElement.style.touchAction = 'none'; // disable touch scroll
|
|||
_domElement.style.touchAction = 'none' // disable touch scroll
|
|||
|
|||
let _selected = null, _hovered = null; |
|||
let _selected = null, _hovered = null |
|||
|
|||
const _intersections = []; |
|||
const _intersections = [] |
|||
|
|||
//
|
|||
//
|
|||
|
|||
let isMove = false; |
|||
let isMove = false |
|||
|
|||
const scope = this; |
|||
const scope = this |
|||
|
|||
function activate() { |
|||
_domElement.addEventListener( 'pointermove', onPointerMove ); |
|||
_domElement.addEventListener( 'pointerdown', onPointerDown ); |
|||
_domElement.addEventListener( 'pointerup', onPointerCancel ); |
|||
_domElement.addEventListener( 'pointerleave', onPointerCancel ); |
|||
} |
|||
function activate() { |
|||
_domElement.addEventListener('pointermove', onPointerMove) |
|||
_domElement.addEventListener('pointerdown', onPointerDown) |
|||
_domElement.addEventListener('pointerup', onPointerCancel) |
|||
_domElement.addEventListener('pointerleave', onPointerCancel) |
|||
} |
|||
|
|||
function deactivate() { |
|||
_domElement.removeEventListener( 'pointermove', onPointerMove ); |
|||
_domElement.removeEventListener( 'pointerdown', onPointerDown ); |
|||
_domElement.removeEventListener( 'pointerup', onPointerCancel ); |
|||
_domElement.removeEventListener( 'pointerleave', onPointerCancel ); |
|||
function deactivate() { |
|||
_domElement.removeEventListener('pointermove', onPointerMove) |
|||
_domElement.removeEventListener('pointerdown', onPointerDown) |
|||
_domElement.removeEventListener('pointerup', onPointerCancel) |
|||
_domElement.removeEventListener('pointerleave', onPointerCancel) |
|||
|
|||
_domElement.style.cursor = ''; |
|||
} |
|||
_domElement.style.cursor = '' |
|||
} |
|||
|
|||
function dispose() { |
|||
deactivate(); |
|||
} |
|||
function dispose() { |
|||
deactivate() |
|||
} |
|||
|
|||
function setObjects( objects ) { |
|||
_objects = objects; |
|||
} |
|||
function setObjects(objects) { |
|||
_objects = objects |
|||
} |
|||
|
|||
function getObjects() { |
|||
return _objects; |
|||
} |
|||
function getObjects() { |
|||
return _objects |
|||
} |
|||
|
|||
function getRaycaster() { |
|||
return _raycaster; |
|||
} |
|||
function getRaycaster() { |
|||
return _raycaster |
|||
} |
|||
|
|||
function onPointerMove( event ) { |
|||
function onPointerMove(event) { |
|||
|
|||
if ( !scope.enabled || !scope.enabledMove) return; |
|||
if (!scope.enabled || !scope.enabledMove) return |
|||
|
|||
isMove = true; |
|||
isMove = true |
|||
|
|||
updatePointer( event ); |
|||
updatePointer(event) |
|||
|
|||
_raycaster.setFromCamera( _pointer, _camera ); |
|||
_raycaster.setFromCamera(_pointer, _camera) |
|||
|
|||
if ( _selected ) { |
|||
if (_selected) { |
|||
|
|||
if ( _raycaster.ray.intersectPlane( _plane, _intersection ) ) { |
|||
if (_raycaster.ray.intersectPlane(_plane, _intersection)) { |
|||
|
|||
_selected.position.copy( _intersection.sub( _offset ).applyMatrix4( _inverseMatrix ) ); |
|||
const pos = _intersection.sub(_offset).applyMatrix4(_inverseMatrix) |
|||
const newIntersection = calcPositionUseSnap(event, pos) |
|||
_selected.position.copy(newIntersection) |
|||
|
|||
} |
|||
} |
|||
|
|||
scope.dispatchEvent( { type: 'drag', object: _selected } ); |
|||
scope.dispatchEvent({ type: 'drag', object: _selected }) |
|||
|
|||
return; |
|||
return |
|||
|
|||
} |
|||
} |
|||
|
|||
// hover support
|
|||
// hover support
|
|||
|
|||
if ( event.pointerType === 'mouse' || event.pointerType === 'pen' ) { |
|||
if (event.pointerType === 'mouse' || event.pointerType === 'pen') { |
|||
|
|||
_intersections.length = 0; |
|||
_intersections.length = 0 |
|||
|
|||
_raycaster.setFromCamera( _pointer, _camera ); |
|||
_raycaster.intersectObjects( _objects, true, _intersections ); |
|||
_raycaster.setFromCamera(_pointer, _camera) |
|||
_raycaster.intersectObjects(_objects, true, _intersections) |
|||
|
|||
if ( _intersections.length > 0 ) { |
|||
if (_intersections.length > 0) { |
|||
|
|||
const object = _intersections[ 0 ].object; |
|||
const object = _intersections[0].object |
|||
|
|||
_plane.setFromNormalAndCoplanarPoint( _camera.getWorldDirection( _plane.normal ), _worldPosition.setFromMatrixPosition( object.matrixWorld ) ); |
|||
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(object.matrixWorld)) |
|||
|
|||
if ( _hovered !== object && _hovered !== null ) { |
|||
if (_hovered !== object && _hovered !== null) { |
|||
|
|||
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } ); |
|||
scope.dispatchEvent({ type: 'hoveroff', object: _hovered }) |
|||
|
|||
_domElement.style.cursor = 'auto'; |
|||
_hovered = null; |
|||
_domElement.style.cursor = 'auto' |
|||
_hovered = null |
|||
|
|||
} |
|||
} |
|||
|
|||
if ( _hovered !== object ) { |
|||
if (_hovered !== object) { |
|||
|
|||
scope.dispatchEvent( { type: 'hoveron', object: object } ); |
|||
scope.dispatchEvent({ type: 'hoveron', object: object }) |
|||
|
|||
_domElement.style.cursor = 'pointer'; |
|||
_hovered = object; |
|||
_domElement.style.cursor = 'pointer' |
|||
_hovered = object |
|||
|
|||
} |
|||
} |
|||
|
|||
} else { |
|||
} else { |
|||
|
|||
if ( _hovered !== null ) { |
|||
if (_hovered !== null) { |
|||
|
|||
scope.dispatchEvent( { type: 'hoveroff', object: _hovered } ); |
|||
scope.dispatchEvent({ type: 'hoveroff', object: _hovered }) |
|||
|
|||
_domElement.style.cursor = 'auto'; |
|||
_hovered = null; |
|||
_domElement.style.cursor = 'auto' |
|||
_hovered = null |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
function onPointerDown( event ) { |
|||
if (scope.enabled === false) return; |
|||
function onPointerDown(event) { |
|||
if (scope.enabled === false) return |
|||
|
|||
updatePointer(event); |
|||
updatePointer(event) |
|||
|
|||
_intersections.length = 0; |
|||
_intersections.length = 0 |
|||
|
|||
_raycaster.setFromCamera( _pointer, _camera ); |
|||
let objects = _objects; |
|||
_raycaster.setFromCamera(_pointer, _camera) |
|||
let objects = _objects |
|||
|
|||
_raycaster.intersectObjects( objects, true, _intersections ); |
|||
_raycaster.intersectObjects(objects, true, _intersections) |
|||
|
|||
if (_intersections.length > 0) { |
|||
_selected = (scope.transformGroup === true) ? _objects[ 0 ] : _intersections[0].object; |
|||
if (_intersections.length > 0) { |
|||
_selected = (scope.transformGroup === true) ? _objects[0] : _intersections[0].object |
|||
|
|||
if(scope.enabledMove) { |
|||
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld)); |
|||
if (_raycaster.ray.intersectPlane(_plane, _intersection)) { |
|||
_inverseMatrix.copy(_selected.parent.matrixWorld).invert(); |
|||
_offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld)); |
|||
} |
|||
if (scope.enabledMove) { |
|||
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld)) |
|||
if (_raycaster.ray.intersectPlane(_plane, _intersection)) { |
|||
_inverseMatrix.copy(_selected.parent.matrixWorld).invert() |
|||
_offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld)) |
|||
} |
|||
|
|||
_domElement.style.cursor = 'move'; |
|||
} |
|||
scope.dispatchEvent( { type: 'dragstart', object: _selected,e:event } ); |
|||
} |
|||
_domElement.style.cursor = 'move' |
|||
} |
|||
scope.dispatchEvent({ type: 'dragstart', object: _selected, e: event }) |
|||
} |
|||
|
|||
isMove = false; |
|||
} |
|||
isMove = false |
|||
} |
|||
|
|||
function onPointerCancel(event) { |
|||
if ( scope.enabled === false ) return; |
|||
function onPointerCancel(event) { |
|||
if (scope.enabled === false) return |
|||
|
|||
if ( _selected ) { |
|||
scope.dispatchEvent( { type: 'dragend', object: _selected,e:event } ); |
|||
_selected = null; |
|||
}else if(!isMove){ |
|||
// 添加点击空白处的事件
|
|||
scope.dispatchEvent( { type: 'clickblank',e:event } ); |
|||
} |
|||
if (_selected) { |
|||
scope.dispatchEvent({ type: 'dragend', object: _selected, e: event }) |
|||
_selected = null |
|||
} else if (!isMove) { |
|||
// 添加点击空白处的事件
|
|||
scope.dispatchEvent({ type: 'clickblank', e: event }) |
|||
} |
|||
|
|||
_domElement.style.cursor = _hovered ? 'pointer' : 'auto'; |
|||
} |
|||
_domElement.style.cursor = _hovered ? 'pointer' : 'auto' |
|||
} |
|||
|
|||
function updatePointer( event ) { |
|||
const rect = _domElement.getBoundingClientRect(); |
|||
function updatePointer(event) { |
|||
const rect = _domElement.getBoundingClientRect() |
|||
|
|||
_pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1; |
|||
_pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1; |
|||
} |
|||
_pointer.x = (event.clientX - rect.left) / rect.width * 2 - 1 |
|||
_pointer.y = -(event.clientY - rect.top) / rect.height * 2 + 1 |
|||
} |
|||
|
|||
activate(); |
|||
activate() |
|||
|
|||
// API
|
|||
// API
|
|||
|
|||
this.enabled = true; |
|||
this.enabledMove = true; |
|||
this.transformGroup = false; |
|||
this.enabled = true |
|||
this.enabledMove = true |
|||
this.transformGroup = false |
|||
|
|||
this.activate = activate; |
|||
this.deactivate = deactivate; |
|||
this.dispose = dispose; |
|||
this.setObjects = setObjects; |
|||
this.getObjects = getObjects; |
|||
this.getRaycaster = getRaycaster; |
|||
this.activate = activate |
|||
this.deactivate = deactivate |
|||
this.dispose = dispose |
|||
this.setObjects = setObjects |
|||
this.getObjects = getObjects |
|||
this.getRaycaster = getRaycaster |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
export { DragControls }; |
|||
export { DragControls } |
|||
|
|||
@ -1,22 +0,0 @@ |
|||
export interface ICursorTool { |
|||
|
|||
/** |
|||
* 初始化工具 |
|||
*/ |
|||
init(viewport: any): void |
|||
|
|||
/** |
|||
* 开始运行工具, 进入 cursor 状态 |
|||
*/ |
|||
start(): void |
|||
|
|||
/** |
|||
* 停止运行工具, 鼠标点击右键, 或者 ESC 退出 cursor 状态 |
|||
*/ |
|||
stop(): void |
|||
|
|||
/** |
|||
* 销毁工具, 释放资源 |
|||
*/ |
|||
destory():void |
|||
} |
|||
@ -1,321 +0,0 @@ |
|||
import * as THREE from 'three' |
|||
import Viewport from '@/designer/Viewport.ts' |
|||
import { getUnitString, numberToString } from '@/utils/webutils' |
|||
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' |
|||
import Constract from '@/designer/Constract.ts' |
|||
import type { ICursorTool } from '@/designer/model2DEditor/tools/CursorTool.ts' |
|||
import type Measure from '@/model/itemType/measure/Measure.ts' |
|||
import { getItemTypeByName } from '@/runtime/DefineItemType.ts' |
|||
import { LINE_NAME } from '@/model/itemType/measure/Measure.ts' |
|||
import type { ItemJson } from '@/model/itemType/ItemTypeDefine.ts' |
|||
|
|||
let pdFn, pmFn, puFn, kdFn |
|||
|
|||
const TEMP_POINT_NAME = '_measure_temp_point' |
|||
const TMP_LABEL_NAME = '_measure_temp_label' |
|||
const TMP_TYPE = '_TMP' |
|||
|
|||
/** |
|||
* 用于在 threejs 中创建一系列的 object_for_measure 点,并标记他的距离 |
|||
*/ |
|||
export default class MeasureTool implements ICursorTool { |
|||
/** |
|||
* 测量工具所在的视图窗口,从这里可以取到 所有 Three.js 相关的对象. |
|||
* 比如: |
|||
* - viewport.scene 场景 |
|||
* - viewport.renderer 渲染器 |
|||
* - viewport.controls 控制器 |
|||
* - viewport.camera 摄像机 |
|||
* - viewport.raycaster 射线投射器 |
|||
* - viewport.dragControl 拖拽控制器 |
|||
* - viewport.measure 测量工具 |
|||
*/ |
|||
viewport: Viewport |
|||
|
|||
/** |
|||
* 当前测绘内容组, 所有测量点、线、标签都在这个组中. 但不包括临时点、线 |
|||
*/ |
|||
measure: Measure |
|||
|
|||
/** |
|||
* 是否完成测量 |
|||
*/ |
|||
isCompleted = false |
|||
/** |
|||
* 是否鼠标移动事件 |
|||
*/ |
|||
mouseMoved = false |
|||
|
|||
/** |
|||
* 当前鼠标所在的画布, 对应 viewport.renderer.domElement |
|||
*/ |
|||
canvas: HTMLCanvasElement |
|||
|
|||
/** |
|||
* 用于存储临时点 |
|||
*/ |
|||
tempPointMarker?: THREE.Mesh |
|||
|
|||
/** |
|||
* 临时线条 |
|||
*/ |
|||
tempLine?: THREE.Line |
|||
|
|||
/** |
|||
* 临时标签对象, 用于在鼠标移动时显示距离 |
|||
*/ |
|||
tempLabel?: CSS2DObject |
|||
|
|||
/** |
|||
* 保存上次点击时间,以便检测双击事件 |
|||
* @protected |
|||
*/ |
|||
lastClickTime: number = 0 |
|||
|
|||
/** |
|||
* 测量起始点 |
|||
*/ |
|||
startPoint?: THREE.Object3D = undefined |
|||
|
|||
/** |
|||
* 上次鼠标移动位置 |
|||
*/ |
|||
lastMovePosition: THREE.Vector3 | undefined = undefined |
|||
|
|||
mode: CursorMode |
|||
|
|||
/** |
|||
* 测量工具初始化 |
|||
*/ |
|||
init(viewport: Viewport) { |
|||
this.viewport = viewport |
|||
this.canvas = this.viewport.renderer.domElement as HTMLCanvasElement |
|||
|
|||
this.measure = getItemTypeByName('measure')!.clazz |
|||
} |
|||
|
|||
/** |
|||
* 测量工具开始, 监听鼠标事件, 变量初始化等 |
|||
*/ |
|||
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('进入测量模式') |
|||
} |
|||
|
|||
/** |
|||
* 停止测量模式, 清除所有临时点、线、标签. 停止所有鼠标事件监听 |
|||
*/ |
|||
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.tempLine && this.viewport.scene.remove(this.tempLine) |
|||
this.tempLabel && this.viewport.scene.remove(this.tempLabel) |
|||
this.tempPointMarker = undefined |
|||
this.tempLine = undefined |
|||
this.tempLabel = undefined |
|||
} |
|||
|
|||
/** |
|||
* 销毁测量工具, 当视图窗口被销毁时调用 |
|||
*/ |
|||
destory() { |
|||
} |
|||
|
|||
/** |
|||
* 鼠标按下事件 |
|||
*/ |
|||
mousedown() { |
|||
this.mouseMoved = false |
|||
} |
|||
|
|||
/** |
|||
* 鼠标移动,创建对应的临时点与线 |
|||
*/ |
|||
mousemove(e: MouseEvent) { |
|||
if (this.isCompleted) return |
|||
|
|||
this.mouseMoved = true |
|||
|
|||
// 当前鼠标所在的点
|
|||
const point = this.viewport.getClosestIntersection(e) |
|||
if (!point) { |
|||
return |
|||
} |
|||
|
|||
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) |
|||
} |
|||
|
|||
// 移动时绘制临时线
|
|||
if (this.startPoint) { |
|||
// 获取最后一个点
|
|||
if (!this.tempLine) { |
|||
this.tempLine = this.measure.createLineBasic() |
|||
this.viewport.scene.add(this.tempLine) |
|||
} |
|||
|
|||
const p0 = this.startPoint.position |
|||
const line = this.tempLine |
|||
const geom = line.geometry |
|||
geom.setFromPoints([p0, point]) |
|||
|
|||
const dist = p0.distanceTo(point) |
|||
const label = `${numberToString(dist)} ${getUnitString(this.mode)}` |
|||
const position = new THREE.Vector3().addVectors(p0, point).multiplyScalar(0.5) |
|||
this.addOrUpdateTempLabel(label, position) |
|||
} |
|||
|
|||
// this.viewport.dispatchSignal('sceneGraphChanged')
|
|||
} |
|||
|
|||
/** |
|||
* 鼠标松开事件 |
|||
*/ |
|||
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) |
|||
} |
|||
} |
|||
} |
|||
|
|||
private onMouseClicked(e: MouseEvent) { |
|||
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 itemJson = { |
|||
t: this.measure.name, |
|||
a: 'ln', |
|||
tf: [ |
|||
[point.x, point.y, point.z], |
|||
[this.measure.defaultRotation.x, this.measure.defaultRotation.y, this.measure.defaultRotation.z], |
|||
[this.measure.defaultScale.x, this.measure.defaultScale.y, this.measure.defaultScale.z] |
|||
], |
|||
dt: { |
|||
link: [] as string[] |
|||
} |
|||
} as ItemJson |
|||
const marker = this.measure.createPoint(point, itemJson) |
|||
this.measure.group.add(marker) |
|||
|
|||
// 把点加入拖拽控制器
|
|||
this.viewport.dragControl.setDragObjects([marker], 'push') |
|||
|
|||
if (this.startPoint) { |
|||
// 如果起始点存在,则将新点添加到起始点的链接中
|
|||
this.startPoint.userData.link.push(marker.uuid) |
|||
this.measure.createLine(this.viewport.scene, this.startPoint, marker) |
|||
} |
|||
|
|||
// 更新起始点为新添加的点
|
|||
this.startPoint = marker |
|||
|
|||
// 删除临时线
|
|||
this.tempPointMarker && this.viewport.scene.remove(this.tempPointMarker) |
|||
this.tempPointMarker = undefined |
|||
|
|||
this.tempLabel && this.viewport.scene.remove(this.tempLabel) |
|||
this.tempLabel = undefined |
|||
|
|||
this.tempLine && this.viewport.scene.remove(this.tempLabel) |
|||
this.tempLine = undefined |
|||
} |
|||
|
|||
/** |
|||
* 创建点标记 |
|||
*/ |
|||
createTempPointMarker(position?: THREE.Vector3): THREE.Mesh { |
|||
const p = position |
|||
const scale = 0.25 |
|||
|
|||
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, 0.1, scale) |
|||
if (p) { |
|||
obj.position.set(p.x, p.y, p.z) |
|||
} |
|||
|
|||
obj.name = TEMP_POINT_NAME |
|||
obj.userData = { |
|||
mode: this.mode, |
|||
type: TMP_TYPE |
|||
} |
|||
return obj |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 添加或更新临时标签和位置 |
|||
*/ |
|||
addOrUpdateTempLabel(label: string, position: THREE.Vector3) { |
|||
if (!this.tempLabel) { |
|||
this.tempLabel = this.measure.createLabel(label) |
|||
this.tempLabel.name = TMP_LABEL_NAME |
|||
this.tempLabel.userData = { |
|||
mode: this.mode, |
|||
type: TMP_TYPE |
|||
} |
|||
this.viewport.scene.add(this.tempLabel) |
|||
} |
|||
this.tempLabel.position.set(position.x, position.y, position.z) |
|||
this.tempLabel.element.innerHTML = label |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
import type { ActionType } from '@/model/itemType/ItemTypeDefine.ts' |
|||
|
|||
export interface IGridHelper { |
|||
/** |
|||
* 启用坐标轴 |
|||
*/ |
|||
axesEnabled: boolean; |
|||
/** |
|||
* 坐标轴大小, 单位米 |
|||
*/ |
|||
axesSize: number; |
|||
/** |
|||
* 坐标轴分割数 |
|||
*/ |
|||
axesDivisions: number; |
|||
/** |
|||
* 坐标轴颜色, 十六进制颜色值 |
|||
*/ |
|||
axesColor: number; |
|||
/** |
|||
* 坐标轴透明度 |
|||
*/ |
|||
axesOpacity: number; |
|||
|
|||
/** |
|||
* 启用网格 |
|||
*/ |
|||
gridEnabled: boolean; |
|||
/** |
|||
* 网格大小, 单位米 |
|||
*/ |
|||
gridSize: number; |
|||
/** |
|||
* 网格分割数 |
|||
*/ |
|||
gridDivisions: number; |
|||
/** |
|||
* 网格颜色, 十六进制颜色值 |
|||
*/ |
|||
gridColor: number; |
|||
/** |
|||
* 网格透明度 |
|||
*/ |
|||
gridOpacity: number; |
|||
|
|||
/** |
|||
* 启用吸附 |
|||
*/ |
|||
snapEnabled: boolean; |
|||
/** |
|||
* 吸附距离, 单位米 |
|||
*/ |
|||
snapDistance: number; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 定义物体类型的元数据 |
|||
* 举例: |
|||
* { |
|||
* id: 'p1', // 物体ID, 唯一标识, 需保证唯一, three.js 中的 uuid
|
|||
* t: 'measure', // 物体类型, measure表示测量, 需交给 itemType.name == 'measure' 的组件处理
|
|||
* a: 'ln', // 交互类型, ln表示线点操作, pt 表示点操作
|
|||
* l: '测量1', // 标签名称, 显示用
|
|||
* c: '#ff0000', // 颜色, 显示用. 十六进制颜色值, three.js 中的材质颜色
|
|||
* tf: [ // 变换矩阵, 3x3矩阵, 采用Y轴向上为正, X轴向右, Z轴向前的右手坐标系
|
|||
* [-9.0, 0, -1.0], // 平移向量 position
|
|||
* [0, 0, 0], // 旋转向量 rotation, 表示绕Y轴旋转的角度, 单位为度。对应 three.js 应进行"角度"转"弧度"的换算
|
|||
* [0.25, 0.1, 0.25] // 缩放向量 scale
|
|||
* ], |
|||
* dt: { // 用户数据, 可自定义, 一般用在 three.js 的 userData 中
|
|||
* link: ['p2'], // 用于 a='ln' 的测量线段, 关联的点对象(uuid)
|
|||
* center: [], // 物流关联对象(uuid)
|
|||
* in: [], // 物流入方向关联的对象(uuid)
|
|||
* out: [] // 物流出方向关联的对象(uuid)
|
|||
* } |
|||
* } |
|||
*/ |
|||
export interface ItemJson { |
|||
/** |
|||
* 物体名称, 显示用, 最后初始化到 three.js 的 name 中, 可以不设置, 可以不唯一 |
|||
*/ |
|||
name?: string |
|||
|
|||
/** |
|||
* 对应 three.js 中的 uuid, 物体ID, 唯一标识, 需保证唯一 |
|||
*/ |
|||
id?: string |
|||
|
|||
/** |
|||
* 物体类型, 对应 defineItemType.name |
|||
*/ |
|||
t: string |
|||
|
|||
/** |
|||
* 交互类型 |
|||
*/ |
|||
a: ActionType |
|||
|
|||
/** |
|||
* 标签名称, 显示用, 最后初始化到 three.js 的 userData.label 中 |
|||
*/ |
|||
l?: string |
|||
|
|||
/** |
|||
* 颜色, 最后初始化到 three.js 的 userData.color 中 |
|||
*/ |
|||
c?: string |
|||
|
|||
/** |
|||
* 变换矩阵, 3x3矩阵, 采用Y轴向上为正, X轴向右, Z轴向前的右手坐标系 |
|||
*/ |
|||
tf: [ |
|||
/** |
|||
* 平移向量 position, 三维坐标 |
|||
*/ |
|||
[number, number, number], |
|||
/** |
|||
* 旋转向量 rotation, 表示绕Y轴旋转的角度, 单位为度。对应 three.js 应进行"角度"转"弧度"的换算 |
|||
*/ |
|||
[number, number, number], |
|||
/** |
|||
* 缩放向量 scale, 三维缩放比例 |
|||
*/ |
|||
[number, number, number], |
|||
] |
|||
|
|||
/** |
|||
* 用户数据, 可自定义, 一般用在 three.js 的 userData 中 |
|||
*/ |
|||
dt: { |
|||
/** |
|||
* 测量线段关联的点对象(uuid), 仅在 a='ln' 时有效 |
|||
*/ |
|||
link?: string[] |
|||
|
|||
/** |
|||
* 物流关联对象(uuid) |
|||
*/ |
|||
center?: string[] |
|||
/** |
|||
* 物流入方向关联的对象(uuid) |
|||
*/ |
|||
in?: string[] |
|||
/** |
|||
* 物流出方向关联的对象(uuid) |
|||
*/ |
|||
out?: string[] |
|||
|
|||
/** |
|||
* 其他自定义数据, 可以存储任何数据 |
|||
*/ |
|||
[key: string]: any |
|||
}, |
|||
|
|||
/** |
|||
* 子元素, 用于分组等, 可以为空数组 |
|||
*/ |
|||
items: ItemJson[] |
|||
} |
|||
Loading…
Reference in new issue