17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-30 14:20:28 +01:00

Plan tool: cofanie, złącza, przejścia rozjazdowe, and a demo to open

What the plan tool was missing was not geometry but the ordinary business of
editing: taking a mistake back, dividing what is drawn, and seeing what the next
click will do before making it.

Cofanie. The document is plain data, so a step is a copy of it. What counts as one
step is decided by watching the document - a fingerprint over the tracks, turnouts
and catalogue - rather than by dressing every edit with a call: a frame that
changes anything opens a step, and the step closes when the drawing settles. That
is what makes a drag, or a number typed digit by digit, one step and not thirty,
and nothing can be forgotten on the way in. Ctrl+Z / Ctrl+Y; the scene editor
listens for the same keys, so it hands them over while the plan has the keyboard.

Złącze i wycięcie. `insert_joint` divides whichever element a station lands in and
leaves one chain: the ground does not move, the curvature at the new joint is the
one that was always there, and the fit reads the chain the same way afterwards. It
is refused on a straight, because two straights end to end are one straight and the
skeleton will not read that, and under a turnout, which is one piece of geometry on
one element. `cut_track` takes a piece out and does leave two tracks - a gap is the
ground between them, not a hole in a chain.

Przejście rozjazdowe. A turnout may now stand against another one: where it stands
is then worked out from that one's frog, not authored, so moving either carries the
other with it and a trapez stays shut. The frog has to land on the line leaving the
other frog - not on the track's axis, which is 1,469 m further on for a 1:9 - so
the station is settled by laying the turnout and walking it along until it does.

Drawing is a tool now, not what a click happens to do: until Rysuj is on, a click
picks what is under it. With it on, the cursor at the growing end shows the corner
and the straight the next click would lay, worked out by the same fit_corner the
click itself uses. A chain only grows at a loose end, and the first element of a
track is put down by two clicks instead of landing on the plan's zero.

Also here: the curvature strip under the panel, pikietaż in the rows and at the
cursor, how far the laid axis runs from the clicks it was drawn from, the file
format saying which frame and which units it is in (v9), plans chosen from the
directory instead of typed, and editor/samples/demo.m0s - two parallel tracks and a
trapez - with the generator that wrote it and a test that opens it and demands it
lays clean.
This commit is contained in:
maj00r
2026-09-14 16:35:36 +02:00
parent 07cd3b3769
commit 366645ef59
18 changed files with 2584 additions and 41 deletions

45
editor/samples/demo.m0s Normal file
View File

@@ -0,0 +1,45 @@
m0s 9
crs 2180
units m rad 1/m
next 63
view 644000.0 493100.0 260
origin 1 1 643640.5594274866 493212.77532093093
scn scenery/plan_demo.scn
types 1
type 9 27.138 0.005 0.07 0 4
tseg 0 3.3108 0 0 0
tseg 1 7.188 190 190 0
tseg 2 13.836872023040172 190 190 0
tseg 3 2.7808 0 0 0
tname Rz 1:9 R190
turnouts 4
turnout 5 2 400.0 1 1 1 0.0 0
tutype Rz 1:9 R190
turnout 6 4 470.4179524922183 1 0 1 0.0 5
tutype Rz 1:9 R190
turnout 9 4 600.0 -1 1 1 0.0 0
tutype Rz 1:9 R190
turnout 10 2 670.4179524922184 -1 0 1 0.0 9
tutype Rz 1:9 R190
tracks 4
track 2
tkname tor 1
anchor pose 643400.0 493100.0 1.5707963267948966
elems 1
elem 1 line 0.0 0 1200.0
track 4
tkname tor 2
anchor pose 643400.0 493104.75 1.5707963267948966
elems 1
elem 3 line 0.0 0 1200.0
epar 1 4.75
track 8
tkname trapez 1-2 (wstawka)
anchor port 5 frog
elems 1
elem 7 line 0.0 0 16.405126913934424
track 12
tkname trapez 3-4 (wstawka)
anchor port 9 frog
elems 1
elem 11 line 0.0 0 16.405126913934424

119
editor/samples/make_demo.py Normal file
View File

