I already wrote an image vectorizer function which converts B/W pixel data to what else if not into a GreasedLine
outline Unfortunatelly I can’t open source.
@Giles_Thompson
However ChatGPT is here to help:
<canvas id="canvas" width="200" height="200"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
// Example: Drawing a black and white image on the canvas
ctx.fillStyle = "white";
ctx.fillRect(0, 0, 200, 200); // White background
ctx.fillStyle = "black";
ctx.fillRect(50, 50, 100, 100); // Black square
// Step 1: Extract pixel data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data; // This is an array with RGBA values
// Step 2: Convert the pixel data to black-and-white threshold
function getBWPixel(x, y) {
const index = (y * canvas.width + x) * 4;
const r = pixels[index];
const g = pixels[index + 1];
const b = pixels[index + 2];
// Simple threshold for black and white
return (r + g + b) / 3 < 128 ? 1 : 0; // 1 for black, 0 for white
}
// Step 3: Contour detection (basic scanline algorithm)
function findContours() {
const paths = [];
const visited = Array(canvas.width * canvas.height).fill(false);
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
if (getBWPixel(x, y) === 1 && !visited[y * canvas.width + x]) {
const path = traceContour(x, y, visited);
if (path.length) {
paths.push(path);
}
}
}
}
return paths;
}
// Step 4: Tracing the contour using a simple "flood fill" approach
function traceContour(x, y, visited) {
const stack = [[x, y]];
const path = [];
const directions = [
[0, -1], // Up
[1, 0], // Right
[0, 1], // Down
[-1, 0] // Left
];
while (stack.length) {
const [cx, cy] = stack.pop();
if (cx < 0 || cy < 0 || cx >= canvas.width || cy >= canvas.height) continue;
const index = cy * canvas.width + cx;
if (!visited[index] && getBWPixel(cx, cy) === 1) {
visited[index] = true;
path.push([cx, cy]);
// Add neighboring pixels
for (const [dx, dy] of directions) {
stack.push([cx + dx, cy + dy]);
}
}
}
return path;
}
// Step 5: Convert contours to SVG
function convertToSVG(paths) {
const svgPaths = paths.map(path => {
const d = path.map((point, index) => {
const [x, y] = point;
return index === 0 ? `M${x},${y}` : `L${x},${y}`;
}).join(" ") + " Z"; // Close the path
return `<path d="${d}" fill="black" stroke="none"/>`;
}).join("\n");
return `
<svg width="${canvas.width}" height="${canvas.height}" xmlns="http://www.w3.org/2000/svg">
${svgPaths}
</svg>
`;
}
// Process the pixel data and generate the SVG
const contours = findContours();
const svgString = convertToSVG(contours);
console.log(svgString);
// You can insert the SVG into the DOM or download it
const svgBlob = new Blob([svgString], { type: 'image/svg+xml' });
const url = URL.createObjectURL(svgBlob);
const link = document.createElement('a');
link.href = url;
link.download = 'image.svg';
link.innerText = 'Download SVG';
document.body.appendChild(link);
</script>
Once you have the vectors you can use the PolygonMeshBuilder
to extrude the shape.
EDIT:
@Tricotou seems that we are suggesting the same approach but you were faster