@rr0/ufoathome 0.19.6 → 0.21.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/README.md +76 -15
- package/dist-embed/UfoRecorderMessages_fr-Dvf46rNk.js +1 -0
- package/dist-embed/rr0-ufo-recorder.mjs +234 -198
- package/dist-embed-eyewitness/rr0-eyewitness.mjs +229 -195
- package/dist-embed-scene/rr0-scene.mjs +226 -192
- package/dist-embed-ufo/rr0-ufo.mjs +3 -3
- package/package.json +1 -1
- package/dist-embed/UfoRecorderMessages_fr-DU4ld1Gl.js +0 -1
package/README.md
CHANGED
|
@@ -320,7 +320,7 @@ interface SightingRecordingJson {
|
|
|
320
320
|
selected: boolean
|
|
321
321
|
title?: string // shown as an on-canvas tooltip when hovered
|
|
322
322
|
behindCloud?: boolean // the witness reported it behind cloud at this instant — stated, never deduced (see below)
|
|
323
|
-
|
|
323
|
+
angular?: { widthDeg: number, heightDeg: number } // how big it LOOKED — the only size a testimony holds, see Apparent size
|
|
324
324
|
points?: { x: number, y: number }[] // "polygon" shapes only
|
|
325
325
|
}
|
|
326
326
|
}>
|
|
@@ -332,6 +332,7 @@ interface SightingRecordingJson {
|
|
|
332
332
|
weatherTrack?: { keyframes: Array<{ t: number, weather: Weather }> }
|
|
333
333
|
weather?: Weather // legacy static fallback for recordings predating weatherTrack
|
|
334
334
|
weatherSource?: { id: string, name: string, url: string } // the meteorological record weatherTrack was looked up from — see Weather is looked up, not remembered. Absent = the witness's own account
|
|
335
|
+
instrument?: "eye" | "rectilinear-lens" // what it was observed THROUGH — see Instrument. Absent = the naked eye
|
|
335
336
|
soundTrack?: { keyframes: Array<{ t: number, sound: { kind: "none" | "hum" | "whistle" | "rumble" | "crackle", volume: number, pitchHz: number, src?: string } }> } // what the witness heard — see What it sounded like
|
|
336
337
|
decor?: DecorObject[] // buildings, trees, streetlights, vehicles, other witnesses — see src/engine/model/Decor.ts
|
|
337
338
|
}
|
|
@@ -504,20 +505,80 @@ diff shows the weather and nothing else.
|
|
|
504
505
|
appearance field, and held rather than blended. It is *stated*, for the same reason
|
|
505
506
|
`DecorObject.occludesSourceIds` is: this format describes a 2D appearance on the witness's own field
|
|
506
507
|
of view, not where an object was in space, so nothing in it can deduce whether cloud came between
|
|
507
|
-
them. A
|
|
508
|
-
|
|
509
|
-
disappearance happens to occur.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
508
|
+
them. A recording holds no distance at all (see *Apparent size* below), and the sky's own gaps are
|
|
509
|
+
procedural noise — leaving the question to geometry means tuning the weather until the reported
|
|
510
|
+
disappearance happens to occur. So the witness's account is the whole answer: no `behindCloud`, no
|
|
511
|
+
cloud.
|
|
512
|
+
|
|
513
|
+
There used to be a geometric fallback here, for a recording that stated a real distance and made no
|
|
514
|
+
claim about cloud. It went when stated distances did, and it had earned it: the one case it fired on
|
|
515
|
+
was Chiles-Whitted, where "it disappeared into the cloud deck" turned out to be an interrogator's
|
|
516
|
+
reconstruction that Whitted himself denied to McDonald in 1968.
|
|
517
|
+
|
|
518
|
+
### Apparent size — and why there is no real one
|
|
519
|
+
|
|
520
|
+
A witness never perceives meters. They perceive an angle: the thing covered a thumbnail at arm's length, or a fifth
|
|
521
|
+
of the windshield, or two full Moons. "About thirty meters long" is a conclusion they drew from a distance they
|
|
522
|
+
could not perceive either, and the two errors multiply. So a recording stores `angular` — how wide and how tall the
|
|
523
|
+
object *looked*, in degrees — and stores no real size and no real distance anywhere.
|
|
524
|
+
|
|
525
|
+
`bounds` is that angle projected onto the fixed 640x360 canvas at the pose's own field of view **and through the
|
|
526
|
+
recording's own instrument** (see below), which is what every editing gesture, hit-test and renderer keeps working
|
|
527
|
+
on. The angle is authoritative: `SightingShapes` (`src/engine/persistence/SightingShapes.ts`) re-derives `bounds`
|
|
528
|
+
from it on load and reads it back from `bounds` on save, so a file survives a change of canvas, of field of view or
|
|
529
|
+
of instrument, and if the two ever disagree the angle wins. `ImageProjection`
|
|
530
|
+
(`src/engine/instrument/ImageProjection.ts`) owns the conversion itself.
|
|
531
|
+
|
|
532
|
+
Through an eye at 60° across 360px, one degree is exactly 6px and the full Moon about 3.1px — so an object of 3.5m
|
|
533
|
+
at 90m is 13px wide, not the 90px an author reaches for unaided. That is why the editor's **Try a size** / **at a distance of**
|
|
534
|
+
fields exist: type a hypothesis, get the angle it implies on the canvas, and the meters are forgotten the moment
|
|
535
|
+
they have been applied. They are an authoring aid, never testimony.
|
|
536
|
+
|
|
537
|
+
### Instrument — an eye is not a lens
|
|
538
|
+
|
|
539
|
+
`instrument` says what the observation was made through, and it changes the geometry of every frame.
|
|
540
|
+
|
|
541
|
+
A camera lens maps a direction to its sensor as `r = f·tan θ`: straight lines stay straight, and everything away
|
|
542
|
+
from the axis is stretched by `sec²θ` — 42% at 33° off-centre, 105% at the corner of a 16:9 frame with a 60°
|
|
543
|
+
vertical field. That is *correct* for a photograph and only looks right from the projection centre, about half an
|
|
544
|
+
image-width from the screen. An eye does no such thing: it perceives an angle as an angle wherever it falls, so
|
|
545
|
+
`instrument: "eye"` renders `r = f·θ` — image distance proportional to angle, which is what lets a ruler held to
|
|
546
|
+
the screen mean something. (A slight tangential stretch of `θ/sin θ` remains, 6% at 33°; no flat image escapes
|
|
547
|
+
trading one distortion for another.)
|
|
548
|
+
|
|
549
|
+
three.js's camera can only do the pinhole, so `EquidistantProjectionPass` renders the scene into an offscreen
|
|
550
|
+
target with a deliberately wider field and resamples it in one fullscreen pass. Everything that *aims* at the scene
|
|
551
|
+
rather than drawing it — the decor raycasts behind `isScreenPointOccluded` and `decorDistancesAt` — goes through
|
|
552
|
+
`directionFor`, since a point on the visible image no longer means what the pinhole camera thinks it means.
|
|
553
|
+
|
|
554
|
+
This is also why a change of instrument **moves** shapes and not just resizes them (`SightingShapes.reproject`): a
|
|
555
|
+
pixel only names a direction once a projection is named. Leaving positions alone is exactly how an object drawn in
|
|
556
|
+
several parts comes apart — the fuselage grows and its row of windows stays put.
|
|
557
|
+
|
|
558
|
+
Every case file here declares `eye`, because every one of them was watched rather than filmed. Until this existed
|
|
559
|
+
they were all rendered as photographs, which is what made Socorro's dynamite shack read as twice the size it
|
|
560
|
+
subtends.
|
|
561
|
+
|
|
562
|
+
One residual worth naming: an angular extent is stored as its *on-axis* value, and applied to a shape wherever it
|
|
563
|
+
sits. For an object 9° off-axis subtending 9°, that is about 2.5% out. The overlay draws axis-aligned boxes and
|
|
564
|
+
cannot express more; it is a fifth of the error it replaces, and it shrinks towards the centre of the frame.
|
|
565
|
+
|
|
566
|
+
#### Where meters do come back
|
|
567
|
+
|
|
568
|
+
The only real distance a testimony can support is an **inequality**, and only where the witness saw the object
|
|
569
|
+
cross something whose position is known: it passed *behind* that hangar (at least that far), or *in front of* that
|
|
570
|
+
tree (at most that far). `DecorObject.eastM/northM` give the decor its real position, `occludesSourceIds` says
|
|
571
|
+
which side of it the object was on, and `SceneRenderer.decorDistancesAt` raycasts the exact line of sight to
|
|
572
|
+
measure the crossing.
|
|
573
|
+
|
|
574
|
+
An angle plus a distance is a size, and a size does not change as the object flies — so every crossing narrows the
|
|
575
|
+
object's real width from one side, for the whole recording, and the narrowed width reads back as a distance at
|
|
576
|
+
every other instant. `SizeEstimate` (`src/engine/shape/SizeEstimate.ts`) accumulates that, reports a contradiction
|
|
577
|
+
rather than clamping one, and the editor prints the result under the apparent size.
|
|
578
|
+
|
|
579
|
+
Most sightings constrain nothing at all — a light in an empty night sky crosses nothing — and the readout then says
|
|
580
|
+
so. "Unknown" is the honest answer for a majority of cases, and saying it out loud is the entire point of not
|
|
581
|
+
storing a number instead.
|
|
521
582
|
|
|
522
583
|
This format is deliberately independent of [`@rr0/data`](https://github.com/RR0/data)'s `RR0Event`/`@rr0/time`'s
|
|
523
584
|
`Level2Date`/`@rr0/place`'s `Place` classes, even though its `time`/`place` fields are structurally aligned with
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={oval:"Ovale",polygon:"Polygone",addVertex:"Ajouter un sommet",deleteVertex:"Supprimer le sommet",notAPolygon:"Sélectionnez une seule forme polygonale",tooFewVertices:"Une forme a besoin d'au moins 3 points",color:"Couleur",transparency:"Transparence",halo:"Halo",shape:"Forme",shapeTitle:"Nom",objectSize:"Essayer une taille",objectDistance:"à une distance de",apparentSize:"soit {deg}° — {moons}× la Lune",utcOffset:"Fuseau horaire",utcOffsetPlaceholder:"d'après la longitude",objectSizePlaceholder:"largeur supposée",objectDistancePlaceholder:"distance supposée",realSizeBetween:"Largeur réelle : entre {min} et {max} m, d'après ce qu'il croise",realSizeAtLeast:"Largeur réelle : au moins {min} m, d'après ce qu'il passe derrière",realSizeAtMost:"Largeur réelle : au plus {max} m, d'après ce qu'il passe devant",realSizeUnknown:"Largeur réelle indéterminée : rien dans la scène ne croise sa ligne de visée",realSizeContradiction:"Contradiction : les passages déclarés ne peuvent pas être vrais ensemble",realDistanceHere:" — soit ici entre {min} et {max} m",addShape:"Ajouter une forme",deleteShape:"Supprimer la forme",play:"Lecture",pause:"Pause",noDuration:"Aucune durée d'observation",autoReplay:"Lecture automatique",group:"Grouper",ungroup:"Dégrouper",bringToFront:"Placer devant",sendToBack:"Placer derrière",contextMenuDelete:"Supprimer",confirmDeleteShape:"Supprimer {name} ? Cette action est irréversible.",confirmDeleteShapes:"Supprimer {count} formes ? Cette action est irréversible.",onlyOneShape:"Il n'y a qu'une seule forme",alreadyAtFront:"Cette forme est déjà la plus en avant",alreadyAtBack:"Cette forme est déjà la plus en arrière",needTwoShapesToGroup:"Sélectionnez au moins deux formes pour les grouper",notGrouped:"Cette forme ne fait pas partie d'un groupe",multipleShapesSelected:"Plusieurs formes sélectionnées — sélectionnez-en une seule pour modifier ceci",cloudBase:"Base des nuages",elevation:"Altitude",samplingRate:"Échantillonnage",duration:"Durée",durationPlaceholder:"durée de l'observation",durationImprecise:"Ces dates ne permettent pas de déterminer précisément une durée — saisissez-la manuellement.",export:"Exporter le JSON",importFile:"Charger un fichier JSON",importUrl:"Ou charger depuis une URL",importUrlPlaceholder:"https://…/sighting.json",importButton:"Charger",importError:"Impossible de charger cet enregistrement — vérifiez le fichier ou l'URL et réessayez.",record:"Enregistrer",stop:"Arrêter",according:"selon",sourceElevation:"Relief",sourceImagery:"Imagerie",placeName:"Lieu",placeNamePlaceholder:"Valensole, France",searchPlace:"Localiser",placeMatch:"Correspondances",placeSearching:"Recherche du lieu…",placeMatchFound:"lieu trouvé",placeMatchesFound:"lieux trouvés",placeNotFound:"Aucun lieu de ce nom",placeSearchFailed:"Recherche de lieu momentanément indisponible",utcOffsetImplausible:"Ce fuseau ne correspond pas à la longitude saisie, dont l'heure solaire est UTC{solar} — la scène et le relevé météo obéissent pourtant à ce qui est déclaré ici. Videz le champ pour le déduire de la longitude.",timeZoneManual:"décalage saisi",altitudeAboveSeaLevel:"Altitude du témoin au-dessus du niveau de la mer — le sol du lieu saisi en fixe le minimum : un témoin dans les Alpes n'est pas à 0 m.",groundAt:"sol à {m} m",latitude:"Latitude",longitude:"Longitude",heading:"Orientation",headingPlaceholder:"inconnu",pitch:"Inclinaison",observationTime:"Début de l'observation",observationEndTime:"Fin de l'observation",edtfInvalid:"Date/heure EDTF invalide (ex. 1965-07-01T05:00, 2025-06?, 2025~).",edtfPlaceholder:"AAAA-MM-JJThh:mm[?~%] ou hh:mm",observationTimeHint:"EDTF — ex. 1965-07-01T05:00, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:00 si la date est inconnue",observationEndTimeHint:"EDTF — ex. 1965-07-01T05:10, 2025-06? (incertain), 2025~ (approximatif), ou juste 05:10 si la date est inconnue",presetsGroupLabel:"Forme de l'ovni",witnessId:"ID témoin",witnessDirName:"Répertoire du témoin",witnessTitle:"Titre du témoin",witnessLastName:"Nom de famille du témoin",witnessFirstNames:"Prénoms du témoin",caseId:"ID de l'affaire",description:"Description",tags:"Mots-clés",tagsPlaceholder:"séparés par des virgules",weather:"Météo",shapeGroup:"Forme",soundGroup:"Son",temporalGroup:"Date et heure",locationGroup:"Lieu",observationGroup:"Observation",witnessGroup:"Témoin",circumstancesGroup:"Circonstances",weatherInferred:"D'après les relevés",weatherInferredTitle:"La météo n'est pas un témoignage : c'est un fait mesurable, relevé pour la date, l'heure et le lieu de l'observation. Décochez pour saisir les conditions rapportées par le témoin, qui priment alors sur le relevé.",weatherLookingUp:"Recherche du relevé…",weatherNeedsDateAndPlace:"Date complète et lieu requis pour retrouver le relevé",weatherNoRecord:"Aucun relevé pour cette date et ce lieu",weatherLookupFailed:"Relevés météo momentanément inaccessibles",cloudCover:"Couverture nuageuse",cloudDarkness:"Obscurité des nuages",precipitationType:"Précipitations",precipitationNone:"Aucune",precipitationRain:"Pluie",precipitationSnow:"Neige",precipitationHail:"Grêle",precipitationIntensity:"Intensité",windDirection:"Direction du vent",windSpeed:"Force du vent",storm:"Orage",soundKind:"Son",soundNone:"Aucun (silencieux)",soundHum:"Bourdonnement",soundWhistle:"Sifflement",soundRumble:"Grondement",soundCrackle:"Crépitement",soundVolume:"Intensité sonore",soundPitch:"Hauteur",soundSrc:"Enregistrement",soundSrcPlaceholder:"URL d'un enregistrement réel",lensFlareBrightness:"Intensité lumineuse",instrument:"Observé avec",cameraDevice:"Appareil photo/vidéo",decor:"Décor",decorBuilding:"Bâtiment",decorTree:"Arbre",decorStreetlight:"Lampadaire",decorVehicle:"Véhicule",decorWitness:"Autre témoin",addDecor:"Ajouter",deleteDecor:"Supprimer l'élément de décor",decorEast:"Distance à l'est",decorNorth:"Distance au nord",decorHeading:"Orientation",decorLit:"Allumé",decorTitle:"Nom",decorSightingUrl:"URL de l'enregistrement du témoin",viewTestimony:"Voir le témoignage",noWitnessRecording:"Aucune URL d'enregistrement définie pour ce témoin",masks:"Masque",addWitness:"Ajouter un témoin",decorFloors:"Étages",decorOccupiedFloor:"Étage occupé",decorWitnessSide:"Emplacement du témoin",decorWitnessSideNone:"Non présent",decorWindows:"Fenêtres",decorSideFront:"Avant",decorSideBehind:"Arrière",decorSideLeft:"Gauche",decorSideRight:"Droite",decorSideFrontLeft:"Avant-gauche",decorSideFrontRight:"Avant-droit",decorSideBehindLeft:"Arrière-gauche",decorSideBehindRight:"Arrière-droit"};export{e as ufoRecorderMessages_fr};
|