4 Commits
Author SHA1 Message Date
nilsandClaude Opus 5.5 69b1adee2b App 2.40.0: Grid with map straight from above, full height
The map fills the whole screen, top-down and heading up, about 400 m
ahead and 450 m to each side, over a ground grid that turns with it.
The speed moves to a panel under the gears; the battery wall, the
clock, the gears and the disc stand on dark panels over the map.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-27 00:12:06 +01:00
nilsandClaude Opus 5.5 90c2c75fc6 SPECS: app 2.39.2 published
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-27 00:06:51 +01:00
nilsandClaude Opus 5.5 2d7b324899 App 2.39.2: Grid with map seen from higher up
The map gets its own camera, tilted 45 degrees down from about 300 m up,
so roads keep their shape some 400 m ahead instead of collapsing into
the horizon. In map mode the floor starts at half height, the speed is a
little smaller, the floor's grid is fixed to the ground and turns with
the roads, and the power disc has a dark backing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-27 00:06:07 +01:00
nilsandClaude Opus 5.5 09cbd21634 k8s: pin server 2.8.0; app 2.39.1 published
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
2026-10-26 23:56:28 +01:00
7 changed files with 146 additions and 82 deletions
+20
View File
@@ -138,6 +138,26 @@ tyre pressures, GPS (lat, lon, elevation, heading) and the is_* flags. It does *
confirmed car facts, app structure, UI conventions, build/test/ship, documents. It replaces confirmed car facts, app structure, UI conventions, build/test/ship, documents. It replaces
upstream's EVABRPUploader text. The test emulator is now in the flake: `nix run .#car-emulator` upstream's EVABRPUploader text. The test emulator is now in the flake: `nix run .#car-emulator`
(Android 9 AOSP at 1920×720, 160 dpi; the car build installs as the system user — checked). (Android 9 AOSP at 1920×720, 160 dpi; the car build installs as the system user — checked).
- 2026-10-27 — App 2.40.0: **Grid with map straight from above, full height** (owner: 2.39.2
"does not look right"; wanted the map over the whole height and fully top-down). The map is
the whole background in map mode: orthographic, heading up, 1.2 layout units per metre (about
400 m ahead, 125 m behind, 450 m to each side on the car's screen), the car 150 units above
the bottom edge; ground grid every 50 m fixed to the ground; roads from 250 m ahead fainter;
the camera, the sky and the floor are gone there. The speed moves to a panel under the gears
(120 units), and the battery wall, the clock, the gears and the disc get dark panels. The
plain Grid is unchanged. Emulator with the Berlin tiles, both layouts. Not tried on the car.
- 2026-10-27 — App 2.39.2 published (`car-2.39.2`).
- 2026-10-27 — App 2.39.2: **Grid with map seen from higher up** (owner: the map looked like a
fish-eye; wanted it more top-down). The map has its own pinhole camera now: 45° down, 300 m
up, the car on its axis just above the read-outs, the top of the floor about 420 m ahead
(scale there ~60 % of the car's, before ~1/20 at 900 m); the floor starts at half height in
map mode and the speed is 170 instead of 210 units; the floor's grid becomes a ground grid
(a line every 50 m, fixed to the ground, turning with the roads); roads from 250 m ahead
fainter; the power disc gets a dark backing over the map. Emulator with the Berlin tiles,
both layouts. Not tried on the car.
- 2026-10-26 — Server 2.8.0 image pushed and pinned in `k8s/server.yaml` by digest
`sha256:8b8d94e0562833d196162866b7fdc6910d5dd219f6eb84572d4d5c91b74f7ff1` (checked with skopeo);
app 2.39.1 published (`car-2.39.1`). Not applied yet: the owner applies it.
- 2026-10-26 — App 2.39.1 and server 2.8.0: **the map's roads come from OpenFreeMap, on - 2026-10-26 — App 2.39.1 and server 2.8.0: **the map's roads come from OpenFreeMap, on
demand.** In the car, server 2.7.0 logged `overpass answered 502`, `overpass could not be demand.** In the car, server 2.7.0 logged `overpass answered 502`, `overpass could not be
reached`, and the owner saw 429s: the public Overpass limits per IP, and a tile takes it ~40 s reached`, and the owner saw 429s: the public Overpass limits per IP, and a tile takes it ~40 s
+5 -3
View File
@@ -165,9 +165,11 @@ films' other side.
## Grid with map ## Grid with map
The Grid, with the roads round the car from OpenStreetMap (through OpenFreeMap) laid on its floor as light lanes, in The Grid over a map: the roads round the car from OpenStreetMap (through OpenFreeMap) fill the
the floor's perspective and turned with the direction you drive: motorways glow brightest, then whole screen as light lanes, straight from above and turned with the direction you drive, about
main roads, then the smaller streets, fading towards the horizon. The car is the chevron on the 400 m ahead and 450 m to each side: motorways glow brightest, then main roads, then the smaller
streets, the far ones fainter, over a ground grid every 50 m that turns and slides with them.
The speed, the battery wall, the power disc and the clock stand on dark panels over the map. The car is the chevron on the
floor, and between GPS fixes it runs on with the speed. The roads come through the car's own floor, and between GPS fixes it runs on with the speed. The roads come through the car's own
server, which fetches only the map tiles you drive through and keeps them (see the README); the car also keeps server, which fetches only the map tiles you drive through and keeps them (see the README); the car also keeps
the last few hundred tiles itself. Without GPS, the server or the roads, the floor says so and the last few hundred tiles itself. Without GPS, the server or the roads, the floor says so and
+2 -2
View File
@@ -34,8 +34,8 @@ android {
applicationId "com.stinnesbeck.nilsmg4" applicationId "com.stinnesbeck.nilsmg4"
minSdk 27 minSdk 27
targetSdk 36 targetSdk 36
versionCode 143 versionCode 145
versionName "2.39.1" versionName "2.40.0"
// Nothing about the server ships in the APK: its URL and ingest token are entered in // Nothing about the server ships in the APK: its URL and ingest token are entered in
// the app's Server tab (ServerConfig), and the database is only reachable by the server. // the app's Server tab (ServerConfig), and the database is only reachable by the server.
+113 -71
View File
@@ -23,10 +23,12 @@ import android.os.SystemClock;
* the trail; the floor stands still when the speed is unread. * the trail; the floor stands still when the speed is unread.
* *
* With a map ("Grid with map", {@link MapRoads}): the roads round the car from OpenStreetMap lie * With a map ("Grid with map", {@link MapRoads}): the roads round the car from OpenStreetMap lie
* on the floor in its perspective, heading up, the bigger ones glowing brighter, with the car * under everything, heading up, the bigger ones glowing brighter, with the car as a light
* as a light marker a little way up the floor; between GPS fixes the car's position runs on * marker low on the screen; between GPS fixes the car's position runs on
* with the speed. The floor's tiles are then 40 m, so they move with the roads, and the trail * with the speed. The map is then the whole background, straight from above (about
* makes way for the map. Without a fix, a server or the roads, the plain floor with a line * 400 m ahead, 450 m to each side), over a ground grid (a line every 50 m) that turns and slides
* with the roads; the speed, the battery wall, the disc and the clock stand on dark panels over
* it, and the trail makes way for the map. Without a fix, a server or the roads, the plain floor with a line
* saying why. * saying why.
* *
* Two palettes: the original cyan, or the chosen accent colour ({@link #setAccentPalette}). A * Two palettes: the original cyan, or the chosen accent colour ({@link #setAccentPalette}). A
@@ -40,10 +42,10 @@ final class GridView extends ClusterCanvasView {
private static final int WALL = 20, DISC = 60; private static final int WALL = 20, DISC = 60;
private static final float TILE_M = 4f; private static final float TILE_M = 4f;
/** /**
* The map: metres per depth step of the floor, the car's depth (just above the read-outs), * The map, straight from above: pixels (in layout units) per metre, the car's height above
* the nearest depth drawn, how far ahead, and the depth from which roads are drawn fainter. * the bottom edge, roads from FADE_M ahead fainter, a ground-grid line every GRID_M.
*/ */
private static final float MAP_UNIT_M = 40f, CAR_DEPTH = 1.6f, NEAR = 0.6f, FAR_M = 900f, FADE_DEPTH = 5f; private static final float MAP_SCALE = 1.2f, CAR_FROM_BOTTOM = 150f, FADE_M = 250f, GRID_M = 50f;
private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint line = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint fill = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -58,13 +60,12 @@ final class GridView extends ClusterCanvasView {
private final int accent, accentLight; private final int accent, accentLight;
/** Null for the plain Grid. */ /** Null for the plain Grid. */
private final MapRoads map; private final MapRoads map;
/** Per road class, near and far (from {@link #FADE_DEPTH}). */ /** Per road class, near and far (from {@link #FADE_M} ahead). */
private final Path[] roads = new Path[RoadTile.CLASSES * 2]; private final Path[] roads = new Path[RoadTile.CLASSES * 2];
private float heading = Float.NaN; private float heading = Float.NaN;
/** The pen while a road is added: the last point, and whether it continues from there. */ private final Path gridPath = new Path();
private float penX, penY; /** This frame's map: pixels per metre and where the car is on screen. */
private boolean penDown; private float mapPx, carSx, carSy;
private Path penPath;
private int main, light, dim, alert; private int main, light, dim, alert;
/** The floor's position within one tile (0..1) and the disc's inner ring angle. */ /** The floor's position within one tile (0..1) and the disc's inner ring angle. */
@@ -107,16 +108,24 @@ final class GridView extends ClusterCanvasView {
advance(); advance();
c.drawColor(0xFF000000); c.drawColor(0xFF000000);
// With a map, the map is the whole background, seen from straight above, and the rest
// stands on dark panels over it.
boolean mapShown = map != null && map.fix() != null && !map.tiles().isEmpty();
float horizon = h * 0.55f; float horizon = h * 0.55f;
if (mapShown) {
drawMap(c, w, h, u);
} else {
drawSky(c, w, horizon); drawSky(c, w, horizon);
drawFloor(c, w, h, horizon, u); drawFloor(c, w, h, horizon, u);
boolean mapShown = map != null && drawMap(c, w, h, horizon, u); }
if (map != null && !mapShown) drawMapState(c, w, h, horizon, u); if (map != null && !mapShown) drawMapState(c, w, h, horizon, u);
if (mapShown) drawPanels(c, w, horizon, u);
drawGears(c, w, u); drawGears(c, w, u);
drawClock(c, w, u); drawClock(c, w, u);
drawSpeed(c, w, horizon, u); if (mapShown) drawMapSpeed(c, w, u);
else drawSpeed(c, w, horizon, u);
drawWall(c, horizon, u); drawWall(c, horizon, u);
drawDisc(c, w, u); drawDisc(c, w, mapShown, u);
if (!mapShown) drawTrail(c, w, h, u); if (!mapShown) drawTrail(c, w, h, u);
drawReadouts(c, w, h, u); drawReadouts(c, w, h, u);
if (mapShown) drawAttribution(c, w, h, u); if (mapShown) drawAttribution(c, w, h, u);
@@ -132,8 +141,7 @@ final class GridView extends ClusterCanvasView {
float dt = lastFrame == 0 ? 0f : Math.min(0.2f, (now - lastFrame) / 1000f); float dt = lastFrame == 0 ? 0f : Math.min(0.2f, (now - lastFrame) / 1000f);
lastFrame = now; lastFrame = now;
if (v.speedKmh != null && v.speedKmh > 0.5f) { if (v.speedKmh != null && v.speedKmh > 0.5f) {
float tile = map != null ? MAP_UNIT_M : TILE_M; floorPhase = (floorPhase + v.speedKmh / 3.6f * dt / TILE_M) % 1f;
floorPhase = (floorPhase + v.speedKmh / 3.6f * dt / tile) % 1f;
} }
if (v.powerKw != null) discTurn = (discTurn + v.powerKw * 3f * dt) % 360f; if (v.powerKw != null) discTurn = (discTurn + v.powerKw * 3f * dt) % 360f;
} }
@@ -172,11 +180,11 @@ final class GridView extends ClusterCanvasView {
// ---------- Map ---------- // ---------- Map ----------
/** The roads round the car on the floor; false when there is nothing to draw. */ /** The roads round the car from straight above, heading up, over a ground grid. */
private boolean drawMap(Canvas c, float w, float h, float horizon, float u) { private void drawMap(Canvas c, float w, float h, float u) {
MapRoads.Fix fix = map.fix(); MapRoads.Fix fix = map.fix();
java.util.Collection<RoadTile> tiles = map.tiles(); java.util.Collection<RoadTile> tiles = map.tiles();
if (fix == null || tiles.isEmpty()) return false; if (fix == null) return;
// Heading up: ease round the shorter way; north up until the car has moved. // Heading up: ease round the shorter way; north up until the car has moved.
float target = Float.isNaN(fix.bearing) ? (Float.isNaN(heading) ? 0f : heading) : fix.bearing; float target = Float.isNaN(fix.bearing) ? (Float.isNaN(heading) ? 0f : heading) : fix.bearing;
if (Float.isNaN(heading)) heading = target; if (Float.isNaN(heading)) heading = target;
@@ -192,13 +200,30 @@ final class GridView extends ClusterCanvasView {
double carX = MapGeometry.worldX(fix.lon) + ran * Math.sin(run) / mpu; double carX = MapGeometry.worldX(fix.lon) + ran * Math.sin(run) / mpu;
double carY = MapGeometry.worldY(fix.lat) - ran * Math.cos(run) / mpu; double carY = MapGeometry.worldY(fix.lat) - ran * Math.cos(run) / mpu;
mapPx = MAP_SCALE * u;
carSx = w / 2f;
carSy = h - CAR_FROM_BOTTOM * u;
float sin = (float) Math.sin(Math.toRadians(heading)), cos = (float) Math.cos(Math.toRadians(heading)); float sin = (float) Math.sin(Math.toRadians(heading)), cos = (float) Math.cos(Math.toRadians(heading));
float lane = w * 0.09f, depth = h - horizon, vx = w / 2f, m = (float) mpu; float m = (float) mpu;
double range = FAR_M / mpu; // Everything on screen is within this many metres of the car.
float reach = (float) Math.hypot(w / 2f, carSy) / mapPx + GRID_M;
double range = reach / mpu;
// The ground grid, fixed to the ground: it turns and slides with the roads.
gridPath.reset();
double carEast = carX * mpu, carNorth = -carY * mpu;
float offE = (float) (carEast % GRID_M), offN = (float) (carNorth % GRID_M);
int lines = (int) (reach / GRID_M) + 1;
for (int k = -lines; k <= lines; k++) {
float e = k * GRID_M - offE, n = k * GRID_M - offN;
gridLine(e, -reach, e, reach, sin, cos);
gridLine(-reach, n, reach, n, sin, cos);
}
for (Path p : roads) p.reset(); for (Path p : roads) p.reset();
for (RoadTile tile : tiles) { for (RoadTile tile : tiles) {
double ox = (double) tile.x * MapGeometry.EXTENT - carX, oy = (double) tile.y * MapGeometry.EXTENT - carY; double ox = (double) tile.x * MapGeometry.EXTENT - carX, oy = (double) tile.y * MapGeometry.EXTENT - carY;
if (ox > range || oy > range || ox + 2 * MapGeometry.EXTENT < -range || oy + 2 * MapGeometry.EXTENT < -range) { if (ox > range || oy > range || ox + MapGeometry.EXTENT < -range || oy + MapGeometry.EXTENT < -range) {
continue; continue;
} }
float fx = (float) ox, fy = (float) oy; float fx = (float) ox, fy = (float) oy;
@@ -207,83 +232,95 @@ final class GridView extends ClusterCanvasView {
if (fx + b[2] < -range || fx + b[0] > range || fy + b[3] < -range || fy + b[1] > range) continue; if (fx + b[2] < -range || fx + b[0] > range || fy + b[3] < -range || fy + b[1] > range) continue;
int cls = tile.classes[i]; int cls = tile.classes[i];
int[] pts = tile.points[i]; int[] pts = tile.points[i];
penDown = false;
float pa = 0f, pf = 0f; float pa = 0f, pf = 0f;
Path prev = null;
for (int k = 0; k < pts.length; k += 2) { for (int k = 0; k < pts.length; k += 2) {
float east = (fx + pts[k]) * m, north = -(fy + pts[k + 1]) * m; float east = (fx + pts[k]) * m, north = -(fy + pts[k + 1]) * m;
float fwd = east * sin + north * cos, side = east * cos - north * sin; float fwd = east * sin + north * cos, side = east * cos - north * sin;
if (k > 0) segment(cls, pa, pf, side, fwd, vx, horizon, lane, depth); if (k > 0) {
Path p = roads[cls * 2 + (Math.min(pf, fwd) > FADE_M ? 1 : 0)];
if (p != prev) p.moveTo(screenX(pa), screenY(pf));
p.lineTo(screenX(side), screenY(fwd));
prev = p;
}
pa = side; pa = side;
pf = fwd; pf = fwd;
} }
} }
} }
c.save();
c.clipRect(0, horizon, w, h);
line.setStrokeJoin(Paint.Join.ROUND); line.setStrokeJoin(Paint.Join.ROUND);
line.setStrokeCap(Paint.Cap.ROUND); line.setStrokeCap(Paint.Cap.ROUND);
// Far roads thin and faint, so the horizon stays a line rather than a tangle. line.setColor(AccentColor.withAlpha(main, 0x26));
line.setStrokeWidth(1f * u);
c.drawPath(gridPath, line);
// Far roads thinner and fainter.
for (int cls = RoadTile.CLASSES - 1; cls >= 0; cls--) { for (int cls = RoadTile.CLASSES - 1; cls >= 0; cls--) {
line.setColor(AccentColor.withAlpha(cls == 0 ? light : main, cls >= 3 ? 0x30 : 0x50)); line.setColor(AccentColor.withAlpha(cls == 0 ? light : main, cls >= 3 ? 0x60 : 0x90));
line.setStrokeWidth((cls == 0 ? 1.6f : 1f) * u); line.setStrokeWidth((cls == 0 ? 2.4f : cls == 3 ? 1.2f : 1.8f) * u);
c.drawPath(roads[cls * 2 + 1], line); c.drawPath(roads[cls * 2 + 1], line);
} }
line.setColor(AccentColor.withAlpha(main, 0x80)); line.setColor(AccentColor.withAlpha(main, 0xA0));
line.setStrokeWidth(1.4f * u); line.setStrokeWidth(1.8f * u);
c.drawPath(roads[3 * 2], line); c.drawPath(roads[3 * 2], line);
line.setColor(AccentColor.withAlpha(main, 0xC0)); line.setColor(AccentColor.withAlpha(main, 0xD8));
line.setStrokeWidth(2f * u); line.setStrokeWidth(2.4f * u);
c.drawPath(roads[2 * 2], line); c.drawPath(roads[2 * 2], line);
glow(c, roads[2], main, 2.4f * u); glow(c, roads[2], main, 2.8f * u);
glow(c, roads[0], light, 3.2f * u); glow(c, roads[0], light, 3.6f * u);
line.setStrokeJoin(Paint.Join.MITER); line.setStrokeJoin(Paint.Join.MITER);
line.setStrokeCap(Paint.Cap.SQUARE); line.setStrokeCap(Paint.Cap.SQUARE);
c.restore();
// The car: a light-cycle chevron pointing ahead. // The car: a light-cycle chevron pointing ahead.
float cy = horizon + depth / CAR_DEPTH;
path.reset(); path.reset();
path.moveTo(vx, cy - 16f * u); path.moveTo(carSx, carSy - 18f * u);
path.lineTo(vx + 11f * u, cy + 10f * u); path.lineTo(carSx + 12f * u, carSy + 11f * u);
path.lineTo(vx, cy + 4f * u); path.lineTo(carSx, carSy + 5f * u);
path.lineTo(vx - 11f * u, cy + 10f * u); path.lineTo(carSx - 12f * u, carSy + 11f * u);
path.close(); path.close();
fill.setColor(light); fill.setColor(light);
c.drawPath(path, fill); c.drawPath(path, fill);
glow(c, path, light, 1.5f * u); glow(c, path, light, 1.5f * u);
return true;
} }
/** One stretch of road from (a0, f0) to (a1, f1) in metres (side, ahead), cut at the near edge. */ private float screenX(float side) {
private void segment(int cls, float a0, float f0, float a1, float f1, float vx, float horizon, float lane, return carSx + side * mapPx;
float depth) {
float d0 = CAR_DEPTH + f0 / MAP_UNIT_M, d1 = CAR_DEPTH + f1 / MAP_UNIT_M;
Path p = roads[cls * 2 + (Math.min(d0, d1) > FADE_DEPTH ? 1 : 0)];
if ((d0 < NEAR && d1 < NEAR) || (f0 > FAR_M && f1 > FAR_M)) {
penDown = false;
return;
} }
boolean cut = false;
if (d0 < NEAR || d1 < NEAR) { private float screenY(float ahead) {
float t = (NEAR - d0) / (d1 - d0); return carSy - ahead * mapPx;
float a = a0 + (a1 - a0) * t, d = NEAR;
if (d0 < NEAR) {
a0 = a;
d0 = d;
cut = true;
} else {
a1 = a;
d1 = d;
} }
/** A grid line between two ground points given east and north of the car, in metres. */
private void gridLine(float e0, float n0, float e1, float n1, float sin, float cos) {
gridPath.moveTo(screenX(e0 * cos - n0 * sin), screenY(e0 * sin + n0 * cos));
gridPath.lineTo(screenX(e1 * cos - n1 * sin), screenY(e1 * sin + n1 * cos));
} }
float x0 = vx + a0 / MAP_UNIT_M * lane / d0, y0 = horizon + depth / d0;
float x1 = vx + a1 / MAP_UNIT_M * lane / d1, y1 = horizon + depth / d1; /** Dark panels under the battery wall and the clock, so the map does not run through them. */
if (!penDown || cut || p != penPath || x0 != penX || y0 != penY) p.moveTo(x0, y0); private void drawPanels(Canvas c, float w, float horizon, float u) {
p.lineTo(x1, y1); chamfer(16f * u, 78f * u, 360f * u, horizon + 86f * u, 14f * u);
penPath = p; fill.setColor(0xC8000000);
penX = x1; c.drawPath(path, fill);
penY = y1; chamfer(w - 210f * u, 12f * u, w - 12f * u, 82f * u, 10f * u);
penDown = true; c.drawPath(path, fill);
// Behind the gears as well.
chamfer(w / 2f - 150f * u, 12f * u, w / 2f + 150f * u, 72f * u, 10f * u);
c.drawPath(path, fill);
}
/** The speed over the map: smaller, on a panel under the gears. */
private void drawMapSpeed(Canvas c, float w, float u) {
chamfer(w / 2f - 130f * u, 80f * u, w / 2f + 130f * u, 232f * u, 14f * u);
fill.setColor(0xC8000000);
c.drawPath(path, fill);
line.setColor(dim);
line.setStrokeWidth(1.5f * u);
c.drawPath(path, line);
text.setTypeface(thin);
text.setTextSize(120f * u);
text.setTextAlign(Paint.Align.CENTER);
glowText(c, fmt(v.speedKmh, "%.0f", "—"), w / 2f, 194f * u, light, u);
caption(c, "KM/H", w / 2f, 220f * u, u, Paint.Align.CENTER);
} }
/** Why the map is not there, on the floor. */ /** Why the map is not there, on the floor. */
@@ -393,8 +430,13 @@ final class GridView extends ClusterCanvasView {
// ---------- Power disc ---------- // ---------- Power disc ----------
/** Zero at the top, −50 kW a quarter turn anticlockwise, 150 kW three quarters clockwise. */ /** Zero at the top, −50 kW a quarter turn anticlockwise, 150 kW three quarters clockwise. */
private void drawDisc(Canvas c, float w, float u) { private void drawDisc(Canvas c, float w, boolean overMap, float u) {
float cx = w - 170f * u, cy = 238f * u, outer = 132f * u, ring = 22f * u; float cx = w - 170f * u, cy = 238f * u, outer = 132f * u, ring = 22f * u;
if (overMap) {
// The map runs under the disc: a dark backing keeps it readable.
fill.setColor(0xD0000000);
c.drawCircle(cx, cy, outer + 16f * u, fill);
}
line.setColor(dim); line.setColor(dim);
line.setStrokeWidth(1.5f * u); line.setStrokeWidth(1.5f * u);
c.drawCircle(cx, cy, outer + 10f * u, line); c.drawCircle(cx, cy, outer + 10f * u, line);
+1 -1
View File
@@ -47,7 +47,7 @@ spec:
containers: containers:
- name: server - name: server
# Pinned by digest so a re-pushed tag cannot change what runs (version in SPECS.md). # Pinned by digest so a re-pushed tag cannot change what runs (version in SPECS.md).
image: git.stinnesbeck.com/nils/mg4-server@sha256:e5da9452a9dbdfabe3340766cc51f18bb1418b2588ab534714550470abe1ce29 image: git.stinnesbeck.com/nils/mg4-server@sha256:8b8d94e0562833d196162866b7fdc6910d5dd219f6eb84572d4d5c91b74f7ff1
ports: ports:
- name: http - name: http
containerPort: 8080 containerPort: 8080
Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 105 KiB