-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added jest and chroma; implmenented sorting by hsv for p.1.2.2 with s…
…ome tests
- Loading branch information
Donnie Flood
authored and
Donnie Flood
committed
Nov 2, 2019
1 parent
a78a544
commit 747def6
Showing
8 changed files
with
251 additions
and
2 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
module.exports = { | ||
"roots": [ | ||
"<rootDir>/src" | ||
], | ||
"transform": { | ||
"^.+\\.tsx?$": "ts-jest" | ||
}, | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
"author": "[email protected]", | ||
"license": "ISC", | ||
"devDependencies": { | ||
"@types/chroma-js": "^1.4.3", | ||
"@types/jest": "^24.0.20", | ||
"@types/node": "^12.11.7", | ||
"@types/p5": "^0.9.0", | ||
|
@@ -31,6 +32,7 @@ | |
} | ||
}, | ||
"dependencies": { | ||
"chroma-js": "^2.0.6", | ||
"p5": "^0.10.2" | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
<html> | ||
|
||
<body> | ||
<script src="./p.1.2.1.ts"></script> | ||
<script src="./p.1.2.2.ts"></script> | ||
</body> | ||
|
||
</html> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import p5, { COLOR_MODE } from "p5"; | ||
import { sortColors, HsbgSortMode } from "../../../utils/color"; | ||
var image = require("./subway.jpg"); | ||
|
||
|
||
|
||
// Generative Design P.1.2.2 | ||
function sketch(p: p5) { | ||
|
||
let img: p5.Image; | ||
let sortMode: HsbgSortMode | undefined; | ||
|
||
|
||
p.setup = () => { | ||
p.createCanvas(420, 420); | ||
img = p.loadImage(image) | ||
}; | ||
|
||
p.draw = () => { | ||
// calculate the tiles and rect size based on mouse x position | ||
let tileCount = Math.floor(p.width / p.max(p.mouseX, 5)); | ||
let rectSize = Math.floor(p.width / tileCount); | ||
|
||
let i = 0; | ||
// sample a pixel for tiles | ||
let colors = new Array<string>(tileCount * tileCount); | ||
for (let gridY = 0; gridY < tileCount; gridY++) { | ||
for (let gridX = 0; gridX < tileCount; gridX++) { | ||
const px = gridX * rectSize; | ||
const py = gridY * rectSize; | ||
// get a single pixel from the image | ||
const pixel = img.get(px, py); | ||
colors[i] = p.color(pixel).toString("#rgb"); | ||
i++; | ||
} | ||
} | ||
// sort pixels | ||
colors = sortColors(colors, sortMode); | ||
|
||
// draw sorted pixel rectangles | ||
i = 0; | ||
for (let gridY = 0; gridY < tileCount; gridY++) { | ||
for (let gridX = 0; gridX < tileCount; gridX++) { | ||
p.fill(p.color(colors[i])); | ||
p.rect(gridX * rectSize, gridY * rectSize, rectSize, rectSize); | ||
i++; | ||
} | ||
} | ||
// show the sortMode | ||
p.fill('black'); | ||
p.text(`sortMode: ${sortMode || 'none'}`, 0, 0, 100, 100); | ||
|
||
}; | ||
|
||
// switch between color modes | ||
p.keyReleased = () => { | ||
switch (p.key) { | ||
case "4": | ||
sortMode = undefined; | ||
break; | ||
case "5": | ||
sortMode = "hue" | ||
break; | ||
case "6": | ||
sortMode = "saturation" | ||
break; | ||
case "7": | ||
sortMode = "brightness" | ||
break; | ||
case "8": | ||
sortMode = "grayscale" | ||
break; | ||
} | ||
}; | ||
} | ||
|
||
new p5(sketch); |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { colorToGrayscale, sortColors } from "./color"; | ||
import chroma from "chroma-js"; | ||
import p5 from "p5"; | ||
|
||
describe("colorToGrayscale", () => { | ||
|
||
it('calculates gray from red', () => { | ||
const grayFromRed = colorToGrayscale("#ff0000"); | ||
expect(grayFromRed).toEqual("#363636"); | ||
}); | ||
|
||
it('calculates gray from green', () => { | ||
const grayFromGreen = colorToGrayscale("#00ff00"); | ||
expect(grayFromGreen).toEqual("#b6b6b6"); | ||
}); | ||
|
||
it('calculates gray from blue', () => { | ||
const grayFromBlue = colorToGrayscale("#0000ff"); | ||
expect(grayFromBlue).toEqual("#121212"); | ||
}); | ||
|
||
it('calculates white from white', () => { | ||
const white = colorToGrayscale("#ffffff"); | ||
expect(white).toEqual("#ffffff") | ||
}) | ||
|
||
it('calculates black from black', () => { | ||
const black = colorToGrayscale("#000000"); | ||
expect(black).toEqual("#000000") | ||
}) | ||
|
||
}) | ||
|
||
describe("color sorting", () => { | ||
const white_to_black = chroma.scale(['white', 'black']).colors(12); | ||
|
||
it("ignores sorting for color mode undefined", () => { | ||
expect(sortColors(white_to_black)).toEqual(white_to_black) | ||
}) | ||
|
||
it("sorts colors from black to white for grayscale", () => { | ||
const black_to_white = chroma.scale(['black', 'white']).colors(12); | ||
expect(sortColors(white_to_black, "grayscale")).toEqual(black_to_white) | ||
}) | ||
|
||
it("sorts by hue", () => { | ||
const mixed_hue = [ | ||
chroma.hsv(200, 0, 0.1).hex(), | ||
chroma.hsv(300, 0, 0.1).hex(), | ||
chroma.hsv(100, 0, 0.1).hex(), | ||
] | ||
const sorted_hue = [ | ||
chroma.hsv(300, 0, 0.1).hex(), | ||
chroma.hsv(200, 0, 0.1).hex(), | ||
chroma.hsv(100, 0, 0.1).hex(), | ||
] | ||
// console.log("mixed_hue", mixed_hue, mixed_hue.map((value) => chroma(value).get('hsv.h'))) | ||
// console.log("sorted_hue", sorted_hue, sorted_hue.map((value) => chroma(value).get('hsv.h'))) | ||
expect(sortColors(mixed_hue, "hue")).toEqual(sorted_hue) | ||
}) | ||
|
||
it("sorts by saturation", () => { | ||
const mixed_sat = [ | ||
chroma.hsv(300, 0.6, 0.1).hex(), | ||
chroma.hsv(200, 1, 0.1).hex(), | ||
chroma.hsv(100, 0.2, 0.1).hex(), | ||
] | ||
const sorted_sat = [ | ||
chroma.hsv(100, 0.2, 0.1).hex(), | ||
chroma.hsv(300, 0.6, 0.1).hex(), | ||
chroma.hsv(200, 1, 0.1).hex(), | ||
] | ||
// console.log("mixed_sat", mixed_sat, mixed_sat.map((value) => chroma(value).get('hsv.s'))) | ||
// console.log("sorted_sat", sorted_sat, sorted_sat.map((value) => chroma(value).get('hsv.s'))) | ||
expect(sortColors(mixed_sat, "saturation")).toEqual(sorted_sat) | ||
}) | ||
|
||
it("sorts by brightness", () => { | ||
const mixed_bright = [ | ||
chroma.hsv(180, 1, 0.2).hex(), | ||
chroma.hsv(180, 1, 0.3).hex(), | ||
chroma.hsv(180, 1, 0.1).hex(), | ||
] | ||
const sorted_bright = [ | ||
chroma.hsv(180, 1, 0.1).hex(), | ||
chroma.hsv(180, 1, 0.2).hex(), | ||
chroma.hsv(180, 1, 0.3).hex(), | ||
] | ||
// console.log("mixed_bright", mixed_bright, mixed_bright.map((value) => chroma(value).get('hsv.v'))) | ||
// console.log("sorted_bright", sorted_bright, sorted_bright.map((value) => chroma(value).get('hsv.v'))) | ||
expect(sortColors(mixed_bright, "brightness")).toEqual(sorted_bright) | ||
}) | ||
|
||
|
||
}) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import chroma from "chroma-js"; | ||
|
||
export type HsbgSortMode = "hue" | "saturation" | "brightness" | "grayscale"; | ||
|
||
export function sortColors(colors: string[], sortMode?: HsbgSortMode) { | ||
if (!sortMode) { | ||
return colors; | ||
} | ||
return colors.sort((a: string, b: string) => { | ||
// sort grayscale | ||
if (sortMode === "grayscale") { | ||
const agray = colorToGrayscale(a); | ||
const bgray = colorToGrayscale(b); | ||
if (agray > bgray) { | ||
return 1; | ||
} else if (agray < bgray) { | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
// sort hsb | ||
let channel: string | undefined; | ||
switch (sortMode) { | ||
case "hue": | ||
channel = "hsv.h"; | ||
break; | ||
case "saturation": | ||
channel = "hsv.s"; | ||
break; | ||
case "brightness": | ||
channel = "hsv.v"; | ||
break; | ||
default: | ||
// shouldn't happen but just in case | ||
return 0; | ||
} | ||
|
||
const ac = chroma(a).get(channel); | ||
const bc = chroma(b).get(channel); | ||
if (ac > bc) { | ||
return 1; | ||
} else if (ac < bc) { | ||
return -1; | ||
} | ||
return 0; | ||
}) | ||
} | ||
|
||
export function colorToGrayscale(rgbaHex: string): string { | ||
// simply calculate C Linear (don't worry about nonlinear gamma correction) | ||
// https://stackoverflow.com/questions/17615963/standard-rgb-to-grayscale-conversion | ||
const [r, g, b] = chroma(rgbaHex).gl(); | ||
const clinear = 0.2126 * r + 0.7152 * g + 0.0722 * b; | ||
return chroma([clinear, clinear, clinear], "gl").hex() | ||
} |