Decompose a triangle to a square.
Last active
May 7, 2016 15:37
-
-
Save bmershon/14972d48da2c362841d6073b267c815f to your computer and use it in GitHub Desktop.
Triangle to Square II
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: gpl-3.0 | |
border: yes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
path { | |
stroke-linecap: butt; | |
} | |
.triangle { | |
pointer-events: none; | |
stroke: #F00000; | |
stroke-dasharray: 5, 5; | |
stroke-width: 2px; | |
fill: none; | |
} | |
.square { | |
pointer-events none; | |
stroke: none; | |
fill: none; | |
} | |
.rectangle { | |
pointer-events: none; | |
stroke-width: 2px; | |
stroke-dasharray: 5, 5; | |
stroke: #aaa; | |
fill: none; | |
} | |
.figure { | |
pointer-events: none; | |
stroke-width: 1px; | |
fill-opacity: 0.5; | |
stroke: #000; | |
} | |
.handle { | |
cursor: move; | |
pointer-events: all; | |
stroke-width: 1px; | |
fill-opacity: 0.5; | |
stroke: #000; | |
fill: #ff9933; | |
} | |
.handle:hover { | |
fill-opacity: 1; | |
} | |
</style> | |
<svg width="960" height="500"> | |
</svg> | |
<script src="https://d3js.org/d3-polygon.v0.2.min.js"></script> | |
<script src="https://d3js.org/d3.v3.min.js"></script> | |
<script> | |
// d3-equidecompose (API subject to change). Copyright 2016, Brooks Mershon and Joy Patel | |
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports,require("d3-polygon")):"function"==typeof define&&define.amd?define(["exports","d3-polygon"],n):n(t.partials=t.partials||{},t.d3_polygon)}(this,function(t,n){"use strict";function r(t,n){var r,o=0;if(t.length!=n.length)throw new Error("Invalid dimensions for dot product.");for(r=0;r<t.length;r++)o+=t[r]*n[r];return o}function o(t){return Math.sqrt(r(t,t))}function e(t,n,e){var a=[n[0]-t[0],n[1]-t[1]],i=[e[0]-t[0],e[1]-t[1]];return o(a)*o(i)==0?0:Math.acos(Math.max(Math.min(1,r(a,i)/(o(a)*o(i))),-1))}function a(t,n,r){return n+r>t&&t>n-r}function i(t,n,r){var o,e=[];for(o=0;o<t.length;o++)e[o]=r(t[o],n[o]);return e}function s(t,n){return i(t,n,function(t,n){return t+n})}function u(t,n){return i(t,n,function(t,n){return t-n})}function f(t,n){return[t*n[0],t*n[1]]}function c(t){return f(1/o(t),t)}function h(t,n,r,o){var e,i,c,h=u(n,t),l=u(o,r),p=u(r,t),g=0;return e=h[0]*-l[1]- -l[0]*h[1],i=(p[0]*-l[1]- -l[0]*p[1])/e,c=(h[0]*p[1]-p[0]*h[1])/e,a(i,.5,.5+g)&&a(c,.5,.5+g)?s(t,f(i,h)):null}function l(t,n){return f(r(t,n)/r(n,n),n)}function p(t){return 180*t/Math.PI}function g(t){var n=t*Math.PI/180,r=Math.sin(n),o=Math.cos(n);return[[o,-1*r,0],[r,o,0],[0,0,1]]}function v(t){var n,r,o=[[0,0,0],[0,0,0],[0,0,0]];for(n=0;t>n;n++)for(r=0;t>r;r++)o[n][r]=0;for(n=0;t>n;n++)o[n][n]=1;return o}function y(t){var n=t[0],r=t[1],o=v(3);return o[0][2]=n,o[1][2]=r,o}function m(t,n){var r,o=[];for(r=0;r<t[0].length;r++)o[r]=t[n][r];return o}function d(t,n){var o,e=[],a=n.slice();for(o=0;o<3-n.length;o++)a.push(1);for(o=0;o<t.length;o++)e[o]=r(m(t,o),a);return e}function M(t,n){var r,o=[];for(r=0;r<t.length;r++)o[r]=t[r][n];return o}function b(t,n){var o,e,a=[[0,0,0],[0,0,0],[0,0,0]];if(t[0].length!=n.length)throw new Error("invalid dimensions");for(o=0;o<t.length;o++)for(e=0;e<n[0].length;e++)a[o][e]=r(m(t,o),M(n,e));return a}function O(t){this.transforms=[],this._matrix=v(3),this._origin=null}function j(){return n.polygonCentroid(this)}function C(t){return n.polygonContains(this,t)}function _(t){var n,r,o;for(n=0,o=this.length;o>n;n++)r=this[n],this[n]=[r[0]+t[0],r[1]+t[1]];return this}function q(t,n){var r,o,e=g(t);for(n&&this.translate([-n[0],-n[1]]),r=0,o=this.length;o>r;r++)this[r]=d(e,this[r]);return n&&this.translate(n),this}function E(){return this._origin?this._origin:(this._origin=this.accumulate(),this._origin)}function P(){var t,n,r=this.clone(),o=this.transforms.length,e=v(3);for(t=o-1;t>=0;t--)n=this.transforms[t],n.rotate&&n.pivot?(r.rotate(n.rotate,n.pivot),e=b(y(f(-1,n.pivot)),e),e=b(g(n.rotate),e),e=b(y(n.pivot),e)):n.translate&&(r.translate(n.translate),e=b(y(n.translate),e));return r._matrix=e,r}function x(){return I(JSON.parse(JSON.stringify(this.slice())))}function I(t){var n=new O;return n.push.apply(n,t),n}function w(t){var r,o,a,i,c,g,v,y,m,d,M,b,O,j,C,_,q,E,P,x,w=0,A=-(1/0);if(0==n.polygonArea(t))return[];for(x=0;3>x;x++)P=0,P=e(t[x%3],t[(x+1)%3],t[(x+2)%3]),P>A&&(A=P,w=x);return r=t[w],o=t[(w+1)%3],a=t[(w+2)%3],d=u(a,o),M=u(r,o),m=s(o,l(M,d)),c=s(r,f(.5,u(m,r))),b=u(o,m),O=u(a,m),j=u(c,m),v=s(m,s(b,j)),y=s(m,s(O,j)),i=h(c,v,r,o),g=h(c,y,r,a),C=I([o,a,g,i]),_=I([i,v,o]),q=I([g,a,y]),_.transforms.push({rotate:p(Math.PI),pivot:i}),q.transforms.push({rotate:p(-Math.PI),pivot:g}),E=[C,_,q],E.rectangle=[o,a,y,v],E}function A(t,n,r){for(var o,e,a=r.length,i=[],s=[],u=[],f=r[a-1],c=[],l=-1,p=[];++l<a;)if(o=f,f=r[l],e=t!==o&&n!==o||u.length&&Object.is(u[0],o)?t!==f&&n!==f||u.length&&!Object.is(u[0],f)?h(t,n,o,f):f:o){if(2==u.length)break;u.push(e),c.push(l)}if(2==u.length&&!Object.is(u[0],u[1])){for(i.push(u[0]),l=c[0];l!=c[1];)Object.is(u[0],r[l])||Object.is(u[1],r[l])||i.push(r[l]),l=(l+1)%a;for(i.push(u[1]),s.push(u[0]),s.push(u[1]),l=c[1];l!=c[0];)Object.is(u[0],r[l])||Object.is(u[1],r[l])||s.push(r[l]),l=(l+1)%a;p.push(I(i),I(s)),p.forEach(function(t){[].push.apply(t.transforms,r.transforms)})}return p}function S(t,n,r){var o,e,a,i=r.length,s=[],u=[];for(a=0;i>a;a++)o=r[a],e=A(t,n,o),2==e.length&&([].push.apply(u,e),s.push(a));return u=u.concat(r.filter(function(t,n){return!s.includes(n)}))}function J(t){var r,e,a,i,l,p,g,v,y,m,d,M,b,O,j,C,_,q,E,P,x,I,w,A,J,N,k=t.rectangle,R=1/0,T=[];for(M=Math.abs(n.polygonArea(k)),b=Math.sqrt(M),e=k[0],l=k[3],p=k[1],g=k[2],r=s(e,f(b,c(u(l,e)))),a=s(e,f(b,c(u(p,e)))),i=s(r,u(a,e)),R=o(u(e,p));R>2*b;){for(I=[],w=[],A=[],J=[],C=s(r,f(.5,u(p,e))),_=s(e,f(.5,u(p,e))),R=o(u(_,p)),q=[r,e,_,C],E=S(C,s(_,u(a,i)),t),x=u(l,e),P=u(e,_),E.forEach(function(t,r){N=n.polygonCentroid(t),t.forEach(function(t,n){Object.is(l,t)?(I.push(r),w.push(n)):Object.is(p,t)&&(A.push(r),J.push(n))}),n.polygonContains(q,N)?t.translate(x).transforms.push({translate:f(-1,x)}):t.translate(P).transforms.push({translate:f(-1,P)})}),l=E[I[0]][w[0]],p=E[A[0]][J[0]],O=1,j=J.length;j>O;O++)p=E[A[O]][J[O]]=E[A[O-1]][J[O-1]];t=E}return T=t,g=s(p,u(l,e)),v=h(r,p,l,g),y=h(r,p,i,a),d=[y,a,p],m=[r,p,g,i],T=S(r,p,T),T=S(i,s(a,f(1,u(a,i))),T),T.forEach(function(t){var o,e;o=n.polygonCentroid(t),n.polygonContains(d,o)?(e=u(r,y),t.translate(e).transforms.push({translate:f(-1,e)})):n.polygonContains(m,o)&&(e=u(r,v),t.translate(e).transforms.push({translate:f(-1,e)}))}),T.square=[e,a,i,r],T}function N(t,n,r,o){var e,a,i,c=u(n,t),h=u(o,r),l=u(t,r);return e=h[0]*-c[1]- -c[0]*h[1],a=(l[0]*-c[1]- -c[0]*l[1])/e,i=(h[0]*l[1]-l[0]*h[1])/e,i>=0&&1>=i?s(r,f(a,h)):[]}function k(t,n){var r=u(t,n[0]),o=u(n[1],n[0]),e=r[0]*o[1]-o[0]*r[1];return e>=0}function R(t){var n;return t.rotate&&t.pivot?n={rotate:-t.rotate,pivot:t.pivot}:t.translate&&(n={translate:f(-1,t.translate)}),n}function T(t,n){var r,o,e,a,i,s,u,f,c,h,l,p,g,v;for(r=I(n.reverse()),o=t.slice(0),i=0;i<r.length;i++)for(u=i+1==r.length?0:i+1,f=[r[i],r[u]],c=o.slice(0),o=[],h=c[c.length-1],s=0;s<c.length;s++)l=c[s],p=!k(l,f),g=!k(h,f),p?(g||(v=N(l,h,r[i],r[u]),v!=[]&&o.push(v)),o.push(l)):g&&(v=N(l,h,r[i],r[u]),v.length&&o.push(v)),h=l;return e=I(o),a=t.transforms.slice(),e.target=n.transforms.slice(0),a=a.concat(n.transforms.slice(0).reverse().map(R)),e.transforms=a,e}function z(t){var n=t.centroid();return t.sort(function(t,r){return Math.atan2(r[1]-n[1],r[0]-n[0])-Math.atan2(t[1]-n[1],t[0]-n[0])}),t}function B(t,n){var r,o,e,a=[];for(o=0;o<t.length;o++)for(e=0;e<n.length;e++)r=T(z(t[o]),z(n[e])),null!=r&&0!=r.length&&0!=r[0].length&&a.push(r);return a}function D(t,r){var o,e,a,i,s,u,f;return o=J(w(t)),e=J(w(r)),a=I(o.square),i=I(e.square),u=n.polygonCentroid(a),f=n.polygonCentroid(i),e.forEach(function(t){t.translate([u[0]-f[0],u[1]-f[1]]),t.transforms.push({translate:[-(u[0]-f[0]),-(u[1]-f[1])]}),t.rotate(F(a,i),u),t.transforms.push({rotate:-F(a,i),pivot:u})}),s=B(o,e),s=s.map(function(t){var n=t.clone();return n.transforms=t.target,n=n.origin(),n.transforms=t.transforms,n})}function F(t,n){var r,o;return r=t[0],o=s(n[0],u(t.centroid(),n.centroid())),180/Math.PI*e(t.centroid(),r,o)}O.prototype=Object.create(Array.prototype),O.prototype.constructor=O,O.prototype.centroid=j,O.prototype.containsPoint=C,O.prototype.translate=_,O.prototype.rotate=q,O.prototype.accumulate=P,O.prototype.origin=E,O.prototype.clone=x,t.angle=e,t.intersect=h,t.triangle2Rectangle=w,t.cutPolygon=A,t.cutCollection=S,t.rectangle2Square=J,t.triangle2Triangle=D,t.clipCollection=B,t.polygon=I}); | |
var a = [650, 200], b = [100, 300], c = [700, 400], | |
A0, A1, data, rectangle, square; | |
data = [a, b, c]; | |
rectangle = partials.triangle2Rectangle(data); | |
var drag = d3.behavior.drag() | |
.origin(function(d) { return {x: d[0], y: d[1]}; }) | |
.on("dragstart", dragstarted) | |
.on("drag", dragged); | |
var color = d3.scale.category10(); | |
var svg = d3.select("svg").append("g"); | |
var line = d3.svg.line() | |
.x(function(d) { return d[0]; }) | |
.y(function(d) { return d[1]; }) | |
.interpolate("linear-closed"); | |
var triangle = svg.append("path") | |
.attr("class", "triangle") | |
.datum(data) | |
.attr("d", line); | |
var handle = svg.selectAll(".handle") | |
.data(data); | |
handle.enter() | |
.append("circle") | |
.attr("class", "handle") | |
.attr("r", 10) | |
.attr("cx", function(d) { return d[0]; }) | |
.attr("cy", function(d) { return d[1]; }) | |
handle.call(drag); | |
update(); | |
function update() { | |
d3.selectAll(".shape").remove(); | |
d3.selectAll(".rectangle").remove(); | |
d3.selectAll(".square").remove(); | |
rectangle = partials.triangle2Rectangle(data); | |
square = partials.rectangle2Square(rectangle); | |
var figure = svg.append("g").attr("class", "") | |
.selectAll("g") | |
.data(square) | |
.data(square) // accumulated transforms | |
.enter().append("g").attr("class", "shape") | |
path = figure.append("path") | |
.style("fill", function(d, i) { return color(i); }) | |
.attr("class", "figure") | |
.attr("d", line); | |
svg.append("path") | |
.attr("class", "rectangle") | |
.datum(rectangle.rectangle) | |
.attr("d", line); | |
svg.append("path") | |
.attr("class", "square") | |
.datum(square.square) | |
.attr("d", line); | |
triangle | |
.attr("d", line); | |
handle | |
.attr("cx", function(d) { return d[0]; }) | |
.attr("cy", function(d) { return d[1]; }); | |
} | |
function dragstarted(d) { | |
this.parentNode.appendChild(this); | |
} | |
function dragged(d) { | |
d[0] = d3.event.x; | |
d[1] = d3.event.y; | |
update(); | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment