mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-18 14:59:19 +02:00
Checkpoint of the editor branch so work isn't carried loose across a branch switch. Builds. - maj0sted turnout geometry moved into the domain: domain::lay_turnout lays the diverging path (pre-blade lead, blade at beta, arc of alfa-beta, frog rail) and solve_junction is a thin adapter onto it. - the maj0sted web/ tree renamed to editor/ (namespace and paths) - there is no WASM here, it's plain native C++ the editor calls. - click places the switch at its start (początek rozjazdu); the branch's leaving straight is locked tangent to the frog; switches can be slid along the through track and flipped L/P. - Polish turnout templates carry catalogue length (Id-1-consistent lengths), plus blade-angle and pre-blade fields (left 0 until the catalogue figures are in). - TURNOUT_SPEC.md: the Bezier-based runtime/model contract. - also folds in other pending branch changes (scenery, tests) to leave a clean tree.
177 lines
7.8 KiB
Markdown
177 lines
7.8 KiB
Markdown
# Rozjazd — kontrakt geometrii dla runtime i modeli
|
||
|
||
Jak edytor przekazuje rozjazd, a runtime ustawia z tego elementy. Podstawą są
|
||
**krzywe Béziera** (kubiczne). To jest warstwa graniczna: wewnątrz edytor liczy
|
||
łuki i klotoidy (kolejowo dokładne), a na wyjściu przybliża je Bézierami z
|
||
zadaną tolerancją. Runtime nie zna „typów" rozjazdów — dostaje instancję i sadzi
|
||
po niej elementy.
|
||
|
||
## Zasada
|
||
|
||
Runtime **nie bazuje na szablonie** (Rz 1:9 R190 itd.). Szablony to tylko preset
|
||
w edytorze, który wypełnia liczby. Każdy rozjazd to instancja o dowolnej
|
||
geometrii (własny skos, promień, przesunięty początek, łuk koszowy w środku).
|
||
Runtime dostaje **rozwiązaną geometrię** i z niej wszystko wyprowadza.
|
||
|
||
## Układ i jednostki
|
||
|
||
- metry; krzywe w XY (płaszczyzna planu),
|
||
- rozstaw toru 1435 mm → półrozstaw **0,7175 m**,
|
||
- pozy podawane jako pozycja + jednostkowy kierunek `(hx, hy)` (styczna, „w
|
||
kierunku jazdy"). Normalna lewa = `(-hy, hx)`,
|
||
- każdy element modelu ma origin w punkcie wstawienia, **+X = kierunek jazdy**,
|
||
**+Y = w lewo**, **+Z = w górę**. Runtime wstawia go podając tylko pozę —
|
||
żadnego per-model obrotu.
|
||
|
||
## Instancja rozjazdu
|
||
|
||
```
|
||
Turnout {
|
||
side : Left | Right // strona odgałęzienia (mirror)
|
||
crossing_mark : n // skos 1:n -> dobór odlewu krzyżownicy
|
||
rail_profile : "49E1" | "60E1" // -> dobór iglic / profilu szyny
|
||
gauge : 1.435
|
||
|
||
PR : Pose // początek rozjazdu: styczny do toru zasadniczego
|
||
KR : Pose // koniec (krzyżownica): kierunek = tor zasadniczy obrócony o alfa
|
||
crossing_angle : alfa = atan(1/n) // radiany
|
||
|
||
blade_angle : beta // kąt nagięcia iglicy (kąt zwrotnicowy)
|
||
pre_blade_length : d // odcinek przediglicowy: prosty, PR -> ostrze iglicy
|
||
|
||
through_axis : [Bezier...] // tor zasadniczy przez rozjazd (zwykle prosty)
|
||
diverging_axis: [Bezier...] // przediglicowy -> iglica(prosta @ beta) -> luk -> prosta -> KR
|
||
}
|
||
|
||
Pose { x, y, hx, hy } // pozycja + jednostkowa styczna
|
||
Bezier { p0, p1, p2, p3 } // kubiczna, punkty kontrolne w XY (metry)
|
||
```
|
||
|
||
Tor zasadniczy nie jest dzielony — `through_axis` jest podany dla kompletności
|
||
(runtime często ma już tor główny). Rozjazd wnosi przede wszystkim
|
||
`diverging_axis` + osprzęt + podrozjazdnice spinające oba tory.
|
||
|
||
## Początek odnogi: odcinek przediglicowy + iglica
|
||
|
||
Odnoga **nie zaczyna się łukiem**. To zwrotnica sieczna z iglicą prostą:
|
||
|
||
1. **odcinek przediglicowy** — prosta w kierunku toru zasadniczego (kąt 0), od
|
||
`PR` do ostrza iglicy. Tu rozjazd jest jeszcze równoległy do toru głównego;
|
||
tu siedzi napęd.
|
||
2. **iglica** — **prosta odcięta pod kątem `beta`** („prosta dogięta do
|
||
prostego"). Odnoga odchodzi od toru zasadniczego dopiero tutaj, o kąt nagięcia
|
||
iglicy `beta`.
|
||
3. **łuk** — promień `R`, skręca od `beta` do kąta krzyżownicy `alfa`, czyli o
|
||
**`alfa − beta`** (nie o całe `alfa`).
|
||
4. **prosta krzyżownicowa** — pod kątem `alfa`, do `KR`.
|
||
|
||
Stąd `diverging_axis` = `[przediglicowy] [iglica @ beta] [łuk] [prosta] `. Iglice
|
||
łukowe (styczne, `beta ≈ 0`, ostrze wtopione w łuk) to inny wariant — na razie
|
||
modelujemy iglicę prostą.
|
||
|
||
**Model:** zespół iglic kotwiony w ostrzu (`PR` + `pre_blade_length` wzdłuż toru
|
||
głównego), zorientowany wzdłuż toru zasadniczego, nadaje `beta`; długość iglicy
|
||
jest po stronie modelu.
|
||
|
||
## Bézier — definicja i próbkowanie
|
||
|
||
Kubiczna:
|
||
|
||
```
|
||
B(t) = (1-t)^3 p0 + 3(1-t)^2 t p1 + 3(1-t) t^2 p2 + t^3 p3, t in [0,1]
|
||
B'(t) = 3(1-t)^2 (p1-p0) + 6(1-t)t (p2-p1) + 3t^2 (p3-p2)
|
||
```
|
||
|
||
- **pozycja** = `B(t)`, **kierunek** = `normalize(B'(t))`, `heading = atan2(B'y, B'x)`.
|
||
- styczna na końcach: w `t=0` wzdłuż `p1-p0`, w `t=1` wzdłuż `p3-p2` — dlatego
|
||
uchwyty `p1`, `p2` leżą **na stycznych toru** w węzłach,
|
||
- kolejne segmenty są **G1** (styczne się zgadzają) — geometria edytora jest
|
||
styczna z konstrukcji, więc łączenia są gładkie.
|
||
|
||
**Uwaga o parametryzacji:** `t` **nie jest** długością łuku. Do równego rozstawu
|
||
podrozjazdnic/podkładów trzeba reparametryzować po długości: całkować `|B'(t)|`
|
||
(albo próbkować adaptacyjnie) i szukać `t` dla zadanej odległości. Nie rozstawiaj
|
||
elementów po stałym kroku `t`.
|
||
|
||
## Skąd biorą się Béziery (aproksymacja)
|
||
|
||
**Reguła: jeden element osi = jeden Bézier.** Bez dzielenia. Węzły wyznaczają
|
||
`p0`/`p3` i styczne w węzłach, uchwyty leżą na tych stycznych:
|
||
|
||
- **prosta** → `p1`, `p2` na linii między `p0` a `p3` (uchwyty w 1/3 długości).
|
||
- **łuk** kąta `theta`, promień `R` → uchwyty wzdłuż stycznych o długości
|
||
|
||
```
|
||
k = (4/3) * R * tan(theta/4)
|
||
```
|
||
|
||
Dla kątów rozjazdowych (`theta < 8°`) błąd jest rzędu ułamka milimetra.
|
||
- **klotoida** (krzywa przejściowa, jeśli użyta) → też **jeden Bézier**: `p0`,
|
||
`p3` i styczne z końców klotoidy, uchwyty na stycznych w 1/3 cięciwy. Kubiczna
|
||
nie odda liniowo zmiennej krzywizny co do joty, ale przy długościach
|
||
przejściówek w rozjeździe odchyłka jest mała i to akceptujemy — jeden segment,
|
||
bez podziału.
|
||
|
||
Styczne w węzłach są wspólne dla sąsiednich elementów, więc Béziery łączą się G1.
|
||
|
||
## Szyny — krzywe przesunięte
|
||
|
||
Szyny to osie przesunięte o `±0,7175 m` wzdłuż normalnej lewej. **Nie** trzymaj
|
||
ich jako osobnych Bézierów — przesunięcie Béziera nie jest Bézierem. Runtime
|
||
próbkuje pozę osi i offsetuje per-próbka:
|
||
|
||
```
|
||
lewa = B(t) + (-hy, hx) * 0.7175
|
||
prawa = B(t) - (-hy, hx) * 0.7175
|
||
```
|
||
|
||
Na promieniach rozjazdowych rozjeżdżanie się offsetu jest pomijalne.
|
||
|
||
## Podział elementów
|
||
|
||
**Ciągłe — sadzone z pozy wzdłuż osi (zero zależności od typu):**
|
||
|
||
- **szyny** — po obu osiach, offset jak wyżej; potrzebny profil przekroju,
|
||
- **podrozjazdnice** — po długości (reparametryzacja!), prostopadle do
|
||
dwusiecznej obu osi, wydłużane w strefie iglic i krzyżownicy tak, by objąć oba
|
||
tory. Parametryczne (skalowana długość) albo zestaw dyskretny — do ustalenia.
|
||
|
||
**Dyskretne odlewy — dobierane parametrem, nie nazwą szablonu:**
|
||
|
||
- **krzyżownica** — fizyczny odlew o kącie `alfa`; dobierana po **`crossing_mark`
|
||
(skos 1:n)**, wstawiana w `KR`, ustawiona pod `crossing_angle`,
|
||
- **iglice (zwrotnica)** — dobierane po **`rail_profile`** (i ew. `R`), wstawiane
|
||
w `PR`,
|
||
- **kierownice** — dowiązane do `KR`,
|
||
- **napęd + latarnia** — w `PR`, odsunięte w bok o ustalony offset.
|
||
|
||
Runtime: geometrię szyn/podrozjazdnic robi z Bézierów, a krzyżownicę i iglice
|
||
**wybiera po (`crossing_mark`, `rail_profile`)** i wstawia w `PR`/`KR`.
|
||
|
||
## Iglice — stany
|
||
|
||
Rozjazd się przekłada. Iglice potrzebują dwóch stanów (na wprost / na bok) albo
|
||
animacji przełożenia — do ustalenia z modelarzem. Pozycja bazowa: `PR`.
|
||
|
||
## Do ustalenia z modelarzem
|
||
|
||
1. origin w pionie: główka szyny czy spód podkładu,
|
||
2. profil: 49E1 / 60E1 / oba,
|
||
3. podrozjazdnice: parametryczne czy zestaw dyskretny,
|
||
4. iglice: ruchome (dwa stany / animacja) czy statyczne,
|
||
5. które elementy bierze modelarz, a które generuje runtime.
|
||
|
||
## Do zrobienia po stronie edytora
|
||
|
||
`JunctionGeom` ma dziś `PR (px,py)`, `KR (fx,fy)`, kierunek wyjścia `(fhx,fhy)`,
|
||
długość i osie w postaci polilinii. Do kontraktu trzeba dołożyć:
|
||
|
||
- **`crossing_mark` i `rail_profile`** w danych eksportu,
|
||
- **`blade_angle` (beta) i `pre_blade_length`** — dziś `lay_turnout` kładzie łuk
|
||
styczny od `PR` (beta = 0, brak przediglicowego). Trzeba: odcinek przediglicowy
|
||
(prosty), iglicę prostą pod `beta`, łuk o `alfa − beta`, prostą do `KR`,
|
||
- te dwa parametry **w szablonach** (`turnout_preset`) per typ,
|
||
- osie jako **Béziery** (dziś polilinie) — konwersja łuk/klotoida → kubiczne wg
|
||
powyższego,
|
||
- jawne oznaczenie „to rozjazd" (nie zwykły łuk), z `PR`/`KR`/`alfa`.
|