Skip to content

Instantly share code, notes, and snippets.

@Niekes
Last active February 6, 2018 06:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Niekes/293aa96c653b669ef0ca04fa4f1d5403 to your computer and use it in GitHub Desktop.
Save Niekes/293aa96c653b669ef0ca04fa4f1d5403 to your computer and use it in GitHub Desktop.
d3-3d: Winding Order
license: gpl-3.0

Testing d3-3d. If the triangles get drawn counter-clockwise the front face should be visible. The front face is the brighter one.

!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.d3=t.d3||{})}(this,function(t){"use strict";function e(t){var e=t.slice(0),r=0;e.push(e[0]);for(var o=0;o<=t.length-1;o++){var n=o+1,c=e[o].rotated,a=e[n].rotated;r+=(a.x-c.x)*(a.y+c.y)}return r>0}function r(t){for(var e=0,r=0,o=0,n=t.length,c=n-1;c>=0;c--)e+=t[c].rotated.x,r+=t[c].rotated.y,o+=t[c].rotated.z;return{x:e/n,y:r/n,z:o/n}}function o(t,e){var r=e.rotateCenter;t.x-=r[0],t.y-=r[1],t.z-=r[2];var o=a(n(c(t,e.y),e.x),e.z);return o.x+=r[0],o.y+=r[1],o.z+=r[2],o}function n(t,e){var r=Math.sin(e),o=Math.cos(e);return{x:t.x,y:t.y*o-t.z*r,z:t.y*r+t.z*o}}function c(t,e){var r=Math.sin(e),o=Math.cos(e);return{x:t.z*r+t.x*o,y:t.y,z:t.z*o-t.x*r}}function a(t,e){var r=Math.sin(e),o=Math.cos(e);return{x:t.x*o-t.y*r,y:t.y*r+t.y*o,z:t.z}}function d(t,e,r,n){for(var c=t.length-1;c>=0;c--){var a=t[c];a.rotated=o({x:r.x(a),y:r.y(a),z:r.z(a)},n),a.centroid=a.rotated,a.projected=e.project(a.rotated,e)}return t}function u(t,o,n,c){for(var a=[],u=t.length-1;u>=0;u--){var i=t[u],f=d([i[0],i[1],i[2],i[3],i[4],i[5],i[6],i[7]],o,n,c),y=f[0],p=f[1],x=f[2],j=f[3],z=f[4],h=f[5],l=f[6],v=f[7],g=[y,p,x,j],s=[v,l,h,z],L=[z,h,p,y],N=[j,x,l,v],I=[z,y,j,v],M=[p,h,l,x];g.centroid=r(g),s.centroid=r(s),L.centroid=r(L),N.centroid=r(N),I.centroid=r(I),M.centroid=r(M),g.ccw=e(g),s.ccw=e(s),L.ccw=e(L),N.ccw=e(N),I.ccw=e(I),M.ccw=e(M),g.face="front",s.face="back",L.face="left",N.face="right",I.face="top",M.face="bottom";var w=[g,s,L,N,I,M];a.push(w)}return a}function i(t,e,n,c){for(var a=t.length-1;a>=0;a--){for(var d=t[a],u=d.length/2,i=parseInt(u),f=d.length-1;f>=0;f--){var y=d[f];y.rotated=o({x:n.x(y),y:n.y(y),z:n.z(y)},c),y.projected=e.project(y.rotated,e)}d.centroid=i===u?r([d[u-1],d[u]]):{x:d[i].rotated.x,y:d[i].rotated.y,z:d[i].rotated.z}}return t}function f(t,e,n,c){for(var a=t.length-1;a>=0;a--){var d=t[a],u=d[0],i=d[1];u.rotated=o({x:n.x(u),y:n.y(u),z:n.z(u)},c),i.rotated=o({x:n.x(i),y:n.y(i),z:n.z(i)},c),u.projected=e.project(u.rotated,e),i.projected=e.project(i.rotated,e),d.centroid=r(d)}return t}function y(t,n,c,a){for(var d=t.length-1;d>=0;d--){var u=t[d],i=u[0],f=u[1],y=u[2],p=u[3];i.rotated=o({x:c.x(i),y:c.y(i),z:c.z(i)},a),f.rotated=o({x:c.x(f),y:c.y(f),z:c.z(f)},a),y.rotated=o({x:c.x(y),y:c.y(y),z:c.z(y)},a),p.rotated=o({x:c.x(p),y:c.y(p),z:c.z(p)},a),i.projected=n.project(i.rotated,n),f.projected=n.project(f.rotated,n),y.projected=n.project(y.rotated,n),p.projected=n.project(p.rotated,n),u.ccw=e(u),u.centroid=r(u)}return t}function p(t,n,c,a){for(var d=t.length-1;d>=0;d--){for(var u=t[d],i=u.length-1;i>=0;i--){var f=u[i];f.rotated=o({x:c.x(f),y:c.y(f),z:c.z(f)},a),f.projected=n.project(f.rotated,n)}u.ccw=e(u),u.centroid=r(u)}return t}function x(t,n,c,a){for(var d=t.length-1;d>=0;d--){var u=t[d],i=u[0],f=u[1],y=u[2];i.rotated=o({x:c.x(i),y:c.y(i),z:c.z(i)},a),f.rotated=o({x:c.x(f),y:c.y(f),z:c.z(f)},a),y.rotated=o({x:c.x(y),y:c.y(y),z:c.z(y)},a),i.projected=n.project(i.rotated,n),f.projected=n.project(f.rotated,n),y.projected=n.project(y.rotated,n),u.ccw=e(u),u.centroid=r(u)}return t}function j(t){for(var e=t[t.length-1],r="M"+e.projected.x+","+e.projected.y,o=t.length-2;o>=0;o--){var n=t[o].projected;r+="L"+n.x+","+n.y}return r}function z(t){return"M"+t[0].projected.x+","+t[0].projected.y+"L"+t[1].projected.x+","+t[1].projected.y+"L"+t[2].projected.x+","+t[2].projected.y+"L"+t[3].projected.x+","+t[3].projected.y+"Z"}function h(t){for(var e=t[t.length-1],r="M"+e.projected.x+","+e.projected.y,o=t.length-2;o>=0;o--){var n=t[o].projected;r+="L"+n.x+","+n.y}return r+="Z"}function l(t){return"M"+t[0].projected.x+","+t[0].projected.y+"L"+t[1].projected.x+","+t[1].projected.y+"L"+t[2].projected.x+","+t[2].projected.y+"Z"}function v(t,e){return{x:e.origin[0]+e.scale*t.x,y:e.origin[1]+e.scale*t.y}}function g(t){return t[0]}function s(t){return t[1]}function L(t){return t[2]}t._3d=function(){function t(t){return P[E](t,{scale:r,origin:e,project:o},{x:I,y:M,z:w},{x:n,y:c,z:a,rotateCenter:N})}var e=[0,0],r=1,o=v,n=0,c=0,a=0,N=[0,0,0],I=g,M=s,w=L,E="POINT",P={CUBE:u,LINE_STRIP:i,LINE:f,PLANE:y,POINT:d,POLYGON:p,TRIANGLE:x},O={CUBE:z,LINE_STRIP:j,PLANE:z,POLYGON:h,TRIANGLE:l};return t.origin=function(r){return arguments.length?(e=r,t):e},t.scale=function(e){return arguments.length?(r=e,t):r},t.rotateX=function(e){return arguments.length?(n=e,t):n},t.rotateY=function(e){return arguments.length?(c=e,t):c},t.rotateZ=function(e){return arguments.length?(a=e,t):a},t.shape=function(e){return arguments.length?(E=e,t):E},t.rotateCenter=function(e){return arguments.length?(N=e,t):N},t.x=function(e){return arguments.length?(I="function"==typeof e?e:+e,t):I},t.y=function(e){return arguments.length?(M="function"==typeof e?e:+e,t):M},t.z=function(e){return arguments.length?(w="function"==typeof e?e:+e,t):w},t.sort=function(t,e){var r=t.centroid.z,o=e.centroid.z;return r<o?-1:r>o?1:r>=o?0:NaN},t.draw=function(t){if("POINT"!==E&&"LINE"!==E)return O[E](t)},t},Object.defineProperty(t,"__esModule",{value:!0})});
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="d3-3d.js"></script>
<body>
<svg width="960" height="500"></svg>
<script>
var data = [
[{x:0,y:-1,z: 0},{x:-1,y:1,z: 0},{x:1,y:1,z: 0}],
[{x:0,y:-1,z:-1},{x:-1,y:1,z:-1},{x:1,y:1,z:-1}],
[{x:0,y:-1,z:-2},{x:-1,y:1,z:-2},{x:1,y:1,z:-2}],
[{x:0,y:-1,z:-3},{x:-1,y:1,z:-3},{x:1,y:1,z:-3}],
[{x:0,y:-1,z:-4},{x:-1,y:1,z:-4},{x:1,y:1,z:-4}]
];
var origin = [480, 250], alpha = 0, beta = 0;
var svg = d3.select('svg').call(d3.drag().on('drag', dragged).on('start', dragStart).on('end', dragEnd)).append('g');
var color = [d3.color('crimson'), d3.color('teal'), d3.color('limegreen'), d3.color('purple'), d3.color('tomato')];
var mx, my, mouseX, mouseY;
var _3d = d3._3d()
.scale(100)
.x(function(d){ return d.x; })
.y(function(d){ return d.y; })
.z(function(d){ return d.z; })
.origin(origin)
.rotateCenter([0,0,-2])
.shape('POLYGON');
var data3D = _3d(data);
function dragStart(){
mx = d3.event.x;
my = d3.event.y;
}
function dragged(){
mouseX = mouseX || 0;
mouseY = mouseY || 0;
beta = (d3.event.x - mx + mouseX) * Math.PI / 360;
alpha = (d3.event.y - my + mouseY) * Math.PI / 720 * (-1);
processData(_3d.rotateY(beta).rotateX(alpha)(data));
}
function dragEnd(){
mouseX = d3.event.x - mx + mouseX;
mouseY = d3.event.y - my + mouseY;
}
function processData(data){
var triangles = svg.selectAll('path').data(data);
triangles
.enter()
.append('path')
.merge(triangles)
.attr('stroke', function(d, i){
return d.ccw ? color[i].brighter(1) : color[i].darker(2);
})
.attr('fill', function(d, i){
return d.ccw ? color[i] : color[i].darker(1);
})
.attr('fill-opacity', 0.95)
.sort(_3d.sort)
.attr('d', _3d.draw);
triangles.exit().remove();
}
processData(data3D);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment