【问题标题】:How to draw an outline around any line如何在任何线周围绘制轮廓
【发布时间】:2011-04-12 21:19:08
【问题描述】:

所以我有一条由 n 个点组成的任意线(参见图 1 中的示例)

我想在这条线周围画一个轮廓(见图2),所以我需要计算周围多边形的点。

我首先对线路进行扩张,但这不起作用 - 参见图 3

关于如何做到这一点的任何建议?

我怀疑计算每条线段的法线用于平移下面的新线和当前位置上方的新线,然后将每条新线延伸到无穷大并将这些点定义为交点?

【问题讨论】:

  • 我认为我的回答不够笼统,所以我将其删除。我们会看看我能不能想出更好的东西。

标签: math vector geometry outline


【解决方案1】:

首先将每条线复制两次,每边复制一次,距离每条原始线所需宽度的一半。这会给你图像中的绿线。然后您需要按顺序(编号)访问它们并处理松散的末端。

当线不相交时(2-3、6-7 和 12-13),您添加 线连接(蓝色)。线连接可以是 斜角连接 (2-3),只需连接点,也可以是 斜接,方法是延长线直到它们相遇 (6-7) 或圆形连接通过制作曲线。

当线相交时,只需取交点(蓝点)。

在行尾,您需要添加一个end cap(也是蓝色的)。一个端盖可以是一个 butt cap (8-9) 通过连接点,一个 projecting cap (1-16) 通过在连接它们之前稍微延长线,或圆帽(未显示)。

最终结果是一个多边形(或路径,如果它包括圆形连接),然后您可以对其进行描边或填充。

【讨论】:

  • 我过去做过类似的任务,但这不适用于比轮廓宽度更小的线条。可以对正常的交叉口进行一些额外的测试来处理大多数情况,但遗憾的是我没有弄清楚它是 100% 可靠的状态:(
【解决方案2】:

我找到了一种计算线条轮廓点的方法。对于原始线的每个点,您必须为轮廓计算 2 个点:

  1. 对于原始线的每个线段(2 点之间),您必须计算其法线向量(红色)
  2. 对于每个点,添加上一条和下一条线段的法线。这会产生一个新的向量(绿色)
  3. 将新向量除以以下值:kl+1,其中 kl 是法线向量的点积。你会得到蓝色的矢量。然后将此向量添加到当前点及其相反向量上,您将获得当前点的 2 个轮廓点

上面的颜色对应this image

我用 C 编写了这个函数,但是我使用了 Accelerate Framework,所以它不是很容易阅读。您可以找到源代码here 和运行演示的视频here

【讨论】:

  • 谁能解释这背后的数学原理?第三点有点混乱(除以dotProduct+1)。
【解决方案3】:

在渲染之前创建所有线条。

当你这样做时,它们应该重叠,如下所示:

很明显,我画的是那些被修剪过的会露出轮廓的。

【讨论】:

  • 你通过什么方式计算轮廓点的位置?
  • 您将在给定距离处选取与原始线平行的线,并且该距离在每个方向上都更长。然后你也可以加入他们的行尾。
【解决方案4】:

如果您有线段的点,您可以轻松地为每个线段创建两条平行线,并计算它们与下一条线(而不是线段)相交的连接点。该网站应该为您提供所有计算超快速交叉路口所需的信息:

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

【讨论】:

    【解决方案5】:

    这是我在 Objective-C 中的一些代码(即使它有时是错误的,我不知道为什么,让我知道它对你有什么好处......):

    • 它获取折线的每条边,然后在右侧(与折线、CCW 或 CW 相同的方式)在当前边的垂线上创建第一个数组,然后在左侧创建第二个数组.
    • 对于每条边,它会测试 2 条无限直线(因为您的边是线段)应该相交的点。
    • 最后它以所需的顺序添加每个点以制作多边形

      - (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;
      
      }
      

    这里是交点的一些解释,用C代码:http://alienryderflex.com/intersect/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-30
      • 2012-09-05
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      • 1970-01-01
      • 2018-02-13
      • 2019-11-04
      相关资源
      最近更新 更多