@@ -0,0 +1,119 @@
"""Writes editor/samples/demo.m0s: the plan the editor is handed to open.
It is deliberately small — two parallel tracks and a trapez between them — because
that is the case worth having in front of you: a przejście rozjazdowe is the one
piece of trackwork whose two halves have to agree with each other, and everything
about how the editor ties one turnout to another shows up here.
The numbers are not drawn by eye. A 1:9 turnout leaves its frog 1,469 m off the
track it stands on, so between tracks 4,75 m apart the wstawka has 1,811 m left to
cover, which at the crossing angle is 16,405 m of straight and nothing else.
"""
import math
import pathlib
ORIGIN = (643640.5594274866, 493212.77532093093) # what the scenery's zero stands for
EAST = math.pi / 2.0 # azimuth: radians clockwise from north
ALPHA = math.atan(1.0 / 9.0) # the crossing angle
FROG_LAT = 190.0 * (1.0 - math.cos(ALPHA)) + 2.7808 * math.sin(ALPHA) # KR off the track
FROG_ALONG = 3.3108 + 190.0 * math.sin(ALPHA) + 2.7808 * math.cos(ALPHA) # and along it
X0, Y0 = 643400.0, 493100.0
LENGTH = 1200.0
SPACING = 4.75
GAP = (SPACING - 2.0 * FROG_LAT) / math.sin(ALPHA) # the wstawka between the two frogs
GAP_ALONG = GAP * math.cos(ALPHA)
def g(value):
return repr(float(value))
ids = iter(range(1, 200))
tracks = []
turnouts = []
def nid():
return next(ids)
def track(name, anchor, elems):
tid = nid()
tracks.append((tid, name, anchor, elems))
return tid
def elem(kind, radius, hand, length, hold=None):
eid = nid()
out = ["elem %d %s %s %d %s" % (eid, kind, g(radius), hand, g(length))]
if hold is not None:
out.append("epar %d %s" % (hold[0], g(hold[1])))
return eid, out
def turnout(on, station, side, facing=1, opposite=0, kind="Rz 1:9 R190"):
"""`side` is which way the branch comes out in the world: +1 left of the
kilometrage, -1 right. A trailing turnout is laid on a reversed pose, so the
same side of the ground is the opposite hand."""
tid = nid()
turnouts.append((tid, on, station, side if facing else -side, facing, 1, 0.0, opposite, kind))
return tid
# --- the two tracks. the second is held at międzytorze against the first, so it lies
# exactly parallel and stays that way when the first one is edited
main_elems = []
MAIN_STRAIGHT, l = elem("line", 0, 0, LENGTH)
main_elems += l
TOR1 = track("tor 1", "anchor pose %s %s %s" % (g(X0), g(Y0), g(EAST)), main_elems)
second_elems = []
_, l = elem("line", 0, 0, LENGTH, hold=(MAIN_STRAIGHT, SPACING))
second_elems += l
TOR2 = track("tor 2", "anchor pose %s %s %s" % (g(X0), g(Y0 + SPACING), g(EAST)), second_elems)
def crossover(name, from_track, from_station, side, from_begin, to_track, to_begin):
"""A turnout on one track, the wstawka off its frog, and the turnout standing
against it on the other. The far one is trailing: it opens back toward the
near one, which is what makes the two of them one przejście rozjazdowe."""
near = turnout(from_track, from_station, side)
# the far one stands against the near one: where it stands is worked out from that
# one's frog, so moving either of them carries the other with it. the station written
# here is only where it would go if the two were ever unpaired
meets = (from_begin + from_station + FROG_ALONG + GAP_ALONG) - to_begin
far = turnout(to_track, meets + FROG_ALONG, -side, facing=0, opposite=near)
_, elems = elem("line", 0, 0, GAP)
track(name, "anchor port %d frog" % near, elems)
return near, far
# the trapez: one crossover up onto tor 2, the other back down onto tor 1
crossover("trapez 1-2 (wstawka)", TOR1, 400.0, 1, 0.0, TOR2, 0.0)
crossover("trapez 3-4 (wstawka)", TOR2, 600.0, -1, 0.0, TOR1, 0.0)
# --- the file
curve = 190.0 * ALPHA - 7.188
out = ["m0s 9", "crs 2180", "units m rad 1/m", "next %d" % (next(ids) + 50)]
out += ["view %s %s 260" % (g(X0 + LENGTH * 0.5), g(Y0))]
out += ["origin 1 1 %s %s" % (g(ORIGIN[0]), g(ORIGIN[1]))]
out += ["scn scenery/plan_demo.scn"]
out += ["types 1", "type 9 27.138 0.005 0.07 0 4",
"tseg 0 3.3108 0 0 0", "tseg 1 7.188 190 190 0",
"tseg 2 %s 190 190 0" % g(curve), "tseg 3 2.7808 0 0 0", "tname Rz 1:9 R190"]
out += ["turnouts %d" % len(turnouts)]
for tid, on, station, hand, facing, bft, bend, opposite, kind in turnouts:
out += ["turnout %d %d %s %d %d %d %s %d" % (tid, on, g(station), hand, facing, bft, g(bend), opposite),
"tutype " + kind]
out += ["tracks %d" % len(tracks)]
for tid, name, anchor, elems in tracks:
out += ["track %d" % tid, "tkname " + name, anchor,
"elems %d" % sum(1 for e in elems if e.startswith("elem "))]
out += elems
pathlib.Path(__file__).with_name("demo.m0s").write_text("\n".join(out) + "\n", encoding="utf-8")
print("tory: %d, rozjazdy: %d, wstawka: %.3f m" % (len(tracks), len(turnouts), GAP))