Refactor: update coefficient mapping to a1=-1-r, a2=r+delta and remove 0.5 step
This commit is contained in:
+11
-11
@@ -451,11 +451,11 @@
|
||||
|
||||
<div class="grid grid-cols-2 gap-2 mb-4">
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r = (a2 - a1 - 1) / 2</span>
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r = -1 - a1</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-secondary" :title="formatFineCoeff(currentR)">{{ formatFineCoeff(currentR) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ = (a1 + a2 + 1) / 2</span>
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ = a1 + a2 + 1</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-secondary" :title="formatFineCoeff(currentDelta)">{{ formatFineCoeff(currentDelta) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -463,7 +463,7 @@
|
||||
<div class="role-bg-warning-soft border role-border-warning-soft rounded-lg p-3">
|
||||
<p class="text-[10px] role-text-warning leading-relaxed">
|
||||
<span class="font-bold">運算說明:</span><br>
|
||||
δ = (a1 + a2 + 1) / 2,r = (a2 - a1 - 1) / 2。更改數值會自動更新圖表;桌機可用 ± 或 shift + 滾輪,觸控時按住數值面板上下拖曳。
|
||||
a1 = -1 - r, a2 = r + δ。更改數值會自動更新圖表;桌機可用 ± 或 shift + 滾輪,觸控時按住數值面板上下拖曳。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -605,21 +605,21 @@
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold role-text-secondary" :title="formatFixedFineCoeff(fixedCurrentDelta)">{{ formatFixedFineCoeff(fixedCurrentDelta) }}</span>
|
||||
</button>
|
||||
|
||||
<article v-if="!isTouchInput" class="block" :class="{'opacity-40 pointer-events-none': fixedAFineStep === 0.5}">
|
||||
<article v-if="!isTouchInput" class="block">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<div class="grid grid-cols-[2rem_1fr_2rem] gap-1">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', -1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm" :disabled="fixedAFineStep === 0.5">-</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">-</button>
|
||||
<input type="number" :value="formatFixedFineCoeff(fixedCurrentR)" :step="fixedAFineStep"
|
||||
@change="setFixedAFineDirect('r', $event.target.value)" @keydown="onlyNumberKey"
|
||||
@wheel="handleFixedAFineWheel('r', $event)"
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300" :disabled="fixedAFineStep === 0.5">
|
||||
class="min-w-0 w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-xs font-mono text-center text-slate-700 dark:text-gray-300">
|
||||
<button type="button" @pointerdown.prevent="startFixedA1A2Repeat('r', 1)" @pointerup="stopFixedA1A2Repeat" @pointerleave="stopFixedA1A2Repeat" @pointercancel="stopFixedA1A2Repeat"
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm" :disabled="fixedAFineStep === 0.5">+</button>
|
||||
class="stepper-button w-full h-[42px] flex items-center justify-center rounded-full role-bg-primary-soft role-hover-primary-soft role-text-primary transition-all duration-150 active:scale-90 text-lg font-bold shadow-sm">+</button>
|
||||
</div>
|
||||
</article>
|
||||
<button v-else type="button" @pointerdown.prevent="startFixedAFineDrag('r', $event)"
|
||||
class="touch-none select-none w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20" :class="{'opacity-40 pointer-events-none': fixedAFineStep === 0.5}">
|
||||
class="touch-none select-none w-full bg-white dark:bg-gray-800 border border-slate-200 dark:border-gray-700 rounded-lg p-3 text-center active:border-slate-400 active:ring-2 active:ring-slate-400/20">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r</span>
|
||||
<span class="block max-w-full truncate text-base font-mono font-bold text-slate-700 dark:text-gray-300" :title="formatFixedFineCoeff(fixedCurrentR)">{{ formatFixedFineCoeff(fixedCurrentR) }}</span>
|
||||
</button>
|
||||
@@ -627,18 +627,18 @@
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 mb-4">
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r = (a2 - a1 - a0) / 2</span>
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">r = -a0 - a1</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-secondary" :title="formatFixedFineCoeff(fixedCurrentR)">{{ formatFixedFineCoeff(fixedCurrentR) }}</span>
|
||||
</div>
|
||||
<div class="min-w-0 bg-white dark:bg-gray-800 border border-slate-100 dark:border-gray-700 rounded-lg p-2 text-center shadow-sm">
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ = (a1 + a2 + a0) / 2</span>
|
||||
<span class="text-[9px] text-slate-400 block font-bold mb-1">δ = a1 + a2 + a0</span>
|
||||
<span class="block max-w-full truncate text-sm font-mono font-bold role-text-secondary" :title="formatFixedFineCoeff(fixedCurrentDelta)">{{ formatFixedFineCoeff(fixedCurrentDelta) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="role-bg-warning-soft border role-border-warning-soft rounded-lg p-3">
|
||||
<p class="text-[10px] role-text-warning leading-relaxed">
|
||||
<span class="font-bold">運算說明:</span><br>
|
||||
a0 = 2^{{ shiftBitsA }},δ = (a1 + a2 + a0) / 2,r = (a2 - a1 - a0) / 2。
|
||||
a0 = 2^{{ shiftBitsA }},a1 = -a0 - r, a2 = r + δ。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+23
-50
@@ -58,7 +58,7 @@ export default {
|
||||
shiftBitRepeatDelayTimer: null,
|
||||
shiftBitRepeatTimer: null,
|
||||
shiftBitDrag: null,
|
||||
fixedAFineStep: 0.5,
|
||||
fixedAFineStep: 1,
|
||||
fixedAFineDrag: null,
|
||||
fixedAFineRepeatDelayTimer: null,
|
||||
fixedAFineRepeatTimer: null,
|
||||
@@ -171,10 +171,13 @@ export default {
|
||||
return a[2] || 0;
|
||||
},
|
||||
currentDelta() {
|
||||
return (this.currentA1 + this.currentA2 + 1) / 2;
|
||||
// New Model: a1 = -1 - r, a2 = r + delta
|
||||
// => delta = a1 + a2 + 1
|
||||
return this.currentA1 + this.currentA2 + 1;
|
||||
},
|
||||
currentR() {
|
||||
return (this.currentA2 - this.currentA1 - 1) / 2;
|
||||
// New Model: a1 = -1 - r => r = -1 - a1
|
||||
return -1 - this.currentA1;
|
||||
},
|
||||
currentA1A2Diff() {
|
||||
return this.currentA1 - this.currentA2;
|
||||
@@ -192,10 +195,13 @@ export default {
|
||||
return this.fixedPointCoeffs.a[2]?.val || 0;
|
||||
},
|
||||
fixedCurrentDelta() {
|
||||
return (this.fixedCurrentA1 + this.fixedCurrentA2 + this.fixedA0Int) / 2;
|
||||
// New Model: a1 = -a0 - r, a2 = r + delta
|
||||
// => delta = a1 + a2 + a0
|
||||
return this.fixedCurrentA1 + this.fixedCurrentA2 + this.fixedA0Int;
|
||||
},
|
||||
fixedCurrentR() {
|
||||
return (this.fixedCurrentA2 - this.fixedCurrentA1 - this.fixedA0Int) / 2;
|
||||
// New Model: a1 = -a0 - r => r = -a0 - a1
|
||||
return -this.fixedA0Int - this.fixedCurrentA1;
|
||||
},
|
||||
fixedCurrentA1A2Diff() {
|
||||
return this.fixedCurrentA1 - this.fixedCurrentA2;
|
||||
@@ -337,7 +343,7 @@ export default {
|
||||
this.aFineStep = Number(clampedStep.toPrecision(12));
|
||||
},
|
||||
adjustFixedAFineStep(direction) {
|
||||
const steps = [0.5, 1, 2, 4, 8, 32, 256, 1024, 4096, 16384, 65536];
|
||||
const steps = [1, 2, 4, 8, 32, 256, 1024, 4096, 16384, 65536];
|
||||
let currentIdx = steps.indexOf(this.fixedAFineStep);
|
||||
|
||||
// 如果當前值不在數列中,找最接近的一個
|
||||
@@ -358,7 +364,8 @@ export default {
|
||||
const nextDelta = Number(delta);
|
||||
const nextR = Number(r);
|
||||
if (!Number.isFinite(nextDelta) || !Number.isFinite(nextR)) return;
|
||||
this.setA1A2(-1 - nextR + nextDelta, nextR + nextDelta);
|
||||
// New Model: a1 = -1 - r, a2 = r + delta
|
||||
this.setA1A2(-1 - nextR, nextR + nextDelta);
|
||||
},
|
||||
setAFineDirect(target, rawValue) {
|
||||
const nextValue = parseFloat(rawValue);
|
||||
@@ -413,20 +420,14 @@ export default {
|
||||
window.removeEventListener('pointercancel', this.stopAFineDrag);
|
||||
},
|
||||
setFixedDeltaR(delta, r) {
|
||||
// 使用「兩倍值」空間進行整數運算
|
||||
let S = Math.round(delta * 2);
|
||||
let D = Math.round(r * 2);
|
||||
const nextDelta = Math.round(delta);
|
||||
const nextR = Math.round(r);
|
||||
const a0 = this.fixedA0Int;
|
||||
|
||||
// 核心邏輯:a1, a2 為整數的充要條件是 S 與 D 的奇偶性相同
|
||||
// 這裡採用「優先滿足目標值」的策略:如果奇偶不同,則微調其中一個
|
||||
// 由於此函數由 UI 直接調用,我們無法判斷使用者剛才改了誰
|
||||
// 所以我們預設採用 Math.round 來尋找最接近的整數組合
|
||||
const a2 = Math.round((S + D) / 2);
|
||||
const a1 = a2 - D - a0;
|
||||
|
||||
this.fixedOverrides.a[1] = a1;
|
||||
this.fixedOverrides.a[2] = a2;
|
||||
// New Model: a1 = -a0 - r, a2 = r + delta
|
||||
// If r and delta are integers, a1 and a2 are guaranteed to be integers.
|
||||
this.fixedOverrides.a[1] = -a0 - nextR;
|
||||
this.fixedOverrides.a[2] = nextR + nextDelta;
|
||||
this.debouncedUpdateBode();
|
||||
},
|
||||
setFixedAFineDirect(target, rawValue) {
|
||||
@@ -439,40 +440,12 @@ export default {
|
||||
}
|
||||
},
|
||||
adjustFixedAFineValue(target, steps) {
|
||||
let S = Math.round(this.fixedCurrentDelta * 2);
|
||||
let D = Math.round(this.fixedCurrentR * 2);
|
||||
const dVal = Math.round(steps * this.fixedAFineStep * 2);
|
||||
const a0 = this.fixedA0Int;
|
||||
|
||||
const dVal = Math.round(steps * this.fixedAFineStep);
|
||||
if (target === 'delta') {
|
||||
// 優先保證 delta 變動到位
|
||||
S += dVal;
|
||||
// 檢查奇偶性是否匹配
|
||||
if ((S % 2 + 2) % 2 !== (D % 2 + 2) % 2) {
|
||||
// 核心:震盪平衡邏輯。
|
||||
// 如果 S 變為偶數,D 應該變為最近的偶數;如果 S 變為奇數,D 應該變為最近的奇數。
|
||||
// 我們判斷目前 D 是在 .5 還是 .0,然後向反方向跳轉,防止單向漂移。
|
||||
if (D % 2 !== 0) {
|
||||
D -= 1; // 10.5 -> 10.0
|
||||
} else {
|
||||
D += 1; // 10.0 -> 10.5
|
||||
}
|
||||
}
|
||||
this.setFixedDeltaR(this.fixedCurrentDelta + dVal, this.fixedCurrentR);
|
||||
} else if (target === 'r') {
|
||||
// 如果 r 沒被禁用,則優先保證 r 變動到位
|
||||
D += dVal;
|
||||
if ((S % 2 + 2) % 2 !== (D % 2 + 2) % 2) {
|
||||
if (S % 2 !== 0) S -= 1;
|
||||
else S += 1;
|
||||
}
|
||||
this.setFixedDeltaR(this.fixedCurrentDelta, this.fixedCurrentR + dVal);
|
||||
}
|
||||
|
||||
const a2 = (S + D) / 2;
|
||||
const a1 = a2 - D - a0;
|
||||
|
||||
this.fixedOverrides.a[1] = a1;
|
||||
this.fixedOverrides.a[2] = a2;
|
||||
this.debouncedUpdateBode();
|
||||
},
|
||||
handleFixedAFineWheel(target, event) {
|
||||
if (!event.shiftKey) {
|
||||
|
||||
Reference in New Issue
Block a user