Skip to content

Instantly share code, notes, and snippets.

@bmershon
Last active May 7, 2016 15:37
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 bmershon/14972d48da2c362841d6073b267c815f to your computer and use it in GitHub Desktop.
Save bmershon/14972d48da2c362841d6073b267c815f to your computer and use it in GitHub Desktop.
Triangle to Square II
license: gpl-3.0
border: yes
<!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