Hong-Phuc Bui
2026-10-18 25064146c744dc9adb74f32c4b8cb2aba23f6666
Interactive UI
6 files added
240 ■■■■■ changed files
02-0-kurswinkel-benutzer-interactive/.gitignore patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/README.md 11 ●●●●● patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/common.css 27 ●●●●● patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/deploy.sh 23 ●●●●● patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/index.html 28 ●●●●● patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/tangente.js 151 ●●●●● patch | view | raw | blame | history
02-0-kurswinkel-benutzer-interactive/.gitignore
02-0-kurswinkel-benutzer-interactive/README.md
New file
@@ -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?
02-0-kurswinkel-benutzer-interactive/common.css
New file
@@ -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;
    }
}
02-0-kurswinkel-benutzer-interactive/deploy.sh
New file
@@ -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/
02-0-kurswinkel-benutzer-interactive/index.html
New file
@@ -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>
02-0-kurswinkel-benutzer-interactive/tangente.js
New file
@@ -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] ]
);