Browse Source

snap 吸附

master
修宁 7 months ago
parent
commit
28605fc280
  1. 2
      package.json
  2. 4
      pnpm-lock.yaml
  3. 54
      src/designer/Viewport.ts
  4. 163
      src/designer/model2DEditor/DragControls.js
  5. 22
      src/designer/model2DEditor/tools/CursorTool.ts
  6. 321
      src/designer/model2DEditor/tools/MeasureTool.ts
  7. 23
      src/model/ModelUtils.ts
  8. 19
      src/model/WorldModel.ts
  9. 160
      src/model/WorldModelType.ts
  10. 18
      src/model/example1.js
  11. 105
      src/model/itemType/ItemTypeDefine.ts
  12. 2
      src/model/itemType/Toolbox.ts
  13. 1
      tsconfig.app.json

2
package.json

@ -40,7 +40,7 @@
"@rolldown/pluginutils": "1.0.0-beta.8-commit.56abf23",
"@tsconfig/node22": "^22.0.1",
"@types/jquery": "^3.3.31",
"@types/lodash-es": "^4.17.7",
"@types/lodash": "^4.17.7",
"@types/node": "^22.14.0",
"@types/three": "^0.176.0",
"@vicons/antd": "^0.13.0",

4
pnpm-lock.yaml

@ -87,9 +87,9 @@ importers:
'@types/jquery':
specifier: ^3.3.31
version: 3.5.32
'@types/lodash-es':
'@types/lodash':
specifier: ^4.17.7
version: 4.17.12
version: 4.17.17
'@types/node':
specifier: ^22.14.0
version: 22.15.21

54
src/designer/Viewport.ts

