|
|
Inset A Polygon By A Fixed, Perpendicular Distance, With C Code Sample
© 2007 Darel Rex Finley. This complete article, unmodified, may be freely
distributed for educational purposes.

Suppose you have a polygon like the black one above. How do you create a polygon like the blue one, that is inset by a fixed distance (red) from every side of the black polygon? The key is figuring out how to inset each corner of the polygon so that the new sides are exactly the right distance from the old sides. To do that, we will offset each side by the desired distance, then find the point where they intersect, like so:

If the two sides do not intersect, extend them as far as necessary until they do:

If the two sides are colinear, they have no real intersection point, so we will just use the point where they meet:

C Code Sample
// public-domain code by Darel Rex Finley, 2007
// See diagrams at http://alienryderflex.com/polygon_inset
void insetPolygon(double *x, double *y, int corners, double insetDist) {
double startX=x[0], startY=y[0], a, b, c, d, e, f ;
int i ;
// Polygon must have at least three corners to be inset.
if (corners<3) return;
// Inset the polygon.
c=x[corners-1]; d=y[corners-1]; e=x[0]; f=y[0];
for (i=0; i<corners-1; i++) {
a=c; b=d; c=e; d=f; e=x[i+1]; f=y[i+1];
insetCorner(a,b,c,d,e,f,&x[i],&y[i],insetDist); }
insetCorner(c,d,e,f,startX,startY,&x[i],&y[i],insetDist); }
// Given the sequentially connected points (a,b), (c,d), and (e,f), this
// function returns, in (C,D), a bevel-inset replacement for point (c,d).
//
// Note: If vectors (a,b)->(c,d) and (c,d)->(e,f) are exactly 180° opposed,
// or if either segment is zero-length, this function will do
// nothing; i.e. point (C,D) will not be set.
inline insetCorner(
double a, double b, // previous point
double c, double d, // current point that needs to be inset
double e, double f, // next point
double *C, double *D, // storage location for new, inset point
double insetDist) { // amount of inset (perpendicular to each line segment)
double c1=c, d1=d, c2=c, d2=d, dx1, dy1, dist1, dx2, dy2, dist2, insetX, insetY ;
// Calculate length of line segments.
dx1=c-a; dy1=d-b; dist1=sqrt(dx1*dx1+dy1*dy1);
dx2=e-c; dy2=f-d; dist2=sqrt(dx2*dx2+dy2*dy2);
// Exit if either segment is zero-length.
if (dist1==0. || dist2==0.) return;
// Inset each of the two line segments.
insetX= dy1/dist1*insetDist; a+=insetX; c1+=insetX;
insetY=-dx1/dist1*insetDist; b+=insetY; d1+=insetY;
insetX= dy2/dist2*insetDist; e+=insetX; c2+=insetX;
insetY=-dx2/dist2*insetDist; f+=insetY; d2+=insetY;
// If inset segments connect perfectly, return the connection point.
if (c1==c2 && d1==d2) {
*C=c1; *D=d1; return; }
// Return the intersection point of the two inset segments (if any).
if (lineIntersection(a,b,c1,d1,c2,d2,e,f,&insetX,&insetY)) {
*C=insetX; *D=insetY; }}
|
This code is not significantly disturbed by polygons that include colinear switchbacks — it just won’t inset the points that can’t be inset. However, polygons that have narrow pieces that don’t leave enough room for inset will give wierd results that don’t look line a polygon inset, for example:

Clockwise vs. counterclockwise
The above code is written to work correctly on a clockwise-traced polygon. If the polygon is traced counterclockwise, the new polygon will be outset — for example, the blue polygon at the top of this article would become the black one. You can also outset a polygon by passing a negative value in insetDist. And, if your polygon is traced counterclockwise, you can inset it by passing a negative value in insetDist.
Send me an e-mail!
|
Does the brace style in the above code sample freak
you out? Click here to see it explained in a new
window. |
Back to tutorials.
|