feat: планиметрия Phase 2 — инструменты построения
- GeoEngine: система производных объектов (recompute, propagateDeps), каскадная цепочка зависимостей при перемещении точек - 6 новых инструментов в GeoSim: * midpoint — середина отрезка (производная точка) * perpbisect — серединный перпендикуляр (derived_line) * anglebisect — биссектриса угла ABC (derived_line) * parallel — параллельная прямая через точку (derived_line) * perpendicular — перпендикуляр через точку (derived_line) * intersect — точка пересечения двух прямых (производная точка) - Производные объекты: пунктирный стиль, светящийся ободок, автообновление при перемещении родительских точек - Двухфазный UI для parallel/perpendicular/intersect: _pendingLineRef + _drawLineRefHighlight (подсветка первой линии) - lab.html: 6 новых кнопок в секции "Построения", счётчик построений, onHintChange callback для контекстных подсказок Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+67
-15
@@ -3800,6 +3800,34 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="gp-section-title" style="margin-top:4px">Построения</div>
|
||||
<div class="geo-tool-grid">
|
||||
<button id="geo-btn-midpoint" class="geo-tool-btn" onclick="geoSetTool('midpoint',this)" title="Середина отрезка — 2 точки">
|
||||
<svg viewBox="0 0 24 24" fill="none"><line x1="3" y1="12" x2="21" y2="12" stroke-width="2"/><circle cx="12" cy="12" r="3.5" fill="currentColor"/></svg>
|
||||
Середина
|
||||
</button>
|
||||
<button id="geo-btn-perpbisect" class="geo-tool-btn" onclick="geoSetTool('perpbisect',this)" title="Серединный перпендикуляр — 2 точки">
|
||||
<svg viewBox="0 0 24 24" fill="none"><line x1="4" y1="18" x2="20" y2="6" stroke-width="2"/><line x1="12" y1="2" x2="12" y2="22" stroke-width="1.5" stroke-dasharray="3,2"/><circle cx="12" cy="12" r="2.5" fill="currentColor"/></svg>
|
||||
⊥ биссект.
|
||||
</button>
|
||||
<button id="geo-btn-anglebisect" class="geo-tool-btn" onclick="geoSetTool('anglebisect',this)" title="Биссектриса угла — 3 точки: A, вершина, B">
|
||||
<svg viewBox="0 0 24 24" fill="none"><polyline points="4,20 12,4 20,20" stroke-width="2"/><line x1="12" y1="4" x2="12" y2="20" stroke-width="1.5" stroke-dasharray="3,2"/></svg>
|
||||
∠ биссект.
|
||||
</button>
|
||||
<button id="geo-btn-parallel" class="geo-tool-btn" onclick="geoSetTool('parallel',this)" title="Параллельная прямая — клик на линию, затем на точку">
|
||||
<svg viewBox="0 0 24 24" fill="none"><line x1="3" y1="8" x2="21" y2="8" stroke-width="2"/><line x1="3" y1="16" x2="21" y2="16" stroke-width="2" opacity=".5" stroke-dasharray="4,3"/></svg>
|
||||
|| прямая
|
||||
</button>
|
||||
<button id="geo-btn-perpendicular" class="geo-tool-btn" onclick="geoSetTool('perpendicular',this)" title="Перпендикулярная прямая — клик на линию, затем на точку">
|
||||
<svg viewBox="0 0 24 24" fill="none"><line x1="3" y1="12" x2="21" y2="12" stroke-width="2"/><line x1="12" y1="4" x2="12" y2="20" stroke-width="2" opacity=".5" stroke-dasharray="4,3"/></svg>
|
||||
⊥ прямая
|
||||
</button>
|
||||
<button id="geo-btn-intersect" class="geo-tool-btn" onclick="geoSetTool('intersect',this)" title="Точка пересечения — клик на две прямые">
|
||||
<svg viewBox="0 0 24 24" fill="none"><line x1="4" y1="20" x2="20" y2="4" stroke-width="2"/><line x1="4" y1="4" x2="20" y2="20" stroke-width="2"/><circle cx="12" cy="12" r="3.5" fill="currentColor"/></svg>
|
||||
Пересеч.
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Display options -->
|
||||
<div class="gp-section-title" style="margin-top:6px">Параметры</div>
|
||||
<label class="geo-toggle-row" onclick="geoToggle('showGrid',this)">
|
||||
@@ -3845,6 +3873,7 @@
|
||||
<div class="geo-stat-row"><span>Отрезки</span><b id="geo-st-segs">0</b></div>
|
||||
<div class="geo-stat-row"><span>Окружности</span><b id="geo-st-circs">0</b></div>
|
||||
<div class="geo-stat-row"><span>Многоугольники</span><b id="geo-st-polys">0</b></div>
|
||||
<div class="geo-stat-row"><span>Построения</span><b id="geo-st-constr">0</b></div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
@@ -5269,15 +5298,21 @@
|
||||
/* ── geometry (planimetry) ── */
|
||||
|
||||
const _GEO_HINTS = {
|
||||
select: 'Клик — выбрать объект, перетащи точку для перемещения',
|
||||
point: 'Клик — поставить точку',
|
||||
segment: 'Кликни 2 точки для отрезка',
|
||||
line: 'Кликни 2 точки для прямой',
|
||||
ray: 'Кликни: начало, затем направление',
|
||||
circle: 'Клик — центр; второй клик — радиус',
|
||||
triangle: 'Кликни 3 точки для треугольника',
|
||||
quad: 'Кликни 4 точки для четырёхугольника',
|
||||
polygon: 'Кликай точки; двойной клик или Enter — завершить',
|
||||
select: 'Клик — выбрать объект, перетащи точку для перемещения',
|
||||
point: 'Клик — поставить точку',
|
||||
segment: 'Кликни 2 точки для отрезка',
|
||||
line: 'Кликни 2 точки для прямой',
|
||||
ray: 'Кликни: начало, затем направление',
|
||||
circle: 'Клик — центр; второй клик — радиус',
|
||||
triangle: 'Кликни 3 точки для треугольника',
|
||||
quad: 'Кликни 4 точки для четырёхугольника',
|
||||
polygon: 'Кликай точки; двойной клик или Enter — завершить',
|
||||
midpoint: 'Кликни 2 точки — получи середину отрезка',
|
||||
perpbisect: 'Кликни 2 точки — получи серединный перпендикуляр',
|
||||
anglebisect: 'Кликни: точку A, затем вершину угла, затем точку B',
|
||||
parallel: 'Сначала кликни на прямую/отрезок, затем на точку',
|
||||
perpendicular:'Сначала кликни на прямую/отрезок, затем на точку',
|
||||
intersect: 'Кликни на первую прямую, затем на вторую',
|
||||
};
|
||||
|
||||
function geoSetTool(name, btnEl) {
|
||||
@@ -5285,8 +5320,22 @@
|
||||
geomSim.setTool(name);
|
||||
document.querySelectorAll('.geo-tool-btn').forEach(b => b.classList.remove('active'));
|
||||
if (btnEl) btnEl.classList.add('active');
|
||||
_geoShowHint(name);
|
||||
}
|
||||
|
||||
function _geoShowHint(name, phase2) {
|
||||
const hint = document.getElementById('geo-hint');
|
||||
if (hint) hint.textContent = _GEO_HINTS[name] || '';
|
||||
if (!hint) return;
|
||||
if (phase2) {
|
||||
const phase2hints = {
|
||||
parallel: 'Теперь кликни на точку — через неё проведём прямую',
|
||||
perpendicular: 'Теперь кликни на точку — через неё проведём перпендикуляр',
|
||||
intersect: 'Теперь кликни на вторую прямую',
|
||||
};
|
||||
hint.textContent = phase2hints[name] || _GEO_HINTS[name] || '';
|
||||
} else {
|
||||
hint.textContent = _GEO_HINTS[name] || '';
|
||||
}
|
||||
}
|
||||
|
||||
function geoToggle(prop, rowEl) {
|
||||
@@ -5300,10 +5349,12 @@
|
||||
function _geoUpdateStats() {
|
||||
if (!geomSim) return;
|
||||
const s = geomSim.getStats();
|
||||
document.getElementById('geo-st-pts').textContent = s.pts;
|
||||
document.getElementById('geo-st-segs').textContent = s.segs;
|
||||
document.getElementById('geo-st-circs').textContent = s.circs;
|
||||
document.getElementById('geo-st-polys').textContent = s.polys;
|
||||
document.getElementById('geo-st-pts').textContent = s.pts;
|
||||
document.getElementById('geo-st-segs').textContent = s.segs;
|
||||
document.getElementById('geo-st-circs').textContent = s.circs;
|
||||
document.getElementById('geo-st-polys').textContent = s.polys;
|
||||
const cEl = document.getElementById('geo-st-constr');
|
||||
if (cEl) cEl.textContent = s.constructions || 0;
|
||||
}
|
||||
|
||||
function _openGeometry() {
|
||||
@@ -5322,7 +5373,8 @@
|
||||
const canvas = document.getElementById('geo-canvas');
|
||||
if (!geomSim) {
|
||||
geomSim = new GeoSim(canvas);
|
||||
geomSim.onUpdate = _geoUpdateStats;
|
||||
geomSim.onUpdate = _geoUpdateStats;
|
||||
geomSim.onHintChange = (tool, phase) => _geoShowHint(tool, phase > 1);
|
||||
|
||||
// keyboard shortcuts
|
||||
canvas.setAttribute('tabindex', '0');
|
||||
|
||||
Reference in New Issue
Block a user