@ -1,6 +1,5 @@
import _ from 'lodash'
import * as THREE from 'three'
import { GridHelper, OrthographicCamera, Raycaster, Scene, Vector3, WebGLRenderer } from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import EsDragControls from './model2DEditor/EsDragControls'
import Stats from 'three/examples/jsm/libs/stats.module'
@ -14,6 +13,7 @@ import { CSS2DRenderer } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { getAllItemTypes } from '@/runtime/DefineItemType.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type Toolbox from '@/model/itemType/Toolbox.ts'
import { calcPositionUseSnap } from '@/model/ModelUtils.ts'
/**
*
@ -21,15 +21,15 @@ import type Toolbox from '@/model/itemType/Toolbox.ts'
*/
export default class Viewport {
viewerDom: HTMLElement
scene: Scene
camera: OrthographicCamera
renderer: WebGLRenderer
axesHelper: GridHelper
gridHelper: GridHelper
scene: THREE.Scene
camera: THREE.OrthographicCamera
renderer: THREE.WebGLRenderer
axesHelper: THREE.GridHelper
gridHelper: THREE.GridHelper
statsControls: Stats
controls: OrbitControls
worldModel: WorldModel
raycaster: Raycaster
raycaster: THREE.Raycaster
dragControl: EsDragControls
animationFrameId: any = null
@ -140,20 +140,30 @@ export default class Viewport {
this.initMode2DCamera()
// 辅助线
const axesHelper = new THREE.GridHelper(1000, 2)
axesHelper.material.color.setHex(0x000000)
const gridOption = this.worldModel.gridOption
const axesHelper = new THREE.GridHelper(gridOption.axesSize, gridOption.axesDivisions)
axesHelper.material.color.setHex(gridOption.axesColor)
axesHelper.material.linewidth = 2
axesHelper.material.opacity = gridOption.gridOpacity
axesHelper.material.transparent = true
if (!gridOption.axesEnabled) {
axesHelper.visible = false
}
// @ts-ignore
axesHelper.material.vertexColors = false
this.axesHelper = axesHelper
this.scene.add(this.axesHelper)
const gridHelper = new THREE.GridHelper(1000, 1000)
gridHelper.material.color.setHex(0x999999)
gridHelper.material.opacity = 0.8
const gridHelper = new THREE.GridHelper(gridOption.gridSize, gridOption.gridDivisions)
gridHelper.material.color.setHex(gridOption.gridColor)
gridHelper.material.opacity = gridOption.gridOpacity
gridHelper.material.transparent = true
// @ts-ignore
gridHelper.material.vertexColors = false
if (!gridOption.gridEnabled) {
gridHelper.visible = false
}
this.gridHelper = gridHelper
this.scene.add(this.gridHelper)
@ -224,7 +234,7 @@ export default class Viewport {
this.resizeObserver.observe(this.viewerDom)
// 初始化射线投射器
this.raycaster = new Raycaster()
this.raycaster = new THREE.Raycaster()
// 初始化所有常驻工具
for (const tool of this.tools) {
@ -356,6 +366,8 @@ export default class Viewport {
}
this.renderer.setSize(width, height)
this.css2DRenderer.setSize(width, height)
this.css3DRenderer.setSize(width, height)
break
}
}
@ -425,15 +437,6 @@ export default class Viewport {
}
}
/**
*
*/
getGridHelpAtPosition(param: { x: number; z: number }) {
const pickPosition = new THREE.Vector2(param.x, param.z)
this.raycaster.setFromCamera(pickPosition, this.camera)
return this.raycaster.intersectObject(this.gridHelper, false)
}
getIntersects(point: THREE.Vector2) {
const mouse = new THREE.Vector2()
mouse.set((point.x * 2) - 1, -(point.y * 2) + 1)
@ -443,7 +446,8 @@ export default class Viewport {
}
/**
* canvas (renderer.domElement)
* x,y,z
* canvas (renderer.domElement)
*/
getClosestIntersection(e: MouseEvent) {
const _point = new THREE.Vector2()
@ -452,7 +456,9 @@ export default class Viewport {
const intersects = this.getIntersects(_point)
if (intersects && intersects.length > 2) {
return new Vector3(intersects[0].point.x, 0.1, intersects[1].point.z)
const point = new THREE.Vector3(intersects[0].point.x, 0.1, intersects[1].point.z)
return calcPositionUseSnap(e, point)
}
return null
}

163
src/designer/model2DEditor/DragControls.js

@ -5,86 +5,89 @@ import {
Raycaster,
Vector2,
Vector3
} from 'three';
} from 'three'
import { calcPositionUseSnap } from '@/model/ModelUtils.js'
const _plane = new Plane();
const _raycaster = new Raycaster();
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();
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();
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 );
_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 );
_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();
deactivate()
}
function setObjects(objects) {
_objects = objects;
_objects = objects
}
function getObjects() {
return _objects;
return _objects
}
function getRaycaster() {
return _raycaster;
return _raycaster
}
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 (_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
}
@ -92,32 +95,32 @@ class DragControls extends EventDispatcher {
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) {
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) {
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) {
scope.dispatchEvent( { type: 'hoveron', object: object } );
scope.dispatchEvent({ type: 'hoveron', object: object })
_domElement.style.cursor = 'pointer';
_hovered = object;
_domElement.style.cursor = 'pointer'
_hovered = object
}
@ -125,10 +128,10 @@ class DragControls extends EventDispatcher {
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
}
@ -139,73 +142,73 @@ class DragControls extends EventDispatcher {
}
function onPointerDown(event) {
if (scope.enabled === false) return;
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;
_selected = (scope.transformGroup === true) ? _objects[0] : _intersections[0].object
if (scope.enabledMove) {
_plane.setFromNormalAndCoplanarPoint(_camera.getWorldDirection(_plane.normal), _worldPosition.setFromMatrixPosition(_selected.matrixWorld));
_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));
_inverseMatrix.copy(_selected.parent.matrixWorld).invert()
_offset.copy(_intersection).sub(_worldPosition.setFromMatrixPosition(_selected.matrixWorld))
}
_domElement.style.cursor = 'move';
_domElement.style.cursor = 'move'
}
scope.dispatchEvent( { type: 'dragstart', object: _selected,e:event } );
scope.dispatchEvent({ type: 'dragstart', object: _selected, e: event })
}
isMove = false;
isMove = false
}
function onPointerCancel(event) {
if ( scope.enabled === false ) return;
if (scope.enabled === false) return
if (_selected) {
scope.dispatchEvent( { type: 'dragend', object: _selected,e:event } );
_selected = null;
scope.dispatchEvent({ type: 'dragend', object: _selected, e: event })
_selected = null
} else if (!isMove) {
// 添加点击空白处的事件
scope.dispatchEvent( { type: 'clickblank',e:event } );
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();
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
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 }

22
src/designer/model2DEditor/tools/CursorTool.ts

@ -1,22 +0,0 @@
export interface ICursorTool {
/**
*
*/
init(viewport: any): void
/**
* , cursor
*/
start(): void
/**
* , , ESC 退 cursor
*/
stop(): void
/**
* ,
*/
destory():void
}

321
src/designer/model2DEditor/tools/MeasureTool.ts

@ -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
}
}

