Initial commit.
This commit is contained in:
commit
977917475f
3
.babelrc
Normal file
3
.babelrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"presets": ["latest"]
|
||||||
|
}
|
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
node_modules
|
||||||
|
lib
|
25
package.json
Normal file
25
package.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "themer-wallpaper-block-wave",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Wallpaper generator for themer.",
|
||||||
|
"main": "lib/index.js",
|
||||||
|
"author": "mjswensen",
|
||||||
|
"license": "MIT",
|
||||||
|
"jest": {
|
||||||
|
"rootDir": "lib"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "babel --out-dir lib src",
|
||||||
|
"start": "watch 'yarn run build' src",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-cli": "^6.18.0",
|
||||||
|
"babel-preset-latest": "^6.16.0",
|
||||||
|
"jest-cli": "^17.0.3",
|
||||||
|
"watch": "^1.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"svg2png": "^4.1.0"
|
||||||
|
}
|
||||||
|
}
|
90
src/index.js
Normal file
90
src/index.js
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import weightedRandom from './weighted-random';
|
||||||
|
import svg2png from 'svg2png';
|
||||||
|
|
||||||
|
export const render = (colors, options) => {
|
||||||
|
|
||||||
|
const colorWeights = new Map([
|
||||||
|
['accent0', 1],
|
||||||
|
['accent1', 1],
|
||||||
|
['accent2', 1],
|
||||||
|
['accent3', 1],
|
||||||
|
['accent4', 1],
|
||||||
|
['accent5', 1],
|
||||||
|
['accent6', 1],
|
||||||
|
['accent7', 1],
|
||||||
|
['shade0', 0],
|
||||||
|
['shade1', 8],
|
||||||
|
['shade2', 6],
|
||||||
|
['shade3', 4],
|
||||||
|
['shade4', 3],
|
||||||
|
['shade5', 2],
|
||||||
|
['shade6', 1],
|
||||||
|
['shade7', 1],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const colorSets = [{ name: 'dark', colors: colors.dark }, { name: 'light', colors: colors.light }].filter(colorSet => !!colorSet.colors);
|
||||||
|
|
||||||
|
const sizes = [ // TODO: get from args with this and a device being the default
|
||||||
|
{
|
||||||
|
w: 2880,
|
||||||
|
h: 1800,
|
||||||
|
s: 36,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const deepFlatten = arr => arr.reduce((cumulative, inner) => cumulative.concat(Array.isArray(inner) ? deepFlatten(inner) : inner), []);
|
||||||
|
|
||||||
|
return deepFlatten(colorSets.map(colorSet => sizes.map(size => {
|
||||||
|
let blockMaxSize = size.s / 3;
|
||||||
|
let blockMinSize = blockMaxSize * 2/3;
|
||||||
|
let blocks = [];
|
||||||
|
for (let i = 0; i < size.w; i += size.s) {
|
||||||
|
for (let j = 0; j < size.h; j += size.s) {
|
||||||
|
let color = colorSet.colors[weightedRandom(colorWeights)];
|
||||||
|
let xPosition = (i + size.s / 2) / size.w;
|
||||||
|
let yPosition = (j + size.s / 2) / size.h;
|
||||||
|
let positionScaleFactor = Math.abs(xPosition - yPosition);
|
||||||
|
let blockSize = blockMaxSize - (blockMaxSize - blockMinSize) * positionScaleFactor;
|
||||||
|
let padding = (size.s - blockSize) / 2;
|
||||||
|
blocks.push({
|
||||||
|
x: i + padding,
|
||||||
|
y: j + padding,
|
||||||
|
w: blockSize,
|
||||||
|
h: blockSize,
|
||||||
|
c: color,
|
||||||
|
g: Math.random() < 0.01,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { size: size, blocks: blocks };
|
||||||
|
}).map(svgData => {
|
||||||
|
const svgString = `
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" style="background-color: ${colorSet.colors.shade0};" width="${svgData.size.w}" height="${svgData.size.h}" viewBox="0 0 ${svgData.size.w} ${svgData.size.h}">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="overlay" x1="1" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" stop-color="${colorSet.colors.shade0}"/>
|
||||||
|
<stop offset="50%" stop-color="${colorSet.colors.shade0}" stop-opacity="0"/>
|
||||||
|
<stop offset="100%" stop-color="${colorSet.colors.shade0}"/>
|
||||||
|
</linearGradient>
|
||||||
|
<filter id="glow">
|
||||||
|
<feGaussianBlur stdDeviation="5" result="coloredBlur"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="coloredBlur"/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
${svgData.blocks.map(block => `<rect x="${block.x}" y="${block.y}" width="${block.w}" height="${block.h}" fill="${block.c}" rx="2" ry="2" ${block.g ? `filter="url(#glow)"` : ''} />`).join('\n')}
|
||||||
|
<rect x="0" y="0" width="${svgData.size.w}" height="${svgData.size.h}" fill="url(#overlay)"/>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
const basename = `themer-wallpaper-block-wave-${colorSet.name}-${svgData.size.w}x${svgData.size.h}`;
|
||||||
|
const svgBuffer = new Buffer(svgString, 'utf-8');
|
||||||
|
return [
|
||||||
|
Promise.resolve({ name: `${basename}.svg`, contents: svgBuffer }),
|
||||||
|
svg2png(svgBuffer).then(pngBuffer => ({ name: `${basename}.png`, contents: pngBuffer })),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
};
|
5
src/weighted-random.js
Normal file
5
src/weighted-random.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default map => {
|
||||||
|
const cumulativeWeights = Array.from(map.values()).reduce((c, weight, i) => c.concat(weight + (i === 0 ? 0 : c[i-1])), []);
|
||||||
|
const random = cumulativeWeights[cumulativeWeights.length-1] * Math.random();
|
||||||
|
return Array.from(map.keys())[cumulativeWeights.findIndex(cw => random < cw)];
|
||||||
|
};
|
27
src/weighted-random.test.js
Normal file
27
src/weighted-random.test.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import wr from './weighted-random';
|
||||||
|
|
||||||
|
describe('weighted random', () => {
|
||||||
|
it('selects a random key from a map of keys to weights', () => {
|
||||||
|
|
||||||
|
const testData = new Map([
|
||||||
|
['a', 1],
|
||||||
|
['b', 3],
|
||||||
|
['c', 1],
|
||||||
|
]);
|
||||||
|
const total = Array.from(testData.values()).reduce((total, weight) => total + weight, 0);
|
||||||
|
const samples = 1000;
|
||||||
|
|
||||||
|
let frequency = {
|
||||||
|
'a': 0,
|
||||||
|
'b': 0,
|
||||||
|
'c': 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < total * samples; i++) frequency[wr(testData)]++;
|
||||||
|
|
||||||
|
expect(Math.round(frequency['a'] / samples)).toBe(1);
|
||||||
|
expect(Math.round(frequency['b'] / samples)).toBe(3);
|
||||||
|
expect(Math.round(frequency['c'] / samples)).toBe(1);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user