Skip to content

Commit 08bbe7a

Browse files
committed
Get outline.
1 parent e42b73d commit 08bbe7a

File tree

2 files changed

+66
-139
lines changed

2 files changed

+66
-139
lines changed

src/pg/inputPixelEditor/inputPixelEditor.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,14 @@ export default class PgInputPixelEditor extends HTMLElement {
228228
if (y >= data[0].length) {
229229
for (let l = 0; l < layerCount; l++) {
230230
data[l].push(new Array(this.width).fill(0));
231+
this.#export.push(new Array(this.width).fill(0));
231232
}
232233
}
233234
for (let x = 0; x < this.width; x++) {
234235
if (x >= data[0][y].length) {
235236
for (let l = 0; l < layerCount; l++) {
236237
data[l][y].push(0);
238+
this.#export[y].push(0);
237239
}
238240
}
239241
for (let l = 0; l < layerCount; l++) {
@@ -305,6 +307,7 @@ export default class PgInputPixelEditor extends HTMLElement {
305307
);
306308
// Verify this is the only place setting pixel data!
307309
this.#data[this.#layer][y][x] = color;
310+
console.log(this.#data);
308311
this.#updateExport(x, y);
309312
this.#delayedChange();
310313
}
@@ -1086,7 +1089,7 @@ export default class PgInputPixelEditor extends HTMLElement {
10861089
* Outline.
10871090
*/
10881091
outline() {
1089-
const pixels = getOutline(this.#data[this.#layer]);
1092+
const pixels = getOutline(this.#data[this.#layer], true);
10901093
pixels.forEach(([x, y]) => {
10911094
this.#setPixel(x, y, this.#color);
10921095
});
Lines changed: 62 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +1,87 @@
1-
/*export function getOutline(grid) {
2-
const outline: number[][] = [];
3-
const rows = grid.length;
4-
const cols = grid[0].length;
5-
6-
// Directions: [dx, dy]
7-
const dirs = [
8-
[0, -1], // up
9-
[1, 0], // right
10-
[0, 1], // down
11-
[-1, 0] // left
1+
export function getOutline(pixels: number[][], ignoreInside: boolean = false) {
2+
const moatPixels: number[][] = [];
3+
const height = pixels.length;
4+
if (height === 0) return moatPixels;
5+
const width = pixels[0].length;
6+
7+
const directions = [
8+
[0, 1],
9+
[0, -1],
10+
[1, 0],
11+
[-1, 0]
1212
];
1313

14-
for (let y = 0; y < rows; y++) {
15-
for (let x = 0; x < cols; x++) {
16-
if (grid[y][x] !== 1) continue;
14+
// ---------------------------------------------------------
15+
// STEP 1: If ignoring inside, flood-fill from the outside
16+
// ---------------------------------------------------------
17+
let outsideZero: boolean[][] = Array.from({ length: height }, () =>
18+
Array(width).fill(false)
19+
);
1720

18-
// Check if this cell touches the boundary of the shape
19-
let isBoundary = false;
21+
if (ignoreInside) {
22+
const queue: [number, number][] = [];
2023

21-
for (const [dx, dy] of dirs) {
22-
const nx = x + dx;
23-
const ny = y + dy;
24-
25-
// If neighbor is out of bounds OR is zero, this is an outline cell
26-
if (ny < 0 || ny >= rows || nx < 0 || nx >= cols || grid[ny][nx] === 0) {
27-
isBoundary = true;
28-
break;
29-
}
30-
}
31-
32-
if (isBoundary) {
33-
outline.push([x, y]);
34-
}
24+
// Seed flood-fill with all border zeros
25+
for (let x = 0; x < width; x++) {
26+
if (pixels[0][x] === 0) queue.push([x, 0]);
27+
if (pixels[height - 1][x] === 0) queue.push([x, height - 1]);
3528
}
36-
}
37-
38-
return outline;
39-
} */
40-
41-
export function getOutline(grid) {
42-
const rows = grid.length;
43-
const cols = grid[0].length;
44-
45-
// ------------------------------------------------------------
46-
// 1. Flood-fill background reachable from outside
47-
// ------------------------------------------------------------
48-
const outside = Array.from({ length: rows }, () => Array(cols).fill(false));
49-
const queue: number[][] = [];
50-
51-
// Add border 0-cells
52-
for (let x = 0; x < cols; x++) {
53-
if (grid[0][x] === 0) queue.push([x, 0]);
54-
if (grid[rows - 1][x] === 0) queue.push([x, rows - 1]);
55-
}
56-
for (let y = 0; y < rows; y++) {
57-
if (grid[y][0] === 0) queue.push([0, y]);
58-
if (grid[y][cols - 1] === 0) queue.push([cols - 1, y]);
59-
}
60-
61-
// BFS flood fill
62-
while (queue.length) {
63-
const [x, y] = queue.shift() as number[];
64-
if (outside[y][x]) continue;
65-
outside[y][x] = true;
66-
67-
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
68-
for (const [dx, dy] of dirs) {
69-
const nx = x + dx, ny = y + dy;
70-
if (
71-
nx >= 0 && nx < cols &&
72-
ny >= 0 && ny < rows &&
73-
grid[ny][nx] === 0 &&
74-
!outside[ny][nx]
75-
) {
76-
queue.push([nx, ny]);
77-
}
29+
for (let y = 0; y < height; y++) {
30+
if (pixels[y][0] === 0) queue.push([0, y]);
31+
if (pixels[y][width - 1] === 0) queue.push([width - 1, y]);
7832
}
79-
}
80-
81-
// ------------------------------------------------------------
82-
// 2. Collect boundary cells (unordered)
83-
// ------------------------------------------------------------
84-
const boundary = new Set();
85-
const key = (x, y) => `${x},${y}`;
8633

87-
for (let y = 0; y < rows; y++) {
88-
for (let x = 0; x < cols; x++) {
89-
if (grid[y][x] === 0) continue;
90-
91-
const dirs = [[1,0],[-1,0],[0,1],[0,-1]];
92-
for (const [dx, dy] of dirs) {
93-
const nx = x + dx, ny = y + dy;
34+
// BFS flood-fill
35+
while (queue.length > 0) {
36+
const [x, y] = queue.shift()!;
37+
if (outsideZero[y][x]) continue;
38+
outsideZero[y][x] = true;
9439

40+
for (const [dx, dy] of directions) {
41+
const nx = x + dx;
42+
const ny = y + dy;
9543
if (
96-
nx < 0 || nx >= cols ||
97-
ny < 0 || ny >= rows ||
98-
(grid[ny][nx] !== 1 && outside[ny][nx])
44+
nx >= 0 &&
45+
nx < width &&
46+
ny >= 0 &&
47+
ny < height &&
48+
pixels[ny][nx] === 0 &&
49+
!outsideZero[ny][nx]
9950
) {
100-
boundary.add(key(x, y));
101-
break;
51+
queue.push([nx, ny]);
10252
}
10353
}
10454
}
10555
}
10656

107-
if (boundary.size === 0) return [];
108-
109-
// ------------------------------------------------------------
110-
// 3. SAFE OUTLINE WALK (no infinite loops)
111-
// ------------------------------------------------------------
112-
113-
// Convert boundary set to a fast lookup
114-
const isBoundary = (x, y) => boundary.has(key(x, y));
115-
116-
// Find a starting boundary cell
117-
const [startKey] = boundary;
118-
// @ts-ignore
119-
const [sx, sy] = startKey.split(',').map(Number);
57+
// ---------------------------------------------------------
58+
// STEP 2: Collect moat pixels
59+
// ---------------------------------------------------------
60+
const moatSet = new Set<string>();
12061

121-
const outline: number[][] = [];
122-
const visited = new Set();
123-
let current = { x: sx, y: sy };
62+
for (let y = 0; y < height; y++) {
63+
for (let x = 0; x < width; x++) {
64+
if (pixels[y][x] !== 1) continue;
12465

125-
// Moore-neighborhood directions (8 directions)
126-
const dirs8 = [
127-
[1,0], [1,1], [0,1], [-1,1],
128-
[-1,0], [-1,-1], [0,-1], [1,-1]
129-
];
66+
for (const [dx, dy] of directions) {
67+
const nx = x + dx;
68+
const ny = y + dy;
13069

131-
const maxSteps = rows * cols * 8;
70+
if (nx < 0 || nx >= width || ny < 0 || ny >= height) continue;
13271

133-
for (let step = 0; step < maxSteps; step++) {
134-
outline.push([current.x, current.y]);
135-
visited.add(key(current.x, current.y));
72+
if (pixels[ny][nx] === 0) {
73+
// If ignoring inside, only count zeros reachable from outside
74+
if (ignoreInside && !outsideZero[ny][nx]) continue;
13675

137-
// Try to find next boundary neighbor
138-
let next = null;
139-
for (const [dx, dy] of dirs8) {
140-
const nx = current.x + dx;
141-
const ny = current.y + dy;
142-
if (isBoundary(nx, ny) && !visited.has(key(nx, ny))) {
143-
// @ts-ignore
144-
next = { x: nx, y: ny };
145-
break;
76+
const key = `${nx},${ny}`;
77+
if (!moatSet.has(key)) {
78+
moatSet.add(key);
79+
moatPixels.push([nx, ny]);
80+
}
81+
}
14682
}
14783
}
148-
149-
// If no next step → open contour (C-shape)
150-
if (!next) break;
151-
152-
// If next is start → closed loop
153-
// @ts-ignore
154-
if (next.x === sx && next.y === sy) {
155-
outline.push([sx, sy]);
156-
break;
157-
}
158-
159-
current = next;
16084
}
16185

162-
return outline;
86+
return moatPixels;
16387
}

0 commit comments

Comments
 (0)