diff --git a/.env.example b/.env.example index 11e6ac4..f5e32fd 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # Copy this file to .env and configure your MQTT feed. No map key or build is needed. # Keep .env and any optional .secrets files private. -CARTOLITE_IMAGE=ghcr.io/n30nex/cartolite-server:0.7.0 +CARTOLITE_IMAGE=ghcr.io/n30nex/cartolite-server:0.7.1 CARTOLITE_BIND_ADDR=127.0.0.1 CARTOLITE_PORT=8080 # Optional CARTO override: see compose.carto.yml and docs/deployment.md. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 26c78e7..bac4262 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ concurrency: env: IMAGE_NAME: ghcr.io/n30nex/cartolite-server - # Exact published 0.6.4, in addition to the legacy 0.4.1 source migration below. - UPGRADE_FROM_IMAGE: ghcr.io/n30nex/cartolite-server@sha256:aac2452b05f6de3a79404e3d2ea807d99543ef38ee67e63a478eb6914d7e8a97 + # Exact published 0.7.0, in addition to the legacy 0.4.1 source migration below. + UPGRADE_FROM_IMAGE: ghcr.io/n30nex/cartolite-server@sha256:33d8a85d5052f7fd220845b0f46d5873b627c6507bd8721bc83713872328b41d jobs: backend: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0534e54..81c351c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.7.1 - Correct 3D Netgraph camera orientation + +- Correct the reversed perspective depth so 3D looks down onto the scene: nearby groups appear larger and raised connections sit above the ground. +- Keep panning, pointer anchoring, route picking and packet effects aligned with the corrected camera; top-down mode is unchanged. +- Add physical camera-orientation regressions alongside the existing projection, gesture, mobile and scale checks. + ## 0.7.0 - 3D Netgraph and connection inspection - Start top-down, with a 3D button for raised geographic groups, arched links and depth-aware live packet effects. diff --git a/VERSION b/VERSION index faef31a..39e898a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.7.0 +0.7.1 diff --git a/compose.yml b/compose.yml index 3eb732e..1d9172b 100644 --- a/compose.yml +++ b/compose.yml @@ -2,7 +2,7 @@ name: cartolite-server services: cartolite: - image: ${CARTOLITE_IMAGE:-ghcr.io/n30nex/cartolite-server:0.7.0} + image: ${CARTOLITE_IMAGE:-ghcr.io/n30nex/cartolite-server:0.7.1} restart: unless-stopped ports: - "${CARTOLITE_BIND_ADDR:-127.0.0.1}:${CARTOLITE_PORT:-8080}:8080" diff --git a/web/package-lock.json b/web/package-lock.json index 94ca48f..bf2db10 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "cartolite-server-web", - "version": "0.7.0", + "version": "0.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cartolite-server-web", - "version": "0.7.0", + "version": "0.7.1", "dependencies": { "maplibre-gl": "6.4.1" }, diff --git a/web/package.json b/web/package.json index 9c4d71b..040e0a3 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "cartolite-server-web", - "version": "0.7.0", + "version": "0.7.1", "private": true, "type": "module", "scripts": { diff --git a/web/src/netgraph/camera.test.ts b/web/src/netgraph/camera.test.ts index 9b2437c..406de0d 100644 --- a/web/src/netgraph/camera.test.ts +++ b/web/src/netgraph/camera.test.ts @@ -13,6 +13,24 @@ describe('Netgraph shared 3D geometry', () => { } }); + it('views the ground from above: foreground is larger and raised links rise toward the camera', () => { + for (const tilt of [Math.PI / 6, GRAPH_TILT, 1.1]) for (const yaw of [-2.8, 0, 1.4]) { + const p = new GraphProjection({ ...camera, x: 0, y: 0, zoom: 1, tilt, yaw }, 1200, 800); + const point = (y: number, z = 0) => ({ x: y * Math.sin(yaw), y: y * Math.cos(yaw), z }); + const front = p.project(point(600)), back = p.project(point(-600)); + expect(front.scale).toBeGreaterThan(1); expect(back.scale).toBeLessThan(1); + expect(front.y).toBeGreaterThan(400); expect(back.y).toBeLessThan(400); + for (const y of [-600, 0, 600]) { + expect(p.project(point(y, 100)).y).toBeLessThan(p.project(point(y)).y); + } + // A point lifted along the viewing ray stays centered and gets nearer. + const alongRay = p.project(point(300 * Math.sin(tilt), 300 * Math.cos(tilt))); + expect(alongRay.x).toBeCloseTo(600); expect(alongRay.y).toBeCloseTo(400); + expect(alongRay.depth).toBeCloseTo(-300); + expect(alongRay.scale).toBeCloseTo(camera.distance / (camera.distance - 300)); + } + }); + it('fits neighbours and arc crests into the space left by desktop and mobile inspectors', () => { const a = { x: -450, y: -100, z: 18 }, b = { x: 900, y: 450, z: 28 }; const points = [a, b, graphArcPoint(a, b, 0.5, true)]; @@ -47,7 +65,7 @@ describe('Netgraph shared 3D geometry', () => { it('clips behind-camera points without drawing or picking a line across the gap', () => { const projection = new GraphProjection({ ...camera, tilt: 1, distance: 1000 }, 1200, 800); - expect(projection.project({ x: 0, y: -5000 }).visible).toBe(false); + expect(projection.project({ x: 0, y: 5000 }).visible).toBe(false); const broken = [{ x: 0, y: 0, progress: 0 }, { x: 100, y: 100, progress: 0.5, breakBefore: true }, { x: 110, y: 110, progress: 1 }]; expect(distanceToGraphPath(broken, 50, 50)).toBeGreaterThan(50); expect(graphPathPoint(broken, 0.25).x).toBeLessThan(0); diff --git a/web/src/netgraph/camera.ts b/web/src/netgraph/camera.ts index e3ea01c..82178c4 100644 --- a/web/src/netgraph/camera.ts +++ b/web/src/netgraph/camera.ts @@ -25,7 +25,9 @@ export class GraphProjection { const dx = point.x - camera.x, dy = point.y - camera.y; const x = dx * this.cy - dy * this.sy, y = dx * this.sy + dy * this.cy; const z = camera.tilt === 0 ? 0 : (point.z ?? 0); - const depth = y * this.st - z * this.ct; + // The camera sits above the foreground (+y, +z). Screen-down and + // depth must be orthogonal; foreground objects are nearer, not farther. + const depth = -y * this.st - z * this.ct; const denominator = camera.distance + depth; if (!Number.isFinite(denominator) || denominator < camera.distance * 0.08) return hidden(); const scale = camera.tilt === 0 ? 1 : camera.distance / denominator; @@ -38,9 +40,9 @@ export class GraphProjection { ground(x: number, y: number): GraphPoint { const u = (x - this.width / 2) / this.camera.zoom; const v = (y - this.height / 2) / this.camera.zoom; - const denominator = Math.max(0.08, this.ct - v * this.st / this.camera.distance); + const denominator = Math.max(0.08, this.ct + v * this.st / this.camera.distance); const ry = v / denominator; - const rx = u * (1 + ry * this.st / this.camera.distance); + const rx = u * (1 - ry * this.st / this.camera.distance); return { x: this.camera.x + rx * this.cy + ry * this.sy, y: this.camera.y - rx * this.sy + ry * this.cy }; }