@harpern/edu-modelhub-viewer-core 0.1.0-beta.0 → 0.1.0-beta.1
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 +15 -0
- package/README.md +3 -0
- package/dist/announce.d.ts +1 -1
- package/dist/coordinates.d.ts +1 -1
- package/dist/index.cjs +12 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +811 -361
- package/dist/metadata.d.ts +7 -2
- package/dist/normalize.d.ts +1 -1
- package/dist/regions.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/types.d.ts +147 -0
- package/package.json +2 -4
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,21 @@ here.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [0.1.0-beta.1] - 2026-09-13
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- The published package could not be installed. It declared a runtime
|
|
14
|
+
dependency on `@osuecampus/edu-modelhub-extractor`, which is not on the
|
|
15
|
+
registry yet, so `0.1.0-beta.0` failed to resolve for every consumer. The
|
|
16
|
+
extractor is now compiled into the bundle and is a build input rather than
|
|
17
|
+
a runtime dependency, and the payload types are declared here instead of
|
|
18
|
+
imported, so nothing in the published files points at a package the
|
|
19
|
+
consumer does not have.
|
|
20
|
+
- `GlbParseError` is no longer re-exported. Use `isGlbParseError(error)`,
|
|
21
|
+
which checks the error name: with the parser bundled in, a class identity
|
|
22
|
+
check against a separate copy would silently fail.
|
|
23
|
+
|
|
9
24
|
## [Unreleased]
|
|
10
25
|
|
|
11
26
|
### Added
|
package/README.md
CHANGED
|
@@ -42,6 +42,9 @@ package manager tells you when to switch.
|
|
|
42
42
|
`three` is a peer dependency. `three-mesh-bvh` is an optional peer: install
|
|
43
43
|
it to get marker occlusion, leave it out and markers simply never dim.
|
|
44
44
|
|
|
45
|
+
Nothing else is required. The EDU_modelhub parser and validator are compiled
|
|
46
|
+
into this package, so it installs and runs on its own.
|
|
47
|
+
|
|
45
48
|
## What it does
|
|
46
49
|
|
|
47
50
|
**Metadata, with the precedence the spec requires.** One fetch, embedded
|
package/dist/announce.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* are throttled: a held arrow key would otherwise flood the live region and
|
|
7
7
|
* drown out everything else.
|
|
8
8
|
*/
|
|
9
|
-
import type { EduModelHub } from "
|
|
9
|
+
import type { EduModelHub } from "./types.js";
|
|
10
10
|
/** Default gap between camera announcements, in milliseconds. */
|
|
11
11
|
export declare const CAMERA_ANNOUNCE_INTERVAL = 500;
|
|
12
12
|
/** A one-line summary spoken when a model finishes loading. */
|
package/dist/coordinates.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* undeclared payload is now read as Y-up and only an explicit marker is
|
|
13
13
|
* converted.
|
|
14
14
|
*/
|
|
15
|
-
import type { EduModelHub } from "
|
|
15
|
+
import type { EduModelHub } from "./types.js";
|
|
16
16
|
/** A position as stored in a payload, or as handed to a renderer. */
|
|
17
17
|
export type Vec3 = [number, number, number];
|
|
18
18
|
/** True when `value` is three finite numbers. */
|
package/dist/index.cjs
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const h=require("@osuecampus/edu-modelhub-extractor"),m=require("three");function R(e){return e.filter(t=>t.severity==="error")}function v(e){const[t,i=""]=e.split("?",2);if(!/\.glb$/i.test(t))return null;const n=t.replace(/\.glb$/i,".modelhub.json");return i?`${n}?${i}`:n}function K(e,t){var o;if(!e||e.startsWith("blob:")||e.startsWith("data:"))return"";const i=t??((o=globalThis.location)==null?void 0:o.href),n=i?new URL(e,i).href:e,r=n.lastIndexOf("/");return r===-1?"":n.slice(0,r+1)}async function $(e,t=fetch){const i=await t(e);if(!i.ok)throw new Error(`Failed to fetch ${e} (HTTP ${i.status}).`);return i.arrayBuffer()}function _(e){const t=h.parseGlb(e);if(!t)return{payload:null,source:"none",findings:[],problem:"This model carries no EDU_modelhub metadata."};const i=h.validate(t);return{payload:t,source:"embedded",findings:i,problem:null}}async function q(e,t={}){const i=t.fetchImpl??fetch,n=await $(e,i),r=_(n);if(r.payload)return{...r,bytes:n};if(t.sidecar===!1)return{...r,bytes:n};const s=(t.sidecarUrl??v)(e);if(!s)return{...r,bytes:n};try{const c=await i(s);if(c.ok){const u=h.adaptSidecar(await c.json());return{payload:u,source:"sidecar",findings:h.validate(u),problem:null,bytes:n}}}catch{}return{payload:null,source:"none",findings:[],problem:"This model has no EDU_modelhub metadata, either embedded in the file or in a sidecar beside it.",bytes:n}}function j(e,t="EDU_modelhub",i=console){if(!(e!=null&&e.length))return;const n=R(e),r=e.filter(o=>o.severity!=="error");n.length&&i.warn(`${t}: ${n.length} validation error(s)`,n),r.length&&i.info(`${t}: ${r.length} validation warning(s)`,r)}function T(e){return Array.isArray(e)&&e.length===3&&e.every(t=>typeof t=="number"&&Number.isFinite(t))}function A(e){return e==="blender"}function x(e,t){if(!T(e))return null;const[i,n,r]=e;return A(t)?[i,r,-n]:[i,n,r]}function Z(e,t){if(!T(e))return null;const[i,n,r]=e;return A(t)?[i,-r,n]:[i,n,r]}function C(e){if(!Array.isArray(e))return[];const t=new Set;return e.map((i,n)=>{const o=(typeof(i==null?void 0:i.id)=="string"?i.id.trim():"")||`annotation-${n+1}`;let s=o,c=2;for(;t.has(s);)s=`${o}-${c++}`;return t.add(s),{...i??{},id:s}})}function M(e){const t=C(e==null?void 0:e.annotations),i=n=>Number.isFinite(n.readingOrder)?n.readingOrder:Number.MAX_SAFE_INTEGER;return t.map((n,r)=>({annotation:n,index:r})).sort((n,r)=>i(n.annotation)-i(r.annotation)||n.index-r.index).map(n=>n.annotation)}function S(e){return(Array.isArray(e==null?void 0:e.regions)?e.regions:[]).map((i,n)=>({region:i,index:n})).sort((i,n)=>(i.region.order??Number.MAX_SAFE_INTEGER)-(n.region.order??Number.MAX_SAFE_INTEGER)||i.index-n.index).map(i=>i.region)}function I(e){var t;return e?((t=e.label)==null?void 0:t.trim())||e.id||"Unnamed region":"Unknown region"}function X(e){if(!e)return[];const t=[],i=/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g;let n=0,r;for(;(r=i.exec(e))!==null;)r.index>n&&t.push({text:e.slice(n,r.index)}),t.push({text:r[1],url:r[2]}),n=r.index+r[0].length;return n<e.length&&t.push({text:e.slice(n)}),t.filter(o=>o.text.trim()||o.url)}function Q(e){const t=e==null?void 0:e.coordinateSpace,i=S(e),n=M(e),r=new Map(i.map(a=>[a.id,a])),o=new Map(n.map(a=>[a.id,a])),s=new Map;for(const a of n){const l=a.regionId;if(!l)continue;const d=s.get(l);d?d.push(a):s.set(l,[a])}const c=[];for(const a of n){const l=a.anchor;if(!l||l.type!=="marker")continue;const d=x(l.position,t);if(!d)continue;const f=a.camera,p=f?x(f.eye,t):null,g=f?x(f.target,t):null;c.push({annotation:a,position:d,...p&&g?{camera:{eye:p,target:g,...f!=null&&f.fov?{fov:f.fov}:{}}}:{}})}const u=new Set(i.filter(a=>a.visibilityDefault==="hidden").map(a=>a.id));return{payload:e,regions:i,regionsById:r,annotations:n,annotationsById:o,annotationsByRegion:s,markerAnchors:c,hiddenRegionIds:u}}function Y(e){return e.isMesh===!0}function J(e,t,i=[]){var u;const n=new Map,r=new Map,o=new Map,s=new Map;for(const[a,l]of((t==null?void 0:t.meshes)??[]).entries())for(const d of l.primitives??[]){const f=(u=d.extras)==null?void 0:u.modelhubRegionId;typeof f=="string"&&f&&(s.set(a,f),l.name&&r.set(l.name,f))}for(const a of(t==null?void 0:t.nodes)??[]){if(a.mesh===void 0||!a.name)continue;const l=s.get(a.mesh);l&&o.set(a.name,l)}const c=new Set(i.map(a=>a.id));return e.traverse(a=>{var p,g;if(!Y(a))return;const l=(g=(p=a.geometry)==null?void 0:p.userData)==null?void 0:g.modelhubRegionId,d=typeof l=="string"&&l||o.get(a.name)||r.get(a.name)||(c.has(a.name)?a.name:void 0);if(!d)return;const f=n.get(d);f?f.push(a):n.set(d,[a]),a.userData.modelhubOriginalMaterial===void 0&&(a.userData.modelhubOriginalMaterial=a.material)}),n}function ee(e){const t={};for(const[i,n]of e)t[i]=n.length;return t}function B(e,t,i){for(const n of e.get(t)??[])n.visible=i}function te(e,t){for(const i of e.keys())B(e,i,!t.has(i))}function ne(e,t,i){for(const n of e.get(t)??[])n.material=i}function ie(e,t){for(const i of e.get(t)??[]){const n=i.userData.modelhubOriginalMaterial;n&&(i.material=n)}}const k="#2196f3",re="#ff5722";function N(e,t={}){const{fill:i=k,text:n="#ffffff",textStroke:r="rgba(0, 0, 0, 0.45)"}=t,o=256,s=document.createElement("canvas");s.width=o,s.height=o;const c=s.getContext("2d");return c&&(c.beginPath(),c.arc(o/2,o/2,o/2-4,0,Math.PI*2),c.fillStyle=i,c.fill(),c.strokeStyle="#ffffff",c.lineWidth=8,c.stroke(),c.fillStyle=n,c.strokeStyle=r,c.lineWidth=10,c.font="bold 132px Arial, sans-serif",c.textAlign="center",c.textBaseline="middle",c.strokeText(String(e),o/2,o/2),c.fillText(String(e),o/2,o/2)),s}function D(e,t=.2,i){const n=new m.SpriteMaterial({map:new m.CanvasTexture(N(e,i)),transparent:!0,depthTest:!1}),r=new m.Sprite(n);return r.scale.set(t,t,1),r.renderOrder=999,r}function oe(e,t,i){var r;const n=e.material;(r=n.map)==null||r.dispose(),n.map=new m.CanvasTexture(N(t,{fill:i})),n.needsUpdate=!0}function se(e,t,i=.2,n){return t.map((r,o)=>{const s=D(o+1,i,n);return s.position.set(...r.position),s.name=`modelhub_marker_${r.annotation.id}`,s.userData.annotationId=r.annotation.id,s.userData.displayNumber=o+1,e.add(s),s})}function ae(e){const{camera:t,model:i,anchors:n,width:r,height:o,isOccluded:s}=e,c=e.referenceDistance??1,u=new m.Vector3,a={};i.updateMatrixWorld();for(const l of n){u.set(...l.position).applyMatrix4(i.matrixWorld);const d=u.distanceTo(t.position),f=s?s(u,d):!1,p=u.clone().project(t);a[l.annotation.id]={x:(p.x*.5+.5)*r,y:(-p.y*.5+.5)*o,scale:Math.min(1.6,Math.max(.55,c/Math.max(d,.001))),zIndex:Math.max(1,Math.round((1-p.z)*1e5))+(f?0:2e5),inView:p.z<1&&p.x>-1.05&&p.x<1.05&&p.y>-1.05&&p.y<1.05,occluded:f}}return a}const L=.03,ce={enabled:!1,isOccluded:()=>!1,dispose:()=>{}};async function le(e){var o;let t=[],i=[];if(e.traverse(s=>{if(!s.isMesh)return;const c=s.geometry;c!=null&&c.computeBoundsTree&&(t.push(c),i.push(s))}),!t.length)return ce;for(const s of t)s.boundsTree||(o=s.computeBoundsTree)==null||o.call(s,{maxLeafTris:8});const n=new m.Raycaster;n.firstHitOnly=!0;const r=new m.Vector3;return{enabled:!0,isOccluded(s,c,u){return r.subVectors(c,s.position).normalize(),n.set(s.position,r),n.near=0,n.far=Math.max(0,u-L),n.camera=s,n.intersectObjects(i,!1).length>0},dispose(){var s;for(const c of t)(s=c.disposeBoundsTree)==null||s.call(c);t=[],i=[]}}}const y={headlight:{ambient:.7,lights:[{color:[1,1,1],intensity:.9,position:[4,8,6]},{color:[1,1,1],intensity:2.4,position:[0,0,0],attach:!0,spot:!0}]},studio:{ambient:.85,lights:[{color:[.78,.8,1],intensity:1.45,position:[1.35,6.75,-7.26]},{color:[1,.88,.71],intensity:1.5,position:[-5.71,3.5,7.43]},{color:[.42,.74,1],intensity:.25,position:[7.23,6.78,1.29]}]},"dual-cam":{ambient:.6,lights:[{color:[.6,.48,.3],intensity:1.6,position:[-1,0,1],attach:!0},{color:[.42,.42,.6],intensity:1.6,position:[1,0,1],attach:!0}]},"studio-cam":{ambient:.7,lights:[{color:[.78,.8,1],intensity:1.35,position:[-1,.3,1],attach:!0},{color:[1,.88,.71],intensity:1.4,position:[1,.3,1],attach:!0},{color:[.42,.74,1],intensity:.25,position:[7.23,6.78,1.29]}]}},U="headlight";function ue(){return Object.keys(y)}function de(e){var t;(t=e.parent)==null||t.remove(e)}function fe(e){var c;const{scene:t,camera:i,exposure:n=1}=e;(c=e.previous)==null||c.remove();const r=y[e.preset??""]??y[U],o=[],s=new m.AmbientLight(16777215,r.ambient*n);t.add(s),o.push(s);for(const u of r.lights){let a;if(u.spot){const d=new m.SpotLight(new m.Color(...u.color));d.angle=.5,d.penumbra=.9,d.decay=0,a=d}else a=new m.DirectionalLight(new m.Color(...u.color));a.intensity=u.intensity,a.position.set(...u.position);const l=a.target;u.attach?(l==null||l.position.set(0,0,u.spot?-1:0),i.add(a),l&&i.add(l)):(t.add(a),l&&t.add(l)),o.push(a),l&&o.push(l)}return{lights:o,remove(){for(const u of o)de(u);o.length=0}}}const b=[{id:"white",label:"White",hex:"#ffffff",light:!0},{id:"light-grey",label:"Light grey",hex:"#d4d4d4",light:!0},{id:"medium-grey",label:"Medium grey",hex:"#808080",light:!1},{id:"dark-grey",label:"Dark grey",hex:"#252525",light:!1},{id:"black",label:"Black",hex:"#000000",light:!1}],F="dark-grey";function V(e){if(typeof e!="string")return"";const t=e.trim().replace(/^#/,"");return/^[0-9a-f]{6}$/i.test(t)?`#${t.toLowerCase()}`:/^[0-9a-f]{3}$/i.test(t)?`#${[...t].map(i=>i+i).join("").toLowerCase()}`:""}function P(e){const t=Number.parseInt(e.replace("#",""),16);return Number.isFinite(t)?(.299*(t>>16&255)+.587*(t>>8&255)+.114*(t&255))/255:0}function me(e,t){const i=V(t);return i?{id:"custom",label:"Custom",hex:i,light:P(i)>.5}:b.find(n=>n.id===e)??b.find(n=>n.id===F)??b[0]}const G=2;function w(e){e.updateMatrixWorld(!0);const t=new m.Box3,i=new m.Box3;return e.traverse(n=>{const r=n,o=n;r.isSkinnedMesh?(r.computeBoundingBox(),r.boundingBox&&(i.copy(r.boundingBox).applyMatrix4(r.matrixWorld),t.union(i))):o.isMesh&&o.geometry&&(o.geometry.boundingBox||o.geometry.computeBoundingBox(),o.geometry.boundingBox&&(i.copy(o.geometry.boundingBox).applyMatrix4(o.matrixWorld),t.union(i)))}),t.isEmpty()?new m.Box3().setFromObject(e):t}function pe(e,t=G){const n=w(e).getSize(new m.Vector3),r=Math.max(n.x,n.y,n.z)||1,o=t/r;e.scale.multiplyScalar(o);const s=w(e);return e.position.sub(s.getCenter(new m.Vector3)),o}function ge(e,t=1.3){return t/Math.tan(e*Math.PI/360)}const z=.5,W=1.5;function he(e,t,i={}){t.updateMatrixWorld();const n=new m.Vector3(...e.position).applyMatrix4(t.matrixWorld);if(e.camera){const u=new m.Vector3(...e.camera.eye).applyMatrix4(t.matrixWorld),l=new m.Vector3(...e.camera.target).applyMatrix4(t.matrixWorld).clone().sub(u);l.lengthSq()<1e-10&&l.copy(n).sub(u),l.normalize();const d=Math.max(n.distanceTo(u),.2);return{position:u,target:u.clone().addScaledVector(l,d),...e.camera.fov?{fov:e.camera.fov}:{}}}const r=i.outward??!0,o=new m.Vector3(n.x,0,n.z);o.lengthSq()<1e-8&&o.set(0,0,1),o.normalize();const s=r?-(i.outwardDistance??z):i.inwardDistance??W,c=n.clone().addScaledVector(o,s);return c.y=n.y,{position:c,target:n}}const xe={cancel:()=>{}};function be(e){const{camera:t,controls:i,to:n,onFrame:r,onDone:o}=e,s=()=>{r==null||r(),o==null||o()};if(e.reducedMotion)return t.position.copy(n.position),i.target.copy(n.target),i.update(),s(),xe;const c=e.durationMs??900,u=t.position.clone(),a=i.target.clone(),l=performance.now();let d=null;const f=p=>{const g=Math.min((p-l)/c,1),O=g*(2-g);t.position.lerpVectors(u,n.position,O),i.target.lerpVectors(a,n.target,O),i.update(),r==null||r(),g<1?d=requestAnimationFrame(f):(d=null,o==null||o())};return d=requestAnimationFrame(f),{cancel(){d!==null&&cancelAnimationFrame(d),d=null}}}function ye(e,t,i){const n=t||i;e.fov!==n&&(e.fov=n,e.updateProjectionMatrix())}const H=500;function Me(e){var r,o,s;if(!e)return"Model loaded, but it carries no educational metadata.";const t=[];(r=e.model)!=null&&r.title&&t.push(`${e.model.title} loaded`),(o=e.model)!=null&&o.altText&&t.push(e.model.altText);const i=((s=e.regions)==null?void 0:s.length)??0,n=M(e).length;return i&&t.push(`${i} labelled ${i===1?"region":"regions"}`),n&&t.push(`${n} ${n===1?"annotation":"annotations"}`),t.map(c=>c.replace(/\.$/,"")).join(". ")+"."}function Ie(e,t,i){return`Annotation ${t+1} of ${i}: ${e||"Untitled"}.`}function we(e,t=H,i=()=>Date.now()){let n=-1/0,r=null,o=null;const s=a=>{n=i(),r=null,e(a)};return{announce:a=>{const l=i()-n;if(l>=t){s(a);return}r=a,o||(o=setTimeout(()=>{o=null,r!==null&&s(r)},t-l))},flush:()=>{o&&(clearTimeout(o),o=null),r!==null&&s(r)}}}function E(e,t){return`## ${e}
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=require("three"),_=new Set(["__proto__","constructor","prototype"]);function U(e,t){const n=new WeakSet,o=[{value:e,path:""}],r=(i,s)=>{o.push({value:i,path:s})};for(;o.length>0;){const{value:i,path:s}=o.pop();if(!(typeof i!="object"||i===null||n.has(i)))if(n.add(i),Array.isArray(i))for(let c=0;c<i.length;c++)r(i[c],`${s}[${c}]`);else t(i,s,r)}}function re(e){const t=[];return U(e,(n,o,r)=>{for(const i of Object.keys(n)){const s=o===""?i:`${o}.${i}`;_.has(i)?t.push({path:s,key:i}):r(n[i],s)}}),t}function ie(e){return U(e,(t,n,o)=>{for(const r of Object.keys(t))_.has(r)?delete t[r]:o(t[r],n)}),e}const L=1179937895,E=2,se=1313821514,ae=21;class y extends Error{constructor(t){super(t),this.name="GlbParseError"}}function ce(e){return e instanceof ArrayBuffer?new DataView(e):new DataView(e.buffer,e.byteOffset,e.byteLength)}function ue(e){return e===null?"null":Array.isArray(e)?"an array":typeof e}function le(e){const t=ce(e);if(t.byteLength<ae)throw new y(`Input too short to be a GLB file (${t.byteLength} bytes).`);const n=t.getUint32(0,!0);if(n!==L)throw new y(`Invalid GLB magic: expected 0x${L.toString(16)}, got 0x${n.toString(16)}.`);const o=t.getUint32(4,!0);if(o!==E)throw new y(`Unsupported GLB version: expected ${E}, got ${o}.`);const r=t.getUint32(8,!0);if(r>t.byteLength)throw new y(`GLB header reports ${r} bytes but only ${t.byteLength} bytes were supplied.`);if(t.byteLength<20)throw new y("GLB file truncated before first chunk header.");const i=t.getUint32(12,!0),s=t.getUint32(16,!0);if(s!==se)throw new y(`First GLB chunk is not JSON (type 0x${s.toString(16)}).`);if(20+i>r)throw new y(`JSON chunk claims ${i} bytes but the declared GLB length is only ${r} bytes.`);if(20+i>t.byteLength)throw new y(`JSON chunk claims ${i} bytes but only ${t.byteLength-20} bytes remain in the buffer.`);const c=new Uint8Array(t.buffer,t.byteOffset+20,i),l=new TextDecoder("utf-8").decode(c);let a;try{a=JSON.parse(l)}catch(u){throw new y(`Failed to parse GLB JSON chunk: ${u.message}`)}if(a===null||typeof a!="object"||Array.isArray(a))throw new y(`GLB JSON chunk root must be an object, got ${ue(a)}.`);return a}function de(e){const t=le(e).extensions;if(t===null||typeof t!="object"||Array.isArray(t))return null;const n=t.EDU_modelhub;return n===void 0?null:ie(n)}const fe=/^[a-z][a-z0-9_]*$/,me=/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/,he=/^[a-z]{2}$/,pe=/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;function d(e,t){return{field:e,message:t,severity:"error"}}function $(e,t){return{field:e,message:t,severity:"warning"}}function x(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function w(e){return typeof e=="string"&&e.trim()!==""}function F(e){return typeof e=="number"&&Number.isInteger(e)&&e>=0}function h(e){if(e===void 0)return"undefined";let t;if(typeof e=="string")t=`"${e}"`;else try{const n=JSON.stringify(e);if(n===void 0)return typeof e;t=n}catch{return Array.isArray(e)?"an array":typeof e}return t.length>60?`${t.slice(0,57)}...`:t}function V(e){const t=[],n=e;if(!x(n))return[d("",`Payload must be a JSON object, got ${h(n)}.`)];ge(n,t),ye(n.specVersion,t),be(n.coordinateSpace,t),$e(n.model,t);const o=Se(n.regions,t);return Ie(n.annotations,o,t),t}function ge(e,t){for(const n of re(e))t.push(d(n.path,`Disallowed key "${n.key}" at ${n.path} (prototype-pollution vector).`))}function ye(e,t){(typeof e!="string"||!pe.test(e))&&t.push(d("specVersion",`specVersion must be a semver string (e.g. "0.1.0"), got ${h(e)}.`))}function be(e,t){e!==void 0&&e!=="gltf"&&e!=="blender"&&t.push(d("coordinateSpace",`coordinateSpace must be "gltf" or "blender" when present, got ${h(e)}.`))}function $e(e,t){if(!x(e)){t.push(d("model",`model is required and must be an object, got ${h(e)}.`));return}const n=e,o=n.title;w(o)||t.push(d("model.title",`model.title is required and must be a non-empty string, got ${h(o)}.`));const r=n.altText;w(r)?r.length>300&&t.push(d("model.altText",`model.altText exceeds 300 characters (${r.length} chars).`)):t.push(d("model.altText",`model.altText is required and must be a non-empty string, got ${h(r)}.`));const i=n.language;i!==void 0&&(typeof i!="string"||!he.test(i))&&t.push($("model.language",`model.language should be an ISO 639-1 two-letter code (e.g. "en"), got ${h(i)}.`)),we(n,t),xe(n.source,t),Ae(n,t),w(n.longDescription)||t.push($("model.longDescription","model.longDescription is recommended."))}function we(e,t){const n=["CC0","CC BY","CC BY-SA","All Rights Reserved"],o=e.license;o!==void 0&&(typeof o!="string"||!n.includes(o))&&t.push(d("model.license",`model.license must be one of ${n.map(r=>`"${r}"`).join(", ")}, got ${h(o)}.`)),(o==="CC BY"||o==="CC BY-SA")&&!w(e.attribution)&&t.push($("model.attribution",'model.attribution is recommended when license is "CC BY" or "CC BY-SA".'))}function xe(e,t){if(e===void 0)return;const n=["scan","photogrammetry","manual","other"];if(typeof e!="string"||!n.includes(e)){const o=n.map(r=>`"${r}"`).join(", ");t.push(d("model.source",`model.source must be one of ${o}, got ${h(e)}.`))}}function Ae(e,t){for(const n of["created","modified"]){const o=e[n];o!==void 0&&(typeof o!="string"||!me.test(o))&&t.push($(`model.${n}`,`model.${n} should be ISO 8601 UTC (YYYY-MM-DDTHH:MM:SSZ), got ${h(o)}.`))}}function Se(e,t){const n=new Set;if(!Array.isArray(e)||e.length===0)return t.push(d("regions","At least one region is required.")),n;for(let o=0;o<e.length;o++){const r=e[o],i=`regions[${o}]`;if(!x(r)){t.push(d(i,`${i} must be an object, got ${h(r)}.`));continue}ve(r,i,n,t)}return n}function ve(e,t,n,o){const r=e.id;typeof r!="string"||!fe.test(r)?o.push(d(`${t}.id`,`${t}.id must be a non-empty snake_case string (lowercase letters, digits, underscores; must start with a letter), got ${h(r)}.`)):n.has(r)?o.push(d(`${t}.id`,`Duplicate region id "${r}" at ${t}.`)):n.add(r);const i=e.visibilityDefault;i!==void 0&&i!=="shown"&&i!=="hidden"&&o.push(d(`${t}.visibilityDefault`,`${t}.visibilityDefault must be "shown" or "hidden", got ${h(i)}.`));const s=e.order;s!==void 0&&!F(s)&&o.push($(`${t}.order`,`${t}.order should be a non-negative integer, got ${h(s)}.`))}function Ie(e,t,n){if(!Array.isArray(e)){n.push(d("annotations","annotations must be an array."));return}e.length<3&&n.push($("annotations",`Only ${e.length} annotation(s) defined (3–8 recommended).`)),e.length>8&&n.push($("annotations",`${e.length} annotations defined (3–8 recommended).`));const o={seenRegionIds:t,seenAnnotationIds:new Set,seenReadingOrders:new Set};for(let r=0;r<e.length;r++){const i=e[r],s=`annotations[${r}]`;if(!x(i)){n.push(d(s,`${s} must be an object, got ${h(i)}.`));continue}Me(i,s,o,n)}Be(o.seenReadingOrders,n)}function Me(e,t,n,o){const r=e.id;w(r)?n.seenAnnotationIds.has(r)?o.push(d(`${t}.id`,`Duplicate annotation id "${r}" at ${t}.`)):n.seenAnnotationIds.add(r):o.push(d(`${t}.id`,`${t}.id is required and must be a non-empty string, got ${h(r)}.`));const i=e.regionId;i!==void 0&&i!==""&&(typeof i!="string"||!n.seenRegionIds.has(i))&&o.push(d(`${t}.regionId`,`${t}.regionId ${h(i)} does not match any region id.`)),Oe(e.readingOrder,t,n,o),Te(e.anchor,t,o),Ne(e.camera,t,o)}function Oe(e,t,n,o){e!==void 0&&(F(e)?n.seenReadingOrders.has(e)?o.push(d(`${t}.readingOrder`,`Duplicate readingOrder value ${e} at ${t}.`)):n.seenReadingOrders.add(e):o.push(d(`${t}.readingOrder`,`${t}.readingOrder must be a non-negative integer, got ${h(e)}.`)))}function Te(e,t,n){if(!x(e)){n.push(d(`${t}.anchor`,`${t}.anchor is required and must be an object, got ${h(e)}.`));return}const o=e,r=o.type;if(r==="marker"){const i=o.position;(!Array.isArray(i)||i.length!==3||!i.every(s=>typeof s=="number"))&&n.push(d(`${t}.anchor.position`,`${t}.anchor.position must be an array of exactly 3 numbers for type "marker".`))}else r==="node"||r==="primitive"||r==="aabb"?w(o.ref)||n.push(d(`${t}.anchor.ref`,`${t}.anchor.ref is required for anchor type "${r}".`)):n.push(d(`${t}.anchor.type`,`${t}.anchor.type must be "marker", "node", "primitive", or "aabb", got ${h(r)}.`))}function Ne(e,t,n){if(e===void 0)return;if(!x(e)){n.push(d(`${t}.camera`,`${t}.camera must be an object when present, got ${h(e)}.`));return}for(const r of["eye","target"]){const i=e[r];(!Array.isArray(i)||i.length!==3||!i.every(s=>typeof s=="number"&&Number.isFinite(s)))&&n.push(d(`${t}.camera.${r}`,`${t}.camera.${r} must be an array of exactly 3 finite numbers.`))}const o=e.fov;o!==void 0&&(typeof o!="number"||!(o>0))&&n.push(d(`${t}.camera.fov`,`${t}.camera.fov must be a number greater than 0 when present, got ${h(o)}.`))}function Be(e,t){if(e.size===0)return;const n=[...e].sort((o,r)=>o-r);for(let o=0;o<n.length;o++)if(n[o]!==o){t.push($("annotations[*].readingOrder",`readingOrder values are not sequential from 0. Expected 0–${n.length-1} but found gaps or wrong start.`));break}}class k extends Error{constructor(t){super(t),this.name="AdaptError"}}class Ce{adapt(t){var n,o;const r=t.model,i=((n=r==null?void 0:r.title)==null?void 0:n.trim())??"",s=((o=r==null?void 0:r.altText)==null?void 0:o.trim())??"";if(!i)throw new k("Sidecar is missing required field: model.title");if(!s)throw new k("Sidecar is missing required field: model.altText");return t}}const Re=new Ce;function Le(e){return Re.adapt(e)}function G(e){return e.filter(t=>t.severity==="error")}function P(e){const[t,n=""]=e.split("?",2);if(!/\.glb$/i.test(t))return null;const o=t.replace(/\.glb$/i,".modelhub.json");return n?`${o}?${n}`:o}function Ee(e,t){var i;if(!e||e.startsWith("blob:")||e.startsWith("data:"))return"";const n=t??((i=globalThis.location)==null?void 0:i.href),o=n?new URL(e,n).href:e,r=o.lastIndexOf("/");return r===-1?"":o.slice(0,r+1)}async function z(e,t=fetch){const n=await t(e);if(!n.ok)throw new Error(`Failed to fetch ${e} (HTTP ${n.status}).`);return n.arrayBuffer()}function j(e){const t=de(e);if(!t)return{payload:null,source:"none",findings:[],problem:"This model carries no EDU_modelhub metadata."};const n=V(t);return{payload:t,source:"embedded",findings:n,problem:null}}async function ke(e,t={}){const n=t.fetchImpl??fetch,o=await z(e,n),r=j(o);if(r.payload)return{...r,bytes:o};if(t.sidecar===!1)return{...r,bytes:o};const s=(t.sidecarUrl??P)(e);if(!s)return{...r,bytes:o};try{const c=await n(s);if(c.ok){const l=Le(await c.json());return{payload:l,source:"sidecar",findings:V(l),problem:null,bytes:o}}}catch{}return{payload:null,source:"none",findings:[],problem:"This model has no EDU_modelhub metadata, either embedded in the file or in a sidecar beside it.",bytes:o}}function De(e,t="EDU_modelhub",n=console){if(!(e!=null&&e.length))return;const o=G(e),r=e.filter(i=>i.severity!=="error");o.length&&n.warn(`${t}: ${o.length} validation error(s)`,o),r.length&&n.info(`${t}: ${r.length} validation warning(s)`,r)}function _e(e){return(e==null?void 0:e.name)==="GlbParseError"}function T(e){return Array.isArray(e)&&e.length===3&&e.every(t=>typeof t=="number"&&Number.isFinite(t))}function N(e){return e==="blender"}function A(e,t){if(!T(e))return null;const[n,o,r]=e;return N(t)?[n,r,-o]:[n,o,r]}function Ue(e,t){if(!T(e))return null;const[n,o,r]=e;return N(t)?[n,-r,o]:[n,o,r]}function W(e){if(!Array.isArray(e))return[];const t=new Set;return e.map((n,o)=>{const i=(typeof(n==null?void 0:n.id)=="string"?n.id.trim():"")||`annotation-${o+1}`;let s=i,c=2;for(;t.has(s);)s=`${i}-${c++}`;return t.add(s),{...n??{},id:s}})}function I(e){const t=W(e==null?void 0:e.annotations),n=o=>Number.isFinite(o.readingOrder)?o.readingOrder:Number.MAX_SAFE_INTEGER;return t.map((o,r)=>({annotation:o,index:r})).sort((o,r)=>n(o.annotation)-n(r.annotation)||o.index-r.index).map(o=>o.annotation)}function B(e){return(Array.isArray(e==null?void 0:e.regions)?e.regions:[]).map((n,o)=>({region:n,index:o})).sort((n,o)=>(n.region.order??Number.MAX_SAFE_INTEGER)-(o.region.order??Number.MAX_SAFE_INTEGER)||n.index-o.index).map(n=>n.region)}function M(e){var t;return e?((t=e.label)==null?void 0:t.trim())||e.id||"Unnamed region":"Unknown region"}function Fe(e){if(!e)return[];const t=[],n=/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g;let o=0,r;for(;(r=n.exec(e))!==null;)r.index>o&&t.push({text:e.slice(o,r.index)}),t.push({text:r[1],url:r[2]}),o=r.index+r[0].length;return o<e.length&&t.push({text:e.slice(o)}),t.filter(i=>i.text.trim()||i.url)}function Ve(e){const t=e==null?void 0:e.coordinateSpace,n=B(e),o=I(e),r=new Map(n.map(a=>[a.id,a])),i=new Map(o.map(a=>[a.id,a])),s=new Map;for(const a of o){const u=a.regionId;if(!u)continue;const f=s.get(u);f?f.push(a):s.set(u,[a])}const c=[];for(const a of o){const u=a.anchor;if(!u||u.type!=="marker")continue;const f=A(u.position,t);if(!f)continue;const m=a.camera,g=m?A(m.eye,t):null,b=m?A(m.target,t):null;c.push({annotation:a,position:f,...g&&b?{camera:{eye:g,target:b,...m!=null&&m.fov?{fov:m.fov}:{}}}:{}})}const l=new Set(n.filter(a=>a.visibilityDefault==="hidden").map(a=>a.id));return{payload:e,regions:n,regionsById:r,annotations:o,annotationsById:i,annotationsByRegion:s,markerAnchors:c,hiddenRegionIds:l}}function Ge(e){return e.isMesh===!0}function Pe(e,t,n=[]){var l;const o=new Map,r=new Map,i=new Map,s=new Map;for(const[a,u]of((t==null?void 0:t.meshes)??[]).entries())for(const f of u.primitives??[]){const m=(l=f.extras)==null?void 0:l.modelhubRegionId;typeof m=="string"&&m&&(s.set(a,m),u.name&&r.set(u.name,m))}for(const a of(t==null?void 0:t.nodes)??[]){if(a.mesh===void 0||!a.name)continue;const u=s.get(a.mesh);u&&i.set(a.name,u)}const c=new Set(n.map(a=>a.id));return e.traverse(a=>{var g,b;if(!Ge(a))return;const u=(b=(g=a.geometry)==null?void 0:g.userData)==null?void 0:b.modelhubRegionId,f=typeof u=="string"&&u||i.get(a.name)||r.get(a.name)||(c.has(a.name)?a.name:void 0);if(!f)return;const m=o.get(f);m?m.push(a):o.set(f,[a]),a.userData.modelhubOriginalMaterial===void 0&&(a.userData.modelhubOriginalMaterial=a.material)}),o}function ze(e){const t={};for(const[n,o]of e)t[n]=o.length;return t}function q(e,t,n){for(const o of e.get(t)??[])o.visible=n}function je(e,t){for(const n of e.keys())q(e,n,!t.has(n))}function We(e,t,n){for(const o of e.get(t)??[])o.material=n}function qe(e,t){for(const n of e.get(t)??[]){const o=n.userData.modelhubOriginalMaterial;o&&(n.material=o)}}const H="#2196f3",He="#ff5722";function C(e,t={}){const{fill:n=H,text:o="#ffffff",textStroke:r="rgba(0, 0, 0, 0.45)"}=t,i=256,s=document.createElement("canvas");s.width=i,s.height=i;const c=s.getContext("2d");return c&&(c.beginPath(),c.arc(i/2,i/2,i/2-4,0,Math.PI*2),c.fillStyle=n,c.fill(),c.strokeStyle="#ffffff",c.lineWidth=8,c.stroke(),c.fillStyle=o,c.strokeStyle=r,c.lineWidth=10,c.font="bold 132px Arial, sans-serif",c.textAlign="center",c.textBaseline="middle",c.strokeText(String(e),i/2,i/2),c.fillText(String(e),i/2,i/2)),s}function Y(e,t=.2,n){const o=new p.SpriteMaterial({map:new p.CanvasTexture(C(e,n)),transparent:!0,depthTest:!1}),r=new p.Sprite(o);return r.scale.set(t,t,1),r.renderOrder=999,r}function Ye(e,t,n){var r;const o=e.material;(r=o.map)==null||r.dispose(),o.map=new p.CanvasTexture(C(t,{fill:n})),o.needsUpdate=!0}function Ke(e,t,n=.2,o){return t.map((r,i)=>{const s=Y(i+1,n,o);return s.position.set(...r.position),s.name=`modelhub_marker_${r.annotation.id}`,s.userData.annotationId=r.annotation.id,s.userData.displayNumber=i+1,e.add(s),s})}function Ze(e){const{camera:t,model:n,anchors:o,width:r,height:i,isOccluded:s}=e,c=e.referenceDistance??1,l=new p.Vector3,a={};n.updateMatrixWorld();for(const u of o){l.set(...u.position).applyMatrix4(n.matrixWorld);const f=l.distanceTo(t.position),m=s?s(l,f):!1,g=l.clone().project(t);a[u.annotation.id]={x:(g.x*.5+.5)*r,y:(-g.y*.5+.5)*i,scale:Math.min(1.6,Math.max(.55,c/Math.max(f,.001))),zIndex:Math.max(1,Math.round((1-g.z)*1e5))+(m?0:2e5),inView:g.z<1&&g.x>-1.05&&g.x<1.05&&g.y>-1.05&&g.y<1.05,occluded:m}}return a}const K=.03,Xe={enabled:!1,isOccluded:()=>!1,dispose:()=>{}};async function Qe(e){var i;let t=[],n=[];if(e.traverse(s=>{if(!s.isMesh)return;const c=s.geometry;c!=null&&c.computeBoundsTree&&(t.push(c),n.push(s))}),!t.length)return Xe;for(const s of t)s.boundsTree||(i=s.computeBoundsTree)==null||i.call(s,{maxLeafTris:8});const o=new p.Raycaster;o.firstHitOnly=!0;const r=new p.Vector3;return{enabled:!0,isOccluded(s,c,l){return r.subVectors(c,s.position).normalize(),o.set(s.position,r),o.near=0,o.far=Math.max(0,l-K),o.camera=s,o.intersectObjects(n,!1).length>0},dispose(){var s;for(const c of t)(s=c.disposeBoundsTree)==null||s.call(c);t=[],n=[]}}}const v={headlight:{ambient:.7,lights:[{color:[1,1,1],intensity:.9,position:[4,8,6]},{color:[1,1,1],intensity:2.4,position:[0,0,0],attach:!0,spot:!0}]},studio:{ambient:.85,lights:[{color:[.78,.8,1],intensity:1.45,position:[1.35,6.75,-7.26]},{color:[1,.88,.71],intensity:1.5,position:[-5.71,3.5,7.43]},{color:[.42,.74,1],intensity:.25,position:[7.23,6.78,1.29]}]},"dual-cam":{ambient:.6,lights:[{color:[.6,.48,.3],intensity:1.6,position:[-1,0,1],attach:!0},{color:[.42,.42,.6],intensity:1.6,position:[1,0,1],attach:!0}]},"studio-cam":{ambient:.7,lights:[{color:[.78,.8,1],intensity:1.35,position:[-1,.3,1],attach:!0},{color:[1,.88,.71],intensity:1.4,position:[1,.3,1],attach:!0},{color:[.42,.74,1],intensity:.25,position:[7.23,6.78,1.29]}]}},Z="headlight";function Je(){return Object.keys(v)}function et(e){var t;(t=e.parent)==null||t.remove(e)}function tt(e){var c;const{scene:t,camera:n,exposure:o=1}=e;(c=e.previous)==null||c.remove();const r=v[e.preset??""]??v[Z],i=[],s=new p.AmbientLight(16777215,r.ambient*o);t.add(s),i.push(s);for(const l of r.lights){let a;if(l.spot){const f=new p.SpotLight(new p.Color(...l.color));f.angle=.5,f.penumbra=.9,f.decay=0,a=f}else a=new p.DirectionalLight(new p.Color(...l.color));a.intensity=l.intensity,a.position.set(...l.position);const u=a.target;l.attach?(u==null||u.position.set(0,0,l.spot?-1:0),n.add(a),u&&n.add(u)):(t.add(a),u&&t.add(u)),i.push(a),u&&i.push(u)}return{lights:i,remove(){for(const l of i)et(l);i.length=0}}}const S=[{id:"white",label:"White",hex:"#ffffff",light:!0},{id:"light-grey",label:"Light grey",hex:"#d4d4d4",light:!0},{id:"medium-grey",label:"Medium grey",hex:"#808080",light:!1},{id:"dark-grey",label:"Dark grey",hex:"#252525",light:!1},{id:"black",label:"Black",hex:"#000000",light:!1}],X="dark-grey";function Q(e){if(typeof e!="string")return"";const t=e.trim().replace(/^#/,"");return/^[0-9a-f]{6}$/i.test(t)?`#${t.toLowerCase()}`:/^[0-9a-f]{3}$/i.test(t)?`#${[...t].map(n=>n+n).join("").toLowerCase()}`:""}function J(e){const t=Number.parseInt(e.replace("#",""),16);return Number.isFinite(t)?(.299*(t>>16&255)+.587*(t>>8&255)+.114*(t&255))/255:0}function nt(e,t){const n=Q(t);return n?{id:"custom",label:"Custom",hex:n,light:J(n)>.5}:S.find(o=>o.id===e)??S.find(o=>o.id===X)??S[0]}const ee=2;function O(e){e.updateMatrixWorld(!0);const t=new p.Box3,n=new p.Box3;return e.traverse(o=>{const r=o,i=o;r.isSkinnedMesh?(r.computeBoundingBox(),r.boundingBox&&(n.copy(r.boundingBox).applyMatrix4(r.matrixWorld),t.union(n))):i.isMesh&&i.geometry&&(i.geometry.boundingBox||i.geometry.computeBoundingBox(),i.geometry.boundingBox&&(n.copy(i.geometry.boundingBox).applyMatrix4(i.matrixWorld),t.union(n)))}),t.isEmpty()?new p.Box3().setFromObject(e):t}function ot(e,t=ee){const o=O(e).getSize(new p.Vector3),r=Math.max(o.x,o.y,o.z)||1,i=t/r;e.scale.multiplyScalar(i);const s=O(e);return e.position.sub(s.getCenter(new p.Vector3)),i}function rt(e,t=1.3){return t/Math.tan(e*Math.PI/360)}const te=.5,ne=1.5;function it(e,t,n={}){t.updateMatrixWorld();const o=new p.Vector3(...e.position).applyMatrix4(t.matrixWorld);if(e.camera){const l=new p.Vector3(...e.camera.eye).applyMatrix4(t.matrixWorld),u=new p.Vector3(...e.camera.target).applyMatrix4(t.matrixWorld).clone().sub(l);u.lengthSq()<1e-10&&u.copy(o).sub(l),u.normalize();const f=Math.max(o.distanceTo(l),.2);return{position:l,target:l.clone().addScaledVector(u,f),...e.camera.fov?{fov:e.camera.fov}:{}}}const r=n.outward??!0,i=new p.Vector3(o.x,0,o.z);i.lengthSq()<1e-8&&i.set(0,0,1),i.normalize();const s=r?-(n.outwardDistance??te):n.inwardDistance??ne,c=o.clone().addScaledVector(i,s);return c.y=o.y,{position:c,target:o}}const st={cancel:()=>{}};function at(e){const{camera:t,controls:n,to:o,onFrame:r,onDone:i}=e,s=()=>{r==null||r(),i==null||i()};if(e.reducedMotion)return t.position.copy(o.position),n.target.copy(o.target),n.update(),s(),st;const c=e.durationMs??900,l=t.position.clone(),a=n.target.clone(),u=performance.now();let f=null;const m=g=>{const b=Math.min((g-u)/c,1),R=b*(2-b);t.position.lerpVectors(l,o.position,R),n.target.lerpVectors(a,o.target,R),n.update(),r==null||r(),b<1?f=requestAnimationFrame(m):(f=null,i==null||i())};return f=requestAnimationFrame(m),{cancel(){f!==null&&cancelAnimationFrame(f),f=null}}}function ct(e,t,n){const o=t||n;e.fov!==o&&(e.fov=o,e.updateProjectionMatrix())}const oe=500;function ut(e){var r,i,s;if(!e)return"Model loaded, but it carries no educational metadata.";const t=[];(r=e.model)!=null&&r.title&&t.push(`${e.model.title} loaded`),(i=e.model)!=null&&i.altText&&t.push(e.model.altText);const n=((s=e.regions)==null?void 0:s.length)??0,o=I(e).length;return n&&t.push(`${n} labelled ${n===1?"region":"regions"}`),o&&t.push(`${o} ${o===1?"annotation":"annotations"}`),t.map(c=>c.replace(/\.$/,"")).join(". ")+"."}function lt(e,t,n){return`Annotation ${t+1} of ${n}: ${e||"Untitled"}.`}function dt(e,t=oe,n=()=>Date.now()){let o=-1/0,r=null,i=null;const s=a=>{o=n(),r=null,e(a)};return{announce:a=>{const u=n()-o;if(u>=t){s(a);return}r=a,i||(i=setTimeout(()=>{i=null,r!==null&&s(r)},t-u))},flush:()=>{i&&(clearTimeout(i),i=null),r!==null&&s(r)}}}function D(e,t){return`## ${e}
|
|
2
2
|
${t}
|
|
3
3
|
|
|
4
|
-
`}function
|
|
4
|
+
`}function ft(e,t={}){const n=e.model??{},o=B(e),r=I(e);let i=`# ${n.title||"Untitled model"}
|
|
5
5
|
|
|
6
|
-
`;if(
|
|
6
|
+
`;if(n.altText&&(i+=D(t.summaryHeading??"Summary",n.altText)),n.longDescription&&(i+=D("Full description",n.longDescription)),o.length){i+=`## Regions (${o.length})
|
|
7
7
|
|
|
8
|
-
`;for(const s of
|
|
8
|
+
`;for(const s of o)i+=`### ${M(s)}
|
|
9
9
|
${s.description||"No description."}
|
|
10
10
|
|
|
11
|
-
`}return r.length&&(
|
|
11
|
+
`}return r.length&&(i+=`## Annotations (${r.length})
|
|
12
12
|
|
|
13
|
-
`,r.forEach((s,c)=>{const
|
|
13
|
+
`,r.forEach((s,c)=>{const l=s.regionId?` (${M(o.find(u=>u.id===s.regionId))})`:"",a=`${c+1}. ${s.title||"Untitled"}${l}`;i+=`### ${a}
|
|
14
14
|
${s.body||"No description."}
|
|
15
15
|
|
|
16
|
-
`})),
|
|
17
|
-
`,
|
|
18
|
-
`,
|
|
19
|
-
`,
|
|
20
|
-
`,
|
|
21
|
-
`,
|
|
16
|
+
`})),i+=`## Licensing
|
|
17
|
+
`,i+=`- **License:** ${n.license||"Not specified"}
|
|
18
|
+
`,i+=`- **Attribution:** ${n.attribution||"Not specified"}
|
|
19
|
+
`,i+=`- **Creator:** ${n.creator||"Not specified"}
|
|
20
|
+
`,i+=`- **Source:** ${n.source||"Not specified"}
|
|
21
|
+
`,i}function mt(e){var n;return`${(((n=e.model)==null?void 0:n.title)||"model").trim().replace(/[^\w\-. ]+/g,"").replace(/\s+/g,"_")||"model"}_transcript.md`}function ht(e){e&&e.traverse(t=>{var i,s;const n=t,o=n.geometry;o&&(o.boundsTree&&o.disposeBoundsTree&&o.disposeBoundsTree(),(i=o.dispose)==null||i.call(o));const r=[].concat(n.material??[]);for(const c of r)if(c){for(const l of Object.values(c)){const a=l;a&&a.isTexture&&a.dispose()}(s=c.dispose)==null||s.call(c)}})}exports.BACKGROUND_OPTIONS=S;exports.CAMERA_ANNOUNCE_INTERVAL=oe;exports.DEFAULT_BACKGROUND=X;exports.DEFAULT_LIGHTING=Z;exports.LIGHTING_PRESETS=v;exports.MARKER_FILL=H;exports.MARKER_FILL_ACTIVE=He;exports.NORMALIZED_SIZE=ee;exports.OCCLUSION_BIAS=K;exports.TOUR_DISTANCE_INWARD=ne;exports.TOUR_DISTANCE_OUTWARD=te;exports.annotationSummary=lt;exports.annotationsInReadingOrder=I;exports.applyFov=ct;exports.applyLighting=tt;exports.applyVisibilityDefaults=je;exports.buildRegionMeshMap=Pe;exports.buildTranscript=ft;exports.clearRegionHighlight=qe;exports.computeDisplayBox=O;exports.computeTourPlacement=it;exports.createAnnouncer=dt;exports.createMarkerSprite=Y;exports.createMarkerSprites=Ke;exports.createOcclusionIndex=Qe;exports.disposeSceneGraph=ht;exports.distanceForFov=rt;exports.drawMarkerCanvas=C;exports.errorsOf=G;exports.fetchBytes=z;exports.flyCamera=at;exports.frameModel=ot;exports.fromViewerPosition=Ue;exports.highlightRegion=We;exports.isGlbParseError=_e;exports.isVec3=T;exports.lightingPresetNames=Je;exports.loadModel=ke;exports.logFindings=De;exports.luminance=J;exports.modelSummary=ut;exports.needsConversion=N;exports.normalizeAnnotationIds=W;exports.normalizeHexColor=Q;exports.normalizeModel=Ve;exports.parseBodySegments=Fe;exports.projectMarkers=Ze;exports.readEmbedded=j;exports.regionBindingCounts=ze;exports.regionLabel=M;exports.regionsInOrder=B;exports.resolveBackground=nt;exports.resourcePathFor=Ee;exports.setMarkerSpriteColor=Ye;exports.setRegionVisible=q;exports.sidecarUrlFor=P;exports.toViewerPosition=A;exports.transcriptFileName=mt;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* renderer and its own UI; this package supplies the behaviour underneath so
|
|
11
11
|
* two viewers of the same model agree about what it says.
|
|
12
12
|
*/
|
|
13
|
-
export {
|
|
13
|
+
export { errorsOf, fetchBytes, loadModel, logFindings, isGlbParseError, readEmbedded, resourcePathFor, sidecarUrlFor, type LoadOptions, type LoadedModel, type MetadataResult, type MetadataSource } from "./metadata.js";
|
|
14
|
+
export type { Annotation, AnnotationCamera, EduModelHub, ModelLicense, ModelMetadata, ModelSource, Region, ValidationResult, ValidationSeverity, VisibilityDefault } from "./types.js";
|
|
14
15
|
export { fromViewerPosition, isVec3, needsConversion, toViewerPosition, type Vec3 } from "./coordinates.js";
|
|
15
16
|
export { annotationsInReadingOrder, normalizeAnnotationIds, normalizeModel, parseBodySegments, regionLabel, regionsInOrder, type BodySegment, type IdentifiedAnnotation, type MarkerAnchor, type NormalizedModel } from "./normalize.js";
|
|
16
17
|
export { applyVisibilityDefaults, buildRegionMeshMap, clearRegionHighlight, highlightRegion, regionBindingCounts, setRegionVisible, type GltfJsonLike, type RegionMeshMap } from "./regions.js";
|