Compare commits
5 Commits
477d47e9e6
...
9547a20875
| Author | SHA1 | Date | |
|---|---|---|---|
| 9547a20875 | |||
| 24403718bf | |||
| 9382b063aa | |||
| abd1af2653 | |||
| 53ac45bccd |
+997
-4
File diff suppressed because it is too large
Load Diff
@@ -3672,6 +3672,67 @@
|
||||
</div>
|
||||
<div id="points-info" style="font-size:0.65rem;color:rgba(255,255,255,0.4);margin-top:2px"></div>
|
||||
|
||||
<!-- ── Построения (прямые / плоскости) ── -->
|
||||
<div class="gp-section-title" style="margin-top:8px;margin-bottom:6px">Построения</div>
|
||||
<div class="st-tool-grid">
|
||||
<button class="st-tool-btn" id="stereo-line-btn" onclick="stereoLineMode(this)" title="Прямая через 2 точки — кликните две вершины или точки">
|
||||
<svg viewBox="0 0 24 24"><line x1="3" y1="21" x2="21" y2="3"/><circle cx="6" cy="18" r="2" fill="currentColor"/><circle cx="18" cy="6" r="2" fill="currentColor"/></svg>Прямая
|
||||
</button>
|
||||
<button class="st-tool-btn" id="stereo-plane-btn" onclick="stereoPlaneMode(this)" title="Плоскость через 3 точки — кликните три вершины или точки">
|
||||
<svg viewBox="0 0 24 24"><polygon points="3,16 13,20 21,8 11,4" fill="none"/><circle cx="3" cy="16" r="1.8" fill="currentColor"/><circle cx="21" cy="8" r="1.8" fill="currentColor"/><circle cx="11" cy="4" r="1.8" fill="currentColor"/></svg>Плоскость
|
||||
</button>
|
||||
<button class="st-tool-btn st-tool-btn-wide" id="stereo-intersect-btn" onclick="stereoIntersectMode(this)" title="Пересечение: выберите 2 объекта в списке (прямая∩плоскость → точка, плоскость∩плоскость → прямая)">
|
||||
<svg viewBox="0 0 24 24"><line x1="3" y1="7" x2="21" y2="17"/><line x1="3" y1="17" x2="21" y2="7"/><circle cx="12" cy="12" r="2.4" fill="currentColor"/></svg>Пересечение
|
||||
</button>
|
||||
</div>
|
||||
<div class="st-tool-grid" style="margin-top:3px">
|
||||
<button class="st-tool-btn" id="stereo-rel-lpar-btn" onclick="stereoRelMode('lpar',this)" title="Прямая, параллельная выбранной прямой, через точку">
|
||||
<svg viewBox="0 0 24 24"><line x1="4" y1="8" x2="20" y2="6"/><line x1="4" y1="18" x2="20" y2="16"/></svg>∥ прямая
|
||||
</button>
|
||||
<button class="st-tool-btn" id="stereo-rel-lperp-btn" onclick="stereoRelMode('lperp',this)" title="Прямая, перпендикулярная выбранной плоскости, через точку">
|
||||
<svg viewBox="0 0 24 24"><ellipse cx="12" cy="17" rx="9" ry="3" fill="none"/><line x1="12" y1="3" x2="12" y2="17"/><path d="M12 14 L15 14 L15 17" fill="none"/></svg>⟂ прямая
|
||||
</button>
|
||||
<button class="st-tool-btn" id="stereo-rel-ppar-btn" onclick="stereoRelMode('ppar',this)" title="Плоскость, параллельная выбранной плоскости, через точку">
|
||||
<svg viewBox="0 0 24 24"><polygon points="3,11 12,14 21,6 12,3" fill="none"/><polygon points="3,18 12,21 21,13 12,10" fill="none"/></svg>∥ плоск.
|
||||
</button>
|
||||
<button class="st-tool-btn" id="stereo-rel-pperp-btn" onclick="stereoRelMode('pperp',this)" title="Плоскость, перпендикулярная выбранной прямой, через точку">
|
||||
<svg viewBox="0 0 24 24"><polygon points="3,14 12,17 21,9 12,6" fill="none"/><line x1="12" y1="2" x2="12" y2="21"/></svg>⟂ плоск.
|
||||
</button>
|
||||
</div>
|
||||
<div class="st-tool-grid" style="margin-top:3px">
|
||||
<button class="st-tool-btn" id="stereo-divide-btn" onclick="stereoDivideMode(this)" title="Точка, делящая отрезок AB в отношении m:n — задайте m,n и кликните 2 точки">
|
||||
<svg viewBox="0 0 24 24"><line x1="3" y1="12" x2="21" y2="12"/><circle cx="3" cy="12" r="1.8" fill="currentColor"/><circle cx="21" cy="12" r="1.8" fill="currentColor"/><circle cx="11" cy="12" r="2.6" fill="currentColor"/></svg>Деление
|
||||
</button>
|
||||
<button class="st-tool-btn" id="stereo-dragpt-btn" onclick="stereoDragPointMode(this)" title="Перетаскивать построенные точки мышью (в плоскости экрана)">
|
||||
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3" fill="currentColor"/><path d="M12 3v3M12 18v3M3 12h3M18 12h3"/></svg>Тащить
|
||||
</button>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:5px;margin-top:4px;font-size:.72rem;color:rgba(255,255,255,.55)">
|
||||
<span>m</span>
|
||||
<input id="st-div-m" type="number" min="0" step="1" value="1" oninput="stereoDivideRatio()" style="width:40px;background:#1a1030;color:#f0e8ff;border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:3px 4px;font-size:.72rem">
|
||||
<span>:</span>
|
||||
<input id="st-div-n" type="number" min="0" step="1" value="1" oninput="stereoDivideRatio()" style="width:40px;background:#1a1030;color:#f0e8ff;border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:3px 4px;font-size:.72rem">
|
||||
<span>n</span><span style="opacity:.55">(AM:MB)</span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:4px;margin-top:4px">
|
||||
<input id="st-pt-x" type="number" step="0.5" value="0" title="x" style="width:42px;background:#1a1030;color:#f0e8ff;border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:3px 4px;font-size:.72rem">
|
||||
<input id="st-pt-y" type="number" step="0.5" value="0" title="y" style="width:42px;background:#1a1030;color:#f0e8ff;border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:3px 4px;font-size:.72rem">
|
||||
<input id="st-pt-z" type="number" step="0.5" value="0" title="z" style="width:42px;background:#1a1030;color:#f0e8ff;border:1px solid rgba(255,255,255,.12);border-radius:6px;padding:3px 4px;font-size:.72rem">
|
||||
<button class="st-action-btn" onclick="stereoAddCoordPoint()" style="flex:1" title="Поставить точку по координатам">Точка (x,y,z)</button>
|
||||
</div>
|
||||
<div class="st-action-grid" style="margin-top:3px">
|
||||
<button class="st-action-btn" onclick="stereoConstructHistUndo()" title="Отменить (Ctrl+Z)">Отменить</button>
|
||||
<button class="st-action-btn" onclick="stereoConstructHistRedo()" title="Вернуть (Ctrl+Shift+Z)">Вернуть</button>
|
||||
</div>
|
||||
<div class="st-action-grid" style="margin-top:3px">
|
||||
<button class="st-action-btn" onclick="stereoConstructUndo()">Удалить последнее</button>
|
||||
<button class="st-action-btn" onclick="stereoConstructClear()">Очистить</button>
|
||||
</div>
|
||||
<div id="construct-hint" style="font-size:0.63rem;color:rgba(255,255,255,0.38);margin-top:3px;line-height:1.4"></div>
|
||||
<div style="font-size:0.6rem;color:rgba(255,255,255,0.3);margin-top:2px;line-height:1.35">Клик по плоскости в списке — показать её сечением (заливка, площадь, периметр, натуральная величина).</div>
|
||||
<div id="construct-list" style="font-size:0.7rem;margin-top:4px;line-height:1.6"></div>
|
||||
<div id="construct-trueshape" style="display:none;margin-top:6px;background:rgba(6,214,224,0.05);border:1px solid rgba(6,214,224,0.18);border-radius:8px;padding:6px"></div>
|
||||
|
||||
<!-- ── Метки рёбер ── -->
|
||||
<div class="gp-section-title" style="margin-top:8px;margin-bottom:6px">Метки рёбер</div>
|
||||
<div class="st-tool-grid">
|
||||
|
||||
@@ -64,4 +64,60 @@
|
||||
|
||||
---
|
||||
|
||||
История: создан 2026-05-30. Фаза 6 добавлена 2026-05-30.
|
||||
## Раунд «Конструктор» (2026-06-17) — упор на ученика-самоучку (песочница)
|
||||
|
||||
Цель: превратить отличный **визуализатор** в полноценный **конструктор** для самостоятельных
|
||||
построений. Приоритеты, выбранные пользователем: **Фаза A (конструкторное ядро)** и
|
||||
**Фаза C (сечения+)**. A — фундамент C (сечение через прямую+точку, параллельно прямой/плоскости
|
||||
опираются на объекты-прямые/плоскости).
|
||||
|
||||
### Фаза A — Конструкторное ядро
|
||||
|
||||
Прямые и плоскости как объекты первого класса + пересечения + параллели/перпендикуляры +
|
||||
общий undo/redo + дерево именованных объектов.
|
||||
|
||||
- [x] A1 — **Объектная модель + базовые построения.** `_lines[]` (имена a,b,c…), `_planes[]`
|
||||
(имена α,β,γ…), группа `_constructGroup`, сериализуемое хранение `{x,y,z}`. Инструменты
|
||||
«Прямая по 2 точкам» и «Плоскость по 3 точкам» (пикинг вершин/точек). Плоскость рисует
|
||||
полупрозрачный квад + пунктирную рамку + **сечение тела этой плоскостью** (через `_sliceByPlane`,
|
||||
делает плоскость осмысленной сразу). Панель «Построения», список объектов с уравнением плоскости.
|
||||
- [x] A2 — **Пересечения** (прямая∩плоскость → точка `_cpoints` имена M,N,K…; плоскость∩плоскость
|
||||
→ прямая; прямая∩прямая → точка или «скрещиваются») — выбор 2 объектов в дереве (`setIntersectMode`/
|
||||
`pickConstructObject`). **Интерактивное дерево**: видимость (глаз)/удаление (×) по объекту, выбор
|
||||
для пересечения. Точки-пересечения пикабельны → по ним строятся новые прямые/плоскости.
|
||||
- [x] A3 — **Параллели/перпендикуляры** через точку (`setRelMode`/`_onRelClick`): `lpar` прямая ∥
|
||||
прямой; `lperp` прямая ⟂ плоскости; `ppar` плоскость ∥ плоскости; `pperp` плоскость ⟂ прямой
|
||||
(= «плоскость по точке и нормали» через `_createPlaneFromPointNormal` — мост к Фазе C). Поток:
|
||||
кнопка op → выбор опоры в дереве → клик точки. **Общий undo/redo** конструкторного слоя (JSON-
|
||||
снапшот `_undoStack`/`_redoStack`, кап 60; хуки в create/remove/clear; Ctrl+Z / Ctrl+Shift+Z /
|
||||
Ctrl+Y + кнопки «Отменить»/«Вернуть»). Видимость — не шаг истории (намеренно).
|
||||
|
||||
### Фаза B — Умные точки
|
||||
|
||||
- [x] B1 — **Деление отрезка m:n** (`setDivideMode`/`setDivideRatio`): задаёшь m,n → кликаешь 2 точки A,B
|
||||
→ точка делит AB как AM:MB = m:n (`t = m/(m+n)`), создаётся как `_cpoints` (M,N,K…).
|
||||
- [x] B2 — **Точка по координатам** (`addPointAt(x,y,z)`): поля x/y/z + кнопка → точка-построение.
|
||||
- [x] B3 — **Перетаскивание точек** (`setDragPointMode`): pointerdown по точке-построению (приоритет над
|
||||
орбитой) → drag в плоскости, обращённой к камере (нормаль = направление камеры, фикс. на старте);
|
||||
`_beginCPointDrag`/`_dragCPointWithRay`/`_rayPlaneHit`/`_endCPointDrag`; снапшот истории на старте
|
||||
(undo откатывает весь drag). Точки-пересечения/деления непараметрические (downstream-объекты
|
||||
копируют позицию при создании и за перетаскиванием НЕ следуют — параметрический граф = бэклог).
|
||||
|
||||
### Фаза C — Сечения+
|
||||
|
||||
- [x] C1 — Сечение **плоскостью-объектом** (из Фазы A): клик по плоскости в дереве (нормальный режим)
|
||||
→ `setSectionPlane` показывает её заливкой + подписи вершин K,L,M… + площадь/периметр в readout
|
||||
(`_activeSectionPolygon`, `getReadout`). Удаление/очистка/смена фигуры сбрасывают сечение.
|
||||
- [x] C2 — **Покрыто Фазой A** (отдельный код не нужен): сечение через прямую+точку = плоскость по
|
||||
3 точкам (2 с прямой + 1); сечение ∥ плоскости через точку = rel-операция `ppar` → затем клик
|
||||
как сечение. Дополнительный UI признан избыточным.
|
||||
- [x] C3 — **«Натуральная величина» сечения** (`getTrueShape`): разворот многоугольника в его
|
||||
плоскость (ортонормированный базис от нормали) с сохранением истинных длин → 2D-мини-панель
|
||||
(SVG, штриховка `<pattern>`, подписи вершин, длины сторон, S/P). Проверено сохранение длин и
|
||||
площади для прямого и наклонного сечений.
|
||||
- [ ] C4 — Честный конструктивный алгоритм следов с анимацией (из бэклога Ф6) — **отложено**
|
||||
(крупная отдельная фича; текущий гибрид Ф6 + сечения-объекты Фазы A/C закрывают практику).
|
||||
|
||||
---
|
||||
|
||||
История: создан 2026-05-30. Фаза 6 добавлена 2026-05-30. Раунд «Конструктор» (Фазы A,C) — 2026-06-17.
|
||||
|
||||
Reference in New Issue
Block a user