release: bump version to 0.49.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,26 @@ const DEFAULT_AVOIDANCE_PRECISION = 4;
|
||||
const DEFAULT_AVOIDANCE_RADIUS = 1.1;
|
||||
const DEFAULT_AVOIDANCE_STEP = 0.35;
|
||||
const AVOIDANCE_RING_SLOT_COUNT = 8;
|
||||
|
||||
// Named avoidance profiles. Layers that should mutex with each other (e.g. fan
|
||||
// out when sharing the same city center) must reference the SAME profile —
|
||||
// markers are bucketed by the resulting key, and only equal keys collide.
|
||||
//
|
||||
// city — ~1.1km grid (precision 2). Use for site/observatory/POI markers
|
||||
// that often share a city-center coordinate from geocoding.
|
||||
// precise — ~11m grid (precision 4). Use for markers with building-level
|
||||
// coordinates (default; preserves prior behavior).
|
||||
//
|
||||
// Layers that need a fully custom cluster identity (e.g. a city ID string)
|
||||
// can pass `getKey: (item, position) => "..."` instead of using a profile.
|
||||
export const SURFACE_AVOIDANCE_PROFILES = Object.freeze({
|
||||
city: Object.freeze({ precision: 2, radius: 1.4, step: 0.5 }),
|
||||
precise: Object.freeze({
|
||||
precision: DEFAULT_AVOIDANCE_PRECISION,
|
||||
radius: DEFAULT_AVOIDANCE_RADIUS,
|
||||
step: DEFAULT_AVOIDANCE_STEP,
|
||||
}),
|
||||
});
|
||||
const TANGENT_EPSILON_SQ = 1e-6;
|
||||
const avoidanceNorthPole = new THREE.Vector3(0, 1, 0);
|
||||
const avoidanceFallbackEast = new THREE.Vector3(1, 0, 0);
|
||||
@@ -58,7 +78,16 @@ function createCanvas(width, height) {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getAvoidanceKey(position, basePosition, precision = 4) {
|
||||
function getAvoidanceKey(item, position, basePosition, config) {
|
||||
if (typeof config?.getKey === "function") {
|
||||
const key = config.getKey(item, position, basePosition);
|
||||
if (key) return String(key);
|
||||
}
|
||||
|
||||
const precision = Number.isFinite(config?.precision)
|
||||
? config.precision
|
||||
: DEFAULT_AVOIDANCE_PRECISION;
|
||||
|
||||
if (position instanceof THREE.Vector3) {
|
||||
return [
|
||||
"vec",
|
||||
@@ -85,11 +114,14 @@ function recomputeAvoidanceBucket(key) {
|
||||
if (!entries || entries.length === 0) return;
|
||||
|
||||
const affectedLayerIds = new Set(entries.map((entry) => entry.layerId));
|
||||
const crossLayer = affectedLayerIds.size > 1;
|
||||
if (entries.length === 1) {
|
||||
const entry = entries[0];
|
||||
entry.marker.position.copy(entry.marker.userData.icon_base_position);
|
||||
entry.marker.userData.icon_avoidance_index = 0;
|
||||
entry.marker.userData.icon_avoidance_count = 1;
|
||||
entry.marker.userData.icon_avoidance_layer_count = 1;
|
||||
entry.marker.userData.icon_avoidance_cross_layer = false;
|
||||
notifyAvoidancePositionChanged(affectedLayerIds);
|
||||
return;
|
||||
}
|
||||
@@ -127,6 +159,8 @@ function recomputeAvoidanceBucket(key) {
|
||||
entry.marker.position.copy(avoidancePositionScratch);
|
||||
entry.marker.userData.icon_avoidance_index = index;
|
||||
entry.marker.userData.icon_avoidance_count = count;
|
||||
entry.marker.userData.icon_avoidance_layer_count = affectedLayerIds.size;
|
||||
entry.marker.userData.icon_avoidance_cross_layer = crossLayer;
|
||||
});
|
||||
|
||||
notifyAvoidancePositionChanged(affectedLayerIds);
|
||||
@@ -657,9 +691,10 @@ export function createInteractableLayer(options = {}) {
|
||||
if (!position) return;
|
||||
const kind = getKind(item);
|
||||
const avoidanceKey = getAvoidanceKey(
|
||||
item,
|
||||
rawPosition,
|
||||
position,
|
||||
avoidanceConfig.precision,
|
||||
avoidanceConfig,
|
||||
);
|
||||
const marker = new THREE.Object3D();
|
||||
marker.position.copy(position);
|
||||
|
||||
Reference in New Issue
Block a user