topfans/frontend/composables/useLenticularCraftTiltPreview.js

164 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 铸爱光栅预览lenticular-result / asset-detail 等):物理参数、离散档位与陀螺仪启动节奏
*/
import { ref, nextTick } from 'vue'
import { useLenticularPreview } from '@/composables/useLenticularPreview.js'
import { useLenticularStudioTilt } from '@/composables/useLenticularStudioTilt.js'
const FLOW_PHYSICS = {
angleStability: 52,
transitionSmoothness: 40,
tiltSensitivity: 96,
sensorDeadzoneStrength: 0,
parallaxDepth: 18,
lenticularAnchorFloor: 0.1,
lenticularNonDominantResidualMin: 0.092,
lenticularPrevLayerGhostMin: 0.098,
lenticularBlendBaseScale: 1.1,
}
const DISCRETE_STEP_DEG = 13
const DISCRETE_STEP_HYST_DEG = 5
const TILT_MAG_TAU_MS = 150
const TILT_MAG_MAX_RISE_DPS = 82
const TILT_MAG_MAX_FALL_DPS = 168
const TILT_AWAY_FROM_LEVEL_DPS = 72
const TILT_RISE_TIGHTEN = 0.42
export const LENTICULAR_TILT_START_DELAY_MS = 560
/**
* @param {import('vue').Ref<Array>|import('vue').Ref<unknown>} layersRef
*/
export function useLenticularCraftTiltPreview(layersRef) {
const gyroSourceLabel = ref('simulation')
const { physics, layerTransforms, simulate, relax, snapSimulatedTilt } = useLenticularPreview(layersRef)
Object.assign(physics, FLOW_PHYSICS)
const discreteStableStep = ref(0)
let tiltMagEma = 0
let lastTiltMagSampleAt = 0
let lastSignedDegHint = null
function resetTiltMagFilter() {
tiltMagEma = 0
lastTiltMagSampleAt = 0
lastSignedDegHint = null
}
function getLayersArray() {
const v = layersRef.value !== undefined ? layersRef.value : layersRef
return Array.isArray(v) ? v : []
}
function simulateFromSignedDegrees(tiltMagDeg, signedDegHint) {
const ls = getLayersArray()
const n = Math.max(1, ls.length)
const raw = Math.abs(Number(tiltMagDeg) || 0)
const now = Date.now()
let dt = 33
if (lastTiltMagSampleAt > 0) {
dt = now - lastTiltMagSampleAt
}
lastTiltMagSampleAt = now
dt = Math.max(16, Math.min(100, dt))
const alpha = 1 - Math.exp(-dt / TILT_MAG_TAU_MS)
let nextEma = tiltMagEma + (raw - tiltMagEma) * alpha
const sec = dt * 0.001
let maxRise = TILT_MAG_MAX_RISE_DPS * sec
const maxFall = TILT_MAG_MAX_FALL_DPS * sec
if (Number.isFinite(signedDegHint)) {
if (lastSignedDegHint != null && sec > 1e-6) {
const dAbsSignedDt =
(Math.abs(signedDegHint) - Math.abs(lastSignedDegHint)) / sec
if (dAbsSignedDt > TILT_AWAY_FROM_LEVEL_DPS) {
maxRise *= TILT_RISE_TIGHTEN
}
}
lastSignedDegHint = signedDegHint
} else {
lastSignedDegHint = null
}
let dMag = nextEma - tiltMagEma
if (dMag > 0) {
dMag = Math.min(dMag, maxRise)
} else {
dMag = Math.max(dMag, -maxFall)
}
tiltMagEma += dMag
const absDeg = tiltMagEma
const STEP = DISCRETE_STEP_DEG
const MARGIN = DISCRETE_STEP_HYST_DEG
const stepBefore = discreteStableStep.value
let s = stepBefore
while (absDeg >= (s + 1) * STEP + MARGIN) s++
while (s > 0 && absDeg <= s * STEP - MARGIN) s--
discreteStableStep.value = s
const idx = s % n
const sens = physics.tiltSensitivity / 100
const mul = 0.44 + sens * 0.52
const u = (idx + 0.5) / n
const gPick = Math.max(-1, Math.min(1, 2 * u - 1))
const gamma = Math.max(-1, Math.min(1, gPick / mul))
simulate(gamma, 0)
}
function lockPreviewStill() {
discreteStableStep.value = 0
resetTiltMagFilter()
simulateFromSignedDegrees(0)
snapSimulatedTilt({ resetLayerSmoothing: true })
}
const { start: startTilt, stop: stopTilt } = useLenticularStudioTilt({
simulate,
simulateFromSignedDegrees,
gyroSourceLabel,
useStudioAccelDirect: true,
onTiltDriverFallback: () => {
discreteStableStep.value = 0
resetTiltMagFilter()
},
})
let entryTiltTimer = null
function cancelScheduledTiltStart() {
if (entryTiltTimer != null) {
clearTimeout(entryTiltTimer)
entryTiltTimer = null
}
}
function scheduleTiltStart() {
cancelScheduledTiltStart()
nextTick(() => {
relax(0.86)
lockPreviewStill()
entryTiltTimer = setTimeout(() => {
entryTiltTimer = null
startTilt()
}, LENTICULAR_TILT_START_DELAY_MS)
})
}
function stopTiltPreview() {
cancelScheduledTiltStart()
stopTilt()
}
return {
physics,
layerTransforms,
simulate,
relax,
gyroSourceLabel,
scheduleTiltStart,
stopTiltPreview,
lockPreviewStill,
}
}