So I have an array of points (x, y) that form a path. I need to calculate the points of a surrounding polygon. Essentially CGPathCreateCopyByStrokingPath
if you're familiar with iOS. Unfortunately, ObjC is not an option. I need to implement it either in Javascript, PHP, etc.
-------
| | |
| | |
\ \ \
\ \ \
\ \ \
\ \ \
\ \ \-----------
\ \----------- |
\---------------
Apologize for the bad ascii art.
I have a semi-working version in Javascript, but I have issues with corners.
function pathToPoly(points) {
var numOfPoints = points.length;
var fullPath = [];
var leftPaths = [];
var rightPaths = [];
var pad = 20;
for(var i=0; i<numOfPoints-1; i++) {
var pointA = points[i];
var pointB = points[i+1];
var slope = (pointB.Y - pointA.Y) / (pointB.X - pointA.X);
var inverseSlope = -1 / slope;
var inverseAngle = Math.atan(inverseSlope);
if(inverseAngle < 0) {
leftPaths.push({
X1: pointA.X - pad * Math.cos(inverseAngle),
Y1: pointA.Y - pad * Math.sin(inverseAngle),
X2: pointB.X - pad * Math.cos(inverseAngle),
Y2: pointB.Y - pad * Math.sin(inverseAngle)
});
rightPaths.push({
X1: pointA.X + pad * Math.cos(inverseAngle),
Y1: pointA.Y + pad * Math.sin(inverseAngle),
X2: pointB.X + pad * Math.cos(inverseAngle),
Y2: pointB.Y + pad * Math.sin(inverseAngle)
});
} else {
rightPaths.push({
X1: pointA.X - pad * Math.cos(inverseAngle),
Y1: pointA.Y - pad * Math.sin(inverseAngle),
X2: pointB.X - pad * Math.cos(inverseAngle),
Y2: pointB.Y - pad * Math.sin(inverseAngle)
});
leftPaths.push({
X1: pointA.X + pad * Math.cos(inverseAngle),
Y1: pointA.Y + pad * Math.sin(inverseAngle),
X2: pointB.X + pad * Math.cos(inverseAngle),
Y2: pointB.Y + pad * Math.sin(inverseAngle)
});
}
if(drawSides) {
var leftSide = leftPaths[i];
var rightSide = rightPaths[i];
context.beginPath();
context.moveTo(leftSide.X1, leftSide.Y1);
context.lineTo(leftSide.X2, leftSide.Y2);
context.lineWidth = 1;
context.strokeStyle = 'yellow';
context.stroke();
context.beginPath();
context.moveTo(rightSide.X1, rightSide.Y1);
context.lineTo(rightSide.X2, rightSide.Y2);
context.lineWidth = 1;
context.strokeStyle = 'cyan';
context.stroke();
}
}
for(var i=0; i<numOfPoints-1; i++) {
var line1 = leftPaths[i];
var line2 = leftPaths[i+1];
fullPath.push({
X: line1.X1,
Y: line1.Y1
});
fullPath.push({
X: line1.X2,
Y: line1.Y2,
});
if(line2) {
fullPath.push({
X: line2.X1,
Y: line2.Y1,
});
}
}
fullPath.push({
X: leftPaths[numOfPoints-2].X2,
Y: leftPaths[numOfPoints-2].Y2
});
for(var i=numOfPoints-2; i>=0; i--) {
var line1 = rightPaths[i];
var line2 = rightPaths[i-1];
fullPath.push({
X: line1.X2,
Y: line1.Y2
});
fullPath.push({
X: line1.X1,
Y: line1.Y1,
});
if(line2) {
fullPath.push({
X: line2.X2,
Y: line2.Y2,
});
}
}
fullPath.push({
X: rightPaths[0].X1,
Y: rightPaths[0].Y1
});
return fullPath;
}
This code draws parallel lines on each side of every segment, and connects them. But for turns, my methods creates an INVALID polygon. The sides "swap" and generate an "inverse" area. And being able to calculate the area is crucial for my application.
Example (my reputation is too low to post images): The blue works fine, but the red fails (notice how the bottom right turn, the sides swap).

Any suggestions?
Looks like it fails when and only when the line bends at an acute angle (based on a small sample), correct?
I haven't gone thru the algorithm thoroughly, but just an educated guess: I wonder if you've taken into account the fact that for atan(slope)
, there are two right answers, and the javascript atan()
function may not be returning the answer you're expecting (180 degrees off)?
atan2(y, x) eliminates this ambiguity, if you can use it.