Skip to content

Commit

Permalink
Merge pull request #7 from pshihn/dev
Browse files Browse the repository at this point in the history
added color diff matcher
  • Loading branch information
pshihn authored Feb 7, 2020
2 parents 58c4530 + 309c871 commit ab56ae3
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 40 deletions.
54 changes: 27 additions & 27 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"name": "legra",
"version": "0.2.0",
"version": "0.3.0",
"description": "Create graphics using Lego like brick shapes.",
"main": "lib/legra.umd.js",
"module": "lib/legra.js",
"browser": "lib/legra.iife.js",
"types": "bin/legra.d.ts",
"scripts": {
"build": "rm -rf bin && tsc && rollup -c",
"lint": "tslint -p tsconfig.json",
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
Expand All @@ -28,9 +29,9 @@
},
"homepage": "https://legrajs.com",
"devDependencies": {
"rollup": "^1.27.0",
"rollup-plugin-terser": "^5.1.2",
"rollup": "^1.31.0",
"rollup-plugin-terser": "^5.2.0",
"tslint": "^5.20.1",
"typescript": "^3.7.2"
"typescript": "^3.7.5"
}
}
80 changes: 80 additions & 0 deletions src/color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
export interface Color {
r: number;
g: number;
b: number;
}

export interface LabColor {
l: number;
a: number;
b: number;
}

const labMap = new Map<string, LabColor>();

function toLab(c: Color): LabColor {
const rgb = `${c.r}, ${c.g}, ${c.b}`;
if (labMap.has(rgb)) {
return labMap.get(rgb)!;
}
let [r, g, b] = [c.r / 255, c.g / 255, c.b / 255];
r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92;
let x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
let y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1.00000;
let z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
x = (x > 0.008856) ? Math.pow(x, 1 / 3) : (7.787 * x) + 16 / 116;
y = (y > 0.008856) ? Math.pow(y, 1 / 3) : (7.787 * y) + 16 / 116;
z = (z > 0.008856) ? Math.pow(z, 1 / 3) : (7.787 * z) + 16 / 116;
const lab: LabColor = {
l: (116 * y) - 16,
a: 500 * (x - y),
b: 200 * (y - z)
};
labMap.set(rgb, lab);
return lab;
}

function colorDifference(a: Color, b: Color) {
const labA = toLab(a);
const labB = toLab(b);
return Math.sqrt(
Math.pow(labB.l - labA.l, 2) +
Math.pow(labB.a - labA.a, 2) +
Math.pow(labB.b - labA.b, 2)
);
}

// function colorDifference2(a: Color, b: Color) {
// const rbar = (a.r + b.r) / 2;
// return Math.sqrt(
// ((2 + (rbar / 256)) * Math.pow(b.r - a.r, 2)) +
// (4 * Math.pow(b.g - a.g, 2)) +
// ((2 + ((255 - rbar) / 256)) * Math.pow(b.b - a.b, 2))
// );
// }

interface ColorDiffItem {
diff: number;
color: Color;
}

export function closestColor(color: Color, palette: Color[]): Color {
if (palette.length) {
const diffItems = palette.map<ColorDiffItem>((p) => {
const diff = colorDifference(color, p);
return {
diff,
color: p
};
});
diffItems.sort((a, b) => {
return a.diff - b.diff;
});
console.log(color, diffItems[0]);
return diffItems[0].color;
} else {
return color;
}
}
2 changes: 1 addition & 1 deletion src/geometry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function angle(o: Point, v1: Point, v2: Point): number {
const dx1 = v1[0] - o[0];
const dy1 = v1[1] - o[1];
const dx2 = v2[0] - o[0];
const dy2 = v2[1] - o[1]
const dy2 = v2[1] - o[1];
const cross = dx1 * dy2 - dy1 * dx2;
const dot = dx1 * dx2 + dy1 * dy2;
return Math.atan2(cross, dot);
Expand Down
27 changes: 22 additions & 5 deletions src/legra-core.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Point, Rectangle, EdgeEntry, ActiveEdgeEntry } from './geometry.js';
import { Bezier } from './bezier.js';
import { Color, closestColor } from './color';

export interface ImageOrImageBitmap {
width: number;
Expand All @@ -9,12 +10,14 @@ export interface ImageOrImageBitmap {
export interface BrickRenderOptions {
color?: string;
filled?: boolean;
palette?: Color[];
}

export interface BrickRenderOptionsResolved extends BrickRenderOptions {
brickSize: number;
color: string;
filled: boolean;
palette: Color[];
}

const radiusCache = new Map<number, number>();
Expand Down Expand Up @@ -475,15 +478,29 @@ export function drawImage(ctx: CanvasRenderingContext2D, style: BrickRenderOptio
const refCtx = refCanvas.getContext('2d')!;
refCtx.drawImage(image as any, src[0], src[1], srcSize[0], srcSize[1], 0, 0, dstSize[0], dstSize[1]);
const imageData = refCtx.getImageData(0, 0, refW, refH);
const colorMap = new Map<string, Color>();
for (let j = 0; j < refH; j++) {
for (let i = 0; i < refW; i++) {
const r = imageData.data[(j * refW * 4) + (i * 4)];
const g = imageData.data[(j * refW * 4) + (i * 4) + 1];
const b = imageData.data[(j * refW * 4) + (i * 4) + 2];
let color: Color = {
r: imageData.data[(j * refW * 4) + (i * 4)],
g: imageData.data[(j * refW * 4) + (i * 4) + 1],
b: imageData.data[(j * refW * 4) + (i * 4) + 2]
};
if (style.palette && style.palette.length) {
const ckey = `${color.r},${color.g},${color.b}`;
if (colorMap.has(ckey)) {
color = colorMap.get(ckey)!;
} else {
const matchingColor = closestColor(color, style.palette);
colorMap.set(ckey, matchingColor);
color = matchingColor;
}
}
const pixelStyle: BrickRenderOptionsResolved = {
brickSize,
color: `rgb(${r}, ${g}, ${b})`,
filled: false
color: `rgb(${color.r}, ${color.g}, ${color.b})`,
filled: false,
palette: []
};
drawBrick(dst[0] + i, dst[1] + j, ctx, pixelStyle);
}
Expand Down
10 changes: 7 additions & 3 deletions src/legra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export default class Legra {
private defaultOptions: BrickRenderOptionsResolved = {
brickSize: 24,
color: '#2196F3',
filled: false
filled: false,
palette: []
};

constructor(ctx: CanvasRenderingContext2D, brickSize = 24, options?: BrickRenderOptions) {
Expand All @@ -21,6 +22,9 @@ export default class Legra {
if (typeof options.filled === 'boolean') {
this.defaultOptions.filled = options.filled;
}
if (options.palette) {
this.defaultOptions.palette = options.palette;
}
}
}

Expand Down Expand Up @@ -67,7 +71,7 @@ export default class Legra {
quadraticCurve(x1, y1, cpx, cpy, x2, y2, this.ctx, this.opt(options));
}

drawImage(image: ImageOrImageBitmap, dst: Point, dstSize?: Point, src?: Point, srcSize?: Point) {
drawImage(this.ctx, this.opt(), image, dst, dstSize, src, srcSize);
drawImage(image: ImageOrImageBitmap, dst: Point, dstSize?: Point, src?: Point, srcSize?: Point, options?: BrickRenderOptions) {
drawImage(this.ctx, this.opt(options), image, dst, dstSize, src, srcSize);
}
}

0 comments on commit ab56ae3

Please sign in to comment.