@bubo-squared/gyroview 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +216 -0
- package/dist/define.d.ts +3 -0
- package/dist/define.js +4 -0
- package/dist/index.d.ts +959 -0
- package/dist/index.js +13 -0
- package/dist/packages/adapters/blob/src/BlobRandomAccessSource.js +37 -0
- package/dist/packages/adapters/blob/src/index.js +2 -0
- package/dist/packages/adapters/fetch/src/HttpRangeSource.js +194 -0
- package/dist/packages/adapters/fetch/src/HttpResourceLocator.js +35 -0
- package/dist/packages/adapters/fetch/src/httpRequest.js +126 -0
- package/dist/packages/adapters/fetch/src/index.js +3 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyAudioSegments.js +133 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyDemuxer.js +63 -0
- package/dist/packages/adapters/mediabunny/src/MediabunnyVideoTrackReader.js +84 -0
- package/dist/packages/adapters/mediabunny/src/SegmentChannel.js +84 -0
- package/dist/packages/adapters/mediabunny/src/bufferSources.js +12 -0
- package/dist/packages/adapters/mediabunny/src/index.js +2 -0
- package/dist/packages/adapters/mse-audio/src/MediaSourceAudioClock.js +139 -0
- package/dist/packages/adapters/mse-audio/src/SourceBufferFeeder.js +174 -0
- package/dist/packages/adapters/mse-audio/src/events.js +26 -0
- package/dist/packages/adapters/mse-audio/src/index.js +2 -0
- package/dist/packages/adapters/mse-audio/src/mediaSourceSupport.js +82 -0
- package/dist/packages/adapters/three/src/ThreeFrameRenderer.js +274 -0
- package/dist/packages/adapters/three/src/compileAndProve.js +14 -0
- package/dist/packages/adapters/three/src/fullscreenPass.js +52 -0
- package/dist/packages/adapters/three/src/index.js +2 -0
- package/dist/packages/adapters/three/src/lensTextures.js +25 -0
- package/dist/packages/adapters/three/src/pictureMaterials.js +44 -0
- package/dist/packages/adapters/three/src/renderInto.js +15 -0
- package/dist/packages/adapters/three/src/rendererUniforms.js +157 -0
- package/dist/packages/adapters/three/src/samplingStrategies.js +26 -0
- package/dist/packages/adapters/three/src/seamMeter/SeamMeterPass.js +79 -0
- package/dist/packages/adapters/three/src/seamMeter/rowMeans.js +38 -0
- package/dist/packages/adapters/three/src/shaderPrograms.js +66 -0
- package/dist/packages/adapters/three/src/shaders/analysis.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/equirectangularRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fixedJoin.js +4 -0
- package/dist/packages/adapters/three/src/shaders/fullscreen.vert.js +4 -0
- package/dist/packages/adapters/three/src/shaders/header.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensModels.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensSampling.js +4 -0
- package/dist/packages/adapters/three/src/shaders/lensTextures.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rawLenses.frag.js +4 -0
- package/dist/packages/adapters/three/src/shaders/rectilinearRays.js +4 -0
- package/dist/packages/adapters/three/src/shaders/screenAreas.js +4 -0
- package/dist/packages/adapters/three/src/shaders/stitch.frag.js +4 -0
- package/dist/packages/adapters/three/src/webglRenderer.js +37 -0
- package/dist/packages/adapters/webcodecs/src/WebCodecsVideoDecoderPort.js +135 -0
- package/dist/packages/adapters/webcodecs/src/index.js +2 -0
- package/dist/packages/core/src/application/gainMatching/GainMatching.js +66 -0
- package/dist/packages/core/src/application/gainMatching/GainMatchingFrameSink.js +62 -0
- package/dist/packages/core/src/application/playback/ClockWatch.js +76 -0
- package/dist/packages/core/src/application/playback/DecodePipeline.js +188 -0
- package/dist/packages/core/src/application/playback/DecodeRun.js +93 -0
- package/dist/packages/core/src/application/playback/FramePairQueue.js +93 -0
- package/dist/packages/core/src/application/playback/FramePairer.js +60 -0
- package/dist/packages/core/src/application/playback/PlaybackSession.js +417 -0
- package/dist/packages/core/src/application/playback/SeekOrder.js +36 -0
- package/dist/packages/core/src/application/playback/SessionLifecycle.js +42 -0
- package/dist/packages/core/src/application/playback/StartGate.js +55 -0
- package/dist/packages/core/src/application/playback/TimeUpdates.js +31 -0
- package/dist/packages/core/src/application/playback/WallClock.js +46 -0
- package/dist/packages/core/src/application/playback/keyframeTimeAt.js +15 -0
- package/dist/packages/core/src/application/playback/probeDecoding.js +168 -0
- package/dist/packages/core/src/application/recording/frameTimesOf.js +22 -0
- package/dist/packages/core/src/application/recording/inspectLayout.js +21 -0
- package/dist/packages/core/src/application/recording/inspectRecording.js +124 -0
- package/dist/packages/core/src/application/recording/locateOtherLensFile.js +18 -0
- package/dist/packages/core/src/application/recording/motionOf.js +39 -0
- package/dist/packages/core/src/application/recording/readRecording.js +31 -0
- package/dist/packages/core/src/application/recording/timeRecording.js +57 -0
- package/dist/packages/core/src/application/stabilization/StabilizingFrameSink.js +31 -0
- package/dist/packages/core/src/domain/format/boxes/BoxLayout.js +7 -0
- package/dist/packages/core/src/domain/format/boxes/boxConstants.js +16 -0
- package/dist/packages/core/src/domain/format/boxes/scanBoxes.js +59 -0
- package/dist/packages/core/src/domain/format/calibration/CalibrationVersion.js +13 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/CalibrationStringLayout.js +34 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/LegacyCalibrationLayout.js +44 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/MeiCalibrationLayout.js +45 -0
- package/dist/packages/core/src/domain/format/calibration/layouts/PolynomialCalibrationLayout.js +44 -0
- package/dist/packages/core/src/domain/format/calibration/offsetTokens.js +65 -0
- package/dist/packages/core/src/domain/format/calibration/parseOffsetString.js +52 -0
- package/dist/packages/core/src/domain/format/calibration/selectCalibration.js +46 -0
- package/dist/packages/core/src/domain/format/captureOrigin.js +20 -0
- package/dist/packages/core/src/domain/format/constants.js +33 -0
- package/dist/packages/core/src/domain/format/info/infoFields.js +110 -0
- package/dist/packages/core/src/domain/format/info/parseInfoRecord.js +90 -0
- package/dist/packages/core/src/domain/format/info/readInfoRecord.js +14 -0
- package/dist/packages/core/src/domain/format/layout/detectLensLayout.js +122 -0
- package/dist/packages/core/src/domain/format/naming/RecordingFileName.js +67 -0
- package/dist/packages/core/src/domain/format/records/TrailerRecords.js +83 -0
- package/dist/packages/core/src/domain/format/records/exposure/parseExposureRecord.js +48 -0
- package/dist/packages/core/src/domain/format/records/gyro/FloatGyroSampleLayout.js +39 -0
- package/dist/packages/core/src/domain/format/records/gyro/RawGyroSampleLayout.js +55 -0
- package/dist/packages/core/src/domain/format/records/gyro/SampleColumns.js +82 -0
- package/dist/packages/core/src/domain/format/records/gyro/gyroLayouts.js +12 -0
- package/dist/packages/core/src/domain/format/records/gyro/parseGyroRecord.js +129 -0
- package/dist/packages/core/src/domain/format/trailer/RecordHeader.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/Trailer.js +23 -0
- package/dist/packages/core/src/domain/format/trailer/TrailerFooter.js +27 -0
- package/dist/packages/core/src/domain/format/trailer/locateRecords.js +80 -0
- package/dist/packages/core/src/domain/format/trailer/parseRecordIndex.js +28 -0
- package/dist/packages/core/src/domain/format/trailer/readTrailer.js +32 -0
- package/dist/packages/core/src/domain/motion/exposure/ExposureRecord.js +53 -0
- package/dist/packages/core/src/domain/motion/gyro/GyroTrack.js +60 -0
- package/dist/packages/core/src/domain/motion/gyro/medianStep.js +11 -0
- package/dist/packages/core/src/domain/motion/gyro/repairedTimeline.js +101 -0
- package/dist/packages/core/src/domain/motion/imu/ImuFrame.js +112 -0
- package/dist/packages/core/src/domain/motion/orientation/OrientationTrack.js +146 -0
- package/dist/packages/core/src/domain/motion/orientation/estimateGyroBias.js +64 -0
- package/dist/packages/core/src/domain/motion/orientation/gravity.js +97 -0
- package/dist/packages/core/src/domain/motion/stabilization/Stabilizer.js +13 -0
- package/dist/packages/core/src/domain/motion/stabilization/stabilizers.js +86 -0
- package/dist/packages/core/src/domain/motion/timing/CaptureClock.js +41 -0
- package/dist/packages/core/src/domain/motion/timing/FrameTimes.js +92 -0
- package/dist/packages/core/src/domain/motion/timing/frameTimeSources.js +61 -0
- package/dist/packages/core/src/domain/motion/timing/resolveFrameTimes.js +68 -0
- package/dist/packages/core/src/domain/optics/EquidistantModel.js +39 -0
- package/dist/packages/core/src/domain/optics/MeiModel.js +45 -0
- package/dist/packages/core/src/domain/optics/PolynomialModel.js +44 -0
- package/dist/packages/core/src/domain/optics/gainMatch.js +62 -0
- package/dist/packages/core/src/domain/optics/lensGeometry.js +18 -0
- package/dist/packages/core/src/domain/optics/lensPose.js +35 -0
- package/dist/packages/core/src/domain/optics/opticsConstants.js +5 -0
- package/dist/packages/core/src/domain/playback/PlayerState.js +83 -0
- package/dist/packages/core/src/domain/stitching/LensLayout.js +21 -0
- package/dist/packages/core/src/domain/stitching/StitchingSetup.js +78 -0
- package/dist/packages/core/src/domain/stitching/seamDisparity.js +4 -0
- package/dist/packages/core/src/domain/stitching/seamDisparityField.js +5 -0
- package/dist/packages/core/src/domain/stitching/seamJoin.js +16 -0
- package/dist/packages/core/src/domain/stitching/seamMismatch.js +1 -0
- package/dist/packages/core/src/domain/stitching/seamRing.js +4 -0
- package/dist/packages/core/src/domain/stitching/seamStrip.js +26 -0
- package/dist/packages/core/src/domain/view/Framing.js +31 -0
- package/dist/packages/core/src/domain/view/PictureQuality.js +26 -0
- package/dist/packages/core/src/domain/view/ViewMode.js +9 -0
- package/dist/packages/core/src/domain/view/ViewState.js +48 -0
- package/dist/packages/core/src/domain/view/lensTilesView.js +63 -0
- package/dist/packages/core/src/domain/view/magnification.js +103 -0
- package/dist/packages/core/src/domain/view/normalView.js +29 -0
- package/dist/packages/core/src/domain/view/panoramaView.js +99 -0
- package/dist/packages/core/src/domain/view/rectilinear.js +40 -0
- package/dist/packages/core/src/domain/view/screenLayout.js +113 -0
- package/dist/packages/core/src/domain/view/viewGestures.js +98 -0
- package/dist/packages/core/src/domain/view/viewModes.js +14 -0
- package/dist/packages/core/src/index.js +59 -0
- package/dist/packages/core/src/ports/FramePair.js +6 -0
- package/dist/packages/core/src/shared/async/Deferred.js +42 -0
- package/dist/packages/core/src/shared/async/RunStop.js +40 -0
- package/dist/packages/core/src/shared/binary/ByteRange.js +38 -0
- package/dist/packages/core/src/shared/binary/ByteReader.js +77 -0
- package/dist/packages/core/src/shared/errors/GyroViewError.js +81 -0
- package/dist/packages/core/src/shared/errors/reportLater.js +11 -0
- package/dist/packages/core/src/shared/events/Outbox.js +68 -0
- package/dist/packages/core/src/shared/events/TypedEmitter.js +55 -0
- package/dist/packages/core/src/shared/keysOf.js +10 -0
- package/dist/packages/core/src/shared/lazy.js +11 -0
- package/dist/packages/core/src/shared/math/Matrix3.js +93 -0
- package/dist/packages/core/src/shared/math/Quaternion.js +133 -0
- package/dist/packages/core/src/shared/math/Vector3.js +54 -0
- package/dist/packages/core/src/shared/math/clamp.js +9 -0
- package/dist/packages/core/src/shared/protobuf/ProtobufMessage.js +157 -0
- package/dist/packages/core/src/shared/text/urlPath.js +31 -0
- package/dist/packages/core/src/shared/text/utf8.js +12 -0
- package/dist/packages/core/src/shared/units/angle.js +22 -0
- package/dist/packages/core/src/shared/units/time.js +13 -0
- package/dist/packages/player/src/PlayerSource.js +15 -0
- package/dist/packages/player/src/browserPlayer.js +18 -0
- package/dist/packages/player/src/choices.js +25 -0
- package/dist/packages/player/src/composition/Disposables.js +31 -0
- package/dist/packages/player/src/composition/browserPorts.js +46 -0
- package/dist/packages/player/src/composition/buildPipeline.js +153 -0
- package/dist/packages/player/src/composition/ensureDecodable.js +63 -0
- package/dist/packages/player/src/composition/openInputs.js +179 -0
- package/dist/packages/player/src/composition/openRecording.js +42 -0
- package/dist/packages/player/src/controls/ChoiceMenu.js +113 -0
- package/dist/packages/player/src/controls/PictureMenus.js +47 -0
- package/dist/packages/player/src/controls/SeekBar.js +125 -0
- package/dist/packages/player/src/controls/SoundControls.js +41 -0
- package/dist/packages/player/src/controls/TransportButtons.js +31 -0
- package/dist/packages/player/src/controls/ViewGestures.js +136 -0
- package/dist/packages/player/src/controls/Wording.js +78 -0
- package/dist/packages/player/src/controls/controlParts.js +36 -0
- package/dist/packages/player/src/controls/controls.js +4 -0
- package/dist/packages/player/src/controls/controlsBar.js +23 -0
- package/dist/packages/player/src/controls/controlsMarkup.js +85 -0
- package/dist/packages/player/src/controls/customControls.js +59 -0
- package/dist/packages/player/src/controls/formatTime.js +19 -0
- package/dist/packages/player/src/controls/icons.js +40 -0
- package/dist/packages/player/src/controls/keyboard.js +118 -0
- package/dist/packages/player/src/controls/messages.js +94 -0
- package/dist/packages/player/src/controls/parseMarkup.js +31 -0
- package/dist/packages/player/src/controls/viewButtons.js +14 -0
- package/dist/packages/player/src/element/ElementLoads.js +119 -0
- package/dist/packages/player/src/element/FullscreenToggle.js +39 -0
- package/dist/packages/player/src/element/GyroViewElement.js +313 -0
- package/dist/packages/player/src/element/IdleWatcher.js +71 -0
- package/dist/packages/player/src/element/TypedEventElement.js +22 -0
- package/dist/packages/player/src/element/accessibleRegion.js +19 -0
- package/dist/packages/player/src/element/attributeNames.js +51 -0
- package/dist/packages/player/src/element/attributes.js +101 -0
- package/dist/packages/player/src/element/defineGyroView.js +21 -0
- package/dist/packages/player/src/element/earlyProperties.js +36 -0
- package/dist/packages/player/src/element/elementSource.js +23 -0
- package/dist/packages/player/src/element/liveSettings.js +138 -0
- package/dist/packages/player/src/element/playbackAttributes.js +53 -0
- package/dist/packages/player/src/element/playerEventMirror.js +33 -0
- package/dist/packages/player/src/element/reflectedProperties.js +78 -0
- package/dist/packages/player/src/element/styles.js +4 -0
- package/dist/packages/player/src/element/template.js +47 -0
- package/dist/packages/player/src/index.js +10 -0
- package/dist/packages/player/src/inspectRecording.js +22 -0
- package/dist/packages/player/src/player/DrawingBufferFit.js +77 -0
- package/dist/packages/player/src/player/FrameLoop.js +40 -0
- package/dist/packages/player/src/player/PictureSettings.js +60 -0
- package/dist/packages/player/src/player/Player.js +377 -0
- package/dist/packages/player/src/player/PlayerEvents.js +28 -0
- package/dist/packages/player/src/player/PlayerPhase.js +21 -0
- package/dist/packages/player/src/player/PlayerSound.js +61 -0
- package/dist/packages/player/src/player/PlayerView.js +113 -0
- package/dist/packages/player/src/player/SessionRelay.js +66 -0
- package/dist/packages/player/src/player/ensureFinite.js +26 -0
- package/dist/packages/player/src/player/loadRecording.js +65 -0
- package/dist/packages/player/src/player/transportEvents.js +29 -0
- package/dist/standalone.js +4919 -0
- package/package.json +73 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { degrees } from "../../shared/units/angle.js";
|
|
2
|
+
import { DEFAULT_VIEW, viewRotation } from "./ViewState.js";
|
|
3
|
+
import { WHOLE_SCREEN, aspectOf } from "./screenLayout.js";
|
|
4
|
+
import { lookAt, panView, zoomViewAt } from "./viewGestures.js";
|
|
5
|
+
import { withView } from "./Framing.js";
|
|
6
|
+
//#region ../../packages/core/src/domain/view/normalView.ts
|
|
7
|
+
/**
|
|
8
|
+
* A rectilinear window into the stitched sphere over the whole viewport, turned by drags and
|
|
9
|
+
* arrows and zoomed by its field of view toward the pointer.
|
|
10
|
+
*/
|
|
11
|
+
var NORMAL_VIEW = {
|
|
12
|
+
isStabilized: true,
|
|
13
|
+
canPan: () => true,
|
|
14
|
+
pan: (framing, delta, { viewport }) => withView(framing, panView(framing.view, delta, viewport.width)),
|
|
15
|
+
turn: (framing, turn) => {
|
|
16
|
+
const { view } = framing;
|
|
17
|
+
return withView(framing, lookAt(view, degrees(view.yaw + turn.yaw), degrees(view.pitch + turn.pitch)));
|
|
18
|
+
},
|
|
19
|
+
zoom: (framing, zoom, { viewport }) => withView(framing, zoomViewAt(framing.view, zoom, aspectOf(viewport))),
|
|
20
|
+
reset: (framing) => withView(framing, DEFAULT_VIEW),
|
|
21
|
+
picture: ({ view }) => ({
|
|
22
|
+
kind: "rectilinear",
|
|
23
|
+
rotation: viewRotation(view),
|
|
24
|
+
fieldOfView: view.fieldOfView,
|
|
25
|
+
area: WHOLE_SCREEN
|
|
26
|
+
})
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { NORMAL_VIEW };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { FULL_TURN, HALF_TURN, degrees, degreesToRadians } from "../../shared/units/angle.js";
|
|
2
|
+
import { rotationAboutY } from "../../shared/math/Matrix3.js";
|
|
3
|
+
import { HALF, aspectOf, fittedRectangle, pictureAt, shiftOf } from "./screenLayout.js";
|
|
4
|
+
import { lookAt } from "./viewGestures.js";
|
|
5
|
+
import { FITTED, clampMagnification, magnifiedArea, magnifyAt, panMagnification, pictureShiftOf } from "./magnification.js";
|
|
6
|
+
import { withView } from "./Framing.js";
|
|
7
|
+
//#region ../../packages/core/src/domain/view/panoramaView.ts
|
|
8
|
+
/**
|
|
9
|
+
* An equirectangular picture spans a full turn across for a half turn from top to bottom.
|
|
10
|
+
*/
|
|
11
|
+
var EQUIRECTANGULAR_ASPECT = 2;
|
|
12
|
+
function levelled(fitted, magnification) {
|
|
13
|
+
const clamped = clampMagnification(fitted, magnification);
|
|
14
|
+
return {
|
|
15
|
+
...clamped,
|
|
16
|
+
centre: {
|
|
17
|
+
x: HALF,
|
|
18
|
+
y: clamped.centre.y
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function shownPanorama(framing, { viewport }) {
|
|
23
|
+
const fitted = fittedRectangle(EQUIRECTANGULAR_ASPECT, aspectOf(viewport));
|
|
24
|
+
return {
|
|
25
|
+
fitted,
|
|
26
|
+
panorama: levelled(fitted, framing.panorama)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function withPanorama(framing, panorama, yawDelta) {
|
|
30
|
+
const { view } = framing;
|
|
31
|
+
return {
|
|
32
|
+
...withView(framing, lookAt(view, degrees(view.yaw + yawDelta), view.pitch)),
|
|
33
|
+
panorama
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A drag turns the panorama a whole turn per width of the picture as shown, and moves it up and
|
|
38
|
+
* down once it is taller than the viewport.
|
|
39
|
+
*/
|
|
40
|
+
function panPanorama(framing, delta, context) {
|
|
41
|
+
const { fitted, panorama } = shownPanorama(framing, context);
|
|
42
|
+
const shift = shiftOf(delta, context.viewport);
|
|
43
|
+
const moved = panMagnification(fitted, panorama, {
|
|
44
|
+
across: 0,
|
|
45
|
+
down: shift.down
|
|
46
|
+
});
|
|
47
|
+
const turned = pictureShiftOf(fitted, panorama, shift).across * FULL_TURN;
|
|
48
|
+
return withPanorama(framing, moved, degrees(-turned));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Sideways the arrows turn it; up and down they move a panorama taller than the viewport by the
|
|
52
|
+
* angle they name, the picture spanning a half turn from top to bottom.
|
|
53
|
+
*/
|
|
54
|
+
function turnPanorama(framing, turn, context) {
|
|
55
|
+
const { fitted, panorama } = shownPanorama(framing, context);
|
|
56
|
+
return withPanorama(framing, levelled(fitted, {
|
|
57
|
+
...panorama,
|
|
58
|
+
centre: {
|
|
59
|
+
x: HALF,
|
|
60
|
+
y: panorama.centre.y - turn.pitch / HALF_TURN
|
|
61
|
+
}
|
|
62
|
+
}), turn.yaw);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Keeps the point under the pointer where it is: up and down by the magnification's own rule,
|
|
66
|
+
* sideways by turning the panorama as far as the pointer's longitude moved.
|
|
67
|
+
*/
|
|
68
|
+
function zoomPanorama(framing, zoom, context) {
|
|
69
|
+
const { fitted, panorama } = shownPanorama(framing, context);
|
|
70
|
+
const zoomed = levelled(fitted, magnifyAt(fitted, panorama, zoom));
|
|
71
|
+
const before = pictureAt(magnifiedArea(fitted, panorama), zoom.focus);
|
|
72
|
+
const after = pictureAt(magnifiedArea(fitted, zoomed), zoom.focus);
|
|
73
|
+
return withPanorama(framing, zoomed, degrees((before.x - after.x) * FULL_TURN));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Level, as an exported equirectangular video: the yaw picks the direction at the centre, and the
|
|
77
|
+
* panorama magnifies up to four times and moves up and down within itself.
|
|
78
|
+
*/
|
|
79
|
+
var PANORAMA_VIEW = {
|
|
80
|
+
isStabilized: true,
|
|
81
|
+
canPan: () => true,
|
|
82
|
+
pan: panPanorama,
|
|
83
|
+
turn: turnPanorama,
|
|
84
|
+
zoom: zoomPanorama,
|
|
85
|
+
reset: (framing) => ({
|
|
86
|
+
...withView(framing, lookAt(framing.view, degrees(0), framing.view.pitch)),
|
|
87
|
+
panorama: FITTED
|
|
88
|
+
}),
|
|
89
|
+
picture: (framing, context) => {
|
|
90
|
+
const { fitted, panorama } = shownPanorama(framing, context);
|
|
91
|
+
return {
|
|
92
|
+
kind: "equirectangular",
|
|
93
|
+
rotation: rotationAboutY(degreesToRadians(framing.view.yaw)),
|
|
94
|
+
area: magnifiedArea(fitted, panorama)
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
//#endregion
|
|
99
|
+
export { PANORAMA_VIEW };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { degrees, degreesToRadians } from "../../shared/units/angle.js";
|
|
2
|
+
import { magnitudeOf, scaleVector } from "../../shared/math/Vector3.js";
|
|
3
|
+
import { transformVector } from "../../shared/math/Matrix3.js";
|
|
4
|
+
import { viewRotation } from "./ViewState.js";
|
|
5
|
+
//#region ../../packages/core/src/domain/view/rectilinear.ts
|
|
6
|
+
/**
|
|
7
|
+
* Half the width of the image plane at unit distance, so that a rectilinear picture spans the
|
|
8
|
+
* given horizontal field of view across its width. The renderer's shader takes it as it is.
|
|
9
|
+
*/
|
|
10
|
+
function planeHalfExtentOf(fieldOfView) {
|
|
11
|
+
return Math.tan(degreesToRadians(degrees(fieldOfView / 2)));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Width over height of an area of a viewport with the given aspect.
|
|
15
|
+
*/
|
|
16
|
+
function aspectOfArea(area, viewportAspect) {
|
|
17
|
+
return viewportAspect * area.width / area.height;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The view-space direction (x right, y down, z forward) through a point of a rectilinear picture,
|
|
21
|
+
* given as fractions of the picture; `rectilinearRays.glsl` computes the same for every pixel.
|
|
22
|
+
*/
|
|
23
|
+
function rayThroughPicture(fieldOfView, point, pictureAspect) {
|
|
24
|
+
const halfExtent = planeHalfExtentOf(fieldOfView);
|
|
25
|
+
const plane = [
|
|
26
|
+
(point.x * 2 - 1) * halfExtent,
|
|
27
|
+
(point.y * 2 - 1) * halfExtent / pictureAspect,
|
|
28
|
+
1
|
|
29
|
+
];
|
|
30
|
+
return scaleVector(plane, 1 / magnitudeOf(plane));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The camera body direction seen through a point of the normal view's picture, which fills the
|
|
34
|
+
* viewport, so its points are the viewport's.
|
|
35
|
+
*/
|
|
36
|
+
function directionAt(view, point, viewportAspect) {
|
|
37
|
+
return transformVector(viewRotation(view), rayThroughPicture(view.fieldOfView, point, viewportAspect));
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { aspectOfArea, directionAt, planeHalfExtentOf, rayThroughPicture };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ensureInvariant } from "../../shared/errors/GyroViewError.js";
|
|
2
|
+
//#region ../../packages/core/src/domain/view/screenLayout.ts
|
|
3
|
+
/**
|
|
4
|
+
* Half of a length along one axis, as a fraction of it: the middle of the viewport or of a
|
|
5
|
+
* picture.
|
|
6
|
+
*/
|
|
7
|
+
var HALF = .5;
|
|
8
|
+
var WHOLE_SCREEN = {
|
|
9
|
+
x: 0,
|
|
10
|
+
y: 0,
|
|
11
|
+
width: 1,
|
|
12
|
+
height: 1
|
|
13
|
+
};
|
|
14
|
+
var SCREEN_CENTRE = {
|
|
15
|
+
x: HALF,
|
|
16
|
+
y: HALF
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* An empty viewport counts as one unit wide and high, so no measure of it divides by zero.
|
|
20
|
+
*/
|
|
21
|
+
function measured(length) {
|
|
22
|
+
return Math.max(length, 1);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Width over height.
|
|
26
|
+
*/
|
|
27
|
+
function aspectOf(viewport) {
|
|
28
|
+
return viewport.width / measured(viewport.height);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A drag as fractions of the viewport it happened on.
|
|
32
|
+
*/
|
|
33
|
+
function shiftOf(delta, viewport) {
|
|
34
|
+
return {
|
|
35
|
+
across: delta.x / measured(viewport.width),
|
|
36
|
+
down: delta.y / measured(viewport.height)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The point of a picture shown at a point of the viewport, as fractions of the picture.
|
|
41
|
+
*/
|
|
42
|
+
function pictureAt(area, point) {
|
|
43
|
+
return {
|
|
44
|
+
x: (point.x - area.x) / area.width,
|
|
45
|
+
y: (point.y - area.y) / area.height
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* One square tile per lens, in a row or in a column, whichever gives the larger tiles on this
|
|
50
|
+
* viewport, centred with bars around. Square because every lens layout the player accepts has
|
|
51
|
+
* square lens images: square tracks, or the halves of a 2:1 packed frame.
|
|
52
|
+
*/
|
|
53
|
+
function lensTiles(lensCount, viewportAspect) {
|
|
54
|
+
const row = fittedRectangle(lensCount, viewportAspect);
|
|
55
|
+
const column = fittedRectangle(1 / lensCount, viewportAspect);
|
|
56
|
+
return row.height >= column.height / lensCount ? tilesAcross(row, lensCount) : tilesDown(column, lensCount);
|
|
57
|
+
}
|
|
58
|
+
function tilesAcross(area, count) {
|
|
59
|
+
const width = area.width / count;
|
|
60
|
+
return Array.from({ length: count }, (_unused, index) => ({
|
|
61
|
+
...area,
|
|
62
|
+
x: area.x + index * width,
|
|
63
|
+
width
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
function tilesDown(area, count) {
|
|
67
|
+
const height = area.height / count;
|
|
68
|
+
return Array.from({ length: count }, (_unused, index) => ({
|
|
69
|
+
...area,
|
|
70
|
+
y: area.y + index * height,
|
|
71
|
+
height
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The smallest rectangle holding every one of the given rectangles.
|
|
76
|
+
*/
|
|
77
|
+
function boundsOf(rectangles) {
|
|
78
|
+
ensureInvariant(rectangles.length > 0, "bounds need at least one rectangle");
|
|
79
|
+
const left = Math.min(...rectangles.map((rectangle) => rectangle.x));
|
|
80
|
+
const top = Math.min(...rectangles.map((rectangle) => rectangle.y));
|
|
81
|
+
const right = Math.max(...rectangles.map((rectangle) => rectangle.x + rectangle.width));
|
|
82
|
+
const bottom = Math.max(...rectangles.map((rectangle) => rectangle.y + rectangle.height));
|
|
83
|
+
return {
|
|
84
|
+
x: left,
|
|
85
|
+
y: top,
|
|
86
|
+
width: right - left,
|
|
87
|
+
height: bottom - top
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The largest centred rectangle of the content's shape inside the viewport. Both aspects are
|
|
92
|
+
* width over height; the bars it leaves go above and below wider content, beside narrower.
|
|
93
|
+
*/
|
|
94
|
+
function fittedRectangle(contentAspect, viewportAspect) {
|
|
95
|
+
if (contentAspect >= viewportAspect) {
|
|
96
|
+
const height = viewportAspect / contentAspect;
|
|
97
|
+
return {
|
|
98
|
+
x: 0,
|
|
99
|
+
y: (1 - height) * HALF,
|
|
100
|
+
width: 1,
|
|
101
|
+
height
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const width = contentAspect / viewportAspect;
|
|
105
|
+
return {
|
|
106
|
+
x: (1 - width) * HALF,
|
|
107
|
+
y: 0,
|
|
108
|
+
width,
|
|
109
|
+
height: 1
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { HALF, SCREEN_CENTRE, WHOLE_SCREEN, aspectOf, boundsOf, fittedRectangle, lensTiles, pictureAt, shiftOf };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { degrees, degreesToRadians, radians, radiansToDegrees, wrapHalfTurn } from "../../shared/units/angle.js";
|
|
2
|
+
import { rotationAboutX, transformVector } from "../../shared/math/Matrix3.js";
|
|
3
|
+
import { clampView } from "./ViewState.js";
|
|
4
|
+
import { SCREEN_CENTRE } from "./screenLayout.js";
|
|
5
|
+
import { directionAt, rayThroughPicture } from "./rectilinear.js";
|
|
6
|
+
//#region ../../packages/core/src/domain/view/viewGestures.ts
|
|
7
|
+
/**
|
|
8
|
+
* How much each wheel notch or pinch step magnifies, in every view mode.
|
|
9
|
+
*/
|
|
10
|
+
var ZOOM_STEP = 1.1;
|
|
11
|
+
/**
|
|
12
|
+
* The magnification `steps` zoom steps make: above 1 zooms in, below 1 out.
|
|
13
|
+
*/
|
|
14
|
+
function zoomFactor(steps) {
|
|
15
|
+
return ZOOM_STEP ** steps;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Angle covered by one pixel across the viewport: the field of view spread over its width.
|
|
19
|
+
*/
|
|
20
|
+
function degreesPerPixel(view, viewportWidth) {
|
|
21
|
+
return degrees(view.fieldOfView / Math.max(viewportWidth, 1));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Turns the view by a drag: dragging the picture to the right turns the viewer to the left, as
|
|
25
|
+
* grabbing a globe does, by as many degrees as the drag covers at the current field of view.
|
|
26
|
+
*/
|
|
27
|
+
function panView(view, delta, viewportWidth) {
|
|
28
|
+
const perPixel = degreesPerPixel(view, viewportWidth);
|
|
29
|
+
return clampView({
|
|
30
|
+
...view,
|
|
31
|
+
yaw: degrees(view.yaw - delta.x * perPixel),
|
|
32
|
+
pitch: degrees(view.pitch + delta.y * perPixel)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Narrows the field of view by `steps` zoom steps (negative widens), within its bounds.
|
|
37
|
+
*/
|
|
38
|
+
function zoomView(view, steps) {
|
|
39
|
+
return clampView({
|
|
40
|
+
...view,
|
|
41
|
+
fieldOfView: degrees(view.fieldOfView / zoomFactor(steps))
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Zooms the normal view toward a point of the viewport: the field of view narrows or widens by
|
|
46
|
+
* the zoom's steps within its bounds, and the view turns so that the direction under the point
|
|
47
|
+
* stays under it. When the poles leave no such turn, it zooms about the centre.
|
|
48
|
+
*/
|
|
49
|
+
function zoomViewAt(view, zoom, viewportAspect) {
|
|
50
|
+
const zoomed = zoomView(view, zoom.steps);
|
|
51
|
+
if (zoom.focus.x === SCREEN_CENTRE.x && zoom.focus.y === SCREEN_CENTRE.y || zoomed.fieldOfView === view.fieldOfView) return zoomed;
|
|
52
|
+
const direction = directionAt(view, zoom.focus, viewportAspect);
|
|
53
|
+
const ray = rayThroughPicture(zoomed.fieldOfView, zoom.focus, viewportAspect);
|
|
54
|
+
const pitch = pitchRaising(ray, direction[1], view.pitch);
|
|
55
|
+
if (pitch === void 0) return zoomed;
|
|
56
|
+
const tilted = transformVector(rotationAboutX(degreesToRadians(pitch)), ray);
|
|
57
|
+
const yaw = radiansToDegrees(radians(Math.atan2(direction[0], direction[2]) - Math.atan2(tilted[0], tilted[2])));
|
|
58
|
+
return clampView({
|
|
59
|
+
...zoomed,
|
|
60
|
+
yaw,
|
|
61
|
+
pitch
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The pitch that brings `ray` to the height `height` of the target direction, which the yaw
|
|
66
|
+
* applied after it leaves alone: `y cos θ - z sin θ` is `reach cos(θ + phase)`. Of the two
|
|
67
|
+
* solutions the one nearer `current`; none when no pitch reaches that height.
|
|
68
|
+
*/
|
|
69
|
+
function pitchRaising(ray, height, current) {
|
|
70
|
+
const [, y, z] = ray;
|
|
71
|
+
const cosine = height / Math.hypot(y, z);
|
|
72
|
+
if (Math.abs(cosine) > 1) return void 0;
|
|
73
|
+
const phase = radiansToDegrees(radians(Math.atan2(z, y)));
|
|
74
|
+
const spread = radiansToDegrees(radians(Math.acos(cosine)));
|
|
75
|
+
const rising = wrapHalfTurn(degrees(spread - phase));
|
|
76
|
+
const falling = wrapHalfTurn(degrees(-spread - phase));
|
|
77
|
+
return Math.abs(rising - current) <= Math.abs(falling - current) ? rising : falling;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* How many zoom steps a pinch from `previousDistance` to `distance` is worth, in the same steps
|
|
81
|
+
* as a wheel notch: positive when the fingers spread (zoom in), zero when either distance is
|
|
82
|
+
* degenerate.
|
|
83
|
+
*/
|
|
84
|
+
function zoomStepsForPinch(previousDistance, distance) {
|
|
85
|
+
return previousDistance <= 0 || distance <= 0 ? 0 : Math.log(distance / previousDistance) / Math.log(ZOOM_STEP);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Points the view at a direction, clamped like every other change.
|
|
89
|
+
*/
|
|
90
|
+
function lookAt(view, yaw, pitch) {
|
|
91
|
+
return clampView({
|
|
92
|
+
...view,
|
|
93
|
+
yaw,
|
|
94
|
+
pitch
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
export { lookAt, panView, zoomFactor, zoomStepsForPinch, zoomViewAt };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LENS_TILES_VIEW } from "./lensTilesView.js";
|
|
2
|
+
import { NORMAL_VIEW } from "./normalView.js";
|
|
3
|
+
import { PANORAMA_VIEW } from "./panoramaView.js";
|
|
4
|
+
//#region ../../packages/core/src/domain/view/viewModes.ts
|
|
5
|
+
var RULES = {
|
|
6
|
+
normal: NORMAL_VIEW,
|
|
7
|
+
equirectangular: PANORAMA_VIEW,
|
|
8
|
+
"raw-lenses": LENS_TILES_VIEW
|
|
9
|
+
};
|
|
10
|
+
function viewModeRulesFor(mode) {
|
|
11
|
+
return RULES[mode];
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { viewModeRulesFor };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { degrees } from "./shared/units/angle.js";
|
|
2
|
+
import "./domain/optics/EquidistantModel.js";
|
|
3
|
+
import "./domain/format/calibration/CalibrationVersion.js";
|
|
4
|
+
import { GYRO_VIEW_ERROR_CODES, GyroViewError, asGyroViewError, ensureIndexInRange, ensureInvariant, hasErrorCode, isAbortError, isGyroViewErrorCode, messageOf } from "./shared/errors/GyroViewError.js";
|
|
5
|
+
import { lazy } from "./shared/lazy.js";
|
|
6
|
+
import { microseconds, microsecondsToSeconds, milliseconds, seconds, secondsToMicroseconds } from "./shared/units/time.js";
|
|
7
|
+
import "./domain/motion/exposure/ExposureRecord.js";
|
|
8
|
+
import "./shared/math/Vector3.js";
|
|
9
|
+
import { ByteRange } from "./shared/binary/ByteRange.js";
|
|
10
|
+
import "./domain/motion/timing/CaptureClock.js";
|
|
11
|
+
import { readRecording } from "./application/recording/readRecording.js";
|
|
12
|
+
import "./application/recording/inspectLayout.js";
|
|
13
|
+
import { inspectRecording } from "./application/recording/inspectRecording.js";
|
|
14
|
+
import { clamp } from "./shared/math/clamp.js";
|
|
15
|
+
import "./domain/motion/timing/FrameTimes.js";
|
|
16
|
+
import { IDENTITY_MATRIX3 } from "./shared/math/Matrix3.js";
|
|
17
|
+
import "./domain/motion/imu/ImuFrame.js";
|
|
18
|
+
import "./shared/math/Quaternion.js";
|
|
19
|
+
import "./domain/motion/orientation/OrientationTrack.js";
|
|
20
|
+
import { timeRecording } from "./application/recording/timeRecording.js";
|
|
21
|
+
import "./domain/format/naming/RecordingFileName.js";
|
|
22
|
+
import { fileNameOfUrl } from "./shared/text/urlPath.js";
|
|
23
|
+
import { locateOtherLensFile } from "./application/recording/locateOtherLensFile.js";
|
|
24
|
+
import "./domain/stitching/LensLayout.js";
|
|
25
|
+
import { detectLensLayout } from "./domain/format/layout/detectLensLayout.js";
|
|
26
|
+
import { DEFAULT_STABILIZATION_MODE, STABILIZATION_MODES } from "./domain/motion/stabilization/Stabilizer.js";
|
|
27
|
+
import { stabilizerFor } from "./domain/motion/stabilization/stabilizers.js";
|
|
28
|
+
import "./domain/optics/lensPose.js";
|
|
29
|
+
import "./domain/optics/gainMatch.js";
|
|
30
|
+
import { clampView, isSameView } from "./domain/view/ViewState.js";
|
|
31
|
+
import { DEFAULT_PICTURE_QUALITY, PICTURE_QUALITIES, pixelRatioCapOf } from "./domain/view/PictureQuality.js";
|
|
32
|
+
import { DEFAULT_VIEW_MODE, VIEW_MODES } from "./domain/view/ViewMode.js";
|
|
33
|
+
import { SCREEN_CENTRE, aspectOf } from "./domain/view/screenLayout.js";
|
|
34
|
+
import { aspectOfArea, planeHalfExtentOf } from "./domain/view/rectilinear.js";
|
|
35
|
+
import { lookAt, zoomStepsForPinch } from "./domain/view/viewGestures.js";
|
|
36
|
+
import "./domain/view/magnification.js";
|
|
37
|
+
import { DEFAULT_FRAMING, isSameFraming } from "./domain/view/Framing.js";
|
|
38
|
+
import { viewModeRulesFor } from "./domain/view/viewModes.js";
|
|
39
|
+
import "./domain/stitching/seamRing.js";
|
|
40
|
+
import { buildStitchingSetup, lensFrameOrder } from "./domain/stitching/StitchingSetup.js";
|
|
41
|
+
import { StabilizingFrameSink } from "./application/stabilization/StabilizingFrameSink.js";
|
|
42
|
+
import { GainMatchingFrameSink } from "./application/gainMatching/GainMatchingFrameSink.js";
|
|
43
|
+
import "./domain/stitching/seamStrip.js";
|
|
44
|
+
import "./domain/stitching/seamMismatch.js";
|
|
45
|
+
import "./domain/stitching/seamDisparity.js";
|
|
46
|
+
import "./domain/stitching/seamDisparityField.js";
|
|
47
|
+
import "./domain/stitching/seamJoin.js";
|
|
48
|
+
import { keysOf } from "./shared/keysOf.js";
|
|
49
|
+
import { Outbox } from "./shared/events/Outbox.js";
|
|
50
|
+
import "./application/playback/FramePairQueue.js";
|
|
51
|
+
import { Deferred } from "./shared/async/Deferred.js";
|
|
52
|
+
import { RunStop, STOPPED } from "./shared/async/RunStop.js";
|
|
53
|
+
import "./application/playback/DecodePipeline.js";
|
|
54
|
+
import { probeDecoding } from "./application/playback/probeDecoding.js";
|
|
55
|
+
import { WallClock } from "./application/playback/WallClock.js";
|
|
56
|
+
import { isFlowing } from "./domain/playback/PlayerState.js";
|
|
57
|
+
import { TypedEmitter } from "./shared/events/TypedEmitter.js";
|
|
58
|
+
import { PlaybackSession } from "./application/playback/PlaybackSession.js";
|
|
59
|
+
export { ByteRange, DEFAULT_FRAMING, DEFAULT_PICTURE_QUALITY, DEFAULT_STABILIZATION_MODE, DEFAULT_VIEW_MODE, Deferred, GYRO_VIEW_ERROR_CODES, GainMatchingFrameSink, GyroViewError, IDENTITY_MATRIX3, Outbox, PICTURE_QUALITIES, PlaybackSession, RunStop, SCREEN_CENTRE, STABILIZATION_MODES, STOPPED, StabilizingFrameSink, TypedEmitter, VIEW_MODES, WallClock, asGyroViewError, aspectOf, aspectOfArea, buildStitchingSetup, clamp, clampView, degrees, detectLensLayout, ensureIndexInRange, ensureInvariant, fileNameOfUrl, hasErrorCode, inspectRecording, isAbortError, isFlowing, isGyroViewErrorCode, isSameFraming, isSameView, keysOf, lazy, lensFrameOrder, locateOtherLensFile, lookAt, messageOf, microseconds, microsecondsToSeconds, milliseconds, pixelRatioCapOf, planeHalfExtentOf, probeDecoding, readRecording, seconds, secondsToMicroseconds, stabilizerFor, timeRecording, viewModeRulesFor, zoomStepsForPinch };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region ../../packages/core/src/shared/async/Deferred.ts
|
|
2
|
+
/**
|
|
3
|
+
* A promise settled from outside, once: later settlements are ignored. Lets a producer report
|
|
4
|
+
* an outcome to whoever is waiting without knowing them.
|
|
5
|
+
*/
|
|
6
|
+
var Deferred = class {
|
|
7
|
+
promise;
|
|
8
|
+
isSettledNow = false;
|
|
9
|
+
resolvePromise;
|
|
10
|
+
rejectPromise;
|
|
11
|
+
constructor() {
|
|
12
|
+
this.promise = new Promise((resolve, reject) => {
|
|
13
|
+
this.resolvePromise = resolve;
|
|
14
|
+
this.rejectPromise = reject;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
get isSettled() {
|
|
18
|
+
return this.isSettledNow;
|
|
19
|
+
}
|
|
20
|
+
resolve(value) {
|
|
21
|
+
if (this.isSettledNow) return;
|
|
22
|
+
this.isSettledNow = true;
|
|
23
|
+
this.resolvePromise?.(value);
|
|
24
|
+
}
|
|
25
|
+
reject(reason) {
|
|
26
|
+
if (this.isSettledNow) return;
|
|
27
|
+
this.isSettledNow = true;
|
|
28
|
+
this.rejectPromise?.(reason);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Settles as `outcome` does, once it has: hands work begun elsewhere to whoever waits here.
|
|
32
|
+
*/
|
|
33
|
+
async follow(outcome) {
|
|
34
|
+
try {
|
|
35
|
+
this.resolve(await outcome);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
this.reject(error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
export { Deferred };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { ensureInvariant } from "../errors/GyroViewError.js";
|
|
2
|
+
//#region ../../packages/core/src/shared/async/RunStop.ts
|
|
3
|
+
var STOPPED = Symbol("stopped");
|
|
4
|
+
/**
|
|
5
|
+
* Stops a run that waits for one thing at a time. `race` settles with `STOPPED` as soon as `stop`
|
|
6
|
+
* is called; only the wait in progress is remembered, so an hour-long run of short waits keeps
|
|
7
|
+
* nothing of the ones that finished. Once stopped it starts no work at all: work started and left
|
|
8
|
+
* unwatched could fail later with nobody to hear it.
|
|
9
|
+
*/
|
|
10
|
+
var RunStop = class {
|
|
11
|
+
isStopped = false;
|
|
12
|
+
wakeWaiter;
|
|
13
|
+
get wasStopped() {
|
|
14
|
+
return this.isStopped;
|
|
15
|
+
}
|
|
16
|
+
stop() {
|
|
17
|
+
this.isStopped = true;
|
|
18
|
+
this.wakeWaiter?.();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The waiter is in place before `start` runs, so a stop that `start` itself causes (a decoder
|
|
22
|
+
* failing at once) ends the wait too.
|
|
23
|
+
*/
|
|
24
|
+
async race(start) {
|
|
25
|
+
if (this.isStopped) return STOPPED;
|
|
26
|
+
ensureInvariant(this.wakeWaiter === void 0, "a run stop races one wait at a time");
|
|
27
|
+
const stopped = new Promise((resolve) => {
|
|
28
|
+
this.wakeWaiter = () => {
|
|
29
|
+
resolve(STOPPED);
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
try {
|
|
33
|
+
return await Promise.race([start(), stopped]);
|
|
34
|
+
} finally {
|
|
35
|
+
this.wakeWaiter = void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
export { RunStop, STOPPED };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { GyroViewError } from "../errors/GyroViewError.js";
|
|
2
|
+
//#region ../../packages/core/src/shared/binary/ByteRange.ts
|
|
3
|
+
/**
|
|
4
|
+
* A contiguous span of bytes in a source: `offset` is inclusive, `end` is exclusive.
|
|
5
|
+
*/
|
|
6
|
+
var ByteRange = class ByteRange {
|
|
7
|
+
offset;
|
|
8
|
+
length;
|
|
9
|
+
constructor(offset, length) {
|
|
10
|
+
this.offset = offset;
|
|
11
|
+
this.length = length;
|
|
12
|
+
}
|
|
13
|
+
static of(offset, length) {
|
|
14
|
+
if (!Number.isSafeInteger(offset) || offset < 0) throw new GyroViewError("invalid-byte-range", `offset must be a non-negative integer, got ${offset}`);
|
|
15
|
+
if (!Number.isSafeInteger(length) || length < 0) throw new GyroViewError("invalid-byte-range", `length must be a non-negative integer, got ${length}`);
|
|
16
|
+
return new ByteRange(offset, length);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The last `length` bytes of a source of `totalSize` bytes.
|
|
20
|
+
*/
|
|
21
|
+
static lastOf(totalSize, length) {
|
|
22
|
+
if (length > totalSize) throw new GyroViewError("invalid-byte-range", `cannot take ${length} bytes from a ${totalSize}-byte source`);
|
|
23
|
+
return this.of(totalSize - length, length);
|
|
24
|
+
}
|
|
25
|
+
get end() {
|
|
26
|
+
return this.offset + this.length;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Refuses a range that runs past the end of a `totalSize`-byte source with the error the
|
|
30
|
+
* `RandomAccessSource` contract asks for; `source` names the source in the message.
|
|
31
|
+
*/
|
|
32
|
+
ensureWithin(totalSize, source) {
|
|
33
|
+
if (this.end <= totalSize) return;
|
|
34
|
+
throw new GyroViewError("invalid-byte-range", `range ${this.offset}+${this.length} exceeds the ${totalSize}-byte ${source}`);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { ByteRange };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { GyroViewError } from "../errors/GyroViewError.js";
|
|
2
|
+
//#region ../../packages/core/src/shared/binary/ByteReader.ts
|
|
3
|
+
var UINT8_SIZE = 1;
|
|
4
|
+
var UINT16_SIZE = 2;
|
|
5
|
+
var UINT32_SIZE = 4;
|
|
6
|
+
var UINT64_SIZE = 8;
|
|
7
|
+
var FLOAT64_SIZE = 8;
|
|
8
|
+
var IS_LITTLE_ENDIAN = true;
|
|
9
|
+
var IS_BIG_ENDIAN = false;
|
|
10
|
+
/**
|
|
11
|
+
* Bounds-checked, little-endian reads over a byte array. Every read is positional, so callers
|
|
12
|
+
* express the record layouts they parse as named offsets rather than as a moving cursor.
|
|
13
|
+
*/
|
|
14
|
+
var ByteReader = class {
|
|
15
|
+
bytes;
|
|
16
|
+
view;
|
|
17
|
+
constructor(bytes) {
|
|
18
|
+
this.bytes = bytes;
|
|
19
|
+
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
20
|
+
}
|
|
21
|
+
get length() {
|
|
22
|
+
return this.bytes.byteLength;
|
|
23
|
+
}
|
|
24
|
+
uint8At(offset) {
|
|
25
|
+
this.ensureAvailable(offset, UINT8_SIZE);
|
|
26
|
+
return this.view.getUint8(offset);
|
|
27
|
+
}
|
|
28
|
+
uint16LeAt(offset) {
|
|
29
|
+
this.ensureAvailable(offset, UINT16_SIZE);
|
|
30
|
+
return this.view.getUint16(offset, IS_LITTLE_ENDIAN);
|
|
31
|
+
}
|
|
32
|
+
uint32LeAt(offset) {
|
|
33
|
+
this.ensureAvailable(offset, UINT32_SIZE);
|
|
34
|
+
return this.view.getUint32(offset, IS_LITTLE_ENDIAN);
|
|
35
|
+
}
|
|
36
|
+
uint32BeAt(offset) {
|
|
37
|
+
this.ensureAvailable(offset, UINT32_SIZE);
|
|
38
|
+
return this.view.getUint32(offset, IS_BIG_ENDIAN);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads an unsigned 64-bit integer as a JavaScript number; rejects values beyond 2^53 - 1.
|
|
42
|
+
*/
|
|
43
|
+
uint64LeAt(offset) {
|
|
44
|
+
this.ensureAvailable(offset, UINT64_SIZE);
|
|
45
|
+
return this.toSafeNumber(this.view.getBigUint64(offset, IS_LITTLE_ENDIAN), offset);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Reads a big-endian unsigned 64-bit integer (ISOBMFF box sizes) as a safe JavaScript number.
|
|
49
|
+
*/
|
|
50
|
+
uint64BeAt(offset) {
|
|
51
|
+
this.ensureAvailable(offset, UINT64_SIZE);
|
|
52
|
+
return this.toSafeNumber(this.view.getBigUint64(offset, IS_BIG_ENDIAN), offset);
|
|
53
|
+
}
|
|
54
|
+
float64LeAt(offset) {
|
|
55
|
+
this.ensureAvailable(offset, FLOAT64_SIZE);
|
|
56
|
+
return this.view.getFloat64(offset, IS_LITTLE_ENDIAN);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns a view (not a copy) of `length` bytes starting at `offset`.
|
|
60
|
+
*/
|
|
61
|
+
bytesAt(offset, length) {
|
|
62
|
+
this.ensureAvailable(offset, length);
|
|
63
|
+
return this.bytes.subarray(offset, offset + length);
|
|
64
|
+
}
|
|
65
|
+
asciiAt(offset, length) {
|
|
66
|
+
return Array.from(this.bytesAt(offset, length), (byte) => String.fromCodePoint(byte)).join("");
|
|
67
|
+
}
|
|
68
|
+
toSafeNumber(value, offset) {
|
|
69
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER)) throw new GyroViewError("binary-unsafe-integer", `uint64 at offset ${offset} (${value.toString()}) exceeds the safe integer range`);
|
|
70
|
+
return Number(value);
|
|
71
|
+
}
|
|
72
|
+
ensureAvailable(offset, length) {
|
|
73
|
+
if (offset < 0 || length < 0 || offset + length > this.bytes.byteLength) throw new GyroViewError("binary-out-of-bounds", `read of ${length} byte(s) at offset ${offset} exceeds ${this.bytes.byteLength} available`);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { ByteReader };
|