mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-29 08:07:29 +01:00
Editor checkpoint: turnout domain service, web->editor rename, Id-1 templates
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.
This commit is contained in:
176
TURNOUT_SPEC.md
Normal file
176
TURNOUT_SPEC.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user