Skip to content

Commit

Permalink
A few major changes (#8)
Browse files Browse the repository at this point in the history
* a few major changes

- remove install functions
- move stuff around
  - group getters and setters together and such
- fix bugs
- make things shorter
- rename Meal.error to Meal.need

* update colour test

* update limit test

* add meal test
  • Loading branch information
Magnogen committed Jul 11, 2022
1 parent 77592d1 commit e0d3d8a
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 137 deletions.
236 changes: 106 additions & 130 deletions Elements.js
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
// jshint esversion: 10
// jshint esversion: 11
const Elements = {};

/* Main installer
* for installing everything into a scope (or technically an object)
*/

{
Elements.install = function (global) {
if (Elements.installed) return;

if (!Elements.Colour.installed) Elements.Colour.install(global);
if (!Elements.Limit.installed) Elements.Limit.install(global);
if (!Elements.Meal.installed) Elements.Meal.install(global);

Elements.installed = true;
}
}

/* Colour
* Creating and editing colours
* RGBA, HSLA and CMYA are supported
*/

{
// hsl2rgb() and rgb2hsl() adapted from Kamil Kiełczewski's answer from StackOverflow
// https://stackoverflow.com/a/64090995

// h[0-360] s[0-1] l[0-1] -> r[0-255] g[0-255] b[0-255]
const hsl2rgb = (h=0, s=1, l=0.5) => {
let a = s * Math.min(l, 1-l);
let f = (n, k=(n+(h+360)/30)%12) => l - a * Math.max(Math.min(k-3, 9-k, 1), -1);
return [255*f(0), 255*f(8), 255*f(4)]
}
// r[0-255] g[0-255] b[0-255] -> h[0-360] s[0-1] l[0-1]
const rgb2hsl = (_r=0, _g=0, _b=0) => {
let [r, g, b] = [_r/255, _g/255, _b/255];
let v=Math.max(r,g,b), c=v-Math.min(r,g,b), f=(1-Math.abs(v+v-c-1));
let h= c && ((v==r) ? (g-b)/c : ((v==g) ? 2+(b-r)/c : 4+(r-g)/c));
return [60*(h<0?h+6:h), f ? c/f : 0, (v+v-c)/2]
}

// c[0-255] m[0-255] y[0-255] <-> r[0-255] g[0-255] b[0-255]
const cmy2rgb = (c=0, m=0, y=0) => [ 255-c, 255-m, 255-y ];
const rgb2cmy = cmy2rgb;

Elements.Colour = class Colour {
constructor(type, ...data) {
this.r = 0; this.g = 0; this.b = 0; this.a = 255;
if (typeof type == 'string') {
if (!['rgb', 'rgba', 'hsl', 'hsla', 'cmy', 'cmya'].includes(type.toLowerCase())) throw {
name: 'SyntaxError',
message: `Colour type must be of "HSL", "RGB" or "CMY", recieved "${type}"`
}
if (!['rgb','rgba','hsl','hsla','cmy','cmya'].includes(type.toLowerCase())) throw {
name: 'Elements.Colour',
message: `Expected Colour Type and received ${ type }`
};
else if (['hsl', 'hsla'].includes(type)) this.hsla = data;
else if (['cmy', 'cmya'].includes(type)) this.cmya = data;
else this.rgba = data;
} else if (typeof type === 'object' && type.isElementColour) {
} else if (typeof type === 'object' && type instanceof Colour) {
this.rgba = type.rgba;
}
}
toRGB() { return `rgba(${this.rgba.map(Math.floor).join(', ')})` }
toHex() {
let hex = '#';
hex += (0|this.r).toString(16);
hex += (0|this.g).toString(16);
hex += (0|this.b).toString(16);
hex += (0|this.a).toString(16);
return hex
}
toRGB() { return `rgba(${this.rgba.map(n => 0|n).join(', ')})` }
toHex() { return '#'+this.rgba.map(c => (0|c).toString(16).padStart(2,'0')).join('') }

// this.r, this.g, this.b, and this.a are all actual values
get rgb() { return [this.r, this.g, this.b] }
get rgba() { return [this.r, this.g, this.b, this.a] }
set rgb(data) { this.rgba = [data, this.a] }
get rgba() { return [...this.rgb, this.a] }
set rgba(_data) {
let data = _data.flat(Infinity);
this.r = data[0]===undefined ? this.r : data[0];
this.g = data[1]===undefined ? this.g : data[1];
this.b = data[2]===undefined ? this.b : data[2];
this.a = data[3]===undefined ? this.a : data[3];
}

get h() { return this.hsl[0] }
set h(hue) { let b4 = this.hsla; this.hsla = [hue, b4[1], b4[2], b4[3]] }
get s() { return this.hsl[1] }
set s(saturation) { let b4 = this.hsla; this.hsla = [b4[0], saturation, b4[2], b4[3]] }
get l() { return this.hsl[2] }
set l(lightness) { let b4 = this.hsla; this.hsla = [b4[0], b4[1], lightness, b4[3]] }
get hsl() { return Elements.Colour.rgb2hsl(this.r, this.g, this.b) }
set hsl(_data) {
let data = [...Array(3)].map((e, i)=>_data[i]);
this.hsla = [...data, this.a] }
set h(hue) { let b4 = this.hsla; this.hsla = [hue , b4[1], b4[2], b4[3]] }
set s(sat) { let b4 = this.hsla; this.hsla = [b4[0], sat , b4[2], b4[3]] }
set l(lig) { let b4 = this.hsla; this.hsla = [b4[0], b4[1], lig , b4[3]] }
get hsl() { return rgb2hsl(this.r, this.g, this.b) }
get hsla() { return [...this.hsl, this.a] }
set hsl(data) { this.hsla = [...data, this.a]; }
set hsla(_data) {
let data = _data.flat(Infinity);
let b4 = this.hsl;
data[0] = data[0] ? data[0] : b4[0];
data[1] = data[1] ? data[1] : b4[1];
data[2] = data[2] ? data[2] : b4[2];
data = [...Elements.Colour.hsl2rgb(data[0], data[1], data[2]), data[3]];
data = [...hsl2rgb(data[0], data[1], data[2]), data[3]];
this.r = data[0];
this.g = data[1];
this.b = data[2];
this.a = data[3]===undefined ? this.a : data[3];
}

get c() { return this.cmy[0] }
set c(cyan) { let b4 = this.cmya; this.cmya = [cyan, b4[1], b4[2], b4[3]] }
get m() { return this.cmy[1] }
set m(magenta) { let b4 = this.cmya; this.cmya = [b4[0], magenta, b4[2], b4[3]] }
get y() { return this.cmy[2] }
set y(yellow) { let b4 = this.cmya; this.cmya = [b4[0], b4[1], yellow, b4[3]] }
set c(cyn) { let b4 = this.cmya; this.cmya = [cyn , b4[1], b4[2], b4[3]] }
set m(mag) { let b4 = this.cmya; this.cmya = [b4[0], mag , b4[2], b4[3]] }
set y(yel) { let b4 = this.cmya; this.cmya = [b4[0], b4[1], yel , b4[3]] }
get cmy() { return [255-this.r, 255-this.g, 255-this.b] }
set cmy(data) { this.cmya = [data, this.a] }
get cmya() { return [...this.cmy, this.a] }
Expand All @@ -97,37 +97,6 @@ const Elements = {};
this.a = data[3]===undefined ? this.a : data[3];
}
}

// rgb2hsl() and hsl2rgb() adapted from Kamil Kiełczewski's answer from StackOverflow
// https://stackoverflow.com/a/64090995

// h[0-360] s[0-1] l[0-1] -> r[0-255] g[0-255] b[0-255]
Elements.Colour.hsl2rgb = function (h=0, s=1, l=0.5) {
let a = s * Math.min(l, 1-l);
let f = (n, k=(n+(h+360)/30)%12) => l - a * Math.max(Math.min(k-3, 9-k, 1), -1);
return [255*f(0), 255*f(8), 255*f(4)]
}

// r[0-255] g[0-255] b[0-255] -> h[0-360] s[0-1] l[0-1]
Elements.Colour.rgb2hsl = function (_r=0, _g=0, _b=0) {
let [r, g, b] = [_r/255, _g/255, _b/255];
let v=Math.max(r,g,b), c=v-Math.min(r,g,b), f=(1-Math.abs(v+v-c-1));
let h= c && ((v==r) ? (g-b)/c : ((v==g) ? 2+(b-r)/c : 4+(r-g)/c));
return [60*(h<0?h+6:h), f ? c/f : 0, (v+v-c)/2]
}

// c[0-255] m[0-255] y[0-255] <-> r[0-255] g[0-255] b[0-255]
Elements.Colour.cmy2rgb = (c=0, m=0, y=0) => [ 255-c, 255-m, 255-y ];
Elements.Colour.rgb2cmy = Elements.Colour.cmy2rgb;

Elements.Colour.install = function (global) {
global.Colour ??= Elements.Colour;
global.Colour.rgb2hsl ??= Elements.Colour.rgb2hsl;
global.Colour.hsl2rgb ??= Elements.Colour.hsl2rgb;
global.Colour.rgb2cmy ??= Elements.Colour.rgb2cmy;
global.Colour.cmy2rgb ??= Elements.Colour.cmy2rgb;
Elements.Colour.installed = true;
};
}

