draw-solid-figure.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826
  1. <template>
  2. <div id="draw-panel" class="sm-panel" v-drag>
  3. <div class="sm-function-module-sub-section" style="margin:0" v-stopdrag>
  4. <div class="sm-half-L">
  5. <label style="width:auto">
  6. <input type="radio" value="single" v-model="drawMode" />
  7. 单个
  8. </label>
  9. <label style="width:auto">
  10. <input type="radio" value="continuous" v-model="drawMode" />
  11. 连续
  12. </label>
  13. </div>
  14. <div class="sm-half-L symbolic">
  15. <div
  16. v-for="model in basicGeometries"
  17. :key="model.type"
  18. class="symbolic-box"
  19. style="width: 40px;"
  20. :class="{ 'theme-border-color': selectedModel && selectedModel.type === model.type }"
  21. @click="selectModel(model)"
  22. >
  23. <img :src="model.thumbnail" alt="" class="model-thumbnail" />
  24. <label style="width:100%">{{ model.name }}</label>
  25. </div>
  26. </div>
  27. <div class="sm-half-L">
  28. <label style="width: 35%;">颜色</label>
  29. <el-color-picker v-model="selectedColor" size="mini" style="width:63%"></el-color-picker>
  30. </div>
  31. <div class="sm-half-L">
  32. <label style="width: 35%;">透明度</label>
  33. <el-slider v-model="selectedOpacity" :min="0" :max="1" :step="0.1" style="width:63%"></el-slider>
  34. </div>
  35. <div class="sm-half-L" v-if="selectedEntity">
  36. <label style="width: 35%;">位置 X</label>
  37. <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.x" />
  38. </div>
  39. <div class="sm-half-L" v-if="selectedEntity">
  40. <label style="width: 35%;">位置 Y</label>
  41. <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.y" />
  42. </div>
  43. <div class="sm-half-L" v-if="selectedEntity">
  44. <label style="width: 35%;">位置 Z</label>
  45. <input type="number" class="sm-input" style="width:63%" step="0.1" v-model="position.z" />
  46. </div>
  47. <div class="sm-half-L" v-if="selectedEntity" style="display: flex; align-items: center;">
  48. <label style="width: 35%;">旋转</label>
  49. <div style="width:63%; display: flex; gap: 4px;">
  50. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.x" />
  51. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.y" />
  52. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="1" v-model="rotation.z" />
  53. </div>
  54. </div>
  55. <div class="sm-half-L" v-if="selectedEntity" style="display: flex; align-items: center;">
  56. <label style="width: 35%;">缩放</label>
  57. <div style="width:63%; display: flex; gap: 4px;">
  58. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.x" />
  59. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.y" />
  60. <input type="number" class="sm-input" style="flex: 1; width: 45px;" step="0.1" min="0.1" v-model="scale.z" />
  61. </div>
  62. </div>
  63. <div class="boxchild">
  64. <button class="tbtn" type="button" v-on:click="startDraw">{{ isDrawing ? '停止绘制' : '绘制' }}</button>
  65. <button class="tbtn tbtn-margin-left" type="button" v-on:click="clear">清除</button>
  66. <button class="tbtn tbtn-margin-left" type="button" v-on:click="close">关闭</button>
  67. </div>
  68. </div>
  69. </div>
  70. </template>
  71. <script>
  72. import { ref, onMounted, onUnmounted, reactive, watch } from 'vue';
  73. import { ElMessage } from 'element-plus';
  74. export default {
  75. name: 'Sm3dDrawSolidFigure',
  76. emits: ['close'],
  77. setup(props, { emit }) {
  78. // 基础几何体模型
  79. const basicGeometries = ref([
  80. {
  81. type: 'box',
  82. name: '立方体',
  83. thumbnail: '/img/componentsImg/draw-solid-figuree.png',
  84. dimensions: { x: 1, y: 1, z: 1 }
  85. },
  86. {
  87. type: 'cylinder',
  88. name: '圆柱体',
  89. thumbnail: '/img/componentsImg/draw-solid-figuree.png',
  90. dimensions: { length: 1, radius: 0.5 }
  91. },
  92. {
  93. type: 'sphere',
  94. name: '球体',
  95. thumbnail: '/img/componentsImg/draw-solid-figuree.png',
  96. dimensions: { radius: 0.5 }
  97. },
  98. {
  99. type: 'cone',
  100. name: '圆锥体',
  101. thumbnail: '/img/componentsImg/draw-solid-figuree.png',
  102. dimensions: { length: 1, radius: 0.5 }
  103. }
  104. ]);
  105. // 选中的实体
  106. const selectedEntity = ref(null);
  107. // 选中实体的属性
  108. const selectedColor = ref('#409eff');
  109. const selectedOpacity = ref(0.8);
  110. const position = reactive({ x: 0, y: 0, z: 0 });
  111. const rotation = reactive({ x: 0, y: 0, z: 0 });
  112. const scale = reactive({ x: 1, y: 1, z: 1 });
  113. // 绘制模式
  114. const drawMode = ref('single');
  115. // 选中的模型类型
  116. const selectedModel = ref(null);
  117. // 绘制状态
  118. const isDrawing = ref(false);
  119. // 绘制激活标志,用于区分是否正在进行绘制(避免与选中实体冲突)
  120. let isDrawHandlerActive = false;
  121. // 防止循环更新的标志
  122. let isUpdatingProperties = false;
  123. let viewer = null;
  124. let currentEditor = null;
  125. const close = () => {
  126. // 通知父组件关闭此组件
  127. emit('close', 'Sm3dDrawSolidFigure');
  128. };
  129. // 选择模型类型
  130. const selectModel = (model) => {
  131. selectedModel.value = model;
  132. ElMessage.success(`已选择 ${model.name}`);
  133. };
  134. // 开始绘制
  135. const startDraw = () => {
  136. console.log('startDraw called, isDrawing:', isDrawing.value, 'selectedModel:', selectedModel.value);
  137. if (!selectedModel.value) {
  138. ElMessage.warning('请先选择模型类型');
  139. return;
  140. }
  141. isDrawing.value = !isDrawing.value;
  142. console.log('After toggle, isDrawing:', isDrawing.value);
  143. if (isDrawing.value) {
  144. // 清除之前选中的实体的高亮
  145. if (selectedEntity.value) {
  146. removeHighlight(selectedEntity.value);
  147. if (currentEditor) {
  148. currentEditor.deactivate();
  149. currentEditor = null;
  150. }
  151. selectedEntity.value = null;
  152. }
  153. isDrawHandlerActive = true;
  154. viewer.enableCursorStyle = false;
  155. viewer._element.style.cursor = 'crosshair';
  156. ElMessage.success(`开始${drawMode.value === 'single' ? '单个' : '连续'}绘制模式`);
  157. } else {
  158. ElMessage.info('已停止绘制');
  159. selectedModel.value = null;
  160. isDrawHandlerActive = false;
  161. viewer.enableCursorStyle = true;
  162. viewer._element.style.cursor = 'default';
  163. }
  164. };
  165. // 清除所有模型
  166. const clear = () => {
  167. if (viewer) {
  168. viewer.entities.removeAll();
  169. selectedEntity.value = null;
  170. selectedModel.value = null;
  171. isDrawing.value = false;
  172. if (currentEditor) {
  173. currentEditor.deactivate();
  174. currentEditor = null;
  175. }
  176. ElMessage.success('已清除所有模型');
  177. }
  178. };
  179. // 左键点击处理
  180. const onLeftClick = (movement) => {
  181. console.log('=== Left click ===');
  182. console.log('isDrawing:', isDrawing.value);
  183. console.log('selectedModel:', selectedModel.value);
  184. console.log('drawMode:', drawMode.value);
  185. // 绘制模式
  186. if (isDrawing.value === true && selectedModel.value !== null) {
  187. console.log('Entering DRAW mode');
  188. let position = viewer.scene.pickPosition(movement.position);
  189. console.log('Position:', position, 'defined:', Cesium.defined(position));
  190. if (!Cesium.defined(position)) {
  191. console.warn('Cannot get position');
  192. return;
  193. }
  194. createGeometry(position, selectedModel.value);
  195. console.log('Geometry created, drawMode:', drawMode.value);
  196. if (drawMode.value === 'single') {
  197. console.log('Single mode - stopping draw');
  198. isDrawing.value = false;
  199. isDrawHandlerActive = false;
  200. selectedModel.value = null;
  201. viewer.enableCursorStyle = true;
  202. viewer._element.style.cursor = 'default';
  203. ElMessage.info('已完成单个绘制');
  204. } else {
  205. console.log('Continuous mode - continuing');
  206. }
  207. return;
  208. }
  209. console.log('Entering SELECT mode');
  210. // 非绘制模式:选择实体
  211. let pickedObject = viewer.scene.pick(movement.position);
  212. console.log('Picked object:', pickedObject);
  213. if (Cesium.defined(pickedObject) && pickedObject.id) {
  214. selectEntity(pickedObject.id);
  215. } else if (isDrawing.value === false) {
  216. clearSelection();
  217. }
  218. };
  219. // 右键点击处理
  220. const onRightClick = (movement) => {
  221. console.log('=== Right click ===');
  222. console.log('isDrawing:', isDrawing.value);
  223. console.log('drawMode:', drawMode.value);
  224. console.log('Condition result:', isDrawing.value === true && drawMode.value === 'continuous');
  225. if (isDrawing.value === true && drawMode.value === 'continuous') {
  226. console.log('Stopping continuous draw');
  227. isDrawing.value = false;
  228. isDrawHandlerActive = false;
  229. selectedModel.value = null;
  230. viewer.enableCursorStyle = true;
  231. viewer._element.style.cursor = 'default';
  232. ElMessage.info('已结束连续绘制');
  233. }
  234. };
  235. // 阻止右键默认菜单
  236. const onContextMenu = (event) => {
  237. if (isDrawing.value) {
  238. event.preventDefault();
  239. }
  240. };
  241. // 键盘事件处理函数
  242. const handleKeyDown = (event) => {
  243. if (event.key === 'Enter') {
  244. event.preventDefault();
  245. }
  246. };
  247. // 初始化场景事件
  248. const initSceneEvents = () => {
  249. if (!viewer) return;
  250. // 左键点击 - 绘制或选择
  251. viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  252. viewer.screenSpaceEventHandler.setInputAction(onLeftClick, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  253. // 右键点击 - 结束连续绘制
  254. viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  255. viewer.screenSpaceEventHandler.setInputAction(onRightClick, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  256. // 阻止右键默认菜单
  257. viewer._element.addEventListener('contextmenu', onContextMenu);
  258. // 键盘事件
  259. document.addEventListener('keydown', handleKeyDown);
  260. };
  261. // 清理事件
  262. const cleanupEvents = () => {
  263. if (viewer) {
  264. viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  265. viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
  266. viewer._element.removeEventListener('contextmenu', onContextMenu);
  267. viewer.enableCursorStyle = true;
  268. viewer._element.style.cursor = 'default';
  269. }
  270. document.removeEventListener('keydown', handleKeyDown);
  271. if (currentEditor) {
  272. currentEditor.deactivate();
  273. currentEditor = null;
  274. }
  275. isDrawing.value = false;
  276. isDrawHandlerActive = false;
  277. };
  278. // 选择实体
  279. const selectEntity = (entity) => {
  280. if (selectedEntity.value) {
  281. removeHighlight(selectedEntity.value);
  282. if (currentEditor) {
  283. currentEditor.deactivate();
  284. currentEditor = null;
  285. }
  286. }
  287. selectedEntity.value = entity;
  288. updateSelectedEntityProperties();
  289. addHighlight(entity);
  290. addEditor(entity);
  291. };
  292. // 清除选择
  293. const clearSelection = () => {
  294. if (selectedEntity.value) {
  295. removeHighlight(selectedEntity.value);
  296. if (currentEditor) {
  297. currentEditor.deactivate();
  298. currentEditor = null;
  299. }
  300. selectedEntity.value = null;
  301. }
  302. };
  303. // 添加模型高亮效果
  304. const addHighlight = (entity) => {
  305. if (!entity) return;
  306. // 保存原始状态(只在第一次保存)
  307. if (entity.box) {
  308. if (entity.originalOutline === undefined) {
  309. entity.originalOutline = entity.box.outline;
  310. entity.originalOutlineColor = entity.box.outlineColor;
  311. }
  312. entity.box.outline = true;
  313. entity.box.outlineColor = new Cesium.Color(1, 1, 0, 1);
  314. } else if (entity.cylinder) {
  315. if (entity.originalOutline === undefined) {
  316. entity.originalOutline = entity.cylinder.outline;
  317. entity.originalOutlineColor = entity.cylinder.outlineColor;
  318. }
  319. entity.cylinder.outline = true;
  320. entity.cylinder.outlineColor = new Cesium.Color(1, 1, 0, 1);
  321. } else if (entity.ellipsoid) {
  322. if (entity.originalOutline === undefined) {
  323. entity.originalOutline = entity.ellipsoid.outline;
  324. entity.originalOutlineColor = entity.ellipsoid.outlineColor;
  325. }
  326. entity.ellipsoid.outline = true;
  327. entity.ellipsoid.outlineColor = new Cesium.Color(1, 1, 0, 1);
  328. }
  329. };
  330. // 移除模型高亮效果
  331. const removeHighlight = (entity) => {
  332. if (!entity) return;
  333. if (entity.box) {
  334. if (entity.originalOutline !== undefined) {
  335. entity.box.outline = entity.originalOutline;
  336. entity.box.outlineColor = entity.originalOutlineColor;
  337. } else {
  338. entity.box.outline = false;
  339. }
  340. } else if (entity.cylinder) {
  341. if (entity.originalOutline !== undefined) {
  342. entity.cylinder.outline = entity.originalOutline;
  343. entity.cylinder.outlineColor = entity.originalOutlineColor;
  344. } else {
  345. entity.cylinder.outline = false;
  346. }
  347. } else if (entity.ellipsoid) {
  348. if (entity.originalOutline !== undefined) {
  349. entity.ellipsoid.outline = entity.originalOutline;
  350. entity.ellipsoid.outlineColor = entity.originalOutlineColor;
  351. } else {
  352. entity.ellipsoid.outline = false;
  353. }
  354. }
  355. };
  356. // 添加操作轴编辑器
  357. const addEditor = (entity) => {
  358. if (!entity) return;
  359. // 清除之前的编辑器
  360. if (currentEditor) {
  361. currentEditor.deactivate();
  362. currentEditor = null;
  363. }
  364. };
  365. // 创建几何体
  366. const createGeometry = (position, model) => {
  367. if (!viewer) return;
  368. const color = Cesium.Color.fromCssColorString(selectedColor.value).withAlpha(selectedOpacity.value);
  369. let entity;
  370. switch (model.type) {
  371. case 'box':
  372. entity = viewer.entities.add({
  373. position: position,
  374. box: {
  375. dimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
  376. material: new Cesium.ColorMaterialProperty(color)
  377. },
  378. modelType: model.type,
  379. modelName: model.name,
  380. originalDimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
  381. _rotation: { x: 0, y: 0, z: 0 },
  382. _scale: { x: 1, y: 1, z: 1 }
  383. });
  384. break;
  385. case 'sphere':
  386. entity = viewer.entities.add({
  387. position: position,
  388. ellipsoid: {
  389. radii: new Cesium.Cartesian3(model.dimensions.radius, model.dimensions.radius, model.dimensions.radius),
  390. material: new Cesium.ColorMaterialProperty(color)
  391. },
  392. modelType: model.type,
  393. modelName: model.name,
  394. originalDimensions: new Cesium.Cartesian3(model.dimensions.radius, model.dimensions.radius, model.dimensions.radius),
  395. _rotation: { x: 0, y: 0, z: 0 },
  396. _scale: { x: 1, y: 1, z: 1 }
  397. });
  398. break;
  399. case 'cylinder':
  400. entity = viewer.entities.add({
  401. position: position,
  402. cylinder: {
  403. length: model.dimensions.length,
  404. topRadius: model.dimensions.radius,
  405. bottomRadius: model.dimensions.radius,
  406. material: new Cesium.ColorMaterialProperty(color)
  407. },
  408. modelType: model.type,
  409. modelName: model.name,
  410. originalDimensions: {
  411. length: model.dimensions.length,
  412. topRadius: model.dimensions.radius,
  413. bottomRadius: model.dimensions.radius
  414. },
  415. _rotation: { x: 0, y: 0, z: 0 },
  416. _scale: { x: 1, y: 1, z: 1 }
  417. });
  418. break;
  419. case 'ellipsoid':
  420. entity = viewer.entities.add({
  421. position: position,
  422. ellipsoid: {
  423. radii: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
  424. material: new Cesium.ColorMaterialProperty(color)
  425. },
  426. modelType: model.type,
  427. modelName: model.name,
  428. originalDimensions: new Cesium.Cartesian3(model.dimensions.x, model.dimensions.y, model.dimensions.z),
  429. _rotation: { x: 0, y: 0, z: 0 },
  430. _scale: { x: 1, y: 1, z: 1 }
  431. });
  432. break;
  433. case 'cone':
  434. entity = viewer.entities.add({
  435. position: position,
  436. cylinder: {
  437. length: model.dimensions.length,
  438. topRadius: 0,
  439. bottomRadius: model.dimensions.radius,
  440. material: new Cesium.ColorMaterialProperty(color)
  441. },
  442. modelType: model.type,
  443. modelName: model.name,
  444. originalDimensions: {
  445. length: model.dimensions.length,
  446. topRadius: 0,
  447. bottomRadius: model.dimensions.radius
  448. },
  449. _rotation: { x: 0, y: 0, z: 0 },
  450. _scale: { x: 1, y: 1, z: 1 }
  451. });
  452. break;
  453. default:
  454. return;
  455. }
  456. if (entity) {
  457. ElMessage.success(`${model.name} 创建成功`);
  458. // 清除之前的高亮和编辑器
  459. if (selectedEntity.value) {
  460. removeHighlight(selectedEntity.value);
  461. if (currentEditor) {
  462. currentEditor.deactivate();
  463. currentEditor = null;
  464. }
  465. }
  466. selectedEntity.value = entity;
  467. updateSelectedEntityProperties();
  468. // 添加高亮效果
  469. addHighlight(entity);
  470. // 添加操作轴
  471. addEditor(entity);
  472. return entity;
  473. }
  474. return null;
  475. };
  476. // 更新选中实体的属性
  477. const updateSelectedEntityProperties = () => {
  478. if (!selectedEntity.value) return;
  479. console.log('Updating properties for entity:', selectedEntity.value);
  480. // 获取实体
  481. const entity = selectedEntity.value;
  482. // 设置标志,防止循环更新
  483. isUpdatingProperties = true;
  484. // 更新位置(经纬度保留6位小数,高度保留6位小数)
  485. const cartesian = entity.position.getValue(viewer.clock.currentTime);
  486. if (cartesian) {
  487. const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
  488. position.x = Number(Cesium.Math.toDegrees(cartographic.longitude).toFixed(6));
  489. position.y = Number(Cesium.Math.toDegrees(cartographic.latitude).toFixed(6));
  490. position.z = Number(cartographic.height.toFixed(6));
  491. }
  492. // 更新颜色和透明度
  493. let material = null;
  494. if (entity.box) {
  495. material = entity.box.material;
  496. } else if (entity.cylinder) {
  497. material = entity.cylinder.material;
  498. } else if (entity.ellipsoid) {
  499. material = entity.ellipsoid.material;
  500. }
  501. if (material) {
  502. let color = null;
  503. if (material.color) {
  504. if (typeof material.color.getValue === 'function') {
  505. color = material.color.getValue(viewer.clock.currentTime);
  506. } else {
  507. color = material.color;
  508. }
  509. }
  510. if (color instanceof Cesium.Color) {
  511. selectedColor.value = color.toCssColorString();
  512. selectedOpacity.value = Number(color.alpha.toFixed(2));
  513. } else if (material instanceof Cesium.Color) {
  514. selectedColor.value = material.toCssColorString();
  515. selectedOpacity.value = Number(material.alpha.toFixed(2));
  516. }
  517. }
  518. // 更新旋转(保留2位小数)
  519. if (entity._rotation) {
  520. // 优先使用保存的旋转值
  521. rotation.x = Number(entity._rotation.x.toFixed(2));
  522. rotation.y = Number(entity._rotation.y.toFixed(2));
  523. rotation.z = Number(entity._rotation.z.toFixed(2));
  524. } else if (entity.orientation) {
  525. try {
  526. const orientation = entity.orientation.getValue ? entity.orientation.getValue(viewer.clock.currentTime) : entity.orientation;
  527. if (orientation) {
  528. const hpr = Cesium.HeadingPitchRoll.fromQuaternion(orientation);
  529. rotation.x = Number(Cesium.Math.toDegrees(hpr.pitch).toFixed(2));
  530. rotation.y = Number(Cesium.Math.toDegrees(hpr.heading).toFixed(2));
  531. rotation.z = Number(Cesium.Math.toDegrees(hpr.roll).toFixed(2));
  532. // 保存初始旋转值
  533. entity._rotation = { x: rotation.x, y: rotation.y, z: rotation.z };
  534. }
  535. } catch (error) {
  536. console.error('Error updating rotation:', error);
  537. }
  538. } else {
  539. rotation.x = 0;
  540. rotation.y = 0;
  541. rotation.z = 0;
  542. // 保存初始旋转值
  543. entity._rotation = { x: 0, y: 0, z: 0 };
  544. }
  545. // 更新缩放 - 从实体保存的原始尺寸计算(保留2位小数)
  546. if (entity._scale) {
  547. // 优先使用保存的缩放值
  548. scale.x = Number(entity._scale.x.toFixed(2));
  549. scale.y = Number(entity._scale.y.toFixed(2));
  550. scale.z = Number(entity._scale.z.toFixed(2));
  551. } else if (entity.box) {
  552. if (entity.originalDimensions) {
  553. const currentDimensions = entity.box.dimensions.getValue ? entity.box.dimensions.getValue(viewer.clock.currentTime) : entity.box.dimensions;
  554. if (currentDimensions && entity.originalDimensions) {
  555. scale.x = Number((currentDimensions.x / entity.originalDimensions.x).toFixed(2));
  556. scale.y = Number((currentDimensions.y / entity.originalDimensions.y).toFixed(2));
  557. scale.z = Number((currentDimensions.z / entity.originalDimensions.z).toFixed(2));
  558. // 保存缩放值
  559. entity._scale = { x: scale.x, y: scale.y, z: scale.z };
  560. }
  561. } else {
  562. scale.x = 1;
  563. scale.y = 1;
  564. scale.z = 1;
  565. // 保存缩放值
  566. entity._scale = { x: 1, y: 1, z: 1 };
  567. }
  568. } else if (entity.cylinder) {
  569. if (entity.originalDimensions) {
  570. const currentLength = entity.cylinder.length.getValue ? entity.cylinder.length.getValue(viewer.clock.currentTime) : entity.cylinder.length;
  571. const currentTopRadius = entity.cylinder.topRadius.getValue ? entity.cylinder.topRadius.getValue(viewer.clock.currentTime) : entity.cylinder.topRadius;
  572. const currentBottomRadius = entity.cylinder.bottomRadius.getValue ? entity.cylinder.bottomRadius.getValue(viewer.clock.currentTime) : entity.cylinder.bottomRadius;
  573. if (currentLength && entity.originalDimensions.length) {
  574. // X轴向控制一个方向的半径,Y轴向控制另一个方向的半径,Z轴向控制高度
  575. scale.x = Number((currentTopRadius / entity.originalDimensions.topRadius).toFixed(2));
  576. scale.y = Number((currentBottomRadius / entity.originalDimensions.bottomRadius).toFixed(2));
  577. scale.z = Number((currentLength / entity.originalDimensions.length).toFixed(2));
  578. // 保存缩放值
  579. entity._scale = { x: scale.x, y: scale.y, z: scale.z };
  580. }
  581. } else {
  582. scale.x = 1;
  583. scale.y = 1;
  584. scale.z = 1;
  585. // 保存缩放值
  586. entity._scale = { x: 1, y: 1, z: 1 };
  587. }
  588. } else if (entity.ellipsoid) {
  589. if (entity.originalDimensions) {
  590. const currentRadii = entity.ellipsoid.radii.getValue ? entity.ellipsoid.radii.getValue(viewer.clock.currentTime) : entity.ellipsoid.radii;
  591. if (currentRadii && entity.originalDimensions) {
  592. scale.x = Number((currentRadii.x / entity.originalDimensions.x).toFixed(2));
  593. scale.y = Number((currentRadii.y / entity.originalDimensions.y).toFixed(2));
  594. scale.z = Number((currentRadii.z / entity.originalDimensions.z).toFixed(2));
  595. // 保存缩放值
  596. entity._scale = { x: scale.x, y: scale.y, z: scale.z };
  597. }
  598. } else {
  599. scale.x = 1;
  600. scale.y = 1;
  601. scale.z = 1;
  602. // 保存缩放值
  603. entity._scale = { x: 1, y: 1, z: 1 };
  604. }
  605. }
  606. // 重置标志
  607. isUpdatingProperties = false;
  608. console.log('Updated properties:', {
  609. selectedColor: selectedColor.value,
  610. selectedOpacity: selectedOpacity.value,
  611. rotation: rotation,
  612. scale: scale
  613. });
  614. };
  615. // 监听属性变化,更新实体
  616. watch(selectedColor, (newColor) => {
  617. if (!selectedEntity.value) return;
  618. const color = Cesium.Color.fromCssColorString(newColor).withAlpha(selectedOpacity.value);
  619. const material = new Cesium.ColorMaterialProperty(color);
  620. if (selectedEntity.value.box) {
  621. selectedEntity.value.box.material = material;
  622. } else if (selectedEntity.value.cylinder) {
  623. selectedEntity.value.cylinder.material = material;
  624. } else if (selectedEntity.value.ellipsoid) {
  625. selectedEntity.value.ellipsoid.material = material;
  626. }
  627. });
  628. watch(selectedOpacity, (newOpacity) => {
  629. if (!selectedEntity.value) return;
  630. const color = Cesium.Color.fromCssColorString(selectedColor.value).withAlpha(newOpacity);
  631. const material = new Cesium.ColorMaterialProperty(color);
  632. if (selectedEntity.value.box) {
  633. selectedEntity.value.box.material = material;
  634. } else if (selectedEntity.value.cylinder) {
  635. selectedEntity.value.cylinder.material = material;
  636. } else if (selectedEntity.value.ellipsoid) {
  637. selectedEntity.value.ellipsoid.material = material;
  638. }
  639. });
  640. watch(position, (newPosition) => {
  641. if (!selectedEntity.value) return;
  642. const cartesian = Cesium.Cartesian3.fromDegrees(newPosition.x, newPosition.y, newPosition.z);
  643. selectedEntity.value.position = cartesian;
  644. }, { deep: true });
  645. watch(scale, (newScale) => {
  646. if (!selectedEntity.value) return;
  647. console.log('Scale changed:', newScale, 'Entity:', selectedEntity.value);
  648. // 保存缩放值到实体对象中(使用对象字面量创建新对象,避免引用问题)
  649. selectedEntity.value._scale = { x: newScale.x, y: newScale.y, z: newScale.z };
  650. // 保存原始尺寸,避免累积缩放
  651. if (selectedEntity.value.box) {
  652. // 保存原始尺寸
  653. if (!selectedEntity.value.originalDimensions) {
  654. selectedEntity.value.originalDimensions = selectedEntity.value.box.dimensions.getValue(viewer.clock.currentTime);
  655. }
  656. const originalDimensions = selectedEntity.value.originalDimensions;
  657. selectedEntity.value.box.dimensions = new Cesium.Cartesian3(originalDimensions.x * newScale.x, originalDimensions.y * newScale.y, originalDimensions.z * newScale.z);
  658. } else if (selectedEntity.value.cylinder) {
  659. // 保存原始尺寸
  660. if (!selectedEntity.value.originalDimensions) {
  661. selectedEntity.value.originalDimensions = {
  662. length: selectedEntity.value.cylinder.length.getValue(viewer.clock.currentTime),
  663. topRadius: selectedEntity.value.cylinder.topRadius.getValue(viewer.clock.currentTime),
  664. bottomRadius: selectedEntity.value.cylinder.bottomRadius.getValue(viewer.clock.currentTime)
  665. };
  666. }
  667. const originalDimensions = selectedEntity.value.originalDimensions;
  668. // X轴向控制一个方向的半径,Y轴向控制另一个方向的半径,Z轴向控制高度
  669. selectedEntity.value.cylinder.length = originalDimensions.length * newScale.z;
  670. selectedEntity.value.cylinder.topRadius = originalDimensions.topRadius * newScale.x;
  671. selectedEntity.value.cylinder.bottomRadius = originalDimensions.bottomRadius * newScale.y;
  672. } else if (selectedEntity.value.ellipsoid) {
  673. // 保存原始尺寸
  674. if (!selectedEntity.value.originalDimensions) {
  675. selectedEntity.value.originalDimensions = selectedEntity.value.ellipsoid.radii.getValue(viewer.clock.currentTime);
  676. }
  677. const originalDimensions = selectedEntity.value.originalDimensions;
  678. selectedEntity.value.ellipsoid.radii = new Cesium.Cartesian3(originalDimensions.x * newScale.x, originalDimensions.y * newScale.y, originalDimensions.z * newScale.z);
  679. }
  680. }, { deep: true });
  681. // 监听旋转变化,更新实体
  682. watch(rotation, (newRotation) => {
  683. if (!selectedEntity.value || isUpdatingProperties) return;
  684. // 计算旋转矩阵
  685. const heading = Cesium.Math.toRadians(newRotation.y);
  686. const pitch = Cesium.Math.toRadians(newRotation.x);
  687. const roll = Cesium.Math.toRadians(newRotation.z);
  688. const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
  689. const orientation = Cesium.Transforms.headingPitchRollQuaternion(
  690. selectedEntity.value.position.getValue(viewer.clock.currentTime),
  691. hpr
  692. );
  693. selectedEntity.value.orientation = orientation;
  694. // 保存原始旋转值到实体
  695. selectedEntity.value._rotation = { ...newRotation };
  696. }, { deep: true });
  697. onMounted(() => {
  698. console.log('Sm3dDrawSolidFigure mounted, viewer:', window.viewer);
  699. viewer = window.viewer;
  700. if (viewer) {
  701. console.log('Initializing scene events...');
  702. initSceneEvents();
  703. } else {
  704. console.error('Viewer not found!');
  705. }
  706. });
  707. onUnmounted(() => {
  708. // 清理事件
  709. cleanupEvents();
  710. });
  711. return {
  712. basicGeometries,
  713. selectedEntity,
  714. selectedColor,
  715. selectedOpacity,
  716. position,
  717. rotation,
  718. scale,
  719. drawMode,
  720. selectedModel,
  721. isDrawing,
  722. close,
  723. selectModel,
  724. startDraw,
  725. clear
  726. };
  727. }
  728. };
  729. </script>
  730. <style scoped>
  731. .model-thumbnail {
  732. width: 30px;
  733. height: 30px;
  734. object-fit: cover;
  735. margin-bottom: 5px;
  736. }
  737. .symbolic-box {
  738. text-align: center;
  739. cursor: pointer;
  740. padding: 5px;
  741. border-radius: 4px;
  742. transition: all 0.3s;
  743. }
  744. .symbolic-box:hover {
  745. background-color: #f0f0f0;
  746. }
  747. .symbolic-box.theme-border-color {
  748. background-color: #ecf5ff;
  749. border: 1px solid #409eff;
  750. box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
  751. }
  752. .symbolic-box label {
  753. font-size: 12px;
  754. color: #606266;
  755. display: block;
  756. margin-top: 5px;
  757. }
  758. .symbolic-box.theme-border-color label {
  759. color: #409eff;
  760. font-weight: 500;
  761. }
  762. </style>