1
0
Fork 0

initial commit

main
Alina Marquardt 2023-04-04 21:21:43 +02:00
commit e3677f6f65
14 changed files with 69748 additions and 0 deletions

7
README.md Normal file
View File

@ -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
![Screenshot](example/screenshot.jpg)

BIN
example/screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 KiB

576
gradientmaps.js Executable file
View File

@ -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) { }

41
index.html Normal file
View File

@ -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>

7
leaf.svg Normal file
View File

@ -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

2
modifier.js Executable file

File diff suppressed because one or more lines are too long

292
offset.js Normal file
View File

@ -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);
},
};

16494
paper-full.js Normal file

File diff suppressed because it is too large Load Diff

583
stackblur.js Executable file
View File

@ -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 });
})));

173
textured3d.js Normal file
View File

@ -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() {
//
});

50214
three.js Executable file

File diff suppressed because one or more lines are too long

761
veingen.js Normal file
View File

@ -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);
}
}

579
veingen3d.js Normal file
View File

@ -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);
}
}

19
veins.html Normal file
View File

@ -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>