Add VoronoiTool to examples (work in progress).

This commit is contained in:
Jonathan Puckey 2011-03-09 19:40:34 +01:00
parent b643fdfc1f
commit 1bb9a262d9

View file

@ -0,0 +1,627 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Example</title>
<script type="text/javascript">var root = '../../'</script>
<script type="text/javascript" src="../../src/load.js"></script>
<script type="text/javascript" src="http://www.raymondhill.net/voronoi/rhill-voronoi-core-min.js"></script>
<script type="text/paperscript" canvas="canvas">
document.currentStyle = {
strokeWidth: 1,
}
var shiftDown, altDown;
function onMouseDown(event) {
// shiftDown = Key.isDown('shift');
// altDown = Key.isDown('alt');
voronoiDrawer.initialize();
if(altDown || shiftDown)
var removed = voronoiDrawer.removePoint(event.point);
if(altDown) {
if(removed) {
voronoiDrawer.initPoints();
voronoiDrawer.render();
}
} else {
voronoiDrawer.addAndRender(event.point, true, false)
}
}
function onMouseDrag(event) {
if(!altDown) {
if(event.count == 1)
voronoiDrawer.showPointsDown();
voronoiDrawer.addAndRender(event.point, true, true)
voronoiDrawer.showPointsDrag();
}
}
function onMouseUp(event) {
if(!altDown && !shiftDown) {
voronoiDrawer.addAndRender(event.point, false, false)
}
if(shiftDown) {
voronoiDrawer.initPoints();
voronoiDrawer.render();
}
}
var voronoiDrawer = {
voronoi: new Voronoi(),
points: [],
initialize: function() {
this.getGroup();
this.setBbox();
},
getGroup: function() {
// this.group = null;
// this.vGroup = null;
// var groups = document.getItems({
// type: Group
// });
// for(var i = 0, l = groups.length; i < l; i++) {
// var group = groups[i];
// if(group.name == 'Voronoi') {
// this.group = group;
// this.vGroup = group.children['vGroup'];
// i = l;
// }
// }
if(!this.group) {
this.group = new Group();
this.group.name = 'Voronoi';
this.group.data = {};
var path = new Path();
this.group.data.path = path;
path.removeFromParent();
}
return this.group;
},
initPoints: function() {
this.points = [];
var segments = this.group.data.path.segments;
for(var i = 0, l = segments.length; i < l; i++) {
this.points.push(segments[i].point);
}
},
setBbox: function() {
this.bbox = {
xl: 0,
xr: document.bounds.width,
yt: 0,
yb: document.bounds.height
}
},
addPoint: function(point) {
this.group.data.path.add(point.round());
},
setPoints: function(points) {
for(var i = 0, l = points.length; i < l; i++) {
points[i] = points[i].round();
}
this.points = points;
this.group.data.path = new Path(points);
},
addRandomPoint: function() {
var point = Point.random() * document.bounds.size;
this.addPoint(point);
},
pop: function() {
this.group.data.path.segments.pop();
},
showPointsDown: function() {
var size = 2 / document.activeView.zoom;
for(var i = 0, l = this.points.length - 1; i < l; i++) {
new Path.Circle(this.points[i], size).removeOn({
up: true
});
}
},
showPointsDrag: function() {
var size = 2 / document.activeView.zoom;
new Path.Circle(this.points[this.points.length - 1], size).removeOn({
up: true,
drag: true
});
},
getOffsettedPath: function(path) {
var cloned = path.clone();
var offset = Math.min(5, Math.max(Math.abs(path.area) / 4000, 1));
path.fillColor = null;
path.strokeColor='black';
path.strokeWidth = offset * 2;
var offsetted = path.expand();
if(offsetted && offsetted.children.length) {
var compoundPath = offsetted.children[0];
if(compoundPath.children.length) {
var inner = offsetted.children[0].children[0];
inner.moveAbove(offsetted);
inner.fillColor = 'black';
}
offsetted.remove();
}
if(inner)
inner.data.path = cloned;
return inner;
},
removeSmallBits: function(path) {
var averageLength = path.length / path.segments.length;
for(var i = 0, l = path.segments.length; i < l; i++) {
var segment = path.segments[i];
var nextSegment = path.segments[i + 1 >= l ? (i + 1) % l : i + 1];
var length = (nextSegment.point - segment.point).length;
if(length / averageLength < 0.1) {
var prevSegment = path.segments[i - 1 < 0 ? l - 1 : i - 1];
var line1 = new Line(prevSegment.point, segment.point, true);
var afterSegment = path.segments[i + 2 >= l ? (i + 2) % l : i + 2];
var line2 = new Line(nextSegment.point, afterSegment.point, true);
var intersection = line1.getIntersectionPoint(line2);
path.insert(i + 1 >= l ? (i + 1) % l : i + 1, intersection);
segment.remove();
nextSegment.remove();
l = path.segments.length;
i = 0;
averageLength = path.length / path.segments.length;
};
}
},
roundPath: function(path) {
var segments = path.segments.clone();
path.segments = [];
for(var i = 0, l = segments.length; i < l; i++) {
var curPoint = segments[i].point;
var nextPoint = segments[i + 1 == l ? 0 : i + 1].point;
var prevPoint = segments[i - 1 < 0 ? segments.length - 1 : i - 1].point;
var nextDelta = curPoint - nextPoint;
var prevDelta = curPoint - prevPoint;
nextDelta.length *= 0.2;
prevDelta.length *= 0.2;
path.add(curPoint - prevDelta);
path.add(curPoint - nextDelta);
}
},
redrawVoronoi: function(diagram, activePoint) {
for(var i = 0, l = diagram.sites.length; i < l; i++) {
this.drawCell(diagram, i);
}
},
drawVoronoi: function(diagram, activePoint) {
if(this.points.length == 2)
this.drawCell(diagram, 0, activePoint);
var newCell = this.drawCell(diagram, this.points.length - 1, activePoint);
if(!activePoint && newCell) {
var checkPath = newCell.data.path;
for(var i = 0, l = this.vGroup.children.length; i < l; i++) {
var path = this.vGroup.children[i];
var toCheckPath = path.data.path;
if(toCheckPath && checkPath.bounds.intersects(toCheckPath.bounds)) {// && toCheckPath.intersects(checkPath)) {
var index = this.findIndex(toCheckPath.data.center);
path.remove();
this.drawCell(diagram, index, activePoint);
}
}
}
},
removeAllPoints: function() {
this.group.data.path.segments = [];
},
removePoint: function(point) {
var hitResult = this.vGroup.hitTest(point);
if(hitResult) {
var point = hitResult.item.data.path.data.center;
var segments = this.group.data.path.segments;
for(var i = 0, l = segments.length; i < l; i++) {
var segment = segments[i];
if(point == segment.point.multiply(100).round()) {
i = l;
segment.remove();
}
}
}
return !!hitResult;
},
findIndex: function(point) {
for(var i = 0, l = this.points.length; i < l; i++) {
if(point == this.points[i].multiply(100).round())
return i;
}
},
drawCell: function(diagram, index, remove) {
var cell = diagram.cells[diagram.sites[index].id];
// there is no guarantee a Voronoi cell will exist for any
// particular site
if (cell !== undefined) {
var halfedges = cell.halfedges;
var nHalfedges = halfedges.length;
if (nHalfedges < 3) return;
var path = new Path();
//path.strokeColor = 'black';
path.data = {};
if(remove) {
path.removeOn({
drag: true,
up: true
});
}
if(!remove)
this.vGroup.appendTop(path);
var startPoint = new Point(halfedges[0].getStartpoint());
path.add(startPoint);
for (var iHalfedge = 0; iHalfedge < nHalfedges; iHalfedge++) {
var curPoint = new Point(halfedges[iHalfedge].getEndpoint());
path.add(curPoint);
}
path.data.center = this.points[index].multiply(100).round();
path.closed = true;
path.fillColor = new GrayColor(1 - path.length / 4000);
if(values.drawBlobs) {
var offPath = this.getOffsettedPath(path);
if(offPath) {
this.removeSmallBits(offPath);
this.roundPath(offPath);
if(values.smoothBlobs) {
offPath.smooth();
}
if(!remove) {
this.vGroup.appendTop(offPath);
} else {
offPath.removeOn({
drag: true,
up: true
});
}
}
path = offPath;
} else {
var dataPath = new Path(path.segments);
dataPath.data = {};
dataPath.data.center = path.data.center;
path.data.path = dataPath;
dataPath.remove();
}
return path;
}
},
fastRender: function(activePoint) {
if(!this.vGroup) {
this.vGroup = new Group();
this.vGroup.name = 'vGroup';
this.group.appendTop(this.vGroup);
}
this.voronoi.setSites(this.points);
var diagram = this.voronoi.compute(this.bbox);
if(this.points.length > 1)
this.drawVoronoi(diagram, activePoint);
},
render: function() {
if(this.vGroup)
this.vGroup.remove();
this.vGroup = new Group();
this.vGroup.name = 'vGroup';
this.group.appendTop(this.vGroup);
this.voronoi.setSites(this.points);
var diagram = this.voronoi.compute(this.bbox);
this.redrawVoronoi(diagram);
},
addAndRender: function(point, preview, remove) {
if(remove) this.pop();
this.addPoint(point);
this.initPoints();
if(preview) {
this.render(point);
} else {
this.render(null, true);
}
}
};
var values = {
randomAmount: 10,
drawBlobs: false,
smoothBlobs: true
}
var drawers = {
// 'Hexagons': function() {
// var components = {
// columns: { type: 'number', label: 'Columns', value: this.columns || 5 },
// rows: { type: 'number', label: 'Rows', value: this.rows || 8 }
// };
// var vPoints = [];
// // Now we bring up the dialog
// var values = Dialog.prompt('Settings', components);
// if(values) {
// this.columns = values.columns;
// this.rows = values.rows * 2;
//
// var size = new Size(this.columns, this.rows);
// var col = document.bounds.size / size;
// for(var i = -1; i < this.columns + 1; i++) {
// for(var j = -1; j < this.rows + 1; j++) {
// var point = new Point(i, j) / new Point(size) * document.bounds.size + col / 2;
// if(j % 4 == 2 || j% 4 == 3) {
// point += new Point(col.width / 2, 0);
// }
//
// vPoints.push(point);
// }
// }
// }
// return vPoints;
// },
'Bee Hive': function() {
var components = {
columns: { type: 'number', label: 'Columns', value: this.columns || 5 },
rows: { type: 'number', label: 'Rows', value: this.rows || 8 },
loose: { type: 'checkbox', label: 'Loose', value: true }
};
var vPoints = [];
// Now we bring up the dialog
var values = Dialog.prompt('Settings', components);
if(values) {
this.columns = values.columns;
this.rows = values.rows;
var size = new Size(this.columns, this.rows);
var col = document.bounds.size / size;
for(var i = -1; i < this.columns + 1; i++) {
for(var j = -1; j < this.rows + 1; j++) {
var point = new Point(i, j) / new Point(size) * document.bounds.size + col / 2;
if(j % 2)
point += new Point(col.width / 2, 0);
if(values.loose)
point += (col / 4) * Point.random() - col / 4;
vPoints.push(point);
}
}
}
return vPoints;
},
'Teeth': function() {
var components = {
columns: { type: 'number', label: 'Columns', value: this.columns || 15 },
rows: { type: 'number', label: 'Rows', value: this.rows || 15 },
loose: { type: 'checkbox', label: 'Loose', value: true }
};
var vPoints = [];
// Now we bring up the dialog
var values = Dialog.prompt('Settings', components);
if(values) {
this.columns = values.columns;
this.rows = values.rows;
var size = new Size(this.columns, this.rows);
var col = document.bounds.size / size;
for(var i = -1; i < this.columns ; i++) {
for(var j = -1; j < this.rows; j++) {
var point = new Point(i, j) / size * document.bounds.size + col / 2;
if(j % 2)
point += new Point(col.width / 2, 0);
if(i % 2)
point += new Point(0, col.height / 2);
if(values.loose)
point += (col / 8) * Point.random() - col / 8;
vPoints.push(point);
}
}
}
return vPoints;
},
'Grid': function() {
var components = {
columns: { type: 'number', label: 'Columns', value: this.columns || 3 },
rows: { type: 'number', label: 'Rows', value: this.rows || 5 },
loose: { type: 'checkbox', label: 'Loose', value: Base.pick(this.loose, false) }
};
var vPoints = [];
// Now we bring up the dialog
var values = Dialog.prompt('Settings', components);
if(values) {
this.columns = values.columns;
this.rows = values.rows;
this.parts = values.parts;
this.loose = values.loose;
var size = new Size(this.columns, this.rows);
var col = document.bounds.size / size;
for(var i = -1; i < this.columns + 1; i++) {
for(var j = -1; j < this.rows + 1; j++) {
var point = new Point(i, j) / new Point(size) * document.bounds.size + col / 2;
if(this.loose)
point += (col / 4) * Point.random() - col / 4;
vPoints.push(point);
}
}
}
return vPoints;
},
'Citrus': function() {
var components = {
columns: { type: 'number', label: 'Columns', value: this.columns || 3 },
rows: { type: 'number', label: 'Rows', value: this.rows || 5 },
parts: { type: 'number', label: 'Rows', value: this.parts || 8 }
};
var vPoints = [];
// Now we bring up the dialog
var values = Dialog.prompt('Settings', components);
if(values) {
this.columns = values.columns;
this.rows = values.rows;
this.parts = values.parts;
var size = new Size(this.columns, this.rows);
var col = document.bounds.size / size;
for(var i = -1; i < this.columns + 1; i++) {
for(var j = -1; j < this.rows + 1; j++) {
var point = new Point(i, j) / new Point(size) * document.bounds.size + col / 2;
if(j % 2)
point += new Point(col.width / 2, 0);
vPoints.push(point);
var offset = i % 2 ? 45 : 0;
for(var z = 0; z < this.parts; z++) {
var vector = new Point(col.width / 6, 0).rotate(offset + z / this.parts * 360 * (i % 2 ? 1 : -1) * (j % 2 ? -1 : 1));
vPoints.push(point + vector);
}
}
}
}
return vPoints;
},
'Circular': function() {
var point = document.bounds.center;
var vPoints = [point];
var segments = 10;
var height = document.bounds.height * 1.5 / 10;
var vector = new Point(10, 0).normalize(height / 4);
var offset = 0;
for (var i = 0; i < 10; i++) {
segments += 2;
offset += 20;
for (var j = 0; j < segments; j++) {
vector.angle = i % 2 ? 360 / segments / 2 : 0;
vector.angle += 360 / segments * j + offset;
vPoints.push(point + vector);
}
vector.length += height / 2;
}
return vPoints;
}
}
var options = [];
Base.each(drawers, function(drawer, i) {
options.push(i);
});
values.drawer = options[0];
var components = {
ruler0: { label: 'Selected Paths', type: 'ruler' },
addPoints: {
type: 'button',
value: 'Add Selected Points',
onClick: function() {
var paths = document.getItems({
type: Path,
selected: true
});
if(paths.length) {
voronoiDrawer.initialize();
for(var i = 0, l = paths.length; i < l; i++) {
var segments = paths[i].segments;
for(var j = 0, k = segments.length; j < k; j++) {
voronoiDrawer.addPoint(segments[j].point);
}
}
voronoiDrawer.initPoints();
voronoiDrawer.render();
} else {
Dialog.alert('Please select a path first.');
}
}
},
ruler1: { label: 'Random Points', type: 'ruler' },
randomAmount: {
steppers: true,
label: 'Amount'
},
addRandomPoints: {
type: 'button',
value: 'Add Random Points',
onClick: function() {
voronoiDrawer.initialize();
for(var i = 0, l = values.randomAmount; i < l; i++) {
voronoiDrawer.addRandomPoint();
}
voronoiDrawer.initPoints();
voronoiDrawer.render();
}
},
ruler2: { label: 'Grid Generators', type: 'ruler' },
drawer: {
label: 'Type',
options: options
},
renderGrid: {
type: 'button',
value: 'Render Grid',
onClick: function() {
voronoiDrawer.initialize();
var points = drawers[values.drawer]();
if(points.length) {
var confirmed = true;
if(points.length > 800) {
confirmed = Dialog.confirm('You are about to generate a Voronoi diagram with ' + points.length + ' points. This could take a while and could potentially crash Illustrator. Do you want to live recklessly?');
}
if(confirmed) {
voronoiDrawer.setPoints(points);
voronoiDrawer.render();
}
}
}
},
ruler3: { label: 'Settings', type: 'ruler' },
drawBlobs: {
label: 'Inset Paths',
onChange: function(value) {
components.smoothBlobs.enabled = !!value;
voronoiDrawer.initialize();
voronoiDrawer.initPoints();
if(voronoiDrawer.points.length)
voronoiDrawer.render();
}
},
smoothBlobs: {
label: 'Smooth',
onChange: function(value) {
voronoiDrawer.initialize();
voronoiDrawer.initPoints();
if(voronoiDrawer.points.length)
voronoiDrawer.render();
}
}
};
// var palette = new Palette('Voronoi Tool', components, values);
</script>
</head>
<body>
<canvas id='canvas' width=1024 height=768></canvas>
</body>