feat: Initialize SvelteKit project, add tsconfig.json, and introduce a new Calculator.svelte component.
This commit is contained in:
187
hdyc-svelte/src/lib/engine.ts
Normal file
187
hdyc-svelte/src/lib/engine.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
// ─── Pure conversion engine ─────────────────────────────────────────
|
||||
// No DOM dependencies — just math. Used by the Calculator component.
|
||||
|
||||
import type { CalculatorDef } from './data/calculators';
|
||||
|
||||
export interface SolveResult {
|
||||
val1: string;
|
||||
val2: string;
|
||||
val3: string;
|
||||
}
|
||||
|
||||
function fmt(n: number): string {
|
||||
return parseFloat(n.toFixed(6)).toString();
|
||||
}
|
||||
|
||||
function gcd(a: number, b: number): number {
|
||||
a = Math.abs(Math.round(a));
|
||||
b = Math.abs(Math.round(b));
|
||||
return b ? gcd(b, a % b) : a;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the conversion for a given calculator definition.
|
||||
* `source` indicates which field the user is typing in (1, 2, or 3).
|
||||
* Returns new string values for all fields.
|
||||
*/
|
||||
export function solve(
|
||||
calc: CalculatorDef,
|
||||
source: 1 | 2 | 3,
|
||||
rawVal1: string,
|
||||
rawVal2: string,
|
||||
rawVal3: string
|
||||
): SolveResult {
|
||||
const v1 = parseFloat(rawVal1);
|
||||
const v2 = parseFloat(rawVal2);
|
||||
const v3 = parseFloat(rawVal3);
|
||||
const factor = calc.factor ?? 1;
|
||||
const offset = calc.offset ?? 0;
|
||||
|
||||
let out: SolveResult = { val1: rawVal1, val2: rawVal2, val3: rawVal3 };
|
||||
|
||||
switch (calc.type) {
|
||||
case 'standard':
|
||||
if (source === 1) {
|
||||
out.val2 = !isNaN(v1) ? fmt(v1 * factor + offset) : '';
|
||||
} else {
|
||||
out.val1 = !isNaN(v2) ? fmt((v2 - offset) / factor) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'inverse':
|
||||
if (source === 1) {
|
||||
out.val2 = (!isNaN(v1) && v1 !== 0) ? fmt(factor / v1) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v2) && v2 !== 0) ? fmt(factor / v2) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case '3col':
|
||||
if (source === 1 || source === 2) {
|
||||
out.val3 = (!isNaN(v1) && !isNaN(v2) && v2 !== 0) ? fmt(v1 / v2) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v3) && !isNaN(v2)) ? fmt(v3 * v2) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case '3col-mul':
|
||||
if (source === 1 || source === 2) {
|
||||
out.val3 = (!isNaN(v1) && !isNaN(v2)) ? fmt(v1 * v2) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v3) && !isNaN(v2) && v2 !== 0) ? fmt(v3 / v2) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'base': {
|
||||
const fromBase = calc.fromBase ?? 10;
|
||||
const toBase = calc.toBase ?? 2;
|
||||
if (source === 1) {
|
||||
const val = rawVal1.trim();
|
||||
if (!val) { out.val2 = ''; break; }
|
||||
const dec = parseInt(val, fromBase);
|
||||
out.val2 = !isNaN(dec) ? dec.toString(toBase).toUpperCase() : 'Invalid';
|
||||
} else {
|
||||
const val = rawVal2.trim();
|
||||
if (!val) { out.val1 = ''; break; }
|
||||
const dec = parseInt(val, toBase);
|
||||
out.val1 = !isNaN(dec) ? dec.toString(fromBase).toUpperCase() : 'Invalid';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'text-bin':
|
||||
if (source === 1) {
|
||||
out.val2 = rawVal1.split('').map(c => c.charCodeAt(0).toString(2).padStart(8, '0')).join(' ');
|
||||
} else {
|
||||
try {
|
||||
out.val1 = rawVal2.split(' ').map(b => String.fromCharCode(parseInt(b, 2))).join('');
|
||||
} catch { out.val1 = 'Error'; }
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bin-text':
|
||||
if (source === 1) {
|
||||
try {
|
||||
out.val2 = rawVal1.split(' ').map(b => String.fromCharCode(parseInt(b, 2))).join('');
|
||||
} catch { out.val2 = 'Error'; }
|
||||
} else {
|
||||
out.val1 = rawVal2.split('').map(c => c.charCodeAt(0).toString(2).padStart(8, '0')).join(' ');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'dms-dd':
|
||||
if (source === 1) {
|
||||
if (!isNaN(v1)) {
|
||||
const d = Math.floor(v1);
|
||||
const md = (v1 - d) * 60;
|
||||
const m = Math.floor(md);
|
||||
const sec = ((md - m) * 60).toFixed(2);
|
||||
out.val2 = `${d}° ${m}' ${sec}"`;
|
||||
} else { out.val2 = ''; }
|
||||
} else {
|
||||
const match = rawVal2.match(/(?:([0-9.-]+)\s*°)?\s*(?:([0-9.-]+)\s*')?\s*(?:([0-9.-]+)\s*")?/);
|
||||
if (match && rawVal2.trim().length > 0) {
|
||||
const d = parseFloat(match[1]) || 0;
|
||||
const m = parseFloat(match[2]) || 0;
|
||||
const sec = parseFloat(match[3]) || 0;
|
||||
out.val1 = fmt(d + m / 60 + sec / 3600);
|
||||
} else { out.val1 = ''; }
|
||||
}
|
||||
break;
|
||||
|
||||
case 'dec-frac':
|
||||
if (source === 1) {
|
||||
if (!isNaN(v1)) {
|
||||
const parts = v1.toString().split('.');
|
||||
const len = parts[1] ? parts[1].length : 0;
|
||||
const den = Math.pow(10, len);
|
||||
const num = v1 * den;
|
||||
const div = gcd(num, den);
|
||||
out.val2 = `${num / div}/${den / div}`;
|
||||
} else { out.val2 = ''; }
|
||||
} else {
|
||||
const parts = rawVal2.split('/');
|
||||
if (parts.length === 2 && !isNaN(Number(parts[0])) && !isNaN(Number(parts[1])) && Number(parts[1]) !== 0) {
|
||||
out.val1 = fmt(Number(parts[0]) / Number(parts[1]));
|
||||
} else {
|
||||
const f = parseFloat(parts[0]);
|
||||
out.val1 = !isNaN(f) ? f.toString() : '';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'db-int':
|
||||
if (source === 1) {
|
||||
out.val2 = !isNaN(v1) ? (1e-12 * Math.pow(10, v1 / 10)).toExponential(6) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v2) && v2 > 0) ? fmt(10 * Math.log10(v2 / 1e-12)) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'db-spl':
|
||||
if (source === 1) {
|
||||
out.val2 = !isNaN(v1) ? fmt(20 * Math.pow(10, v1 / 20)) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v2) && v2 > 0) ? fmt(20 * Math.log10(v2 / 20)) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'db-v':
|
||||
if (source === 1) {
|
||||
out.val2 = !isNaN(v1) ? fmt(Math.pow(10, v1 / 20)) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v2) && v2 > 0) ? fmt(20 * Math.log10(v2)) : '';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'db-w':
|
||||
if (source === 1) {
|
||||
out.val2 = !isNaN(v1) ? fmt(Math.pow(10, v1 / 10)) : '';
|
||||
} else {
|
||||
out.val1 = (!isNaN(v2) && v2 > 0) ? fmt(10 * Math.log10(v2)) : '';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user