无法与Bentley Ottmann实施所有交叉点

时间:2016-06-21 11:53:02

标签: java algorithm geometry processing

我在Processing中编写了一个程序,用于实现线段交叉问题的Bentley-Ottmann算法。

程序返回一组点,但没有找到所有的交点。有谁能告诉我,我做错了什么?

  • ArrayList保存与扫描线相交的活动线段。
  • 对于每个端点,我在TreeMap(Double,List(Line2D))结构中存储其x位置和它所属的Line2D。
  • 我持有一个Lines2D列表(TreeMap(Double, List(Line2D)),因为如果我找到一个交叉点,我会报告它的x位置和相交的两条线。

我想我遵循了算法所需的所有步骤。我研究了“计算几何 - 算法和应用”,以及一个名为“计算一组线段中的交叉点:Bentley-Ottmann算法”的文章,来自Michiel Smid。

虽然,我没有考虑垂直线的限制,与三个线段的交叉等。大多数时候要求得到满足,但结果仍然不是预期的。

这是一个例子

enter image description here

这是我的代码

import java.util.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.List;
import java.util.concurrent.TimeUnit;

Algorithm algorithm = new Algorithm();
List<Line2D> initialList = new ArrayList<Line2D>();
List<Double> xStructure = new ArrayList<Double>();
Set<Point2D> intersections = new HashSet<Point2D>();

void setup() {
  size(640, 480);
  initialList = algorithm.createListOfRandomLines(5);
  long start = System.currentTimeMillis();
  intersections = algorithm.runBentleyOttmann(initialList);
  long stop = System.currentTimeMillis() - start;
  System.out.println(String.format("Execution Time: %f sec", (float)TimeUnit.MILLISECONDS.toSeconds(stop)));
}

void draw() {
  background(255);
  strokeWeight(1);
  stroke(0);
  for(Line2D l : initialList)
    line((float)l.getX1(), (float)l.getY1(), (float)l.getX2(), (float)l.getY2());
  strokeWeight(5);
  stroke(255,0,0);
  for(Point2D p : intersections)
    point((float)p.getX(), (float)p.getY());
}

class Algorithm {    
  TreeMap<Double, List<Line2D>> xStructure;  
  Double slPosition;
  List<Line2D> slStatus; // Y-structure
  Set<Point2D> intersectionPoints;

  public Algorithm() {
    intersectionPoints = new HashSet<Point2D>();
    slPosition = Double.MIN_VALUE;
    slStatus = new ArrayList<Line2D>();
    xStructure = new TreeMap<Double, List<Line2D>>();
  }

  public Set<Point2D> runBentleyOttmann(List<Line2D> list) {
    //System.out.println("DEBUG INFO: runBentleyOttmann got input a list of " + list.size() + " line segments.");
    xStructure = createMapOfSortedEndpointsAndCorespondingLineSegment(list);
    //System.out.println("DEBUG INFO: runBentleyOttmann x-structure now contains the " + xStructure.size() + "  endpoints.");
    //System.out.println("DEBUG INTO: runBentleyOttmann sweep line is at position " + slPosition);
    //System.out.println("DEBUG INFO: runBentleyOttmann begins while loop");
    while (!xStructure.isEmpty()) {      
      Map.Entry<Double, List<Line2D>> entry = xStructure.pollFirstEntry();
      Double min = entry.getKey();
      slPosition = min;
      List<Line2D> lines = entry.getValue();
      //System.out.println("-----DEBUG INFO: runBentleyOttmann let min be the minimum element of x-structure " + min);      
      //System.out.println("-----DEBUG INFO: runBentleyOttmann delete min from the x-structure");
      //System.out.println("-----DEBUG INFO: runBentleyOttmann checks endpoint type");
      if (checkEndpointType(lines) == EndpointType.LEFT) {
        //System.out.println("----------DEBUG INFO: Endpoint Type LEFT");
        handleLeftEndpoint(lines);        
      }
      else if (checkEndpointType(lines) == EndpointType.RIGHT) {
        //System.out.println("----------DEBUG INFO: Endpoint Type RIGHT");        
        handleRightEndpoint(lines);
      }
      else if (checkEndpointType(lines) == EndpointType.INTERSECTION) {
        //System.out.println("----------DEBUG INFO: Endpoint Type INTERSECTION");
        handleIntersectionEndpoint(lines);
      }
    }
    //System.out.println("DEBUG INFO: runBentleyOttmann ends while loop");
    return intersectionPoints;
  }

  public void handleIntersectionEndpoint(List<Line2D> lines) {
    Line2D l1 = slStatus.get(slStatus.indexOf(lines.get(0)));
    Line2D l2 = slStatus.get(slStatus.indexOf(lines.get(1)));
    if (slStatus.indexOf(l1) > slStatus.indexOf(l2)) {
      Line2D ltemp = slStatus.get(slStatus.indexOf(lines.get(0)));
      l1 = l2;
      l2 = ltemp;
    }
    Line2D prev;
    Line2D next;
    if (slStatus.indexOf(l1) > 0) {
      prev = getAboveLine(l1);
      if (prev != null)
        if (l2.intersectsLine(prev)) {
          Point2D ip = findIntersectionPoint(l2, prev);
          if (ip.getX() > slPosition) {
            intersectionPoints.add(findIntersectionPoint(l2, prev));
            List<Line2D> tempList = new ArrayList<Line2D>();
            tempList.add(l2);
            tempList.add(prev);
            xStructure.put(ip.getX(), tempList);
          }
        }
    } 
    if (slStatus.indexOf(l2) < slStatus.size()-1) {
      next = getBelowLine(l2);
      if (next != null)
        if (l1.intersectsLine(next)) {
          Point2D ip = findIntersectionPoint(l1, next);
          if (ip.getX() > slPosition) {
            intersectionPoints.add(findIntersectionPoint(l1, next));
            List<Line2D> tempList = new ArrayList<Line2D>();
            tempList.add(l1);
            tempList.add(next);
            xStructure.put(ip.getX(), tempList);
          }
        }
    }
    System.out.println(slStatus.indexOf(l1) + " " + slStatus.indexOf(l2));
  }

  public void handleLeftEndpoint(List<Line2D> lines) {
    //System.out.println("HANDLE LEFT ENDPOINT started");
    slStatus.add(lines.get(0));
    //System.out.println("Line [X1=" + lines.get(0).getX1() + ", Y1=" + lines.get(0).getY1() + ", X2=" + lines.get(0).getX2() + ", Y2=" + lines.get(0).getY2() + "] added to Y-Structure.");
    Collections.sort(slStatus, new YComparator());
    //System.out.println("Y-Structure is sorted by Y endpoint");
    Line2D above = getAboveLine(lines.get(0));
    if (above != null)
      if (lines.get(0).intersectsLine(above)) {
        Point2D ip = findIntersectionPoint(lines.get(0), above);
        if (ip.getX() > slPosition) {
          intersectionPoints.add(findIntersectionPoint(lines.get(0), above));
          List<Line2D> tempList = new ArrayList<Line2D>();
          tempList.add(lines.get(0));
          tempList.add(above);
          xStructure.put(ip.getX(), tempList);
        }
      }
    Line2D below = getBelowLine(lines.get(0));
    if (below != null)
      if (lines.get(0).intersectsLine(below)) {
        Point2D ip = findIntersectionPoint(lines.get(0), below);
          if (ip.getX() > slPosition) {
            intersectionPoints.add(findIntersectionPoint(lines.get(0), below));
            List<Line2D> tempList = new ArrayList<Line2D>();
            tempList.add(lines.get(0));
            tempList.add(below);
            xStructure.put(ip.getX(), tempList);
          }
      }
    //System.out.println("HANDLE LEFT ENDPOINT finished");    
  }

  public void handleRightEndpoint(List<Line2D> lines) {
    //System.out.println("HANDLE RIGHT ENDPOINT started");
    Line2D above = getAboveLine(lines.get(0));
    Line2D below = getBelowLine(lines.get(0));
    if (above != null && below != null)    
      if (above.intersectsLine(below)){
        Point2D ip = findIntersectionPoint(lines.get(0), below);
        if (ip.getX() > slPosition)
          intersectionPoints.add(findIntersectionPoint(lines.get(0), below));
      }
    slStatus.remove(lines.get(0));
    //System.out.println("Line [X1=" + lines.get(0).getX1() + ", Y1=" + lines.get(0).getY1() + ", X2=" + lines.get(0).getX2() + ", Y2=" + lines.get(0).getY2() + "] deleted from Y-Structure.");
  }

  public Line2D getAboveLine(Line2D line) {
    if (slStatus.indexOf(line) > 0) {
      Line2D a = slStatus.get(slStatus.indexOf(line)-1);
      //System.out.println("getAboveLine returns line [X1=" + a.getX1() + ", Y1=" + a.getY1() + ", X2=" + a.getX2() + ", Y2=" + a.getY2() + "]");
      return a;
    }
    //System.out.println("getAboveLine returns no line");
    return null;
  }

  public Line2D getBelowLine(Line2D line) {
    if (slStatus.indexOf(line) < slStatus.size()-1) {
      Line2D b = slStatus.get(slStatus.indexOf(line)+1);
      //System.out.println("getBelowLine returns line [X1=" + b.getX1() + ", Y1=" + b.getY1() + ", X2=" + b.getX2() + ", Y2=" + b.getY2() + "]");
      //System.out.println("getAboveLine returns no line");
      return b;
    }
    return null;
  }

  public EndpointType checkEndpointType(List<Line2D> lines) {
    if (lines.size() == 2)
      return EndpointType.INTERSECTION;
    if (slStatus.contains(lines.get(0)))
      return EndpointType.RIGHT;
    if (!slStatus.contains(lines.get(0)))
      return EndpointType.LEFT;  
    return null;
  }

  public List<Line2D> sortEndpointsByXCoordinate(List<Line2D> inputList) {
    List<Line2D> returnList = new ArrayList(inputList);
    Collections.sort(returnList, new XComparator());
    return returnList;
  }

  public TreeMap<Double, List<Line2D>> createMapOfSortedEndpointsAndCorespondingLineSegment(List<Line2D> inputList) {
    TreeMap returnTreeMap = new TreeMap<Double, Line2D>();
    for (Line2D l : inputList) {
      List<Line2D> tempList = new ArrayList<Line2D>();
      tempList.add(l);
      returnTreeMap.put(l.getX1(), tempList);      
      returnTreeMap.put(l.getX2(), tempList);
    }
    return returnTreeMap;
  }

  public List<Double> createListOfSortedEndpoints(List<Line2D> inputList) {
    List<Double> returnList = new ArrayList<Double>();
    for (Line2D l : inputList) {
      returnList.add(l.getBounds().getX());
      returnList.add(l.getBounds().getY());
    }
    Collections.sort(returnList);
    //System.out.println("DEBUG INFO: createListOfSortedEndpoints returns " + returnList.size() + " endpoints");
    return returnList;
  }

  public List<Line2D> createListOfRandomLines(int size) {
    Set<Line2D> hashset = new HashSet<Line2D>();

    Random random = new Random();

    while (hashset.size() != size) {
      Line2D line = new Line2D.Double();
      line.setLine(random.nextInt(width-1)+1, random.nextInt(height-1)+1, random.nextInt(width-1)+1, random.nextInt(height-1)+1);
      hashset.add(line);
    }

    List<Line2D> returnList = new ArrayList(hashset);  

    return returnList;
  }

  public Point2D findIntersectionPoint(Line2D l1, Line2D l2) {
    Double p0_x = l1.getX1();
    Double p0_y = l1.getY1();
    Double p1_x = l1.getX2();
    Double p1_y = l1.getY2();
    Double p2_x = l2.getX1();
    Double p2_y = l2.getY1();
    Double p3_x = l2.getX2();
    Double p3_y = l2.getY2();
    Double s1_x = p1_x - p0_x;
    Double s1_y = p1_y - p0_y;
    Double s2_x = p3_x - p2_x;
    Double s2_y = p3_y - p2_y;
    Double s, t;
    s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
    Double i_x, i_y;
    i_x = p0_x + (t * s1_x);
    i_y = p0_y + (t * s1_y);
    Point2D p = new Point2D.Double(i_x, i_y);
    return p;
  }
}

class XComparator implements Comparator<Line2D>{
  public int compare(Line2D l1, Line2D l2){
    return new Double(l1.getBounds().getX()).compareTo(l2.getBounds().getX());
  }
}

class YComparator implements Comparator<Line2D>{
  public int compare(Line2D l1, Line2D l2){
    return new Double(l1.getBounds().getY()).compareTo(l2.getBounds().getY());
  }
}

enum EndpointType {
  LEFT, RIGHT, INTERSECTION
};

1 个答案:

答案 0 :(得分:0)

我建议考虑以下Java实现:https://github.com/stanislav-antonov/bentley-ottmann

它非常干净和简约,但是仍然可以处理所有主要的边缘情况。