【问题标题】:Overlapping line segments重叠线段
【发布时间】:2009-08-23 02:45:36
【问题描述】:

下图说明了我在创建曼哈顿图时遇到的问题:


Overlapping Lines

框包围了与现有行重叠的大部分行 [(tx,midy)-(sx,midy)](在下面的代码中由 psegment 表示)。我已经删除了重叠的箭头(和尾巴),并且对如何检查重叠有点困惑。

这是有问题的代码:

  Line2D.Double segment = new Line2D.Double( sx, midy, tx, midy );

  // Associate the middle-y point with the bounds of the target object.
  // On subsequent draws of targets with a similar mid-y, make sure that
  // there are no overlapping lines.
  //
  if( midPointMap.put( midy, segment ) != null ) {
    //if( midy == 90 ) {
    // New Line.
    //
    System.err.printf( "NEW: (%3.2f, %3d)-(%3.2f, %3d)\n", sx, midy, tx,
                       midy );

    for( Line2D.Double psegment : midPointMap.getValues( midy ) ) {
      // Previous Line.
      //
      System.err.printf( "OLD: (%3.2f, %3d)-(%3.2f, %3d)\n",
                         psegment.getX1(), midy, psegment.getX2(), midy );
    }
    //}
  }

  // Line for the bus.
  //
  result.moveTo( sx, midy );
  result.lineTo( tx, midy );

这是另一个示例图片,可让您了解曼哈顿布局:

在上图中,Dialog 和 Window 之间的线已重叠(在此缩放下不太明显)。该图说明了如何存在多个子类,因此检测重叠必须考虑沿同一 y 中线的多个源 (sx, sy) 的多个目标 (tx, ty)。

midPointMap 变量是一个哈希集,每个键可以包含多个值:

  private MultiValueMap<Integer, Line2D.Double> midPointMap =
    new MultiValueMap<Integer, Line2D.Double>();

这会将中间值映射到一组线段。

如果线条与现有线段重叠,有什么想法如何不画线?

更新 #1

请注意,每个“总线”的线段没有特定的顺序。

【问题讨论】:

    标签: java algorithm graph


    【解决方案1】:

    我可能遗漏了一些东西(例如,我不明白您为什么要这样做 - 也许您正在用不同的颜色或其他东西绘制东西?如果您只是想优化一些写入操作,我'不确定你是否真的会从中获得任何好处)。

    但是,假设有充分的理由这样做,我认为以下算法会起作用:

    1. 确定所有水平线段,并按 y 位置降序和线段长度降序排列
    2. 绘制第一条线段
    3. 将第二条线段的 y 位置与列表中具有相同 y 位置的所有先前行(在本例中为第一行)进行比较。如果您没有得到精确的 y 位置匹配,请绘制线段,然后对后续线段重复第 3 步
    4. 如果确实得到了精确的 y 位置匹配,请比较较短段的端点以查看它的 x 位置是否在较长段的两个端点的 x 位置之间。如果是,那么你有重叠。如果没有,请检查另一个端点。

    我假设段的布局是这样的,你不能有两个部分相互重叠的段,像这样(段是 aA 和 bB):

    a=====b===A=========B

    如果您确实有这种可能性,那么您必须决定如何解决。

    PS - 请务必添加为什么您要删除这些片段的简要说明。我很好奇!

    【讨论】:

    • 我不确定我理解为什么你不能比较每个段端点的 x 位置。点 a 有一个 x 坐标。点 b 有一个 x 坐标。比较这两个坐标。点 A 和点 B 相同...如果问题是您的库将段列为具有 x 位置和长度,只需计算第二个 x 位置,对吗?您稍后发布的解决方案正是这样做的。
    【解决方案2】:

    这是一个完整的解决方案:

      // If line segments would overlap, this gets set to false.
      //
      boolean drawSegment = true;
    
      Line2D.Double segment = new Line2D.Double( sx, midy, tx, midy );
    
      // Associate the middle-y point with the bounds of the target object.
      // On subsequent draws of targets with a similar mid-y, make sure that
      // there are no overlapping lines.
      //
      if( midPointMap.put( midy, segment ) != null ) {
        // Check previous lines for overlap. Each previous line segment has
        // values in the form: (sx, mid-y)-(tx, mid-y), which map to
        // (getX1(), midy)-(getX2(), midy).
        //
        for( Line2D.Double psegment : midPointMap.getValues( midy ) ) {
          // If the lines have the same source point, and differ in their
          // target point, then they might overlap
          //
          if( sx == psegment.getX1() && tx != psegment.getX2() ) {
            double pdx = psegment.getX1() - psegment.getX2();
            double cdx = sx - tx;
    
            // At this juncture: the mid-y points are the same, the source
            // points of the previous segment and the current segment are the
            // same, and the target points of the segments differ.
            //
            // If both lines go in the same direction (relative to the same
            // source point), then they overlap. The difference of the tx
            // and X2 points is how much overlap exists.
            //
            // There are two actionable possibilities: (1) psegment is longer
            // than the current segment; or (2) psegment is shorter.
            //
            // If psegment is longer, then no segment must be drawn. If
            // psegment is shorter, the difference between psegment and the
            // current segment must be drawn.
            //
            if( tx < sx && psegment.getX2() < sx ) {
              // SEGMENT IS TO THE LEFT OF SOURCE
              //
              if( pdx > cdx ) {
                // If the previous segment is longer, then draw nothing.
                //
                drawSegment = false;
              }
              else {
                // If the previous segment is shorter, then draw the
                // difference. That is, change the source point for
                // this segment to the target point of the previous segment.
                //
                sx = psegment.getX2();
              }
            }
            else if( tx > sx && psegment.getX2() > sx ) {
              // SEGMENT IS TO THE RIGHT OF SOURCE
              //
              if( pdx < cdx ) {
                // If the previous segment is longer, then draw nothing.
                //
                drawSegment = false;
              }
              else {
                // If the previous segment is shorter, then draw the
                // difference. That is, change the source point for
                // this segment to the target point of the previous segment.
                //
                sx = psegment.getX2();
              }
            }
          }
        }
      }
    
      // Draw the line for the bus.
      //
      if( drawSegment ) {
        result.moveTo( sx, midy );
        result.lineTo( tx, midy );
      }
    

    【讨论】:

      猜你喜欢
      • 2021-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-26
      • 1970-01-01
      • 1970-01-01
      • 2015-03-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多