initial commit
commit
e3677f6f65
|
@ -0,0 +1,7 @@
|
|||
# Vein 3d
|
||||
|
||||
Generative art experiment based on vein generation by Bleeptrack.
|
||||
|
||||
It's taking the generated veins (first panel), generates a bump map, albedo and specular map and maps it to a disc rotating in space. The goal was to loosely mimic the look of a leaf. Veins are randomized on every reload
|
||||
|
||||

|
Binary file not shown.
After Width: | Height: | Size: 388 KiB |
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
Copyright 2013 Adobe Systems Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
Gradient Maps support
|
||||
Author: Alan Greenblatt (blatt@adobe.com, @agreenblatt, blattchat.com)
|
||||
*/
|
||||
|
||||
window.GradientMaps = function(scope) {
|
||||
function GradientMaps() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
GradientMaps.prototype = {
|
||||
init: function() {
|
||||
},
|
||||
|
||||
calcStopsArray: function(stopsDecl) {
|
||||
/*
|
||||
* Each stop consists of a color and an optional percentage or length
|
||||
* stops: <color-stop> [, <color-stop>]
|
||||
* <color-stop>: color [ <percentage> | <length> ]?
|
||||
*
|
||||
* If the first color-stop does not have a length or percentage, it defaults to 0%
|
||||
* If the last color-stop does not have a length or percentage, it defaults to 100%
|
||||
* If a color-stop, other than the first or last, does not have a length or percentage, it is assigned the position half way between the previous and the next stop.
|
||||
* If a color-stop, other than the first or last, has a specified position less than the previous stop, its position is changed to be equal to the largest specified position of any prior color-stop.
|
||||
*/
|
||||
|
||||
var matches = stopsDecl.match(/(((rgb|hsl)a?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*0?\.?\d+)?\)|\w+|#[0-9a-fA-F]{1,6})(\s+(0?\.\d+|\d{1,3}%))?)/g);
|
||||
|
||||
var stopsDeclArr = stopsDecl.split(',');
|
||||
var stops = [];
|
||||
|
||||
matches.forEach(function(colorStop) {
|
||||
var colorStopMatches = colorStop.match(/(?:((rgb|hsl)a?\(\d{1,3},\s*\d{1,3},\s*\d{1,3}(?:,\s*0?\.?\d+)?\)|\w+|#[0-9a-fA-F]{1,6})(\s+(?:0?\.\d+|\d{1,3}%))?)/);
|
||||
if (colorStopMatches && colorStopMatches.length >= 4) {
|
||||
posMatch = colorStopMatches[3];
|
||||
stops.push({
|
||||
color: parseCSSColor(colorStopMatches[1]),
|
||||
pos: posMatch ? parse_css_float(posMatch) * 100 : null
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Need to calculate the positions where they aren't specified.
|
||||
* In the case of the first and last stop, we may even have to add a new stop.
|
||||
*
|
||||
* Go through the array of stops, finding ones where the position is not specified.
|
||||
* Then, find the next specified position or terminate on the last stop.
|
||||
* Finally, evenly distribute the unspecified positions, with the first stop at 0
|
||||
* and the last stop at 100.
|
||||
*/
|
||||
|
||||
if (stops.length >= 1) {
|
||||
// If the first stop's position is not specified, set it to 0.
|
||||
var stop = stops[0];
|
||||
if (!stop.pos)
|
||||
stop.pos = 0;
|
||||
else
|
||||
stop.pos = Math.min(100, Math.max(0, stop.pos));
|
||||
|
||||
var currentPos = stop.pos;
|
||||
|
||||
// If the last stop's position is not specified, set it to 100.
|
||||
stop = stops[stops.length-1];
|
||||
if (!stop.pos)
|
||||
stop.pos = 100;
|
||||
else
|
||||
stop.pos = Math.min(100, Math.max(0, stop.pos));
|
||||
|
||||
// Make sure that all positions are in ascending order
|
||||
for (var i = 1; i < stops.length-1; i++) {
|
||||
stop = stops[i];
|
||||
if (stop.pos && stop.pos < currentPos)
|
||||
stop.pos = currentPos;
|
||||
if (stop.pos > 100) stop.pos = 100;
|
||||
currentPos = stop.pos;
|
||||
}
|
||||
|
||||
// Find any runs of unpositioned stops and calculate them
|
||||
var i = 1;
|
||||
while (i < (stops.length-1)) {
|
||||
if (!stops[i].pos) {
|
||||
// Find the next positioned stop. You'll always have at least the
|
||||
// last stop at 100.
|
||||
for (var j = i+1; j < stops.length; j++) {
|
||||
if (stops[j].pos)
|
||||
break;
|
||||
}
|
||||
|
||||
var startPos = stops[i-1].pos;
|
||||
var endPos = stops[j].pos;
|
||||
var nStops = j - 1 + 1;
|
||||
|
||||
var delta = Math.round((endPos - startPos) / nStops);
|
||||
while (i < j) {
|
||||
stops[i].pos = stops[i-1].pos + delta;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (stops[0].pos != 0) {
|
||||
stops.unshift({
|
||||
color: stops[0].color,
|
||||
pos: 0
|
||||
});
|
||||
}
|
||||
|
||||
if (stops[stops.length-1].pos != 100) {
|
||||
stops.push({
|
||||
color: stops[stops.length-1].color,
|
||||
pos: 100
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return stops;
|
||||
},
|
||||
|
||||
findMatchingDistributedNSegs: function(stops) {
|
||||
var maxNumSegs = 100;
|
||||
var matched = false;
|
||||
for (var nSegs = 1; !matched && nSegs <= maxNumSegs; nSegs++) {
|
||||
var segSize = maxNumSegs / nSegs;
|
||||
matched = true;
|
||||
for (var i = 1; i < stops.length-1; i++) {
|
||||
var pos = stops[i].pos;
|
||||
if (pos < segSize) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
var rem = pos % segSize;
|
||||
var maxDiff = 1.0;
|
||||
if (!(rem < maxDiff || (segSize - rem) < maxDiff)) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matched)
|
||||
return nSegs;
|
||||
}
|
||||
|
||||
return nSegs;
|
||||
},
|
||||
|
||||
calcDistributedColors: function(stops, nSegs) {
|
||||
var colors = [stops[0].color];
|
||||
|
||||
var segSize = 100 / nSegs;
|
||||
for (var i = 1; i < stops.length-1; i++) {
|
||||
var stop = stops[i];
|
||||
var n = Math.round(stop.pos / segSize);
|
||||
colors[n] = stop.color;
|
||||
}
|
||||
|
||||
colors[nSegs] = stops[stops.length-1].color;
|
||||
|
||||
var i = 1;
|
||||
while (i < colors.length) {
|
||||
if (!colors[i]) {
|
||||
for (var j = i+1; j < colors.length; j++) {
|
||||
if (colors[j])
|
||||
break;
|
||||
}
|
||||
|
||||
// Need to evenly distribute colors stops from svgStop[i-1] to svgStop[j]
|
||||
|
||||
var startColor = colors[i-1];
|
||||
var r = startColor[0];
|
||||
var g = startColor[1];
|
||||
var b = startColor[2];
|
||||
var a = startColor[3];
|
||||
|
||||
var endColor = colors[j];
|
||||
|
||||
var nSegs = j - i + 1;
|
||||
var dr = (endColor[0] - r) / nSegs;
|
||||
var dg = (endColor[1] - g) / nSegs;
|
||||
var db = (endColor[2] - b) / nSegs;
|
||||
var da = (endColor[3] - a) / nSegs;
|
||||
|
||||
while (i < j) {
|
||||
r += dr;
|
||||
g += dg;
|
||||
b += db;
|
||||
a += da;
|
||||
colors[i] = [r, g, b, a];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return colors;
|
||||
},
|
||||
|
||||
addElement: function(doc, parent, tagname, ns, attributes) {
|
||||
var elem = ns ? doc.createElementNS(ns, tagname) : doc.createElement(tagname);
|
||||
if (attributes) {
|
||||
Object.keys(attributes).forEach(function(key, index, keys) {
|
||||
elem.setAttribute(key, attributes[key]);
|
||||
});
|
||||
//elem.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
|
||||
if (parent) parent.appendChild(elem);
|
||||
return elem;
|
||||
},
|
||||
|
||||
addSVGComponentTransferFilter: function(elem, colors) {
|
||||
var filter = null;
|
||||
var svg = null;
|
||||
var svgns = 'http://www.w3.org/2000/svg';
|
||||
var filterID = elem.getAttribute('data-gradientmap-filter');
|
||||
|
||||
var svgIsNew = false;
|
||||
|
||||
var doc = elem.ownerDocument;
|
||||
|
||||
if (filterID) {
|
||||
filter = doc.getElementById(filterID);
|
||||
if (filter) {
|
||||
// Remove old component transfer function
|
||||
var componentTransfers = filter.getElementsByTagNameNS(svgns, 'feComponentTransfer');
|
||||
if (componentTransfers) {
|
||||
for (var i = componentTransfers.length-1; i >= 0; --i)
|
||||
filter.removeChild(componentTransfers[i]);
|
||||
|
||||
svg = filter.parentElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The last thing to be set previously is 'svg'. If that is still null, that will handle any errors
|
||||
if (!svg) {
|
||||
var svg = this.addElement(doc, null, 'svg', svgns, {
|
||||
'version': '1.1',
|
||||
'width': 0,
|
||||
'height': 0
|
||||
});
|
||||
|
||||
filterID = 'filter-' + (new Date().getTime());
|
||||
filter = this.addElement(doc, svg, 'filter', svgns, {'id': filterID});
|
||||
elem.setAttribute('data-gradientmap-filter', filterID);
|
||||
|
||||
// First, apply a color matrix to turn the source into a grayscale
|
||||
var colorMatrix = this.addElement(doc, filter, 'feColorMatrix', svgns, {
|
||||
'type': 'matrix',
|
||||
'values': '0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0',
|
||||
'result': 'gray'
|
||||
});
|
||||
|
||||
svgIsNew = true;
|
||||
}
|
||||
|
||||
// Now apply a component transfer to remap the colors
|
||||
var componentTransfer = this.addElement(doc, filter, 'feComponentTransfer', svgns, {'color-interpolation-filters': 'sRGB'});
|
||||
|
||||
var redTableValues = "";
|
||||
var greenTableValues = "";
|
||||
var blueTableValues = "";
|
||||
var alphaTableValues = "";
|
||||
|
||||
colors.forEach(function(color, index, colors) {
|
||||
redTableValues += (color[0] / 255.0 + " ");
|
||||
greenTableValues += (color[1] / 255.0 + " ");
|
||||
blueTableValues += (color[2] / 255.0 + " ");
|
||||
alphaTableValues += (color[3] + " ");
|
||||
});
|
||||
|
||||
this.addElement(doc, componentTransfer, 'feFuncR', svgns, {'type': 'table', 'tableValues': redTableValues.trim()});
|
||||
this.addElement(doc, componentTransfer, 'feFuncG', svgns, {'type': 'table', 'tableValues': greenTableValues.trim()});
|
||||
this.addElement(doc, componentTransfer, 'feFuncB', svgns, {'type': 'table', 'tableValues': blueTableValues.trim()});
|
||||
this.addElement(doc, componentTransfer, 'feFuncA', svgns, {'type': 'table', 'tableValues': alphaTableValues.trim()});
|
||||
|
||||
var isIE = this.isIE();
|
||||
|
||||
var filterDecl = 'url(#' + filterID + ')';
|
||||
|
||||
if (!isIE) {
|
||||
elem.style['-webkit-filter'] = filterDecl;
|
||||
elem.style['filter'] = filterDecl;
|
||||
}
|
||||
|
||||
if (svgIsNew) {
|
||||
elem.parentElement.insertBefore(svg, elem);
|
||||
|
||||
if (this.isIE()) {
|
||||
var rect = elem.getBoundingClientRect();
|
||||
this.addElement(doc, svg, 'image', svgns, {
|
||||
'width': elem.width,
|
||||
'height': elem.height,
|
||||
'href': elem.src,
|
||||
'filter': filterDecl
|
||||
});
|
||||
|
||||
svg.setAttribute('width', elem.width);
|
||||
svg.setAttribute('height', elem.height);
|
||||
svg.setAttribute('viewbox', '0 0 '+ elem.width +' '+ elem.height);
|
||||
|
||||
elem.style._display = elem.style.display;
|
||||
elem.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
//elem.setAttribute('style', '-webkit-filter: url(#' + filterID + '); filter: url(#' + filterID + ')');
|
||||
},
|
||||
|
||||
applyGradientMap: function(elem, gradient) {
|
||||
var stops = this.calcStopsArray(gradient);
|
||||
var nSegs = this.findMatchingDistributedNSegs(stops);
|
||||
var colors = this.calcDistributedColors(stops, nSegs);
|
||||
|
||||
this.addSVGComponentTransferFilter(elem, colors);
|
||||
},
|
||||
|
||||
removeGradientMap: function(elem) {
|
||||
var filterID = elem.getAttribute('data-gradientmap-filter');
|
||||
if (filterID) {
|
||||
var doc = elem.ownerDocument;
|
||||
var filter = doc.getElementById(filterID);
|
||||
if (filter) {
|
||||
var svg = filter.parentElement;
|
||||
svg.removeChild(filter);
|
||||
}
|
||||
|
||||
var isIE = this.isIE();
|
||||
if (isIE) {
|
||||
var image = svg.ownerDocument.querySelector('image');
|
||||
if (image) {
|
||||
svg.removeChild(image);
|
||||
}
|
||||
}
|
||||
|
||||
if (svg.childNodes.length <= 0) {
|
||||
var parent = svg.parentElement;
|
||||
parent.removeChild(svg);
|
||||
}
|
||||
|
||||
|
||||
elem.removeAttribute('data-gradientmap-filter');
|
||||
elem.style['-webkit-filter'] = '';
|
||||
elem.style['filter'] = '';
|
||||
elem.style.display = '';
|
||||
}
|
||||
},
|
||||
|
||||
isIE: function() {
|
||||
var ua = window.navigator.userAgent;
|
||||
return ua.indexOf('MSIE ') > -1
|
||||
|| ua.indexOf('Trident/') > -1
|
||||
|| ua.indexOf('Edge/') > -1;
|
||||
}
|
||||
}
|
||||
|
||||
return new GradientMaps();
|
||||
}(window);
|
||||
|
||||
// (c) Dean McNamee <dean@gmail.com>, 2012.
|
||||
//
|
||||
// https://github.com/deanm/css-color-parser-js
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
// http://www.w3.org/TR/css3-color/
|
||||
var kCSSColorTable = {
|
||||
"transparent": [0,0,0,0], "aliceblue": [240,248,255,1],
|
||||
"antiquewhite": [250,235,215,1], "aqua": [0,255,255,1],
|
||||
"aquamarine": [127,255,212,1], "azure": [240,255,255,1],
|
||||
"beige": [245,245,220,1], "bisque": [255,228,196,1],
|
||||
"black": [0,0,0,1], "blanchedalmond": [255,235,205,1],
|
||||
"blue": [0,0,255,1], "blueviolet": [138,43,226,1],
|
||||
"brown": [165,42,42,1], "burlywood": [222,184,135,1],
|
||||
"cadetblue": [95,158,160,1], "chartreuse": [127,255,0,1],
|
||||
"chocolate": [210,105,30,1], "coral": [255,127,80,1],
|
||||
"cornflowerblue": [100,149,237,1], "cornsilk": [255,248,220,1],
|
||||
"crimson": [220,20,60,1], "cyan": [0,255,255,1],
|
||||
"darkblue": [0,0,139,1], "darkcyan": [0,139,139,1],
|
||||
"darkgoldenrod": [184,134,11,1], "darkgray": [169,169,169,1],
|
||||
"darkgreen": [0,100,0,1], "darkgrey": [169,169,169,1],
|
||||
"darkkhaki": [189,183,107,1], "darkmagenta": [139,0,139,1],
|
||||
"darkolivegreen": [85,107,47,1], "darkorange": [255,140,0,1],
|
||||
"darkorchid": [153,50,204,1], "darkred": [139,0,0,1],
|
||||
"darksalmon": [233,150,122,1], "darkseagreen": [143,188,143,1],
|
||||
"darkslateblue": [72,61,139,1], "darkslategray": [47,79,79,1],
|
||||
"darkslategrey": [47,79,79,1], "darkturquoise": [0,206,209,1],
|
||||
"darkviolet": [148,0,211,1], "deeppink": [255,20,147,1],
|
||||
"deepskyblue": [0,191,255,1], "dimgray": [105,105,105,1],
|
||||
"dimgrey": [105,105,105,1], "dodgerblue": [30,144,255,1],
|
||||
"firebrick": [178,34,34,1], "floralwhite": [255,250,240,1],
|
||||
"forestgreen": [34,139,34,1], "fuchsia": [255,0,255,1],
|
||||
"gainsboro": [220,220,220,1], "ghostwhite": [248,248,255,1],
|
||||
"gold": [255,215,0,1], "goldenrod": [218,165,32,1],
|
||||
"gray": [128,128,128,1], "green": [0,128,0,1],
|
||||
"greenyellow": [173,255,47,1], "grey": [128,128,128,1],
|
||||
"honeydew": [240,255,240,1], "hotpink": [255,105,180,1],
|
||||
"indianred": [205,92,92,1], "indigo": [75,0,130,1],
|
||||
"ivory": [255,255,240,1], "khaki": [240,230,140,1],
|
||||
"lavender": [230,230,250,1], "lavenderblush": [255,240,245,1],
|
||||
"lawngreen": [124,252,0,1], "lemonchiffon": [255,250,205,1],
|
||||
"lightblue": [173,216,230,1], "lightcoral": [240,128,128,1],
|
||||
"lightcyan": [224,255,255,1], "lightgoldenrodyellow": [250,250,210,1],
|
||||
"lightgray": [211,211,211,1], "lightgreen": [144,238,144,1],
|
||||
"lightgrey": [211,211,211,1], "lightpink": [255,182,193,1],
|
||||
"lightsalmon": [255,160,122,1], "lightseagreen": [32,178,170,1],
|
||||
"lightskyblue": [135,206,250,1], "lightslategray": [119,136,153,1],
|
||||
"lightslategrey": [119,136,153,1], "lightsteelblue": [176,196,222,1],
|
||||
"lightyellow": [255,255,224,1], "lime": [0,255,0,1],
|
||||
"limegreen": [50,205,50,1], "linen": [250,240,230,1],
|
||||
"magenta": [255,0,255,1], "maroon": [128,0,0,1],
|
||||
"mediumaquamarine": [102,205,170,1], "mediumblue": [0,0,205,1],
|
||||
"mediumorchid": [186,85,211,1], "mediumpurple": [147,112,219,1],
|
||||
"mediumseagreen": [60,179,113,1], "mediumslateblue": [123,104,238,1],
|
||||
"mediumspringgreen": [0,250,154,1], "mediumturquoise": [72,209,204,1],
|
||||
"mediumvioletred": [199,21,133,1], "midnightblue": [25,25,112,1],
|
||||
"mintcream": [245,255,250,1], "mistyrose": [255,228,225,1],
|
||||
"moccasin": [255,228,181,1], "navajowhite": [255,222,173,1],
|
||||
"navy": [0,0,128,1], "oldlace": [253,245,230,1],
|
||||
"olive": [128,128,0,1], "olivedrab": [107,142,35,1],
|
||||
"orange": [255,165,0,1], "orangered": [255,69,0,1],
|
||||
"orchid": [218,112,214,1], "palegoldenrod": [238,232,170,1],
|
||||
"palegreen": [152,251,152,1], "paleturquoise": [175,238,238,1],
|
||||
"palevioletred": [219,112,147,1], "papayawhip": [255,239,213,1],
|
||||
"peachpuff": [255,218,185,1], "peru": [205,133,63,1],
|
||||
"pink": [255,192,203,1], "plum": [221,160,221,1],
|
||||
"powderblue": [176,224,230,1], "purple": [128,0,128,1],
|
||||
"red": [255,0,0,1], "rosybrown": [188,143,143,1],
|
||||
"royalblue": [65,105,225,1], "saddlebrown": [139,69,19,1],
|
||||
"salmon": [250,128,114,1], "sandybrown": [244,164,96,1],
|
||||
"seagreen": [46,139,87,1], "seashell": [255,245,238,1],
|
||||
"sienna": [160,82,45,1], "silver": [192,192,192,1],
|
||||
"skyblue": [135,206,235,1], "slateblue": [106,90,205,1],
|
||||
"slategray": [112,128,144,1], "slategrey": [112,128,144,1],
|
||||
"snow": [255,250,250,1], "springgreen": [0,255,127,1],
|
||||
"steelblue": [70,130,180,1], "tan": [210,180,140,1],
|
||||
"teal": [0,128,128,1], "thistle": [216,191,216,1],
|
||||
"tomato": [255,99,71,1], "turquoise": [64,224,208,1],
|
||||
"violet": [238,130,238,1], "wheat": [245,222,179,1],
|
||||
"white": [255,255,255,1], "whitesmoke": [245,245,245,1],
|
||||
"yellow": [255,255,0,1], "yellowgreen": [154,205,50,1]}
|
||||
|
||||
function clamp_css_byte(i) { // Clamp to integer 0 .. 255.
|
||||
i = Math.round(i); // Seems to be what Chrome does (vs truncation).
|
||||
return i < 0 ? 0 : i > 255 ? 255 : i;
|
||||
}
|
||||
|
||||
function clamp_css_float(f) { // Clamp to float 0.0 .. 1.0.
|
||||
return f < 0 ? 0 : f > 1 ? 1 : f;
|
||||
}
|
||||
|
||||
function parse_css_int(str) { // int or percentage.
|
||||
if (str[str.length - 1] === '%')
|
||||
return clamp_css_byte(parseFloat(str) / 100 * 255);
|
||||
return clamp_css_byte(parseInt(str));
|
||||
}
|
||||
|
||||
function parse_css_float(str) { // float or percentage.
|
||||
if (str[str.length - 1] === '%')
|
||||
return clamp_css_float(parseFloat(str) / 100);
|
||||
return clamp_css_float(parseFloat(str));
|
||||
}
|
||||
|
||||
function css_hue_to_rgb(m1, m2, h) {
|
||||
if (h < 0) h += 1;
|
||||
else if (h > 1) h -= 1;
|
||||
|
||||
if (h * 6 < 1) return m1 + (m2 - m1) * h * 6;
|
||||
if (h * 2 < 1) return m2;
|
||||
if (h * 3 < 2) return m1 + (m2 - m1) * (2/3 - h) * 6;
|
||||
return m1;
|
||||
}
|
||||
|
||||
function parseCSSColor(css_str) {
|
||||
// Remove all whitespace, not compliant, but should just be more accepting.
|
||||
var str = css_str.replace(/ /g, '').toLowerCase();
|
||||
|
||||
// Color keywords (and transparent) lookup.
|
||||
if (str in kCSSColorTable) return kCSSColorTable[str].slice(); // dup.
|
||||
|
||||
// #abc and #abc123 syntax.
|
||||
if (str[0] === '#') {
|
||||
if (str.length === 4) {
|
||||
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
|
||||
if (!(iv >= 0 && iv <= 0xfff)) return null; // Covers NaN.
|
||||
return [((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
|
||||
(iv & 0xf0) | ((iv & 0xf0) >> 4),
|
||||
(iv & 0xf) | ((iv & 0xf) << 4),
|
||||
1];
|
||||
} else if (str.length === 7) {
|
||||
var iv = parseInt(str.substr(1), 16); // TODO(deanm): Stricter parsing.
|
||||
if (!(iv >= 0 && iv <= 0xffffff)) return null; // Covers NaN.
|
||||
return [(iv & 0xff0000) >> 16,
|
||||
(iv & 0xff00) >> 8,
|
||||
iv & 0xff,
|
||||
1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
var op = str.indexOf('('), ep = str.indexOf(')');
|
||||
if (op !== -1 && ep + 1 === str.length) {
|
||||
var fname = str.substr(0, op);
|
||||
var params = str.substr(op+1, ep-(op+1)).split(',');
|
||||
var alpha = 1; // To allow case fallthrough.
|
||||
switch (fname) {
|
||||
case 'rgba':
|
||||
if (params.length !== 4) return null;
|
||||
alpha = parse_css_float(params.pop());
|
||||
// Fall through.
|
||||
case 'rgb':
|
||||
if (params.length !== 3) return null;
|
||||
return [parse_css_int(params[0]),
|
||||
parse_css_int(params[1]),
|
||||
parse_css_int(params[2]),
|
||||
alpha];
|
||||
case 'hsla':
|
||||
if (params.length !== 4) return null;
|
||||
alpha = parse_css_float(params.pop());
|
||||
// Fall through.
|
||||
case 'hsl':
|
||||
if (params.length !== 3) return null;
|
||||
var h = (((parseFloat(params[0]) % 360) + 360) % 360) / 360; // 0 .. 1
|
||||
// NOTE(deanm): According to the CSS spec s/l should only be
|
||||
// percentages, but we don't bother and let float or percentage.
|
||||
var s = parse_css_float(params[1]);
|
||||
var l = parse_css_float(params[2]);
|
||||
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
|
||||
var m1 = l * 2 - m2;
|
||||
return [clamp_css_byte(css_hue_to_rgb(m1, m2, h+1/3) * 255),
|
||||
clamp_css_byte(css_hue_to_rgb(m1, m2, h) * 255),
|
||||
clamp_css_byte(css_hue_to_rgb(m1, m2, h-1/3) * 255),
|
||||
alpha];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try { exports.parseCSSColor = parseCSSColor } catch(e) { }
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Load the Paper.js library -->
|
||||
<script type="text/javascript" src="paper-full.js"></script>
|
||||
<script type="text/javascript" src="textured3d.js"></script>
|
||||
<script type="text/javascript" src="offset.js"></script>
|
||||
<script type="text/javascript" src="stackblur.js"></script>
|
||||
<script type="text/javascript" src="three.js"></script>
|
||||
<script type="text/javascript" src="modifier.js"></script>
|
||||
|
||||
<script type="text/javascript" src="veingen3d.js"></script>
|
||||
|
||||
<style>
|
||||
.smallcanvas {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border:1px solid #ddd;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
#three {
|
||||
width: 906px;
|
||||
height: 500px;
|
||||
border:1px solid #ddd;
|
||||
float: none;
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<canvas class="smallcanvas" id="grower"></canvas>
|
||||
<canvas class="smallcanvas" id="bump"></canvas>
|
||||
<canvas class="smallcanvas" id="diff"></canvas>
|
||||
<canvas class="smallcanvas" id="gloss"></canvas>
|
||||
<div class="largecanvas" id="three"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1183 1713" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
|
||||
<g transform="matrix(1,0,0,1,-711.5,-246.5)">
|
||||
<path d="M1302.16,1959L971,1868L712,1637L719,1340L970,905L1127,633L1101,457L987,247L1345,397L1590,610L1759,878L1894,1259L1868,1600L1659,1878L1302.16,1959Z" style="fill:none;stroke:black;stroke-width:1px;"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 751 B |
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
Copyright (c) 2014-2017, Jan Bösenberg & Jürg Lehni
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
var OffsetUtils = {
|
||||
offsetPath: function(path, offset, result) {
|
||||
var outerPath = new Path({ insert: false }),
|
||||
epsilon = Numerical.GEOMETRIC_EPSILON,
|
||||
enforeArcs = true;
|
||||
for (var i = 0; i < path.curves.length; i++) {
|
||||
var curve = path.curves[i];
|
||||
if (curve.hasLength(epsilon)) {
|
||||
var segments = this.getOffsetSegments(curve, offset),
|
||||
start = segments[0];
|
||||
if (outerPath.isEmpty()) {
|
||||
outerPath.addSegments(segments);
|
||||
} else {
|
||||
var lastCurve = outerPath.lastCurve;
|
||||
if (!lastCurve.point2.isClose(start.point, epsilon)) {
|
||||
if (enforeArcs || lastCurve.getTangentAtTime(1).dot(start.point.subtract(curve.point1)) >= 0) {
|
||||
this.addRoundJoin(outerPath, start.point, curve.point1, Math.abs(offset));
|
||||
} else {
|
||||
// Connect points with a line
|
||||
outerPath.lineTo(start.point);
|
||||
}
|
||||
}
|
||||
outerPath.lastSegment.handleOut = start.handleOut;
|
||||
outerPath.addSegments(segments.slice(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (path.isClosed()) {
|
||||
if (!outerPath.lastSegment.point.isClose(outerPath.firstSegment.point, epsilon) && (enforeArcs ||
|
||||
outerPath.lastCurve.getTangentAtTime(1).dot(outerPath.firstSegment.point.subtract(path.firstSegment.point)) >= 0)) {
|
||||
this.addRoundJoin(outerPath, outerPath.firstSegment.point, path.firstSegment.point, Math.abs(offset));
|
||||
}
|
||||
outerPath.closePath();
|
||||
}
|
||||
return outerPath;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an offset for the specified curve and returns the segments of
|
||||
* that offset path.
|
||||
*
|
||||
* @param {Curve} curve the curve to be offset
|
||||
* @param {Number} offset the offset distance
|
||||
* @returns {Segment[]} an array of segments describing the offset path
|
||||
*/
|
||||
getOffsetSegments: function(curve, offset) {
|
||||
if (curve.isStraight()) {
|
||||
var n = curve.getNormalAtTime(0.5).multiply(offset),
|
||||
p1 = curve.point1.add(n),
|
||||
p2 = curve.point2.add(n);
|
||||
return [new Segment(p1), new Segment(p2)];
|
||||
} else {
|
||||
var curves = this.splitCurveForOffseting(curve),
|
||||
segments = [];
|
||||
for (var i = 0, l = curves.length; i < l; i++) {
|
||||
var offsetCurves = this.getOffsetCurves(curves[i], offset, 0),
|
||||
prevSegment;
|
||||
for (var j = 0, m = offsetCurves.length; j < m; j++) {
|
||||
var curve = offsetCurves[j],
|
||||
segment = curve.segment1;
|
||||
if (prevSegment) {
|
||||
prevSegment.handleOut = segment.handleOut;
|
||||
} else {
|
||||
segments.push(segment);
|
||||
}
|
||||
segments.push(prevSegment = curve.segment2);
|
||||
}
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Approach for Curve Offsetting based on:
|
||||
* "A New Shape Control and Classification for Cubic Bézier Curves"
|
||||
* Shi-Nine Yang and Ming-Liang Huang
|
||||
*/
|
||||
offsetCurve_middle: function(curve, offset) {
|
||||
var v = curve.getValues(),
|
||||
p1 = curve.point1.add(Curve.getNormal(v, 0).multiply(offset)),
|
||||
p2 = curve.point2.add(Curve.getNormal(v, 1).multiply(offset)),
|
||||
pt = Curve.getPoint(v, 0.5).add(
|
||||
Curve.getNormal(v, 0.5).multiply(offset)),
|
||||
t1 = Curve.getTangent(v, 0),
|
||||
t2 = Curve.getTangent(v, 1),
|
||||
div = t1.cross(t2) * 3 / 4,
|
||||
d = pt.multiply(2).subtract(p1.add(p2)),
|
||||
a = d.cross(t2) / div,
|
||||
b = d.cross(t1) / div;
|
||||
return new Curve(p1, t1.multiply(a), t2.multiply(-b), p2);
|
||||
},
|
||||
|
||||
offsetCurve_average: function(curve, offset) {
|
||||
var v = curve.getValues(),
|
||||
p1 = curve.point1.add(Curve.getNormal(v, 0).multiply(offset)),
|
||||
p2 = curve.point2.add(Curve.getNormal(v, 1).multiply(offset)),
|
||||
t = this.getAverageTangentTime(v),
|
||||
u = 1 - t,
|
||||
pt = Curve.getPoint(v, t).add(
|
||||
Curve.getNormal(v, t).multiply(offset)),
|
||||
t1 = Curve.getTangent(v, 0),
|
||||
t2 = Curve.getTangent(v, 1),
|
||||
div = t1.cross(t2) * 3 * t * u,
|
||||
v = pt.subtract(
|
||||
p1.multiply(u * u * (1 + 2 * t)).add(
|
||||
p2.multiply(t * t * (3 - 2 * t)))),
|
||||
a = v.cross(t2) / (div * u),
|
||||
b = v.cross(t1) / (div * t);
|
||||
return new Curve(p1, t1.multiply(a), t2.multiply(-b), p2);
|
||||
},
|
||||
|
||||
/**
|
||||
* This algorithm simply scales the curve so its end points are at the
|
||||
* calculated offsets of the original end points.
|
||||
*/
|
||||
offsetCurve_simple: function (crv, dist) {
|
||||
// calculate end points of offset curve
|
||||
var p1 = crv.point1.add(crv.getNormalAtTime(0).multiply(dist));
|
||||
var p4 = crv.point2.add(crv.getNormalAtTime(1).multiply(dist));
|
||||
// get scale ratio
|
||||
var pointDist = crv.point1.getDistance(crv.point2);
|
||||
// TODO: Handle cases when pointDist == 0
|
||||
var f = p1.getDistance(p4) / pointDist;
|
||||
if (crv.point2.subtract(crv.point1).dot(p4.subtract(p1)) < 0) {
|
||||
f = -f; // probably more correct than connecting with line
|
||||
}
|
||||
// Scale handles and generate offset curve
|
||||
return new Curve(p1, crv.handle1.multiply(f), crv.handle2.multiply(f), p4);
|
||||
},
|
||||
|
||||
getOffsetCurves: function(curve, offset, method) {
|
||||
var errorThreshold = 0.01,
|
||||
radius = Math.abs(offset),
|
||||
offsetMethod = this['offsetCurve_' + (method || 'middle')],
|
||||
that = this;
|
||||
|
||||
function offsetCurce(curve, curves, recursion) {
|
||||
var offsetCurve = offsetMethod.call(that, curve, offset),
|
||||
cv = curve.getValues(),
|
||||
ov = offsetCurve.getValues(),
|
||||
count = 16,
|
||||
error = 0;
|
||||
for (var i = 1; i < count; i++) {
|
||||
var t = i / count,
|
||||
p = Curve.getPoint(cv, t),
|
||||
n = Curve.getNormal(cv, t),
|
||||
roots = Curve.getCurveLineIntersections(ov, p.x, p.y, n.x, n.y),
|
||||
dist = 2 * radius;
|
||||
for (var j = 0, l = roots.length; j < l; j++) {
|
||||
var d = Curve.getPoint(ov, roots[j]).getDistance(p);
|
||||
if (d < dist)
|
||||
dist = d;
|
||||
}
|
||||
var err = Math.abs(radius - dist);
|
||||
if (err > error)
|
||||
error = err;
|
||||
}
|
||||
if (error > errorThreshold && recursion++ < 8) {
|
||||
if (error === radius) {
|
||||
// console.log(cv);
|
||||
}
|
||||
var curve2 = curve.divideAtTime(that.getAverageTangentTime(cv));
|
||||
offsetCurce(curve, curves, recursion);
|
||||
offsetCurce(curve2, curves, recursion);
|
||||
} else {
|
||||
curves.push(offsetCurve);
|
||||
}
|
||||
return curves;
|
||||
}
|
||||
|
||||
return offsetCurce(curve, [], 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Split curve into sections that can then be treated individually by an
|
||||
* offset algorithm.
|
||||
*/
|
||||
splitCurveForOffseting: function(curve) {
|
||||
var curves = [curve.clone()], // Clone so path is not modified.
|
||||
that = this;
|
||||
if (curve.isStraight())
|
||||
return curves;
|
||||
|
||||
function splitAtRoots(index, roots) {
|
||||
for (var i = 0, prevT, l = roots && roots.length; i < l; i++) {
|
||||
var t = roots[i],
|
||||
curve = curves[index].divideAtTime(
|
||||
// Renormalize curve-time for multiple roots:
|
||||
i ? (t - prevT) / (1 - prevT) : t);
|
||||
prevT = t;
|
||||
if (curve)
|
||||
curves.splice(++index, 0, curve);
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively splits the specified curve if the angle between the two
|
||||
// handles is too large (we use 60° as a threshold).
|
||||
function splitLargeAngles(index, recursion) {
|
||||
var curve = curves[index],
|
||||
v = curve.getValues(),
|
||||
n1 = Curve.getNormal(v, 0),
|
||||
n2 = Curve.getNormal(v, 1).negate(),
|
||||
cos = n1.dot(n2);
|
||||
if (cos > -0.5 && ++recursion < 4) {
|
||||
curves.splice(index + 1, 0,
|
||||
curve.divideAtTime(that.getAverageTangentTime(v)));
|
||||
splitLargeAngles(index + 1, recursion);
|
||||
splitLargeAngles(index, recursion);
|
||||
}
|
||||
}
|
||||
|
||||
// Split curves at cusps and inflection points.
|
||||
var info = curve.classify();
|
||||
if (info.roots && info.type !== 'loop') {
|
||||
splitAtRoots(0, info.roots);
|
||||
}
|
||||
|
||||
// Split sub-curves at peaks.
|
||||
for (var i = curves.length - 1; i >= 0; i--) {
|
||||
splitAtRoots(i, Curve.getPeaks(curves[i].getValues()));
|
||||
}
|
||||
|
||||
// Split sub-curves with too large angle between handles.
|
||||
for (var i = curves.length - 1; i >= 0; i--) {
|
||||
//splitLargeAngles(i, 0);
|
||||
}
|
||||
return curves;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the first curve-time where the curve has its tangent in the same
|
||||
* direction as the average of the tangents at its beginning and end.
|
||||
*/
|
||||
getAverageTangentTime: function(v) {
|
||||
var tan = Curve.getTangent(v, 0).add(Curve.getTangent(v, 1)),
|
||||
tx = tan.x,
|
||||
ty = tan.y,
|
||||
abs = Math.abs,
|
||||
flip = abs(ty) < abs(tx),
|
||||
s = flip ? ty / tx : tx / ty,
|
||||
ia = flip ? 1 : 0, // the abscissa index
|
||||
io = ia ^ 1, // the ordinate index
|
||||
a0 = v[ia + 0], o0 = v[io + 0],
|
||||
a1 = v[ia + 2], o1 = v[io + 2],
|
||||
a2 = v[ia + 4], o2 = v[io + 4],
|
||||
a3 = v[ia + 6], o3 = v[io + 6],
|
||||
aA = -a0 + 3 * a1 - 3 * a2 + a3,
|
||||
aB = 3 * a0 - 6 * a1 + 3 * a2,
|
||||
aC = -3 * a0 + 3 * a1,
|
||||
oA = -o0 + 3 * o1 - 3 * o2 + o3,
|
||||
oB = 3 * o0 - 6 * o1 + 3 * o2,
|
||||
oC = -3 * o0 + 3 * o1,
|
||||
roots = [],
|
||||
epsilon = Numerical.CURVETIME_EPSILON,
|
||||
count = Numerical.solveQuadratic(
|
||||
3 * (aA - s * oA),
|
||||
2 * (aB - s * oB),
|
||||
aC - s * oC, roots,
|
||||
epsilon, 1 - epsilon);
|
||||
// Fall back to 0.5, so we always have a place to split...
|
||||
return count > 0 ? roots[0] : 0.5;
|
||||
},
|
||||
|
||||
addRoundJoin: function(path, dest, center, radius) {
|
||||
// return path.lineTo(dest);
|
||||
var middle = path.lastSegment.point.add(dest).divide(2),
|
||||
through = center.add(middle.subtract(center).normalize(radius));
|
||||
path.arcTo(through, dest);
|
||||
},
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,583 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = global || self, factory(global.StackBlur = {}));
|
||||
}(this, (function (exports) { 'use strict';
|
||||
|
||||
function _typeof(obj) {
|
||||
if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
|
||||
_typeof = function (obj) {
|
||||
return typeof obj;
|
||||
};
|
||||
} else {
|
||||
_typeof = function (obj) {
|
||||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||
};
|
||||
}
|
||||
|
||||
return _typeof(obj);
|
||||
}
|
||||
|
||||
function _classCallCheck(instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* StackBlur - a fast almost Gaussian Blur For Canvas
|
||||
*
|
||||
* In case you find this class useful - especially in commercial projects -
|
||||
* I am not totally unhappy for a small donation to my PayPal account
|
||||
* mario@quasimondo.de
|
||||
*
|
||||
* Or support me on flattr:
|
||||
* {@link https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript}
|
||||
* @module StackBlur
|
||||
* @version 0.5
|
||||
* @author Mario Klingemann
|
||||
* Contact: mario@quasimondo.com
|
||||
* Website: {@link http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html}
|
||||
* Twitter: @quasimondo
|
||||
*
|
||||
* @copyright (c) 2010 Mario Klingemann
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
var mulTable = [512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259];
|
||||
var shgTable = [9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24];
|
||||
/**
|
||||
* @param {string|HTMLImageElement} img
|
||||
* @param {string|HTMLCanvasElement} canvas
|
||||
* @param {Float} radius
|
||||
* @param {boolean} blurAlphaChannel
|
||||
* @returns {undefined}
|
||||
*/
|
||||
|
||||
function processImage(img, canvas, radius, blurAlphaChannel) {
|
||||
if (typeof img === 'string') {
|
||||
img = document.getElementById(img);
|
||||
}
|
||||
|
||||
if (!img || !('naturalWidth' in img)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var w = img.naturalWidth;
|
||||
var h = img.naturalHeight;
|
||||
|
||||
if (typeof canvas === 'string') {
|
||||
canvas = document.getElementById(canvas);
|
||||
}
|
||||
|
||||
if (!canvas || !('getContext' in canvas)) {
|
||||
return;
|
||||
}
|
||||
|
||||
canvas.style.width = w + 'px';
|
||||
canvas.style.height = h + 'px';
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
var context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, w, h);
|
||||
context.drawImage(img, 0, 0);
|
||||
|
||||
if (isNaN(radius) || radius < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (blurAlphaChannel) {
|
||||
processCanvasRGBA(canvas, 0, 0, w, h, radius);
|
||||
} else {
|
||||
processCanvasRGB(canvas, 0, 0, w, h, radius);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string|HTMLCanvasElement} canvas
|
||||
* @param {Integer} topX
|
||||
* @param {Integer} topY
|
||||
* @param {Integer} width
|
||||
* @param {Integer} height
|
||||
* @throws {Error|TypeError}
|
||||
* @returns {ImageData} See {@link https://html.spec.whatwg.org/multipage/canvas.html#imagedata}
|
||||
*/
|
||||
|
||||
|
||||
function getImageDataFromCanvas(canvas, topX, topY, width, height) {
|
||||
if (typeof canvas === 'string') {
|
||||
canvas = document.getElementById(canvas);
|
||||
}
|
||||
|
||||
if (!canvas || _typeof(canvas) !== 'object' || !('getContext' in canvas)) {
|
||||
throw new TypeError('Expecting canvas with `getContext` method in processCanvasRGB(A) calls!');
|
||||
}
|
||||
|
||||
var context = canvas.getContext('2d');
|
||||
|
||||
try {
|
||||
return context.getImageData(topX, topY, width, height);
|
||||
} catch (e) {
|
||||
throw new Error('unable to access image data: ' + e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {Integer} topX
|
||||
* @param {Integer} topY
|
||||
* @param {Integer} width
|
||||
* @param {Integer} height
|
||||
* @param {Float} radius
|
||||
* @returns {undefined}
|
||||
*/
|
||||
|
||||
|
||||
function processCanvasRGBA(canvas, topX, topY, width, height, radius) {
|
||||
if (isNaN(radius) || radius < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
radius |= 0;
|
||||
var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height);
|
||||
imageData = processImageDataRGBA(imageData, topX, topY, width, height, radius);
|
||||
canvas.getContext('2d').putImageData(imageData, topX, topY);
|
||||
}
|
||||
/**
|
||||
* @param {ImageData} imageData
|
||||
* @param {Integer} topX
|
||||
* @param {Integer} topY
|
||||
* @param {Integer} width
|
||||
* @param {Integer} height
|
||||
* @param {Float} radius
|
||||
* @returns {ImageData}
|
||||
*/
|
||||
|
||||
|
||||
function processImageDataRGBA(imageData, topX, topY, width, height, radius) {
|
||||
var pixels = imageData.data;
|
||||
var x, y, i, p, yp, yi, yw, rSum, gSum, bSum, aSum, rOutSum, gOutSum, bOutSum, aOutSum, rInSum, gInSum, bInSum, aInSum, pr, pg, pb, pa, rbs;
|
||||
var div = 2 * radius + 1; // const w4 = width << 2;
|
||||
|
||||
var widthMinus1 = width - 1;
|
||||
var heightMinus1 = height - 1;
|
||||
var radiusPlus1 = radius + 1;
|
||||
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
|
||||
var stackStart = new BlurStack();
|
||||
var stack = stackStart;
|
||||
var stackEnd;
|
||||
|
||||
for (i = 1; i < div; i++) {
|
||||
stack = stack.next = new BlurStack();
|
||||
|
||||
if (i === radiusPlus1) {
|
||||
stackEnd = stack;
|
||||
}
|
||||
}
|
||||
|
||||
stack.next = stackStart;
|
||||
var stackIn = null;
|
||||
var stackOut = null;
|
||||
yw = yi = 0;
|
||||
var mulSum = mulTable[radius];
|
||||
var shgSum = shgTable[radius];
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
rInSum = gInSum = bInSum = aInSum = rSum = gSum = bSum = aSum = 0;
|
||||
rOutSum = radiusPlus1 * (pr = pixels[yi]);
|
||||
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||||
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||||
aOutSum = radiusPlus1 * (pa = pixels[yi + 3]);
|
||||
rSum += sumFactor * pr;
|
||||
gSum += sumFactor * pg;
|
||||
bSum += sumFactor * pb;
|
||||
aSum += sumFactor * pa;
|
||||
stack = stackStart;
|
||||
|
||||
for (i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack.a = pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
for (i = 1; i < radiusPlus1; i++) {
|
||||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||||
rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
|
||||
gSum += (stack.g = pg = pixels[p + 1]) * rbs;
|
||||
bSum += (stack.b = pb = pixels[p + 2]) * rbs;
|
||||
aSum += (stack.a = pa = pixels[p + 3]) * rbs;
|
||||
rInSum += pr;
|
||||
gInSum += pg;
|
||||
bInSum += pb;
|
||||
aInSum += pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
pixels[yi + 3] = pa = aSum * mulSum >> shgSum;
|
||||
|
||||
if (pa !== 0) {
|
||||
pa = 255 / pa;
|
||||
pixels[yi] = (rSum * mulSum >> shgSum) * pa;
|
||||
pixels[yi + 1] = (gSum * mulSum >> shgSum) * pa;
|
||||
pixels[yi + 2] = (bSum * mulSum >> shgSum) * pa;
|
||||
} else {
|
||||
pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0;
|
||||
}
|
||||
|
||||
rSum -= rOutSum;
|
||||
gSum -= gOutSum;
|
||||
bSum -= bOutSum;
|
||||
aSum -= aOutSum;
|
||||
rOutSum -= stackIn.r;
|
||||
gOutSum -= stackIn.g;
|
||||
bOutSum -= stackIn.b;
|
||||
aOutSum -= stackIn.a;
|
||||
p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2;
|
||||
rInSum += stackIn.r = pixels[p];
|
||||
gInSum += stackIn.g = pixels[p + 1];
|
||||
bInSum += stackIn.b = pixels[p + 2];
|
||||
aInSum += stackIn.a = pixels[p + 3];
|
||||
rSum += rInSum;
|
||||
gSum += gInSum;
|
||||
bSum += bInSum;
|
||||
aSum += aInSum;
|
||||
stackIn = stackIn.next;
|
||||
rOutSum += pr = stackOut.r;
|
||||
gOutSum += pg = stackOut.g;
|
||||
bOutSum += pb = stackOut.b;
|
||||
aOutSum += pa = stackOut.a;
|
||||
rInSum -= pr;
|
||||
gInSum -= pg;
|
||||
bInSum -= pb;
|
||||
aInSum -= pa;
|
||||
stackOut = stackOut.next;
|
||||
yi += 4;
|
||||
}
|
||||
|
||||
yw += width;
|
||||
}
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
gInSum = bInSum = aInSum = rInSum = gSum = bSum = aSum = rSum = 0;
|
||||
yi = x << 2;
|
||||
rOutSum = radiusPlus1 * (pr = pixels[yi]);
|
||||
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||||
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||||
aOutSum = radiusPlus1 * (pa = pixels[yi + 3]);
|
||||
rSum += sumFactor * pr;
|
||||
gSum += sumFactor * pg;
|
||||
bSum += sumFactor * pb;
|
||||
aSum += sumFactor * pa;
|
||||
stack = stackStart;
|
||||
|
||||
for (i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack.a = pa;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
yp = width;
|
||||
|
||||
for (i = 1; i <= radius; i++) {
|
||||
yi = yp + x << 2;
|
||||
rSum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
||||
gSum += (stack.g = pg = pixels[yi + 1]) * rbs;
|
||||
bSum += (stack.b = pb = pixels[yi + 2]) * rbs;
|
||||
aSum += (stack.a = pa = pixels[yi + 3]) * rbs;
|
||||
rInSum += pr;
|
||||
gInSum += pg;
|
||||
bInSum += pb;
|
||||
aInSum += pa;
|
||||
stack = stack.next;
|
||||
|
||||
if (i < heightMinus1) {
|
||||
yp += width;
|
||||
}
|
||||
}
|
||||
|
||||
yi = x;
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
p = yi << 2;
|
||||
pixels[p + 3] = pa = aSum * mulSum >> shgSum;
|
||||
|
||||
if (pa > 0) {
|
||||
pa = 255 / pa;
|
||||
pixels[p] = (rSum * mulSum >> shgSum) * pa;
|
||||
pixels[p + 1] = (gSum * mulSum >> shgSum) * pa;
|
||||
pixels[p + 2] = (bSum * mulSum >> shgSum) * pa;
|
||||
} else {
|
||||
pixels[p] = pixels[p + 1] = pixels[p + 2] = 0;
|
||||
}
|
||||
|
||||
rSum -= rOutSum;
|
||||
gSum -= gOutSum;
|
||||
bSum -= bOutSum;
|
||||
aSum -= aOutSum;
|
||||
rOutSum -= stackIn.r;
|
||||
gOutSum -= stackIn.g;
|
||||
bOutSum -= stackIn.b;
|
||||
aOutSum -= stackIn.a;
|
||||
p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2;
|
||||
rSum += rInSum += stackIn.r = pixels[p];
|
||||
gSum += gInSum += stackIn.g = pixels[p + 1];
|
||||
bSum += bInSum += stackIn.b = pixels[p + 2];
|
||||
aSum += aInSum += stackIn.a = pixels[p + 3];
|
||||
stackIn = stackIn.next;
|
||||
rOutSum += pr = stackOut.r;
|
||||
gOutSum += pg = stackOut.g;
|
||||
bOutSum += pb = stackOut.b;
|
||||
aOutSum += pa = stackOut.a;
|
||||
rInSum -= pr;
|
||||
gInSum -= pg;
|
||||
bInSum -= pb;
|
||||
aInSum -= pa;
|
||||
stackOut = stackOut.next;
|
||||
yi += width;
|
||||
}
|
||||
}
|
||||
|
||||
return imageData;
|
||||
}
|
||||
/**
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
* @param {Integer} topX
|
||||
* @param {Integer} topY
|
||||
* @param {Integer} width
|
||||
* @param {Integer} height
|
||||
* @param {Float} radius
|
||||
* @returns {undefined}
|
||||
*/
|
||||
|
||||
|
||||
function processCanvasRGB(canvas, topX, topY, width, height, radius) {
|
||||
if (isNaN(radius) || radius < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
radius |= 0;
|
||||
var imageData = getImageDataFromCanvas(canvas, topX, topY, width, height);
|
||||
imageData = processImageDataRGB(imageData, topX, topY, width, height, radius);
|
||||
canvas.getContext('2d').putImageData(imageData, topX, topY);
|
||||
}
|
||||
/**
|
||||
* @param {ImageData} imageData
|
||||
* @param {Integer} topX
|
||||
* @param {Integer} topY
|
||||
* @param {Integer} width
|
||||
* @param {Integer} height
|
||||
* @param {Float} radius
|
||||
* @returns {ImageData}
|
||||
*/
|
||||
|
||||
|
||||
function processImageDataRGB(imageData, topX, topY, width, height, radius) {
|
||||
var pixels = imageData.data;
|
||||
var x, y, i, p, yp, yi, yw, rSum, gSum, bSum, rOutSum, gOutSum, bOutSum, rInSum, gInSum, bInSum, pr, pg, pb, rbs;
|
||||
var div = 2 * radius + 1; // const w4 = width << 2;
|
||||
|
||||
var widthMinus1 = width - 1;
|
||||
var heightMinus1 = height - 1;
|
||||
var radiusPlus1 = radius + 1;
|
||||
var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2;
|
||||
var stackStart = new BlurStack();
|
||||
var stack = stackStart;
|
||||
var stackEnd;
|
||||
|
||||
for (i = 1; i < div; i++) {
|
||||
stack = stack.next = new BlurStack();
|
||||
|
||||
if (i === radiusPlus1) {
|
||||
stackEnd = stack;
|
||||
}
|
||||
}
|
||||
|
||||
stack.next = stackStart;
|
||||
var stackIn = null;
|
||||
var stackOut = null;
|
||||
yw = yi = 0;
|
||||
var mulSum = mulTable[radius];
|
||||
var shgSum = shgTable[radius];
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
rInSum = gInSum = bInSum = rSum = gSum = bSum = 0;
|
||||
rOutSum = radiusPlus1 * (pr = pixels[yi]);
|
||||
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||||
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||||
rSum += sumFactor * pr;
|
||||
gSum += sumFactor * pg;
|
||||
bSum += sumFactor * pb;
|
||||
stack = stackStart;
|
||||
|
||||
for (i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
for (i = 1; i < radiusPlus1; i++) {
|
||||
p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2);
|
||||
rSum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i);
|
||||
gSum += (stack.g = pg = pixels[p + 1]) * rbs;
|
||||
bSum += (stack.b = pb = pixels[p + 2]) * rbs;
|
||||
rInSum += pr;
|
||||
gInSum += pg;
|
||||
bInSum += pb;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
pixels[yi] = rSum * mulSum >> shgSum;
|
||||
pixels[yi + 1] = gSum * mulSum >> shgSum;
|
||||
pixels[yi + 2] = bSum * mulSum >> shgSum;
|
||||
rSum -= rOutSum;
|
||||
gSum -= gOutSum;
|
||||
bSum -= bOutSum;
|
||||
rOutSum -= stackIn.r;
|
||||
gOutSum -= stackIn.g;
|
||||
bOutSum -= stackIn.b;
|
||||
p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2;
|
||||
rInSum += stackIn.r = pixels[p];
|
||||
gInSum += stackIn.g = pixels[p + 1];
|
||||
bInSum += stackIn.b = pixels[p + 2];
|
||||
rSum += rInSum;
|
||||
gSum += gInSum;
|
||||
bSum += bInSum;
|
||||
stackIn = stackIn.next;
|
||||
rOutSum += pr = stackOut.r;
|
||||
gOutSum += pg = stackOut.g;
|
||||
bOutSum += pb = stackOut.b;
|
||||
rInSum -= pr;
|
||||
gInSum -= pg;
|
||||
bInSum -= pb;
|
||||
stackOut = stackOut.next;
|
||||
yi += 4;
|
||||
}
|
||||
|
||||
yw += width;
|
||||
}
|
||||
|
||||
for (x = 0; x < width; x++) {
|
||||
gInSum = bInSum = rInSum = gSum = bSum = rSum = 0;
|
||||
yi = x << 2;
|
||||
rOutSum = radiusPlus1 * (pr = pixels[yi]);
|
||||
gOutSum = radiusPlus1 * (pg = pixels[yi + 1]);
|
||||
bOutSum = radiusPlus1 * (pb = pixels[yi + 2]);
|
||||
rSum += sumFactor * pr;
|
||||
gSum += sumFactor * pg;
|
||||
bSum += sumFactor * pb;
|
||||
stack = stackStart;
|
||||
|
||||
for (i = 0; i < radiusPlus1; i++) {
|
||||
stack.r = pr;
|
||||
stack.g = pg;
|
||||
stack.b = pb;
|
||||
stack = stack.next;
|
||||
}
|
||||
|
||||
yp = width;
|
||||
|
||||
for (i = 1; i <= radius; i++) {
|
||||
yi = yp + x << 2;
|
||||
rSum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i);
|
||||
gSum += (stack.g = pg = pixels[yi + 1]) * rbs;
|
||||
bSum += (stack.b = pb = pixels[yi + 2]) * rbs;
|
||||
rInSum += pr;
|
||||
gInSum += pg;
|
||||
bInSum += pb;
|
||||
stack = stack.next;
|
||||
|
||||
if (i < heightMinus1) {
|
||||
yp += width;
|
||||
}
|
||||
}
|
||||
|
||||
yi = x;
|
||||
stackIn = stackStart;
|
||||
stackOut = stackEnd;
|
||||
|
||||
for (y = 0; y < height; y++) {
|
||||
p = yi << 2;
|
||||
pixels[p] = rSum * mulSum >> shgSum;
|
||||
pixels[p + 1] = gSum * mulSum >> shgSum;
|
||||
pixels[p + 2] = bSum * mulSum >> shgSum;
|
||||
rSum -= rOutSum;
|
||||
gSum -= gOutSum;
|
||||
bSum -= bOutSum;
|
||||
rOutSum -= stackIn.r;
|
||||
gOutSum -= stackIn.g;
|
||||
bOutSum -= stackIn.b;
|
||||
p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2;
|
||||
rSum += rInSum += stackIn.r = pixels[p];
|
||||
gSum += gInSum += stackIn.g = pixels[p + 1];
|
||||
bSum += bInSum += stackIn.b = pixels[p + 2];
|
||||
stackIn = stackIn.next;
|
||||
rOutSum += pr = stackOut.r;
|
||||
gOutSum += pg = stackOut.g;
|
||||
bOutSum += pb = stackOut.b;
|
||||
rInSum -= pr;
|
||||
gInSum -= pg;
|
||||
bInSum -= pb;
|
||||
stackOut = stackOut.next;
|
||||
yi += width;
|
||||
}
|
||||
}
|
||||
|
||||
return imageData;
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
var BlurStack = function BlurStack() {
|
||||
_classCallCheck(this, BlurStack);
|
||||
|
||||
this.r = 0;
|
||||
this.g = 0;
|
||||
this.b = 0;
|
||||
this.a = 0;
|
||||
this.next = null;
|
||||
};
|
||||
|
||||
exports.BlurStack = BlurStack;
|
||||
exports.canvasRGB = processCanvasRGB;
|
||||
exports.canvasRGBA = processCanvasRGBA;
|
||||
exports.image = processImage;
|
||||
exports.imageDataRGB = processImageDataRGB;
|
||||
exports.imageDataRGBA = processImageDataRGBA;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
})));
|
|
@ -0,0 +1,173 @@
|
|||
var bumpCanvas, diffCanvas, glossCanvas, bump2d, diff2d, gloss2d;
|
||||
var scene, camera, renderer, leaf, leafmat;
|
||||
|
||||
var seed = Math.floor(Math.random()*1337);
|
||||
|
||||
function seededRand() {
|
||||
var x = Math.sin(seed++) * 10000;
|
||||
return x - Math.floor(x);
|
||||
}
|
||||
function map_range(value, low1, high1, low2, high2) {
|
||||
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
|
||||
}
|
||||
|
||||
function docReady(fn) {
|
||||
// see if DOM is already available
|
||||
if (document.readyState === "complete" || document.readyState === "interactive") {
|
||||
// call on next available tick
|
||||
setTimeout(fn, 1);
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", fn);
|
||||
}
|
||||
}
|
||||
|
||||
function generateTextures() {
|
||||
/*
|
||||
bumpCanvas = document.getElementById('bump');
|
||||
diffCanvas = document.getElementById('diff');
|
||||
bump2d = bumpCanvas.getContext("2d");
|
||||
diff2d = diffCanvas.getContext("2d");
|
||||
*/
|
||||
bumpCanvas = document.getElementById('bump');
|
||||
diffCanvas = document.getElementById('diff');
|
||||
glossCanvas = document.getElementById('gloss');
|
||||
|
||||
bump2d = bumpCanvas.getContext("2d");
|
||||
diff2d = diffCanvas.getContext("2d");
|
||||
gloss2d = glossCanvas.getContext("2d");
|
||||
|
||||
bumpCanvas.width = 300;
|
||||
bumpCanvas.height = 300;
|
||||
diffCanvas.width = 300;
|
||||
diffCanvas.height = 300;
|
||||
glossCanvas.width = 300;
|
||||
glossCanvas.height = 300;
|
||||
|
||||
if (window.devicePixelRatio > 1) {
|
||||
|
||||
bumpCanvas.width *= window.devicePixelRatio;
|
||||
bumpCanvas.height *= window.devicePixelRatio;
|
||||
diffCanvas.width *= window.devicePixelRatio;
|
||||
diffCanvas.height *= window.devicePixelRatio;
|
||||
glossCanvas.width *= window.devicePixelRatio;
|
||||
glossCanvas.height *= window.devicePixelRatio;
|
||||
|
||||
//bump2d.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
}
|
||||
|
||||
bump2d.fillStyle = "white";
|
||||
bump2d.fillRect(0, 0, bumpCanvas.width, bumpCanvas.height);
|
||||
bump2d.drawImage(document.getElementById('grower'), 0, 0);
|
||||
bump2d.globalCompositeOperation='difference';
|
||||
bump2d.fillStyle='white';
|
||||
bump2d.fillRect(0,0,bumpCanvas.width,bumpCanvas.height);
|
||||
|
||||
diff2d.drawImage(bumpCanvas, 0, 0);
|
||||
gloss2d.drawImage(bumpCanvas, 0, 0);
|
||||
|
||||
StackBlur.canvasRGB(bumpCanvas, 0, 0, 600, 600, 3);
|
||||
StackBlur.canvasRGB(diffCanvas, 0, 0, 600, 600, 40);
|
||||
StackBlur.canvasRGB(glossCanvas, 0, 0, 600, 600, 20);
|
||||
|
||||
// Gradient Map
|
||||
|
||||
var imageData = diff2d.getImageData(0, 0, diffCanvas.width, diffCanvas.height);
|
||||
var imageData2 = bump2d.getImageData(0, 0, diffCanvas.width, diffCanvas.height);
|
||||
|
||||
var pixels = imageData.data;
|
||||
var len = pixels.length / 4;
|
||||
var randy = 25;
|
||||
while(len--) {
|
||||
var id = len * 4 + 3;
|
||||
randy += (seededRand()*8)-4;
|
||||
var col = pixels[id-1]*2 + (randy/60) + seededRand()*40;
|
||||
pixels[id - 3] = map_range(col, 0, 255, 40, 210); // red
|
||||
pixels[id - 2] = map_range(col, 0, 255, 100, 255); // green
|
||||
pixels[id - 1] = map_range(col, 0, 255, 30, 50); // blue
|
||||
}
|
||||
diff2d.putImageData(imageData, 0, 0);
|
||||
|
||||
//diff2d.globalCompositeOperation = "screen";
|
||||
|
||||
var pixels2 = imageData2.data;
|
||||
len = pixels.length / 4;
|
||||
while(len--) {
|
||||
var id = len * 4 + 3;
|
||||
var col = pixels2[id-1];
|
||||
pixels2[id - 3] = pixels[id - 3]+map_range(col, 0, 255, 0, 40); // red
|
||||
pixels2[id - 2] = pixels[id - 2]+map_range(col, 0, 255, 0, 30); // green
|
||||
pixels2[id - 1] = pixels[id - 1]; // blue
|
||||
}
|
||||
diff2d.putImageData(imageData2, 0, 0);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function renderScene() {
|
||||
const aspect = 900/500;
|
||||
const stage = document.getElementById('three');
|
||||
|
||||
scene = new THREE.Scene();
|
||||
camera = new THREE.PerspectiveCamera(30, aspect, 0.1, 1000);
|
||||
renderer = new THREE.WebGLRenderer({ antialias: true });
|
||||
|
||||
renderer.setClearColor('#dddddd');
|
||||
renderer.setSize(900,500);
|
||||
|
||||
camera.position.z = 15;
|
||||
|
||||
stage.appendChild(renderer.domElement);
|
||||
|
||||
//let geometry = new THREE.PlaneGeometry( 6, 6, 2 );
|
||||
let geometry = new THREE.CylinderGeometry( 4, 4, 0.01, 64 );
|
||||
//leafmat = new THREE.MeshLambertMaterial( {color: 0x00ccff} );
|
||||
|
||||
leafmat = new THREE.MeshPhongMaterial( {
|
||||
specular: 0xffffff,
|
||||
shininess: 4,
|
||||
bumpScale: 0.03
|
||||
} );
|
||||
//leafmat.emissive = "#FF0000";
|
||||
//leafmat.emissiveMap = new THREE.CanvasTexture(diffCanvas);
|
||||
leafmat.bumpMap = new THREE.CanvasTexture(bumpCanvas);
|
||||
leafmat.specularMap = new THREE.CanvasTexture(glossCanvas);
|
||||
leafmat.map = new THREE.CanvasTexture(diffCanvas);
|
||||
|
||||
//leafmat.map.needsUpdate = true;
|
||||
|
||||
leaf = new THREE.Mesh(geometry, leafmat);
|
||||
|
||||
scene.add(leaf);
|
||||
|
||||
let spotLight = new THREE.SpotLight(0xffffbb, 0.6);
|
||||
spotLight.position.set(0.5, 0, 1);
|
||||
spotLight.position.multiplyScalar(100);
|
||||
var ambientLight = new THREE.AmbientLight(0x666666);
|
||||
|
||||
scene.add(spotLight);
|
||||
scene.add(ambientLight);
|
||||
|
||||
spotLight.castShadow = true;
|
||||
spotLight.shadow.mapSize.width = 2048;
|
||||
spotLight.shadow.mapSize.height = 2048;
|
||||
spotLight.shadow.camera.near = 200;
|
||||
spotLight.shadow.camera.far = 1500;
|
||||
spotLight.shadow.camera.fov = 40;
|
||||
spotLight.shadow.bias = - 0.005;
|
||||
|
||||
renderer.render(scene, camera);
|
||||
|
||||
animate();
|
||||
|
||||
function animate() {
|
||||
requestAnimationFrame( animate );
|
||||
leaf.rotation.z += 0.01;
|
||||
leaf.rotation.x += 0.001;
|
||||
leaf.rotation.y += 0.001;
|
||||
renderer.render( scene, camera );
|
||||
}
|
||||
}
|
||||
|
||||
docReady(function() {
|
||||
//
|
||||
});
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,761 @@
|
|||
var footstring2 = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg7002"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="chairleg2.svg">
|
||||
<defs
|
||||
id="defs6996" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="-115.71429"
|
||||
inkscape:cy="560"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1050"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata6999">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<g
|
||||
id="layer1-2-0-3"
|
||||
inkscape:label="Layer 1"
|
||||
transform="translate(538.86519,131.45315)"
|
||||
style="fill:#c83737">
|
||||
<path
|
||||
sodipodi:nodetypes="ccsccccccccccccccccccccccccccccccccccccccsccccccccccccsccsccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect929-7-9-6"
|
||||
transform="scale(0.26458333)"
|
||||
d="m -2285.8789,560.54102 h 1366.09571 c 0,0 256.17054,-354.30681 241.07225,-499.82227 -0.4423,-4.262873 -2.67383,-12.576172 -2.67383,-12.576172 l -3.02343,-14.230469 -3.41602,-16.070312 c -1.48093,-6.967208 -8.28274,-11.3852308 -15.25,-9.904297 l -196.66406,41.802734 c -6.96726,1.480933 -11.38523,8.282793 -9.9043,15.25 l 3.41602,16.070313 3.02539,14.232422 2.67187,12.576171 c 0.75886,3.57249 -1.52146,7.08391 -5.09375,7.84375 -3.57316,0.76008 -7.0859,-1.52053 -7.8457,-5.09375 l -269.87505,57.36328 c 0.7589,3.57248 -1.5214,7.08389 -5.0937,7.84375 -3.5732,0.76008 -7.0859,-1.52053 -7.8457,-5.09375 l -2.6719,-12.57617 -3.0254,-14.23047 -3.416,-16.07226 c -1.4809,-6.96721 -8.2828,-11.38327 -15.25,-9.90235 l -196.664,41.80274 c -6.9673,1.48093 -11.3853,8.28279 -9.9043,15.25 l 3.416,16.07031 3.0254,14.23047 2.6738,12.57617 12.3496,-2.62305 -12.3496,2.625 c 0.7588,3.57311 -1.5226,7.08482 -5.0957,7.84375 -3.5725,0.75889 -7.084,-1.52144 -7.8438,-5.09375 l -282.1875,59.98047 c -0.2099,0.0446 -0.411,-0.0266 -0.5586,-0.16016 -9.2045,3.59809 -19,5.44552 -28.8828,5.44727 -39.4214,-0.0388 -72.8399,-29.00516 -78.4765,-68.02148 l -107.1133,-265.115238 c -3.3865,1.367887 -7.2408,-0.26807 -8.6094,-3.654297 -1.3692,-3.387063 0.267,-7.242714 3.6543,-8.611328 l 11.9199,-4.816406 13.4903,-5.449219 15.2343,-6.154297 c 6.6042,-2.668271 9.7718,-10.134044 7.1036,-16.738281 l -75.3184,-186.417964 c -2.6683,-6.60421 -10.1321,-9.77374 -16.7363,-7.10547 l -15.2344,6.15429 -13.4883,5.45118 -11.9219,4.8164 c -3.387,1.3676 -7.2415,-0.26931 -8.6093,-3.65625 -1.3679,-3.38652 0.2681,-7.24075 3.6543,-8.60937 l -103.3555,-255.81446 v 0 c -3.3865,1.36782 -7.2407,-0.26812 -8.6093,-3.65429 -1.3692,-3.38706 0.267,-7.24272 3.6543,-8.61133 l 11.9218,-4.81641 13.4903,-5.44922 15.2324,-6.15429 c 6.6042,-2.66827 9.7738,-10.13413 7.1055,-16.73828 L -2159,-769.68359 c -2.6683,-6.60424 -10.1341,-9.77374 -16.7383,-7.10547 l -15.2324,6.15429 -13.4902,5.45118 -11.9063,4.81054 -0.016,0.006 c -93.0715,33.85033 -69.4957,1320.90807 -69.4957,1320.90807 z"
|
||||
style="opacity:1;vector-effect:none;fill:#c83737;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
var footstring = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="26.457907mm"
|
||||
height="22.513216mm"
|
||||
viewBox="0 0 26.457907 22.513216"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
sodipodi:docname="chair_foot.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.24748738"
|
||||
inkscape:cx="1176.0282"
|
||||
inkscape:cy="684.08403"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(595.6009,-125.79659)">
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:#0000ff;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m -594.50431,148.30981 h 19.17899 c 0,0 6.42898,-5.96024 6.17499,-8.40814 -0.007,-0.0717 -0.045,-0.21156 -0.045,-0.21156 l -0.0509,-0.23939 -0.0575,-0.27034 c -0.0249,-0.1172 -0.13933,-0.19152 -0.25654,-0.16661 l -3.30833,0.70322 c -0.11721,0.0249 -0.19153,0.13933 -0.16661,0.25653 l 0.0575,0.27034 0.0509,0.23943 0.0449,0.21155 c 0.0128,0.0601 -0.0256,0.11917 -0.0857,0.13195 -0.0601,0.0128 -0.1192,-0.0256 -0.13198,-0.0857 l -4.53991,0.96498 c 0.0128,0.0601 -0.0256,0.11916 -0.0857,0.13195 -0.0601,0.0128 -0.11921,-0.0256 -0.13199,-0.0857 l -0.0449,-0.21156 -0.0509,-0.23939 -0.0575,-0.27037 c -0.0249,-0.11721 -0.13934,-0.1915 -0.25654,-0.16658 l -3.30834,0.70321 c -0.1172,0.0249 -0.19152,0.13934 -0.16661,0.25654 l 0.0575,0.27034 0.0509,0.23939 0.045,0.21156 0.20775,-0.0441 -0.20775,0.0442 c 0.0128,0.0601 -0.0256,0.11919 -0.0857,0.13195 -0.0601,0.0128 -0.11917,-0.0256 -0.13195,-0.0857 l -4.74703,1.00901 c -0.004,7.5e-4 -0.007,-4.5e-4 -0.009,-0.003 -0.15484,0.0605 -0.31962,0.0916 -0.48588,0.0916 -0.66315,-6.5e-4 -1.22533,-0.48793 -1.32015,-1.14427 l -1.80189,-4.45984 c -0.057,0.023 -0.1218,-0.005 -0.14483,-0.0615 -0.023,-0.057 0.005,-0.12184 0.0615,-0.14487 l 0.20052,-0.081 0.22694,-0.0917 0.25627,-0.10353 c 0.1111,-0.0449 0.16438,-0.17047 0.1195,-0.28157 l -1.26703,-3.13597 c -0.0449,-0.1111 -0.17044,-0.16442 -0.28154,-0.11953 l -0.25628,0.10353 -0.2269,0.0917 -0.20055,0.081 c -0.057,0.023 -0.12182,-0.005 -0.14483,-0.0615 -0.023,-0.057 0.005,-0.1218 0.0615,-0.14483 l -1.73867,-4.30337 v 0 c -0.057,0.023 -0.12181,-0.005 -0.14483,-0.0615 -0.023,-0.057 0.004,-0.12184 0.0615,-0.14486 l 0.20055,-0.081 0.22693,-0.0917 0.25625,-0.10353 c 0.1111,-0.0449 0.16441,-0.17048 0.11953,-0.28158 l -1.26706,-3.13597 c -0.0449,-0.1111 -0.17048,-0.16441 -0.28158,-0.11953 l -0.25624,0.10353 -0.22694,0.0917 -0.20029,0.0809 -2.7e-4,1e-4 c -1.56567,0.56944 0.51315,22.22066 0.51315,22.22066 z"
|
||||
id="rect929"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccsccccccccccccccccccccccccccccccccccccccsccccccccccccsccsccccc" />
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
var seatstring = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg38141"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="chairseat.svg">
|
||||
<defs
|
||||
id="defs38135" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:cx="-115.71429"
|
||||
inkscape:cy="560"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1050"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata38138">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:#a05a2c;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="m -95.394613,430.4764 c 40.10306,0 72.38889,-32.28531 72.38887,-72.38839 v -255.2237 c 0,-40.103049 -32.28581,-72.388329 -72.38887,-72.388329 H -350.61829 c -40.10307,0 -72.38787,32.28528 -72.38787,72.388329 v 255.2237 c 0,40.10308 32.2848,72.38839 72.38787,72.38839 z m -18.361127,-78.25005 a 1.7499995,1.7499997 0 0 1 -1.74977,-1.74974 h -14.00018 a 1.7499995,1.7499997 0 0 1 -1.74977,1.74974 1.7499995,1.7499997 0 0 1 -1.75027,-1.74974 1.7499995,1.7499997 0 0 1 0.0238,-0.2739 c -0.0148,-0.0418 -0.0238,-0.086 -0.0238,-0.13279 v -59.18711 c 0,-0.0456 0.009,-0.0884 0.0228,-0.12917 a 1.7499995,1.7499997 0 0 1 -0.0228,-0.27699 1.7499995,1.7499997 0 0 1 1.75027,-1.7503 1.7499995,1.7499997 0 0 1 1.74977,1.7503 h 14.00018 a 1.7499995,1.7499997 0 0 1 1.74977,-1.7503 1.7499995,1.7499997 0 0 1 1.75028,1.7503 1.7499995,1.7499997 0 0 1 -0.0238,0.27387 c 0.0138,0.0417 0.0238,0.0856 0.0238,0.13229 v 59.18711 c 0,0.0458 -0.009,0.0891 -0.0233,0.1302 a 1.7499995,1.7499997 0 0 1 0.0233,0.27649 1.7499995,1.7499997 0 0 1 -1.75028,1.74974 z m -201.00014,0 a 1.7499995,1.7499997 0 0 1 -1.74977,-1.74974 h -14.00017 a 1.7499995,1.7499997 0 0 1 -1.74977,1.74974 1.7499995,1.7499997 0 0 1 -1.75028,-1.74974 1.7499995,1.7499997 0 0 1 0.0238,-0.2739 c -0.0148,-0.0418 -0.0238,-0.086 -0.0238,-0.13279 v -59.18711 c 0,-0.0459 0.009,-0.0891 0.0233,-0.1302 a 1.7499995,1.7499997 0 0 1 -0.0233,-0.27596 1.7499995,1.7499997 0 0 1 1.75028,-1.7503 1.7499995,1.7499997 0 0 1 1.74977,1.7503 h 14.00017 a 1.7499995,1.7499997 0 0 1 1.74977,-1.7503 1.7499995,1.7499997 0 0 1 1.75028,1.7503 1.7499995,1.7499997 0 0 1 -0.0238,0.27387 c 0.0148,0.0417 0.0238,0.0856 0.0238,0.13229 v 59.18711 c 0,0.0455 -0.009,0.0884 -0.0227,0.12917 a 1.7499995,1.7499997 0 0 1 0.0227,0.27752 1.7499995,1.7499997 0 0 1 -1.75028,1.74974 z m 201.00014,-139.99972 a 1.7499995,1.7499998 0 0 1 -1.74977,-1.75027 h -14.00018 a 1.7499995,1.7499998 0 0 1 -1.74977,1.75027 1.7499995,1.7499998 0 0 1 -1.75027,-1.75027 1.7499995,1.7499998 0 0 1 0.0238,-0.2739 c -0.0148,-0.0417 -0.0238,-0.086 -0.0238,-0.13279 v -59.18658 c 0,-0.0452 0.009,-0.0882 0.0228,-0.1292 a 1.7499995,1.7499998 0 0 1 -0.0228,-0.27749 1.7499995,1.7499998 0 0 1 1.75027,-1.74977 1.7499995,1.7499998 0 0 1 1.74977,1.74977 h 14.00018 a 1.7499995,1.7499998 0 0 1 1.74977,-1.74977 1.7499995,1.7499998 0 0 1 1.75028,1.74977 1.7499995,1.7499998 0 0 1 -0.0238,0.27337 c 0.0148,0.0419 0.0238,0.0863 0.0238,0.13332 v 59.18658 c 0,0.0458 -0.009,0.0891 -0.0233,0.13022 a 1.7499995,1.7499998 0 0 1 0.0233,0.27647 1.7499995,1.7499998 0 0 1 -1.75028,1.75027 z m -201.00014,0 a 1.7499996,1.7499998 0 0 1 -1.74977,-1.75027 h -14.00017 a 1.7499996,1.7499998 0 0 1 -1.74977,1.75027 1.7499996,1.7499998 0 0 1 -1.75028,-1.75027 1.7499996,1.7499998 0 0 1 0.0238,-0.2739 c -0.0148,-0.0417 -0.0238,-0.086 -0.0238,-0.13279 v -59.18658 c 0,-0.0463 0.009,-0.0892 0.0233,-0.13023 a 1.7499996,1.7499998 0 0 1 -0.0233,-0.27646 1.7499996,1.7499998 0 0 1 1.75028,-1.74977 1.7499996,1.7499998 0 0 1 1.74977,1.74977 h 14.00017 a 1.7499996,1.7499998 0 0 1 1.74977,-1.74977 1.7499996,1.7499998 0 0 1 1.75028,1.74977 1.7499996,1.7499998 0 0 1 -0.0238,0.27337 c 0.0148,0.0419 0.0238,0.0863 0.0238,0.13332 v 59.18658 c 0,0.0455 -0.009,0.0884 -0.0227,0.12919 a 1.7499996,1.7499998 0 0 1 0.0227,0.2775 1.7499996,1.7499998 0 0 1 -1.75028,1.75027 z"
|
||||
id="rect1011-0-7"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>`;
|
||||
|
||||
|
||||
|
||||
paper.install(window);
|
||||
window.onload = function() {
|
||||
// Setup directly from canvas id:
|
||||
paper.setup('myCanvas');
|
||||
|
||||
Leaf.veinArea = new Path();
|
||||
Leaf.auxinArea = new Path();
|
||||
Leaf.size = 100; //200;
|
||||
Leaf.auxinR = 13;
|
||||
Leaf.veinR = Leaf.auxinR;
|
||||
Leaf.auxinNr = 30;
|
||||
Leaf.veinGrowRate = 2;
|
||||
Leaf.leafGrowRate = 2; //1.04;
|
||||
Leaf.maxGrowIters = 100;
|
||||
//Leaf.maxSize = 1338*0.75; //legs
|
||||
//Leaf.maxSize = 1511*0.75; //seat
|
||||
Leaf.maxSize = 500;
|
||||
Leaf.branches = [];
|
||||
Leaf.auxin = [];
|
||||
Leaf.color = Color.random();
|
||||
|
||||
Leaf.finishedShape = new Path();
|
||||
Leaf.finished = false;
|
||||
Leaf.final = false;
|
||||
|
||||
Leaf.mouse = new Path.Circle(new Point(0,0),10);
|
||||
|
||||
Leaf.uniform = true;
|
||||
|
||||
|
||||
var leaf = new Leaf(new Point(view.bounds.width/2,view.bounds.height-100));
|
||||
Leaf.trycount = 10;
|
||||
|
||||
/*view.onMouseDown = function(event){
|
||||
|
||||
//leaf.iteration();
|
||||
//console.log(leaf.veins);
|
||||
}
|
||||
|
||||
view.onMouseMove = function(event){
|
||||
|
||||
//Leaf.mouse.position = event.point;
|
||||
//var b = leaf.veins.getClosestBranch(event.point);
|
||||
//Leaf.mouse.fillColor = b.path.strokeColor;
|
||||
//console.log(leaf.veins);
|
||||
}*/
|
||||
|
||||
view.onClick = function(event){
|
||||
var svg = project.exportSVG({ asString: true });
|
||||
var svgBlob = new Blob([svg], {type:"image/svg+xml;charset=utf-8"});
|
||||
var svgUrl = URL.createObjectURL(svgBlob);
|
||||
var downloadLink = document.createElement("a");
|
||||
downloadLink.href = svgUrl;
|
||||
downloadLink.download = "growth.svg";
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
}
|
||||
|
||||
view.onFrame = function(event) {
|
||||
// On each frame, rotate the path by 3 degrees:
|
||||
|
||||
if(leaf.shape.bounds.height<Leaf.maxSize){
|
||||
leaf.iteration();
|
||||
}else{
|
||||
|
||||
if(!Leaf.finished){
|
||||
|
||||
leaf.iteration();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
if(!Leaf.final){
|
||||
console.log('finalize');
|
||||
leaf.removeSmallVeins();
|
||||
|
||||
leaf.sortChildbranches();
|
||||
leaf.rearrangeBranches();
|
||||
leaf.calcWidth();
|
||||
leaf.shape.remove();
|
||||
Leaf.final = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Auxin{
|
||||
constructor(point, closestVein){
|
||||
this.pos = point;
|
||||
this.closest = closestVein;
|
||||
this.drawNode(this.pos);
|
||||
Leaf.auxin.push(this);
|
||||
}
|
||||
|
||||
drawNode(point){
|
||||
this.node = new Path.Circle(point,3);
|
||||
this.node.fillColor = 'red';
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.node.remove();
|
||||
//this.node.fillColor = 'pink';
|
||||
}
|
||||
|
||||
scale(perc, pos){
|
||||
this.node.scale(perc, pos);
|
||||
this.pos = this.node.position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Leaf{
|
||||
|
||||
|
||||
|
||||
constructor(startpoint) {
|
||||
this.pos = startpoint;
|
||||
|
||||
//this.shape = new Path.Circle(new Point(this.pos.x-Leaf.size,this.pos.y-Leaf.size),Leaf.size);
|
||||
|
||||
/*this.shape = new Path();
|
||||
this.shape.add(new Segment(new Point(this.pos.x, this.pos.y), new Point(Leaf.size,0), new Point(-Leaf.size,0)));
|
||||
this.shape.add(new Point(this.pos.x, this.pos.y-Leaf.size));
|
||||
this.shape.closed = true;
|
||||
*/
|
||||
|
||||
var rect = new Rectangle(new Point(startpoint.x-Leaf.size/2, startpoint.y-Leaf.size), new Size(Leaf.size, Leaf.size));
|
||||
this.shape = new Path.Rectangle(rect,10);
|
||||
|
||||
//this.shape = new Path.RegularPolygon(new Point(this.pos.x, this.pos.y-Leaf.size), 3, 50);
|
||||
|
||||
|
||||
//this.shape = project.importSVG(footstring2);
|
||||
//this.shape = project.importSVG(seatstring);
|
||||
|
||||
//this.shape = this.shape.children[1];
|
||||
|
||||
this.shape.strokeColor = 'black';
|
||||
this.shape.strokeWidth = 3;
|
||||
this.shape.fillColor = 'blue';
|
||||
|
||||
|
||||
//this.shape.position = new Point(this.pos.x, this.pos.y);
|
||||
this.shape.position = new Point(this.pos.x, this.pos.y);
|
||||
this.shape.scale(Leaf.size/this.shape.bounds.height, this.pos);
|
||||
//this.shape.translate(new Point(-this.shape.bounds.width/2+Leaf.veinR/2,-this.shape.bounds.height/3+Leaf.veinR/2));
|
||||
|
||||
|
||||
|
||||
this.veins = new VeinGraph(this.pos);
|
||||
this.auxin = [];
|
||||
this.growCount = 0;
|
||||
}
|
||||
|
||||
iteration(){
|
||||
|
||||
this.growVeins();
|
||||
this.removeConsumedAuxin();
|
||||
if(this.shape.bounds.height<Leaf.maxSize){
|
||||
this.growLeaf();
|
||||
}
|
||||
if(!this.placeAuxin()){
|
||||
Leaf.trycount--;
|
||||
}else{
|
||||
Leaf.trycount = 10;
|
||||
}
|
||||
if(Leaf.trycount==0){
|
||||
Leaf.finished = true;
|
||||
}
|
||||
this.attractVeins();
|
||||
}
|
||||
|
||||
placeAuxin(){
|
||||
var f = false;
|
||||
for(var i = 0; i<Leaf.auxinNr; i++){
|
||||
var point = new Point(this.shape.bounds.width, this.shape.bounds.height).multiply(Point.random()).add(this.shape.bounds.topLeft);
|
||||
|
||||
if(this.shape.contains(point) && this.veins.getDist(point)>Leaf.veinR && !this.auxinTooClose(point)){
|
||||
//if(this.isEmptyAuxinNeighbourhood(point) && this.isEmptyVeinNeighbourhood(point)){
|
||||
this.auxin.push(new Auxin(point));
|
||||
f = true;
|
||||
|
||||
|
||||
//tmp.fillColor = 'green';
|
||||
//Leaf.auxinArea.strokeColor = 'red';
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
auxinTooClose(point){
|
||||
for(var i = 0; i<Leaf.auxin.length; i++){
|
||||
if(Leaf.auxin[i].pos.getDistance(point)<Leaf.auxinR){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
removeConsumedAuxin(){
|
||||
var newauxin = []
|
||||
for(var i = 0; i<this.auxin.length; i++){
|
||||
if(this.veins.getDist(this.auxin[i].pos)>Leaf.veinR){
|
||||
newauxin.push(this.auxin[i]);
|
||||
}else{
|
||||
this.auxin[i].remove();
|
||||
}
|
||||
}
|
||||
this.auxin = newauxin;
|
||||
}
|
||||
|
||||
attractVeins(){
|
||||
for(var i = 0; i<this.auxin.length; i++){
|
||||
//var closestNode = this.veins.getClosest(this.auxin[i]);
|
||||
var n = this.veins.getClosestNode(this.auxin[i].pos);
|
||||
//var p = this.veins.getClosestNodePoint(this.auxin[i].pos);
|
||||
var b = this.veins.getClosestBranch(this.auxin[i].pos);
|
||||
n.setInfluence(this.auxin[i]);
|
||||
//var line = new Path.Line(p, this.auxin[i].pos);
|
||||
//line.strokeColor = 'grey';
|
||||
|
||||
this.auxin[i].node.fillColor = b.path.strokeColor;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
growVeins(){
|
||||
this.veins.grow();
|
||||
}
|
||||
|
||||
growLeaf(){
|
||||
var perc = (this.shape.bounds.height+Leaf.leafGrowRate)/this.shape.bounds.height;
|
||||
this.shape.scale(perc, this.pos)
|
||||
if(Leaf.uniform){
|
||||
this.scaleAuxin(perc);
|
||||
this.scaleBranches(perc);
|
||||
}
|
||||
}
|
||||
|
||||
scaleAuxin(perc){
|
||||
for(var i = 0; i<Leaf.auxin.length; i++){
|
||||
Leaf.auxin[i].scale(perc, this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
scaleBranches(perc){
|
||||
for(var i = 0; i<Leaf.branches.length; i++){
|
||||
Leaf.branches[i].scale(perc, this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
this.veins.removeSmallVeins();
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
this.veins.calcWidth();
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
this.veins.sortChildbranches();
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
this.veins.rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VeinGraph{
|
||||
|
||||
constructor(point) {
|
||||
this.root = new VeinBranch(point);
|
||||
this.root.addConnection(new Point(point.x, point.y-10));
|
||||
}
|
||||
|
||||
getClosestNode(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
|
||||
return b.getClosestNode(point);
|
||||
}
|
||||
|
||||
getClosestNodePoint(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
|
||||
return b.getClosestNodePoint(point);
|
||||
}
|
||||
|
||||
getClosestBranch(point){
|
||||
//var b = this.root.getClosestBranch(point, this.root);
|
||||
var b = this.root;
|
||||
var dist = this.root.path.getNearestPoint(point).getDistance(point);
|
||||
var dist2 = 0;
|
||||
for(var i = 0; i<Leaf.branches.length; i++){
|
||||
|
||||
dist2 = Leaf.branches[i].path.getNearestPoint(point).getDistance(point);
|
||||
|
||||
if(dist2<dist){
|
||||
|
||||
dist = dist2;
|
||||
b = Leaf.branches[i];
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
getDist(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
var p = b.getClosestPoint(point);
|
||||
return p.getDistance(point);
|
||||
}
|
||||
|
||||
|
||||
grow(){
|
||||
this.root.grow();
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
this.root.removeSmallVeins();
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
this.root.calcWidth();
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
this.root.sortChildbranches();
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
this.root.rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VeinBranch{
|
||||
constructor(point) {
|
||||
this.path = new Path();
|
||||
//var col = Color.random();
|
||||
//this.path.strokeColor = col;
|
||||
this.path.strokeColor = 'black';
|
||||
this.path.strokeCap = 'round';
|
||||
this.path.strokeWidth = 3;
|
||||
this.nodes = [];
|
||||
this.childBranches = [];
|
||||
if(point){
|
||||
this.addConnection(point);
|
||||
}
|
||||
|
||||
Leaf.branches.push(this);
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
if(this.childBranches.length>0){
|
||||
var newPath = this.path.splitAt(this.path.getLocationOf(this.childBranches[0].path.firstSegment.point));
|
||||
//newPath.strokeColor = Color.random();
|
||||
|
||||
var newBranch = new VeinBranch();
|
||||
newBranch.path = newPath;
|
||||
console.log(newBranch.childBranches);
|
||||
for(var i = 1; i<this.childBranches.length; i++){
|
||||
newBranch.childBranches.push(this.childBranches[i]);
|
||||
}
|
||||
|
||||
|
||||
this.childBranches = [this.childBranches[0]];
|
||||
this.childBranches.push(newBranch);
|
||||
console.log(this);
|
||||
|
||||
this.childBranches[0].rearrangeBranches();
|
||||
this.childBranches[1].rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
//console.log(this.childBranches);
|
||||
this.childBranches.sort((a, b) => (this.path.getOffsetOf(a.path.firstSegment.point) > this.path.getOffsetOf(b.path.firstSegment.point)) ? 1 : -1)
|
||||
//console.log(this.childBranches);
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
this.childBranches[i].sortChildbranches();
|
||||
}
|
||||
}
|
||||
|
||||
addConnection(point){
|
||||
this.path.add(point);
|
||||
this.nodes.push(new VeinNode());
|
||||
}
|
||||
|
||||
addBranch(point1, point2){
|
||||
var b = new VeinBranch(point1);
|
||||
b.addConnection(point2);
|
||||
this.childBranches.push(b);
|
||||
}
|
||||
|
||||
getClosestPoint(point){
|
||||
|
||||
var p = this.path.getNearestPoint(point);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
getClosestNode(point){
|
||||
|
||||
var dist = this.path.firstSegment.point.getDistance(point);
|
||||
var node = 0;
|
||||
for( var i = 1; i<this.path.segments.length; i++){
|
||||
var compdist = this.path.segments[i].point.getDistance(point);
|
||||
if(compdist<dist){
|
||||
node = i;
|
||||
dist = compdist;
|
||||
}
|
||||
}
|
||||
return this.nodes[node];
|
||||
}
|
||||
|
||||
getClosestNodePoint(point){
|
||||
var dist = this.path.firstSegment.point.getDistance(point);
|
||||
var node = 0;
|
||||
for( var i = 1; i<this.path.segments.length; i++){
|
||||
var compdist = this.path.segments[i].point.getDistance(point);
|
||||
if(compdist<dist){
|
||||
node = i;
|
||||
dist = compdist;
|
||||
}
|
||||
}
|
||||
return this.path.segments[node].point;
|
||||
}
|
||||
|
||||
/*getClosestBranch(point, veinbranch){
|
||||
|
||||
var p = this.path.getNearestPoint(point);
|
||||
var compdist = point.getDistance(p);
|
||||
var pvb = veinbranch.path.getNearestPoint(point);
|
||||
var dist = point.getDistance(pvb);
|
||||
|
||||
var smallest = this;
|
||||
if(dist<compdist){
|
||||
smallest = veinbranch;
|
||||
compdist = dist;
|
||||
}
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
|
||||
var b = this.childBranches[i].getClosestBranch(point,smallest);
|
||||
p = b.path.getNearestPoint(point);
|
||||
dist = point.getDistance(p);
|
||||
if(dist<compdist){
|
||||
smallest = this.childBranches[i];
|
||||
compdist = dist;
|
||||
}
|
||||
}
|
||||
return smallest;
|
||||
|
||||
}*/
|
||||
|
||||
grow(){
|
||||
for(var i = 0; i<this.nodes.length; i++){
|
||||
var n = this.nodes[i].grow(this.path.segments[i].point);
|
||||
if(n!=null){
|
||||
var newpoint = this.path.segments[i].point.add(n);
|
||||
if(i==this.nodes.length-1){
|
||||
this.addConnection(newpoint);
|
||||
}else{
|
||||
this.addBranch(this.path.segments[i].point,newpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.childBranches.length; i++){
|
||||
this.childBranches[i].grow();
|
||||
}
|
||||
}
|
||||
|
||||
scale(perc, pos){
|
||||
this.path.scale(perc, pos);
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.path.remove();
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
for(var i = this.childBranches.length-1; i>=0; i--){
|
||||
if(this.childBranches[i].path.length<Leaf.auxinR && this.childBranches[i].childBranches.length==0){
|
||||
this.childBranches[i].remove();
|
||||
this.childBranches.splice(i,1);
|
||||
}else{
|
||||
this.childBranches[i].removeSmallVeins();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
if(this.childBranches.length==0){
|
||||
//this.path.strokeWidth = 10;
|
||||
//return 10;
|
||||
this.path.strokeWidth = Leaf.auxinR-2;
|
||||
this.path.strokeColor = Leaf.color;
|
||||
return Leaf.auxinR-2;
|
||||
}
|
||||
this.path.bringToFront();
|
||||
var wid = 0;
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
var w = this.childBranches[i].calcWidth();
|
||||
wid = wid + Math.pow(w,4);
|
||||
}
|
||||
wid = Math.pow(wid, 1/4);
|
||||
this.path.strokeWidth = wid;
|
||||
this.path.strokeColor = Leaf.color;
|
||||
this.path.strokeColor.hue += wid;
|
||||
return wid;
|
||||
}
|
||||
}
|
||||
|
||||
class VeinNode{
|
||||
|
||||
constructor() {
|
||||
this.auxinInfluence = [];
|
||||
}
|
||||
|
||||
grow(pos){
|
||||
//
|
||||
if(this.auxinInfluence.length>0){
|
||||
var direction = new Point(0,0);
|
||||
var oldDists = [];
|
||||
for(var i = 0; i<this.auxinInfluence.length; i++){
|
||||
var dir = this.auxinInfluence[i].pos.subtract(pos).normalize();
|
||||
direction = direction.add(dir);
|
||||
|
||||
oldDists.push(this.auxinInfluence[i].pos.getDistance(pos));
|
||||
}
|
||||
if(direction.length<1){
|
||||
return null;
|
||||
}
|
||||
direction = direction.normalize(Leaf.veinGrowRate);
|
||||
this.auxinInfluence = [];
|
||||
return direction;
|
||||
|
||||
}
|
||||
|
||||
this.auxinInfluence = [];
|
||||
return null;
|
||||
}
|
||||
|
||||
setInfluence(point){
|
||||
this.auxinInfluence.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,579 @@
|
|||
paper.install(window);
|
||||
window.onload = function() {
|
||||
var canvas = document.getElementById('grower');
|
||||
canvas.width = 300;
|
||||
canvas.height = 300;
|
||||
paper.setup(canvas);
|
||||
|
||||
Leaf.veinArea = new Path();
|
||||
Leaf.auxinArea = new Path();
|
||||
Leaf.size = 100; //200;
|
||||
Leaf.auxinR = 9;
|
||||
Leaf.veinR = Leaf.auxinR;
|
||||
Leaf.auxinNr = 15;
|
||||
Leaf.veinGrowRate = 1.5;
|
||||
Leaf.leafGrowRate = 3; //1.04;
|
||||
Leaf.maxGrowIters = 10;
|
||||
//Leaf.maxSize = 1338*0.75; //legs
|
||||
//Leaf.maxSize = 1511*0.75; //seat
|
||||
Leaf.maxSize = 290;
|
||||
Leaf.branches = [];
|
||||
Leaf.auxin = [];
|
||||
Leaf.color = new Color(0.8, 0.8, 0.8);
|
||||
|
||||
Leaf.midlayerItems = [];
|
||||
Leaf.frontlayerItems = [];
|
||||
|
||||
Leaf.finishedShape = new Path();
|
||||
Leaf.finished = false;
|
||||
Leaf.final = false;
|
||||
|
||||
//Leaf.mouse = new Path.Circle(new Point(0,0),10);
|
||||
|
||||
Leaf.uniform = true;
|
||||
|
||||
|
||||
var leaf = new Leaf(new Point(view.bounds.width/2,view.bounds.height/2));
|
||||
Leaf.trycount = 10;
|
||||
|
||||
/*view.onClick = function(event){
|
||||
var svg = project.exportSVG({ asString: true });
|
||||
var svgBlob = new Blob([svg], {type:"image/svg+xml;charset=utf-8"});
|
||||
var svgUrl = URL.createObjectURL(svgBlob);
|
||||
var downloadLink = document.createElement("a");
|
||||
downloadLink.href = svgUrl;
|
||||
downloadLink.download = "growth.svg";
|
||||
document.body.appendChild(downloadLink);
|
||||
downloadLink.click();
|
||||
document.body.removeChild(downloadLink);
|
||||
}*/
|
||||
|
||||
view.onFrame = function(event) {
|
||||
// On each frame, rotate the path by 3 degrees:
|
||||
|
||||
if(leaf.shape.bounds.height<Leaf.maxSize){
|
||||
leaf.iteration();
|
||||
}else{
|
||||
|
||||
if(!Leaf.finished){
|
||||
|
||||
leaf.iteration();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}else{
|
||||
if(!Leaf.final){
|
||||
console.log('finalize');
|
||||
leaf.removeSmallVeins();
|
||||
|
||||
leaf.sortChildbranches();
|
||||
leaf.rearrangeBranches();
|
||||
leaf.calcWidth();
|
||||
var svgString = leaf.shape.exportSVG({asString: true, bounds: 'content'});
|
||||
console.log(svgString);
|
||||
leaf.shape.remove();
|
||||
|
||||
var midlayer = new Layer(Leaf.midlayerItems);
|
||||
var frontlayer = new Layer(Leaf.frontlayerItems);
|
||||
|
||||
Leaf.final = true;
|
||||
|
||||
setTimeout(function() {
|
||||
// fixing a race condition in some browsers
|
||||
generateTextures();
|
||||
renderScene();
|
||||
}, 10);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Auxin{
|
||||
constructor(point, closestVein){
|
||||
this.pos = point;
|
||||
this.closest = closestVein;
|
||||
this.drawNode(this.pos);
|
||||
Leaf.auxin.push(this);
|
||||
}
|
||||
|
||||
drawNode(point){
|
||||
this.node = new Path.Circle(point,0);
|
||||
this.node.fillColor = 'red';
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.node.remove();
|
||||
//this.node.fillColor = 'pink';
|
||||
}
|
||||
|
||||
scale(perc, pos){
|
||||
this.node.scale(perc, pos);
|
||||
this.pos = this.node.position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Leaf{
|
||||
|
||||
|
||||
|
||||
constructor(startpoint) {
|
||||
this.pos = startpoint;
|
||||
|
||||
//this.shape = new Path.Circle(new Point(this.pos.x-Leaf.size,this.pos.y-Leaf.size),Leaf.size);
|
||||
|
||||
/*this.shape = new Path();
|
||||
this.shape.add(new Segment(new Point(this.pos.x, this.pos.y), new Point(Leaf.size,0), new Point(-Leaf.size,0)));
|
||||
this.shape.add(new Point(this.pos.x, this.pos.y-Leaf.size));
|
||||
this.shape.closed = true;
|
||||
*/
|
||||
|
||||
var rect = new Rectangle(new Point(startpoint.x-Leaf.size/2, startpoint.y-Leaf.size), new Size(Leaf.size, Leaf.size));
|
||||
this.shape = new Path.Ellipse(rect);
|
||||
//this.shape.rotation = 20;
|
||||
|
||||
//this.shape = new Path.RegularPolygon(new Point(this.pos.x, this.pos.y-Leaf.size), 3, 50);
|
||||
|
||||
|
||||
//this.shape = project.importSVG(footstring2);
|
||||
//this.shape = project.importSVG(seatstring);
|
||||
|
||||
//this.shape = this.shape.children[1];
|
||||
|
||||
//this.shape = project.importSVG('leaf.svg', {insert: false, expandShapes: true});
|
||||
//console.log('shape', this.shape);
|
||||
//this.shape = this.shape.children[1];
|
||||
|
||||
this.shape.strokeColor = 'red';
|
||||
this.shape.strokeWidth = 2;
|
||||
this.shape.fillColor = 'yellow';
|
||||
|
||||
|
||||
//this.shape.position = new Point(this.pos.x, this.pos.y);
|
||||
this.shape.position = new Point(this.pos.x, this.pos.y);
|
||||
this.shape.scale(Leaf.size/this.shape.bounds.height, this.pos);
|
||||
//this.shape.translate(new Point(-this.shape.bounds.width/2+Leaf.veinR/2,-this.shape.bounds.height/3+Leaf.veinR/2));
|
||||
|
||||
|
||||
|
||||
this.veins = new VeinGraph(this.pos);
|
||||
this.auxin = [];
|
||||
this.growCount = 0;
|
||||
}
|
||||
|
||||
iteration(){
|
||||
|
||||
this.growVeins();
|
||||
this.removeConsumedAuxin();
|
||||
if(this.shape.bounds.height<Leaf.maxSize){
|
||||
this.growLeaf();
|
||||
}
|
||||
if(!this.placeAuxin()){
|
||||
Leaf.trycount--;
|
||||
}else{
|
||||
Leaf.trycount = 10;
|
||||
}
|
||||
if(Leaf.trycount==0){
|
||||
Leaf.finished = true;
|
||||
}
|
||||
this.attractVeins();
|
||||
}
|
||||
|
||||
placeAuxin(){
|
||||
var f = false;
|
||||
for(var i = 0; i<Leaf.auxinNr; i++){
|
||||
var randPoint = new Point(seededRand(), seededRand());
|
||||
var point = new Point(this.shape.bounds.width, this.shape.bounds.height).multiply(randPoint).add(this.shape.bounds.topLeft);
|
||||
|
||||
if(this.shape.contains(point) && this.veins.getDist(point)>Leaf.veinR && !this.auxinTooClose(point)){
|
||||
//if(this.isEmptyAuxinNeighbourhood(point) && this.isEmptyVeinNeighbourhood(point)){
|
||||
this.auxin.push(new Auxin(point));
|
||||
f = true;
|
||||
|
||||
|
||||
//tmp.fillColor = 'green';
|
||||
//Leaf.auxinArea.strokeColor = 'red';
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
auxinTooClose(point){
|
||||
for(var i = 0; i<Leaf.auxin.length; i++){
|
||||
if(Leaf.auxin[i].pos.getDistance(point)<Leaf.auxinR){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
removeConsumedAuxin(){
|
||||
var newauxin = []
|
||||
for(var i = 0; i<this.auxin.length; i++){
|
||||
if(this.veins.getDist(this.auxin[i].pos)>Leaf.veinR){
|
||||
newauxin.push(this.auxin[i]);
|
||||
}else{
|
||||
this.auxin[i].remove();
|
||||
}
|
||||
}
|
||||
this.auxin = newauxin;
|
||||
}
|
||||
|
||||
attractVeins(){
|
||||
for(var i = 0; i<this.auxin.length; i++){
|
||||
//var closestNode = this.veins.getClosest(this.auxin[i]);
|
||||
var n = this.veins.getClosestNode(this.auxin[i].pos);
|
||||
//var p = this.veins.getClosestNodePoint(this.auxin[i].pos);
|
||||
var b = this.veins.getClosestBranch(this.auxin[i].pos);
|
||||
n.setInfluence(this.auxin[i]);
|
||||
//var line = new Path.Line(p, this.auxin[i].pos);
|
||||
//line.strokeColor = 'grey';
|
||||
|
||||
this.auxin[i].node.fillColor = b.path.strokeColor;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
growVeins(){
|
||||
this.veins.grow();
|
||||
}
|
||||
|
||||
growLeaf(){
|
||||
var perc = (this.shape.bounds.height+Leaf.leafGrowRate)/this.shape.bounds.height;
|
||||
this.shape.scale(perc, this.pos)
|
||||
if(Leaf.uniform){
|
||||
this.scaleAuxin(perc);
|
||||
this.scaleBranches(perc);
|
||||
}
|
||||
}
|
||||
|
||||
scaleAuxin(perc){
|
||||
for(var i = 0; i<Leaf.auxin.length; i++){
|
||||
Leaf.auxin[i].scale(perc, this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
scaleBranches(perc){
|
||||
for(var i = 0; i<Leaf.branches.length; i++){
|
||||
Leaf.branches[i].scale(perc, this.pos);
|
||||
}
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
this.veins.removeSmallVeins();
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
this.veins.calcWidth();
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
this.veins.sortChildbranches();
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
this.veins.rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VeinGraph{
|
||||
|
||||
constructor(point) {
|
||||
this.root = new VeinBranch(point);
|
||||
this.root.addConnection(new Point(point.x, point.y-10));
|
||||
}
|
||||
|
||||
getClosestNode(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
|
||||
return b.getClosestNode(point);
|
||||
}
|
||||
|
||||
getClosestNodePoint(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
|
||||
return b.getClosestNodePoint(point);
|
||||
}
|
||||
|
||||
getClosestBranch(point){
|
||||
//var b = this.root.getClosestBranch(point, this.root);
|
||||
var b = this.root;
|
||||
var dist = this.root.path.getNearestPoint(point).getDistance(point);
|
||||
var dist2 = 0;
|
||||
for(var i = 0; i<Leaf.branches.length; i++){
|
||||
|
||||
dist2 = Leaf.branches[i].path.getNearestPoint(point).getDistance(point);
|
||||
|
||||
if(dist2<dist){
|
||||
|
||||
dist = dist2;
|
||||
b = Leaf.branches[i];
|
||||
}
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
getDist(point){
|
||||
var b = this.getClosestBranch(point);
|
||||
var p = b.getClosestPoint(point);
|
||||
return p.getDistance(point);
|
||||
}
|
||||
|
||||
|
||||
grow(){
|
||||
this.root.grow();
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
this.root.removeSmallVeins();
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
this.root.calcWidth();
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
this.root.sortChildbranches();
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
this.root.rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VeinBranch{
|
||||
constructor(point) {
|
||||
this.path = new Path();
|
||||
//var col = Color.random();
|
||||
//this.path.strokeColor = col;
|
||||
this.path.strokeColor = 'red';
|
||||
this.path.strokeCap = 'round';
|
||||
this.path.strokeWidth = 3;
|
||||
this.nodes = [];
|
||||
this.childBranches = [];
|
||||
if(point){
|
||||
this.addConnection(point);
|
||||
}
|
||||
|
||||
Leaf.branches.push(this);
|
||||
}
|
||||
|
||||
rearrangeBranches(){
|
||||
if(this.childBranches.length>0){
|
||||
var newPath = this.path.splitAt(this.path.getLocationOf(this.childBranches[0].path.firstSegment.point));
|
||||
//newPath.strokeColor = Color.random();
|
||||
|
||||
var newBranch = new VeinBranch();
|
||||
newBranch.path = newPath;
|
||||
console.log(newBranch.childBranches);
|
||||
for(var i = 1; i<this.childBranches.length; i++){
|
||||
newBranch.childBranches.push(this.childBranches[i]);
|
||||
}
|
||||
|
||||
|
||||
this.childBranches = [this.childBranches[0]];
|
||||
this.childBranches.push(newBranch);
|
||||
console.log(this);
|
||||
|
||||
this.childBranches[0].rearrangeBranches();
|
||||
this.childBranches[1].rearrangeBranches();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sortChildbranches(){
|
||||
//console.log(this.childBranches);
|
||||
this.childBranches.sort((a, b) => (this.path.getOffsetOf(a.path.firstSegment.point) > this.path.getOffsetOf(b.path.firstSegment.point)) ? 1 : -1)
|
||||
//console.log(this.childBranches);
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
this.childBranches[i].sortChildbranches();
|
||||
}
|
||||
}
|
||||
|
||||
addConnection(point){
|
||||
this.path.add(point);
|
||||
this.nodes.push(new VeinNode());
|
||||
}
|
||||
|
||||
addBranch(point1, point2){
|
||||
var b = new VeinBranch(point1);
|
||||
b.addConnection(point2);
|
||||
this.childBranches.push(b);
|
||||
}
|
||||
|
||||
getClosestPoint(point){
|
||||
|
||||
var p = this.path.getNearestPoint(point);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
getClosestNode(point){
|
||||
|
||||
var dist = this.path.firstSegment.point.getDistance(point);
|
||||
var node = 0;
|
||||
for( var i = 1; i<this.path.segments.length; i++){
|
||||
var compdist = this.path.segments[i].point.getDistance(point);
|
||||
if(compdist<dist){
|
||||
node = i;
|
||||
dist = compdist;
|
||||
}
|
||||
}
|
||||
return this.nodes[node];
|
||||
}
|
||||
|
||||
getClosestNodePoint(point){
|
||||
var dist = this.path.firstSegment.point.getDistance(point);
|
||||
var node = 0;
|
||||
for( var i = 1; i<this.path.segments.length; i++){
|
||||
var compdist = this.path.segments[i].point.getDistance(point);
|
||||
if(compdist<dist){
|
||||
node = i;
|
||||
dist = compdist;
|
||||
}
|
||||
}
|
||||
return this.path.segments[node].point;
|
||||
}
|
||||
|
||||
/*getClosestBranch(point, veinbranch){
|
||||
|
||||
var p = this.path.getNearestPoint(point);
|
||||
var compdist = point.getDistance(p);
|
||||
var pvb = veinbranch.path.getNearestPoint(point);
|
||||
var dist = point.getDistance(pvb);
|
||||
|
||||
var smallest = this;
|
||||
if(dist<compdist){
|
||||
smallest = veinbranch;
|
||||
compdist = dist;
|
||||
}
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
|
||||
var b = this.childBranches[i].getClosestBranch(point,smallest);
|
||||
p = b.path.getNearestPoint(point);
|
||||
dist = point.getDistance(p);
|
||||
if(dist<compdist){
|
||||
smallest = this.childBranches[i];
|
||||
compdist = dist;
|
||||
}
|
||||
}
|
||||
return smallest;
|
||||
|
||||
}*/
|
||||
|
||||
grow(){
|
||||
for(var i = 0; i<this.nodes.length; i++){
|
||||
var n = this.nodes[i].grow(this.path.segments[i].point);
|
||||
if(n!=null){
|
||||
var newpoint = this.path.segments[i].point.add(n);
|
||||
if(i==this.nodes.length-1){
|
||||
this.addConnection(newpoint);
|
||||
}else{
|
||||
this.addBranch(this.path.segments[i].point,newpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.childBranches.length; i++){
|
||||
this.childBranches[i].grow();
|
||||
}
|
||||
}
|
||||
|
||||
scale(perc, pos){
|
||||
this.path.scale(perc, pos);
|
||||
}
|
||||
|
||||
remove(){
|
||||
this.path.remove();
|
||||
}
|
||||
|
||||
removeSmallVeins(){
|
||||
for(var i = this.childBranches.length-1; i>=0; i--){
|
||||
if(this.childBranches[i].path.length<Leaf.auxinR && this.childBranches[i].childBranches.length==0){
|
||||
this.childBranches[i].remove();
|
||||
this.childBranches.splice(i,1);
|
||||
}else{
|
||||
this.childBranches[i].removeSmallVeins();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
calcWidth(){
|
||||
if(this.childBranches.length==0){
|
||||
//this.path.strokeWidth = 10;
|
||||
//return 10;
|
||||
this.path.strokeWidth = Leaf.auxinR-7;
|
||||
this.path.strokeColor = Leaf.color;
|
||||
return Leaf.auxinR-2;
|
||||
}
|
||||
this.path.bringToFront();
|
||||
var wid = 0;
|
||||
var col = -12;
|
||||
for(var i = 0; i<this.childBranches.length; i++){
|
||||
var w = this.childBranches[i].calcWidth();
|
||||
wid = wid + Math.pow(w,5);
|
||||
col += w;
|
||||
}
|
||||
wid = Math.pow(wid, 1/5);
|
||||
this.path.strokeWidth = wid-9;
|
||||
this.path.strokeColor = Leaf.color;
|
||||
var innerPath1 = this.path.clone();
|
||||
var innerPath2 = this.path.clone();
|
||||
this.path.strokeColor.brightness -= col/127;
|
||||
innerPath1.strokeWidth = (wid-3)/1.5;
|
||||
innerPath1.strokeColor.brightness -= col/127;
|
||||
innerPath2.strokeWidth = (wid-3)/2-2;
|
||||
innerPath2.strokeColor.brightness -= col/16;
|
||||
Leaf.midlayerItems.push(innerPath1);
|
||||
Leaf.frontlayerItems.push(innerPath2);
|
||||
return wid;
|
||||
}
|
||||
}
|
||||
|
||||
class VeinNode{
|
||||
|
||||
constructor() {
|
||||
this.auxinInfluence = [];
|
||||
}
|
||||
|
||||
grow(pos){
|
||||
//
|
||||
if(this.auxinInfluence.length>0){
|
||||
var direction = new Point(0,0);
|
||||
var oldDists = [];
|
||||
for(var i = 0; i<this.auxinInfluence.length; i++){
|
||||
var dir = this.auxinInfluence[i].pos.subtract(pos).normalize();
|
||||
direction = direction.add(dir);
|
||||
|
||||
oldDists.push(this.auxinInfluence[i].pos.getDistance(pos));
|
||||
}
|
||||
if(direction.length<1){
|
||||
return null;
|
||||
}
|
||||
direction = direction.normalize(Leaf.veinGrowRate);
|
||||
this.auxinInfluence = [];
|
||||
return direction;
|
||||
|
||||
}
|
||||
|
||||
this.auxinInfluence = [];
|
||||
return null;
|
||||
}
|
||||
|
||||
setInfluence(point){
|
||||
this.auxinInfluence.push(point);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Load the Paper.js library -->
|
||||
<script type="text/javascript" src="../paper-full.js"></script>
|
||||
|
||||
<script type="text/javascript" src="veingen.js">
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript" src="../offset.js"></script>
|
||||
<canvas id="myCanvas" style="width:100%;" resize></canvas>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue