views:

509

answers:

3
+1  A: 

I may be missing something (for example, I don't understand why you'd want to do this - maybe you are drawing things in different colors or something? If you are just trying to optimize a few write operations, I'm not sure that you are going to really gain anything by this).

But, assuming there is a good reason to do this, I'd think that the following algorithm would work:

  1. Determine all horizontal line segments, and order by y position descending and segment length descending
  2. Draw the first line segment
  3. Compare the second line segment's y-position to all previous lines in the list (in this case, just the first) that have the same y position. If you don't get an exact y-position match, draw the segment, and repeat step 3 for subsequent segments
  4. If you do get an exact y position match, compare the end point of the shorter segment to see if it's x position is between the x position of the two end points of the longer segment. If it is, then you have overlap. If it doesn't, check the other end point.

I'm assuming that the layout of the segments is such that you can't have two segments that partially overlap each other, like this (segments are aA and bB):

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

If you do have that possibility, then you'll have to decide how to resolve that.

PS - please definitely add a brief description of why you want to eliminate those segments. I'm curious!

Kevin Day
When you zoom into the diagram they become extremely obvious. The goal is to produce a graph that is pleasing to the eye. Overlapping lines detract from the diagram.
Dave Jarvis
I should add that the more items that double up, the darker the redrawn lines become. To the point where even without zooming, the lines appear different. There is no possibility of aA - bB.
Dave Jarvis
Steps 1 and 2, given the constraints of the graphing library's API, are not possible. Step 3 would take too long in a critical loop (midPointMap groups line segments along the same y-axis value). Step 4 is basically the problem: how would you implement it? For example, each segment has two X positions, so you cannot compare "its x position" against the end points of a longer segment. Also, the segments cannot be ordered by length.
Dave Jarvis
I'm not sure that I understand why you can't compare the x position of each segment end point. Point a has an x coordinate. Point b has an x coordinate. Compare those two coordinates. Same for point A and point B... If the problem is that your library lists the segment as having an x position and a length, just compute the second x position, right? The solution you posted a bit later does exactly this.
Kevin Day
My mistake, Kevin. I misread. This is a fine answer, but does not actually solve the problem, as there are still a number of details that remained unresolved (as per the solution I submitted). I knew what I had to do, I was stuck on how (i.e., the source code) to do it given the constraints of the API.
Dave Jarvis
And thank you for your help!
Dave Jarvis
A: 

Instead of having multiple line segments how about one line segment per midy.

Then

Graphics2D g2 = (Graphics2D)g;

Double left = tx < sx ? tx : sx; 
Double right = tx > sx ? tx : sx;

Line2D.Double seg = midPointMap.getValue( midy );

psegment.setLine( left < seg.getX1() ? left : seg.getX1(), midy, 
                 right < seg.getX2() ? right : seg.getX2(), midy );

//  etc.

g2.drawShape( seg );

This way you have one horizontal line segment from the minimum x to the maximum x at y.

Clint
Shapes are drawn individually. The method must return the Shape to draw for each edge. It's possible, however, to track which edges have already been drawn. I'm looking for an optimal solution, as well, since this is within a tight loop. Also, the midPointMap maps one value to a set of values. The graphics context is hidden by the graphing library's API. Lastly, even if G2D was available, this solution still would not work, as it would connect JDialog and JFrame as well as the Verifier classes. Thanks though!
Dave Jarvis
A: 

Here is a complete solution:

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

If this can be optimized (or simplified), I would really like to know.

Dave Jarvis