Scale BIM empty-space wheel zoom
This commit is contained in:
parent
b90142a6c8
commit
9dc5dd4aa0
|
|
@ -303,6 +303,8 @@ let modelVersionHistoryLoading = false;
|
||||||
let modelVersionUploading = false;
|
let modelVersionUploading = false;
|
||||||
let versionHistoryModal = null;
|
let versionHistoryModal = null;
|
||||||
let emptyWheelNavigationRestoreTimer = null;
|
let emptyWheelNavigationRestoreTimer = null;
|
||||||
|
let lastSurfaceDollyFactor = null;
|
||||||
|
let lastSurfaceDollyFactorAt = 0;
|
||||||
const GUEST_TOOLBAR_ACTIONS = new Set(["measure", "section", "projection"]);
|
const GUEST_TOOLBAR_ACTIONS = new Set(["measure", "section", "projection"]);
|
||||||
const memoryStore = new Map(); // url -> ArrayBuffer
|
const memoryStore = new Map(); // url -> ArrayBuffer
|
||||||
const blobUrls = [];
|
const blobUrls = [];
|
||||||
|
|
@ -387,6 +389,7 @@ const ZOOM_SPEED_PERCENT_MAX = 100;
|
||||||
const ZOOM_DOLLY_RATE_MIN = 1;
|
const ZOOM_DOLLY_RATE_MIN = 1;
|
||||||
const ZOOM_DOLLY_RATE_MAX = 100;
|
const ZOOM_DOLLY_RATE_MAX = 100;
|
||||||
const ZOOM_DOLLY_RESPONSE_EXPONENT = 1.45;
|
const ZOOM_DOLLY_RESPONSE_EXPONENT = 1.45;
|
||||||
|
const LAST_SURFACE_DOLLY_FACTOR_TTL = 1500;
|
||||||
|
|
||||||
const normalizeZoomSpeed = (value) => {
|
const normalizeZoomSpeed = (value) => {
|
||||||
const numeric = Number(value);
|
const numeric = Number(value);
|
||||||
|
|
@ -401,6 +404,8 @@ const getZoomDollyRate = (value) => {
|
||||||
return ZOOM_DOLLY_RATE_MIN + (Math.pow(t, ZOOM_DOLLY_RESPONSE_EXPONENT) * (ZOOM_DOLLY_RATE_MAX - ZOOM_DOLLY_RATE_MIN));
|
return ZOOM_DOLLY_RATE_MIN + (Math.pow(t, ZOOM_DOLLY_RESPONSE_EXPONENT) * (ZOOM_DOLLY_RATE_MAX - ZOOM_DOLLY_RATE_MIN));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getBaseMouseWheelDollyRate = () => getZoomDollyRate(activeZoomSpeed);
|
||||||
|
|
||||||
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
|
const isHexColor = (value) => /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(String(value || "").trim());
|
||||||
|
|
||||||
const normalizeCustomDisplayColor = (value) => {
|
const normalizeCustomDisplayColor = (value) => {
|
||||||
|
|
@ -466,7 +471,7 @@ const configureCameraControl = () => {
|
||||||
control.smartPivot = true;
|
control.smartPivot = true;
|
||||||
control.zoomOnMouseWheel = true;
|
control.zoomOnMouseWheel = true;
|
||||||
control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate;
|
control.keyboardDollyRate = CAMERA_CONTROL_SETTINGS.keyboardDollyRate;
|
||||||
control.mouseWheelDollyRate = getZoomDollyRate(activeZoomSpeed);
|
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate();
|
||||||
control.dollyInertia = CAMERA_CONTROL_SETTINGS.dollyInertia;
|
control.dollyInertia = CAMERA_CONTROL_SETTINGS.dollyInertia;
|
||||||
control.dollyMinSpeed = CAMERA_CONTROL_SETTINGS.dollyMinSpeed;
|
control.dollyMinSpeed = CAMERA_CONTROL_SETTINGS.dollyMinSpeed;
|
||||||
control.dollyProximityThreshold = CAMERA_CONTROL_SETTINGS.dollyProximityThreshold;
|
control.dollyProximityThreshold = CAMERA_CONTROL_SETTINGS.dollyProximityThreshold;
|
||||||
|
|
@ -1341,6 +1346,60 @@ const getCanvasPointFromEvent = (event) => {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDollyFactorForDistance = (distance) => {
|
||||||
|
const numeric = Number(distance);
|
||||||
|
if (!Number.isFinite(numeric) || numeric <= 0) return 1;
|
||||||
|
return Math.max(
|
||||||
|
CAMERA_CONTROL_SETTINGS.dollyMinSpeed,
|
||||||
|
numeric / CAMERA_CONTROL_SETTINGS.dollyProximityThreshold,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDollyFactorForWorldPos = (worldPos) => {
|
||||||
|
const eye = viewer?.camera?.eye || viewer?.scene?.camera?.eye;
|
||||||
|
if (!eye || !worldPos || worldPos.length < 3) return null;
|
||||||
|
const distance = Math.hypot(
|
||||||
|
Number(worldPos[0]) - Number(eye[0]),
|
||||||
|
Number(worldPos[1]) - Number(eye[1]),
|
||||||
|
Number(worldPos[2]) - Number(eye[2]),
|
||||||
|
);
|
||||||
|
return Number.isFinite(distance) ? getDollyFactorForDistance(distance) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCameraFocusDollyFactor = () => {
|
||||||
|
const camera = viewer?.camera || viewer?.scene?.camera;
|
||||||
|
const directDistance = Number(camera?.eyeLookDist);
|
||||||
|
if (Number.isFinite(directDistance) && directDistance > 0) {
|
||||||
|
return getDollyFactorForDistance(directDistance);
|
||||||
|
}
|
||||||
|
const eye = camera?.eye;
|
||||||
|
const look = camera?.look;
|
||||||
|
if (!eye || !look) return 1;
|
||||||
|
const distance = Math.hypot(
|
||||||
|
Number(look[0]) - Number(eye[0]),
|
||||||
|
Number(look[1]) - Number(eye[1]),
|
||||||
|
Number(look[2]) - Number(eye[2]),
|
||||||
|
);
|
||||||
|
return Number.isFinite(distance) ? getDollyFactorForDistance(distance) : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rememberSurfaceDollyFactor = (hit) => {
|
||||||
|
const factor = getDollyFactorForWorldPos(hit?.worldPos);
|
||||||
|
if (factor === null) return;
|
||||||
|
lastSurfaceDollyFactor = factor;
|
||||||
|
lastSurfaceDollyFactorAt = window.performance?.now?.() || Date.now();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEmptyWheelDollyFactor = () => {
|
||||||
|
const focusFactor = getCameraFocusDollyFactor();
|
||||||
|
const now = window.performance?.now?.() || Date.now();
|
||||||
|
const hasFreshSurfaceFactor = (
|
||||||
|
lastSurfaceDollyFactor !== null &&
|
||||||
|
now - lastSurfaceDollyFactorAt <= LAST_SURFACE_DOLLY_FACTOR_TTL
|
||||||
|
);
|
||||||
|
return hasFreshSurfaceFactor ? Math.min(focusFactor, lastSurfaceDollyFactor) : focusFactor;
|
||||||
|
};
|
||||||
|
|
||||||
const restorePointerNavigation = () => {
|
const restorePointerNavigation = () => {
|
||||||
if (emptyWheelNavigationRestoreTimer) {
|
if (emptyWheelNavigationRestoreTimer) {
|
||||||
window.clearTimeout(emptyWheelNavigationRestoreTimer);
|
window.clearTimeout(emptyWheelNavigationRestoreTimer);
|
||||||
|
|
@ -1350,6 +1409,7 @@ const restorePointerNavigation = () => {
|
||||||
if (!control) return;
|
if (!control) return;
|
||||||
control.followPointer = true;
|
control.followPointer = true;
|
||||||
control.smartPivot = true;
|
control.smartPivot = true;
|
||||||
|
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate();
|
||||||
};
|
};
|
||||||
|
|
||||||
const schedulePointerNavigationRestore = () => {
|
const schedulePointerNavigationRestore = () => {
|
||||||
|
|
@ -1362,16 +1422,15 @@ const schedulePointerNavigationRestore = () => {
|
||||||
}, 80);
|
}, 80);
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasWheelSurfaceHit = (event) => {
|
const getWheelSurfaceHit = (event) => {
|
||||||
if (!activeModels.length || !viewer?.scene?.pick) return true;
|
if (!activeModels.length || !viewer?.scene?.pick) return true;
|
||||||
const point = getCanvasPointFromEvent(event);
|
const point = getCanvasPointFromEvent(event);
|
||||||
if (!point) return true;
|
if (!point) return true;
|
||||||
try {
|
try {
|
||||||
const hit = viewer.scene.pick({
|
return viewer.scene.pick({
|
||||||
canvasPos: point.canvasPos,
|
canvasPos: point.canvasPos,
|
||||||
pickSurface: true,
|
pickSurface: true,
|
||||||
});
|
});
|
||||||
return Boolean(hit?.entity || hit?.worldPos);
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1380,11 +1439,14 @@ const hasWheelSurfaceHit = (event) => {
|
||||||
const guardEmptyWheelNavigation = (event) => {
|
const guardEmptyWheelNavigation = (event) => {
|
||||||
const control = viewer?.cameraControl;
|
const control = viewer?.cameraControl;
|
||||||
if (!control) return;
|
if (!control) return;
|
||||||
if (hasWheelSurfaceHit(event)) {
|
const hit = getWheelSurfaceHit(event);
|
||||||
|
if (hit === true || hit?.entity || hit?.worldPos) {
|
||||||
|
rememberSurfaceDollyFactor(hit === true ? null : hit);
|
||||||
restorePointerNavigation();
|
restorePointerNavigation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
control.followPointer = false;
|
control.followPointer = false;
|
||||||
|
control.mouseWheelDollyRate = getBaseMouseWheelDollyRate() * getEmptyWheelDollyFactor();
|
||||||
schedulePointerNavigationRestore();
|
schedulePointerNavigationRestore();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue