| New file |
| | |
| | | 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] ] |
| | | ); |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | |