Created
February 13, 2023 10:24
-
-
Save skytomo221/6ba8fca6fea1f680c2c9ed187fc1392f to your computer and use it in GitHub Desktop.
LED表示機風SVGジェネレーター
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*! | |
* "led-type-destination-sign-svg-generator.ts" by skytomo in 2022 | |
* | |
* This program is released under the CC0 1.0 Universal. | |
* See https://creativecommons.org/publicdomain/zero/1.0/legalcode | |
*/ | |
import * as fs from "node:fs/promises"; | |
import * as path from "node:path"; | |
import { Canvas, Image, ImageData } from "canvas"; | |
const sum = <T>( | |
arr: T[], | |
fn?: (value: T, index: number, array: T[]) => number | |
): number => | |
fn | |
? sum(arr.map(fn)) | |
: (arr as number[]).reduce((prev, current) => prev + current); | |
const average = <T>( | |
arr: T[], | |
fn?: (value: T, index: number, array: T[]) => number | |
) => sum(arr, fn) / arr.length; | |
const range = (start: number, stop: number, step: number) => | |
Array.from({ length: (stop - start) / step + 1 }, (_, i) => start + i * step); | |
type Color = { | |
red: number; | |
green: number; | |
blue: number; | |
alpha: number; | |
}; | |
class LedTypeDestinationSignSvgGenerator { | |
imagedata: ImageData; | |
width: number; | |
height: number; | |
constructor(imagedata: ImageData, width: number, height: number) { | |
this.imagedata = imagedata; | |
this.width = width; | |
this.height = height; | |
} | |
static range = (start: number, stop: number, step: number) => | |
Array.from( | |
{ length: (stop - start) / step + 1 }, | |
(_, i) => start + i * step | |
); | |
color(index: number): Color { | |
return { | |
red: this.imagedata.data[index], | |
green: this.imagedata.data[index + 1], | |
blue: this.imagedata.data[index + 2], | |
alpha: this.imagedata.data[index + 3], | |
}; | |
} | |
dx() { | |
return Math.round(this.imagedata.width / this.width); | |
} | |
dy() { | |
return Math.round(this.imagedata.height / this.height); | |
} | |
average_color(x: number, y: number) { | |
const index = (x: number, y: number) => (y * this.imagedata.width + x) * 4; | |
const colors = range(x, x + this.dx(), 1) | |
.map((x) => | |
range(y, y + this.dy(), 1).map((y) => this.color(index(x, y))) | |
) | |
.flat(); | |
return { | |
red: Math.round(average(colors, ({ red }) => red)), | |
green: Math.round(average(colors, ({ green }) => green)), | |
blue: Math.round(average(colors, ({ blue }) => blue)), | |
alpha: Math.round(average(colors, ({ alpha }) => alpha)), | |
}; | |
} | |
generate() { | |
return range(0, this.imagedata.height, this.dy()) | |
.map((y) => | |
range(0, this.imagedata.width, this.dx()).map((x) => | |
this.average_color(x, y) | |
) | |
) | |
.filter((y) => | |
y.every( | |
(x) => | |
!isNaN(x.red) && | |
!isNaN(x.blue) && | |
!isNaN(x.green) && | |
!isNaN(x.alpha) | |
) | |
); | |
} | |
to_svg() { | |
const circles = this.generate() | |
.map((line, y) => { | |
return line | |
.map( | |
(pixel, x) => | |
`<circle cx="${x}" cy="${y}" r="0.5" fill="rgba(${pixel.red}, ${ | |
pixel.green | |
}, ${pixel.blue}, ${pixel.alpha / 255})"></circle>` | |
) | |
.join(""); | |
}) | |
.join(""); | |
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${this.width} ${this.height}" height="">${circles}</svg>`; | |
} | |
} | |
fs.readFile(path.join(__dirname, "..", "images", "sakura-miko2.png")) | |
.then((data: Buffer) => { | |
const img = new Image(); | |
img.src = data; | |
const canvas = new Canvas(img.width, img.height); | |
const ctx = canvas.getContext("2d"); | |
ctx.drawImage(img, 0, 0, img.width, img.height); | |
const imagedata = ctx.getImageData(0, 0, img.width, img.height); | |
const generator = new LedTypeDestinationSignSvgGenerator(imagedata, 40, 40); | |
console.log(generator.to_svg()); | |
}) | |
.catch((reason: any) => { | |
console.error(reason); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment