From 94811ea59ca1b8f07284444736b6cc2eb4b35617 Mon Sep 17 00:00:00 2001
From: Hong-Phuc Bui <hong-phuc.bui@htwsaar.de>
Date: Sun, 18 Oct 2026 14:48:11 +0200
Subject: [PATCH] Interactive UI
---
02-0-kurswinkel-benutzer-interactive/tangente.js | 34 ++++++++++++++--
02-0-kurswinkel-benutzer-interactive/sphere-calculator.js | 27 +++++++++++++
02-0-kurswinkel-benutzer-interactive/index.html | 2
3 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/02-0-kurswinkel-benutzer-interactive/index.html b/02-0-kurswinkel-benutzer-interactive/index.html
index 8d0a2f5..55bf94a 100644
--- a/02-0-kurswinkel-benutzer-interactive/index.html
+++ b/02-0-kurswinkel-benutzer-interactive/index.html
@@ -23,6 +23,6 @@
</div>
</div>
</div>
-<script src="tangente.js"></script>
+<script type="module" src="tangente.js"></script>
</body>
</html>
\ No newline at end of file
diff --git a/02-0-kurswinkel-benutzer-interactive/sphere-calculator.js b/02-0-kurswinkel-benutzer-interactive/sphere-calculator.js
new file mode 100644
index 0000000..dca48ff
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/sphere-calculator.js
@@ -0,0 +1,27 @@
+export const toRadians = (deg) => deg * Math.PI / 180;
+export const toDegrees = (rad) => rad * 180 / Math.PI;
+
+export const azimuth = (longitude1, latitude1, longitude2, latitude2) =>{
+ const [lon1, lat1, lon2, lat2] = [longitude1, latitude1, longitude2, latitude2].map(toRadians);
+ const _deltaLon = Math.abs(lon2 - lon1);
+ const PI = Math.PI;
+ const deltaLon = (_deltaLon) <= PI ? _deltaLon : 2*PI - _deltaLon;
+ const q = (Math.tan(PI/4 + lat2/2))/(Math.tan(PI/4 + lat1/2));
+ const ln = 1/Math.log(q);
+ const alpha = Math.atan( deltaLon*ln ) * 180/PI; // degree!
+ const eastCourse = _deltaLon <= PI;
+ const direction = ( lon2>=lon1 && eastCourse) || (lon2<lon1 && !eastCourse);
+ if(lat2 > lat1 && direction) {
+ return alpha;
+ } else if(lat2 < lat1 && direction) {
+ return 180 - alpha;
+ } else if(lat2 < lat1 && !direction) {
+ return 180 + alpha;
+ } else if(lat2 > lat1 && !direction) {
+ return 360 - alpha;
+ }
+ if(lat2 === lat1) {
+ return direction ? 90 : 270;
+ }
+}
+
diff --git a/02-0-kurswinkel-benutzer-interactive/tangente.js b/02-0-kurswinkel-benutzer-interactive/tangente.js
index 1978e4e..9d68024 100644
--- a/02-0-kurswinkel-benutzer-interactive/tangente.js
+++ b/02-0-kurswinkel-benutzer-interactive/tangente.js
@@ -1,3 +1,5 @@
+import {azimuth} from "./sphere-calculator.js";
+
const toXYZ = (longitude, latitude) => {
const lon = (longitude * Math.PI) / 180;
const lat = (latitude * Math.PI) / 180;
@@ -40,6 +42,11 @@
return `${ lon.toFixed(1) }°|${ lat.toFixed(1)}°`;
}
+const solveProjectionPoint = (startX, startY, startZ, targetX, targetY, targetZ) => {
+ const t = targetX*targetX + targetY*targetY + targetZ*targetZ;
+ const k = -(startX*targetX + startY*targetY + startZ*targetZ - t) / t;
+ return [targetX + k * startX, targetY + k*startY, targetZ + k*startZ]
+}
// board commands
const RADIUS = 6;
@@ -70,6 +77,14 @@
{name: "Ziel LAT.", precision: 2}
);
+const displayAzimuth = () => {
+ const a = azimuth(startLongitude.Value(), startLatitude.Value(),
+ targetLongitude.Value(), targetLatitude.Value()
+ );
+ return `Kurswinkel: ${a.toFixed(2)}°`;
+}
+const azimuthText = board.create('text', [8,6, displayAzimuth], {});
+
const view = board.create('view3d',
[[-10, -10], [19, 19],
@@ -86,7 +101,9 @@
}
);
-const origin = view.create('point3d', [0,0,0], {name:"O", fixed: true});
+const origin = view.create('point3d', [0,0,0],
+ {name:"O", fixed: true, highlight: false, showInfobox: false,}
+);
const earth = view.create('sphere3d', [origin, RADIUS]);
const NORTH_POLE = [0, 90];
@@ -118,11 +135,17 @@
const targetPoint = view.create('point3d',
() => [...toXYZ(targetLongitude.Value(), targetLatitude.Value())],
- {id: 'T', fixed: true, highlight: true, showInfobox: false,
-});
+ {id: 'T', fixed: true, highlight: true, showInfobox: false,}
+);
targetPoint.element2D.label.setText( () => "Ziel: " +
toGeographicLabel( ...toLonLat(targetPoint.X(), targetPoint.Y(), targetPoint.Z() ) ) );
-
+const projectionPoint = view.create('point3d',
+ () => solveProjectionPoint(
+ startPoint.X(), startPoint.Y(), startPoint.Z(),
+ targetPoint.X(), targetPoint.Y(), targetPoint.Z()
+ ),
+ {id: 'P', name:"Prj. Ziel", fixed: true, highlight: true, showInfobox: false, strokeColor:"black", fillColor: "gray"}
+);
const routeNormalVector = () => {
return normalVector(
@@ -139,7 +162,8 @@
const projectionDirectionVector = () => [startPoint.X(), startPoint.Y(), startPoint.Z()];
const projectionLine = view.create('line3d',
- [targetPoint, projectionDirectionVector, [0, Infinity] ]
+ [targetPoint, projectionDirectionVector, [0, Infinity]],
+ {strokeColor: "gray", dash: 2, strokeWidth: 1}
);
--
Gitblit v1.10.0