23
src/model/ModelUtils.ts

@ -1,8 +1,29 @@
import * as THREE from 'three'
import type { ItemJson, ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemTypeDefineOption } from '@/model/itemType/ItemTypeDefine.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
import { getAllItemTypes, getItemTypeByName } from '@/runtime/DefineItemType.ts'
/**
*
*/
export function calcPositionUseSnap(e: MouseEvent, point: THREE.Vector3) {
// 按下 ctrl 键,不启用吸附,其他情况启用吸附
const gridOption = worldModel.gridOption
if (!e.ctrlKey && !e.metaKey) {
if (gridOption.snapEnabled && gridOption.snapDistance > 0) {
// 启用吸附, 针对 point 的 x 和 z 坐标进行吸附, 吸附距离为 gridOption.snapDistance
const snapDistance = gridOption.snapDistance
const newPoint = new THREE.Vector3(point.x, point.y, point.z)
newPoint.x = Math.round(newPoint.x / snapDistance) * snapDistance
newPoint.z = Math.round(newPoint.z / snapDistance) * snapDistance
return newPoint
}
}
return point
}
/**
* uuid Object3D
*/
export function findObject3DById(scene: THREE.Object3D, uuid: string): THREE.Object3D | undefined {

19
src/model/WorldModel.ts

@ -7,6 +7,7 @@ import type Viewport from '@/designer/Viewport.ts'
import { loadSceneFromJson } from '@/model/ModelUtils.ts'
import MeasureMeta from './itemType/measure/MeasureMeta'
import type { IGridHelper } from '@/model/WorldModelType.ts'
/**
*
@ -28,6 +29,24 @@ export default class WorldModel {
sceneMap = new Map<string, Scene>()
viewPorts: Viewport[] = []
get gridOption(): IGridHelper {
const data = _.get(this.data, 'Tool.gridHelper')
return _.defaultsDeep(data, {
axesEnabled: true,
axesSize: 1000,
axesDivisions: 4,
axesColor: 0x000000,
axesOpacity: 1,
gridEnabled: true, // 启用网格
gridSize: 1000, // 网格大小, 单位米
gridDivisions: 1000, // 网格分割数
gridColor: 0x999999, // 网格颜色, 十六进制颜色值
gridOpacity: 0.8, // 网格透明度
snapEnabled: true, // 启用吸附
snapDistance: 0.25 // 吸附距离, 单位米
})
}
constructor() {
}

160
src/model/WorldModelType.ts

@ -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[]
}

18
src/model/example1.js

@ -11,7 +11,23 @@ export default {
{ name: 'OnReset', fn: '' },
{ name: 'OnStart', fn: '' },
{ name: 'OnStop', fn: '' }
]
],
gridHelper: {
axesEnabled: true,
axesSize: 1000,
axesDivisions: 4,
axesColor: 0x000000,
axesOpacity: 1,
gridEnabled: true,
gridSize: 1000,
gridDivisions: 1000,
gridColor: 0x999999,
gridOpacity: 0.8,
snapEnabled: true,
snapDistance: 0.25
}
},
items: [
{

105
src/model/itemType/ItemTypeDefine.ts

@ -24,108 +24,3 @@ export interface ItemTypeDefineOption {
actionType: ActionType
clazz: ItemType
}
/**
*
* :
* {
* 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[]
}

2
src/model/itemType/Toolbox.ts

@ -1,7 +1,7 @@
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/itemType/ItemTypeDefine.ts'
import type { ItemJson } from '@/model/WorldModelType.ts'
let pdFn, pmFn, puFn

1
tsconfig.app.json

@ -39,6 +39,7 @@
]
},
"types": [
"node_modules/@types/lodash-es",
]
},
"extends": "@vue/tsconfig/tsconfig.dom.json",

Loading…
Cancel
Save