diff --git a/.gitignore b/.gitignore index be33db3..e648646 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ Thumbs.db .copilot/* .worktrees/ +tmp \ No newline at end of file diff --git a/skills/README.md b/skills/README.md index 96981f3..713de31 100644 --- a/skills/README.md +++ b/skills/README.md @@ -22,11 +22,38 @@ The Cesium platform provides powerful tools for 3D geospatial visualization and - Implement common patterns across CesiumJS development, ion asset management, and 3D Tiles workflows - Troubleshoot platform-specific issues and integrate Cesium technologies effectively -## 📚 Available Skills +## Available Skills -This directory contains Cesium ecosystem agent skills: +This directory contains Cesium ecosystem agent skills organized into two categories: -- **[cesium-context7](./cesium-context7/)**: Comprehensive knowledge of the Cesium platform, including CesiumJS APIs, Cesium ion workflows, 3D Tiles optimization, and best practices for building 3D geospatial applications. Use this skill to prevent hallucinations and ensure accurate Cesium-specific guidance. +### CesiumJS Domain Skills (Baked-In Reference) + +Self-contained, domain-level skills that passively activate when developers ask CesiumJS questions. Each skill provides a Quick Start, API reference, runnable code examples, performance tips, and cross-references. Based on CesiumJS v1.139.x. + +| # | Skill | Description | +|---|-------|-------------| +| 1 | **[cesiumjs-spatial-math](./cesiumjs-spatial-math/)** | Cartesian3, Matrix4, Transforms, Ellipsoid, BoundingSphere, projections | +| 2 | **[cesiumjs-core-utilities](./cesiumjs-core-utilities/)** | Resource, Color, Event, RequestScheduler, error handling, helper functions | +| 3 | **[cesiumjs-time-properties](./cesiumjs-time-properties/)** | Clock, JulianDate, Property system, SampledProperty, splines, interpolation | + +The domain mapping and class ownership rules are documented in **[cesiumjs/DOMAINS.md](./cesiumjs/DOMAINS.md)**. + +### Live Documentation Skill + +- **[cesium-context7](./cesium-context7/)**: Fetches up-to-date Cesium documentation via Context7 MCP tools. Covers CesiumJS, Cesium for Unreal, Cesium for Unity, and the 3D Tiles specification. + +### How the Two Skill Types Work Together + +The baked-in `cesiumjs-*` skills and the live `cesium-context7` skill are **independent and complementary**: + +| | Baked-in Skills (`cesiumjs-*`) | Context7 (`cesium-context7`) | +|---|---|---| +| **Latency** | Instant | Requires MCP round-trip | +| **Availability** | Always works, no MCP required | Requires Context7 MCP server | +| **Content** | Curated patterns, examples, best practices | Full official docs, version-specific | +| **Best for** | Common patterns, Quick Starts, "how do I..." | Version-pinned API signatures, cutting-edge features | + +Both can activate simultaneously. Use baked-in skills for patterns and recipes; use Context7 to validate against a specific CesiumJS version or query Unreal/Unity/3D Tiles spec docs. ## 🚀 Using These Skills diff --git a/skills/cesiumjs-core-utilities/SKILL.md b/skills/cesiumjs-core-utilities/SKILL.md new file mode 100644 index 0000000..e3da2d7 --- /dev/null +++ b/skills/cesiumjs-core-utilities/SKILL.md @@ -0,0 +1,403 @@ +--- +name: cesiumjs-core-utilities +description: "CesiumJS core utilities and networking - Resource, Color, Event, Request, RequestScheduler, error handling, helper functions, feature detection. Use when fetching remote data, managing HTTP requests, working with colors, handling events, debugging errors, or using utility functions like defined, clone, or buildModuleUrl." +--- +# CesiumJS Core Utilities & Networking + +Version baseline: CesiumJS v1.139+ (ES module imports, `defaultValue` removed in v1.134) + +## Breaking Change: defaultValue Removed (v1.134) + +```js +// WRONG (removed in v1.134) +const name = defaultValue(options.name, "default"); +const opts = defaultValue(options, defaultValue.EMPTY_OBJECT); + +// CORRECT (v1.134+) +import { Frozen } from "cesium"; +const name = options.name ?? "default"; +const opts = options ?? Frozen.EMPTY_OBJECT; +``` + +`Frozen.EMPTY_OBJECT` is `Object.freeze({})` and `Frozen.EMPTY_ARRAY` is `Object.freeze([])`. Use them as safe defaults for options objects and array parameters. + +## Resource: HTTP Requests and Data Fetching + +`Resource` is the unified class for all HTTP operations. It wraps URL construction, query parameters, headers, proxying, and retry logic. + +### Fetching Data + +```js +import { Resource } from "cesium"; + +// Static shorthand: accepts a URL string or options object +const jsonData = await Resource.fetchJson({ url: "https://api.example.com/data.json" }); + +// Instance-based: construct once, reuse for multiple fetches +const resource = new Resource({ + url: "https://api.example.com/features", + queryParameters: { format: "json", limit: "100" }, + headers: { "Authorization": "Bearer my-token" }, +}); +const features = await resource.fetchJson(); +const text = await resource.fetchText(); // string +const buffer = await resource.fetchArrayBuffer(); // ArrayBuffer +const blob = await resource.fetchBlob(); // Blob +const image = await resource.fetchImage(); // HTMLImageElement or ImageBitmap +``` + +### Derived Resources and Template Values + +```js +import { Resource } from "cesium"; + +const api = new Resource({ + url: "https://tiles.example.com/{version}/tiles/{z}/{x}/{y}.png", + templateValues: { version: "v2" }, + headers: { "X-Api-Key": "abc123" }, +}); + +// getDerivedResource inherits headers, proxy, and retry settings +const tile = api.getDerivedResource({ + templateValues: { z: "10", x: "512", y: "384" }, +}); +const tileImage = await tile.fetchImage(); + +// Modify query parameters on an existing resource +resource.setQueryParameters({ access_token: "new-token" }); +resource.appendQueryParameters({ extra: "param" }); +``` + +### Retry and Proxy + +```js +import { Resource, DefaultProxy } from "cesium"; + +// Retry on specific HTTP status codes +const resource = new Resource({ + url: "https://api.example.com/unstable", + retryAttempts: 3, + retryCallback: (resource, error) => { + if (error.statusCode === 429) { + return new Promise((resolve) => setTimeout(() => resolve(true), 2000)); + } + return false; + }, +}); + +// DefaultProxy appends the target URL as a query parameter +const proxied = new Resource({ + url: "https://external-server.com/data.json", + proxy: new DefaultProxy("/proxy/"), +}); +// Request goes to: /proxy/?https%3A%2F%2Fexternal-server.com%2Fdata.json +``` + +### POST and PUT + +```js +import { Resource } from "cesium"; + +const resource = new Resource({ url: "https://api.example.com/upload" }); +const result = await resource.post(JSON.stringify({ name: "test" }), { + headers: { "Content-Type": "application/json" }, +}); +// resource.put() works the same way +``` + +## Color + +RGBA components as floats [0.0, 1.0]. Over 140 named constants as frozen static properties (e.g., `Color.RED`, `Color.CORNFLOWERBLUE`, `Color.TRANSPARENT`). + +### Creating Colors + +```js +import { Color } from "cesium"; + +const red = Color.RED; // frozen constant +const custom = new Color(0.2, 0.6, 0.8, 1.0); // float constructor +const blue = Color.fromCssColorString("#3498db"); // hex string +const semiRed = Color.fromCssColorString("rgba(255,0,0,0.5)"); // CSS rgba() +const coral = Color.fromBytes(255, 127, 80, 255); // 0-255 bytes +const hsl = Color.fromHsl(0.58, 0.8, 0.5, 1.0); // hue/sat/light +const bright = Color.fromRandom({ // constrained random + minimumRed: 0.75, minimumGreen: 0.75, minimumBlue: 0.75, alpha: 1.0, +}); +``` + +### Manipulation and Conversion + +```js +import { Color } from "cesium"; + +const base = Color.fromCssColorString("#3498db"); +const translucent = base.withAlpha(0.5); // new Color with alpha +const lighter = base.brighten(0.3, new Color()); // requires result param +const darker = base.darken(0.3, new Color()); +const css = base.toCssColorString(); // "rgb(52,152,219)" +const hex = base.toCssHexString(); // "#3498db" +const bytes = base.toBytes(); // [52, 152, 219, 255] +const equal = Color.RED.equals(new Color(1.0, 0.0, 0.0, 1.0)); // true +``` + +## Event System + +`Event` is the publish-subscribe mechanism used throughout CesiumJS. Classes expose Event properties like `Viewer.selectedEntityChanged` and `Cesium3DTileset.tileLoad`. + +### Basic Usage + +```js +import { Event } from "cesium"; + +const onDataReceived = new Event(); + +// addEventListener returns a removal function +const removeListener = onDataReceived.addEventListener((data) => { + console.log("Received:", data); +}); + +onDataReceived.raiseEvent({ id: 1, value: "test" }); // invoke all listeners +removeListener(); // unsubscribe +``` + +### EventHelper for Batch Cleanup + +```js +import { EventHelper } from "cesium"; + +const helper = new EventHelper(); +helper.add(viewer.selectedEntityChanged, (entity) => { + console.log("Selected:", entity?.name); +}); +helper.add(viewer.clock.onTick, (clock) => { /* per-frame logic */ }); +helper.add(viewer.scene.globe.tileLoadProgressEvent, (queueLength) => { + console.log("Tiles loading:", queueLength); +}); + +// Remove all listeners at once (e.g., in a destroy method) +helper.removeAll(); +``` + +## RequestScheduler Configuration + +`RequestScheduler` is a singleton that manages concurrent request limits. `Request` objects represent individual HTTP requests with priority and throttling (primarily internal). + +```js +import { RequestScheduler } from "cesium"; + +RequestScheduler.maximumRequests = 64; // global max (default: 50) +RequestScheduler.maximumRequestsPerServer = 12; // per-server max (default: 18) + +// Override for known HTTP/2 servers +RequestScheduler.requestsByServer = { + "api.cesium.com:443": 32, + "assets.cesium.com:443": 32, +}; +``` + +## Error Handling + +- **DeveloperError** -- bug in calling code (invalid args). Thrown only in debug builds; fix the code, do not catch. +- **RuntimeError** -- runtime failure (network, shader compile). Catch in production. + +```js +import { RuntimeError, formatError, Cesium3DTileset } from "cesium"; + +try { + const tileset = await Cesium3DTileset.fromUrl("https://example.com/tileset.json"); + viewer.scene.primitives.add(tileset); +} catch (error) { + if (error instanceof RuntimeError) { + console.error("Failed to load tileset:", error.message); + } else { + console.error(formatError(error)); // extracts name, message, stack + } +} +``` + +## Helper Functions + +### defined, clone, combine + +```js +import { defined, clone, combine } from "cesium"; + +// defined: returns true if value is neither null nor undefined +if (defined(entity.billboard)) { + entity.billboard.scale = 2.0; +} + +// clone: shallow by default, pass true for deep +const obj = clone({ a: 1, nested: { b: 2 } }, true); + +// combine: merge objects, first arg's keys take precedence +const merged = combine({ size: 20 }, { size: 10, color: "red" }); +// { size: 20, color: "red" } +``` + +### createGuid, buildModuleUrl + +```js +import { createGuid, buildModuleUrl } from "cesium"; + +const id = createGuid(); // "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx" + +// Resolve paths relative to Cesium installation +const iconUrl = buildModuleUrl("Assets/Textures/maki/marker.png"); +``` + +### URL Utilities + +```js +import { objectToQuery, queryToObject, getExtensionFromUri, getBaseUri } from "cesium"; + +const qs = objectToQuery({ key1: "value 1", key2: ["x", "y"] }); +// "key1=value%201&key2=x&key2=y" + +const parsed = queryToObject("key1=value%201&key2=x&key2=y"); +// { key1: "value 1", key2: ["x", "y"] } + +getExtensionFromUri("https://example.com/model.glb?v=2"); // "glb" +getBaseUri("https://example.com/data/model.glb"); // "https://example.com/data/" +``` + +### destroyObject + +Replaces all methods on an object with functions that throw `DeveloperError`, and sets `isDestroyed()` to return `true`. Standard cleanup pattern for objects holding native resources. + +```js +import { destroyObject } from "cesium"; + +class MyWidget { + constructor(viewer) { + this._handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); + } + isDestroyed() { return false; } + destroy() { + this._handler.destroy(); + return destroyObject(this); + } +} +``` + +## AssociativeArray + +O(1) key lookup with a live `values` array for allocation-free iteration in render loops. + +```js +import { AssociativeArray } from "cesium"; + +const items = new AssociativeArray(); +items.set("building-1", { height: 50 }); +items.set("building-2", { height: 80 }); + +items.get("building-1"); // { height: 50 } +items.contains("building-1"); // true + +// Iterate without per-frame allocations +const values = items.values; +for (let i = 0; i < values.length; i++) { /* process values[i] */ } + +items.remove("building-1"); +items.removeAll(); +``` + +## PinBuilder + +Generates map pin canvas elements with colors, text, maki icons, or custom images. + +```js +import { PinBuilder, Color, Cartesian3, VerticalOrigin } from "cesium"; + +const pin = new PinBuilder(); +const redPin = pin.fromColor(Color.RED, 48); // solid color +const textPin = pin.fromText("A", Color.BLUE, 48); // text label +const iconPin = await pin.fromMakiIconId("hospital", Color.GREEN, 48); // maki icon +const urlPin = await pin.fromUrl("/icons/custom.png", Color.YELLOW, 48); + +viewer.entities.add({ + position: Cartesian3.fromDegrees(-75.17, 39.95), + billboard: { + image: pin.fromText("1", Color.ROYALBLUE, 48), + verticalOrigin: VerticalOrigin.BOTTOM, + }, +}); +``` + +## DistanceDisplayCondition + +Controls entity/billboard/label visibility based on camera distance. + +```js +import { DistanceDisplayCondition, Cartesian3, Color } from "cesium"; + +viewer.entities.add({ + position: Cartesian3.fromDegrees(-75.17, 39.95), + billboard: { + image: "/icons/marker.png", + distanceDisplayCondition: new DistanceDisplayCondition(100.0, 50000.0), + }, +}); +``` + +## Feature Detection and Fullscreen + +```js +import { FeatureDetection, Fullscreen } from "cesium"; + +if (FeatureDetection.supportsWebAssembly()) { /* WASM workers OK */ } +if (FeatureDetection.supportsTypedArrays()) { /* TypedArrays OK */ } + +if (Fullscreen.supportsFullscreen()) { + Fullscreen.requestFullscreen(viewer.container); +} +``` + +## TaskProcessor + +Wraps Web Workers for background computation. Worker is created lazily on first `scheduleTask`. + +```js +import { TaskProcessor, defined } from "cesium"; + +const processor = new TaskProcessor("myWorkerModule"); +const promise = processor.scheduleTask({ data: largeArray, op: "simplify" }); + +if (!defined(promise)) { + // Too many active tasks; retry next frame +} else { + const result = await promise; +} +processor.destroy(); // release worker when done +``` + +## TrustedServers + +Credentials (cookies, auth headers) are sent only to registered servers. + +```js +import { TrustedServers } from "cesium"; + +TrustedServers.add("secure-tiles.example.com", 443); +TrustedServers.contains("https://secure-tiles.example.com/tileset.json"); // true +TrustedServers.remove("secure-tiles.example.com", 443); +``` + +## Performance Tips + +1. **Reuse Resource instances** -- `getDerivedResource` inherits proxy, headers, and retry config without re-parsing the URL. +2. **Tune RequestScheduler for HTTP/2** -- increase `maximumRequests` and per-server limits via `requestsByServer` for faster tile loading. +3. **Use `Frozen.EMPTY_OBJECT` for defaults** -- avoids allocating a new `{}` on every call in hot paths. +4. **Prefer `defined()` over truthiness** -- correctly distinguishes `0`, `""`, and `false` from `null`/`undefined`. +5. **Use AssociativeArray in render loops** -- its `values` array avoids per-frame `Object.keys()` allocations. +6. **Set retryAttempts conservatively** -- gate retries on specific status codes (401, 429, 503) via `retryCallback`. +7. **Destroy TaskProcessors when done** -- idle workers still consume memory. +8. **Never mutate frozen Color constants** -- call `.clone()` or `.withAlpha()` first. +9. **Use `formatError` in catch blocks** -- extracts name, message, and stack from any error type. +10. **Cache PinBuilder output** -- store canvas references when generating many identical pins across frames. + +## See Also + +- **cesiumjs-viewer-setup** -- Viewer initialization, Ion token, scene configuration +- **cesiumjs-imagery** -- Imagery providers that consume `Resource` for tile fetching +- **cesiumjs-entities** -- Entity API using `Color`, `DistanceDisplayCondition`, and `PinBuilder` diff --git a/skills/cesiumjs-spatial-math/SKILL.md b/skills/cesiumjs-spatial-math/SKILL.md new file mode 100644 index 0000000..a067c2a --- /dev/null +++ b/skills/cesiumjs-spatial-math/SKILL.md @@ -0,0 +1,351 @@ +--- +name: cesiumjs-spatial-math +description: "CesiumJS spatial math - Cartesian3, Cartographic, Matrix4, Quaternion, Transforms, Ellipsoid, BoundingSphere, projections, coordinate conversions. Use when converting between coordinate systems, computing positions on the ellipsoid, performing spatial intersection tests, building model matrices, or working with geographic projections." +--- +# CesiumJS Spatial Math & Transforms + +Version baseline: CesiumJS v1.139 (2026-03-05) + +Mathematical foundation for every CesiumJS application: coordinate types, unit conversions, ellipsoid geometry, reference frame transforms, bounding volumes, intersection tests, and projections. + +## Core Concepts + +CesiumJS uses a right-handed Earth-Centered Earth-Fixed (ECEF) coordinate system: + +- **Cartesian3** -- ECEF (x, y, z) in meters. Internal representation for all 3D positions. +- **Cartographic** -- (longitude, latitude, height). Angles are **radians**, height in meters above ellipsoid. + +All angular values in core math are radians. Use `Math.toRadians()` / `Math.toDegrees()`. Math types use a **static-method-with-result** pattern: pass a `result` parameter to reuse allocations. + +## Cartesian3 -- Positions and Vectors + +```js +import { Cartesian3, Math as CesiumMath } from "cesium"; + +// From lon/lat degrees -- most common entry point +const pos = Cartesian3.fromDegrees(-105.0, 40.0); +const elevated = Cartesian3.fromDegrees(-105.0, 40.0, 1500.0); // with height + +// Batch creation: [lon, lat, lon, lat, ...] +const ring = Cartesian3.fromDegreesArray([-105, 40, -100, 40, -100, 35]); + +// With heights: [lon, lat, h, lon, lat, h, ...] +const wall = Cartesian3.fromDegreesArrayHeights([-105, 40, 500, -100, 40, 1000]); + +// From raw ECEF or from radians +const raw = new Cartesian3(-1275096.0, -4797180.0, 4075270.0); +const fromRad = Cartesian3.fromRadians(-1.8326, 0.6981, 1500.0); + +// Constants +Cartesian3.ZERO; // (0,0,0) +Cartesian3.UNIT_X; // (1,0,0) +Cartesian3.UNIT_Y; // (0,1,0) +Cartesian3.UNIT_Z; // (0,0,1) +``` + +### Vector Operations + +```js +const a = new Cartesian3(1.0, 2.0, 3.0); +const b = new Cartesian3(4.0, 5.0, 6.0); +const r = new Cartesian3(); // reusable scratch + +Cartesian3.add(a, b, r); // a + b +Cartesian3.subtract(a, b, r); // a - b +Cartesian3.multiplyByScalar(a, 2.0, r); // a * 2 +Cartesian3.negate(a, r); // -a +Cartesian3.cross(a, b, r); // cross product +Cartesian3.normalize(a, r); // unit vector +Cartesian3.lerp(a, b, 0.5, r); // linear interpolation +Cartesian3.midpoint(a, b, r); // midpoint + +const dot = Cartesian3.dot(a, b); // dot product +const len = Cartesian3.magnitude(a); // ||a|| +const dist = Cartesian3.distance(a, b); // Euclidean distance +const distSq = Cartesian3.distanceSquared(a, b); // faster for comparisons +const angle = Cartesian3.angleBetween(a, b); // radians +``` + +## Cartographic -- Geographic Coordinates + +```js +import { Cartographic, Cartesian3, Math as CesiumMath } from "cesium"; + +const carto = Cartographic.fromDegrees(-105.0, 40.0, 1500.0); +const cartoRad = Cartographic.fromRadians(-1.8326, 0.6981, 1500.0); + +// Cartesian3 <-> Cartographic +const position = Cartesian3.fromDegrees(-105.0, 40.0, 1500.0); +const geo = Cartographic.fromCartesian(position); +const lonDeg = CesiumMath.toDegrees(geo.longitude); // -105.0 +const latDeg = CesiumMath.toDegrees(geo.latitude); // 40.0 +const backToCart = Cartographic.toCartesian(geo); +``` + +## CesiumMath Utilities + +```js +import { Math as CesiumMath } from "cesium"; + +// Degree/radian conversion +const rad = CesiumMath.toRadians(90.0); // PI/2 +const deg = CesiumMath.toDegrees(Math.PI); // 180 + +// Constants: PI, TWO_PI, PI_OVER_TWO, PI_OVER_FOUR, RADIANS_PER_DEGREE +// EPSILON1 (0.1) through EPSILON21 (1e-21) + +const clamped = CesiumMath.clamp(value, 0.0, 1.0); +const interp = CesiumMath.lerp(0.0, 100.0, 0.5); // 50 +const norm = CesiumMath.negativePiToPi(angle); // [-PI, PI] +const pos = CesiumMath.zeroToTwoPi(angle); // [0, 2*PI] +const safeLon = CesiumMath.convertLongitudeRange(angle); // [-PI, PI) +const eq = CesiumMath.equalsEpsilon(a, b, CesiumMath.EPSILON7); // float compare +``` + +## Ellipsoid + +```js +import { Ellipsoid, Cartesian3, Cartographic } from "cesium"; + +// Built-in ellipsoids +Ellipsoid.WGS84; // Earth (default) +Ellipsoid.UNIT_SPHERE; // radius 1 +Ellipsoid.MOON; // lunar sphere +Ellipsoid.MARS; // Mars (v1.133+) + +// Change default (affects Ellipsoid.default everywhere) +Ellipsoid.default = Ellipsoid.MOON; + +// Conversions on a specific ellipsoid +const cart = Ellipsoid.WGS84.cartographicToCartesian( + Cartographic.fromDegrees(-75.0, 40.0, 100.0), +); +const carto = Ellipsoid.WGS84.cartesianToCartographic(cart); + +// Surface normal at a position +const normal = Ellipsoid.WGS84.geodeticSurfaceNormal(cart, new Cartesian3()); + +// Project point onto ellipsoid surface +const onSurface = Ellipsoid.WGS84.scaleToGeodeticSurface(cart, new Cartesian3()); +``` + +## Transforms -- Reference Frames + +`Transforms` builds 4x4 matrices relating local frames to ECEF. The most commonly used function is `eastNorthUpToFixedFrame`. + +### East-North-Up (ENU) + +ENU: X = east, Y = north, Z = up. Standard frame for placing models on the globe. + +```js +import { Cartesian3, Transforms, Matrix4 } from "cesium"; + +const origin = Cartesian3.fromDegrees(-105.0, 40.0); +const enuMatrix = Transforms.eastNorthUpToFixedFrame(origin); +// Columns: [east, north, up, origin] in ECEF +``` + +### Heading-Pitch-Roll Model Matrix + +Standard way to position and orient a 3D model. + +```js +import { Cartesian3, Transforms, HeadingPitchRoll, Math as CesiumMath } from "cesium"; + +const position = Cartesian3.fromDegrees(-105.0, 40.0, 0.0); +const hpr = new HeadingPitchRoll( + CesiumMath.toRadians(90.0), // heading: 90 deg east + 0.0, // pitch: level + 0.0, // roll: none +); +const modelMatrix = Transforms.headingPitchRollToFixedFrame(position, hpr); + +// Just the orientation quaternion (e.g., for Entity.orientation) +const orientation = Transforms.headingPitchRollQuaternion(position, hpr); +``` + +### HeadingPitchRoll + +Heading = rotation about -Z (compass bearing, clockwise). Pitch = about -Y. Roll = about +X. Radians. + +```js +import { HeadingPitchRoll, Math as CesiumMath } from "cesium"; +const hpr = new HeadingPitchRoll(CesiumMath.toRadians(45.0), CesiumMath.toRadians(-10.0), 0.0); +const hprDeg = HeadingPitchRoll.fromDegrees(45.0, -10.0, 0.0); // convenience +``` + +### Other Local Frames + +```js +import { Transforms, Cartesian3 } from "cesium"; +const origin = Cartesian3.fromDegrees(-105.0, 40.0); + +Transforms.northEastDownToFixedFrame(origin); // NED (aviation) +Transforms.northUpEastToFixedFrame(origin); // NUE + +// Custom frame from any combo of east|north|up|west|south|down +const customFn = Transforms.localFrameToFixedFrameGenerator("north", "west"); +const matrix = customFn(origin); + +// Recover heading/pitch/roll from an existing model matrix +const hpr = Transforms.fixedFrameToHeadingPitchRoll(modelMatrix); +``` + +## Matrix4 -- 4x4 Transforms + +Column-major storage (WebGL convention). Constructor takes row-major for readability. + +```js +import { Matrix4, Matrix3, Cartesian3, Quaternion } from "cesium"; + +// Factory methods +Matrix4.fromTranslation(new Cartesian3(10, 20, 30)); +Matrix4.fromRotationTranslation(Matrix3.fromRotationZ(Math.PI / 4), new Cartesian3(100, 0, 0)); +Matrix4.fromTranslationQuaternionRotationScale( + new Cartesian3(0, 0, 0), Quaternion.IDENTITY, new Cartesian3(2, 2, 2), +); +Matrix4.fromUniformScale(5.0); + +// Combine, transform, invert +const combined = Matrix4.multiply(matA, matB, new Matrix4()); +const worldPt = Matrix4.multiplyByPoint(enuMatrix, new Cartesian3(100, 0, 0), new Cartesian3()); +const inv = Matrix4.inverseTransformation(enuMatrix, new Matrix4()); // rigid-body only + +// Decompose +Matrix4.getTranslation(enuMatrix, new Cartesian3()); +Matrix4.getMatrix3(enuMatrix, new Matrix3()); +Matrix4.getScale(enuMatrix, new Cartesian3()); +``` + +## Quaternion -- Rotation + +```js +import { Quaternion, Cartesian3, HeadingPitchRoll, Math as CesiumMath, Matrix3 } from "cesium"; + +Quaternion.IDENTITY; // (0, 0, 0, 1) +const q1 = Quaternion.fromAxisAngle(Cartesian3.UNIT_Z, CesiumMath.toRadians(45.0)); +const q2 = Quaternion.fromHeadingPitchRoll(new HeadingPitchRoll(CesiumMath.toRadians(90), 0, 0)); +const q3 = Quaternion.fromRotationMatrix(Matrix3.fromRotationZ(Math.PI / 2)); +const mid = Quaternion.slerp(q1, q2, 0.5, new Quaternion()); // interpolate +const composed = Quaternion.multiply(q1, q2, new Quaternion()); // compose +``` + +## Geodesic Distance + +```js +import { Cartographic, EllipsoidGeodesic, Cartesian3 } from "cesium"; + +// Surface distance (great-circle via Vincenty) +const geodesic = new EllipsoidGeodesic( + Cartographic.fromDegrees(-73.985, 40.758), // New York + Cartographic.fromDegrees(-0.1276, 51.5074), // London +); +const surfaceDist = geodesic.surfaceDistance; // ~5,570 km +const midCarto = geodesic.interpolateUsingFraction(0.5); // midpoint on surface + +// Chord (straight-line) distance +const chord = Cartesian3.distance(Cartesian3.fromDegrees(-105, 40), Cartesian3.fromDegrees(-104, 40)); +``` + +## BoundingSphere + +```js +import { BoundingSphere, Cartesian3 } from "cesium"; + +const sphere = BoundingSphere.fromPoints( + Cartesian3.fromDegreesArray([-105, 40, -100, 40, -100, 35]), +); // sphere.center (Cartesian3), sphere.radius (number) + +const inside = Cartesian3.distance(sphere.center, Cartesian3.fromDegrees(-102, 37.5)) <= sphere.radius; +``` + +## Ray and Intersection Tests + +```js +import { Ray, IntersectionTests, Plane, Cartesian3, Ellipsoid } from "cesium"; + +const ray = new Ray(new Cartesian3(0, 0, 6378137), new Cartesian3(0, 0, -1)); // auto-normalized +const ptOnRay = Ray.getPoint(ray, 1000.0, new Cartesian3()); + +// Ray-plane: returns Cartesian3 or undefined +const plane = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Z); +const hit = IntersectionTests.rayPlane(ray, plane); + +// Ray-ellipsoid: returns Interval {start, stop} or undefined +const camRay = new Ray(new Cartesian3(0, 0, 20000000), new Cartesian3(0, 0, -1)); +const interval = IntersectionTests.rayEllipsoid(camRay, Ellipsoid.WGS84); +if (interval) { + const nearPt = Ray.getPoint(camRay, interval.start, new Cartesian3()); +} + +// Ray-triangle: returns parametric t or undefined +const t = IntersectionTests.rayTriangleParametric(ray, p0, p1, p2, true); +``` + +## SceneTransforms -- World to Screen + +```js +import { SceneTransforms, Cartesian3 } from "cesium"; +// World -> pixel coordinates (Cartesian2 or undefined if off-screen) +const winPos = SceneTransforms.worldToWindowCoordinates(viewer.scene, Cartesian3.fromDegrees(-105, 40)); +// High-DPI aware variant +const bufPos = SceneTransforms.worldToDrawingBufferCoordinates(viewer.scene, worldPos); +``` + +## Geographic Projections + +```js +import { GeographicProjection, WebMercatorProjection, Cartographic, Ellipsoid } from "cesium"; +const carto = Cartographic.fromDegrees(-105.0, 40.0); + +// Plate Carree: project/unproject between Cartographic and Cartesian3 +const geoProj = new GeographicProjection(Ellipsoid.WGS84); +const xy = geoProj.project(carto); // Cartesian3 +const back = geoProj.unproject(xy); // Cartographic + +// Web Mercator (EPSG:3857) +const merc = new WebMercatorProjection(Ellipsoid.WGS84); +const mercXY = merc.project(carto); +``` + +## Common Patterns + +### Offset a Position in Local ENU + +```js +import { Cartesian3, Transforms, Matrix4 } from "cesium"; + +const origin = Cartesian3.fromDegrees(-105.0, 40.0, 0.0); +const enu = Transforms.eastNorthUpToFixedFrame(origin); +// Move 500m east, 200m north, 100m up in local frame +const worldPt = Matrix4.multiplyByPoint(enu, new Cartesian3(500, 200, 100), new Cartesian3()); +``` + +### Compare Positions with Tolerance + +```js +import { Cartesian3, Math as CesiumMath } from "cesium"; +const a = Cartesian3.fromDegrees(-105.0, 40.0); +const b = Cartesian3.fromDegrees(-105.0001, 40.0001); +Cartesian3.equalsEpsilon(a, b, CesiumMath.EPSILON7); // preferred over === +if (Cartesian3.distance(a, b) < 10.0) { /* within 10m */ } +``` + +## Performance Tips + +1. **Reuse scratch variables.** Pre-allocate `result` objects outside loops to avoid GC pauses. +2. **Use `distanceSquared`** instead of `distance` when comparing -- avoids `Math.sqrt`. +3. **Prefer `Cartesian3.fromDegrees`** over manual Cartographic creation then conversion. +4. **Cache model matrices.** Call `Transforms.eastNorthUpToFixedFrame` once if position is static. +5. **Use `Matrix4.inverseTransformation`** for rigid-body transforms -- faster and more stable than `inverse`. +6. **Batch position creation** with `fromDegreesArray` / `fromDegreesArrayHeights` instead of looping `fromDegrees`. +7. **Guard `Cartesian3.normalize`** -- it throws on zero-length vectors. Check magnitude first. +8. **Use `equalsEpsilon`** for float comparisons. `CesiumMath.EPSILON7` is a good default tolerance. +9. **Pre-compute HPR** outside render loops. Convert to quaternion/matrix only when orientation changes. +10. **Choose the right distance.** `Cartesian3.distance` = chord through Earth. `EllipsoidGeodesic.surfaceDistance` = great-circle. + +## See Also + +- **cesiumjs-camera** -- Camera positioning and flight animations that consume these coordinate types +- **cesiumjs-primitives** -- Geometry and Primitive API that uses model matrices from Transforms +- **cesiumjs-terrain-environment** -- Terrain height queries and globe surface interactions diff --git a/skills/cesiumjs-time-properties/SKILL.md b/skills/cesiumjs-time-properties/SKILL.md new file mode 100644 index 0000000..a1e5ef5 --- /dev/null +++ b/skills/cesiumjs-time-properties/SKILL.md @@ -0,0 +1,353 @@ +--- +name: cesiumjs-time-properties +description: "CesiumJS time, properties, and animation - Clock, JulianDate, TimeInterval, Property, SampledProperty, CallbackProperty, interpolation, splines, CZML temporal data. Use when making entity attributes time-dynamic, configuring the simulation clock, interpolating positions over time, or working with sampled or callback properties." +--- +# CesiumJS Time, Properties & Animation + +Version baseline: CesiumJS v1.139.1 + +Covers the temporal data-binding layer: Clock/JulianDate time system, the Property hierarchy that makes entity attributes change over time, interpolation algorithms, splines, and material properties. Properties live here (not with Entities) because SampledProperty and CallbackProperty are meaningless without Clock/JulianDate. The Material class (Fabric) belongs in cesiumjs-materials-shaders. + +## JulianDate -- The Time Primitive + +Stores whole days + fractional seconds separately for precision. Always uses TAI internally. + +```js +import { JulianDate } from "cesium"; + +// Creation: fromIso8601 (most common), fromDate, now +const date = JulianDate.fromIso8601("2025-06-15T12:00:00Z"); +const jd = JulianDate.fromDate(new Date("2025-06-15T12:00:00Z")); +const now = JulianDate.now(); + +// Conversion: toIso8601, toDate, toGregorianDate +const iso = JulianDate.toIso8601(date); // "2025-06-15T12:00:00Z" +const greg = JulianDate.toGregorianDate(date); // {year, month, day, hour, ...} + +// Arithmetic -- all require a result parameter to avoid allocations +const r = new JulianDate(); +JulianDate.addSeconds(date, 3600, r); // also: addMinutes, addHours, addDays + +// Differences and comparisons +const stop = JulianDate.addHours(date, 24, new JulianDate()); +JulianDate.secondsDifference(stop, date); // 86400 +JulianDate.lessThan(date, stop); // true +JulianDate.compare(date, stop); // negative (date < stop) +``` + +## Clock -- Simulation Time Controller + +The Viewer creates a Clock automatically. Configure it to control playback speed and bounds. + +```js +import { Viewer, JulianDate, ClockRange, ClockStep } from "cesium"; + +const viewer = new Viewer("cesiumContainer"); +const start = JulianDate.fromIso8601("2025-06-15T00:00:00Z"); +const stop = JulianDate.addHours(start, 24, new JulianDate()); +viewer.clock.startTime = start.clone(); +viewer.clock.stopTime = stop.clone(); +viewer.clock.currentTime = start.clone(); +viewer.clock.clockRange = ClockRange.LOOP_STOP; // loop at end +viewer.clock.multiplier = 60; // 60x real-time +viewer.clock.shouldAnimate = true; +viewer.timeline.zoomTo(start, stop); + +viewer.clock.onTick.addEventListener((clock) => { // per-frame callback + console.log(JulianDate.toIso8601(clock.currentTime)); +}); +``` + +| ClockRange | Behavior | +|---|---| +| `UNBOUNDED` | Advances forever in both directions | +| `CLAMPED` | Stops at start/stop time | +| `LOOP_STOP` | Wraps from stop back to start | + +| ClockStep | Behavior | +|---|---| +| `TICK_DEPENDENT` | Each tick advances by `multiplier` seconds (frame-dependent) | +| `SYSTEM_CLOCK_MULTIPLIER` | Elapsed wall time x `multiplier` (default) | +| `SYSTEM_CLOCK` | Real-time; ignores multiplier | + +## TimeInterval & TimeIntervalCollection + +```js +import { TimeInterval, TimeIntervalCollection, JulianDate } from "cesium"; + +const interval = TimeInterval.fromIso8601({ + iso8601: "2025-06-15T00:00:00Z/2025-06-16T00:00:00Z", + data: { phase: "daylight" }, // attach arbitrary data +}); +TimeInterval.contains(interval, JulianDate.fromIso8601("2025-06-15T12:00:00Z")); // true + +// Used by Entity.availability to cull entities outside the time window +const availability = new TimeIntervalCollection([ + new TimeInterval({ + start: JulianDate.fromIso8601("2025-06-15T00:00:00Z"), + stop: JulianDate.fromIso8601("2025-06-16T00:00:00Z"), + }), +]); +``` + +## Property System -- Time-Varying Values + +Every entity attribute is a Property. CesiumJS calls `property.getValue(time)` each frame. + +### ConstantProperty + +Returns the same value regardless of time. CesiumJS auto-wraps raw values, so explicit use is rare. + +```js +import { ConstantProperty, Color } from "cesium"; +const prop = new ConstantProperty(Color.RED); +prop.setValue(Color.BLUE); // fires definitionChanged +``` + +### SampledProperty -- Interpolated Time Series + +Stores discrete samples and interpolates. Type can be `Number`, `Cartesian3`, `Color`, or any `Packable`. + +```js +import { SampledProperty, JulianDate, LagrangePolynomialApproximation, ExtrapolationType } from "cesium"; + +const prop = new SampledProperty(Number); +const t0 = JulianDate.fromIso8601("2025-06-15T00:00:00Z"); +prop.addSample(t0, 1.0); +prop.addSample(JulianDate.addSeconds(t0, 60, new JulianDate()), 2.5); +prop.addSample(JulianDate.addSeconds(t0, 120, new JulianDate()), 1.0); +prop.getValue(JulianDate.addSeconds(t0, 30, new JulianDate())); // ~1.75 + +// Default: LinearApproximation degree 1. Switch to smoother Lagrange: +prop.setInterpolationOptions({ interpolationDegree: 5, interpolationAlgorithm: LagrangePolynomialApproximation }); +prop.forwardExtrapolationType = ExtrapolationType.HOLD; // hold last value outside range +``` + +### SampledPositionProperty -- Interpolated Positions + +Specialized for Cartesian3 positions. Supports reference frames (`ReferenceFrame.FIXED` default, or `INERTIAL`). + +```js +import { SampledPositionProperty, JulianDate, Cartesian3, LagrangePolynomialApproximation, ExtrapolationType } from "cesium"; + +const position = new SampledPositionProperty(); +const start = JulianDate.fromIso8601("2025-06-15T00:00:00Z"); +for (let i = 0; i <= 360; i += 45) { + const rad = (i * Math.PI) / 180; + position.addSample( + JulianDate.addSeconds(start, i, new JulianDate()), + Cartesian3.fromDegrees(-112 + 0.045 * Math.cos(rad), 36 + 0.03 * Math.sin(rad), 2000 + Math.random() * 500), + ); +} +position.setInterpolationOptions({ interpolationDegree: 5, interpolationAlgorithm: LagrangePolynomialApproximation }); +position.forwardExtrapolationType = ExtrapolationType.HOLD; +``` + +| Algorithm | Best For | Degree | +|---|---|---| +| `LinearApproximation` | Fast piecewise-linear | 1 (fixed) | +| `LagrangePolynomialApproximation` | Smooth curves from sparse samples | 1--9 | +| `HermitePolynomialApproximation` | Smooth curves with velocity derivatives | 1--9 | + +### CallbackProperty -- Computed on Demand + +Evaluates a function every frame. Second argument (`isConstant`) must be `false` if value changes. + +```js +import { CallbackProperty, Color, JulianDate } from "cesium"; + +const startTime = JulianDate.now(); +const pulse = new CallbackProperty((time, result) => { + const s = JulianDate.secondsDifference(time, startTime); + return Color.RED.withAlpha(0.5 + 0.5 * Math.sin(s * 2), result ?? new Color()); +}, false); + +// Growing polygon -- mutate the array, property auto-updates +const pts = [/* initial Cartesian3[] */]; +const dynamicPts = new CallbackProperty(() => pts, false); +``` + +### CompositeProperty -- Stitching Properties Over Time + +Delegates to different sub-properties for different time ranges. Each interval's `data` is a Property. + +```js +import { CompositeProperty, ConstantProperty, SampledProperty, TimeInterval, JulianDate } from "cesium"; + +const composite = new CompositeProperty(); +composite.intervals.addInterval(TimeInterval.fromIso8601({ + iso8601: "2025-06-15T00:00:00Z/2025-06-15T12:00:00Z", data: new ConstantProperty(1.0) })); +const sampled = new SampledProperty(Number); +sampled.addSample(JulianDate.fromIso8601("2025-06-15T12:00:00Z"), 1.0); +sampled.addSample(JulianDate.fromIso8601("2025-06-16T00:00:00Z"), 5.0); +composite.intervals.addInterval(TimeInterval.fromIso8601({ + iso8601: "2025-06-15T12:00:00Z/2025-06-16T00:00:00Z", isStartIncluded: false, data: sampled })); +``` + +### VelocityOrientationProperty -- Auto-Orient Along Path + +Computes Quaternion from a position property's velocity. Essential for vehicles and aircraft. + +```js +import { VelocityOrientationProperty, SampledPositionProperty } from "cesium"; +const position = new SampledPositionProperty(); +// ... add samples ... +viewer.entities.add({ + position, orientation: new VelocityOrientationProperty(position), + model: { uri: "aircraft.glb", minimumPixelSize: 64 }, +}); +``` + +### ReferenceProperty -- Cross-Entity Binding + +Links one entity's property to another by ID string (`"entityId#propertyPath"`). + +```js +import { ReferenceProperty } from "cesium"; +viewer.entities.add({ id: "leader", position: Cartesian3.fromDegrees(-75, 40, 1000) }); +viewer.entities.add({ id: "follower", + position: ReferenceProperty.fromString(viewer.entities, "leader#position"), + point: { pixelSize: 10 } }); +``` + +## Material Properties + +Control entity surface appearance. All options accept raw values or Property instances for time-dynamic behavior. Surface types: `ColorMaterialProperty`, `ImageMaterialProperty`, `GridMaterialProperty`, `StripeMaterialProperty`, `CheckerboardMaterialProperty`. Polyline types: `PolylineArrowMaterialProperty`, `PolylineDashMaterialProperty`, `PolylineGlowMaterialProperty`, `PolylineOutlineMaterialProperty`. + +```js +import { ColorMaterialProperty, SampledProperty, Color, JulianDate } from "cesium"; + +const solid = new ColorMaterialProperty(Color.RED); + +// Time-varying color via SampledProperty +const colorProp = new SampledProperty(Color); +const t0 = JulianDate.fromIso8601("2025-06-15T00:00:00Z"); +colorProp.addSample(t0, Color.BLUE); +colorProp.addSample(JulianDate.addHours(t0, 6, new JulianDate()), Color.RED); +const animated = new ColorMaterialProperty(colorProp); +``` + +## Splines -- Parametric Curve Interpolation + +Splines use unitless parametric time (not JulianDate) for smooth animation curves. + +```js +import { HermiteSpline, CatmullRomSpline, Cartesian3 } from "cesium"; + +// Natural cubic (C2, auto-tangents) +const spline = HermiteSpline.createNaturalCubic({ + times: [0, 1.5, 3, 4.5, 6], + points: [ + new Cartesian3(1235398, -4810983, 4146266), new Cartesian3(1372574, -5345182, 4606657), + new Cartesian3(-757983, -5542796, 4514323), new Cartesian3(-2821260, -5248423, 4021290), + new Cartesian3(-2539788, -4724797, 3620093) ], +}); +const point = spline.evaluate(2.0); // evaluate at parametric time t=2 + +// CatmullRom (C1, auto-tangents from control points) +const catmull = new CatmullRomSpline({ times: [0, 1, 2, 3], points: [p0, p1, p2, p3] }); +``` + +| Spline | Use | +|---|---| +| `LinearSpline` | Piecewise-linear (C0), cheapest | +| `HermiteSpline` | Cubic with tangents (C1+); factories: `createNaturalCubic`, `createClampedCubic`, `createC1` | +| `CatmullRomSpline` | Auto-tangents from control points (C1) | +| `QuaternionSpline` | Rotation interpolation via SLERP (C1) | +| `ConstantSpline` | Single value for all times | +| `SteppedSpline` | Holds value until next control point | +| `MorphWeightSpline` | glTF morph target weights (C1) | + +## CZML Temporal Data + +CZML streams time-dynamic data. The `document` packet sets the clock; entity packets use `epoch` + offset arrays for compact positions. Position format: `[secondsFromEpoch, lon, lat, alt, ...]`. + +```js +import { Viewer, CzmlDataSource } from "cesium"; +const czml = [ + { id: "document", version: "1.0", clock: { + interval: "2025-06-15T00:00:00Z/2025-06-15T06:00:00Z", + currentTime: "2025-06-15T00:00:00Z", multiplier: 60, + range: "LOOP_STOP", step: "SYSTEM_CLOCK_MULTIPLIER" } }, + { id: "aircraft", availability: "2025-06-15T00:00:00Z/2025-06-15T06:00:00Z", + position: { epoch: "2025-06-15T00:00:00Z", + cartographicDegrees: [0,-75,40,10000, 10800,-88,42,11000, 21600,-118,34,9000], + interpolationAlgorithm: "LAGRANGE", interpolationDegree: 5 }, + point: { pixelSize: 10, color: { rgba: [255,255,0,255] } } }, +]; +const ds = await CzmlDataSource.load(czml); +const viewer = new Viewer("cesiumContainer", { shouldAnimate: true }); +viewer.dataSources.add(ds); +viewer.zoomTo(ds); +``` + +## EasingFunction -- Camera Flight Curves + +Constants for `camera.flyTo` timing (not Property interpolation). Common values: `LINEAR_NONE`, `CUBIC_IN_OUT`, `QUADRATIC_IN_OUT`. Full set includes `QUARTIC`, `QUINTIC`, `SINUSOIDAL`, `EXPONENTIAL`, `CIRCULAR`, `ELASTIC`, `BACK`, `BOUNCE` variants (each with `_IN`, `_OUT`, `_IN_OUT`). + +```js +import { EasingFunction, Cartesian3 } from "cesium"; +viewer.camera.flyTo({ + destination: Cartesian3.fromDegrees(-75, 40, 50000), + duration: 3.0, + easingFunction: EasingFunction.CUBIC_IN_OUT, +}); +``` + +## Putting It Together: Animated Flight + +Combines Clock, SampledPositionProperty, VelocityOrientationProperty, and availability. + +```js +import { + Viewer, JulianDate, ClockRange, SampledPositionProperty, VelocityOrientationProperty, + TimeIntervalCollection, TimeInterval, Cartesian3, LagrangePolynomialApproximation, +} from "cesium"; + +const viewer = new Viewer("cesiumContainer", { shouldAnimate: true }); +const start = JulianDate.fromIso8601("2025-06-15T16:00:00Z"); +const stop = JulianDate.addSeconds(start, 360, new JulianDate()); +viewer.clock.startTime = start.clone(); +viewer.clock.stopTime = stop.clone(); +viewer.clock.currentTime = start.clone(); +viewer.clock.clockRange = ClockRange.LOOP_STOP; +viewer.clock.multiplier = 10; +viewer.timeline.zoomTo(start, stop); + +const position = new SampledPositionProperty(); +for (let i = 0; i <= 360; i += 45) { + const r = (i * Math.PI) / 180; + position.addSample(JulianDate.addSeconds(start, i, new JulianDate()), + Cartesian3.fromDegrees(-112 + 0.045 * Math.cos(r), 36 + 0.03 * Math.sin(r), 2000)); +} +position.setInterpolationOptions({ interpolationDegree: 5, interpolationAlgorithm: LagrangePolynomialApproximation }); + +viewer.trackedEntity = viewer.entities.add({ + availability: new TimeIntervalCollection([new TimeInterval({ start, stop })]), + position, orientation: new VelocityOrientationProperty(position), + model: { uri: "aircraft.glb", minimumPixelSize: 64 }, + path: { resolution: 1, width: 10 }, +}); +``` + +## Performance Tips + +1. Prefer `SampledPositionProperty` over `CallbackProperty` for positions -- binary search is faster than per-frame callbacks. +2. Keep `interpolationDegree` at 5 or below; higher risks Runge's phenomenon with sparse data. +3. Reuse `JulianDate` result parameters in loops to avoid GC pressure. +4. Set entity `availability` to cull entities outside the current time window. +5. In `CallbackProperty`, return the `result` object to avoid allocations. +6. Load bulk temporal data via `CzmlDataSource` -- optimized for batch sample insertion. +7. Use `ExtrapolationType.HOLD` instead of duplicate trailing samples. +8. Use `ClockStep.TICK_DEPENDENT` for deterministic replay; `SYSTEM_CLOCK_MULTIPLIER` varies with frame rate. +9. Minimize `CallbackProperty` count -- each runs its function every frame. + +## Key Enums + +`ClockRange`: UNBOUNDED, CLAMPED, LOOP_STOP. `ClockStep`: TICK_DEPENDENT, SYSTEM_CLOCK_MULTIPLIER, SYSTEM_CLOCK. `ExtrapolationType`: NONE, HOLD, EXTRAPOLATE. `TimeStandard`: UTC, TAI. `ReferenceFrame`: FIXED, INERTIAL. `TrackingReferenceFrame` (v1.124+): AUTODETECT, ECI, ECEF, INERTIAL, ENU. + +## See Also + +- **cesiumjs-entities** -- Entity, Graphics types, DataSources (consumers of properties) +- **cesiumjs-viewer-setup** -- Viewer, ClockViewModel, Timeline widget +- **cesiumjs-models-particles** -- Model, ModelAnimation (uses time system for playback) diff --git a/skills/cesiumjs/DOMAINS.md b/skills/cesiumjs/DOMAINS.md new file mode 100644 index 0000000..465b906 --- /dev/null +++ b/skills/cesiumjs/DOMAINS.md @@ -0,0 +1,336 @@ +# CesiumJS Skills Domain Mapping + +> **Version baseline:** CesiumJS v1.139.1 (2026-03-05) +> **Last updated:** 2026-03-24 +> **Total public symbols assigned:** ~535 + +This document is the definitive source of truth for the CesiumJS skill decomposition. Every public class, function, and enum in CesiumJS is assigned to exactly one domain. Other domains may cross-reference a symbol, but only one domain **owns** it. + +## Relationship to cesium-context7 + +These baked-in skills and `cesium-context7` are **independent and complementary**: + +| | Baked-in Skills (`cesiumjs-*`) | Context7 (`cesium-context7`) | +|---|---|---| +| **Latency** | Instant — no network call | Requires MCP round-trip | +| **Availability** | Always — no MCP required | Requires Context7 MCP server | +| **Content** | Curated patterns, examples, best practices | Full official docs, version-specific | +| **Best for** | Common patterns, Quick Starts, "how do I..." | Version-pinned API signatures, cutting-edge features | +| **Activation** | Passive via description matching | Explicit via `query-docs` tool call | + +Both can activate simultaneously. Use baked-in skills for patterns and recipes; use Context7 to validate against a specific CesiumJS version. + +--- + +--- + +## Domain Summary + +| # | Skill Name | Entries | Description (passive activation) | +|---|-----------|---------|----------------------------------| +| 9 | `cesiumjs-time-properties` | ~57 | CesiumJS time, properties, and animation - Clock, JulianDate, TimeInterval, Property, SampledProperty, CallbackProperty, interpolation, splines, CZML temporal data. Use when making entity attributes time-dynamic, configuring the simulation clock, interpolating positions over time, or working with sampled or callback properties. | +| 10 | `cesiumjs-spatial-math` | ~55 | CesiumJS spatial math - Cartesian3, Cartographic, Matrix4, Quaternion, Transforms, Ellipsoid, BoundingSphere, projections, coordinate conversions. Use when converting between coordinate systems, computing positions on the ellipsoid, performing spatial intersection tests, building model matrices, or working with geographic projections. | +| 13 | `cesiumjs-core-utilities` | ~46 | CesiumJS core utilities and networking - Resource, Color, Event, Request, RequestScheduler, error handling, helper functions, feature detection. Use when fetching remote data, managing HTTP requests, working with colors, handling events, debugging errors, or using utility functions like defined, clone, or buildModuleUrl. | + +--- + +## Domain 10: cesiumjs-spatial-math (~55 entries) + +### Vectors and Points +- Cartesian2 +- Cartesian3 +- Cartesian4 +- Cartographic +- Spherical + +### Matrices +- Matrix2 +- Matrix3 +- Matrix4 + +### Rotation/Orientation +- Quaternion +- HeadingPitchRoll + +### Transforms +- Transforms +- SceneTransforms +- TranslationRotationScale + +### Ellipsoid and Geodesy +- Ellipsoid +- EllipsoidGeodesic +- EllipsoidRhumbLine +- EllipsoidTangentPlane + +### Bounding Volumes +- BoundingRectangle +- BoundingSphere +- AxisAlignedBoundingBox +- OrientedBoundingBox +- CullingVolume + +### Geometric Primitives +- Plane +- Ray +- Rectangle +- NearFarScalar +- Interval +- Occluder + +### Projections and Tiling +- GeographicProjection +- GeographicTilingScheme +- MapProjection (interface) +- WebMercatorProjection +- WebMercatorTilingScheme +- TilingScheme (interface) + +### Frustums +- PerspectiveFrustum +- PerspectiveOffCenterFrustum +- OrthographicFrustum +- OrthographicOffCenterFrustum + +### Intersections +- IntersectionTests +- Intersections2D + +### Math Utilities +- Math (CesiumMath) + +### Polynomial Solvers +- CubicRealPolynomial +- QuadraticRealPolynomial +- QuarticRealPolynomial +- TridiagonalSystemSolver + +### Specialized +- HilbertOrder +- Simon1994PlanetaryPositions +- Stereographic + +### Enums +- Axis +- Intersect +- Visibility +- ComponentDatatype +- IndexDatatype + +--- + +## Domain 13: cesiumjs-core-utilities (~46 entries) + +### Networking +- Resource +- Request +- RequestScheduler +- RequestErrorEvent +- DefaultProxy +- Proxy (interface) +- TrustedServers + +### Worker Processing +- TaskProcessor + +### Color and Display +- Color +- DistanceDisplayCondition +- PinBuilder + +### Events +- Event +- EventHelper + +### Error Types +- DeveloperError +- RuntimeError + +### Data Structures +- AssociativeArray +- Queue + +### Detection and State +- FeatureDetection +- Fullscreen +- Frozen + +### Global Functions +- defined +- clone +- combine +- createGuid +- buildModuleUrl +- formatError +- destroyObject +- getAbsoluteUri +- getBaseUri +- getExtensionFromUri +- getFilenameFromUri +- getImagePixels +- getTimestamp +- isLeapYear +- mergeSort +- objectToQuery +- queryToObject +- binarySearch +- subdivideArray +- writeTextToCanvas +- srgbToLinear (cross-ref from Domain 8) +- barycentricCoordinates +- pointInsideTriangle + +### Enums +- RequestState +- RequestType +- PixelDatatype +- PixelFormat +- WebGLConstants + +### Notes +- `defaultValue` was removed in v1.134 — use `??` operator instead +- `Frozen.EMPTY_OBJECT` and `Frozen.EMPTY_ARRAY` replace `defaultValue.EMPTY_OBJECT` (v1.128) + +--- + +## Domain 9: cesiumjs-time-properties (~57 entries) + +### Time Core +- Clock +- JulianDate +- GregorianDate +- TimeInterval +- TimeIntervalCollection +- LeapSecond +- Iso8601 + +### Property Interfaces +- Property +- PositionProperty +- MaterialProperty + +### Value Properties +- ConstantProperty +- ConstantPositionProperty +- SampledProperty +- SampledPositionProperty +- CallbackProperty +- CallbackPositionProperty +- CompositeProperty +- CompositePositionProperty +- CompositeMaterialProperty +- ReferenceProperty +- TimeIntervalCollectionProperty +- TimeIntervalCollectionPositionProperty +- PropertyArray +- PositionPropertyArray +- PropertyBag +- NodeTransformationProperty + +### Velocity Properties +- VelocityOrientationProperty +- VelocityVectorProperty + +### Material Properties +- ColorMaterialProperty +- ImageMaterialProperty +- GridMaterialProperty +- StripeMaterialProperty +- CheckerboardMaterialProperty +- PolylineArrowMaterialProperty +- PolylineDashMaterialProperty +- PolylineGlowMaterialProperty +- PolylineOutlineMaterialProperty + +### Splines +- Spline (base) +- CatmullRomSpline +- HermiteSpline +- LinearSpline +- QuaternionSpline +- ConstantSpline +- SteppedSpline +- MorphWeightSpline + +### Interpolation +- HermitePolynomialApproximation +- LagrangePolynomialApproximation +- LinearApproximation +- PackableForInterpolation + +### Animation Helpers +- EasingFunction +- VideoSynchronizer + +### Enums +- ClockRange +- ClockStep +- TimeStandard +- ExtrapolationType +- TrackingReferenceFrame +- ReferenceFrame + +### Design Decision +> Properties live here (not with Entities) because they are the temporal data-binding layer. SampledProperty and CallbackProperty are meaningless without Clock/JulianDate. MaterialProperties (ColorMaterialProperty, etc.) are Property subclasses that vary over time. The `Material` class (Fabric system for Primitives) belongs in cesiumjs-materials-shaders. + +--- + +## Cross-Cutting Ownership Rules + +These rules prevent activation collisions (multiple skills triggering for the same prompt): + +| Concept | Primary Domain | Cross-Referenced In | Rule | +|---------|---------------|--------------------|----| +| `*Graphics` classes | 3 (entities) | 7 (primitives) | Entity API = `*Graphics`; Primitive API = `*Geometry` | +| `*Geometry` classes | 7 (primitives) | 3 (entities) | Same boundary, other direction | +| CustomShader | 8 (materials-shaders) | 4 (3d-tiles), 12 (models) | Shader authoring is distinct from model/tileset loading | +| ImageBasedLighting | 8 (materials-shaders) | 4 (3d-tiles), 12 (models) | PBR lighting is a rendering concept | +| ClippingPlane/Polygon | 4 (3d-tiles) | 6 (terrain-env), 12 (models) | Most common use is 3D Tiles clipping | +| Material (Fabric) | 8 (materials-shaders) | 7 (primitives) | Primitives consume Materials via Appearances | +| Material*Property | 9 (time-properties) | 3 (entities) | Property subclasses (temporal) vs Material class (Fabric) | +| Ion/IonResource | 1 (viewer-setup) | 5 (imagery) | Setup-time config vs provider usage | +| createOsmBuildingsAsync | 1 (viewer-setup) | 4 (3d-tiles) | Factory helper vs tileset config | +| EntityView | 2 (camera) | 3 (entities) | Camera tracking = camera concern | +| ShadowMap | 6 (terrain-env) | 8 (materials-shaders) | Scene-level rendering config | +| SceneTransforms | 10 (spatial-math) | 1 (viewer-setup) | Coordinate transform utility | + +--- + +## Recently Added APIs (v1.120-v1.139) + +| Version | Addition | Domain | +|---------|----------|--------| +| v1.122 | CallbackPositionProperty | 9 | +| v1.123 | maximumTiltAngle (Camera) | 2 | +| v1.124 | TrackingReferenceFrame, Entity.trackingReferenceFrame | 9, 3 | +| v1.124 | ITwinPlatform | 1 | +| v1.128 | ITwinData, Frozen | 1, 13 | +| v1.128 | Frozen.EMPTY_OBJECT, Frozen.EMPTY_ARRAY | 13 | +| v1.133 | Ellipsoid.MARS | 10 | +| v1.134 | Google2DImageryProvider | 5 | +| v1.134 | `defaultValue` removed (use `??`) | 13 | +| v1.135 | Cesium3DTilesTerrainProvider (experimental) | 4 | +| v1.136 | Scene.pickAsync | 11 | +| v1.139 | EquirectangularPanorama | 6 | +| v1.139 | CubeMapPanorama | 6 | +| v1.139 | GoogleStreetViewCubeMapPanoramaProvider | 6 | + + + +## Skill File See Also Cross-References + +| Skill | See Also | +|-------|----------| +| cesiumjs-viewer-setup | camera, entities, imagery, terrain-environment | +| cesiumjs-camera | spatial-math, interaction, entities | +| cesiumjs-entities | time-properties, primitives, interaction | +| cesiumjs-3d-tiles | materials-shaders, interaction, terrain-environment | +| cesiumjs-imagery | viewer-setup, terrain-environment | +| cesiumjs-terrain-environment | viewer-setup, imagery, spatial-math | +| cesiumjs-primitives | entities, materials-shaders, spatial-math | +| cesiumjs-materials-shaders | primitives, 3d-tiles, models-particles | +| cesiumjs-time-properties | entities, viewer-setup, models-particles | +| cesiumjs-spatial-math | camera, primitives, terrain-environment | +| cesiumjs-interaction | entities, 3d-tiles, camera | +| cesiumjs-models-particles | materials-shaders, entities, 3d-tiles | +| cesiumjs-core-utilities | viewer-setup, imagery, entities |