/
counterbugtech
/
KontrBugCAD
Обзор
Документация
Войти
/
counterbugtech
/
KontrBugCAD
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
js/core/parametric/executors/RevolveExecutor.js
1 строка
8 KB
counterbugtech
Обновление до 0.9.44
28 май 2026, 06:20
Верифицирован
28 май 2026, 06:20
cef7548
Код
Авторство
О чём код?
import*as THREE from"three";import{ParametricExecutor}from"./ParametricExecutor.js";import{GeometryUtils}from"utils/GeometryUtils.js";import{ManifoldWrapper}from"../../ManifoldWrapper.js";import{RegionSelector}from"./RegionSelector.js";export class RevolveExecutor extends ParametricExecutor{constructor(e){super(),this.editor=e}execute(e,t){const o=e.parameters;let r=null;if(o.selectedRegionsData&&o.selectedRegionsData.length>0){const e=this.getPlane(t,o);if(!e)throw new Error("Revolve: плоскость не найдена");const n=this.editor?.operationManager?.clipperScale||1e7;if(r=RegionSelector.reconstructFiguresWithHoles(e,o.selectedRegionsData,{clipperScale:n,editor:this.editor}),!r||0===r.length){if(!o.figures)throw new Error("Revolve: не удалось восстановить фигуры по elementIds");console.warn("Revolve: не удалось восстановить фигуры по elementIds, используем сохранённые копии"),r=o.figures}}else{if(!o.figures)throw new Error("Revolve: отсутствуют параметры для построения полигонов");r=o.figures}const n=[];for(const e of r){let t;if(Array.isArray(e.outer)&&e.outer.length>0&&"number"==typeof e.outer[0]){t=[];for(let o=0;o<e.outer.length;o+=2)t.push(new THREE.Vector2(e.outer[o],e.outer[o+1]))}else{if(!(e.outer&&e.outer.points&&Array.isArray(e.outer.points))){console.warn("RevolveExecutor: пропуск фигуры с некорректным внешним контуром",e);continue}t=e.outer.points.map(e=>{if(e instanceof THREE.Vector2)return e.clone();if(e&&"number"==typeof e.x)return new THREE.Vector2(e.x,e.y);throw new Error("Invalid point format in outer contour")})}if(!t||t.length<3)continue;const o=[];if(e.holes&&Array.isArray(e.holes))for(const t of e.holes){let e;if(Array.isArray(t)&&t.length>0&&"number"==typeof t[0]){e=[];for(let o=0;o<t.length;o+=2)e.push(new THREE.Vector2(t[o],t[o+1]))}else{if(!t.points||!Array.isArray(t.points))continue;e=t.points.map(e=>e instanceof THREE.Vector2?e.clone():e&&"number"==typeof e.x?new THREE.Vector2(e.x,e.y):null).filter(e=>null!==e)}e&&e.length>=3&&o.push({points:e,center:GeometryUtils.getContourCenter2D(e),area:Math.abs(GeometryUtils.calculatePolygonArea2D(e)),isClockwise:GeometryUtils.isClockwise2D(e)})}n.push({outer:{points:t,center:GeometryUtils.getContourCenter2D(t),area:Math.abs(GeometryUtils.calculatePolygonArea2D(t)),isClockwise:GeometryUtils.isClockwise2D(t)},holes:o})}if(r=n,0===r.length)throw new Error("Revolve: нет корректных фигур после нормализации");for(const e of r){let t=GeometryUtils.calculatePolygonArea2D(e.outer.points);if(t<0&&(e.outer.points.reverse(),e.outer.area=Math.abs(t),e.outer.center=GeometryUtils.getContourCenter2D(e.outer.points),e.outer.isClockwise=!1),e.holes)for(const t of e.holes){let e=GeometryUtils.calculatePolygonArea2D(t.points);e>0&&(t.points.reverse(),t.area=Math.abs(e),t.center=GeometryUtils.getContourCenter2D(t.points),t.isClockwise=!0)}}const i=ManifoldWrapper.figuresToPolygons(r.map(e=>({outer:e.outer.points.flatMap(e=>[e.x,e.y]),holes:e.holes.map(e=>e.points.flatMap(e=>[e.x,e.y]))})));if(0===i.length)throw new Error("Revolve: нет полигонов");const{angle:s,axis:a="y",direction:l,operationType:c,color:u=16750592,circularSegments:p=0,lineAxisPoints:f=null}=o,E=e.inputs||[],h=t.editor.manifoldWrapper,d=this.getPlane(t,o);if(!d)throw new Error("Revolve: плоскость не найдена");let y,m;if(h.ensureReady(),f){const e=new THREE.Vector3(f.start[0],f.start[1],f.start[2]),t=new THREE.Vector3(f.end[0],f.end[1],f.end[2]);y=e.clone(),m=(new THREE.Vector3).subVectors(t,e).normalize()}else switch(y=new THREE.Vector3(0,0,0),a){case"x":m=new THREE.Vector3(1,0,0);break;case"y":default:m=new THREE.Vector3(0,1,0);break;case"z":m=new THREE.Vector3(0,0,1)}let w=0;if(i.length&&i[0].length){const e=i[0],t=new THREE.Vector3(e[0].x,e[0].y,0),o=d.localToWorld(t);w=this.computePhiStart(y,m,o)}const g=[];for(const e of i){const t=e.map(e=>{const t=new THREE.Vector3(e.x,e.y,0);return d.localToWorld(t)}),o=[];for(const e of t){const t=(new THREE.Vector3).subVectors(e,y),r=t.dot(m),n=t.clone().sub(m.clone().multiplyScalar(r)).length();o.push({x:n,y:r})}if(o.length){const e=o[0],t=o[o.length-1];(Math.abs(e.x-t.x)>1e-6||Math.abs(e.y-t.y)>1e-6)&&o.push({...e});let r=0;for(let e=0;e<o.length-1;e++)r+=o[e].x*o[e+1].y-o[e+1].x*o[e].y;r/=2,r<0&&o.reverse()}g.push(o)}if(0===g.length)throw new Error("Revolve: не удалось построить профили");const R=h.createCrossSection(g,"EvenOdd"),v="negative"===l?-s:s;let T=R.revolve(p,v);R.delete();const b=h.manifoldToThreeGeometry(T);T.delete();const x=(new THREE.Quaternion).setFromUnitVectors(new THREE.Vector3(0,0,1),m),H=(new THREE.Quaternion).setFromAxisAngle(m,w);b.applyQuaternion(x),b.applyQuaternion(H),b.translate(y.x,y.y,y.z);const V=new THREE.Mesh(b,new THREE.MeshStandardMaterial({color:u,flatShading:!0}));if(V.position.set(0,0,0),V.quaternion.identity(),V.castShadow=!0,V.receiveShadow=!0,V.userData={type:"revolve",sourceFigures:r,angle:s,axis:a,direction:l,operationType:c,planeId:o.planeId},"new"===c){const t=GeometryUtils.centerGeometry(b);return V.position.copy(t),e.outputs.length>0?V.uuid=e.outputs[0]:e.outputs=[V.uuid],V}if(!t.editor.booleanOps)throw new Error("Revolve: булевы операции не доступны");const M=E.map(e=>t.objectMap.get(e)).filter(Boolean);if(0===M.length)throw new Error("Revolve: не найдены целевые объекты");if("cut"===c){const o=M.map(e=>t.editor.booleanOps.subtract(e,V));if(!o.every(e=>e))throw new Error("Revolve: вычитание не дало результата");M.forEach(e=>{t.objectMap.delete(e.uuid),t.sceneGroup.remove(e)});const r=M.map(e=>e.uuid),n=this._assignOutputUUIDs(e,o,r);return n.forEach(e=>{e.userData={...e.userData,type:"revolve_boolean",operationType:c}}),1===n.length?n[0]:n}if("join"===c){const o=[V,...M],r=t.editor.booleanOps.unionMultiple(o);if(!r)throw new Error("Revolve: не удалось выполнить объединение");let n=[];e.outputs.length>0?n=[e.outputs[0]]:M.length>0&&(n=[M[0].uuid]);const[i]=this._assignOutputUUIDs(e,[r],n);return i.userData={...i.userData,type:"revolve_boolean",operationType:c},M.forEach(e=>{t.objectMap.delete(e.uuid),t.sceneGroup.remove(e)}),i}throw new Error(`Revolve: неизвестный тип операции ${c}`)}getPlane(e,t){const{planeId:o,isFace:r,originalObjectUuid:n,originalFaceIndex:i,planePosition:s,planeQuaternion:a}=t;if(s&&a){const e=new THREE.Object3D;return e.position.set(s[0],s[1],s[2]),e.quaternion.set(a[0],a[1],a[2],a[3]),e}if(o){let t=e.objectMap.get(o);if(t)return t;if(t=this.findPlaneById(o,e.editor),t)return t}if(r&&n&&void 0!==i){let t=e.objectMap.get(n);if(t||e.editor.scene.traverse(e=>{e.uuid===n&&(t=e)}),t&&t.geometry)return this.createPlaneFromFace(t,i)}throw new Error("Revolve: не найдена плоскость")}createPlaneFromFace(e,t){const o=e.geometry,r=o.attributes.position,n=o.index;if(!n)throw new Error("Geometry has no index");const i=n.array[3*t],s=n.array[3*t+1],a=n.array[3*t+2],l=(new THREE.Vector3).fromBufferAttribute(r,i),c=(new THREE.Vector3).fromBufferAttribute(r,s),u=(new THREE.Vector3).fromBufferAttribute(r,a);l.applyMatrix4(e.matrixWorld),c.applyMatrix4(e.matrixWorld),u.applyMatrix4(e.matrixWorld);const p=(new THREE.Vector3).add(l).add(c).add(u).divideScalar(3),f=GeometryUtils.getTriangleNormal(l,c,u),E=new THREE.Object3D;E.position.copy(p);const h=(new THREE.Quaternion).setFromUnitVectors(new THREE.Vector3(0,0,1),f);return E.quaternion.copy(h),E}findPlaneById(e,t){let o=null;return t.scene.traverse(t=>{t.uuid===e&&t.userData?.type?.includes("plane")&&(o=t)}),o}computePhiStart(e,t,o){const r=(new THREE.Vector3).subVectors(o,e),n=r.dot(t),i=r.clone().sub(t.clone().multiplyScalar(n));if(i.length()<1e-6)return 0;let s=new THREE.Vector3(1,0,0);Math.abs(t.dot(s))>.9999&&(s=new THREE.Vector3(0,1,0));const a=s.clone().sub(t.clone().multiplyScalar(s.dot(t))).normalize();return Math.atan2(i.dot(t.clone().cross(a)),i.dot(a))}}