重叠的线段

时间:2009-08-23 02:45:37

标签: java algorithm graph

下图说明了创建Manhattan图时遇到的问题:


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

这会将mid-y值映射到一组线段。

如果与现有线段重叠,如何不绘制线条?

更新#1

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

2 个答案:

答案 0 :(得分:1)

我可能会遗漏某些东西(例如,我不明白你为什么要这样做 - 也许你正在用不同颜色或其他东西画画?如果你只是想优化一些写操作,我我不确定你是否会真正获得任何收益。

但是,假设有充分的理由这样做,我认为以下算法可行:

  1. 确定所有水平线段,并按y位置降序和段长度降序排序
  2. 绘制第一个线段
  3. 将第二个线段的y位置与列表中所有具有相同y位置的前一行(在这种情况下,只是第一个)进行比较。如果您没有得到精确的y位置匹配,请绘制该段,然后对后续段重复步骤3
  4. 如果确实得到了精确的y位置匹配,请比较较短段的终点,看它的x位置是否在较长段的两个终点的x位置之间。如果是,那么你有重叠。如果没有,请检查另一个终点。
  5. 我假设片段的布局是这样的,你不能有两个部分相互重叠的片段,像这样(片段是aA和bB):

    一个===== b ===甲=========乙

    如果你确实有这种可能性,那么你将不得不决定如何解决这个问题。

    PS - 请务必添加为什么的简要说明,以消除这些细分。我很好奇!

答案 1 :(得分:0)

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

  // 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 );
  }

如果可以优化(或简化),我真的很想知道。