Skip to content

Instantly share code, notes, and snippets.

@nbenns
Last active April 26, 2018 16:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nbenns/fef2778c86b904dde31281afa684a4c2 to your computer and use it in GitHub Desktop.
Save nbenns/fef2778c86b904dde31281afa684a4c2 to your computer and use it in GitHub Desktop.
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN' 'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg width='1000' height='1000'
xmlns='http://www.w3.org/2000/svg'
xmlns:xlink='http://www.w3.org/1999/xlink'
onload='init(evt)' >
<style>
.edge{
fill: white;
stroke: black;
stroke-width: 1;
}
.button{
fill: #2060dd;
stroke: #2580ff;
stroke-width: 1;
}
.button:hover{
stroke-width: 3;
}
</style>
<defs>
<marker id="arrow" orient="auto" markerWidth="2" markerHeight="4" refX="0.1" refY="2">
<path d="M0,0 V4 L2,2 Z" fill="context-stroke"></path>
</marker>
</defs>
<script type='text/ecmascript'>
<![CDATA[
const zoom = 8192;
const Vector = function() {
const _coordinates = arguments[0].slice();
this.__defineGetter__("coordinates", () => _coordinates);
this.toString = () => "[" + _coordinates.join(', ') + "]"
this.negate = () => new Vector(_coordinates.map((c,i) => -1 * _coordinates[i]))
this.add = v => new Vector(_coordinates.map((c,i) => c + v.coordinates[i]))
this.sub = v => new Vector(_coordinates.map((c,i) => c - v.coordinates[i]))
this.scale = s => new Vector(_coordinates.map(c => c * s))
this.rotate = r => {
const ca = Math.cos(r.coordinates[0]);
const sa = Math.sin(r.coordinates[0]);
const cb = Math.cos(r.coordinates[1]);
const sb = Math.sin(r.coordinates[1]);
const cg = Math.cos(r.coordinates[2]);
const sg = Math.sin(r.coordinates[2]);
q = _coordinates;
return new Vector([
cb*cg*q[0] - cb*sg*q[1] + sb*q[2],
(sa*sb*cg + ca*sg)*q[0] + (-sa*sb*sg + ca*cg)*q[1] - sa*cb*q[2],
(-ca*sb*cg + sa*sg)*q[0] + (ca*sb*sg + sa*cg)*q[1] + ca*cg*q[2]
]);
}
this.dot = v => {
const A = _coordinates.slice();
const B = v.coordinates.slice();
return A.map((a,i) => a * B[i]).reduce((c,p) => c+p);
}
this.cross = v => {
const a = _coordinates.slice();
const b = v.coordinates.slice();
return new Vector([a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0]]);
}
this.project = v => [ zoom * _coordinates[0] / _coordinates[2], zoom * _coordinates[1] / _coordinates[2]]
this.length = () => Math.sqrt(this.dot(this, this))
}
//console.log(new Vector([3, 4, 0]).length());
const Obj = function(n) {
const _points = [];
const _faces = [];
var _translation = new Vector([0, 0, 0]);
var _rotation = new Vector([0, 0, 0]);
var _scale = 1;
var _velocity = new Vector([0,0,0]);
var _angvelocity = new Vector([0,0,0]);
var _acceleration = new Vector([0,0,0]);
var _mass = 1.0;
var _name = n;
var _grp = svgDocument.createElementNS("http://www.w3.org/2000/svg", 'g');
_grp.setAttribute("id", _name);
const Face = function(id, stroke, fill) {
var _points = [];
var _transformed = [];
var _normal = [];
var _midpoint = [];
var _translation = [];
var _dom = svgDocument.createElementNS("http://www.w3.org/2000/svg", 'path');
_dom.setAttribute("id", id);
_dom.setAttribute("stroke", stroke);
_dom.setAttribute("fill", fill);
_dom.setAttribute("stroke-width", 1);
//_dom.setAttribute("marker-end", "url(#arrow)");
this.addPoint = p => _points.push(p)
this.__defineGetter__("points", () => _points)
this.draw = (t, r) => {
const p2d = this.convert2d(t, r);
var s = "";
const m = _midpoint.project();
const n = _midpoint.add(_normal).project();
p2d.forEach((p, i, a) => {
if (i == 0) s += "M" + p[0] + " " + p[1] + " "
else s += "L" + p[0] + " " + p[1] + " "
if (i == (a.length - 1)) {
s += "L" + a[0][0] + " " + a[0][1];
//s += " M" + m[0] + " " + m[1] + " L" + n[0] + " " + n[1];
}
});
if (s == "") s = "M0 0";
_dom.setAttribute("d", s);
svgDocument.getElementById(_name).appendChild(_dom);
}
this.convert2d = (t,r) => {
_translation = t;
_transformed = _points.map(p => p.rotate(r).scale(_scale).add(_translation))
const f1 = _transformed[0].sub(_transformed[1]);
const f2 = _transformed[2].sub(_transformed[1]);
_normal = f1.cross(f2);
_midpoint = _transformed.reduce((pv, cv) => pv.add(cv)).scale(_points.length)
if (_normal.coordinates[2] < 0 && _midpoint.coordinates[2] > 0) {
return _transformed.map(function(p) { return p.project(); });
}
else return [];
}
}
this.__defineGetter__("name", () => _name)
this.addPoint = p => _points.push(new Vector(p.slice()))
this.__defineGetter__("points", () => _points)
this.__defineSetter__("scale", s => _scale = s)
this.__defineGetter__("scale", () => _scale)
this.__defineSetter__("rotation", r => _rotation = r)
this.__defineGetter__("rotation", () => _rotation)
this.__defineSetter__("translation", t => _translation = t)
this.__defineGetter__("translation", () => _translation)
this.__defineSetter__("velocity", v => _velocity = v)
this.__defineGetter__("velocity", () => _velocity)
this.__defineSetter__("angvelocity", v => _angvelocity = v)
this.__defineGetter__("angvelocity", () => _angvelocity)
this.createFace = p => {
const colors = ["red", "orange", "yellow", "green", "lightblue", "blue", "darkblue", "indigo", "violet", "purple", "brown", "white", "lightgrey", "grey", "darkgrey", "black", "pink"]
const f = new Face(_name + "-" + _faces.length, 'black', colors[_faces.length % 17]);
p.forEach(n => f.addPoint(_points[n]))
_faces.push(f)
}
this.__defineGetter__("faces", () => _faces)
this.print = () => {
console.log("Points:")
_points.forEach(p => console.log(p))
_faces.forEach((f,i) => f.convert2d(_translation).forEach(p => console.log(p)))
}
this.draw = () => {
svgDocument.getElementById("world").appendChild(_grp)
var path = svgDocument.getElementById(_name);
_translation = _translation.add(_velocity)
_rotation = _rotation.add(_angvelocity)
_faces.forEach(f => f.draw(_translation, _rotation))
}
}
function init(evt)
{
if ( window.svgDocument == null )
{
svgDocument = evt.target.ownerDocument;
}
var forward = new Vector([0, 0, -10]);
var rotX = new Vector([-Math.PI/32, Math.PI/64, 0]);
var startZ = 1500;
var startY = 0;
var sep = 15;
tetrahedron = new Obj("tetrahedron");
tetrahedron.addPoint([1, 1, 1]);
tetrahedron.addPoint([1, -1, -1]);
tetrahedron.addPoint([-1, 1, -1]);
tetrahedron.addPoint([-1, -1, 1]);
tetrahedron.createFace([0, 1, 3]);
tetrahedron.createFace([2, 0, 3]);
tetrahedron.createFace([1, 2, 3]);
tetrahedron.createFace([2, 1, 0]);
tetrahedron.translation = new Vector([-2 * sep, startY, startZ - 1000]);
tetrahedron.velocity = forward.add(new Vector([-0.8, 0.8, 0]));
tetrahedron.angvelocity = rotX;
tetrahedron.scale = 5;
var hexahedron = new Obj("hexahedron");
hexahedron.addPoint([-1, -1, -1]);
hexahedron.addPoint([1, -1, -1]);
hexahedron.addPoint([1, 1, -1]);
hexahedron.addPoint([-1, 1, -1]);
hexahedron.addPoint([-1, -1, 1]);
hexahedron.addPoint([-1, 1, 1]);
hexahedron.addPoint([1, -1, 1]);
hexahedron.addPoint([1, 1, 1]);
hexahedron.createFace([0,1,2,3]);
hexahedron.createFace([4,0,3,5]);
hexahedron.createFace([1,6,7,2]);
hexahedron.createFace([6,4,5,7]);
hexahedron.createFace([4,6,1,0]);
hexahedron.createFace([3,2,7,5]);
hexahedron.translation = new Vector([-sep, startY, startZ - 500]);
hexahedron.velocity = forward.add(new Vector([-0.4, -0.4, 0]));
hexahedron.angvelocity = rotX;
hexahedron.scale = 5;
var octahedron = new Obj("octahedron");
octahedron.addPoint([-1, 0, -1]);
octahedron.addPoint([1, 0, -1]);
octahedron.addPoint([1, 0, 1]);
octahedron.addPoint([-1, 0, 1]);
octahedron.addPoint([0, Math.sqrt(2), 0]);
octahedron.addPoint([0, -Math.sqrt(2), 0]);
octahedron.createFace([0, 1, 4]);
octahedron.createFace([1, 2, 4]);
octahedron.createFace([2, 3, 4]);
octahedron.createFace([3, 0, 4]);
octahedron.createFace([0, 5, 1]);
octahedron.createFace([1, 5, 2]);
octahedron.createFace([2, 5, 3]);
octahedron.createFace([3, 5, 0]);
octahedron.translation = new Vector([0, startY, startZ]);
octahedron.velocity = forward.add(new Vector([0.1, 0.1, 0]));
octahedron.angvelocity = rotX;
octahedron.scale = 6;
var dodecahedron = new Obj("dodecahedron");
phi = (1 + Math.sqrt(5))/2;
b = 1 / phi;
c = 2 - phi;
dodecahedron.addPoint([c, 0, 1]);
dodecahedron.addPoint([b, b, b]);
dodecahedron.addPoint([0, 1, c]);
dodecahedron.addPoint([-b, b, b]);
dodecahedron.addPoint([-c, 0, 1]);
dodecahedron.addPoint([-b, -b, b]);
dodecahedron.addPoint([0, -1, c]);
dodecahedron.addPoint([b, -b, b]);
dodecahedron.addPoint([c, 0, -1]);
dodecahedron.addPoint([b, -b, -b]);
dodecahedron.addPoint([0, -1, -c]);
dodecahedron.addPoint([-b, -b, -b]);
dodecahedron.addPoint([-c, 0, -1]);
dodecahedron.addPoint([-b, b, -b]);
dodecahedron.addPoint([0, 1, -c]);
dodecahedron.addPoint([b, b, -b]);
dodecahedron.addPoint([1, c, 0]);
dodecahedron.addPoint([-1, c, 0]);
dodecahedron.addPoint([-1, -c, 0]);
dodecahedron.addPoint([1, -c, 0]);
dodecahedron.createFace([4, 3, 2, 1, 0]);
dodecahedron.createFace([0, 7, 6, 5, 4]);
dodecahedron.createFace([12, 11, 10, 9, 8]);
dodecahedron.createFace([8, 15, 14, 13, 12]);
dodecahedron.createFace([15, 16, 1, 2, 14]);
dodecahedron.createFace([3, 17, 13, 14, 2]);
dodecahedron.createFace([11, 18, 5, 6, 10]);
dodecahedron.createFace([7, 19, 9, 10, 6]);
dodecahedron.createFace([19, 7, 0, 1, 16]);
dodecahedron.createFace([16, 15, 8, 9, 19]);
dodecahedron.createFace([18, 11, 12, 13, 17]);
dodecahedron.createFace([17, 3, 4, 5, 18]);
dodecahedron.translation = new Vector([sep, startY, startZ + 500]);
dodecahedron.velocity = forward.add(new Vector([0.4, -0.4, 0]));
dodecahedron.angvelocity = rotX;
dodecahedron.scale = 7;
var icosahedron = new Obj("icosahedron");
icosahedron.addPoint([0, 1, phi]);
icosahedron.addPoint([0, -1, phi]);
icosahedron.addPoint([0, 1, -phi]);
icosahedron.addPoint([0, -1, -phi]);
icosahedron.addPoint([1, phi, 0]);
icosahedron.addPoint([-1, phi, 0]);
icosahedron.addPoint([1, -phi, 0]);
icosahedron.addPoint([-1, -phi, 0]);
icosahedron.addPoint([phi, 0, 1]);
icosahedron.addPoint([-phi, 0, 1]);
icosahedron.addPoint([phi, 0, -1]);
icosahedron.addPoint([-phi, 0, -1]);
icosahedron.createFace([0,4,8]);
icosahedron.createFace([9,5,0]);
icosahedron.createFace([0,8,1]);
icosahedron.createFace([1,9,0]);
icosahedron.createFace([0,5,4]);
icosahedron.createFace([10,8,4]);
icosahedron.createFace([11,5,9]);
icosahedron.createFace([6,1,8]);
icosahedron.createFace([7,9,1]);
icosahedron.createFace([4,5,2]);
icosahedron.createFace([10,6,8]);
icosahedron.createFace([1,6,7]);
icosahedron.createFace([7,11,9]);
icosahedron.createFace([4,2,10]);
icosahedron.createFace([11,2,5]);
icosahedron.createFace([3,2,11]);
icosahedron.createFace([10,2,3]);
icosahedron.createFace([6,10,3]);
icosahedron.createFace([11,7,3]);
icosahedron.createFace([3,7,6]);
icosahedron.translation = new Vector([2 * sep, startY, startZ + 1000]);
icosahedron.velocity = forward.add(new Vector([0.8, 0.8, 0]));
icosahedron.angvelocity = rotX;
icosahedron.scale = 4.5;
objects = [tetrahedron, hexahedron, octahedron, dodecahedron, icosahedron];
//objects = [];
setInterval("worldDraw()", 20);
//worldDraw();
}
function worldDraw() {
objects.sort(function(o1, o2) {
return (o2.translation.coordinates[2] - o1.translation.coordinates[2]);
});
objects.forEach(function(o) {
if ((o.translation.coordinates[0] <= -50) || (o.translation.coordinates[0] >= 50)) {
o.velocity = new Vector([o.velocity.coordinates[0] * -1, o.velocity.coordinates[1], o.velocity.coordinates[2]]);
o.angvelocity = new Vector([o.angvelocity.coordinates[0], o.angvelocity.coordinates[1] * -1, o.angvelocity.coordinates[2] * -1]);
}
if ((o.translation.coordinates[1] <= -50) || (o.translation.coordinates[1] >= 50)) {
o.velocity = new Vector([o.velocity.coordinates[0], o.velocity.coordinates[1] * -1, o.velocity.coordinates[2]]);
o.angvelocity = new Vector([o.angvelocity.coordinates[0] * -1, o.angvelocity.coordinates[1], o.angvelocity.coordinates[2] * -1]);
}
if ((o.translation.coordinates[2] <= 50) || (o.translation.coordinates[2] >= 3000)) {
o.velocity = new Vector([o.velocity.coordinates[0], o.velocity.coordinates[1], o.velocity.coordinates[2] * -1]);
o.angvelocity = new Vector([o.angvelocity.coordinates[0] * -1, o.angvelocity.coordinates[1] * -1, o.angvelocity.coordinates[2]]);
}
o.draw();
});
}
function beginRotateX(radians)
{
objects.forEach(function(o) {
o.angvelocity = o.angvelocity.add(new Vector([radians, 0, 0]));
});
}
function endRotateX()
{
objects.forEach(function(o) {
//o.angvelocity = new Vector([0,0,0]);
});
}
function beginRotateY(radians)
{
objects.forEach(function(o) {
o.angvelocity = o.angvelocity.add(new Vector([0, radians, 0]));
});
}
function endRotateY()
{
objects.forEach(function(o) {
//o.angvelocity = o.angvelocity.sub(new Vector([0, radians, 0]));
});
}
]]>
</script>
<g transform="translate(500,500)">
<g id="world" transform="scale(1,-1)">
</g>
</g>
<path stroke="black" fill="none" d="M0 0 L1000 0 L1000 1000 L0 1000 L0 0"></path>
<path class="button"
d="m50.5 250.5 15 -15 0 8 45 0 0 14 -45 0 0 8 z"
onmousedown='beginRotateY(Math.PI/32)'
onmouseout='endRotateY()'
onmouseup='endRotateY()'/>
<path class="button"
d="m190.5 250.5 -15 -15 0 8 -45 0 0 14 45 0 0 8 z"
onmousedown='beginRotateY(-Math.PI/32)'
onmouseout='endRotateY()'
onmouseup='endRotateY()'/>
<path class="button"
d="m255.5 50.5 15 15 -8 0 0 45 -14 0 0 -45 -8 0 z"
onmousedown='beginRotateX(Math.PI/32)'
onmouseout='endRotateX()'
onmouseup='endRotateX()'/>
<path class="button"
d="m255.5 190.5 15 -15 -8 0 0 -45 -14 0 0 45 -8 0 z"
onmousedown='beginRotateX(-Math.PI/32)'
onmouseout='endRotateX()'
onmouseup='endRotateX()'/>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment