|
|
@@ -0,0 +1,826 @@
|
|
|
+<template>
|
|
|
+ <div id="draw-panel" class="sm-panel" v-drag>
|
|
|
+ <div class="sm-function-module-sub-section" style="margin:0" v-stopdrag>
|
|
|
+ <div class="sm-half-L">
|
|
|
+ <label style="width:auto">
|
|
|
+ <input type="radio" value="single" v-model="drawMode" />
|
|
|
+ 单个
|
|
|
+ </label>
|
|
|
+ <label style="width:auto">
|
|
|
+ <input type="radio" value="continuous" v-model="drawMode" />
|
|
|
+ 连续
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L symbolic">
|
|
|
+ <div
|
|
|
+ v-for="model in basicGeometries"
|
|
|
+ :key="model.type"
|
|
|
+ class="symbolic-box"
|
|
|
+ style="width: 40px;"
|
|
|
+ :class="{ 'theme-border-color': selectedModel && selectedModel.type === model.type }"
|
|
|
+ @click="selectModel(model)"
|
|
|
+ >
|
|
|
+ <img :src="model.thumbnail" alt="" class="model-thumbnail" />
|
|
|
+ <label style="width:100%">{{ model.name }}</label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L">
|
|
|
+ <label style="width: 35%;">颜色</label>
|
|
|
+ <el-color-picker v-model="selectedColor" size="mini" style="width:63%"></el-color-picker>
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L">
|
|
|
+ <label style="width: 35%;">透明度</label>
|
|
|
+ <el-slider v-model="selectedOpacity" :min="0" :max="1" :step="0.1" style="width:63%"></el-slider>
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L" v-if="selectedEntity">
|
|
|
+ <label style="width: 35%;">位置 X</label>
|
|
|
+ <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.x" />
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L" v-if="selectedEntity">
|
|
|
+ <label style="width: 35%;">位置 Y</label>
|
|
|
+ <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.y" />
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L" v-if="selectedEntity">
|
|
|
+ <label style="width: 35%;">位置 Z</label>
|
|
|
+ <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.z" />
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L" v-if="selectedEntity" style="display: flex; align-items: center;">
|
|
|
+ <label style="width: 35%;">旋转</label>
|
|
|
+ <div style="width:63%; display: flex; gap: 4px;">
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.x" />
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.y" />
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.z" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="sm-half-L" v-if="selectedEntity" style="display: flex; align-items: center;">
|
|
|
+ <label style="width: 35%;">缩放</label>
|
|
|
+ <div style="width:63%; display: flex; gap: 4px;">
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.x" />
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.y" />
|
|
|
+ <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.z" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="boxchild">
|
|
|
+ <button class="tbtn" type="button" v-on:click="startDraw">{{ isDrawing ? '停止绘制' : '绘制' }}</button>
|
|
|
+ <button class="tbtn tbtn-margin-left" type="button" v-on:click="clear">清除</button>
|
|
|
+ <button class="tbtn tbtn-margin-left" type="button" v-on:click="close">关闭</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'Sm3dDrawSolidFigure',
|
|
|
+ emits: ['close'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ // 基础几何体模型
|
|
|
+ const basicGeometries = ref([
|
|
|
+ {
|
|
|
+ type: 'box',
|
|
|
+ name: '立方体',
|
|
|
+ thumbnail: '/img/componentsImg/draw-solid-figuree.png',
|
|
|
+ dimensions: { x: 1, y: 1, z: 1 }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'cylinder',
|
|
|
+ name: '圆柱体',
|
|
|
+ thumbnail: '/img/componentsImg/draw-solid-figuree.png',
|
|
|
+ dimensions: { length: 1, radius: 0.5 }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'sphere',
|
|
|
+ name: '球体',
|
|
|
+ thumbnail: '/img/componentsImg/draw-solid-figuree.png',
|
|
|
+ dimensions: { radius: 0.5 }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ type: 'cone',
|
|
|
+ name: '圆锥体',
|
|
|
+ thumbnail: '/img/componentsImg/draw-solid-figuree.png',
|
|
|
+ dimensions: { length: 1, radius: 0.5 }
|
|
|
+ }
|
|
|
+ ]);
|
|
|
+
|
|
|
+ // 选中的实体
|
|
|
+ const selectedEntity = ref(null);
|
|
|
+ // 选中实体的属性
|
|
|
+ const selectedColor = ref('#409eff');
|
|
|
+ const selectedOpacity = ref(0.8);
|
|
|
+ const position = reactive({ x: 0, y: 0, z: 0 });
|
|
|
+ const rotation = reactive({ x: 0, y: 0, z: 0 });
|
|
|
+ const scale = reactive({ x: 1, y: 1, z: 1 });
|
|
|
+ // 绘制模式
|
|
|
+ const drawMode = ref('single');
|
|
|
+ // 选中的模型类型
|
|
|
+ const selectedModel = ref(null);
|
|
|
+ // 绘制状态
|
|
|
+ const isDrawing = ref(false);
|
|
|
+ // 绘制激活标志,用于区分是否正在进行绘制(避免与选中实体冲突)
|
|
|
+ let isDrawHandlerActive = false;
|
|
|
+ // 防止循环更新的标志
|
|
|
+ let isUpdatingProperties = false;
|
|
|
+
|
|
|
+ let viewer = null;
|
|
|
+ let currentEditor = null;
|
|
|
+
|
|
|
+ const close = () => {
|
|
|
+ // 通知父组件关闭此组件
|
|
|
+ emit('close', 'Sm3dDrawSolidFigure');
|
|
|
+ };
|
|
|
+
|
|
|
+ // 选择模型类型
|
|
|
+ const selectModel = (model) => {
|
|
|
+ selectedModel.value = model;
|
|
|
+ ElMessage.success(`已选择 ${model.name}`);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 开始绘制
|
|
|
+ const startDraw = () => {
|
|
|
+ console.log('startDraw called, isDrawing:', isDrawing.value, 'selectedModel:', selectedModel.value);
|
|
|
+ if (!selectedModel.value) {
|
|
|
+ ElMessage.warning('请先选择模型类型');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ isDrawing.value = !isDrawing.value;
|
|
|
+ console.log('After toggle, isDrawing:', isDrawing.value);
|
|
|
+ if (isDrawing.value) {
|
|
|
+ // 清除之前选中的实体的高亮
|
|
|
+ if (selectedEntity.value) {
|
|
|
+ removeHighlight(selectedEntity.value);
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ selectedEntity.value = null;
|
|
|
+ }
|
|
|
+ isDrawHandlerActive = true;
|
|
|
+ viewer.enableCursorStyle = false;
|
|
|
+ viewer._element.style.cursor = 'crosshair';
|
|
|
+ ElMessage.success(`开始${drawMode.value === 'single' ? '单个' : '连续'}绘制模式`);
|
|
|
+ } else {
|
|
|
+ ElMessage.info('已停止绘制');
|
|
|
+ selectedModel.value = null;
|
|
|
+ isDrawHandlerActive = false;
|
|
|
+ viewer.enableCursorStyle = true;
|
|
|
+ viewer._element.style.cursor = 'default';
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 清除所有模型
|
|
|
+ const clear = () => {
|
|
|
+ if (viewer) {
|
|
|
+ viewer.entities.removeAll();
|
|
|
+ selectedEntity.value = null;
|
|
|
+ selectedModel.value = null;
|
|
|
+ isDrawing.value = false;
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ ElMessage.success('已清除所有模型');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 左键点击处理
|
|
|
+ const onLeftClick = (movement) => {
|
|
|
+ console.log('=== Left click ===');
|
|
|
+ console.log('isDrawing:', isDrawing.value);
|
|
|
+ console.log('selectedModel:', selectedModel.value);
|
|
|
+ console.log('drawMode:', drawMode.value);
|
|
|
+
|
|
|
+ // 绘制模式
|
|
|
+ if (isDrawing.value === true && selectedModel.value !== null) {
|
|
|
+ console.log('Entering DRAW mode');
|
|
|
+
|
|
|
+ let position = viewer.scene.pickPosition(movement.position);
|
|
|
+ console.log('Position:', position, 'defined:', Cesium.defined(position));
|
|
|
+
|
|
|
+ if (!Cesium.defined(position)) {
|
|
|
+ console.warn('Cannot get position');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ createGeometry(position, selectedModel.value);
|
|
|
+ console.log('Geometry created, drawMode:', drawMode.value);
|
|
|
+
|
|
|
+ if (drawMode.value === 'single') {
|
|
|
+ console.log('Single mode - stopping draw');
|
|
|
+ isDrawing.value = false;
|
|
|
+ isDrawHandlerActive = false;
|
|
|
+ selectedModel.value = null;
|
|
|
+ viewer.enableCursorStyle = true;
|
|
|
+ viewer._element.style.cursor = 'default';
|
|
|
+ ElMessage.info('已完成单个绘制');
|
|
|
+ } else {
|
|
|
+ console.log('Continuous mode - continuing');
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('Entering SELECT mode');
|
|
|
+
|
|
|
+ // 非绘制模式:选择实体
|
|
|
+ let pickedObject = viewer.scene.pick(movement.position);
|
|
|
+ console.log('Picked object:', pickedObject);
|
|
|
+
|
|
|
+ if (Cesium.defined(pickedObject) && pickedObject.id) {
|
|
|
+ selectEntity(pickedObject.id);
|
|
|
+ } else if (isDrawing.value === false) {
|
|
|
+ clearSelection();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 右键点击处理
|
|
|
+ const onRightClick = (movement) => {
|
|
|
+ console.log('=== Right click ===');
|
|
|
+ console.log('isDrawing:', isDrawing.value);
|
|
|
+ console.log('drawMode:', drawMode.value);
|
|
|
+ console.log('Condition result:', isDrawing.value === true && drawMode.value === 'continuous');
|
|
|
+
|
|
|
+ if (isDrawing.value === true && drawMode.value === 'continuous') {
|
|
|
+ console.log('Stopping continuous draw');
|
|
|
+ isDrawing.value = false;
|
|
|
+ isDrawHandlerActive = false;
|
|
|
+ selectedModel.value = null;
|
|
|
+ viewer.enableCursorStyle = true;
|
|
|
+ viewer._element.style.cursor = 'default';
|
|
|
+ ElMessage.info('已结束连续绘制');
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 阻止右键默认菜单
|
|
|
+ const onContextMenu = (event) => {
|
|
|
+ if (isDrawing.value) {
|
|
|
+ event.preventDefault();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 键盘事件处理函数
|
|
|
+ const handleKeyDown = (event) => {
|
|
|
+ if (event.key === 'Enter') {
|
|
|
+ event.preventDefault();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 初始化场景事件
|
|
|
+ const initSceneEvents = () => {
|
|
|
+ if (!viewer) return;
|
|
|
+
|
|
|
+ // 左键点击 - 绘制或选择
|
|
|
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+ viewer.screenSpaceEventHandler.setInputAction(onLeftClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+
|
|
|
+ // 右键点击 - 结束连续绘制
|
|
|
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
+ viewer.screenSpaceEventHandler.setInputAction(onRightClick, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
+
|
|
|
+ // 阻止右键默认菜单
|
|
|
+ viewer._element.addEventListener('contextmenu', onContextMenu);
|
|
|
+
|
|
|
+ // 键盘事件
|
|
|
+ document.addEventListener('keydown', handleKeyDown);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 清理事件
|
|
|
+ const cleanupEvents = () => {
|
|
|
+ if (viewer) {
|
|
|
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
+ viewer._element.removeEventListener('contextmenu', onContextMenu);
|
|
|
+ viewer.enableCursorStyle = true;
|
|
|
+ viewer._element.style.cursor = 'default';
|
|
|
+ }
|
|
|
+ document.removeEventListener('keydown', handleKeyDown);
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ isDrawing.value = false;
|
|
|
+ isDrawHandlerActive = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 选择实体
|
|
|
+ const selectEntity = (entity) => {
|
|
|
+ if (selectedEntity.value) {
|
|
|
+ removeHighlight(selectedEntity.value);
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ selectedEntity.value = entity;
|
|
|
+ updateSelectedEntityProperties();
|
|
|
+ addHighlight(entity);
|
|
|
+ addEditor(entity);
|
|
|
+ };
|
|
|
+
|
|
|
+ // 清除选择
|
|
|
+ const clearSelection = () => {
|
|
|
+ if (selectedEntity.value) {
|
|
|
+ removeHighlight(selectedEntity.value);
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ selectedEntity.value = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加模型高亮效果
|
|
|
+ const addHighlight = (entity) => {
|
|
|
+ if (!entity) return;
|
|
|
+
|
|
|
+ // 保存原始状态(只在第一次保存)
|
|
|
+ if (entity.box) {
|
|
|
+ if (entity.originalOutline === undefined) {
|
|
|
+ entity.originalOutline = entity.box.outline;
|
|
|
+ entity.originalOutlineColor = entity.box.outlineColor;
|
|
|
+ }
|
|
|
+ entity.box.outline = true;
|
|
|
+ entity.box.outlineColor = new Cesium.Color(1, 1, 0, 1);
|
|
|
+ } else if (entity.cylinder) {
|
|
|
+ if (entity.originalOutline === undefined) {
|
|
|
+ entity.originalOutline = entity.cylinder.outline;
|
|
|
+ entity.originalOutlineColor = entity.cylinder.outlineColor;
|
|
|
+ }
|
|
|
+ entity.cylinder.outline = true;
|
|
|
+ entity.cylinder.outlineColor = new Cesium.Color(1, 1, 0, 1);
|
|
|
+ } else if (entity.ellipsoid) {
|
|
|
+ if (entity.originalOutline === undefined) {
|
|
|
+ entity.originalOutline = entity.ellipsoid.outline;
|
|
|
+ entity.originalOutlineColor = entity.ellipsoid.outlineColor;
|
|
|
+ }
|
|
|
+ entity.ellipsoid.outline = true;
|
|
|
+ entity.ellipsoid.outlineColor = new Cesium.Color(1, 1, 0, 1);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 移除模型高亮效果
|
|
|
+ const removeHighlight = (entity) => {
|
|
|
+ if (!entity) return;
|
|
|
+
|
|
|
+ if (entity.box) {
|
|
|
+ if (entity.originalOutline !== undefined) {
|
|
|
+ entity.box.outline = entity.originalOutline;
|
|
|
+ entity.box.outlineColor = entity.originalOutlineColor;
|
|
|
+ } else {
|
|
|
+ entity.box.outline = false;
|
|
|
+ }
|
|
|
+ } else if (entity.cylinder) {
|
|
|
+ if (entity.originalOutline !== undefined) {
|
|
|
+ entity.cylinder.outline = entity.originalOutline;
|
|
|
+ entity.cylinder.outlineColor = entity.originalOutlineColor;
|
|
|
+ } else {
|
|
|
+ entity.cylinder.outline = false;
|
|
|
+ }
|
|
|
+ } else if (entity.ellipsoid) {
|
|
|
+ if (entity.originalOutline !== undefined) {
|
|
|
+ entity.ellipsoid.outline = entity.originalOutline;
|
|
|
+ entity.ellipsoid.outlineColor = entity.originalOutlineColor;
|
|
|
+ } else {
|
|
|
+ entity.ellipsoid.outline = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加操作轴编辑器
|
|
|
+ const addEditor = (entity) => {
|
|
|
+ if (!entity) return;
|
|
|
+
|
|
|
+ // 清除之前的编辑器
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 创建几何体
|
|
|
+ const createGeometry = (position, model) => {
|
|
|
+ if (!viewer) return;
|
|
|
+
|
|
|
+ const color = Cesium.Color.fromCssColorString(selectedColor.value).withAlpha(selectedOpacity.value);
|
|
|
+ let entity;
|
|
|
+
|
|
|
+ switch (model.type) {
|
|
|
+ case 'box':
|
|
|
+ entity = viewer.entities.add({
|
|
|
+ position: position,
|
|
|
+ box: {
|
|
|
+ dimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
|
|
|
+ material: new Cesium.ColorMaterialProperty(color)
|
|
|
+ },
|
|
|
+ modelType: model.type,
|
|
|
+ modelName: model.name,
|
|
|
+ originalDimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
|
|
|
+ _rotation: { x: 0, y: 0, z: 0 },
|
|
|
+ _scale: { x: 1, y: 1, z: 1 }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 'sphere':
|
|
|
+ entity = viewer.entities.add({
|
|
|
+ position: position,
|
|
|
+ ellipsoid: {
|
|
|
+ radii: new Cesium.Cartesian3(model.dimensions.radius, model.dimensions.radius, model.dimensions.radius),
|
|
|
+ material: new Cesium.ColorMaterialProperty(color)
|
|
|
+ },
|
|
|
+ modelType: model.type,
|
|
|
+ modelName: model.name,
|
|
|
+ originalDimensions: new Cesium.Cartesian3(model.dimensions.radius, model.dimensions.radius, model.dimensions.radius),
|
|
|
+ _rotation: { x: 0, y: 0, z: 0 },
|
|
|
+ _scale: { x: 1, y: 1, z: 1 }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 'cylinder':
|
|
|
+ entity = viewer.entities.add({
|
|
|
+ position: position,
|
|
|
+ cylinder: {
|
|
|
+ length: model.dimensions.length,
|
|
|
+ topRadius: model.dimensions.radius,
|
|
|
+ bottomRadius: model.dimensions.radius,
|
|
|
+ material: new Cesium.ColorMaterialProperty(color)
|
|
|
+ },
|
|
|
+ modelType: model.type,
|
|
|
+ modelName: model.name,
|
|
|
+ originalDimensions: {
|
|
|
+ length: model.dimensions.length,
|
|
|
+ topRadius: model.dimensions.radius,
|
|
|
+ bottomRadius: model.dimensions.radius
|
|
|
+ },
|
|
|
+ _rotation: { x: 0, y: 0, z: 0 },
|
|
|
+ _scale: { x: 1, y: 1, z: 1 }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 'ellipsoid':
|
|
|
+ entity = viewer.entities.add({
|
|
|
+ position: position,
|
|
|
+ ellipsoid: {
|
|
|
+ radii: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
|
|
|
+ material: new Cesium.ColorMaterialProperty(color)
|
|
|
+ },
|
|
|
+ modelType: model.type,
|
|
|
+ modelName: model.name,
|
|
|
+ originalDimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
|
|
|
+ _rotation: { x: 0, y: 0, z: 0 },
|
|
|
+ _scale: { x: 1, y: 1, z: 1 }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ case 'cone':
|
|
|
+ entity = viewer.entities.add({
|
|
|
+ position: position,
|
|
|
+ cylinder: {
|
|
|
+ length: model.dimensions.length,
|
|
|
+ topRadius: 0,
|
|
|
+ bottomRadius: model.dimensions.radius,
|
|
|
+ material: new Cesium.ColorMaterialProperty(color)
|
|
|
+ },
|
|
|
+ modelType: model.type,
|
|
|
+ modelName: model.name,
|
|
|
+ originalDimensions: {
|
|
|
+ length: model.dimensions.length,
|
|
|
+ topRadius: 0,
|
|
|
+ bottomRadius: model.dimensions.radius
|
|
|
+ },
|
|
|
+ _rotation: { x: 0, y: 0, z: 0 },
|
|
|
+ _scale: { x: 1, y: 1, z: 1 }
|
|
|
+ });
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (entity) {
|
|
|
+ ElMessage.success(`${model.name} 创建成功`);
|
|
|
+ // 清除之前的高亮和编辑器
|
|
|
+ if (selectedEntity.value) {
|
|
|
+ removeHighlight(selectedEntity.value);
|
|
|
+ if (currentEditor) {
|
|
|
+ currentEditor.deactivate();
|
|
|
+ currentEditor = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ selectedEntity.value = entity;
|
|
|
+ updateSelectedEntityProperties();
|
|
|
+ // 添加高亮效果
|
|
|
+ addHighlight(entity);
|
|
|
+ // 添加操作轴
|
|
|
+ addEditor(entity);
|
|
|
+ return entity;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 更新选中实体的属性
|
|
|
+ const updateSelectedEntityProperties = () => {
|
|
|
+ if (!selectedEntity.value) return;
|
|
|
+
|
|
|
+ console.log('Updating properties for entity:', selectedEntity.value);
|
|
|
+
|
|
|
+ // 获取实体
|
|
|
+ const entity = selectedEntity.value;
|
|
|
+
|
|
|
+ // 设置标志,防止循环更新
|
|
|
+ isUpdatingProperties = true;
|
|
|
+
|
|
|
+ // 更新位置(经纬度保留6位小数,高度保留6位小数)
|
|
|
+ const cartesian = entity.position.getValue(viewer.clock.currentTime);
|
|
|
+ if (cartesian) {
|
|
|
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
|
+ position.x = Number(Cesium.Math.toDegrees(cartographic.longitude).toFixed(6));
|
|
|
+ position.y = Number(Cesium.Math.toDegrees(cartographic.latitude).toFixed(6));
|
|
|
+ position.z = Number(cartographic.height.toFixed(6));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新颜色和透明度
|
|
|
+ let material = null;
|
|
|
+ if (entity.box) {
|
|
|
+ material = entity.box.material;
|
|
|
+ } else if (entity.cylinder) {
|
|
|
+ material = entity.cylinder.material;
|
|
|
+ } else if (entity.ellipsoid) {
|
|
|
+ material = entity.ellipsoid.material;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (material) {
|
|
|
+ let color = null;
|
|
|
+ if (material.color) {
|
|
|
+ if (typeof material.color.getValue === 'function') {
|
|
|
+ color = material.color.getValue(viewer.clock.currentTime);
|
|
|
+ } else {
|
|
|
+ color = material.color;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (color instanceof Cesium.Color) {
|
|
|
+ selectedColor.value = color.toCssColorString();
|
|
|
+ selectedOpacity.value = Number(color.alpha.toFixed(2));
|
|
|
+ } else if (material instanceof Cesium.Color) {
|
|
|
+ selectedColor.value = material.toCssColorString();
|
|
|
+ selectedOpacity.value = Number(material.alpha.toFixed(2));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新旋转(保留2位小数)
|
|
|
+ if (entity._rotation) {
|
|
|
+ // 优先使用保存的旋转值
|
|
|
+ rotation.x = Number(entity._rotation.x.toFixed(2));
|
|
|
+ rotation.y = Number(entity._rotation.y.toFixed(2));
|
|
|
+ rotation.z = Number(entity._rotation.z.toFixed(2));
|
|
|
+ } else if (entity.orientation) {
|
|
|
+ try {
|
|
|
+ const orientation = entity.orientation.getValue ? entity.orientation.getValue(viewer.clock.currentTime) : entity.orientation;
|
|
|
+ if (orientation) {
|
|
|
+ const hpr = Cesium.HeadingPitchRoll.fromQuaternion(orientation);
|
|
|
+ rotation.x = Number(Cesium.Math.toDegrees(hpr.pitch).toFixed(2));
|
|
|
+ rotation.y = Number(Cesium.Math.toDegrees(hpr.heading).toFixed(2));
|
|
|
+ rotation.z = Number(Cesium.Math.toDegrees(hpr.roll).toFixed(2));
|
|
|
+ // 保存初始旋转值
|
|
|
+ entity._rotation = { x: rotation.x, y: rotation.y, z: rotation.z };
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error updating rotation:', error);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ rotation.x = 0;
|
|
|
+ rotation.y = 0;
|
|
|
+ rotation.z = 0;
|
|
|
+ // 保存初始旋转值
|
|
|
+ entity._rotation = { x: 0, y: 0, z: 0 };
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新缩放 - 从实体保存的原始尺寸计算(保留2位小数)
|
|
|
+ if (entity._scale) {
|
|
|
+ // 优先使用保存的缩放值
|
|
|
+ scale.x = Number(entity._scale.x.toFixed(2));
|
|
|
+ scale.y = Number(entity._scale.y.toFixed(2));
|
|
|
+ scale.z = Number(entity._scale.z.toFixed(2));
|
|
|
+ } else if (entity.box) {
|
|
|
+ if (entity.originalDimensions) {
|
|
|
+ const currentDimensions = entity.box.dimensions.getValue ? entity.box.dimensions.getValue(viewer.clock.currentTime) : entity.box.dimensions;
|
|
|
+ if (currentDimensions && entity.originalDimensions) {
|
|
|
+ scale.x = Number((currentDimensions.x / entity.originalDimensions.x).toFixed(2));
|
|
|
+ scale.y = Number((currentDimensions.y / entity.originalDimensions.y).toFixed(2));
|
|
|
+ scale.z = Number((currentDimensions.z / entity.originalDimensions.z).toFixed(2));
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: scale.x, y: scale.y, z: scale.z };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ scale.x = 1;
|
|
|
+ scale.y = 1;
|
|
|
+ scale.z = 1;
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: 1, y: 1, z: 1 };
|
|
|
+ }
|
|
|
+ } else if (entity.cylinder) {
|
|
|
+ if (entity.originalDimensions) {
|
|
|
+ const currentLength = entity.cylinder.length.getValue ? entity.cylinder.length.getValue(viewer.clock.currentTime) : entity.cylinder.length;
|
|
|
+ const currentTopRadius = entity.cylinder.topRadius.getValue ? entity.cylinder.topRadius.getValue(viewer.clock.currentTime) : entity.cylinder.topRadius;
|
|
|
+ const currentBottomRadius = entity.cylinder.bottomRadius.getValue ? entity.cylinder.bottomRadius.getValue(viewer.clock.currentTime) : entity.cylinder.bottomRadius;
|
|
|
+ if (currentLength && entity.originalDimensions.length) {
|
|
|
+ // X轴向控制一个方向的半径,Y轴向控制另一个方向的半径,Z轴向控制高度
|
|
|
+ scale.x = Number((currentTopRadius / entity.originalDimensions.topRadius).toFixed(2));
|
|
|
+ scale.y = Number((currentBottomRadius / entity.originalDimensions.bottomRadius).toFixed(2));
|
|
|
+ scale.z = Number((currentLength / entity.originalDimensions.length).toFixed(2));
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: scale.x, y: scale.y, z: scale.z };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ scale.x = 1;
|
|
|
+ scale.y = 1;
|
|
|
+ scale.z = 1;
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: 1, y: 1, z: 1 };
|
|
|
+ }
|
|
|
+ } else if (entity.ellipsoid) {
|
|
|
+ if (entity.originalDimensions) {
|
|
|
+ const currentRadii = entity.ellipsoid.radii.getValue ? entity.ellipsoid.radii.getValue(viewer.clock.currentTime) : entity.ellipsoid.radii;
|
|
|
+ if (currentRadii && entity.originalDimensions) {
|
|
|
+ scale.x = Number((currentRadii.x / entity.originalDimensions.x).toFixed(2));
|
|
|
+ scale.y = Number((currentRadii.y / entity.originalDimensions.y).toFixed(2));
|
|
|
+ scale.z = Number((currentRadii.z / entity.originalDimensions.z).toFixed(2));
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: scale.x, y: scale.y, z: scale.z };
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ scale.x = 1;
|
|
|
+ scale.y = 1;
|
|
|
+ scale.z = 1;
|
|
|
+ // 保存缩放值
|
|
|
+ entity._scale = { x: 1, y: 1, z: 1 };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置标志
|
|
|
+ isUpdatingProperties = false;
|
|
|
+
|
|
|
+ console.log('Updated properties:', {
|
|
|
+ selectedColor: selectedColor.value,
|
|
|
+ selectedOpacity: selectedOpacity.value,
|
|
|
+ rotation: rotation,
|
|
|
+ scale: scale
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 监听属性变化,更新实体
|
|
|
+ watch(selectedColor, (newColor) => {
|
|
|
+ if (!selectedEntity.value) return;
|
|
|
+ const color = Cesium.Color.fromCssColorString(newColor).withAlpha(selectedOpacity.value);
|
|
|
+ const material = new Cesium.ColorMaterialProperty(color);
|
|
|
+ if (selectedEntity.value.box) {
|
|
|
+ selectedEntity.value.box.material = material;
|
|
|
+ } else if (selectedEntity.value.cylinder) {
|
|
|
+ selectedEntity.value.cylinder.material = material;
|
|
|
+ } else if (selectedEntity.value.ellipsoid) {
|
|
|
+ selectedEntity.value.ellipsoid.material = material;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ watch(selectedOpacity, (newOpacity) => {
|
|
|
+ if (!selectedEntity.value) return;
|
|
|
+ const color = Cesium.Color.fromCssColorString(selectedColor.value).withAlpha(newOpacity);
|
|
|
+ const material = new Cesium.ColorMaterialProperty(color);
|
|
|
+ if (selectedEntity.value.box) {
|
|
|
+ selectedEntity.value.box.material = material;
|
|
|
+ } else if (selectedEntity.value.cylinder) {
|
|
|
+ selectedEntity.value.cylinder.material = material;
|
|
|
+ } else if (selectedEntity.value.ellipsoid) {
|
|
|
+ selectedEntity.value.ellipsoid.material = material;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ watch(position, (newPosition) => {
|
|
|
+ if (!selectedEntity.value) return;
|
|
|
+ const cartesian = Cesium.Cartesian3.fromDegrees(newPosition.x, newPosition.y, newPosition.z);
|
|
|
+ selectedEntity.value.position = cartesian;
|
|
|
+ }, { deep: true });
|
|
|
+
|
|
|
+ watch(scale, (newScale) => {
|
|
|
+ if (!selectedEntity.value) return;
|
|
|
+ console.log('Scale changed:', newScale, 'Entity:', selectedEntity.value);
|
|
|
+ // 保存缩放值到实体对象中(使用对象字面量创建新对象,避免引用问题)
|
|
|
+ selectedEntity.value._scale = { x: newScale.x, y: newScale.y, z: newScale.z };
|
|
|
+ // 保存原始尺寸,避免累积缩放
|
|
|
+ if (selectedEntity.value.box) {
|
|
|
+ // 保存原始尺寸
|
|
|
+ if (!selectedEntity.value.originalDimensions) {
|
|
|
+ selectedEntity.value.originalDimensions = selectedEntity.value.box.dimensions.getValue(viewer.clock.currentTime);
|
|
|
+ }
|
|
|
+ const originalDimensions = selectedEntity.value.originalDimensions;
|
|
|
+ selectedEntity.value.box.dimensions = new Cesium.Cartesian3(originalDimensions.x * newScale.x, originalDimensions.y * newScale.y, originalDimensions.z * newScale.z);
|
|
|
+ } else if (selectedEntity.value.cylinder) {
|
|
|
+ // 保存原始尺寸
|
|
|
+ if (!selectedEntity.value.originalDimensions) {
|
|
|
+ selectedEntity.value.originalDimensions = {
|
|
|
+ length: selectedEntity.value.cylinder.length.getValue(viewer.clock.currentTime),
|
|
|
+ topRadius: selectedEntity.value.cylinder.topRadius.getValue(viewer.clock.currentTime),
|
|
|
+ bottomRadius: selectedEntity.value.cylinder.bottomRadius.getValue(viewer.clock.currentTime)
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const originalDimensions = selectedEntity.value.originalDimensions;
|
|
|
+ // X轴向控制一个方向的半径,Y轴向控制另一个方向的半径,Z轴向控制高度
|
|
|
+ selectedEntity.value.cylinder.length = originalDimensions.length * newScale.z;
|
|
|
+ selectedEntity.value.cylinder.topRadius = originalDimensions.topRadius * newScale.x;
|
|
|
+ selectedEntity.value.cylinder.bottomRadius = originalDimensions.bottomRadius * newScale.y;
|
|
|
+ } else if (selectedEntity.value.ellipsoid) {
|
|
|
+ // 保存原始尺寸
|
|
|
+ if (!selectedEntity.value.originalDimensions) {
|
|
|
+ selectedEntity.value.originalDimensions = selectedEntity.value.ellipsoid.radii.getValue(viewer.clock.currentTime);
|
|
|
+ }
|
|
|
+ const originalDimensions = selectedEntity.value.originalDimensions;
|
|
|
+ selectedEntity.value.ellipsoid.radii = new Cesium.Cartesian3(originalDimensions.x * newScale.x, originalDimensions.y * newScale.y, originalDimensions.z * newScale.z);
|
|
|
+ }
|
|
|
+ }, { deep: true });
|
|
|
+
|
|
|
+ // 监听旋转变化,更新实体
|
|
|
+ watch(rotation, (newRotation) => {
|
|
|
+ if (!selectedEntity.value || isUpdatingProperties) return;
|
|
|
+ // 计算旋转矩阵
|
|
|
+ const heading = Cesium.Math.toRadians(newRotation.y);
|
|
|
+ const pitch = Cesium.Math.toRadians(newRotation.x);
|
|
|
+ const roll = Cesium.Math.toRadians(newRotation.z);
|
|
|
+ const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
|
|
|
+ const orientation = Cesium.Transforms.headingPitchRollQuaternion(
|
|
|
+ selectedEntity.value.position.getValue(viewer.clock.currentTime),
|
|
|
+ hpr
|
|
|
+ );
|
|
|
+ selectedEntity.value.orientation = orientation;
|
|
|
+ // 保存原始旋转值到实体
|
|
|
+ selectedEntity.value._rotation = { ...newRotation };
|
|
|
+ }, { deep: true });
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ console.log('Sm3dDrawSolidFigure mounted, viewer:', window.viewer);
|
|
|
+ viewer = window.viewer;
|
|
|
+ if (viewer) {
|
|
|
+ console.log('Initializing scene events...');
|
|
|
+ initSceneEvents();
|
|
|
+ } else {
|
|
|
+ console.error('Viewer not found!');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ onUnmounted(() => {
|
|
|
+ // 清理事件
|
|
|
+ cleanupEvents();
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ basicGeometries,
|
|
|
+ selectedEntity,
|
|
|
+ selectedColor,
|
|
|
+ selectedOpacity,
|
|
|
+ position,
|
|
|
+ rotation,
|
|
|
+ scale,
|
|
|
+ drawMode,
|
|
|
+ selectedModel,
|
|
|
+ isDrawing,
|
|
|
+ close,
|
|
|
+ selectModel,
|
|
|
+ startDraw,
|
|
|
+ clear
|
|
|
+ };
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.model-thumbnail {
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ object-fit: cover;
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.symbolic-box {
|
|
|
+ text-align: center;
|
|
|
+ cursor: pointer;
|
|
|
+ padding: 5px;
|
|
|
+ border-radius: 4px;
|
|
|
+ transition: all 0.3s;
|
|
|
+}
|
|
|
+
|
|
|
+.symbolic-box:hover {
|
|
|
+ background-color: #f0f0f0;
|
|
|
+}
|
|
|
+
|
|
|
+.symbolic-box.theme-border-color {
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ border: 1px solid #409eff;
|
|
|
+ box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.symbolic-box label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #606266;
|
|
|
+ display: block;
|
|
|
+ margin-top: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.symbolic-box.theme-border-color label {
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+</style>
|