app/javascript/animation/default_playlist_images.js
const seedrandom = require('seedrandom')
function Triangulr (props) {
// Tests
if (typeof props.width !== 'number' || props.width <= 0) {
throw 'Triangulr: width must be positive';
}
if (typeof props.height !== 'number' || props.height <= 0) {
throw 'Triangulr: height must be positive';
}
if (typeof props.lineHeight !== 'number' || props.lineHeight <= 0) {
throw 'Triangulr: lineHeight must be set and be positive number';
}
if (!!props.pointArea && typeof props.pointArea !== 'number' || props.pointArea < 0) {
throw 'Triangulr: pointArea must be set and be a positive number';
}
/* if (!!props.strokeWidth && typeof props.strokeWidth !== 'number' || props.strokeWidth < 0) {
throw 'Triangulr: strokeWidth must be set and be a positive number';
} */
if (!!props.renderingFunction && typeof props.renderingFunction !== 'function') {
throw 'Triangulr: renderingFunction must be a function';
}
// Save input
this.mapWidth = props.width*2;
this.mapHeight = props.height*2;
this.lineHeight = props.lineHeight;
this.pointArea = !!props.pointArea ? props.pointArea : 0;
this.strokeWidth = !!props.strokeWidth ? props.strokeWidth : 1;
this.colorRendering = !!props.renderingFunction ? props.renderingFunction : this.generateGray;
this.triangleLine = Math.sqrt(Math.pow(this.lineHeight/2, 2) + Math.pow(this.lineHeight, 2));
this.originX = - this.triangleLine;
this.originY = - this.lineHeight;
this.lines = [];
this.exportData = [];
this.lineMapping();
this.createTriangles();
return this.generateDom();
}
/**
* lineMapping
* generate this.lines from the contructor info
*
*/
Triangulr.prototype.lineMapping = function () {
var x, y, line;
var lineX = Math.ceil(this.mapWidth/this.triangleLine) + 4;
var lineY = Math.ceil(this.mapHeight/this.lineHeight) + 2;
var parite = this.triangleLine/4;
for(y = 0; y<lineY; y++) {
line = [];
for(x = 0; x<lineX; x++) {
line.push({
x: x * this.triangleLine + Math.round(Math.random() * this.pointArea * 2) - this.pointArea + this.originX + parite,
y: y * this.lineHeight + Math.round(Math.random() * this.pointArea * 2) - this.pointArea + this.originX
});
}
this.lines.push(line);
parite *= -1;
}
};
/**
* createTriangles
* use points form this.lines to generate triangles
* and put them into this.exportData
*
*/
Triangulr.prototype.createTriangles = function () {
var x, parite, lineA, lineB, aIndex, bIndex, points, poly, pointsList;
var counter = 0;
var lineParite = true;
this.exportData = [];
for(x = 0; x<this.lines.length -1; x++) {
lineA = this.lines[x];
lineB = this.lines[x+1];
aIndex = 0;
bIndex = 0;
parite = lineParite;
do {
// Get the good points
points = [lineA[aIndex], lineB[bIndex]];
if (parite) {
bIndex++;
points.push(lineB[bIndex]);
}
else {
aIndex++;
points.push(lineA[aIndex]);
}
parite = !parite;
// Save the triangle
pointsList = [
points[0],
points[1],
points[2]
];
this.exportData.push({
style: {
fill: this.colorRendering({
counter: counter,
x: aIndex + bIndex - 1,
y: x,
lines: this.lines.length,
cols: (lineA.length - 2) * 2,
points: pointsList
})
},
points: pointsList
});
counter++;
} while (aIndex != lineA.length-1 && bIndex != lineA.length-1);
lineParite = !lineParite;
}
};
/**
* generateDom
* generate the SVG object from exportData content
*
* @return {[object]} Svg DOM object
*/
Triangulr.prototype.generateDom = function () {
var i, j, data, points, style, polygon;
var svgTag = document.createElementNS('http://www.w3.org/2000/svg','svg');
var defsTag = document.createElementNS('http://www.w3.org/2000/svg','defs');
var gTag = document.createElementNS('http://www.w3.org/2000/svg','g');
var output = '';
svgTag.setAttribute('viewBox', '20 0 ' + this.mapWidth/2 + ' ' + this.mapHeight/2);
svgTag.setAttribute('preserveAspectRatio', 'xMinYMin slice');
//gTag.setAttribute('filter', 'url(#edgeClean)');
for(i in this.exportData) {
data = this.exportData[i];
polygon = document.createElementNS('http://www.w3.org/2000/svg','path');
points = 'M' + data.points[0].x + ' ' + data.points[0].y + ' ';
points += 'L' + data.points[1].x + ' ' + data.points[1].y + ' ';
points += 'L' + data.points[2].x + ' ' + data.points[2].y + ' Z';
//points += 'L' + data.points[2].x + ' ' + data.points[1].y + ' Z';
polygon.setAttribute('d', points);
polygon.setAttribute('fill', data.style.fill);
polygon.setAttribute('stroke', data.style.fill);
if(this.strokeWidth > 1){
// if there's a large stroke with, lets curve
polygon.setAttribute('stroke-linecap', 'round');
polygon.setAttribute('stroke-width', this.strokeWidth);
polygon.setAttribute('stroke-linejoin', 'round');
}else{
polygon.setAttribute('stroke-width', this.strokeWidth);
}
//polygon.setAttribute('shape-rendering', 'geometricPrecision');
//polygon.setAttribute('shape-rendering', 'crispEdges');
gTag.appendChild(polygon);
}
//console.log(this.cleanEdge())
svgTag.appendChild(defsTag);
defsTag.appendChild(this.cleanEdge());
svgTag.appendChild(gTag);
return svgTag;
};
Triangulr.prototype.cleanEdge = function () {
var filterTag = document.createElementNS('http://www.w3.org/2000/svg','filter');
var feComponentTransfer = document.createElementNS('http://www.w3.org/2000/svg','feComponentTransfer');
var feFuncA = document.createElementNS('http://www.w3.org/2000/svg','feFuncA');
feComponentTransfer.appendChild(feFuncA);
filterTag.appendChild(feComponentTransfer);
var output = '';
filterTag.setAttribute('color-interpolation-filters', 'sRGB');
filterTag.setAttribute('id', 'edgeClean');
feFuncA.setAttribute('type', 'table');
feFuncA.setAttribute('tableValues', '0 .5 1 1');
//console.log(filterTag);
return filterTag;
}
/**
* generateGray
* default color generator when no function is
* given to the constructor
* it generate dark grey colors
*
* @param {[object]} path Info object relative to current triangle
* @return {[string]} Color generated
*/
Triangulr.prototype.generateGray = function (path) {
var code = Math.floor(Math.random()*5).toString(16);
code += Math.floor(Math.random()*16).toString(16);
return '#'+code+code+code;
};
// Exports
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], function() {
return Triangulr;
});
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = Triangulr;
} else {
// Browser globals
window.Triangulr = Triangulr;
}
function randomBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
var colorGenerator = function (path) {
var random = 32;
var ratio = (path.x * path.y) / (path.cols * path.lines);
var code = Math.floor(255 - (ratio * (255-random)) - Math.random()*random).toString(16);
var color = '#'+code+code+code;
//console.log(color)
return color;
};/*
svg = new Triangulr (960, 400, 80, 40, colorGenerator); */
function getRandomColor() {
var letters = '123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 15)];
}
//console.log(color)
return color;
}
function getGreenColor(){
var max = 10;
var min = 200;
var green = Math.floor(Math.random() * (max - min + 1)) + min;
return "rgb(0," + green + ",0)";
}
function getPurpleColor(){
var max = 150;
var min = 120;
var blue = Math.floor(Math.random() * (max - min + 1)) + min;
var red = Math.floor(Math.random() * (max - min + 1)) + min;
//return "rgb(0," + green + ",0)";
return "rgb("+red+", 0," + blue + ")";
}
// adapted from: https://gist.github.com/aemkei/1325937
function hsl2rgb(h, s, b){
h *= 6;
s = [
b += s *= b < .5 ? b : 1 - b,
b - h % 1 * s * 2,
b -= s *= 2,
b,
b + h % 1 * s,
b + s
];
return "rgb(" +
Math.floor(s[ ~~h % 6 ] * 255) + "," + // red
Math.floor(s[ (h|16) % 6 ] * 255) + "," + // green
Math.floor(s[ (h|8) % 6 ] * 255) + ")" // blue
}
function colorsFromAHue(hue){
//var saturation = randomBetween(35,100)/100
var saturation = randomBetween(25,100)/100
var brightness = randomBetween(10,50)/100
return hsl2rgb(hue, saturation, brightness)
}
//var mySVG = new Triangulr (800, 600, 8, 40, getGreenColor);
//var mySVG = new Triangulr (800, 600, 8, 200, getPurpleColor);
//var mySVG = new Triangulr (800, 600, 28, 23, colorGenerator);
export function makeSVGFromTitle(height, title){
seedrandom(title, { global: true }) // Make all Math.random calls repeatable for this album
var lineHeight = randomBetween(height/20, height/10)
var pointArea = randomBetween(10, 40)
var whichColorFunction = Math.random()
if(whichColorFunction > .15){
var hue = Math.random() // keep the hue for a whole album
var colorFunction = colorsFromAHue.bind(null, hue)
}else{
var colorFunction = getRandomColor
}
// do we want circle shapes or just triangles?
if(Math.random() > .85)
var strokeWidth = randomBetween(10,50)
else
var strokeWidth = .2
//console.log('lineheight is ' + lineHeight + ' and pointArea is ' + pointArea);
var props = {width:200, height:200, lineHeight:lineHeight, pointArea:pointArea, strokeWidth:strokeWidth, renderingFunction:colorFunction}
return new Triangulr (props);
}
//var mySVG = new Triangulr (800, 600, 80, 400, getRandomColor);