Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ viewer3d-core/builders/railing — classic scripts с общей глобаль
```
styles-desktop.css?v=50 styles-mobile.css?v=3 state.js?v=77
canvas.js?v=65 shared/house-builder.js?v=89 ResourceManager.js?v=14
viewer3d-core.js?v=161 viewer3d-builders.js?v=66 viewer3d-railing.js?v=18
viewer3d-core.js?v=161 viewer3d-builders.js?v=67 viewer3d-railing.js?v=18
viewer3d-entourage.js?v=14 editor3d.js?v=20 nav-desktop.js?v=140
nav-mobile.js?v=5 project-io.js?v=7 backend_API/Calculator.js?v=3
```
Expand Down Expand Up @@ -949,6 +949,19 @@ JSON-контракта `POST /api/calculate` и схемы БД лежит в g
- В «Порядок подключения скриптов» добавлен актуальный срез `?v=N`, чтобы версии не искать
по журналу; в решения — две строки про снап ограждения.

Ветка **Mobile**: калитка — столб по оси, запасной разбор полотна.

- **Столб калитки встаёт центром на начало проёма**, как столбы секций (секция
ставится по оси стартового столба). Раньше в ноль уходил край столба, и он
отъезжал от соседней секции на полсечения (~5.5 см). Ширина проёма прежняя.
- **Запасной разбор полотна калитки — по детальности примитивов.** Если в файле
у материалов нет говорящих имён (серверная модель может отличаться от нашей),
полотном считается самый «тяжёлый» примитив створки — минимум вдвое больше
вершин, чем у остальных (003: 680 против 48…216, 005: 17 050 против 404). В
консоли видно, каким способом опознано полотно: «по материалам файла», «по
детальности примитивов створки» или «по именам и площади».
- Cache-bust: `viewer3d-builders.js?v=67`.

Ветка **Mobile**: правки 2026-10-04 — калитка, кольцо дорожки, смета, фильтры.

- **Калитка красится по материалам файла.** Створка в `mod_wicket_003/005` — один
Expand Down
3 changes: 3 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,9 @@
как у секций того же забора. Смещения в наших моделях (`mod_wicket_003/005`)
не воспроизвелось: столб калитки стоит на линии забора. Файл с сервера
сверить не удалось — сервер из среды разработки недоступен.
- [x] Калитка (повтор): столб отъезжал от секции на полсечения — теперь стоит
центром на начале проёма, как столбы секций. Полотно без говорящих имён
материалов опознаётся по детальности примитивов створки.
- [x] Дорожки: замкнутое кольцо сшивается на шве (миттер, как на внутренних
углах); сшитые точки (шов кольца, стык линий) перетаскиваются вместе.
- [x] Смета: остался только расчёт по спецификации; описание проекта и
Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@
<!-- 3D viewer: core (сцена/материалы/оркестратор) → builders (конструкции) →
railing (ограждение/навесы). Общая глобальная область, порядок важен. -->
<script src="viewer3d-core.js?v=161"></script>
<script src="viewer3d-builders.js?v=66"></script>
<script src="viewer3d-builders.js?v=67"></script>
<script src="viewer3d-railing.js?v=18"></script>
<script src="viewer3d-entourage.js?v=14"></script>
<!-- Редактирование прямо в 3D (версия 3D-UI): выбор объектов, сетка, подписи.
Expand Down
31 changes: 30 additions & 1 deletion viewer3d-builders.js
Original file line number Diff line number Diff line change
Expand Up @@ -2590,7 +2590,13 @@ function _wicketNormalizeProto(scene) {
const box = _wicketClosedBox(scene, post);
if (!box || !isFinite(box.min.x) || !isFinite(box.max.x)) return proto;
const pb = post ? new THREE.Box3().setFromObject(post) : box;
scene.position.x -= box.min.x; // начало проёма — в нуле
// Столб калитки встаёт ЦЕНТРОМ на начало проёма — как столбы секций забора
// (_fenceProtoPosts: секция ставится по оси стартового столба). Раньше в ноль
// уходил край столба, и он отъезжал от соседней секции на полсечения
// (правка 2026-10-04). Ширина проёма от этого не меняется: от оси столба до
// дальнего края закрытой створки плюс полсечения — ровно до грани столба
// следующей секции, который стоит центром на конце проёма.
scene.position.x -= post ? (pb.min.x + pb.max.x) / 2 : box.min.x;
scene.position.y -= box.min.y; // низ — на земле
scene.position.z -= (pb.min.z + pb.max.z) / 2; // столб — на линии забора
const w = box.max.x - box.min.x, h = box.max.y - box.min.y;
Expand Down Expand Up @@ -3444,6 +3450,29 @@ function _wicketPanelFlags(proto) {
: (o.material && o.material.name)) || '';
let flags = meshes.map(o => WICKET_PANEL_MAT_RE.test(matName(o)));
let how = 'по материалам файла';
// Материалы без говорящих имён (файл с сервера может отличаться от нашего):
// у створки из нескольких примитивов полотно — самый детальный из них. Доски
// и плетёнка намного «тяжелее» рамы и ручек: в 003 — 680 вершин против
// 48…216, в 005 — 17 050 против 404. Разбор по площади, как у секции, здесь
// не годится: габарит рамы почти равен габариту полотна.
if (!flags.some(Boolean)) {
const post = _wicketPost(proto);
let leaf = null;
proto.traverse(o => {
if (leaf || o.isMesh || !o.children) return;
const kids = o.children.filter(c => c.isMesh && c !== post);
if (kids.length >= 2) leaf = kids;
});
if (leaf) {
const cnt = o => (o.geometry && o.geometry.attributes.position) ? o.geometry.attributes.position.count : 0;
const best = leaf.reduce((a, b) => (cnt(b) > cnt(a) ? b : a));
const rest = Math.max(...leaf.filter(o => o !== best).map(cnt));
if (cnt(best) >= rest * 2) {
flags = meshes.map(o => o === best);
how = 'по детальности примитивов створки';
}
}
}
if (!flags.some(Boolean)) {
const panels = (typeof _fenceProtoPanels === 'function') ? _fenceProtoPanels(proto) : new Set();
flags = panels.size ? meshes.map(o => panels.has(o)) : null;
Expand Down
Loading