Expand preview and center dominant sampling
This commit is contained in:
+5
-5
@@ -76,8 +76,8 @@ input[type="range"] { accent-color: var(--green); }
|
||||
.zoom-control button { width: 36px; height: 36px; border: 1px solid var(--line); background: white; cursor: pointer; font-size: 20px; }
|
||||
.zoom-control input { width: 95px; }
|
||||
.zoom-control output { width: 38px; color: var(--muted); font-size: 11px; text-align: right; }
|
||||
.canvas-viewport { position: relative; width: min(100%, 82vh, 1000px); aspect-ratio: 1 / 1; height: auto; min-height: 0; flex: 0 0 auto; align-self: center; contain: size layout; overflow: hidden; }
|
||||
.canvas-stage { position: absolute; inset: 0; width: auto; height: auto; overflow: scroll; display: block; padding: 36px; cursor: grab; touch-action: none; overscroll-behavior: contain; user-select: none; scrollbar-gutter: stable; }
|
||||
.canvas-viewport { position: relative; width: 100%; aspect-ratio: 1 / 1; height: auto; min-height: 0; flex: 0 0 auto; align-self: stretch; contain: size layout; overflow: hidden; }
|
||||
.canvas-stage { position: absolute; inset: 0; width: auto; height: auto; overflow: scroll; display: block; padding: 12px; cursor: grab; touch-action: none; overscroll-behavior: contain; user-select: none; scrollbar-gutter: stable; }
|
||||
.canvas-stage.is-dragging { cursor: grabbing; }
|
||||
.canvas-wrap { width: max-content; line-height: 0; position: relative; margin: auto; border: 1px solid var(--ink); box-shadow: 10px 10px 0 rgba(32,39,36,.13); background: white; }
|
||||
.canvas-wrap canvas { cursor: inherit; image-rendering: pixelated; }
|
||||
@@ -161,8 +161,8 @@ input[type="range"] { accent-color: var(--green); }
|
||||
.pattern-toolbar { align-items: flex-start; flex-direction: column; padding: 20px; }
|
||||
.zoom-control { width: 100%; }
|
||||
.zoom-control input { flex: 1; }
|
||||
.canvas-viewport { width: min(100%, 82vh); height: auto; min-height: 0; aspect-ratio: 1 / 1; flex-basis: auto; }
|
||||
.canvas-stage { display: block; padding: 24px; }
|
||||
.canvas-viewport { width: 100%; height: auto; min-height: 0; aspect-ratio: 1 / 1; flex-basis: auto; }
|
||||
.canvas-stage { display: block; padding: 10px; }
|
||||
.canvas-wrap { margin: auto; }
|
||||
.pattern-footer { align-items: flex-start; flex-wrap: wrap; padding: 14px 18px; }
|
||||
.view-options { width: 100%; margin: 5px 0 0; justify-content: space-between; }
|
||||
@@ -182,7 +182,7 @@ input[type="range"] { accent-color: var(--green); }
|
||||
@media (max-width: 370px) {
|
||||
.view-options { gap: 6px; }
|
||||
.view-options label { font-size: 10px; }
|
||||
.canvas-stage { padding: 16px; }
|
||||
.canvas-stage { padding: 8px; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) { * { scroll-behavior: auto !important; transition: none !important; } }
|
||||
|
||||
+8
-4
@@ -214,7 +214,9 @@ function sampleDominantRegions(
|
||||
cropX: number,
|
||||
cropY: number,
|
||||
) {
|
||||
const scale = 4;
|
||||
// An odd sampling grid has a real center point and avoids directional ties
|
||||
// at object boundaries (the former 4x4 grid favored one side on exact ties).
|
||||
const scale = 5;
|
||||
const sample = document.createElement("canvas");
|
||||
sample.width = width * scale;
|
||||
sample.height = height * scale;
|
||||
@@ -228,7 +230,7 @@ function sampleDominantRegions(
|
||||
|
||||
for (let row = 0; row < height; row++) {
|
||||
for (let column = 0; column < width; column++) {
|
||||
const groups: Array<{ count: number; lab: Oklab; samples: Array<{ rgb: [number, number, number]; lab: Oklab }> }> = [];
|
||||
const groups: Array<{ count: number; lab: Oklab; samples: Array<{ rgb: [number, number, number]; lab: Oklab }>; containsCenter: boolean }> = [];
|
||||
let transparentCount = 0;
|
||||
for (let offsetY = 0; offsetY < scale; offsetY++) {
|
||||
for (let offsetX = 0; offsetX < scale; offsetX++) {
|
||||
@@ -239,6 +241,7 @@ function sampleDominantRegions(
|
||||
}
|
||||
const rgb: [number, number, number] = [source[sourceIndex], source[sourceIndex + 1], source[sourceIndex + 2]];
|
||||
const lab = rgbToOklab(rgb);
|
||||
const isCenter = offsetX === Math.floor(scale / 2) && offsetY === Math.floor(scale / 2);
|
||||
let closest: (typeof groups)[number] | undefined;
|
||||
let closestDistance = Number.POSITIVE_INFINITY;
|
||||
for (const group of groups) {
|
||||
@@ -253,8 +256,9 @@ function sampleDominantRegions(
|
||||
closest.lab = closest.lab.map((value, channel) => (value * closest.count + lab[channel]) / total) as Oklab;
|
||||
closest.count = total;
|
||||
closest.samples.push({ rgb, lab });
|
||||
closest.containsCenter ||= isCenter;
|
||||
} else {
|
||||
groups.push({ count: 1, lab: [...lab], samples: [{ rgb, lab }] });
|
||||
groups.push({ count: 1, lab: [...lab], samples: [{ rgb, lab }], containsCenter: isCenter });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -264,7 +268,7 @@ function sampleDominantRegions(
|
||||
confidence[row * width + column] = transparentCount / (scale * scale);
|
||||
continue;
|
||||
}
|
||||
const dominant = groups.sort((a, b) => b.count - a.count)[0];
|
||||
const dominant = groups.sort((a, b) => b.count - a.count || Number(b.containsCenter) - Number(a.containsCenter))[0];
|
||||
const representative = dominant.samples.reduce((best, current) =>
|
||||
oklabDistance(current.lab, dominant.lab) < oklabDistance(best.lab, dominant.lab) ? current : best,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user