Something went wrong. Try again.
[PRE-ALPHA] an open source web-based 2D/3D graphing calculator cartesium.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199import { spatialStyleSignature, tileCoverage, tileLevel, visibleTileCoordinates, type SpatialDemandTarget, type SpatialResourceKey} from './tiles';import type { Viewport } from './types';
export interface SpatialCompositeResource { resource: SpatialResourceKey;}
export interface MathRect { x_min: number; x_max: number; y_min: number; y_max: number;}
export interface TileCompositeRegion { source: 'tile'; rect: MathRect; resource: SpatialResourceKey; resourceIndex: number;}
export interface FallbackCompositeRegion { source: 'fallback'; rect: MathRect;}
export interface CompositePlan { /** Regions are disjoint: a screen pixel is never painted by both sources. */ regions: Array<TileCompositeRegion | FallbackCompositeRegion>; tileRegions: TileCompositeRegion[]; fallbackRegions: FallbackCompositeRegion[];}
/** True when every visible exact-level tile for every spatial expression is ready. */export function hasCompleteVisibleSpatialTiles( viewport: Viewport, targets: SpatialDemandTarget[], resources: Array<SpatialResourceKey | SpatialCompositeResource>): boolean { const level = tileLevel(viewport); const visible = visibleTileCoordinates(viewport, level);
return targets.every((target) => { const ready = new Set( resources .map((candidate) => ('resource' in candidate ? candidate.resource : candidate)) .filter( (resource) => resource.expressionId === target.expressionId && resource.modelRevision === target.modelRevision && resource.representation === target.representation && spatialStyleSignature(resource) === spatialStyleSignature(target) && resource.level === level ) .map((resource) => `${resource.x}:${resource.y}`) );
return visible.every(({ x, y }) => ready.has(`${x}:${y}`)); });}
/** * Selects exactly one source for each visible region of one spatial expression. * * Completed tiles replace the authoritative fallback only inside their canonical * tile interiors. Sampling gutters may contribute pixels to a tile image, but * never expand its ownership and therefore cannot double-paint a neighboring * tile or the fallback. */export function planSpatialComposite( viewport: Viewport, target: SpatialDemandTarget, resources: Array<SpatialResourceKey | SpatialCompositeResource>): CompositePlan { const visible: MathRect = { x_min: viewport.x_min, x_max: viewport.x_max, y_min: viewport.y_min, y_max: viewport.y_max };
const level = tileLevel(viewport);
const matching = resources .map((candidate, resourceIndex) => ({ resource: 'resource' in candidate ? candidate.resource : candidate, resourceIndex })) .filter( ({ resource }) => resource.expressionId === target.expressionId && resource.modelRevision === target.modelRevision && spatialStyleSignature(resource) === spatialStyleSignature(target) && resource.representation === target.representation ) .sort( (left, right) => Math.abs(left.resource.level - level) - Math.abs(right.resource.level - level) || // At equal distance, prefer the finer stand-in. It loses less detail // when transformed into the current camera. right.resource.level - left.resource.level || left.resource.y - right.resource.y || left.resource.x - right.resource.x || left.resourceIndex - right.resourceIndex );
const tileRegions: TileCompositeRegion[] = []; let uncovered = [visible]; const claimed = new Set<string>();
for (const { resource, resourceIndex } of matching) { const coordinateKey = `${resource.level}:${resource.x}:${resource.y}`;
if (claimed.has(coordinateKey)) continue; claimed.add(coordinateKey); const rect = intersectRects(visible, tileCoverage(resource, 0));
if (!rect) continue;
// Cross-level resources overlap by design. Claim only pieces that a // higher-priority resource has not already claimed, preserving the // compositor's single-owner invariant. const owned = uncovered.flatMap((candidate) => { const overlap = intersectRects(candidate, rect);
return overlap ? [overlap] : []; });
for (const ownedRect of owned) { tileRegions.push({ source: 'tile', rect: ownedRect, resource, resourceIndex }); }
uncovered = uncovered.flatMap((candidate) => subtractRect(candidate, rect)); }
const fallbackRegions = uncovered.map( (rect): FallbackCompositeRegion => ({ source: 'fallback', rect }) );
return { regions: [...fallbackRegions, ...tileRegions], tileRegions, fallbackRegions };}
function intersectRects(left: MathRect, right: MathRect): MathRect | undefined { const rect = { x_min: Math.max(left.x_min, right.x_min), x_max: Math.min(left.x_max, right.x_max), y_min: Math.max(left.y_min, right.y_min), y_max: Math.min(left.y_max, right.y_max) };
return rect.x_min < rect.x_max && rect.y_min < rect.y_max ? rect : undefined;}
function subtractRect(source: MathRect, cut: MathRect): MathRect[] { const overlap = intersectRects(source, cut);
if (!overlap) return [source]; const pieces: MathRect[] = [];
if (source.y_min < overlap.y_min) { pieces.push({ ...source, y_max: overlap.y_min }); }
if (overlap.y_max < source.y_max) { pieces.push({ ...source, y_min: overlap.y_max }); }
if (source.x_min < overlap.x_min) { pieces.push({ x_min: source.x_min, x_max: overlap.x_min, y_min: overlap.y_min, y_max: overlap.y_max }); }
if (overlap.x_max < source.x_max) { pieces.push({ x_min: overlap.x_max, x_max: source.x_max, y_min: overlap.y_min, y_max: overlap.y_max }); }
return pieces;}