From 25064146c744dc9adb74f32c4b8cb2aba23f6666 Mon Sep 17 00:00:00 2001
From: Hong-Phuc Bui <hong-phuc.bui@htwsaar.de>
Date: Sun, 18 Oct 2026 01:30:40 +0200
Subject: [PATCH] Interactive UI
---
02-0-kurswinkel-benutzer-interactive/.gitignore | 0
02-0-kurswinkel-benutzer-interactive/deploy.sh | 23 +++++
02-0-kurswinkel-benutzer-interactive/tangente.js | 151 +++++++++++++++++++++++++++++++++++++
02-0-kurswinkel-benutzer-interactive/index.html | 28 +++++++
02-0-kurswinkel-benutzer-interactive/common.css | 27 ++++++
02-0-kurswinkel-benutzer-interactive/README.md | 11 ++
6 files changed, 240 insertions(+), 0 deletions(-)
diff --git a/02-0-kurswinkel-benutzer-interactive/.gitignore b/02-0-kurswinkel-benutzer-interactive/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/.gitignore
diff --git a/02-0-kurswinkel-benutzer-interactive/README.md b/02-0-kurswinkel-benutzer-interactive/README.md
new file mode 100644
index 0000000..247f38d
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/README.md
@@ -0,0 +1,11 @@
+Lernziel:
+
+* HTTP-Server
+* Syntax von URLs (=Bestandteil eines URL)
+* Relative/absolute Pfade
+
+Task:
+
+* Identifizieren Sie alle URL in der Datei `index.html`
+* Identifizieren Sie die Bestandteile von einer URL.
+ Welcher Bestandteil einer URL haben wir in eine ULR in `index.html` verwendet?
\ No newline at end of file
diff --git a/02-0-kurswinkel-benutzer-interactive/common.css b/02-0-kurswinkel-benutzer-interactive/common.css
new file mode 100644
index 0000000..877a2f5
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/common.css
@@ -0,0 +1,27 @@
+html {
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
+}
+
+h1 {
+ font-size: large;
+}
+
+.container {
+ display: flex;
+ gap: 20px;
+ border: 0;
+ margin: 0;
+ padding: 0;
+}
+
+.column {
+ flex: 1;
+}
+
+@media (max-width: 600px) {
+ .container {
+ flex-direction: column;
+ }
+}
+
+
diff --git a/02-0-kurswinkel-benutzer-interactive/deploy.sh b/02-0-kurswinkel-benutzer-interactive/deploy.sh
new file mode 100755
index 0000000..4236c51
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/deploy.sh
@@ -0,0 +1,23 @@
+sync_git() {
+ SRC=$1
+ DEST=$2
+ echo $SRC
+ echo $DEST
+ mkdir -p $DEST
+ ls $DEST
+ rsync -aHAXxv --numeric-ids --delete --progress --exclude='.git' \
+ --exclude='.gitignore' \
+ --exclude='*/target' \
+ --exclude='.idea' \
+ --exclude='deploy.sh' \
+ --exclude='.venv' \
+ --exclude='site' \
+ $SRC $DEST
+
+ git -C $DEST add .
+ git -C $DEST commit -a -m "Commit Datum: $(date --iso-8601=seconds)"
+ git -C $DEST push origin main
+
+}
+
+sync_git ./ ../../codeberg.page/kurswinkel/
\ No newline at end of file
diff --git a/02-0-kurswinkel-benutzer-interactive/index.html b/02-0-kurswinkel-benutzer-interactive/index.html
new file mode 100644
index 0000000..8d0a2f5
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/index.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Index</title>
+ <script src="https://cdn.jsdelivr.net/npm/jsxgraph/distrib/jsxgraphcore.js"></script>
+ <link rel="stylesheet" type="text/css"
+ href="https://cdn.jsdelivr.net/npm/jsxgraph/distrib/jsxgraph.css">
+ <link rel="stylesheet" type="text/css"
+ href="./common.css"/>
+</head>
+<body>
+<h1>Fluglinien mit Kurswinkel</h1>
+<div class="container">
+ <div class="column">
+ <div id="box" class="jxgbox" style="width:800px; aspect-ratio: 1/1;"></div>
+ </div>
+ <div class="column">
+ <div class="explain">
+ <p>Drehe die Kugel so, dass der Startpunkt sich mit dem Punkt O deckt.
+ (el = 1.05, az = 2.09)</p>
+ <p>Der Winkel Nordpol-Startpunkt-Ziel ist der gesuchte Kurswinkel in der wahren Gestalt.</p>
+ </div>
+ </div>
+</div>
+<script src="tangente.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/02-0-kurswinkel-benutzer-interactive/tangente.js b/02-0-kurswinkel-benutzer-interactive/tangente.js
new file mode 100644
index 0000000..1978e4e
--- /dev/null
+++ b/02-0-kurswinkel-benutzer-interactive/tangente.js
@@ -0,0 +1,151 @@
+const toXYZ = (longitude, latitude) => {
+ const lon = (longitude * Math.PI) / 180;
+ const lat = (latitude * Math.PI) / 180;
+
+ const x = RADIUS * Math.cos(lat) * Math.cos(lon);
+ const y = RADIUS * Math.cos(lat) * Math.sin(lon);
+ const z = RADIUS * Math.sin(lat);
+
+ return [x, y, z];
+};
+
+const toHomogeneousXYZ = (longitude, latitude) => {
+ const xyz = toXYZ(longitude, latitude);
+ xyz.unshift(0);
+ return xyz;
+}
+
+const toLonLat = (x, y, z) => {
+ const R = Math.hypot(x, y, z);
+ const longitude = (Math.atan2(y, x) * 180) / Math.PI; // [-180, 180]
+ const latitude = (Math.asin(Math.max(-1, Math.min(1, z / R))) * 180) / Math.PI; // [-90, 90]
+ return [longitude, latitude];
+};
+
+
+const cross = (a, b) => [
+ a[1] * b[2] - a[2] * b[1],
+ a[2] * b[0] - a[0] * b[2],
+ a[0] * b[1] - a[1] * b[0],
+];
+
+const normalVector = ([lon1, lat1], [lon2, lat2]) => {
+ const n = cross(toXYZ(lon1, lat1), toXYZ(lon2, lat2));
+ n.unshift(0);
+ return n;
+}
+
+
+const toGeographicLabel = (lon, lat) => {
+ return `${ lon.toFixed(1) }°|${ lat.toFixed(1)}°`;
+}
+
+
+// board commands
+const RADIUS = 6;
+const BOARD_SIZE = 14;
+
+const board = JXG.JSXGraph.initBoard(
+ 'box', {
+ boundingbox: [-BOARD_SIZE, BOARD_SIZE, BOARD_SIZE, -BOARD_SIZE],
+ //axis: true
+ });
+const bound = [-10, 10];
+
+const startLongitude = board.create("slider",
+ [[-2, 10], [9, 10], [-180, -30, 180]],
+ {name: "Start LON.", precision: 2}
+);
+const startLatitude = board.create("slider",
+ [[-2, 9], [9, 9], [-90, 60, 90]],
+ {name: "Start LAT.", precision: 2}
+);
+
+const targetLongitude = board.create("slider",
+ [[-2, -10], [9, -10], [-180, 120, 180]],
+ {name: "Ziel LON.", precision: 2}
+);
+const targetLatitude = board.create("slider",
+ [[-2, -11], [9, -11], [-90, 0, 90]],
+ {name: "Ziel LAT.", precision: 2}
+);
+
+
+const view = board.create('view3d',
+ [[-10, -10], [19, 19],
+ [bound, bound, bound]],
+ {
+ axesPosition: 'none',
+ xPlaneRear: {visible: false},
+ yPlaneRear: {visible: false},
+ zPlaneRear: {visible: false},
+ //
+ az: { slider: { visible: true } },
+ el: { slider: { visible: true } },
+ bank: { slider: { visible: true } },
+ }
+);
+
+const origin = view.create('point3d', [0,0,0], {name:"O", fixed: true});
+const earth = view.create('sphere3d', [origin, RADIUS]);
+
+const NORTH_POLE = [0, 90];
+const north_pole = toHomogeneousXYZ(...NORTH_POLE);
+const equator = view.create('circle3d', [origin, north_pole, RADIUS], {
+ strokeColor:"red", fixed: true
+});
+const northPoint = view.create('point3d', [toXYZ(...NORTH_POLE)], {
+ name:'N', fixed: true, highlight: true, showInfobox: false,
+});
+
+
+const prime_meridian_normal = toHomogeneousXYZ(90, 0)
+const prime_meridian = view.create('circle3d', [origin, prime_meridian_normal, RADIUS], {
+ strokeColor: "blue",
+ fixed: true
+});
+
+const startPoint = view.create('point3d',
+ () => [...toXYZ(startLongitude.Value(), startLatitude.Value())],
+ {id:'S', fixed: true, highlight: true, showInfobox: false,}
+);
+startPoint.element2D.label.setText( () => "Start: " +
+ toGeographicLabel( ...toLonLat(startPoint.X(),startPoint.Y(), startPoint.Z() ) ) );
+const tangentPlan = view.create('circle3d',
+ [startPoint, () => [0, startPoint.X(), startPoint.Y(), startPoint.Z()], RADIUS],
+ {fillColor:"red", fillOpacity:0.25, fixed: true}
+);
+
+const targetPoint = view.create('point3d',
+ () => [...toXYZ(targetLongitude.Value(), targetLatitude.Value())],
+ {id: 'T', fixed: true, highlight: true, showInfobox: false,
+});
+targetPoint.element2D.label.setText( () => "Ziel: " +
+ toGeographicLabel( ...toLonLat(targetPoint.X(), targetPoint.Y(), targetPoint.Z() ) ) );
+
+
+const routeNormalVector = () => {
+ return normalVector(
+ [startLongitude.Value(), startLatitude.Value()],
+ [targetLongitude.Value(), targetLatitude.Value()]
+ );
+};
+const route = view.create('circle3d', [origin, routeNormalVector, RADIUS] );
+
+
+const meridianNormalVector = () => normalVector([startLongitude.Value(), startLatitude.Value()], NORTH_POLE);
+const meridianPlan = view.create('circle3d', [origin, meridianNormalVector, RADIUS]);
+const ot = view.create('line3d', [origin, startPoint])
+
+const projectionDirectionVector = () => [startPoint.X(), startPoint.Y(), startPoint.Z()];
+const projectionLine = view.create('line3d',
+ [targetPoint, projectionDirectionVector, [0, Infinity] ]
+);
+
+
+
+
+
+
+
+
--
Gitblit v1.10.0