/
counterbugtech
/
KontrBugCAD
Обзор
Документация
Войти
/
counterbugtech
/
KontrBugCAD
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
js/tools/split-tool.js
1 строка
8 KB
counterbugtech
first commit
04 мар 2026, 11:25
Верифицирован
04 мар 2026, 11:25
b2afbd6
Код
Авторство
О чём код?
import*as THREE from"three";import{ParametricOperation}from"core/parametric/ParametricOperation.js";import{Tool}from"./tool.js";export class SplitTool extends Tool{constructor(e){super("split","fa-cut",e),this.requiresSelection=!0,this.selectedPlane=null,this.tempVisuals=[],this.splitPlane=null,this.propertiesElement=null,this.planes=[],this.splitMode="select"}onActivate(){if(!this.canActivate())return void this.editor.toolManager.restorePreviousTool();if(1!==this.editor.selectedObjects.length)return this.editor.showStatus("Для разрезания выберите ровно один объект","error"),void this.editor.toolManager.restorePreviousTool();const e=this.editor.selectedObjects[0];return"work_plane"===e.userData.type||"sketch_plane"===e.userData.type?(this.editor.showStatus("Нельзя разрезать плоскость","error"),void this.editor.toolManager.restorePreviousTool()):(this.editor.scene.traverse(e=>{e.userData&&"work_plane"===e.userData.type&&this.planes.push(e)}),this.splitMode="select",this.selectedPlane=null,this.createPropertiesSection(),document.body.style.cursor="crosshair",this.editor.showStatus("Выберите рабочую плоскость для разрезания (клик по плоскости)","info"),!0)}onDeactivate(){this.removePropertiesSection(),this.cleanup(),document.body.style.cursor="default"}onMouseDown(e){if(0!==e.button||!this.splitMode)return!1;if(this.editor.updateMousePosition(e),this.editor.raycaster.setFromCamera(this.editor.mouse,this.editor.camera),"select"===this.splitMode){const e=this.editor.raycaster.intersectObjects(this.planes,!0);if(e.length>0){const t=e[0].object;return this.selectedPlane=t,this.splitMode="confirm",this.createSplitVisualization(t),this.updateUI(),this.editor.showStatus('Плоскость выбрана. Нажмите "Разрезать" или Enter для выполнения',"success"),!0}this.editor.showStatus("Кликните по рабочей плоскости","warning")}return!1}onMouseMove(e){if("select"===this.splitMode){this.editor.updateMousePosition(e),this.editor.raycaster.setFromCamera(this.editor.mouse,this.editor.camera);const t=this.editor.raycaster.intersectObjects(this.planes,!0);if(this.planes.forEach(e=>{e.children.forEach(e=>{e.userData.isHighlight&&(e.visible=!1)})}),t.length>0&&!this.selectedPlane){const e=t[0].object;this.highlightPlane(e)}}return!0}highlightPlane(e){let t=e.children.find(e=>e.userData.isHighlight);if(!t){const i=new THREE.PlaneGeometry(10,10),s=new THREE.MeshBasicMaterial({color:16776960,transparent:!0,opacity:.3,side:THREE.DoubleSide});t=new THREE.Mesh(i,s),t.userData.isHighlight=!0,t.renderOrder=999,e.add(t)}t.visible=!0}createSplitVisualization(e){this.cleanupVisualization();const t=this.editor.selectedObjects[0],i=(new THREE.Box3).setFromObject(t),s=new THREE.Vector3;i.getSize(s);const r=new THREE.PlaneGeometry(1.5*s.x,1.5*s.y),o=new THREE.MeshBasicMaterial({color:16711680,transparent:!0,opacity:.3,side:THREE.DoubleSide});this.splitPlane=new THREE.Mesh(r,o),this.splitPlane.position.copy(e.position),this.splitPlane.quaternion.copy(e.quaternion),this.splitPlane.userData.isSplitPlane=!0,this.splitPlane.renderOrder=998,this.editor.scene.add(this.splitPlane),this.tempVisuals.push(this.splitPlane);const n=new THREE.EdgesGeometry(r),l=new THREE.LineBasicMaterial({color:16711680,linewidth:2}),a=new THREE.LineSegments(n,l);a.position.copy(e.position),a.quaternion.copy(e.quaternion),a.userData.isSplitHelper=!0,a.renderOrder=999,this.editor.scene.add(a),this.tempVisuals.push(a)}createPropertiesSection(){const e=document.getElementById("propertiesContent");e&&(this.removePropertiesSection(),this.propertiesElement=document.createElement("div"),this.propertiesElement.className="property-group",this.propertiesElement.setAttribute("data-tool","splitTool"),this.propertiesElement.innerHTML=this.getPropertiesHTML(),e.appendChild(this.propertiesElement),this.bindPropertiesEvents())}removePropertiesSection(){const e=document.querySelector('.property-group[data-tool="splitTool"]');e&&e.remove(),this.propertiesElement=null}getPropertiesHTML(){return`\n <div class="property-group" data-type="split-settings">\n <h4>РАЗРЕЗАНИЕ ОБЪЕКТА</h4>\n\n <div class="property-row">\n <label>Объект:</label>\n <div style="padding: 8px; font-size: 12px;">\n ${this.editor.selectedObjects[0]?.userData?.name||"Выбранный объект"}\n </div>\n </div>\n\n <div class="property-row">\n <label>Плоскость разреза:</label>\n <div id="selectedPlaneInfo" style="padding: 8px; font-size: 12px;">\n ${this.selectedPlane?this.selectedPlane.userData?.name||"Плоскость":"Кликните по рабочей плоскости"}\n </div>\n </div>\n\n <div class="property-row">\n <label>Действие:</label>\n <div style="display: flex; gap: 8px;">\n <button id="applySplit" style="flex: 1; padding: 8px 16px; background: var(--primary-color); color: white; border: none; border-radius: 4px; cursor: pointer;" ${this.selectedPlane?"":"disabled"}>\n <i class="fas fa-cut"></i> Разрезать\n </button>\n <button id="cancelSplit" style="flex: 1; padding: 8px 16px; background: red; color: white; border: none; border-radius: 4px; cursor: pointer;">\n <i class="fas fa-times"></i> Отмена\n </button>\n </div>\n </div>\n\n <div style="padding: 8px; display: flex; font-size: 11px; color: var(--text-secondary);">\n <i class="fas fa-info-circle"></i>\n <div>Выберите рабочую плоскость для разреза</div>\n </div>\n </div>\n `}updateUI(){if(!this.propertiesElement)return;const e=this.propertiesElement.querySelector("#selectedPlaneInfo"),t=this.propertiesElement.querySelector("#applySplit");e&&(e.textContent=this.selectedPlane?this.selectedPlane.userData?.name||"Плоскость":"Кликните по рабочей плоскости"),t&&(t.disabled=!this.selectedPlane,t.style.opacity=this.selectedPlane?"1":"0.5")}bindPropertiesEvents(){if(!this.propertiesElement)return;const e=this.propertiesElement.querySelector("#applySplit");e&&e.addEventListener("click",()=>{this.selectedPlane?this.performSplit():this.editor.showStatus("Сначала выберите плоскость","warning")});const t=this.propertiesElement.querySelector("#cancelSplit");t&&t.addEventListener("click",()=>{this.editor.toolManager.setCurrentTool("select")})}performSplit(){if(!this.selectedPlane)return void this.editor.showStatus("Не выбрана плоскость для разрезания","error");const e=this.editor.selectedObjects[0],t={target:e.uuid,planeId:this.selectedPlane.uuid},i=new ParametricOperation("split",t,[e.uuid]),s=this.editor.parametricModel.addOperation(i);this.editor.clearSelection(),s.forEach(e=>{const t=this.editor.parametricModel.objectMap.get(e);t&&(this.editor.selectedObjects.push(t),this.editor.objectsManager.highlightObject(t))}),this.cleanup(),this.editor.toolManager.setCurrentTool("select")}onKeyDown(e){return"Escape"===e.key?(this.cleanup(),this.editor.toolManager.setCurrentTool("select"),!0):"Enter"===e.key&&"confirm"===this.splitMode&&(this.performSplit(),!0)}cleanupVisualization(){this.tempVisuals.forEach(e=>{e.parent&&e.parent.remove(e),e.geometry&&e.geometry.dispose(),e.material&&e.material.dispose()}),this.tempVisuals=[],this.splitPlane=null,this.planes.forEach(e=>{e.children.forEach(t=>{t.userData.isHighlight&&(e.remove(t),t.geometry&&t.geometry.dispose(),t.material&&t.material.dispose())})})}cleanup(){this.cleanupVisualization(),this.planes=[],this.splitMode="select",this.selectedPlane=null}canActivate(){return 1===this.editor.selectedObjects.length}}