删除lenticular-studio中现已不被引用的代码

This commit is contained in:
liulong 2026-05-18 17:31:26 +08:00
parent 02233db0ec
commit 41e905dcbb
10 changed files with 82 additions and 618 deletions

View File

@ -0,0 +1,70 @@
---
name: "karpathy-guidelines"
description: "Karpathy 启发的编码行为指南,包含四大原则:编码前思考、简洁优先、精准修改、目标驱动执行。适用于所有编码任务,自动引导模型减少常见 LLM 编码错误。"
---
# Karpathy 编码行为指南
源自 [Andrej Karpathy 的观察](https://x.com/karpathy/status/2015883857489522876) 关于 LLM 编码陷阱的总结。
**权衡说明:** 这些指南倾向于谨慎而非速度。对于琐碎任务(简单拼写错误修复、显而易见的一行修改),请自行判断。
## 1. 编码前思考
**不要假设。不要隐藏困惑。呈现权衡。**
在实现之前:
- 明确陈述你的假设。如果不确定,询问。
- 如果存在多种解释,呈现它们 —— 不要默默选择。
- 如果存在更简单的方法,说出来。适时提出异议。
- 如果有不清楚的地方,停下来。指出困惑之处。询问。
## 2. 简洁优先
**用最少的代码解决问题。不要过度推测。**
- 不添加要求之外的功能。
- 不为一次性代码创建抽象。
- 不添加未要求的"灵活性"或"可配置性"。
- 不为不可能发生的场景做错误处理。
- 如果你写了 200 行代码,而 50 行就能搞定,重写它。
**自问:** "资深工程师会觉得这过于复杂吗?" 如果是,简化。
## 3. 精准修改
**只碰必须碰的。只清理自己造成的混乱。**
编辑现有代码时:
- 不要"改进"相邻的代码、注释或格式。
- 不要重构没坏的东西。
- 匹配现有风格,即使你更倾向于不同的写法。
- 如果注意到无关的死代码,提一下 —— 不要删除它。
当你的改动产生孤儿代码时:
- 删除因你的改动而变得无用的导入/变量/函数。
- 不要删除预先存在的死代码,除非被要求。
**检验标准:** 每一行修改都应该能直接追溯到用户的请求。
## 4. 目标驱动执行
**定义成功标准。循环验证直到达成。**
将指令式任务转化为可验证的目标:
- "添加验证" → "为无效输入编写测试,然后让它们通过"
- "修复 bug" → "编写重现 bug 的测试,然后让它通过"
- "重构 X" → "确保重构前后测试都能通过"
对于多步骤任务,说明一个简短的计划:
```
1. [步骤] → 验证: [检查]
2. [步骤] → 验证: [检查]
3. [步骤] → 验证: [检查]
```
强有力的成功标准让你能够独立循环执行。弱标准("让它工作")需要不断澄清。
---
**这些指南在起作用的标志:** diff 中不必要的改动更少、因过度复杂而导致的重写更少、澄清问题在实现之前提出而不是在犯错之后。

View File

@ -1,5 +1,5 @@
/**
* 铸爱流程中的光栅预览 lenticular-studio 一致的物理参数离散档位与陀螺仪启动节奏
* 铸爱光栅预览lenticular-result / asset-detail 物理参数离散档位与陀螺仪启动节奏
*/
import { ref, nextTick } from 'vue'
import { useLenticularPreview } from '@/composables/useLenticularPreview.js'

View File

@ -1,5 +1,5 @@
/**
* 光栅卡工作室倾斜驱动App 优先 imengyu-UniAndroidGyroDCloud 插件 id=6237否则加速度计
* 光栅卡倾斜驱动铸爱预览共用App 优先 imengyu-UniAndroidGyroDCloud 插件 id=6237否则加速度计
*
* 与插件约定对齐官方说明
* - 模块`imengyu-UniAndroidGyro-GyroModule``uni.requireNativePlugin`
@ -171,7 +171,7 @@ function uvForPlane(nx, ny, nz, mode) {
* @param {(x: number, y: number) => void} opts.simulate
* @param {(tiltMagDeg: number, signedDegHint?: number) => void} [opts.simulateFromSignedDegrees] 若提供用相对进入时基准的**倾角标量**驱动预览由页面做离散档位等可选第二参为有符号倾角加速度计路径可传以平衡两侧跟手
* @param {import('vue').Ref<string>} opts.gyroSourceLabel
* @param {boolean} [opts.useStudioAccelDirect] 光栅工作室加速度计用重力投影直连免原 warmup
* @param {boolean} [opts.useStudioAccelDirect] 铸爱光栅预览加速度计用重力投影直连免原 warmup
* @param {() => void} [opts.onTiltDriverFallback] 从原生陀螺失败/超时回退到加速度计时调用用于重置离散档位等 UI 状态
*/
export function useLenticularStudioTilt(opts) {

View File

@ -134,15 +134,6 @@
}
}
},
{
"path": "pages/castlove/lenticular-studio",
"style": {
"navigationStyle": "custom",
"app-plus": {
"bounce": "none"
}
}
},
{
"path": "pages/castlove/laser-card-studio",
"style": {

View File

@ -140,7 +140,7 @@ export default {
//
//
if (pos === 2) {
if (card.name === '光栅卡') {
if (card.name === '光栅卡' || card.name === '镭射卡') {
const route = this.cardRoutes[card.name];
if (route) {
uni.navigateTo({
@ -174,7 +174,7 @@ export default {
},
handleSkip() {
const card = this.cardList[this.selectedIndex]
if (card.name === '光栅卡') {
if (card.name === '光栅卡' || card.name === '镭射卡') {
const route = this.cardRoutes[card.name]
if (route) {
uni.navigateTo({

View File

@ -1,596 +0,0 @@
<template>
<view class="physics-page">
<view class="top-bar">
<view class="top-bar-btn" @tap="goBack">
<text class="nav-glyph"></text>
</view>
<view class="top-bar-title">
<text class="top-bar-title-text">光栅卡工作室</text>
<text class="top-bar-subtitle">倾斜手机 · 重力感应预览</text>
</view>
<view class="top-bar-actions">
<view class="top-bar-chip" @tap.stop="onRecalibrate">
<text class="top-bar-chip-text">校零</text>
</view>
<view class="top-bar-btn" @tap="onMore">
<text class="nav-glyph"></text>
</view>
</view>
</view>
<view class="canvas-wrap">
<view class="canvas-bg" />
<LenticularCard
class="preview-stack"
:layers="layers"
:transforms="layerTransforms"
:gyro-source="gyroSourceLabel"
:skip-built-in-touch="true"
:tilt-hint-text="''"
:shimmer-mid-opacity="0.16"
@simulate="simulate"
/>
</view>
<view class="tool-dock">
<view class="dock-rim" />
<view class="layer-strip">
<view class="layer-strip-header">
<text class="layer-strip-title">图层素材</text>
<text class="layer-strip-hint">点击替换 · 长按重置</text>
</view>
<view class="layer-slots">
<view
v-for="layer in layers"
:key="layer.id"
class="layer-slot"
:class="{ 'layer-slot--filled': !!layer.src }"
@tap="pickImage(layer.id)"
@longpress="resetLayer(layer.id)"
>
<image v-if="layer.src" class="layer-slot-img" :src="layer.src" mode="aspectFill" />
<view v-else class="layer-slot-placeholder" :style="placeholderStyle(layer)">
<text class="layer-slot-icon"></text>
</view>
<view class="layer-slot-meta">
<text class="layer-slot-name">{{ layer.label }}</text>
<text class="layer-slot-tag">{{ layer.src ? '已上传' : '点击上传' }}</text>
</view>
<view v-if="layer.src" class="layer-slot-badge" @tap.stop="resetLayer(layer.id)">
<text class="layer-slot-badge-icon">×</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
inheritAttrs: false,
};
</script>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import LenticularCard from '@/components/lenticular/LenticularCard.vue'
import { useLenticularPreview } from '@/composables/useLenticularPreview.js'
import { useLenticularStudioTilt } from '@/composables/useLenticularStudioTilt.js'
import {
buildLenticularLayersTwo,
LENTICULAR_STUDIO_STORAGE_KEY,
} from '@/utils/castloveMintForm.js'
const layers = ref([])
const gyroSourceLabel = ref('simulation')
const { physics, layerTransforms, simulate, relax, snapSimulatedTilt } = useLenticularPreview(layers)
/** 相对进入时基准的倾角标量(度),每满一档换一层;越大需掰得越狠才换图,体感更慢 */
const STUDIO_DISCRETE_STEP_DEG = 13
/** 档位施密特迟滞(度),减轻在档位边界来回跳;换向经过水平附近时略加大更稳 */
const DISCRETE_STEP_HYST_DEG = 5
/**
* 倾角幅度一阶低通的时间常数ms对称跟随进档/退档节奏一致避免抬手慢压手快的忽快忽慢
* dt 按实际回调间隔估算与陀螺轮询/加速度计频率解耦
*/
const STUDIO_TILT_MAG_TAU_MS = 150
/** 平滑后倾角幅度最大爬升速率(度/秒),抑制某一侧翻转时 raw 陡增导致的瞬间跳档 */
const STUDIO_TILT_MAG_MAX_RISE_DPS = 82
/** 回落可略快,减档仍跟手 */
const STUDIO_TILT_MAG_MAX_FALL_DPS = 168
/**
* 有符号倾角相对水平远离的速率阈值/超过则认为该帧在快速掰离水平
* 再收紧爬升限幅典型从下往上翻时 |atan2| 一侧梯度更大
*/
const STUDIO_TILT_AWAY_FROM_LEVEL_DPS = 72
/** 触发「快速远离水平」时,爬升限幅乘数(越小越匀) */
const STUDIO_TILT_RISE_TIGHTEN = 0.42
/** 进入页后延迟开启倾斜监听ms避免首帧/基线采集/大图解码时画面跟着抖 */
const STUDIO_TILT_START_DELAY_MS = 560
const discreteStableStep = ref(0)
/** 与离散档位同步平滑的倾角幅度(度) */
let studioTiltMagEma = 0
/** 上次幅度采样时间戳,用于按真实 dt 做指数平滑 */
let lastStudioTiltMagSampleAt = 0
/** 上一帧有符号倾角(度),仅加速度计路径传入;用于检测快速远离水平 */
let lastStudioSignedDegHint = null
function resetStudioTiltMagFilter() {
studioTiltMagEma = 0
lastStudioTiltMagSampleAt = 0
lastStudioSignedDegHint = null
}
/**
* 将相对基准的倾角标量非负映射为 LenticularEngine gamma
* @param {number} tiltMagDeg 相对校零的倾角幅度非负
* @param {number} [signedDegHint] 有符号倾角仅加速度计路径传入用于识别快速远离水平的一侧并收紧爬升
*/
function simulateFromSignedDegrees(tiltMagDeg, signedDegHint) {
const ls = layers.value || []
const n = Math.max(1, ls.length)
const raw = Math.abs(Number(tiltMagDeg) || 0)
const now = Date.now()
let dt = 33
if (lastStudioTiltMagSampleAt > 0) {
dt = now - lastStudioTiltMagSampleAt
}
lastStudioTiltMagSampleAt = now
dt = Math.max(16, Math.min(100, dt))
const alpha = 1 - Math.exp(-dt / STUDIO_TILT_MAG_TAU_MS)
let nextEma = studioTiltMagEma + (raw - studioTiltMagEma) * alpha
const sec = dt * 0.001
let maxRise = STUDIO_TILT_MAG_MAX_RISE_DPS * sec
const maxFall = STUDIO_TILT_MAG_MAX_FALL_DPS * sec
if (Number.isFinite(signedDegHint)) {
if (lastStudioSignedDegHint != null && sec > 1e-6) {
const dAbsSignedDt =
(Math.abs(signedDegHint) - Math.abs(lastStudioSignedDegHint)) / sec
if (dAbsSignedDt > STUDIO_TILT_AWAY_FROM_LEVEL_DPS) {
maxRise *= STUDIO_TILT_RISE_TIGHTEN
}
}
lastStudioSignedDegHint = signedDegHint
} else {
lastStudioSignedDegHint = null
}
let dMag = nextEma - studioTiltMagEma
if (dMag > 0) {
dMag = Math.min(dMag, maxRise)
} else {
dMag = Math.max(dMag, -maxFall)
}
studioTiltMagEma += dMag
const absDeg = studioTiltMagEma
const STEP = STUDIO_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)
/* 不在此 snap保留引擎内 displayGamma 渐近angleStability / transitionSmoothness
* 切换档位时 u 连续扫过叠化带才能看到一张渐隐另一张渐显 */
}
const { start: startTilt, stop: stopTilt, recalibrate: recalibrateTilt } = useLenticularStudioTilt({
simulate,
simulateFromSignedDegrees,
gyroSourceLabel,
useStudioAccelDirect: true,
onTiltDriverFallback: () => {
discreteStableStep.value = 0
resetStudioTiltMagFilter()
},
})
/** 首屏与换素材后:档位与 EMA 归零,并写入与 0° 档位一致的 gamma传感器未开时保持静止 */
function lockStudioPreviewStill() {
discreteStableStep.value = 0
resetStudioTiltMagFilter()
simulateFromSignedDegrees(0)
snapSimulatedTilt({ resetLayerSmoothing: true })
}
let entryTiltTimer = null
function goBack() {
uni.navigateBack({
delta: 1,
fail: () => {},
})
}
function onMore() {
uni.showActionSheet({
itemList: ['恢复为进入工作室时的图片'],
success: (res) => {
if (res.tapIndex === 0) {
const raw = uni.getStorageSync(LENTICULAR_STUDIO_STORAGE_KEY)
if (!raw) return
try {
const p = typeof raw === 'string' ? JSON.parse(raw) : raw
layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
discreteStableStep.value = 0
resetStudioTiltMagFilter()
nextTick(() => relax(0.86))
uni.showToast({ title: '已恢复', icon: 'none' })
} catch (e) {
console.error(e)
}
}
},
})
}
function placeholderStyle(layer) {
if (layer.background) {
return { background: layer.background }
}
return { background: 'rgba(6,14,32,0.55)' }
}
function pickImage(layerId) {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const path = res.tempFilePaths && res.tempFilePaths[0]
if (!path) return
const idx = layers.value.findIndex((l) => l.id === layerId)
if (idx < 0) return
const cur = layers.value[idx]
const next = { ...cur, src: path, background: undefined }
if (layerId === 'base') next.dots = undefined
const copy = [...layers.value]
copy[idx] = next
layers.value = copy
nextTick(() => relax(0.86))
},
fail: (err) => {
const msg = (err && err.errMsg) || ''
if (!msg.includes('cancel')) {
uni.showToast({ title: '选择图片失败', icon: 'none' })
}
},
})
}
function resetLayer(layerId) {
const fresh = buildLenticularLayersTwo('', '')
const defLayer = fresh.find((l) => l.id === layerId)
const idx = layers.value.findIndex((l) => l.id === layerId)
if (idx < 0 || !defLayer) return
const copy = [...layers.value]
copy[idx] = { ...defLayer }
layers.value = copy
nextTick(() => relax(0.86))
}
function onRecalibrate() {
discreteStableStep.value = 0
resetStudioTiltMagFilter()
recalibrateTilt()
nextTick(() => {
simulateFromSignedDegrees(0)
snapSimulatedTilt({ resetLayerSmoothing: true })
})
uni.showToast({ title: '已重置水平基准', icon: 'none' })
}
onMounted(() => {
try {
const raw = uni.getStorageSync(LENTICULAR_STUDIO_STORAGE_KEY)
if (!raw) {
uni.showToast({ title: '缺少素材数据,请返回上一步', icon: 'none' })
setTimeout(() => goBack(), 1600)
return
}
const p = typeof raw === 'string' ? JSON.parse(raw) : raw
layers.value = buildLenticularLayersTwo(p.bgPath || '', p.subjectPath || '')
/* 光栅感:略抬残影/ghost叠化带宽略加宽配合非 snap 的 gamma 渐近,换图时渐隐渐显更明显 */
Object.assign(physics, {
/* 略提高:让 smoothedSensor → displayGamma 在档位间多走几帧,叠化更明显 */
angleStability: 52,
transitionSmoothness: 40,
tiltSensitivity: 96,
sensorDeadzoneStrength: 0,
parallaxDepth: 18,
lenticularAnchorFloor: 0.1,
lenticularNonDominantResidualMin: 0.092,
lenticularPrevLayerGhostMin: 0.098,
lenticularBlendBaseScale: 1.1,
})
} catch (e) {
console.error('[lenticular-studio] load payload', e)
uni.showToast({ title: '数据无效', icon: 'none' })
setTimeout(() => goBack(), 1600)
return
}
nextTick(() => {
lockStudioPreviewStill()
if (entryTiltTimer != null) {
clearTimeout(entryTiltTimer)
entryTiltTimer = null
}
entryTiltTimer = setTimeout(() => {
entryTiltTimer = null
startTilt()
}, STUDIO_TILT_START_DELAY_MS)
})
})
onUnmounted(() => {
if (entryTiltTimer != null) {
clearTimeout(entryTiltTimer)
entryTiltTimer = null
}
stopTilt()
})
</script>
<style>
.nav-glyph {
font-size: 22px;
line-height: 1;
font-weight: 600;
color: inherit;
}
</style>
<style scoped>
.physics-page {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
background: #0b1326;
color: #dae2fd;
overflow: hidden;
position: relative;
}
.top-bar {
position: relative;
z-index: 50;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 16px;
height: 56px;
padding-top: calc(env(safe-area-inset-top) + 4px);
background: rgba(11, 19, 38, 0.88);
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.top-bar-actions {
display: flex;
align-items: center;
gap: 4px;
}
.top-bar-chip {
padding: 6px 12px;
border-radius: 999px;
background: rgba(45, 52, 73, 0.75);
border: 1px solid rgba(255, 255, 255, 0.12);
}
.top-bar-chip-text {
font-size: 12px;
font-weight: 600;
color: #ddb7ff;
}
.top-bar-btn {
width: 44px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
color: #ddb7ff;
}
.top-bar-title {
display: flex;
flex-direction: column;
align-items: center;
}
.top-bar-title-text {
font-size: 18px;
font-weight: 600;
color: #dae2fd;
}
.top-bar-subtitle {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.16em;
color: #4cd7f6;
margin-top: 2px;
}
.canvas-wrap {
flex: 1;
min-height: 0;
position: relative;
display: flex;
align-items: center;
justify-content: center;
padding: 8px 10px 4px;
z-index: 10;
}
.preview-stack {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
min-height: 0;
display: flex;
align-items: center;
justify-content: center;
contain: layout paint;
transform: translateZ(0);
}
.canvas-bg {
position: absolute;
inset: 0;
background:
radial-gradient(ellipse at 50% 35%, rgba(132, 43, 210, 0.18), transparent 55%),
radial-gradient(ellipse at 50% 85%, rgba(76, 215, 246, 0.1), transparent 60%);
pointer-events: none;
}
.tool-dock {
position: relative;
z-index: 20;
margin: 0 10px calc(10px + env(safe-area-inset-bottom));
max-width: 460px;
align-self: center;
width: calc(100% - 20px);
background: rgba(34, 42, 61, 0.88);
border: 1px solid rgba(77, 67, 84, 0.45);
border-radius: 20px;
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 10px;
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
overflow: hidden;
}
.dock-rim {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(221, 183, 255, 0.45), transparent);
}
.layer-strip {
display: flex;
flex-direction: column;
gap: 8px;
}
.layer-strip-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 2px;
}
.layer-strip-title {
font-size: 13px;
font-weight: 600;
color: #dae2fd;
}
.layer-strip-hint {
font-size: 11px;
color: rgba(207, 194, 214, 0.6);
}
.layer-slots {
display: flex;
gap: 8px;
}
.layer-slot {
flex: 1;
position: relative;
display: flex;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
background: rgba(6, 14, 32, 0.55);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.layer-slot--filled {
border-color: rgba(76, 215, 246, 0.5);
box-shadow: 0 0 14px rgba(76, 215, 246, 0.15);
}
.layer-slot-img {
width: 100%;
height: 56px;
display: block;
}
.layer-slot-placeholder {
width: 100%;
height: 56px;
display: flex;
align-items: center;
justify-content: center;
background-size: cover;
background-position: center;
}
.layer-slot-icon {
font-size: 22px;
color: rgba(221, 183, 255, 0.85);
}
.layer-slot-meta {
display: flex;
flex-direction: column;
padding: 6px 8px 8px;
gap: 2px;
background: rgba(6, 14, 32, 0.55);
}
.layer-slot-name {
font-size: 12px;
font-weight: 600;
color: #dae2fd;
}
.layer-slot-tag {
font-size: 10px;
color: rgba(207, 194, 214, 0.7);
}
.layer-slot--filled .layer-slot-tag {
color: #4cd7f6;
}
.layer-slot-badge {
position: absolute;
top: 4px;
right: 4px;
width: 22px;
height: 22px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: rgba(0, 0, 0, 0.55);
border: 1px solid rgba(255, 255, 255, 0.18);
}
.layer-slot-badge-icon {
font-size: 14px;
color: #dae2fd;
}
</style>

View File

@ -1 +0,0 @@
/* 已移除单图上传相关样式 */

View File

@ -2,7 +2,7 @@
* 铸爱 统一Thinking 选择 详情确认 铸造路由与 Storage
*
* 本模块管理铸爱castlove生成流程的页面跳转数据持久化和状态管理
* 支持四种模式API生成预填充选择光栅工作室镭射工作室
* 支持四种模式API生成预填充选择光栅卡流程lenticular/*镭射工作室
*/
import {
@ -31,7 +31,7 @@ export const CRAFT_SELECTED_INDEX_KEY = 'craft_selected_index'
export const FLOW_MODE_API = 'api'
/** 模式:预填充(用户已有所需图片,直接进入选择) */
export const FLOW_MODE_PREFILLED = 'prefilled'
/** 模式:光栅工作室工作台生成不走AI四图) */
/** 模式:光栅lenticular-create → thinking → result不走 AI 四图) */
export const FLOW_MODE_LENTICULAR = 'lenticular'
/** 模式镭射工作室工作台生成不走AI四图 */
export const FLOW_MODE_LASER = 'laser'
@ -317,7 +317,7 @@ export function startPrefilledSelectionFlow({
}
/**
* 持久化光栅工作室预览元数据
* 持久化光栅卡预览元数据写入 LENTICULAR_STUDIO_STORAGE_KEY
* 在进入光栅结果页面前调用保存背景图主体图材质等信息
*
* @param {Object} formData - 包含lenticularBgImagelenticularSubjectImage等字段
@ -425,7 +425,7 @@ export function isDetailAfterSelect(formData) {
}
/**
* 判断是否为光栅工作室类型
* 判断是否为光栅卡工作室流程studioKind === lenticular
* @param {Object} formData - 表单数据
* @returns {boolean}
*/

View File

@ -24,14 +24,14 @@ export function defaultLenticularDots() {
]
}
/** 进入光栅工作室前写入的临时数据create → lenticular-studio */
/** 光栅卡流程lenticular-create / thinking 写入的 bg+subject 预览载荷(见 persistLenticularPreviewMeta */
export const LENTICULAR_STUDIO_STORAGE_KEY = 'lenticular_studio_payload'
/** 单图工艺create → 镭射工坊Laser-Card 页)入口载荷 */
export const CASTLOVE_LASER_ENTRY_KEY = 'castlove_laser_entry_payload'
/**
* 仅背景 + 主体两图层无高光层光栅卡工作室一致
* 仅背景 + 主体两图层无高光层铸爱光栅预览result 一致
* @param {string} bgSrc 背景图本地路径或 URL
* @param {string} subjectSrc 主体图本地路径或 URL
*/

View File

@ -24,7 +24,7 @@ export const DEFAULT_PHYSICS = {
sensorDeadzoneStrength: 1,
/**
* 以下为可选叠化微调undefined 时用引擎内置默认
* 光栅工作室离散档位预览可压低另一张图残影
* 铸爱光栅离散档位预览可压低另一张图残影
*/
/** 首层(常为底图)最低可见度 0~1默认约 0.18 */
lenticularAnchorFloor: undefined,