/* Limit
Expand All @@ -136,7 +105,30 @@ const Elements = {};
*/

{
Elements.Limit = {};
Elements.Limit = class Limit {
constructor(min, max) {
[this.min, this.max] = min < max ? [min, max] : [max, min];
}
clamp(n) {
if (n < this.min) return this.min;
if (n > this.max) return this.max;
return n;
}
wrap(n) {
let N = n;
while (N < this.min) N += this.max-this.min;
while (N > this.max) N -= this.max-this.min;
return N;
}
fold(n) {
let N = (n-this.min) % (2*(this.max-this.min)) + this.min;
if (N > this.max) return 2*this.max - N;
return N;
}
sigmoid(n) {
return (this.max-this.min) / (1 + Math.exp(4 * (this.min-n) / (this.max-this.min) + 2)) + this.min;
}
};

Elements.Limit.clamp = (n, a, b) => {
if (b < a) return Elements.Limit.clamp(n, b, a);
Expand All @@ -146,9 +138,10 @@ const Elements = {};
};
Elements.Limit.wrap = (n, a, b) => {
if (b < a) return Elements.Limit.wrap(n, b, a);
if (n < a) return Elements.Limit.wrap(n+b-a, a, b);
if (n > b) return Elements.Limit.wrap(n-b-a, a, b);
return n;
let N = n;
while (N < a) N += b-a;
while (N > b) N -= b-a;
return N;
};
Elements.Limit.fold = (n, a, b) => {
if (b < a) return Elements.Limit.fold(n, b, a);
Expand All @@ -160,42 +153,39 @@ const Elements = {};
if (b < a) return Elements.Limit.sigmoid(n, b, a);
return (b - a) / (1 + Math.exp(4 * (a - n) / (b - a) + 2)) + a;
};

Elements.Limit.install = function (global) {
global.Limit ??= Elements.Limit;
global.Limit.clamp ??= Elements.Limit.clamp;
global.Limit.wrap ??= Elements.Limit.wrap;
global.Limit.fold ??= Elements.Limit.fold;
Elements.Limit.installed = true;
};
}

