Skip to content

Commit 0c56e6d

Browse files
committed
simple style
1 parent a5ac7cc commit 0c56e6d

3 files changed

Lines changed: 42 additions & 105 deletions

File tree

apps/probe-viewer/src/components/ProbeCanvas.tsx

Lines changed: 16 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -207,47 +207,28 @@ export const ProbeCanvas = forwardRef<HTMLCanvasElement, ProbeCanvasProps>(
207207
if (probe.probe_planar_contour && probe.probe_planar_contour.length > 1) {
208208
ctx.beginPath();
209209
probe.probe_planar_contour.forEach((point, index) => {
210-
const [x, y] = projectPoint(point);
210+
const [px, py] = projectPoint(point);
211211
if (index === 0) {
212-
ctx.moveTo(x, y);
212+
ctx.moveTo(px, py);
213213
} else {
214-
ctx.lineTo(x, y);
214+
ctx.lineTo(px, py);
215215
}
216216
});
217217
ctx.closePath();
218-
ctx.fillStyle = "rgba(180, 185, 195, 0.7)"; // Metallic silver
219-
ctx.strokeStyle = "rgba(100, 105, 115, 0.95)";
220-
ctx.lineWidth = Math.max(1.2, 2.5 * (scale / 100));
218+
219+
// Technical line-art: a faint cool wash so the shank reads as a region,
220+
// with a thin precise outline. No fill gradient or shadow.
221+
ctx.fillStyle = "rgba(51, 65, 85, 0.05)";
221222
ctx.fill();
223+
ctx.strokeStyle = "rgba(51, 65, 85, 0.9)";
224+
ctx.lineWidth = Math.max(1, Math.min(1.6, 2 * (scale / 120)));
222225
ctx.stroke();
223226
}
224227

225228
const contactPositions = probe.contact_positions ?? [];
226229
const contactShapes = probe.contact_shapes ?? [];
227230
const contactShapeParams = probe.contact_shape_params ?? [];
228231

229-
// A contact's pixel dimensions, used both to size the metallic gradient and
230-
// to skip the sheen on pads too small for it to register.
231-
const contactDims = (shape: string, params: ContactShapeParams) => {
232-
switch (shape) {
233-
case "circle": {
234-
const d = (params.radius ?? 5) * 2 * scale;
235-
return { w: d, h: d, minPx: d, gradient: true };
236-
}
237-
case "square": {
238-
const s = (params.width ?? 10) * scale;
239-
return { w: s, h: s, minPx: s, gradient: true };
240-
}
241-
case "rect": {
242-
const w = (params.width ?? 10) * scale;
243-
const h = (params.height ?? 15) * scale;
244-
return { w, h, minPx: Math.min(w, h), gradient: true };
245-
}
246-
default:
247-
return { w: 0, h: 0, minPx: 0, gradient: false };
248-
}
249-
};
250-
251232
// Draws one contact path centered on the current origin (callers translate
252233
// the context to the pad position first). Rectangular pads get lightly
253234
// rounded corners so they read as real electrode pads, not hard tiles.
@@ -263,7 +244,7 @@ export const ProbeCanvas = forwardRef<HTMLCanvasElement, ProbeCanvasProps>(
263244
case "rect": {
264245
const w = (params.width ?? 10) * scale;
265246
const h = (shape === "square" ? (params.width ?? 10) : (params.height ?? 15)) * scale;
266-
const r = Math.min(w, h) * 0.18;
247+
const r = Math.min(w, h) * 0.12;
267248
if (typeof ctx.roundRect === "function") {
268249
ctx.roundRect(-w / 2, -h / 2, w, h, r);
269250
} else {
@@ -283,54 +264,21 @@ export const ProbeCanvas = forwardRef<HTMLCanvasElement, ProbeCanvasProps>(
283264
}
284265
};
285266

286-
// One metallic gold gradient per distinct pad size per frame. Contacts are
287-
// usually uniform, so this is built once and reused across all of them.
288-
const gradientCache = new Map<string, CanvasGradient>();
289-
const goldGradient = (w: number, h: number) => {
290-
const key = `${Math.round(w)}x${Math.round(h)}`;
291-
let g = gradientCache.get(key);
292-
if (!g) {
293-
g = ctx.createLinearGradient(-w / 2, -h / 2, w / 2, h / 2);
294-
g.addColorStop(0, "rgba(248, 228, 156, 1)"); // warm highlight (top-left)
295-
g.addColorStop(0.45, "rgba(212, 175, 55, 1)"); // gold body
296-
g.addColorStop(1, "rgba(146, 108, 28, 1)"); // deep bronze (bottom-right)
297-
gradientCache.set(key, g);
298-
}
299-
return g;
300-
};
301-
267+
// Flat gold contacts (the recognizable electrode convention), with a defined
268+
// bronze outline and no gradient or shadow — focal without the metallic
269+
// shine that was pulling focus.
302270
contactPositions.forEach((position, index) => {
303271
const [x, y] = projectPoint(position);
304272
const shape = contactShapes[index] ?? "";
305273
const params = contactShapeParams[index] ?? {};
306-
const dims = contactDims(shape, params);
307274

308275
ctx.save();
309276
ctx.translate(x, y);
310277
drawContactShape(shape, params);
311-
312-
// Metallic sheen when the pad is big enough to show it; below that a flat
313-
// gold that looks identical at that size but is cheaper.
314-
ctx.fillStyle =
315-
dims.gradient && dims.minPx >= 5
316-
? goldGradient(dims.w, dims.h)
317-
: "rgba(212, 175, 55, 1)";
318-
319-
// A soft, capped shadow lifts the pad off the silver shank without the
320-
// hard offset double-image the previous two-pass approach produced.
321-
ctx.shadowColor = "rgba(15, 12, 4, 0.35)";
322-
ctx.shadowBlur = Math.min(5, Math.max(1.5, dims.minPx * 0.12));
323-
ctx.shadowOffsetX = 0;
324-
ctx.shadowOffsetY = Math.min(2.5, Math.max(0.4, dims.minPx * 0.06));
278+
ctx.fillStyle = "rgba(212, 175, 55, 1)";
325279
ctx.fill();
326-
327-
// Clear the shadow before the rim so the outline stays crisp.
328-
ctx.shadowColor = "transparent";
329-
ctx.shadowBlur = 0;
330-
ctx.shadowOffsetY = 0;
331-
332-
ctx.lineWidth = Math.min(2, Math.max(0.8, dims.minPx * 0.03));
333-
ctx.strokeStyle = "rgba(110, 80, 25, 0.85)";
280+
ctx.lineWidth = Math.max(1, Math.min(1.8, 2.5 * (scale / 150)));
281+
ctx.strokeStyle = "rgba(110, 80, 25, 0.9)";
334282
ctx.stroke();
335283
ctx.restore();
336284
});

apps/probe-viewer/src/components/ProbeOverview.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,10 @@ export function ProbeOverview({
121121
else ctx.lineTo(x, y);
122122
});
123123
ctx.closePath();
124-
ctx.fillStyle = "rgba(180, 185, 195, 0.8)";
125-
ctx.strokeStyle = "rgba(100, 105, 115, 0.95)";
124+
// Technical line-art: a faint cool wash with a thin outline, matching the
125+
// main canvas.
126+
ctx.fillStyle = "rgba(51, 65, 85, 0.06)";
127+
ctx.strokeStyle = "rgba(51, 65, 85, 0.85)";
126128
ctx.lineWidth = 1;
127129
ctx.fill();
128130
ctx.stroke();
@@ -146,9 +148,9 @@ export function ProbeOverview({
146148
const viewRectX = (effectiveViewCenterX - geometry.centerX) * minimapScale + offsetX - viewRectWidth / 2;
147149
const viewRectY = -(effectiveViewCenterY - geometry.centerY) * minimapScale + offsetY - viewRectHeight / 2;
148150

149-
// Draw viewport rectangle
150-
ctx.strokeStyle = "rgba(59, 130, 246, 0.9)"; // Blue
151-
ctx.fillStyle = "rgba(59, 130, 246, 0.15)";
151+
// Draw viewport rectangle (graphite accent, matching the monochrome chrome)
152+
ctx.strokeStyle = "rgba(15, 23, 42, 0.85)";
153+
ctx.fillStyle = "rgba(15, 23, 42, 0.08)";
152154
ctx.lineWidth = 2;
153155
ctx.fillRect(viewRectX, viewRectY, viewRectWidth, viewRectHeight);
154156
ctx.strokeRect(viewRectX, viewRectY, viewRectWidth, viewRectHeight);
@@ -192,7 +194,7 @@ export function ProbeOverview({
192194
ctx.fillText(label, barX + scaleBarPixels / 2, barY - 4);
193195

194196
// Border around minimap
195-
ctx.strokeStyle = "rgba(100, 105, 115, 0.5)";
197+
ctx.strokeStyle = "rgba(51, 65, 85, 0.3)";
196198
ctx.lineWidth = 1;
197199
ctx.strokeRect(0.5, 0.5, MINIMAP_WIDTH - 1, MINIMAP_HEIGHT - 1);
198200

apps/probe-viewer/src/utils/exportUtils.ts

Lines changed: 18 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -347,53 +347,52 @@ function generateProbeSvgString(
347347

348348
const elements: string[] = [];
349349

350-
// Probe contour
350+
// Probe contour: technical line-art — a faint cool wash so the shank reads as
351+
// a region, with a thin precise outline. No fill gradient or shadow.
351352
if (probe.probe_planar_contour && probe.probe_planar_contour.length > 1) {
352353
const points = probe.probe_planar_contour
353354
.map((p) => projectPoint(p).join(","))
354355
.join(" ");
355-
const strokeWidth = Math.max(1.2, 2.5 * (scale / 100));
356+
const strokeWidth = Math.max(1, Math.min(1.6, 2 * (scale / 120)));
356357
elements.push(
357-
`<polygon points="${points}" fill="rgba(180, 185, 195, 0.7)" stroke="rgba(100, 105, 115, 0.95)" stroke-width="${strokeWidth}" stroke-linejoin="round"/>`
358+
`<polygon points="${points}" fill="rgba(51, 65, 85, 0.05)" stroke="rgb(51, 65, 85)" stroke-opacity="0.9" stroke-width="${strokeWidth}" stroke-linejoin="round"/>`
358359
);
359360
}
360361

361362
const contactPositions = probe.contact_positions ?? [];
362363
const contactShapes = probe.contact_shapes ?? [];
363364
const contactShapeParams = probe.contact_shape_params ?? [];
364-
const shadowOffset = 0.4 * scale; // 0.4 micrometer offset for subtle depth
365-
const contactStrokeWidth = Math.max(1.2, 2.5 * (scale / 150));
366365

367-
// Helper to generate contact SVG element
366+
// Helper to generate one contact SVG element: flat gold (the electrode
367+
// convention) with a defined bronze outline, no gradient or shadow.
368+
const contactStrokeWidth = Math.max(1, Math.min(1.8, 2.5 * (scale / 150)));
368369
const generateContactSvg = (
369370
x: number,
370371
y: number,
371372
shape: string,
372-
params: ContactShapeParams,
373-
isShadow: boolean
373+
params: ContactShapeParams
374374
): string => {
375-
const fill = isShadow ? "rgba(30, 20, 5, 0.7)" : "rgba(212, 175, 55, 1.0)";
376-
const stroke = isShadow ? "none" : "rgba(80, 60, 15, 0.9)";
377-
const sw = isShadow ? 0 : contactStrokeWidth;
378-
375+
const common = `fill="rgb(212, 175, 55)" stroke="rgb(110, 80, 25)" stroke-opacity="0.9" stroke-width="${contactStrokeWidth}"`;
379376
switch (shape) {
380377
case "circle": {
381378
const radius = (params.radius ?? 5) * scale;
382-
return `<circle cx="${x}" cy="${y}" r="${radius}" fill="${fill}" stroke="${stroke}" stroke-width="${sw}"/>`;
379+
return `<circle cx="${x}" cy="${y}" r="${radius}" ${common}/>`;
383380
}
384381
case "square": {
385382
const side = (params.width ?? 10) * scale;
386-
return `<rect x="${x - side / 2}" y="${y - side / 2}" width="${side}" height="${side}" fill="${fill}" stroke="${stroke}" stroke-width="${sw}"/>`;
383+
const r = side * 0.12;
384+
return `<rect x="${x - side / 2}" y="${y - side / 2}" width="${side}" height="${side}" rx="${r}" ry="${r}" ${common}/>`;
387385
}
388386
case "rect": {
389387
const w = (params.width ?? 10) * scale;
390388
const h = (params.height ?? 15) * scale;
391-
return `<rect x="${x - w / 2}" y="${y - h / 2}" width="${w}" height="${h}" fill="${fill}" stroke="${stroke}" stroke-width="${sw}"/>`;
389+
const r = Math.min(w, h) * 0.12;
390+
return `<rect x="${x - w / 2}" y="${y - h / 2}" width="${w}" height="${h}" rx="${r}" ry="${r}" ${common}/>`;
392391
}
393392
default: {
394-
// Unknown shape: small circle
393+
// Unknown shape: a small plain gold dot.
395394
const markerSize = Math.max(3, Math.min(10, 7 * (scale / 100)));
396-
return `<circle cx="${x}" cy="${y}" r="${markerSize * 0.4}" fill="${fill}" stroke="${stroke}" stroke-width="${sw}"/>`;
395+
return `<circle cx="${x}" cy="${y}" r="${markerSize * 0.4}" ${common}/>`;
397396
}
398397
}
399398
};
@@ -404,31 +403,19 @@ function generateProbeSvgString(
404403
const size = Math.max((p.radius ?? 0) * 2, p.width ?? 0, p.height ?? 0);
405404
return Math.max(max, size);
406405
}, 10);
407-
const frameMargin = maxContactSizeUm * scale + shadowOffset;
406+
const frameMargin = maxContactSizeUm * scale + 4;
408407
const isContactInFrame = (x: number, y: number) =>
409408
x >= -frameMargin &&
410409
x <= widthPx + frameMargin &&
411410
y >= -frameMargin &&
412411
y <= heightPx + frameMargin;
413412

414-
// First pass: shadows
415-
contactPositions.forEach((position, index) => {
416-
const [x, y] = projectPoint(position);
417-
if (!isContactInFrame(x, y)) return;
418-
const shape = contactShapes[index] ?? "";
419-
const params = contactShapeParams[index] ?? {};
420-
elements.push(
421-
generateContactSvg(x + shadowOffset, y + shadowOffset, shape, params, true)
422-
);
423-
});
424-
425-
// Second pass: gold contacts
426413
contactPositions.forEach((position, index) => {
427414
const [x, y] = projectPoint(position);
428415
if (!isContactInFrame(x, y)) return;
429416
const shape = contactShapes[index] ?? "";
430417
const params = contactShapeParams[index] ?? {};
431-
elements.push(generateContactSvg(x, y, shape, params, false));
418+
elements.push(generateContactSvg(x, y, shape, params));
432419
});
433420

434421
// Scale bar (L-shaped, bottom-left corner)

0 commit comments

Comments
 (0)