diff --git a/frontend/js/labs/stereo.js b/frontend/js/labs/stereo.js index b495f0d..43fa899 100644 --- a/frontend/js/labs/stereo.js +++ b/frontend/js/labs/stereo.js @@ -115,7 +115,8 @@ class StereoSim { this._labelGroup = new THREE.Group(); this._sectionGroup = new THREE.Group(); this._sphereGroup = new THREE.Group(); - this._measureGroup = new THREE.Group(); + this._measureGroup = new THREE.Group(); + this._measurePickGroup = new THREE.Group(); this._gridGroup = new THREE.Group(); this._markGroup = new THREE.Group(); this._derivedGroup = new THREE.Group(); @@ -124,6 +125,7 @@ class StereoSim { this.scene.add(this._sectionGroup); this.scene.add(this._sphereGroup); this.scene.add(this._measureGroup); + this.scene.add(this._measurePickGroup); this.scene.add(this._markGroup); this.scene.add(this._derivedGroup); this.scene.add(this._labelGroup); @@ -310,10 +312,52 @@ class StereoSim { this._pointMode = false; this._angleMode = null; this._measurePicks = []; - if (!on) { this._clearGroup(this._measureGroup); this._measurements = []; } + this._clearGroup(this._measurePickGroup); + if (!on) { this._measurements = []; this._rebuildMeasureGroup(); } this.renderer.domElement.style.cursor = on ? 'crosshair' : 'grab'; } + removeLastMeasurement() { + if (!this._measurements.length) return; + this._measurements.pop(); + this._rebuildMeasureGroup(); + } + + clearMeasurements() { + this._measurements = []; + this._measurePicks = []; + this._rebuildMeasureGroup(); + this._clearGroup(this._measurePickGroup); + } + + _rebuildMeasureGroup() { + this._clearGroup(this._measureGroup); + for (const m of this._measurements) { + const g = new THREE.Group(); + // spheres at endpoints + for (const pos of [m.posA, m.posB]) { + const sGeo = new THREE.SphereGeometry(0.14, 12, 12); + const sMat = new THREE.MeshBasicMaterial({ color: 0xFFD166 }); + const s = new THREE.Mesh(sGeo, sMat); + s.position.copy(pos); + g.add(s); + } + // dashed line + const lineGeo = new THREE.BufferGeometry().setFromPoints([m.posA, m.posB]); + const lineMat = new THREE.LineDashedMaterial({ color: 0xFFD166, dashSize: 0.15, gapSize: 0.1, transparent: true, opacity: 0.9 }); + const line = new THREE.Line(lineGeo, lineMat); + line.computeLineDistances(); + g.add(line); + // label + const mid = new THREE.Vector3().addVectors(m.posA, m.posB).multiplyScalar(0.5); + const label = this._makeTextSprite(`${m.from}${m.to} = ${m.dist}`, '#FFD166', 40); + label.position.copy(mid).add(new THREE.Vector3(0, 0.3, 0)); + label.scale.set(1.4, 0.5, 1); + g.add(label); + this._measureGroup.add(g); + } + } + setAngleMode(mode) { // mode: 'edge' | 'linePlane' | 'dihedral' | null this._angleMode = mode; @@ -1718,34 +1762,24 @@ class StereoSim { this._measurePicks.push(bestPick); - // Highlight picked point + // Highlight first pick in temporary group const sGeo = new THREE.SphereGeometry(0.14, 12, 12); const sMat = new THREE.MeshBasicMaterial({ color: 0xFFD166 }); const s = new THREE.Mesh(sGeo, sMat); s.position.copy(bestPick.pos); - this._measureGroup.add(s); + this._measurePickGroup.add(s); if (this._measurePicks.length === 2) { const [a, b] = this._measurePicks; const dist = a.pos.distanceTo(b.pos); - const mid = new THREE.Vector3().addVectors(a.pos, b.pos).multiplyScalar(0.5); - - // Dashed line - const pts = [a.pos, b.pos]; - const lineGeo = new THREE.BufferGeometry().setFromPoints(pts); - const lineMat = new THREE.LineDashedMaterial({ color: 0xFFD166, dashSize: 0.15, gapSize: 0.1, transparent: true, opacity: 0.9 }); - const line = new THREE.Line(lineGeo, lineMat); - line.computeLineDistances(); - this._measureGroup.add(line); - - // Label - const label = this._makeTextSprite(`${a.label}${b.label} = ${dist.toFixed(2)}`, '#FFD166', 40); - label.position.copy(mid).add(new THREE.Vector3(0, 0.3, 0)); - label.scale.set(1.4, 0.5, 1); - this._measureGroup.add(label); - - this._measurements.push({ from: a.label, to: b.label, dist: Math.round(dist * 100) / 100 }); + this._measurements.push({ + from: a.label, to: b.label, + dist: Math.round(dist * 100) / 100, + posA: a.pos.clone(), posB: b.pos.clone(), + }); this._measurePicks = []; + this._clearGroup(this._measurePickGroup); + this._rebuildMeasureGroup(); } } diff --git a/frontend/lab.html b/frontend/lab.html index 1fb823b..fa2a280 100644 --- a/frontend/lab.html +++ b/frontend/lab.html @@ -3670,6 +3670,8 @@
+ +
Углы и расстояния
@@ -8223,6 +8225,14 @@ if (stereoSim) stereoSim.toggleMeasure(on); } + function stereoMeasureUndo() { + if (stereoSim) stereoSim.removeLastMeasurement(); + } + + function stereoMeasureClear() { + if (stereoSim) stereoSim.clearMeasurements(); + } + function stereoToggleHeight(btn) { const on = !btn.classList.contains('active'); btn.classList.toggle('active', on);