lens focal distance bug fixes
This commit is contained in:
parent
fba31918c6
commit
f38ffef984
1 changed files with 78 additions and 94 deletions
|
|
@ -1,117 +1,101 @@
|
||||||
// app/api/options/lens/route.ts
|
// app/api/options/lens/route.ts
|
||||||
import { NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
import { directusFetch } from "@/lib/directus";
|
import { directusFetch } from "@/lib/directus";
|
||||||
|
|
||||||
type Target =
|
/**
|
||||||
| "settings_fiber"
|
* Parse "110x110", "110×110", "110 X 110", etc. -> [110, 110]
|
||||||
| "settings_uv"
|
* Returns null if we can't find two numbers.
|
||||||
| "settings_co2gal"
|
*/
|
||||||
| "settings_co2gan";
|
function parseFieldSize(s: unknown): [number, number] | null {
|
||||||
|
if (!s) return null;
|
||||||
/** Map target -> Directus collection */
|
const nums = String(s).match(/\d+(\.\d+)?/g)?.map((n) => Number(n)) ?? [];
|
||||||
function collectionForTarget(t?: string) {
|
if (nums.length >= 2 && Number.isFinite(nums[0]) && Number.isFinite(nums[1])) {
|
||||||
switch ((t ?? "") as Target) {
|
return [nums[0], nums[1]];
|
||||||
case "settings_fiber":
|
|
||||||
case "settings_uv":
|
|
||||||
case "settings_co2gal":
|
|
||||||
return "laser_scan_lens" as const; // has field_size + focal_length
|
|
||||||
case "settings_co2gan":
|
|
||||||
return "laser_focus_lens" as const; // has name (no focal_length)
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse "110x110", "110×110", "110 x 110", or single "110" => {w,h} */
|
/** Pull a clean integer from a value like "F160", "160", "160mm" */
|
||||||
function parseFieldSize(raw: unknown): { w: number; h: number } | null {
|
function parseFocalLength(v: unknown): number | null {
|
||||||
if (raw == null) return null;
|
if (v == null) return null;
|
||||||
const s = String(raw).trim();
|
const m = String(v).match(/\d+(\.\d+)?/);
|
||||||
const m = s.match(/(\d+(?:\.\d+)?)(?:\s*[x×]\s*(\d+(?:\.\d+)?))?/i);
|
|
||||||
if (!m) return null;
|
if (!m) return null;
|
||||||
const w = Number(m[1]);
|
const n = Number(m[0]);
|
||||||
const h = m[2] ? Number(m[2]) : w;
|
return Number.isFinite(n) ? Math.round(n) : null;
|
||||||
if (!Number.isFinite(w) || !Number.isFinite(h)) return null;
|
|
||||||
return { w, h };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fmtNum = (n: number) =>
|
/** Natural-ish compare for scan lens sizes: sort by width then height */
|
||||||
Number.isInteger(n) ? String(n) : String(n).replace(/\.0+$/, "");
|
function sizeCompare(a: string, b: string): number {
|
||||||
const dimsText = (d: { w: number; h: number }) => `${fmtNum(d.w)}x${fmtNum(d.h)}`;
|
const as = parseFieldSize(a);
|
||||||
|
const bs = parseFieldSize(b);
|
||||||
|
if (!as && !bs) return String(a).localeCompare(String(b));
|
||||||
|
if (!as) return 1;
|
||||||
|
if (!bs) return -1;
|
||||||
|
// width, then height
|
||||||
|
if (as[0] !== bs[0]) return as[0] - bs[0];
|
||||||
|
if (as[1] !== bs[1]) return as[1] - bs[1];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
export async function GET(req: Request) {
|
export async function GET(req: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const { searchParams } = new URL(req.url);
|
const { searchParams } = new URL(req.url);
|
||||||
const target = searchParams.get("target") || undefined;
|
const target = searchParams.get("target") || "";
|
||||||
const q = (searchParams.get("q") || "").toLowerCase().trim();
|
const q = (searchParams.get("q") || "").trim().toLowerCase();
|
||||||
const limit = Number(searchParams.get("limit") || "500");
|
const limit = Number(searchParams.get("limit") || "500");
|
||||||
|
|
||||||
const coll = collectionForTarget(target);
|
const isGantry = target === "settings_co2gan";
|
||||||
if (!coll) return NextResponse.json({ data: [] });
|
|
||||||
|
|
||||||
if (coll === "laser_scan_lens") {
|
if (isGantry) {
|
||||||
// fiber / uv / co2gal → scan lenses have field_size + focal_length
|
// CO2 Gantry -> FOCUS lenses
|
||||||
const { data } = await directusFetch<{ data: any[] }>(
|
const url = `/items/laser_focus_lens?fields=id,name&limit=${limit}`;
|
||||||
`/items/${coll}?fields=id,field_size,focal_length&limit=${encodeURIComponent(
|
const { data } = await directusFetch<{ data: Array<{ id: string | number; name?: string }> }>(url);
|
||||||
String(limit)
|
let rows = (data ?? []).map((r) => ({
|
||||||
)}`
|
id: String(r.id),
|
||||||
);
|
label: r.name?.trim() || String(r.id),
|
||||||
|
_key: r.name?.toLowerCase() ?? "",
|
||||||
|
}));
|
||||||
|
|
||||||
const rows = (data ?? []).map((r) => {
|
if (q) rows = rows.filter((r) => r.label.toLowerCase().includes(q));
|
||||||
const dim = parseFieldSize(r.field_size);
|
rows.sort((a, b) => a.label.localeCompare(b.label));
|
||||||
const fnumRaw = r.focal_length;
|
|
||||||
const label =
|
|
||||||
dim && fnumRaw != null && fnumRaw !== ""
|
|
||||||
? `${dimsText(dim)} (F${fmtNum(Number(fnumRaw))})`
|
|
||||||
: dim
|
|
||||||
? dimsText(dim)
|
|
||||||
: String(r.field_size ?? r.id);
|
|
||||||
|
|
||||||
const area =
|
return NextResponse.json({ data: rows.map(({ id, label }) => ({ id, label })) });
|
||||||
dim && Number.isFinite(dim.w) && Number.isFinite(dim.h)
|
|
||||||
? dim.w * dim.h
|
|
||||||
: Number.POSITIVE_INFINITY;
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: String(r.id),
|
|
||||||
label,
|
|
||||||
_sort: { area, w: dim?.w ?? Number.POSITIVE_INFINITY, h: dim?.h ?? Number.POSITIVE_INFINITY },
|
|
||||||
_search: `${r.field_size ?? ""} ${r.focal_length ?? ""} ${label}`.toLowerCase(),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const filtered = q ? rows.filter((r) => r._search.includes(q)) : rows;
|
|
||||||
|
|
||||||
filtered.sort((a, b) => {
|
|
||||||
if (a._sort.area !== b._sort.area) return a._sort.area - b._sort.area;
|
|
||||||
if (a._sort.w !== b._sort.w) return a._sort.w - b._sort.w;
|
|
||||||
if (a._sort.h !== b._sort.h) return a._sort.h - b._sort.h;
|
|
||||||
return a.label.localeCompare(b.label);
|
|
||||||
});
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
data: filtered.map(({ id, label }) => ({ id, label })),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CO2 Gantry → focus lenses only have "name"
|
// Galvo/UV/Fiber -> SCAN lenses with field_size + focal_length
|
||||||
const { data } = await directusFetch<{ data: any[] }>(
|
const url = `/items/laser_scan_lens?fields=id,field_size,focal_length&limit=${limit}`;
|
||||||
`/items/${coll}?fields=id,name&limit=${encodeURIComponent(String(limit))}`
|
const { data } = await directusFetch<{
|
||||||
);
|
data: Array<{ id: string | number; field_size?: string; focal_length?: string | number }>;
|
||||||
|
}>(url);
|
||||||
|
|
||||||
const rows = (data ?? []).map((r) => ({
|
let rows = (data ?? []).map((r) => {
|
||||||
id: String(r.id),
|
const sizeTxt = (r.field_size ?? "").toString().trim(); // e.g. "110x110"
|
||||||
label: String(r.name ?? r.id),
|
const fmm = parseFocalLength(r.focal_length); // e.g. 160
|
||||||
_search: String(r.name ?? r.id).toLowerCase(),
|
// Build label: "110x110mm (F160)" or "110x110mm" if focal length missing
|
||||||
}));
|
const label =
|
||||||
|
sizeTxt
|
||||||
const filtered = q ? rows.filter((r) => r._search.includes(q)) : rows;
|
? `${sizeTxt}mm${fmm != null ? ` (F${fmm})` : ""}`
|
||||||
filtered.sort((a, b) => a.label.localeCompare(b.label));
|
: `${r.id}`;
|
||||||
|
return {
|
||||||
return NextResponse.json({
|
id: String(r.id),
|
||||||
data: filtered.map(({ id, label }) => ({ id, label })),
|
label,
|
||||||
|
_size: sizeTxt,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
|
||||||
console.error("[options/lens] error:", err?.message || err);
|
if (q) {
|
||||||
return NextResponse.json({ data: [] }, { status: 200 });
|
const qq = q.toLowerCase();
|
||||||
|
rows = rows.filter((r) => r.label.toLowerCase().includes(qq));
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.sort((a, b) => {
|
||||||
|
const c = sizeCompare(a._size, b._size);
|
||||||
|
return c !== 0 ? c : a.label.localeCompare(b.label);
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({ data: rows.map(({ id, label }) => ({ id, label })) });
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error("[options/lens] error:", e?.message || e);
|
||||||
|
return NextResponse.json({ error: e?.message || "Internal error" }, { status: 500 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue