196 lines
5.7 KiB
JavaScript
196 lines
5.7 KiB
JavaScript
/**
|
||
* 铸爱流程中的光栅预览:与 lenticular-studio 一致的物理参数、离散档位与陀螺仪启动节奏
|
||
*/
|
||
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: 1,
|
||
parallaxDepth: 18,
|
||
lenticularAnchorFloor: 0.1,
|
||
lenticularNonDominantResidualMin: 0.092,
|
||
lenticularPrevLayerGhostMin: 0.098,
|
||
lenticularBlendBaseScale: 1.1,
|
||
}
|
||
|
||
const DISCRETE_STEP_DEG = 13
|
||
const DISCRETE_STEP_HYST_DEG = 9
|
||
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) {
|
||
console.log('[DEBUG simulateFromSignedDegrees] called, tiltMagDeg:', tiltMagDeg, 'signedDegHint:', 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)) {
|
||
console.log('[DEBUG simulateFromSignedDegrees] signedDegHint is finite:', 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
|
||
|
||
// 二值模式:仅允许 0(中性) 或 1(倾斜),防止同方向重复递增档位
|
||
const ENTER_THRESHOLD = STEP + MARGIN
|
||
const EXIT_THRESHOLD = MARGIN
|
||
|
||
if (s === 0) {
|
||
// 中性状态:仅当倾斜超过阈值才进入档位1
|
||
if (absDeg >= ENTER_THRESHOLD) {
|
||
s = 1
|
||
console.log('[DEBUG] 进入档位1, absDeg:', absDeg.toFixed(2), 'signedDegHint:', signedDegHint)
|
||
}
|
||
} else if (s === 1) {
|
||
// 档位1状态:检测方向变化才允许退回
|
||
// 如果 signedDegHint 符号改变(反方向倾斜),立即回到中性
|
||
if (lastSignedDegHint !== null && Number.isFinite(signedDegHint)) {
|
||
if (signedDegHint * lastSignedDegHint < 0) {
|
||
// 方向改变,回到中性
|
||
s = 0
|
||
lastSignedDegHint = signedDegHint
|
||
console.log('[DEBUG] 方向改变,回到中性, signedDegHint:', signedDegHint.toFixed(2))
|
||
}
|
||
} else if (absDeg <= EXIT_THRESHOLD) {
|
||
// 没有方向信息时,使用角度阈值
|
||
s = 0
|
||
console.log('[DEBUG] 角度不足,回到中性, absDeg:', absDeg.toFixed(2))
|
||
}
|
||
}
|
||
|
||
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() {
|
||
console.log('[DEBUG useLenticularCraftTiltPreview] scheduleTiltStart called')
|
||
cancelScheduledTiltStart()
|
||
nextTick(() => {
|
||
relax(0.86)
|
||
lockPreviewStill()
|
||
console.log('[DEBUG useLenticularCraftTiltPreview] entryTiltTimer set, delay:', LENTICULAR_TILT_START_DELAY_MS)
|
||
entryTiltTimer = setTimeout(() => {
|
||
entryTiltTimer = null
|
||
console.log('[DEBUG useLenticularCraftTiltPreview] startTilt about to be called')
|
||
startTilt()
|
||
console.log('[DEBUG useLenticularCraftTiltPreview] startTilt called')
|
||
}, LENTICULAR_TILT_START_DELAY_MS)
|
||
})
|
||
}
|
||
|
||
function stopTiltPreview() {
|
||
cancelScheduledTiltStart()
|
||
stopTilt()
|
||
}
|
||
|
||
return {
|
||
physics,
|
||
layerTransforms,
|
||
simulate,
|
||
relax,
|
||
gyroSourceLabel,
|
||
scheduleTiltStart,
|
||
stopTiltPreview,
|
||
lockPreviewStill,
|
||
}
|
||
}
|