{
Elements.Meal = class Meal {
constructor(plate, { tokensOnly = false } = {}) {
if (typeof plate != 'string') err()
constructor(plate, tokensOnly=false) {
if (typeof plate != 'string') throw {
name: 'Elements.Meal',
message: `Expected String and received ${ typeof plate }`
};
this.plate = plate;
this.tokensOnly = tokensOnly
this.tokensOnly = tokensOnly;
this.index = 0;
this.line = 0;
this.column = 0;
}
finished() { return this.plate.length == 0; }
finished() { return this.plate.length == this.index }
first(check) {
if (this.finished()) return;
if (typeof check == 'string') return this.plate.startsWith(check);
if (typeof check == 'number') return this.plate.substring(0, check + 1);
if (typeof check == 'undefined') return this.plate[0];
if (typeof check == 'string')
return this.plate.substring(this.index, this.index+check.length) == check;
if (typeof check == 'number')
return this.plate.substring(this.index, this.index+check+1);
if (typeof check == 'undefined')
return this.plate[this.index];
}
eat(edible) {
if (typeof edible == 'string') {
if (!this.first(edible)) return null;
this.plate = this.plate.slice(edible.length);
this.index += edible.length;
const splitted = edible.split('\n');
this.line += splitted.length - 1;
if (splitted.length > 1) this.column = 0
this.column += splitted[splitted.length-1].length
const lines = edible.split('\n');
this.line += lines.length - 1;
if (lines.length > 1) this.column = 0;
this.column += lines[lines.length-1].length;
return edible;
}
else if (typeof edible == 'function') {
Expand All @@ -205,7 +195,6 @@ const Elements = {};
copy.column = this.column;
const out = edible(copy);
if (out == null) return null;
this.plate = copy.plate;
this.index = copy.index;
this.line = copy.line;
this.column = copy.column;
Expand Down Expand Up @@ -234,14 +223,6 @@ const Elements = {};
}
return food.tokensOnly ? content : content.join('');
}
Elements.Meal.error = (edible, messenger) => food => {
const content = food.eat(edible)
if (content == null) throw {
name: 'ElementsMealError',
message: typeof messenger == 'function' ? messenger(food) : messenger
};
return content;
};
Elements.Meal.many = edible => food => {
let content = [], current = food.eat(edible);
while (current != null) {
Expand All @@ -257,32 +238,27 @@ const Elements = {};
if (Array.isArray(value)) return mapper(...value);
return mapper(value);
};
Elements.Meal.need = (edible, messenger) => food => {
const content = food.eat(edible)
if (content == null) throw {
name: 'Elements.Meal.Need',
message: typeof messenger == 'function' ? messenger(food) : messenger
};
return content;
};
Elements.Meal.not = edible => food => food.eat(edible) == null ? food.eat(food.first()) : null;

Elements.Meal.ignore = edible => food => food.eat(edible) == null ? null : '';
Elements.Meal.maybe = edible => food => (food.eat(edible) ?? '');
Elements.Meal.upto = edible => food => food.eat(Elements.Meal.many(Elements.Meal.not(edible)));
Elements.Meal._ = food => food.eat(Elements.Meal.maybe(
Elements.Meal.many(Elements.Meal.any(' ', '\t'))
));
Elements.Meal.__ = food => food.eat(
Elements.Meal.many(Elements.Meal.any(' ', '\t'))
);
Elements.Meal.install = function (global) {
global.Meal ??= Elements.Meal;
global.Meal.any ??= Elements.Meal.any; // \
global.Meal.around ??= Elements.Meal.around; // |
global.Meal.chain ??= Elements.Meal.chain; // |
global.Meal.error ??= Elements.Meal.error; // |- Fundamentals
global.Meal.many ??= Elements.Meal.many; // |
global.Meal.map ??= Elements.Meal.map; // |
global.Meal.not ??= Elements.Meal.not; // /

global.Meal.ignore ??= Elements.Meal.ignore; // \
global.Meal.maybe ??= Elements.Meal.maybe; // |
global.Meal.upto ??= Elements.Meal.upto; // |- Helpers
global.Meal._ ??= Elements.Meal._; // |
global.Meal.__ ??= Elements.Meal.__; // /
Elements.Meal.installed = true;
};
Elements.Meal._ = food => {
const out = food.eat( Elements.Meal.maybe(Elements.Meal.many(Elements.Meal.any(' ','\t'))) );
if (out === null) return null;
return food.tokensOnly ? out.join('') : out;
}
Elements.Meal.__ = food => {
const out = food.eat( Elements.Meal.many(Elements.Meal.any(' ', '\t')) );
if (out === null) return null;
return food.tokensOnly ? out.join('') : out;
}
}
9 changes: 5 additions & 4 deletions tests/colour.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
Elements.install(this)
const { Colour } = Elements

let teal = new Colour('cmy', 255, 75, 100)
teal.s = 0.5
teal.l = 0.5
console.log('teal', teal.hsl)
// a nice looking teal colour tbh
document.body.style.backgroundColor = `rgba(${teal.r}, ${teal.g}, ${teal.b}, ${teal.a})`
document.body.style.backgroundColor = teal.toHex()

let peach = new Colour(teal)
peach.r = 255
console.log('peach', peach.hsl)
console.log('peach', peach.rgb)
// a cute peach colour lmao
document.body.style.backgroundColor = `rgba(${peach.r}, ${peach.g}, ${peach.b}, ${peach.a})`
document.body.style.backgroundColor = peach.toRGB()


16 changes: 13 additions & 3 deletions tests/limit.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
Elements.install(this)
const { Limit } = Elements

console.log(Limit.sigmoid(-Infinity, 0, 1)) // 0
console.log(Limit.sigmoid(0, 0, 1)) // 0.11920...
console.log(Limit.sigmoid(0, 0, 1)) // 0.1192...
console.log(Limit.sigmoid(0.5, 0, 1)) // 0.5
console.log(Limit.sigmoid(1, 0, 1)) // 0.88079...
console.log(Limit.sigmoid(1, 0, 1)) //0.8807...
console.log(Limit.sigmoid(Infinity, 0, 1)) // 1



const bound = new Limit(0, 0.123)

console.log(bound.sigmoid(-Infinity))
console.log(bound.sigmoid(0))
console.log(bound.sigmoid(0.5))
console.log(bound.sigmoid(1))
console.log(bound.sigmoid(Infinity))
// should all be the same

0 comments on commit e0d3d8a

Please sign in to comment.