How to draw an outline around any line

2019-01-18 13:05发布

enter image description here

So I have a arbitary line (See an example shown in fig 1) made up of n points

I want to draw an outline around this line (see fig 2) so I need to calculate the points of the surrounding polygon.

I started by performing a dilation on the line but this wont work - see figure 3

Any suggestions on how to do this?

I suspect calculating the normal of each line segment for use in translating the new line below and a new line above its current position and then extending each new line to infinity and defining the points as the intersections?

5条回答
看我几分像从前
2楼-- · 2019-01-18 13:36

Create all the lines before you render them.

When you do, they should overlap, like this: enter image description here

The ones I drew, obviously, are the ones that get trimmed which would reveal the outline.

查看更多
欢心
3楼-- · 2019-01-18 13:40

First duplicate each line twice, once on each side at a distance of half the width you want from each original line. That gives you the green lines in the image. Then you need to visit them in order (numbered) and deal with the loose ends.

line outlining

When the lines don't meet (2-3, 6-7 and 12-13) you add a line join (in blue). A line join can be a bevel join (2-3) by just connecting the points, or a miter join by extending the lines until they meet (6-7) or a round join by making a curve.

When the lines do meet, just take the intersection point (blue dots).

At the line ends, you need to add an end cap (also in blue). An end cap can be a butt cap (8-9) by connecting the points, a projecting cap (1-16) by extending the lines a little before connecting them, or a round cap (not shown).

The end result is a polygon (or path if it includes round joins) that you can then stroke or fill.

查看更多
仙女界的扛把子
4楼-- · 2019-01-18 13:43

If you have the points of the line segments, you can easily create two parallel lines to each segment and calculate the connection point where they intersect with the next if they were lines (and not line segments). This site should give you all you need to calculate super fast intersections:

http://www.math.niu.edu/~rusin/known-math/95/line_segs

查看更多
走好不送
5楼-- · 2019-01-18 13:46

I have figured out a way to calculate the outline points of a line. For each point of the original line you will have to compute 2 points for the outline:

  1. for each line segment of the original line (between 2 points) you have to compute its normal vector (red)
  2. for each point, add the normals of the previous and next line segment. This produces a new vector (green)
  3. divide the new vector with the value: kl+1 , where kl is the dot product of the normal vectors. You'll get the blue vector. Then add this vector on the current point and its opposite vector and you'll get the 2 outline points for the current point

The colors above correspond to this image. enter image description here

I have programmed this function in C, but I used Accelerate Framework so it's not very easy to read. You can find the source code here and a video running the demo here.

查看更多
啃猪蹄的小仙女
6楼-- · 2019-01-18 13:52

