diff --git a/src/core/Constract.ts b/src/core/Constract.ts index a957ae6..b97c525 100644 --- a/src/core/Constract.ts +++ b/src/core/Constract.ts @@ -12,8 +12,8 @@ export default Object.freeze({ // 测量相关的光标模式 CursorModeMeasure: 'measure', - CursorModeWay: 'way', + CursorModeGstore: 'gstore', // 选择模式 CursorModeSelectByRec: 'selectByRec' diff --git a/src/core/base/BaseInteraction.ts b/src/core/base/BaseInteraction.ts index 05a4dc5..9255dfc 100644 --- a/src/core/base/BaseInteraction.ts +++ b/src/core/base/BaseInteraction.ts @@ -54,10 +54,41 @@ export default abstract class BaseInteraction { alphaToCoverage: true }) + /** + * 物品是否"单点", 不允许连线 + */ + get isSinglePointMode(): boolean { + return false + } + constructor(itemTypeName: string) { this.itemTypeName = itemTypeName } + createPointOfItem(catchPoint: ItemJson, point: THREE.Vector3): ItemJson { + const renderer = getRenderer(this.itemTypeName) + + const defaultScale = renderer.defaultScale + const defaultRotation = renderer.defaultRotation + + _.extend(catchPoint, { + id: system.createUUID(), + t: this.itemTypeName, + v: true, + 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[] + } + }) + return catchPoint + } + /** * 拖拽点开始 */ @@ -125,7 +156,14 @@ export default abstract class BaseInteraction { } if (this.linkStartPointId) { - this.linkStartPointObject = this.viewport.entityManager.findObjectsById(this.linkStartPointId)?.[0] + if (this.isSinglePointMode) { + // 单点模式不需要起始点 + this.linkStartPointId = undefined + this.linkStartPointObject = undefined + + } else { + this.linkStartPointObject = this.viewport.entityManager.findObjectsById(this.linkStartPointId)?.[0] + } } pdFn = this.mousedown.bind(this) @@ -266,9 +304,29 @@ export default abstract class BaseInteraction { } this.lastClickTime = now + // 如果正式的点命中到同类型的节点上,则不添加新的点,只牵线到该点 let catchPoint: ItemJson | null = this.viewport.stateManager.findItemByPosition(point, this.itemTypeName) + + if (this.isSinglePointMode) { + // 单点模式,直接添加点 + if (catchPoint) { + // 如果已经有点了,则不再添加 + system.msg('Point already exists at this position.') + return + } + + // 则添加一个新的点 + const stateManager = this.viewport.stateManager + stateManager.beginStateUpdate({ createFromInteraction: true }) + catchPoint = {} as ItemJson + catchPoint = this.createPointOfItem(catchPoint, point) + stateManager.vdata.items.push(catchPoint) + stateManager.endStateUpdate() + return + } + let from: ItemJson | undefined = undefined if (this.linkStartPointId) { from = this.viewport.stateManager.findItemById(this.linkStartPointId) @@ -300,28 +358,9 @@ export default abstract class BaseInteraction { stateManager.endStateUpdate() } else { - const renderer = getRenderer(this.itemTypeName) - - const defaultScale = renderer.defaultScale - const defaultRotation = renderer.defaultRotation - // 添加正式点 - catchPoint = { - id: system.createUUID(), - t: this.itemTypeName, - v: true, - 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 - + catchPoint = {} as ItemJson + catchPoint = this.createPointOfItem(catchPoint, point) // 提交状态管理器 const stateManager = this.viewport.stateManager @@ -396,6 +435,8 @@ export default abstract class BaseInteraction { const obj = new CSS2DObject(div) return obj } + + } export interface DragOption { diff --git a/src/core/manager/WorldModel.ts b/src/core/manager/WorldModel.ts index 446fd8b..8b6f429 100644 --- a/src/core/manager/WorldModel.ts +++ b/src/core/manager/WorldModel.ts @@ -3,6 +3,7 @@ import { reactive, watch } from 'vue' import EventBus from '@/runtime/EventBus' import Measure from '@/modules/measure' import Way from '@/modules/way' +import Gstore from '@/modules/gstore' import StateManager from '@/core/manager/StateManager.ts' export interface WorldModelState { @@ -62,7 +63,8 @@ export default class WorldModel { return Promise.all([ Measure, - Way + Way, + Gstore ]).then(() => { console.log('世界模型初始化完成') diff --git a/src/editor/Model2DEditor.vue b/src/editor/Model2DEditor.vue index 527b932..364a4fd 100644 --- a/src/editor/Model2DEditor.vue +++ b/src/editor/Model2DEditor.vue @@ -32,13 +32,18 @@ :type="state?.cursorMode===Constract.CursorModeSLink?'primary':''" @click="()=>state.cursorMode = Constract.CursorModeSLink"> - - + + +
((resolve, reject) => { + new THREE.TextureLoader().load( + MoveLinePointPng, + (texture) => { + this.pointMaterial = new THREE.SpriteMaterial({ + map: texture, + transparent: true, + side: THREE.DoubleSide + }) + resolve() + }, + undefined, + function(err) { + reject(err) + } + ) + }) + } + + /** + * 所有的点,必须使用同一个尺寸, 改属性也无效 + */ + override afterCreateOrUpdatePoint(item: ItemJson, option: RendererCudOption, objects: THREE.Object3D[]) { + super.afterCreateOrUpdatePoint(item, option, objects) + + const point = objects[0] + point.position.y = this.defulePositionY + point.scale.set(this.defaultScale.x, this.defaultScale.y, this.defaultScale.z) + point.rotation.set( + THREE.MathUtils.degToRad(this.defaultRotation.x), + THREE.MathUtils.degToRad(this.defaultRotation.y), + THREE.MathUtils.degToRad(this.defaultRotation.z) + ) + } + + + createLineBasic(start: ItemJson, end: ItemJson, type: LinkType): THREE.Object3D[] { + const group = new THREE.Group() + const startPosition = new THREE.Vector3(start.tf[0][0], 0.01, start.tf[0][2]) + const endPosition = new THREE.Vector3(end.tf[0][0], 0.01, end.tf[0][2]) + const width = 1 + + const curve = new THREE.LineCurve3(startPosition, endPosition) + const tubeGeometry = new THREE.TubeGeometry(curve, 1, width / 2, 8, false) + const lineMesh = new THREE.Mesh(tubeGeometry, this.lineMaterial) + group.add(lineMesh) + + + const midPoint = new THREE.Vector3() + .addVectors(startPosition, endPosition) + .multiplyScalar(0.5) + + const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) + + const label = new Text() + label.text = distance + label.font = SimSunTTF + label.fontSize = 0.2 + label.color = '#5f5f5f' + label.opacity = 0.8 + label.anchorX = 'center' + label.anchorY = 'middle' + label.depthOffset = 1 + label.material.depthTest = false + label.name = GstoreRenderer.LABEL_NAME + label.quaternion.copy(this.tempViewport.camera.quaternion) + label.sync() + label.position.set(midPoint.x, midPoint.y, midPoint.z) + + group.add(label) + + return [group] + } + + updateLine(start: ItemJson, end: ItemJson, type: LinkType, option?: RendererCudOption) { + super.updateLine(start, end, type, option) + + const startPosition = new THREE.Vector3(start.tf[0][0], 0.01, start.tf[0][2]) + const endPosition = new THREE.Vector3(end.tf[0][0], 0.01, end.tf[0][2]) + const width = 1 + + const lineId = getLineId(start.id, end.id, type) + const lines = this.tempViewport.entityManager.findLineObjectsById(lineId) + const group: THREE.Group = lines[0] as THREE.Group + + // 清空group里的元素 + const label: Text = group.children[1] + group.clear() + + const curve = new THREE.LineCurve3(startPosition, endPosition) + const tubeGeometry = new THREE.TubeGeometry(curve, 1, width / 2, 8, false) + const lineMesh = new THREE.Mesh(tubeGeometry, this.lineMaterial) + group.add(lineMesh) + + const midPoint = new THREE.Vector3() + .addVectors(startPosition, endPosition) + .multiplyScalar(0.5) + + const distance = (startPosition.distanceTo(endPosition) * 1000).toFixed(0) + label.text = distance + label.quaternion.copy(this.tempViewport.camera.quaternion) + label.sync() + label.position.set(midPoint.x, midPoint.y, midPoint.z) + group.add(label) + } + + createPointBasic(item: ItemJson, option?: RendererCudOption): THREE.Object3D[] { + const obj = new THREE.Sprite(this.pointMaterial as THREE.SpriteMaterial) + obj.name = GstoreRenderer.POINT_NAME + return [obj] + } + + appendToScene(...objects: THREE.Object3D[]) { + const dragObjects = objects.filter(obj => !!obj.userData.draggable) + this.tempViewport.dragControl.setDragObjects(dragObjects, 'push') + // this.tempViewport.dragControl.setDragObjects(objects, 'remove') + + this.tempViewport?.scene.add(...objects) + } + + dispose() { + super.dispose() + this.pointMaterial.dispose() + this.lineMaterial.dispose() + } +} \ No newline at end of file diff --git a/src/modules/gstore/index.ts b/src/modules/gstore/index.ts new file mode 100644 index 0000000..bbaefd9 --- /dev/null +++ b/src/modules/gstore/index.ts @@ -0,0 +1,15 @@ +import { defineModule } from '@/core/manager/ModuleManager.ts' +import GstoreRenderer from './GstoreRenderer.ts' +import GstoreEntity from './GstoreEntity.ts' +import GstoreMeta from './GstoreMeta.ts' +import GstoreInteraction from './GstoreInteraction.ts' + +export const ITEM_TYPE_NAME = 'gstore' + +export default defineModule({ + name: ITEM_TYPE_NAME, + renderer: new GstoreRenderer(ITEM_TYPE_NAME), + interaction: new GstoreInteraction(ITEM_TYPE_NAME), + meta: GstoreMeta, + entity: GstoreEntity +}) \ No newline at end of file diff --git a/src/modules/measure/MeasureRenderer.ts b/src/modules/measure/MeasureRenderer.ts index 7034b9a..f4f01b9 100644 --- a/src/modules/measure/MeasureRenderer.ts +++ b/src/modules/measure/MeasureRenderer.ts @@ -29,7 +29,7 @@ export default class MeasureRenderer extends BaseRenderer { lineMaterial: LineMaterial readonly defulePositionY = 0.01 - readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.25, 0.25, 0.1) + readonly defaultScale: THREE.Vector3 = new THREE.Vector3(0.1, 0.1, 0.1) readonly defaultRotation: THREE.Vector3 = new THREE.Vector3(90, 0, 0) constructor(itemTypeName: string) {