Skip to content

Commit

Permalink
added jest and chroma; implmenented sorting by hsv for p.1.2.2 with s…
Browse files Browse the repository at this point in the history
…ome tests
  • Loading branch information
Donnie Flood authored and Donnie Flood committed Nov 2, 2019
1 parent a78a544 commit 747def6
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 2 deletions.
8 changes: 8 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
"roots": [
"<rootDir>/src"
],
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
}
11 changes: 11 additions & 0 deletions package-lock.json

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

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -31,6 +32,7 @@
}
},
"dependencies": {
"chroma-js": "^2.0.6",
"p5": "^0.10.2"
}
}
}
2 changes: 1 addition & 1 deletion src/sketches/learn/gd-book/ch-1/index.html
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>
77 changes: 77 additions & 0 deletions src/sketches/learn/gd-book/ch-1/p.1.2.2.ts
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);
Binary file added src/sketches/learn/gd-book/ch-1/subway.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions src/sketches/utils/color.test.ts
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)
})


})
56 changes: 56 additions & 0 deletions src/sketches/utils/color.ts
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()
}

0 comments on commit 747def6

Please sign in to comment.