mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
v1.5.276 - libs minification
This commit is contained in:
parent
1a2eb2291a
commit
71ca7a1166
17 changed files with 24 additions and 1226 deletions
File diff suppressed because one or more lines are too long
10
index.html
10
index.html
|
|
@ -37,7 +37,7 @@
|
|||
<link rel="stylesheet" href="libs/jquery-ui.css">
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="map" width="100%" height="100%">
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="map" width="100%" height="100%">
|
||||
<defs>
|
||||
<g id="filters">
|
||||
<filter id="blurFilter" x="-1" y="-1" width="100" height="100">
|
||||
|
|
@ -3985,9 +3985,9 @@
|
|||
<script src="modules/military-generator.js"></script>
|
||||
<script src="modules/coa-generator.js"></script>
|
||||
<script src="libs/polylabel.min.js"></script>
|
||||
<script src="libs/lineclip.js"></script>
|
||||
<script src="libs/lineclip.min.js"></script>
|
||||
<script src="libs/jquery-ui.min.js"></script>
|
||||
<script src="libs/seedrandom.min.js"></script>
|
||||
<script src="libs/alea.min.js"></script>
|
||||
<script src="modules/ui/layers.js"></script>
|
||||
|
||||
<script defer src="modules/ui/general.js"></script>
|
||||
|
|
@ -4030,7 +4030,7 @@
|
|||
<script defer src="modules/ui/emblems-editor.js"></script>
|
||||
<script defer src="modules/ui/editors.js"></script>
|
||||
<script defer src="modules/ui/3d.js"></script>
|
||||
<script defer src="libs/rgbquant.js"></script>
|
||||
<script defer src="libs/rgbquant.min.js"></script>
|
||||
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
||||
<script defer src="libs/pell.js"></script>
|
||||
<script defer src="libs/pell.min.js"></script>
|
||||
</body>
|
||||
|
|
|
|||
3
libs/alea.min.js
vendored
Normal file
3
libs/alea.min.js
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/*https://github.com/macmcmeans/aleaPRNG/blob/master/aleaPRNG-1.1.js
|
||||
©2010 Johannes Baagøe, MIT license; Derivative ©2017-2020 W. Mac" McMeans, BSD license.*/
|
||||
const aleaPRNG=function(){return function(n){"use strict";var r,t,e,o,a,u=new Uint32Array(3),i="";function c(n){var a=function(){var n=4022871197,r=function(r){r=r.toString();for(var t=0,e=r.length;t<e;t++){var o=.02519603282416938*(n+=r.charCodeAt(t));o-=n=o>>>0,n=(o*=n)>>>0,n+=4294967296*(o-=n)}return 2.3283064365386963e-10*(n>>>0)};return r.version="Mash 0.9",r}();r=a(" "),t=a(" "),e=a(" "),o=1;for(var u=0;u<n.length;u++)(r-=a(n[u]))<0&&(r+=1),(t-=a(n[u]))<0&&(t+=1),(e-=a(n[u]))<0&&(e+=1);i=a.version,a=null}function f(n){return parseInt(n,10)===n}var l=function(){var n=2091639*r+2.3283064365386963e-10*o;return r=t,t=e,e=n-(o=0|n)};return l.fract53=function(){return l()+1.1102230246251565e-16*(2097152*l()|0)},l.int32=function(){return 4294967296*l()},l.cycle=function(n){(n=void 0===n?1:+n)<1&&(n=1);for(var r=0;r<n;r++)l()},l.range=function(){var n,r;return 1===arguments.length?(n=0,r=arguments[0]):(n=arguments[0],r=arguments[1]),arguments[0]>arguments[1]&&(n=arguments[1],r=arguments[0]),f(n)&&f(r)?Math.floor(l()*(r-n+1))+n:l()*(r-n)+n},l.restart=function(){c(a)},l.seed=function(){c(Array.prototype.slice.call(arguments))},l.version=function(){return"aleaPRNG 1.1.0"},l.versions=function(){return"aleaPRNG 1.1.0, "+i},0===n.length&&(window.crypto.getRandomValues(u),n=[u[0],u[1],u[2]]),a=n,c(n),l}(Array.prototype.slice.call(arguments))};
|
||||
103
libs/lineclip.js
103
libs/lineclip.js
|
|
@ -1,103 +0,0 @@
|
|||
'use strict';
|
||||
// lineclip by mourner, https://github.com/mapbox/lineclip
|
||||
// Cohen-Sutherland line clippign algorithm, adapted to efficiently
|
||||
// handle polylines rather than just segments
|
||||
function lineclip(points, bbox, result) {
|
||||
var len = points.length,
|
||||
codeA = bitCode(points[0], bbox),
|
||||
part = [],
|
||||
i, a, b, codeB, lastCode;
|
||||
if (!result) result = [];
|
||||
|
||||
for (i = 1; i < len; i++) {
|
||||
a = points[i - 1];
|
||||
b = points[i];
|
||||
codeB = lastCode = bitCode(b, bbox);
|
||||
|
||||
while (true) {
|
||||
if (!(codeA | codeB)) { // accept
|
||||
part.push(a);
|
||||
|
||||
if (codeB !== lastCode) { // segment went outside
|
||||
part.push(b);
|
||||
if (i < len - 1) { // start a new line
|
||||
result.push(part);
|
||||
part = [];
|
||||
}
|
||||
} else if (i === len - 1) {
|
||||
part.push(b);
|
||||
}
|
||||
break;
|
||||
|
||||
} else if (codeA & codeB) { // trivial reject
|
||||
break;
|
||||
} else if (codeA) { // a outside, intersect with clip edge
|
||||
a = intersect(a, b, codeA, bbox);
|
||||
codeA = bitCode(a, bbox);
|
||||
} else { // b outside
|
||||
b = intersect(a, b, codeB, bbox);
|
||||
codeB = bitCode(b, bbox);
|
||||
}
|
||||
}
|
||||
codeA = lastCode;
|
||||
}
|
||||
|
||||
if (part.length) result.push(part);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Sutherland-Hodgeman polygon clipping algorithm
|
||||
function polygonclip(points, bbox, secure = 0) {
|
||||
var result, edge, prev, prevInside, inter, i, p, inside;
|
||||
|
||||
// clip against each side of the clip rectangle
|
||||
for (edge = 1; edge <= 8; edge *= 2) {
|
||||
result = [];
|
||||
prev = points[points.length-1];
|
||||
prevInside = !(bitCode(prev, bbox) & edge);
|
||||
|
||||
for (i = 0; i < points.length; i++) {
|
||||
p = points[i];
|
||||
inside = !(bitCode(p, bbox) & edge);
|
||||
inter = inside !== prevInside; // segment goes through the clip window
|
||||
|
||||
const pi = intersect(prev, p, edge, bbox);
|
||||
if (inter) result.push(pi); // add an intersection point
|
||||
if (secure && inter) result.push(pi, pi); // add additional intersection points to secure correct d3 curve
|
||||
if (inside) result.push(p); // add a point if it's inside
|
||||
|
||||
prev = p;
|
||||
prevInside = inside;
|
||||
}
|
||||
points = result;
|
||||
if (!points.length) break;
|
||||
}
|
||||
//result.forEach(p => debug.append("circle").attr("cx", p[0]).attr("cy", p[1]).attr("r", .6).attr("fill", "red"));
|
||||
return result;
|
||||
}
|
||||
|
||||
// intersect a segment against one of the 4 lines that make up the bbox
|
||||
function intersect(a, b, edge, bbox) {
|
||||
return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
|
||||
edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
|
||||
edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
|
||||
edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : null; // left
|
||||
}
|
||||
|
||||
// bit code reflects the point position relative to the bbox:
|
||||
// left mid right
|
||||
// top 1001 1000 1010
|
||||
// mid 0001 0000 0010
|
||||
// bottom 0101 0100 0110
|
||||
function bitCode(p, bbox) {
|
||||
var code = 0;
|
||||
|
||||
if (p[0] < bbox[0]) code |= 1; // left
|
||||
else if (p[0] > bbox[2]) code |= 2; // right
|
||||
|
||||
if (p[1] < bbox[1]) code |= 4; // bottom
|
||||
else if (p[1] > bbox[3]) code |= 8; // top
|
||||
|
||||
return code;
|
||||
}
|
||||
2
libs/lineclip.min.js
vendored
Normal file
2
libs/lineclip.min.js
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// lineclip by mourner, https://github.com/mapbox/lineclip
|
||||
"use strict";function lineclip(t,e,n){var r,i,u,o,s,h=t.length,c=bitCode(t[0],e),f=[];for(n=n||[],r=1;r<h;r++){for(i=t[r-1],o=s=bitCode(u=t[r],e);;){if(!(c|o)){f.push(i),o!==s?(f.push(u),r<h-1&&(n.push(f),f=[])):r===h-1&&f.push(u);break}if(c&o)break;c?c=bitCode(i=intersect(i,u,c,e),e):o=bitCode(u=intersect(i,u,o,e),e)}c=s}return f.length&&n.push(f),n}function polygonclip(t,e,n=0){for(var r,i,u,o,s,h,c,f=1;f<=8;f*=2){for(r=[],u=!(bitCode(i=t[t.length-1],e)&f),s=0;s<t.length;s++){o=(c=!(bitCode(h=t[s],e)&f))!==u;var l=intersect(i,h,f,e);o&&r.push(l),n&&o&&r.push(l,l),c&&r.push(h),i=h,u=c}if(!(t=r).length)break}return r}function intersect(t,e,n,r){return 8&n?[t[0]+(e[0]-t[0])*(r[3]-t[1])/(e[1]-t[1]),r[3]]:4&n?[t[0]+(e[0]-t[0])*(r[1]-t[1])/(e[1]-t[1]),r[1]]:2&n?[r[2],t[1]+(e[1]-t[1])*(r[2]-t[0])/(e[0]-t[0])]:1&n?[r[0],t[1]+(e[1]-t[1])*(r[0]-t[0])/(e[0]-t[0])]:null}function bitCode(t,e){var n=0;return t[0]<e[0]?n|=1:t[0]>e[2]&&(n|=2),t[1]<e[1]?n|=4:t[1]>e[3]&&(n|=8),n}
|
||||
163
libs/pell.js
163
libs/pell.js
|
|
@ -1,163 +0,0 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.Pell = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
const defaultParagraphSeparatorString = 'defaultParagraphSeparator'
|
||||
const formatBlock = 'formatBlock'
|
||||
const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener)
|
||||
const appendChild = (parent, child) => parent.appendChild(child)
|
||||
const createElement = tag => document.createElement(tag)
|
||||
const queryCommandState = command => document.queryCommandState(command)
|
||||
const queryCommandValue = command => document.queryCommandValue(command)
|
||||
const exec = (command, value = null) => document.execCommand(command, false, value)
|
||||
|
||||
const defaultActions = {
|
||||
bold: {
|
||||
icon: '<b>B</b>',
|
||||
title: 'Bold',
|
||||
state: () => queryCommandState('bold'),
|
||||
result: () => exec('bold')
|
||||
},
|
||||
italic: {
|
||||
icon: '<i>I</i>',
|
||||
title: 'Italic',
|
||||
state: () => queryCommandState('italic'),
|
||||
result: () => exec('italic')
|
||||
},
|
||||
underline: {
|
||||
icon: '<u>U</u>',
|
||||
title: 'Underline',
|
||||
state: () => queryCommandState('underline'),
|
||||
result: () => exec('underline')
|
||||
},
|
||||
strikethrough: {
|
||||
icon: '<strike>S</strike>',
|
||||
title: 'Strike-through',
|
||||
state: () => queryCommandState('strikeThrough'),
|
||||
result: () => exec('strikeThrough')
|
||||
},
|
||||
heading1: {
|
||||
icon: '<b>H<sub>1</sub></b>',
|
||||
title: 'Heading 1',
|
||||
result: () => exec(formatBlock, '<h1>')
|
||||
},
|
||||
heading2: {
|
||||
icon: '<b>H<sub>2</sub></b>',
|
||||
title: 'Heading 2',
|
||||
result: () => exec(formatBlock, '<h2>')
|
||||
},
|
||||
paragraph: {
|
||||
icon: '¶',
|
||||
title: 'Paragraph',
|
||||
result: () => exec(formatBlock, '<p>')
|
||||
},
|
||||
quote: {
|
||||
icon: '“ ”',
|
||||
title: 'Quote',
|
||||
result: () => exec(formatBlock, '<blockquote>')
|
||||
},
|
||||
olist: {
|
||||
icon: '#',
|
||||
title: 'Ordered List',
|
||||
result: () => exec('insertOrderedList')
|
||||
},
|
||||
ulist: {
|
||||
icon: '•',
|
||||
title: 'Unordered List',
|
||||
result: () => exec('insertUnorderedList')
|
||||
},
|
||||
code: {
|
||||
icon: '</>',
|
||||
title: 'Code',
|
||||
result: () => exec(formatBlock, '<pre>')
|
||||
},
|
||||
line: {
|
||||
icon: '―',
|
||||
title: 'Horizontal Line',
|
||||
result: () => exec('insertHorizontalRule')
|
||||
},
|
||||
link: {
|
||||
icon: '🔗',
|
||||
title: 'Link',
|
||||
result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
|
||||
},
|
||||
image: {
|
||||
icon: '📷',
|
||||
title: 'Image',
|
||||
result: () => {
|
||||
navigator.clipboard.readText().then(url => exec('insertImage', url))
|
||||
exec('enableObjectResizing')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultClasses = {
|
||||
actionbar: 'pell-actionbar',
|
||||
button: 'pell-button',
|
||||
content: 'pell-content',
|
||||
selected: 'pell-button-selected'
|
||||
}
|
||||
|
||||
const init = settings => {
|
||||
const actions = settings.actions
|
||||
? (
|
||||
settings.actions.map(action => {
|
||||
if (typeof action === 'string') return defaultActions[action]
|
||||
else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }
|
||||
return action
|
||||
})
|
||||
)
|
||||
: Object.keys(defaultActions).map(action => defaultActions[action])
|
||||
|
||||
const classes = { ...defaultClasses, ...settings.classes }
|
||||
|
||||
const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'
|
||||
|
||||
const actionbar = createElement('div')
|
||||
actionbar.className = classes.actionbar
|
||||
appendChild(settings.element, actionbar)
|
||||
|
||||
const content = settings.element.content = createElement('div')
|
||||
content.contentEditable = true
|
||||
content.className = classes.content
|
||||
content.oninput = ({ target: { firstChild } }) => {
|
||||
if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)
|
||||
else if (content.innerHTML === '<br>') content.innerHTML = ''
|
||||
settings.onChange(content.innerHTML)
|
||||
}
|
||||
content.onkeydown = event => {
|
||||
if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
|
||||
setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0)
|
||||
}
|
||||
}
|
||||
appendChild(settings.element, content)
|
||||
|
||||
actions.forEach(action => {
|
||||
const button = createElement('button')
|
||||
button.className = classes.button
|
||||
button.innerHTML = action.icon
|
||||
button.title = action.title
|
||||
button.setAttribute('type', 'button')
|
||||
button.onclick = () => action.result() && content.focus()
|
||||
|
||||
if (action.state) {
|
||||
const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected)
|
||||
addEventListener(content, 'keyup', handler)
|
||||
addEventListener(content, 'mouseup', handler)
|
||||
addEventListener(button, 'click', handler)
|
||||
}
|
||||
|
||||
appendChild(actionbar, button)
|
||||
})
|
||||
|
||||
if (settings.styleWithCSS) exec('styleWithCSS')
|
||||
exec(defaultParagraphSeparatorString, defaultParagraphSeparator)
|
||||
|
||||
return settings.element
|
||||
}
|
||||
|
||||
return {exec, init}
|
||||
|
||||
})));
|
||||
2
libs/pell.min.js
vendored
Normal file
2
libs/pell.min.js
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// https://github.com/jaredreich/pell, MIT License
|
||||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Pell=t()}(this,function(){"use strict";const e=(e,t,n)=>e.addEventListener(t,n),t=(e,t)=>e.appendChild(t),n=e=>document.createElement(e),i=e=>document.queryCommandState(e),o=(e,t=null)=>document.execCommand(e,!1,t),l={bold:{icon:"<b>B</b>",title:"Bold",state:()=>i("bold"),result:()=>o("bold")},italic:{icon:"<i>I</i>",title:"Italic",state:()=>i("italic"),result:()=>o("italic")},underline:{icon:"<u>U</u>",title:"Underline",state:()=>i("underline"),result:()=>o("underline")},strikethrough:{icon:"<strike>S</strike>",title:"Strike-through",state:()=>i("strikeThrough"),result:()=>o("strikeThrough")},heading1:{icon:"<b>H<sub>1</sub></b>",title:"Heading 1",result:()=>o("formatBlock","<h1>")},heading2:{icon:"<b>H<sub>2</sub></b>",title:"Heading 2",result:()=>o("formatBlock","<h2>")},paragraph:{icon:"¶",title:"Paragraph",result:()=>o("formatBlock","<p>")},quote:{icon:"“ ”",title:"Quote",result:()=>o("formatBlock","<blockquote>")},olist:{icon:"#",title:"Ordered List",result:()=>o("insertOrderedList")},ulist:{icon:"•",title:"Unordered List",result:()=>o("insertUnorderedList")},code:{icon:"</>",title:"Code",result:()=>o("formatBlock","<pre>")},line:{icon:"―",title:"Horizontal Line",result:()=>o("insertHorizontalRule")},link:{icon:"🔗",title:"Link",result:()=>navigator.clipboard.readText().then(e=>o("createLink",e))},image:{icon:"📷",title:"Image",result:()=>{navigator.clipboard.readText().then(e=>o("insertImage",e)),o("enableObjectResizing")}}},r={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"};return{exec:o,init:i=>{const a=i.actions?i.actions.map(e=>"string"==typeof e?l[e]:l[e.name]?{...l[e.name],...e}:e):Object.keys(l).map(e=>l[e]),s={...r,...i.classes},c=i.defaultParagraphSeparator||"div",u=n("div");u.className=s.actionbar,t(i.element,u);const d=i.element.content=n("div");return d.contentEditable=!0,d.className=s.content,d.oninput=(({target:{firstChild:e}})=>{e&&3===e.nodeType?o("formatBlock",`<${c}>`):"<br>"===d.innerHTML&&(d.innerHTML=""),i.onChange(d.innerHTML)}),d.onkeydown=(e=>{"Enter"===e.key&&"blockquote"===(e=>document.queryCommandValue(e))("formatBlock")&&setTimeout(()=>o("formatBlock",`<${c}>`),0)}),t(i.element,d),a.forEach(i=>{const o=n("button");if(o.className=s.button,o.innerHTML=i.icon,o.title=i.title,o.setAttribute("type","button"),o.onclick=(()=>i.result()&&d.focus()),i.state){const t=()=>o.classList[i.state()?"add":"remove"](s.selected);e(d,"keyup",t),e(d,"mouseup",t),e(o,"click",t)}t(u,o)}),i.styleWithCSS&&o("styleWithCSS"),o("defaultParagraphSeparator",c),i.element}}});
|
||||
935
libs/rgbquant.js
935
libs/rgbquant.js
|
|
@ -1,935 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2015, Leon Sorokin
|
||||
* All rights reserved. (MIT Licensed)
|
||||
*
|
||||
* RgbQuant.js - an image quantization lib
|
||||
*/
|
||||
|
||||
(function(){
|
||||
function RgbQuant(opts) {
|
||||
opts = opts || {};
|
||||
|
||||
// 1 = by global population, 2 = subregion population threshold
|
||||
this.method = opts.method || 2;
|
||||
// desired final palette size
|
||||
this.colors = opts.colors || 256;
|
||||
// # of highest-frequency colors to start with for palette reduction
|
||||
this.initColors = opts.initColors || 4096;
|
||||
// color-distance threshold for initial reduction pass
|
||||
this.initDist = opts.initDist || 0.01;
|
||||
// subsequent passes threshold
|
||||
this.distIncr = opts.distIncr || 0.005;
|
||||
// palette grouping
|
||||
this.hueGroups = opts.hueGroups || 10;
|
||||
this.satGroups = opts.satGroups || 10;
|
||||
this.lumGroups = opts.lumGroups || 10;
|
||||
// if > 0, enables hues stats and min-color retention per group
|
||||
this.minHueCols = opts.minHueCols || 0;
|
||||
// HueStats instance
|
||||
this.hueStats = this.minHueCols ? new HueStats(this.hueGroups, this.minHueCols) : null;
|
||||
|
||||
// subregion partitioning box size
|
||||
this.boxSize = opts.boxSize || [64,64];
|
||||
// number of same pixels required within box for histogram inclusion
|
||||
this.boxPxls = opts.boxPxls || 2;
|
||||
// palette locked indicator
|
||||
this.palLocked = false;
|
||||
// palette sort order
|
||||
// this.sortPal = ['hue-','lum-','sat-'];
|
||||
|
||||
// dithering/error diffusion kernel name
|
||||
this.dithKern = opts.dithKern || null;
|
||||
// dither serpentine pattern
|
||||
this.dithSerp = opts.dithSerp || false;
|
||||
// minimum color difference (0-1) needed to dither
|
||||
this.dithDelta = opts.dithDelta || 0;
|
||||
|
||||
// accumulated histogram
|
||||
this.histogram = {};
|
||||
// palette - rgb triplets
|
||||
this.idxrgb = opts.palette ? opts.palette.slice(0) : [];
|
||||
// palette - int32 vals
|
||||
this.idxi32 = [];
|
||||
// reverse lookup {i32:idx}
|
||||
this.i32idx = {};
|
||||
// {i32:rgb}
|
||||
this.i32rgb = {};
|
||||
// enable color caching (also incurs overhead of cache misses and cache building)
|
||||
this.useCache = opts.useCache !== false;
|
||||
// min color occurance count needed to qualify for caching
|
||||
this.cacheFreq = opts.cacheFreq || 10;
|
||||
// allows pre-defined palettes to be re-indexed (enabling palette compacting and sorting)
|
||||
this.reIndex = opts.reIndex || this.idxrgb.length == 0;
|
||||
// selection of color-distance equation
|
||||
this.colorDist = opts.colorDist == "manhattan" ? distManhattan : distEuclidean;
|
||||
|
||||
// if pre-defined palette, build lookups
|
||||
if (this.idxrgb.length > 0) {
|
||||
var self = this;
|
||||
this.idxrgb.forEach(function(rgb, i) {
|
||||
var i32 = (
|
||||
(255 << 24) | // alpha
|
||||
(rgb[2] << 16) | // blue
|
||||
(rgb[1] << 8) | // green
|
||||
rgb[0] // red
|
||||
) >>> 0;
|
||||
|
||||
self.idxi32[i] = i32;
|
||||
self.i32idx[i32] = i;
|
||||
self.i32rgb[i32] = rgb;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// gathers histogram info
|
||||
RgbQuant.prototype.sample = function sample(img, width) {
|
||||
if (this.palLocked)
|
||||
throw "Cannot sample additional images, palette already assembled.";
|
||||
|
||||
var data = getImageData(img, width);
|
||||
|
||||
switch (this.method) {
|
||||
case 1: this.colorStats1D(data.buf32); break;
|
||||
case 2: this.colorStats2D(data.buf32, data.width); break;
|
||||
}
|
||||
};
|
||||
|
||||
// image quantizer
|
||||
// todo: memoize colors here also
|
||||
// @retType: 1 - Uint8Array (default), 2 - Indexed array, 3 - Match @img type (unimplemented, todo)
|
||||
RgbQuant.prototype.reduce = function reduce(img, retType, dithKern, dithSerp) {
|
||||
if (!this.palLocked)
|
||||
this.buildPal();
|
||||
|
||||
dithKern = dithKern || this.dithKern;
|
||||
dithSerp = typeof dithSerp != "undefined" ? dithSerp : this.dithSerp;
|
||||
|
||||
retType = retType || 1;
|
||||
|
||||
// reduce w/dither
|
||||
if (dithKern)
|
||||
var out32 = this.dither(img, dithKern, dithSerp);
|
||||
else {
|
||||
var data = getImageData(img),
|
||||
buf32 = data.buf32,
|
||||
len = buf32.length,
|
||||
out32 = new Uint32Array(len);
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
var i32 = buf32[i];
|
||||
out32[i] = this.nearestColor(i32);
|
||||
}
|
||||
}
|
||||
|
||||
if (retType == 1)
|
||||
return new Uint8Array(out32.buffer);
|
||||
|
||||
if (retType == 2) {
|
||||
var out = [],
|
||||
len = out32.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
var i32 = out32[i];
|
||||
out[i] = this.i32idx[i32];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
// adapted from http://jsbin.com/iXofIji/2/edit by PAEz
|
||||
RgbQuant.prototype.dither = function(img, kernel, serpentine) {
|
||||
// http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/
|
||||
var kernels = {
|
||||
FloydSteinberg: [
|
||||
[7 / 16, 1, 0],
|
||||
[3 / 16, -1, 1],
|
||||
[5 / 16, 0, 1],
|
||||
[1 / 16, 1, 1]
|
||||
],
|
||||
FalseFloydSteinberg: [
|
||||
[3 / 8, 1, 0],
|
||||
[3 / 8, 0, 1],
|
||||
[2 / 8, 1, 1]
|
||||
],
|
||||
Stucki: [
|
||||
[8 / 42, 1, 0],
|
||||
[4 / 42, 2, 0],
|
||||
[2 / 42, -2, 1],
|
||||
[4 / 42, -1, 1],
|
||||
[8 / 42, 0, 1],
|
||||
[4 / 42, 1, 1],
|
||||
[2 / 42, 2, 1],
|
||||
[1 / 42, -2, 2],
|
||||
[2 / 42, -1, 2],
|
||||
[4 / 42, 0, 2],
|
||||
[2 / 42, 1, 2],
|
||||
[1 / 42, 2, 2]
|
||||
],
|
||||
Atkinson: [
|
||||
[1 / 8, 1, 0],
|
||||
[1 / 8, 2, 0],
|
||||
[1 / 8, -1, 1],
|
||||
[1 / 8, 0, 1],
|
||||
[1 / 8, 1, 1],
|
||||
[1 / 8, 0, 2]
|
||||
],
|
||||
Jarvis: [ // Jarvis, Judice, and Ninke / JJN?
|
||||
[7 / 48, 1, 0],
|
||||
[5 / 48, 2, 0],
|
||||
[3 / 48, -2, 1],
|
||||
[5 / 48, -1, 1],
|
||||
[7 / 48, 0, 1],
|
||||
[5 / 48, 1, 1],
|
||||
[3 / 48, 2, 1],
|
||||
[1 / 48, -2, 2],
|
||||
[3 / 48, -1, 2],
|
||||
[5 / 48, 0, 2],
|
||||
[3 / 48, 1, 2],
|
||||
[1 / 48, 2, 2]
|
||||
],
|
||||
Burkes: [
|
||||
[8 / 32, 1, 0],
|
||||
[4 / 32, 2, 0],
|
||||
[2 / 32, -2, 1],
|
||||
[4 / 32, -1, 1],
|
||||
[8 / 32, 0, 1],
|
||||
[4 / 32, 1, 1],
|
||||
[2 / 32, 2, 1],
|
||||
],
|
||||
Sierra: [
|
||||
[5 / 32, 1, 0],
|
||||
[3 / 32, 2, 0],
|
||||
[2 / 32, -2, 1],
|
||||
[4 / 32, -1, 1],
|
||||
[5 / 32, 0, 1],
|
||||
[4 / 32, 1, 1],
|
||||
[2 / 32, 2, 1],
|
||||
[2 / 32, -1, 2],
|
||||
[3 / 32, 0, 2],
|
||||
[2 / 32, 1, 2],
|
||||
],
|
||||
TwoSierra: [
|
||||
[4 / 16, 1, 0],
|
||||
[3 / 16, 2, 0],
|
||||
[1 / 16, -2, 1],
|
||||
[2 / 16, -1, 1],
|
||||
[3 / 16, 0, 1],
|
||||
[2 / 16, 1, 1],
|
||||
[1 / 16, 2, 1],
|
||||
],
|
||||
SierraLite: [
|
||||
[2 / 4, 1, 0],
|
||||
[1 / 4, -1, 1],
|
||||
[1 / 4, 0, 1],
|
||||
],
|
||||
};
|
||||
|
||||
if (!kernel || !kernels[kernel]) {
|
||||
throw 'Unknown dithering kernel: ' + kernel;
|
||||
}
|
||||
|
||||
var ds = kernels[kernel];
|
||||
|
||||
var data = getImageData(img),
|
||||
// buf8 = data.buf8,
|
||||
buf32 = data.buf32,
|
||||
width = data.width,
|
||||
height = data.height,
|
||||
len = buf32.length;
|
||||
|
||||
var dir = serpentine ? -1 : 1;
|
||||
|
||||
for (var y = 0; y < height; y++) {
|
||||
if (serpentine)
|
||||
dir = dir * -1;
|
||||
|
||||
var lni = y * width;
|
||||
|
||||
for (var x = (dir == 1 ? 0 : width - 1), xend = (dir == 1 ? width : 0); x !== xend; x += dir) {
|
||||
// Image pixel
|
||||
var idx = lni + x,
|
||||
i32 = buf32[idx],
|
||||
r1 = (i32 & 0xff),
|
||||
g1 = (i32 & 0xff00) >> 8,
|
||||
b1 = (i32 & 0xff0000) >> 16;
|
||||
|
||||
// Reduced pixel
|
||||
var i32x = this.nearestColor(i32),
|
||||
r2 = (i32x & 0xff),
|
||||
g2 = (i32x & 0xff00) >> 8,
|
||||
b2 = (i32x & 0xff0000) >> 16;
|
||||
|
||||
buf32[idx] =
|
||||
(255 << 24) | // alpha
|
||||
(b2 << 16) | // blue
|
||||
(g2 << 8) | // green
|
||||
r2;
|
||||
|
||||
// dithering strength
|
||||
if (this.dithDelta) {
|
||||
var dist = this.colorDist([r1, g1, b1], [r2, g2, b2]);
|
||||
if (dist < this.dithDelta)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Component distance
|
||||
var er = r1 - r2,
|
||||
eg = g1 - g2,
|
||||
eb = b1 - b2;
|
||||
|
||||
for (var i = (dir == 1 ? 0 : ds.length - 1), end = (dir == 1 ? ds.length : 0); i !== end; i += dir) {
|
||||
var x1 = ds[i][1] * dir,
|
||||
y1 = ds[i][2];
|
||||
|
||||
var lni2 = y1 * width;
|
||||
|
||||
if (x1 + x >= 0 && x1 + x < width && y1 + y >= 0 && y1 + y < height) {
|
||||
var d = ds[i][0];
|
||||
var idx2 = idx + (lni2 + x1);
|
||||
|
||||
var r3 = (buf32[idx2] & 0xff),
|
||||
g3 = (buf32[idx2] & 0xff00) >> 8,
|
||||
b3 = (buf32[idx2] & 0xff0000) >> 16;
|
||||
|
||||
var r4 = Math.max(0, Math.min(255, r3 + er * d)),
|
||||
g4 = Math.max(0, Math.min(255, g3 + eg * d)),
|
||||
b4 = Math.max(0, Math.min(255, b3 + eb * d));
|
||||
|
||||
buf32[idx2] =
|
||||
(255 << 24) | // alpha
|
||||
(b4 << 16) | // blue
|
||||
(g4 << 8) | // green
|
||||
r4; // red
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf32;
|
||||
};
|
||||
|
||||
// reduces histogram to palette, remaps & memoizes reduced colors
|
||||
RgbQuant.prototype.buildPal = function buildPal(noSort) {
|
||||
if (this.palLocked || this.idxrgb.length > 0 && this.idxrgb.length <= this.colors) return;
|
||||
|
||||
var histG = this.histogram,
|
||||
sorted = sortedHashKeys(histG, true);
|
||||
|
||||
if (sorted.length == 0)
|
||||
throw "Nothing has been sampled, palette cannot be built.";
|
||||
|
||||
switch (this.method) {
|
||||
case 1:
|
||||
var cols = this.initColors,
|
||||
last = sorted[cols - 1],
|
||||
freq = histG[last];
|
||||
|
||||
var idxi32 = sorted.slice(0, cols);
|
||||
|
||||
// add any cut off colors with same freq as last
|
||||
var pos = cols, len = sorted.length;
|
||||
while (pos < len && histG[sorted[pos]] == freq)
|
||||
idxi32.push(sorted[pos++]);
|
||||
|
||||
// inject min huegroup colors
|
||||
if (this.hueStats)
|
||||
this.hueStats.inject(idxi32);
|
||||
|
||||
break;
|
||||
case 2:
|
||||
var idxi32 = sorted;
|
||||
break;
|
||||
}
|
||||
|
||||
// int32-ify values
|
||||
idxi32 = idxi32.map(function(v){return +v;});
|
||||
|
||||
this.reducePal(idxi32);
|
||||
|
||||
if (!noSort && this.reIndex)
|
||||
this.sortPal();
|
||||
|
||||
// build cache of top histogram colors
|
||||
if (this.useCache)
|
||||
this.cacheHistogram(idxi32);
|
||||
|
||||
this.palLocked = true;
|
||||
};
|
||||
|
||||
RgbQuant.prototype.palette = function palette(tuples, noSort) {
|
||||
this.buildPal(noSort);
|
||||
return tuples ? this.idxrgb : new Uint8Array((new Uint32Array(this.idxi32)).buffer);
|
||||
};
|
||||
|
||||
RgbQuant.prototype.prunePal = function prunePal(keep) {
|
||||
var i32;
|
||||
|
||||
for (var j = 0; j < this.idxrgb.length; j++) {
|
||||
if (!keep[j]) {
|
||||
i32 = this.idxi32[j];
|
||||
this.idxrgb[j] = null;
|
||||
this.idxi32[j] = null;
|
||||
delete this.i32idx[i32];
|
||||
}
|
||||
}
|
||||
|
||||
// compact
|
||||
if (this.reIndex) {
|
||||
var idxrgb = [],
|
||||
idxi32 = [],
|
||||
i32idx = {};
|
||||
|
||||
for (var j = 0, i = 0; j < this.idxrgb.length; j++) {
|
||||
if (this.idxrgb[j]) {
|
||||
i32 = this.idxi32[j];
|
||||
idxrgb[i] = this.idxrgb[j];
|
||||
i32idx[i32] = i;
|
||||
idxi32[i] = i32;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
this.idxrgb = idxrgb;
|
||||
this.idxi32 = idxi32;
|
||||
this.i32idx = i32idx;
|
||||
}
|
||||
};
|
||||
|
||||
// reduces similar colors from an importance-sorted Uint32 rgba array
|
||||
RgbQuant.prototype.reducePal = function reducePal(idxi32) {
|
||||
// if pre-defined palette's length exceeds target
|
||||
if (this.idxrgb.length > this.colors) {
|
||||
// quantize histogram to existing palette
|
||||
var len = idxi32.length, keep = {}, uniques = 0, idx, pruned = false;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
// palette length reached, unset all remaining colors (sparse palette)
|
||||
if (uniques == this.colors && !pruned) {
|
||||
this.prunePal(keep);
|
||||
pruned = true;
|
||||
}
|
||||
|
||||
idx = this.nearestIndex(idxi32[i]);
|
||||
|
||||
if (uniques < this.colors && !keep[idx]) {
|
||||
keep[idx] = true;
|
||||
uniques++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pruned) {
|
||||
this.prunePal(keep);
|
||||
pruned = true;
|
||||
}
|
||||
}
|
||||
// reduce histogram to create initial palette
|
||||
else {
|
||||
// build full rgb palette
|
||||
var idxrgb = idxi32.map(function(i32) {
|
||||
return [
|
||||
(i32 & 0xff),
|
||||
(i32 & 0xff00) >> 8,
|
||||
(i32 & 0xff0000) >> 16,
|
||||
];
|
||||
});
|
||||
|
||||
var len = idxrgb.length,
|
||||
palLen = len,
|
||||
thold = this.initDist;
|
||||
|
||||
// palette already at or below desired length
|
||||
if (palLen > this.colors) {
|
||||
while (palLen > this.colors) {
|
||||
var memDist = [];
|
||||
|
||||
// iterate palette
|
||||
for (var i = 0; i < len; i++) {
|
||||
var pxi = idxrgb[i], i32i = idxi32[i];
|
||||
if (!pxi) continue;
|
||||
|
||||
for (var j = i + 1; j < len; j++) {
|
||||
var pxj = idxrgb[j], i32j = idxi32[j];
|
||||
if (!pxj) continue;
|
||||
|
||||
var dist = this.colorDist(pxi, pxj);
|
||||
|
||||
if (dist < thold) {
|
||||
// store index,rgb,dist
|
||||
memDist.push([j, pxj, i32j, dist]);
|
||||
|
||||
// kill squashed value
|
||||
delete(idxrgb[j]);
|
||||
palLen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// palette reduction pass
|
||||
// console.log("palette length: " + palLen);
|
||||
|
||||
// if palette is still much larger than target, increment by larger initDist
|
||||
thold += (palLen > this.colors * 3) ? this.initDist : this.distIncr;
|
||||
}
|
||||
|
||||
// if palette is over-reduced, re-add removed colors with largest distances from last round
|
||||
if (palLen < this.colors) {
|
||||
// sort descending
|
||||
sort.call(memDist, function(a,b) {
|
||||
return b[3] - a[3];
|
||||
});
|
||||
|
||||
var k = 0;
|
||||
while (palLen < this.colors) {
|
||||
// re-inject rgb into final palette
|
||||
idxrgb[memDist[k][0]] = memDist[k][1];
|
||||
|
||||
palLen++;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var len = idxrgb.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (!idxrgb[i]) continue;
|
||||
|
||||
this.idxrgb.push(idxrgb[i]);
|
||||
this.idxi32.push(idxi32[i]);
|
||||
|
||||
this.i32idx[idxi32[i]] = this.idxi32.length - 1;
|
||||
this.i32rgb[idxi32[i]] = idxrgb[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// global top-population
|
||||
RgbQuant.prototype.colorStats1D = function colorStats1D(buf32) {
|
||||
var histG = this.histogram,
|
||||
num = 0, col,
|
||||
len = buf32.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
col = buf32[i];
|
||||
|
||||
// skip transparent
|
||||
if ((col & 0xff000000) >> 24 == 0) continue;
|
||||
|
||||
// collect hue stats
|
||||
if (this.hueStats)
|
||||
this.hueStats.check(col);
|
||||
|
||||
if (col in histG)
|
||||
histG[col]++;
|
||||
else
|
||||
histG[col] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// population threshold within subregions
|
||||
// FIXME: this can over-reduce (few/no colors same?), need a way to keep
|
||||
// important colors that dont ever reach local thresholds (gradients?)
|
||||
RgbQuant.prototype.colorStats2D = function colorStats2D(buf32, width) {
|
||||
var boxW = this.boxSize[0],
|
||||
boxH = this.boxSize[1],
|
||||
area = boxW * boxH,
|
||||
boxes = makeBoxes(width, buf32.length / width, boxW, boxH),
|
||||
histG = this.histogram,
|
||||
self = this;
|
||||
|
||||
boxes.forEach(function(box) {
|
||||
var effc = Math.max(Math.round((box.w * box.h) / area) * self.boxPxls, 2),
|
||||
histL = {}, col;
|
||||
|
||||
iterBox(box, width, function(i) {
|
||||
col = buf32[i];
|
||||
|
||||
// skip transparent
|
||||
if ((col & 0xff000000) >> 24 == 0) return;
|
||||
|
||||
// collect hue stats
|
||||
if (self.hueStats)
|
||||
self.hueStats.check(col);
|
||||
|
||||
if (col in histG)
|
||||
histG[col]++;
|
||||
else if (col in histL) {
|
||||
if (++histL[col] >= effc)
|
||||
histG[col] = histL[col];
|
||||
}
|
||||
else
|
||||
histL[col] = 1;
|
||||
});
|
||||
});
|
||||
|
||||
if (this.hueStats)
|
||||
this.hueStats.inject(histG);
|
||||
};
|
||||
|
||||
// TODO: group very low lum and very high lum colors
|
||||
// TODO: pass custom sort order
|
||||
RgbQuant.prototype.sortPal = function sortPal() {
|
||||
var self = this;
|
||||
|
||||
this.idxi32.sort(function(a,b) {
|
||||
var idxA = self.i32idx[a],
|
||||
idxB = self.i32idx[b],
|
||||
rgbA = self.idxrgb[idxA],
|
||||
rgbB = self.idxrgb[idxB];
|
||||
|
||||
var hslA = rgb2hsl(rgbA[0],rgbA[1],rgbA[2]),
|
||||
hslB = rgb2hsl(rgbB[0],rgbB[1],rgbB[2]);
|
||||
|
||||
// sort all grays + whites together
|
||||
var hueA = (rgbA[0] == rgbA[1] && rgbA[1] == rgbA[2]) ? -1 : hueGroup(hslA.h, self.hueGroups);
|
||||
var hueB = (rgbB[0] == rgbB[1] && rgbB[1] == rgbB[2]) ? -1 : hueGroup(hslB.h, self.hueGroups);
|
||||
|
||||
var hueDiff = hueB - hueA;
|
||||
if (hueDiff) return -hueDiff;
|
||||
|
||||
var lumDiff = lumGroup(+hslB.l.toFixed(2)) - lumGroup(+hslA.l.toFixed(2));
|
||||
if (lumDiff) return -lumDiff;
|
||||
|
||||
var satDiff = satGroup(+hslB.s.toFixed(2)) - satGroup(+hslA.s.toFixed(2));
|
||||
if (satDiff) return -satDiff;
|
||||
});
|
||||
|
||||
// sync idxrgb & i32idx
|
||||
this.idxi32.forEach(function(i32, i) {
|
||||
self.idxrgb[i] = self.i32rgb[i32];
|
||||
self.i32idx[i32] = i;
|
||||
});
|
||||
};
|
||||
|
||||
// TOTRY: use HUSL - http://boronine.com/husl/
|
||||
RgbQuant.prototype.nearestColor = function nearestColor(i32) {
|
||||
var idx = this.nearestIndex(i32);
|
||||
return idx === null ? 0 : this.idxi32[idx];
|
||||
};
|
||||
|
||||
// TOTRY: use HUSL - http://boronine.com/husl/
|
||||
RgbQuant.prototype.nearestIndex = function nearestIndex(i32) {
|
||||
// alpha 0 returns null index
|
||||
if ((i32 & 0xff000000) >> 24 == 0)
|
||||
return null;
|
||||
|
||||
if (this.useCache && (""+i32) in this.i32idx)
|
||||
return this.i32idx[i32];
|
||||
|
||||
var min = 1000,
|
||||
idx,
|
||||
rgb = [
|
||||
(i32 & 0xff),
|
||||
(i32 & 0xff00) >> 8,
|
||||
(i32 & 0xff0000) >> 16,
|
||||
],
|
||||
len = this.idxrgb.length;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (!this.idxrgb[i]) continue; // sparse palettes
|
||||
|
||||
var dist = this.colorDist(rgb, this.idxrgb[i]);
|
||||
|
||||
if (dist < min) {
|
||||
min = dist;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
};
|
||||
|
||||
RgbQuant.prototype.cacheHistogram = function cacheHistogram(idxi32) {
|
||||
for (var i = 0, i32 = idxi32[i]; i < idxi32.length && this.histogram[i32] >= this.cacheFreq; i32 = idxi32[i++])
|
||||
this.i32idx[i32] = this.nearestIndex(i32);
|
||||
};
|
||||
|
||||
function HueStats(numGroups, minCols) {
|
||||
this.numGroups = numGroups;
|
||||
this.minCols = minCols;
|
||||
this.stats = {};
|
||||
|
||||
for (var i = -1; i < numGroups; i++)
|
||||
this.stats[i] = {num: 0, cols: []};
|
||||
|
||||
this.groupsFull = 0;
|
||||
}
|
||||
|
||||
HueStats.prototype.check = function checkHue(i32) {
|
||||
if (this.groupsFull == this.numGroups + 1)
|
||||
this.check = function() {return;};
|
||||
|
||||
var r = (i32 & 0xff),
|
||||
g = (i32 & 0xff00) >> 8,
|
||||
b = (i32 & 0xff0000) >> 16,
|
||||
hg = (r == g && g == b) ? -1 : hueGroup(rgb2hsl(r,g,b).h, this.numGroups),
|
||||
gr = this.stats[hg],
|
||||
min = this.minCols;
|
||||
|
||||
gr.num++;
|
||||
|
||||
if (gr.num > min)
|
||||
return;
|
||||
if (gr.num == min)
|
||||
this.groupsFull++;
|
||||
|
||||
if (gr.num <= min)
|
||||
this.stats[hg].cols.push(i32);
|
||||
};
|
||||
|
||||
HueStats.prototype.inject = function injectHues(histG) {
|
||||
for (var i = -1; i < this.numGroups; i++) {
|
||||
if (this.stats[i].num <= this.minCols) {
|
||||
switch (typeOf(histG)) {
|
||||
case "Array":
|
||||
this.stats[i].cols.forEach(function(col){
|
||||
if (histG.indexOf(col) == -1)
|
||||
histG.push(col);
|
||||
});
|
||||
break;
|
||||
case "Object":
|
||||
this.stats[i].cols.forEach(function(col){
|
||||
if (!histG[col])
|
||||
histG[col] = 1;
|
||||
else
|
||||
histG[col]++;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Rec. 709 (sRGB) luma coef
|
||||
var Pr = .2126,
|
||||
Pg = .7152,
|
||||
Pb = .0722;
|
||||
|
||||
// http://alienryderflex.com/hsp.html
|
||||
function rgb2lum(r,g,b) {
|
||||
return Math.sqrt(
|
||||
Pr * r*r +
|
||||
Pg * g*g +
|
||||
Pb * b*b
|
||||
);
|
||||
}
|
||||
|
||||
var rd = 255,
|
||||
gd = 255,
|
||||
bd = 255;
|
||||
|
||||
var euclMax = Math.sqrt(Pr*rd*rd + Pg*gd*gd + Pb*bd*bd);
|
||||
// perceptual Euclidean color distance
|
||||
function distEuclidean(rgb0, rgb1) {
|
||||
var rd = rgb1[0]-rgb0[0],
|
||||
gd = rgb1[1]-rgb0[1],
|
||||
bd = rgb1[2]-rgb0[2];
|
||||
|
||||
return Math.sqrt(Pr*rd*rd + Pg*gd*gd + Pb*bd*bd) / euclMax;
|
||||
}
|
||||
|
||||
var manhMax = Pr*rd + Pg*gd + Pb*bd;
|
||||
// perceptual Manhattan color distance
|
||||
function distManhattan(rgb0, rgb1) {
|
||||
var rd = Math.abs(rgb1[0]-rgb0[0]),
|
||||
gd = Math.abs(rgb1[1]-rgb0[1]),
|
||||
bd = Math.abs(rgb1[2]-rgb0[2]);
|
||||
|
||||
return (Pr*rd + Pg*gd + Pb*bd) / manhMax;
|
||||
}
|
||||
|
||||
// http://rgb2hsl.nichabi.com/javascript-function.php
|
||||
function rgb2hsl(r, g, b) {
|
||||
var max, min, h, s, l, d;
|
||||
r /= 255;
|
||||
g /= 255;
|
||||
b /= 255;
|
||||
max = Math.max(r, g, b);
|
||||
min = Math.min(r, g, b);
|
||||
l = (max + min) / 2;
|
||||
if (max == min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
// h = Math.floor(h * 360)
|
||||
// s = Math.floor(s * 100)
|
||||
// l = Math.floor(l * 100)
|
||||
return {
|
||||
h: h,
|
||||
s: s,
|
||||
l: rgb2lum(r,g,b),
|
||||
};
|
||||
}
|
||||
|
||||
function hueGroup(hue, segs) {
|
||||
var seg = 1/segs,
|
||||
haf = seg/2;
|
||||
|
||||
if (hue >= 1 - haf || hue <= haf)
|
||||
return 0;
|
||||
|
||||
for (var i = 1; i < segs; i++) {
|
||||
var mid = i*seg;
|
||||
if (hue >= mid - haf && hue <= mid + haf)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
function satGroup(sat) {
|
||||
return sat;
|
||||
}
|
||||
|
||||
function lumGroup(lum) {
|
||||
return lum;
|
||||
}
|
||||
|
||||
function typeOf(val) {
|
||||
return Object.prototype.toString.call(val).slice(8,-1);
|
||||
}
|
||||
|
||||
var sort = isArrSortStable() ? Array.prototype.sort : stableSort;
|
||||
|
||||
// must be used via stableSort.call(arr, fn)
|
||||
function stableSort(fn) {
|
||||
var type = typeOf(this[0]);
|
||||
|
||||
if (type == "Number" || type == "String") {
|
||||
var ord = {}, len = this.length, val;
|
||||
|
||||
for (var i = 0; i < len; i++) {
|
||||
val = this[i];
|
||||
if (ord[val] || ord[val] === 0) continue;
|
||||
ord[val] = i;
|
||||
}
|
||||
|
||||
return this.sort(function(a,b) {
|
||||
return fn(a,b) || ord[a] - ord[b];
|
||||
});
|
||||
}
|
||||
else {
|
||||
var ord = this.map(function(v){return v});
|
||||
|
||||
return this.sort(function(a,b) {
|
||||
return fn(a,b) || ord.indexOf(a) - ord.indexOf(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// test if js engine's Array#sort implementation is stable
|
||||
function isArrSortStable() {
|
||||
var str = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
return "xyzvwtursopqmnklhijfgdeabc" == str.split("").sort(function(a,b) {
|
||||
return ~~(str.indexOf(b)/2.3) - ~~(str.indexOf(a)/2.3);
|
||||
}).join("");
|
||||
}
|
||||
|
||||
// returns uniform pixel data from various img
|
||||
// TODO?: if array is passed, createimagedata, createlement canvas? take a pxlen?
|
||||
function getImageData(img, width) {
|
||||
var can, ctx, imgd, buf8, buf32, height;
|
||||
|
||||
switch (typeOf(img)) {
|
||||
case "HTMLImageElement":
|
||||
can = document.createElement("canvas");
|
||||
can.width = img.naturalWidth;
|
||||
can.height = img.naturalHeight;
|
||||
ctx = can.getContext("2d");
|
||||
ctx.drawImage(img,0,0);
|
||||
case "Canvas":
|
||||
case "HTMLCanvasElement":
|
||||
can = can || img;
|
||||
ctx = ctx || can.getContext("2d");
|
||||
case "CanvasRenderingContext2D":
|
||||
ctx = ctx || img;
|
||||
can = can || ctx.canvas;
|
||||
imgd = ctx.getImageData(0, 0, can.width, can.height);
|
||||
case "ImageData":
|
||||
imgd = imgd || img;
|
||||
width = imgd.width;
|
||||
if (typeOf(imgd.data) == "CanvasPixelArray")
|
||||
buf8 = new Uint8Array(imgd.data);
|
||||
else
|
||||
buf8 = imgd.data;
|
||||
case "Array":
|
||||
case "CanvasPixelArray":
|
||||
buf8 = buf8 || new Uint8Array(img);
|
||||
case "Uint8Array":
|
||||
case "Uint8ClampedArray":
|
||||
buf8 = buf8 || img;
|
||||
buf32 = new Uint32Array(buf8.buffer);
|
||||
case "Uint32Array":
|
||||
buf32 = buf32 || img;
|
||||
buf8 = buf8 || new Uint8Array(buf32.buffer);
|
||||
width = width || buf32.length;
|
||||
height = buf32.length / width;
|
||||
}
|
||||
|
||||
return {
|
||||
can: can,
|
||||
ctx: ctx,
|
||||
imgd: imgd,
|
||||
buf8: buf8,
|
||||
buf32: buf32,
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
}
|
||||
|
||||
// partitions a rect of wid x hgt into
|
||||
// array of bboxes of w0 x h0 (or less)
|
||||
function makeBoxes(wid, hgt, w0, h0) {
|
||||
var wnum = ~~(wid/w0), wrem = wid%w0,
|
||||
hnum = ~~(hgt/h0), hrem = hgt%h0,
|
||||
xend = wid-wrem, yend = hgt-hrem;
|
||||
|
||||
var bxs = [];
|
||||
for (var y = 0; y < hgt; y += h0)
|
||||
for (var x = 0; x < wid; x += w0)
|
||||
bxs.push({x:x, y:y, w:(x==xend?wrem:w0), h:(y==yend?hrem:h0)});
|
||||
|
||||
return bxs;
|
||||
}
|
||||
|
||||
// iterates @bbox within a parent rect of width @wid; calls @fn, passing index within parent
|
||||
function iterBox(bbox, wid, fn) {
|
||||
var b = bbox,
|
||||
i0 = b.y * wid + b.x,
|
||||
i1 = (b.y + b.h - 1) * wid + (b.x + b.w - 1),
|
||||
cnt = 0, incr = wid - b.w + 1, i = i0;
|
||||
|
||||
do {
|
||||
fn.call(this, i);
|
||||
i += (++cnt % b.w == 0) ? incr : 1;
|
||||
} while (i <= i1);
|
||||
}
|
||||
|
||||
// returns array of hash keys sorted by their values
|
||||
function sortedHashKeys(obj, desc) {
|
||||
var keys = [];
|
||||
|
||||
for (var key in obj)
|
||||
keys.push(key);
|
||||
|
||||
return sort.call(keys, function(a,b) {
|
||||
return desc ? obj[b] - obj[a] : obj[a] - obj[b];
|
||||
});
|
||||
}
|
||||
|
||||
// expose
|
||||
this.RgbQuant = RgbQuant;
|
||||
|
||||
// expose to commonJS
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = RgbQuant;
|
||||
}
|
||||
|
||||
}).call(this);
|
||||
2
libs/rgbquant.min.js
vendored
Normal file
2
libs/rgbquant.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
libs/seedrandom.min.js
vendored
1
libs/seedrandom.min.js
vendored
|
|
@ -1 +0,0 @@
|
|||
!function(a,b){var l,c=eval("this"),d=256,g="random",h=b.pow(d,6),i=b.pow(2,52),j=2*i,k=d-1;function m(r,t,e){var u=[],f=q(function n(r,t){var e,o=[],i=typeof r;if(t&&"object"==i)for(e in r)try{o.push(n(r[e],t-1))}catch(n){}return o.length?o:"string"==i?r:r+"\0"}((t=1==t?{entropy:!0}:t||{}).entropy?[r,s(a)]:null==r?function(){try{var n;return l&&(n=l.randomBytes)?n=n(d):(n=new Uint8Array(d),(c.crypto||c.msCrypto).getRandomValues(n)),s(n)}catch(n){var r=c.navigator,t=r&&r.plugins;return[+new Date,c,t,c.screen,s(a)]}}():r,3),u),p=new n(u),m=function(){for(var n=p.g(6),r=h,t=0;n<i;)n=(n+t)*d,r*=d,t=p.g(1);for(;j<=n;)n/=2,r/=2,t>>>=1;return(n+t)/r};return m.int32=function(){return 0|p.g(4)},m.quick=function(){return p.g(4)/4294967296},m.double=m,q(s(p.S),a),(t.pass||e||function(n,r,t,e){return e&&(e.S&&o(e,p),n.state=function(){return o(p,{})}),t?(b[g]=n,r):n})(m,f,"global"in t?t.global:this==b,t.state)}function n(n){var r,t=n.length,u=this,e=0,o=u.i=u.j=0,i=u.S=[];for(t||(n=[t++]);e<d;)i[e]=e++;for(e=0;e<d;e++)i[e]=i[o=k&o+n[e%t]+(r=i[e])],i[o]=r;(u.g=function(n){for(var r,t=0,e=u.i,o=u.j,i=u.S;n--;)r=i[e=k&e+1],t=t*d+i[k&(i[e]=i[o=k&o+r])+(i[o]=r)];return u.i=e,u.j=o,t})(d)}function o(n,r){return r.i=n.i,r.j=n.j,r.S=n.S.slice(),r}function q(n,r){for(var t,e=n+"",o=0;o<e.length;)r[k&o]=k&(t^=19*r[k&o])+e.charCodeAt(o++);return s(r)}function s(n){return String.fromCharCode.apply(0,n)}if(b["seed"+g]=m,q(b.random(),a),"object"==typeof module&&module.exports){module.exports=m;try{l=require("crypto")}catch(n){}}else"function"==typeof define&&define.amd&&define(function(){return m})}([],Math);
|
||||
4
main.js
4
main.js
|
|
@ -602,7 +602,7 @@ function generateSeed() {
|
|||
else if (optionsSeed.value && optionsSeed.value != seed) seed = optionsSeed.value;
|
||||
else seed = Math.floor(Math.random() * 1e9).toString();
|
||||
optionsSeed.value = seed;
|
||||
Math.seedrandom(seed);
|
||||
Math.random = aleaPRNG(seed);
|
||||
}
|
||||
|
||||
// Place points to calculate Voronoi diagram
|
||||
|
|
@ -636,7 +636,7 @@ function calculateVoronoi(graph, points) {
|
|||
// Mark features (ocean, lakes, islands)
|
||||
function markFeatures() {
|
||||
TIME && console.time("markFeatures");
|
||||
Math.seedrandom(seed); // restart Math.random() to get the same result on heightmap edit in Erase mode
|
||||
Math.random = aleaPRNG(seed); // restart Math.random() to get the same result on heightmap edit in Erase mode
|
||||
const cells = grid.cells, heights = grid.cells.h;
|
||||
cells.f = new Uint16Array(cells.i.length); // cell feature number
|
||||
cells.t = new Int8Array(cells.i.length); // cell type: 1 = land coast; -1 = water near coast;
|
||||
|
|
|
|||
|
|
@ -910,7 +910,7 @@
|
|||
const generateProvinces = function(regenerate) {
|
||||
TIME && console.time("generateProvinces");
|
||||
const localSeed = regenerate ? Math.floor(Math.random() * 1e9).toString() : seed;
|
||||
Math.seedrandom(localSeed);
|
||||
Math.random = aleaPRNG(localSeed);
|
||||
|
||||
const cells = pack.cells, states = pack.states, burgs = pack.burgs;
|
||||
const provinces = pack.provinces = [0];
|
||||
|
|
@ -928,7 +928,7 @@
|
|||
}
|
||||
|
||||
// generate provinces for a selected burgs
|
||||
Math.seedrandom(localSeed);
|
||||
Math.random = aleaPRNG(localSeed);
|
||||
states.forEach(s => {
|
||||
s.provinces = [];
|
||||
if (!s.i || s.removed) return;
|
||||
|
|
|
|||
|
|
@ -388,14 +388,9 @@
|
|||
if (cells.s[e] > 0) cells.culture[e] = c; // assign culture to populated cell
|
||||
cost[e] = totalCost;
|
||||
queue.queue({e, p:totalCost, c});
|
||||
|
||||
//debug.append("text").attr("x", (cells.p[n][0]+cells.p[e][0])/2 - 1).attr("y", (cells.p[n][1]+cells.p[e][1])/2 - 1).text(rn(totalCost-p)).attr("font-size", .8);
|
||||
//const points = [cells.p[n][0], cells.p[n][1], (cells.p[n][0]+cells.p[e][0])/2, (cells.p[n][1]+cells.p[e][1])/2, cells.p[e][0], cells.p[e][1]];
|
||||
//debug.append("polyline").attr("points", points.toString()).attr("marker-mid", "url(#arrow)").attr("opacity", .6);
|
||||
}
|
||||
});
|
||||
}
|
||||
//debug.selectAll(".text").data(cost).enter().append("text").attr("x", (d, e) => cells.p[e][0]-1).attr("y", (d, e) => cells.p[e][1]-1).text(d => d ? rn(d) : "").attr("font-size", 2);
|
||||
TIME && console.timeEnd('expandCultures');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
const generate = function(changeHeights = true) {
|
||||
TIME && console.time('generateRivers');
|
||||
Math.seedrandom(seed);
|
||||
Math.random = aleaPRNG(seed);
|
||||
const cells = pack.cells, p = cells.p, features = pack.features;
|
||||
|
||||
// build distance field in cells from water (cells.t)
|
||||
|
|
@ -250,7 +250,7 @@
|
|||
|
||||
const specify = function() {
|
||||
if (!pack.rivers.length) return;
|
||||
Math.seedrandom(seed);
|
||||
Math.random = aleaPRNG(seed);
|
||||
const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)];
|
||||
const smallType = {"Creek":9, "River":3, "Brook":3, "Stream":1}; // weighted small river types
|
||||
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ function toggleIce(event) {
|
|||
function drawIce() {
|
||||
const cells = grid.cells, vertices = grid.vertices, n = cells.i.length, temp = cells.temp, h = cells.h;
|
||||
const used = new Uint8Array(cells.i.length);
|
||||
Math.seedrandom(seed);
|
||||
Math.random = aleaPRNG(seed);
|
||||
|
||||
const shieldMin = -6; // max temp to form ice shield (glacier)
|
||||
const icebergMax = 2; // max temp to form an iceberg
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ function applyStoredOptions() {
|
|||
|
||||
// randomize options if randomization is allowed (not locked or options='default')
|
||||
function randomizeOptions() {
|
||||
Math.seedrandom(seed); // reset seed to initial one
|
||||
Math.random = aleaPRNG(seed); // reset seed to initial one
|
||||
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
|
||||
|
||||
// 'Options' settings
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ function recalculatePopulation() {
|
|||
}
|
||||
|
||||
function regenerateStates() {
|
||||
Math.seedrandom(Math.floor(Math.random() * 1e9)); // new random seed
|
||||
const localSeed = Math.floor(Math.random() * 1e9); // new random seed
|
||||
Math.random = aleaPRNG(localSeed);
|
||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||
if (!burgs.length) {
|
||||
tip("No burgs to generate states. Please create burgs first", false, "error");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue