var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var r1={x:20,y:60,w:100,h:40,color:'red',id:0}
var r2={x:50,y:20,w:40,h:60,color:'blue',id:1}
var r3={x:100,y:80,w:100,h:60,color:'green',id:2}
var r4={x:160,y:80,w:70,h:60,color:'purple',id:3}
var rects=[r1,r2,r3,r4];
var rpts=[];
ctx.translate(0.50,0.50);
var nextId=0;
for(var i=0;i<rects.length;i++){
var r=rects[i];
var x=r.x;
var y=r.y;
var w=r.w;
var h=r.h;
ctx.strokeStyle=r.color;
ctx.strokeRect(x,y,w,h);
rpts.push({x:x,y:y},{x:x+w,y:y},{x:x+w,y:y+h},{x:x,y:y+h});
}
function multiRectVertices(rects){
var pts=[];
var segs=[];
// rectangle vertices
for(var r=0;r<rects.length;r++){
var rect=rects[r];
var x=rect.x;
var y=rect.y;
var w=rect.w;
var h=rect.h;
pts.push([x,y]);
pts.push([x+w,y]);
pts.push([x+w,y+h]);
pts.push([x,y+h]);
segs.push({x0:x, y0:y, x1:x+w, y1:y});
segs.push({x0:x+w, y0:y, x1:x+w, y1:y+h});
segs.push({x0:x+w, y0:y+h, x1:x, y1:y+h});
segs.push({x0:x, y0:y+h, x1:x, y1:y});
}
// intersection points
for(var s=0;s<segs.length;s++){
line0=segs[s];
for(var i=s+1;i<segs.length;i++){
var intersection=line2lineIntersection(line0,segs[i]);
if(intersection){ pts.push([intersection.x,intersection.y]);}
}
}
//
return(pts);
}
// get rect vertices and intersections
var rectPts=multiRectVertices(rects);
// get simplified perimeter based on pixels
// Attribution: K3N on Stackoverflow, epistemex on GitHub:
// https://github.com/epistemex/msqr
var msPts=MSQR(ctx,{alpha:200,tolerance:1.1})[0];
alignMSPts(msPts,rectPts);
function alignMSPts(msPts,rectPts){
var pt,rpt,px,py,rx,ry,dx,dy,minDist,dist;
for(var i=0;i<msPts.length;i++){
minDist=1000000*100000;
pt=msPts[i];
px=pt.x;
py=pt.y;
for(var j=0;j<rectPts.length;j++){
rpt=rectPts[i];
rx=rpt.x;
ry=rpt.y;
dx=px-rx;
dy=py-ry;
dist=dx*dx+dy*dy;
if(dist<minDist){
minDist=dist;
msPts[i]={x:rx,y:ry};
}
}
}
}
ctx.beginPath();
ctx.moveTo(msPts[0].x,msPts[0].y);
for(var i=1;i<msPts.length;i++){
ctx.lineTo(msPts[i].x,msPts[i].y);
}
ctx.strokeStyle='black';
ctx.lineWidth=3;
ctx.closePath();
ctx.stroke();
ctx.fillStyle='gold';
for(var i=0;i<msPts.length;i++){
ctx.beginPath();
ctx.arc(msPts[i].x,msPts[i].y,3,0,Math.PI*2);
ctx.fill();
}
// Get interseting point of 2 line segments (if any)
// Attribution: http://paulbourke.net/geometry/pointlineplane/
// Segment #1: [x0,y0] to [x1,y1], Segment#2: [x2,y2] to [x3,y3]
function line2lineIntersection(line0,line1){
var x0=line0.x0;
var y0=line0.y0;
var x1=line0.x1;
var y1=line0.y1;
var x2=line1.x0;
var y2=line1.y0;
var x3=line1.x1;
var y3=line1.y1;
var unknownA = (x3-x2) * (y0-y2) - (y3-y2) * (x0-x2);
var unknownB = (x1-x0) * (y0-y2) - (y1-y0) * (x0-x2);
var denominator = (y3-y2) * (x1-x0) - (x3-x2) * (y1-y0);
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
if (denominator == 0) return null;
unknownA /= denominator;
unknownB /= denominator;
var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1)
if(!isIntersecting){return(null);}
return({
x: x0 + unknownA * (x1-x0),
y: y0 + unknownA * (y1-y0)
});
}
body{ background-color:white; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://cdn.rawgit.com/epistemex/msqr/master/msqr.min.js"></script>
<canvas id="canvas" width=300 height=300></canvas>