Friday 31 May 2019

rgba2rgb

I had a lovely little challenge a couple of days ago which, in conjunction with my invite to the First Alpha Testing of Empire of Code, got me excited. It led me to generate the following chunk of code:

const rgba2rgb = rgba => "#" + rgba.match(/.{1,2}/g)
                             .map((c, i, a) => (i !== a.length -1) 
                                 ? parseInt(c, 16) 
                                 : parseInt(c, 16) / 255)
                             .map((c, i, a) => (i !== a.length -1) 
                                 ? ~~((1 - a[3]) * 255 + a[3] * c) 
                                 : null)
                             .reverse()
                             .slice(1)
                             .reverse()
                             .map(c => c.toString(16))
                             .join("");

We needed to convert an RGBA colour to RGB, with the assumption that the original colour was atop a white background. The maths and colour theory are a little confusing, and I went through any number of iterations. The result made me feel a bit smug though as it is teeny - I'm guessing it could be improved though.

Have at it!

EDIT

So I clocked that the above wasn't quite as brilliant as I thought so I've updated it after simplifying it:

const rgba2rgb = function(rgba){ 
 const coloursAsBase16 = rgba.match(/.{1,2}/g);
    console.log("coloursAsBase16", coloursAsBase16);
    const coloursAsBase10 = coloursAsBase16.map(function(c, i, a){ 
     if(i !== a.length - 1){
         return parseInt(c, 16) // Value between 1 - 255
        }else{
         return parseInt(c, 16) / 255 // Value between 0 - 1
        }
    });
    console.log("coloursAsBase10", coloursAsBase10);
    const coloursWithAlpha = coloursAsBase10.reduce(function(t, c, i, a){
     if(i !== a.length - 1){
         return t.concat([parseInt(((1 - a[3]) * 255 + a[3] * c), 10)]);
        }else{
         return t;
        }
    }, [])
    console.log("coloursWithAlpha", coloursWithAlpha);
    const coloursChangedToBase16 = coloursWithAlpha.map(function(c){
     return c.toString(16)
    });
    console.log("coloursChangedToBase16", coloursChangedToBase16);
 return "#" + coloursChangedToBase16.join("");
}

Which led to this one-liner:

const rgba2rgb = rgba => "#" + rgba.match(/.{1,2}/g)
    .map((c, i, a) => (i !== a.length - 1) 
        ? parseInt(c, 16) 
        : parseInt(c, 16) / 255)
    .reduce((t, c, i, a) => (i !== a.length - 1) 
        ? t.concat([parseInt(((1 - a[3]) * 255 + a[3] * c), 10)]) 
        : t, [])
    .map(c => c.toString(16))
    .join("");

Wednesday 8 May 2019

lodash _.sortBy

I needed to order an array of objects in an arbitrary fashion, using strings which make sense to the business but aren't necessarily orderable otherwise. Unfortunately I had to support IE11, so couldn't do fancy JS. Thankfully I had access to lodash. Anyway, I came up with this:

var statuses = ["one", "two", "three", "four"];

var complexObj = {
    dataset: [
        {
            label: "two"
        },
        {
            label: "four"
        },
        {
            label: "three"
        },
        {
            label: "one"
        },
    ]
};

console.table(complexObj.dataset)

complexObj.dataset = _.sortBy(complexObj.dataset, function(n) {
  return _.indexOf(statuses, n.label);
});

console.table(complexObj.dataset)

Which worked a treat, once I'd clocked I need to return the sorted array rather than merely sort it.

Sunday 5 May 2019

Op Art with p5

My Dad and I both love Op art, and he recently forwarded an email from Tumblr with this link. It piqued my interest as I'm playing with p5 an awful lot at the minute in my spare time as you'll see from previous posts.

So I got to it but clocked that there might be an issue in terms of masking the squares which make up the image. A little judicious playing with Gimp meant that I knew the graphic would be made up of multiple squares. The squares either started with a black or yellow background and they changed after a delay. To get around the issue of masking, I opted to use createGraphics() and then embed that graphic in the main body of the image using the image() function. I ended up making a simple error though, but Stack Overflow came to my rescue, and I'm quite pleased with the result:

The code, which is also up on GitHub and as a single file on JSFiddle:

class Tile {

    constructor(p5, x, y, dimension, row, delay) {
        this.p5 = p5;
        this.x = x;
        this.y = y;
        this.dimension = dimension;
        this.delay = delay;
        this.onFirst = row % 2;
        this.on = p5.color(255, 184, 0);
        this.off = p5.color(26, 17, 16);
        this.diameter = Math.sqrt(Math.pow(dimension, 2) * 2)
        this.pg = p5.createGraphics(dimension, dimension)
        this.pg.noStroke();
    }

    update() {
        if (this.delay === 0) {
            if (this.diameter < 0) {
                this.diameter = Math.sqrt(Math.pow(this.dimension, 2) * 2);
                this.onFirst = !this.onFirst;
                this.delay = 120;
            }
            else {
                this.diameter -= 1;
            }
        } else {
            this.delay -= 1;
        }
        return this.draw();
    }

    draw() {
        this.pg.fill(this.onFirst ? this.off : this.on);
        this.pg.rect(0, 0, this.dimension, this.dimension);
        this.pg.fill(this.onFirst ? this.on : this.off);
        this.pg.circle(this.dimension / 2, this.dimension / 2, this.diameter);
        return this.pg;
    }
}

new p5(p5 => {

    const rows = 14;
    const columns = 14;
    const dimension = 40;
    const framerate = 20;
    const tiles = [];
    const delay = 30;

    p5.setup = () => {
        p5.createCanvas(columns * dimension, rows * dimension);
        for (let row = 0; row < rows; row++) {
            for (let column = 0; column < columns; column++) {
                tiles.push(new Tile(
                    p5,
                    column * dimension,
                    row * dimension,
                    dimension,
                    row,
                    column % 2 ? ((rows - row) * 5) + 80 : row * 5
                ));
            }
        }
    };

    p5.draw = () => {
        p5.background(200);
        tiles.forEach((tile) => {
            p5.image(tile.update(), tile.x, tile.y);
        });
    };
});