Compare commits

...

4 Commits

Author SHA1 Message Date
173a440bd6 rotate the offscreen map
this takes us from 60% cpu to about 20% (on my laptop, your hardware
may vary) when we're travelling in a straight line, as we render the
offscreen map only when the bounds change or the target orientation
changes, not every time we move
2025-06-15 17:52:06 +01:00
6b921e2c25 clamp minimum turn speed
if there's less than ten degrees between target and actual, snap to
target. Otherwise we only approach the target asymptotically and never
reach it
2025-06-15 17:49:46 +01:00
ccca847e3c don't force invalidate map widget unless moved
* make the app-state a single-level table so we can easily
copy it and check for changes
* call invalidate_rect only if we've moved or changed orientation
2025-06-14 12:10:49 +01:00
2406a15db9 support cairo-trace (but it doesn't work)
this env var is needed for cairo-trace to run, but note it doesn't
actually print anything. I don't know why but will speculate that
it's a gir thing
2025-06-14 11:54:40 +01:00
2 changed files with 55 additions and 20 deletions

View File

@ -67,6 +67,7 @@ in stdenv.mkDerivation {
];
GIO_EXTRA_MODULES = [ "${glib-networking.out}/lib/gio/modules" ];
RXI_JSON="${rxi-json}/";
CAIRO_TRACE_SO = "${cairo.out}/lib/cairo/libcairo-trace.so";
makeFlags = [ "PREFIX=${placeholder "out"}" "NAME=${pname}" ];

View File

@ -86,7 +86,8 @@ label.readout {
:lon 0
:zoom 17
:course 0 ; direction of travel
:orientation {:target 0 :actual 0} ; map rotation angle from north
:orientation-target 0 ; map rotation angle from north
:orientation-actual 0 ; map rotation angle from north
:tiles {}
}
)
@ -133,17 +134,23 @@ label.readout {
:x (* tile-size num-tiles-x)
:y (* tile-size num-tiles-y)
}
:centre {
:x (/ (+ min-tile-x max-tile-x 1) 2)
:y (/ (+ min-tile-y max-tile-y 1) 2)
}
}))
;; diagonal radius is 538 pixels, 2.1 tiles
(let [bounds (map-bounds-tile 65539.5 45014.5)]
(expect= bounds.min {:x 65537 :y 45012})
(expect= bounds.max {:x 65541 :y 45016}))
(expect= bounds.max {:x 65541 :y 45016})
(expect= bounds.centre {:x 65539.5 :y 45014.5}))
(let [bounds (map-bounds-tile 65539.0 45014.0)]
(expect= bounds.min {:x 65536 :y 45011})
(expect= bounds.max {:x 65541 :y 45016})
(expect= bounds.centre {:x 65539 :y 45014})
)
(fn map-bounds [lat lon zoom]
@ -216,6 +223,10 @@ label.readout {
(g:rectangle 0 0 bounds.pixels.x bounds.pixels.y)
(g:fill)
(g:translate (+ (// bounds.pixels.x 2)) (+ (// bounds.pixels.y 2)))
(g:rotate (* (/ (- app-state.orientation-target) 180) math.pi))
(g:translate (- (// bounds.pixels.x 2)) (- (// bounds.pixels.y 2)))
(cairo-roads g lines bounds)
(g:set_source_rgb 0.2 0.2 0.2)
@ -245,8 +256,16 @@ label.readout {
(fn on-osm-draw [widget g]
(let [(tile-x tile-y) (tiles.latlon->tile app-state.lat app-state.lon app-state.zoom)
bounds (map-bounds-tile tile-x tile-y)
offset-x (- (* tile-size (- tile-x bounds.min.x)) (/ viewport-width 2))
offset-y (- (* tile-size (- tile-y bounds.min.y)) (/ viewport-height 2))]
offset-x (/ (- viewport-width bounds.pixels.x) 2)
offset-y (/ (- viewport-height bounds.pixels.y) 2)
x-to-centre (- tile-x bounds.centre.x)
y-to-centre (- tile-y bounds.centre.y)
angle (- (/ (* math.pi app-state.orientation-target) 180))
x-to-centre-rot (- (* x-to-centre (math.cos angle))
(* y-to-centre (math.sin angle)))
y-to-centre-rot (+ (* x-to-centre (math.sin angle))
(* y-to-centre (math.cos angle)))
]
(when (not map-surface)
(let [window (widget:get_window)]
@ -258,11 +277,16 @@ label.readout {
bounds.pixels.y)
(draw-onto-map-surface bounds app-state.zoom)))))
(g:translate (+ (/ viewport-width 2)) (+ (/ viewport-height 2)))
(g:rotate (* (/ (- 360 app-state.orientation.actual) 180) math.pi))
(g:translate (- (/ viewport-width 2)) (- (/ viewport-height 2)))
(g:set_source_surface map-surface (- offset-x) (- offset-y))
(when (not (= app-state.orientation-actual app-state.orientation-target))
(print (- app-state.orientation-actual app-state.orientation-target))
(g:translate (+ (/ viewport-width 2)) (+ (/ viewport-height 2)))
(g:rotate (* (/ (- 360 (- app-state.orientation-actual app-state.orientation-target)) 180) math.pi))
(g:translate (- (/ viewport-width 2)) (- (/ viewport-height 2))))
(g:set_source_surface map-surface
(- offset-x (* tile-size x-to-centre-rot))
(- offset-y (* tile-size y-to-centre-rot)))
(g:set_operator cairo.Operator.SOURCE)
(g:paint)))
@ -300,9 +324,15 @@ label.readout {
(expect= (hhmmss (+ 45 (* 60 12) (* 60 60 3))) "3:12:45")
(fn turn-smoothly [from to]
(if (< (math.abs (- from to)) 10) to
(+ from (* 0.05 (- to from)))))
(fn update-app-state [new-vals]
(let [old-bounds
(let [old-state (merge {} app-state)
old-bounds
(map-bounds app-state.lat app-state.lon app-state.zoom)]
(merge app-state new-vals)
(let [bounds
@ -311,22 +341,26 @@ label.readout {
(fetch-tiles bounds app-state.tiles app-state.zoom)
(set map-surface nil)))
(when (> (math.abs (- app-state.orientation.target app-state.course)) 20)
(set app-state.orientation.target app-state.course)
(when (> (math.abs (- app-state.orientation-target app-state.course)) 20)
(set app-state.orientation-target app-state.course)
; (-> state-widgets.rose (: :get_window) (: :invalidate_rect nil))
(set map-surface nil))
(when (not (= app-state.orientation.target app-state.orientation.actual))
(set app-state.orientation.actual
(+ app-state.orientation.actual
(* 0.05 (- app-state.orientation.target app-state.orientation.actual))))
)
(when (not (= app-state.orientation-target app-state.orientation-actual))
(set app-state.orientation-actual
(turn-smoothly app-state.orientation-actual app-state.orientation-target)))
(each [name widget (pairs state-widgets)]
(case name
:speed (widget:set_label
(string.format "%.1f km/h" (* app-state.speed 3.6)))
:osm (: (widget:get_window) :invalidate_rect nil)
:osm
(when (not (and ; false
(= old-state.lat app-state.lat)
(= old-state.lon app-state.lon)
(= old-state.orientation-actual
app-state.orientation-actual)
))
(: (widget:get_window) :invalidate_rect nil))
:arrow (: (widget:get_window) :invalidate_rect nil)
:rose (: (widget:get_window) :invalidate_rect nil)
:time (widget:set_label
@ -357,7 +391,7 @@ label.readout {
(fn [self g]
(g:set_source_rgb 0.4 0.0 0.1)
(g:translate (// height 2) (// height 2))
(g:rotate (* (/ (- app-state.course app-state.orientation.actual)
(g:rotate (* (/ (- app-state.course app-state.orientation-actual)
180) math.pi))
(g:translate (// height -2) (// height -2))
(g:set_line_width 4)
@ -389,7 +423,7 @@ label.readout {
(g:stroke)
(g:translate (// height 2) (// height 2))
(g:rotate (- (deg->rad app-state.orientation.actual)))
(g:rotate (- (deg->rad app-state.orientation-actual)))
(g:translate (// height -2) (// height -2))
(g:set_line_width 2)