Here is some code of mine in Objective-C that's doing it (even if it's sometimes buggy, I don't know why, let me know how it goes for you...) :

  • it takes each edge of your polyline, then create a first array on the perpendicular of the current edge, on the right (to be in the same way of your polyline, CCW or CW), then a second array on the left.
  • for each edge it tests the point where the 2 infinite straight lines (as your edges are segments) should intersect.
  • finally it adds each point in the desired order to make a polygon

    - (hOzPolygon2D *) convertToPolygonWithWidth:(double) polyWidth
    {
    
    double shift = polyWidth / 2.;
    
    NSMutableArray *tempEdgesRight = [[[NSMutableArray alloc] init] autorelease];
    NSMutableArray *tempEdgesLeft = [[[NSMutableArray alloc] init] autorelease];
    
    NSMutableArray *tempPolyPoints = [[[NSMutableArray alloc] init] autorelease];
    
    // Move your points on the right by half the desired width
    
    // My edges are already computed in a NSArray* called edges, 
    // but you can use pairs of vectors and adapt all this
    
    for (hOzEdge2D *edge in edges) {
    
    hOzVector2 v = hOzVector2([[edge pointB] x] - [[edge pointA] x], [[edge pointB] y] - [[edge pointA] y]);
    double mag = sqrt (v.x * v.x + v.y * v.y);
    v.x = v.x / mag;
    v.y = v.y / mag;
    
    double temp = v.x;
    v.x = v.y;
    v.y = -temp;
    
    hOzPoint2D *newPointA = [[hOzPoint2D alloc] init];
    [newPointA setX:([[edge pointA] x] + v.x * shift)];
    [newPointA setY:([[edge pointA] y] + v.y * shift)];
    
    hOzPoint2D *newPointB = [[hOzPoint2D alloc] init];
    [newPointB setX:([[edge pointB] x] + v.x * shift)];
    [newPointB setY:([[edge pointB] y] + v.y * shift)];
    
    [tempEdgesRight addObject:[hOzEdge2D edge2DWithPointA:newPointA pointB:newPointB]];
    
    }
    
    // With the same polyline, move on the left
    
    for (int j = [edges count] - 1; j >= 0; j--) {
    
    hOzVector2 v = hOzVector2([[[edges objectAtIndex:j] pointB] x] - [[[edges objectAtIndex:j] pointA] x], [[[edges objectAtIndex:j] pointB] y] - [[[edges objectAtIndex:j] pointA] y]);
    double mag = sqrt (v.x * v.x + v.y * v.y);
    v.x = v.x / mag;
    v.y = v.y / mag;
    
    double temp = v.x;
    v.x = v.y;
    v.y = -temp;
    
    hOzPoint2D *newPointA = [[hOzPoint2D alloc] init];
    [newPointA setX:([[[edges objectAtIndex:j] pointB] x] - v.x * shift)];
    [newPointA setY:([[[edges objectAtIndex:j] pointB] y] - v.y * shift)];
    
    hOzPoint2D *newPointB = [[hOzPoint2D alloc] init];
    [newPointB setX:([[[edges objectAtIndex:j] pointA] x] - v.x * shift)];
    [newPointB setY:([[[edges objectAtIndex:j] pointA] y] - v.y * shift)];
    
    [tempEdgesLeft addObject:[hOzEdge2D edge2DWithPointA:newPointA pointB:newPointB]];
    
    }
    
    
    
    // Add the static points and the intersection points to a points array that will define your polygon
    
    [tempPolyPoints addObject:[[tempEdgesRight objectAtIndex:0] pointA]];  // The first point of the right array
    
    for (int k = 0; k < [tempEdgesRight count] - 1; k++) {
    
    // For this function, see the link below in the answer
    
    hOzPoint2D *inter = [[tempEdgesRight objectAtIndex:k] getIntersectionWithStraight:[tempEdgesRight objectAtIndex:k+1]];   
    
    if (inter == nil) {    // if the edges are parallel, we insert a known point
        [tempPolyPoints addObject:[[tempEdgesRight objectAtIndex:k] pointB]];
    } else {
        [tempPolyPoints addObject:inter];
    }
    }
    
    [tempPolyPoints addObject:[[tempEdgesRight lastObject] pointB]];    // The last point of the right array
    
    [tempPolyPoints addObject:[[tempEdgesLeft objectAtIndex:0] pointA]];
    
    // Then the left array, same thing
    
    for (int k = 0; k < [tempEdgesLeft count] - 1; k++) {
    hOzPoint2D *inter = [[tempEdgesLeft objectAtIndex:k] getIntersectionWithStraight:[tempEdgesLeft objectAtIndex:k+1]];
    
    if (inter == nil) {
        [tempPolyPoints addObject:[[tempEdgesLeft objectAtIndex:k] pointB]];
    } else {
        [tempPolyPoints addObject:inter];
    }
    }
    
    [tempPolyPoints addObject:[[tempEdgesLeft lastObject] pointB]];
    
    // Create your polygon with this new ordered points array.
    hOzPolygon2D *poly = [hOzPolygon2D polygon2DWithArrayOfPoints:tempPolyPoints];
    
    return poly;
    
    }
    

Here is some explanation for the intersection point, with C code : http://alienryderflex.com/intersect/

查看更多
登录 后发表回答