Пример #1
0
 private static Point2D intersect(KDNode node, Segment seg) {
   if (node.isLeaf()) {
     // long begin = System.nanoTime();
     Intersection min = null;
     for (TriangleRef r : node.refs) {
       // inters++;
       Intersection i = seg.intersectTriangle(r.triangle);
       if (i != null) {
         if (min == null || min.getDistance() > i.getDistance()) {
           min = i;
         }
       }
     }
     // interTime += System.nanoTime()-begin;
     return min == null ? null : min.getIntersection();
   } else {
     // recursion++;
     boolean splitAtX = node.splittingPlane.dimension == Dimension.X;
     double splitValue = node.splittingPlane.splitValue;
     return seg.splitAtXorY(splitAtX, splitValue)
         .map(
             (startOnLeftSide, start, end) -> {
               if (startOnLeftSide) {
                 Point2D p = intersect(node.left, start);
                 return p != null || end == null ? p : intersect(node.right, end);
               } else {
                 Point2D p = intersect(node.right, start);
                 return p != null || end == null ? p : intersect(node.left, end);
               }
             });
   }
 }
Пример #2
0
 public void visitHalfPlanes(BiConsumer<Segment, Integer> visitor, KDNode node, int depth) {
   if (node.isLeaf()) { // || depth > 8) {
     return;
   } else {
     if (node.splittingPlane.dimension == Dimension.X) {
       Point2D start = new Point2D(node.splittingPlane.splitValue, node.bounds.min.getY());
       Point2D end = new Point2D(node.splittingPlane.splitValue, node.bounds.max.getY());
       visitor.accept(new Segment(start, end), depth);
       visitHalfPlanes(visitor, node.left, depth + 1);
       // visitHalfPlanes(visitor, node.right, depth+1);
     } else {
       Point2D start = new Point2D(node.bounds.min.getX(), node.splittingPlane.splitValue);
       Point2D end = new Point2D(node.bounds.max.getX(), node.splittingPlane.splitValue);
       visitor.accept(new Segment(start, end), depth);
       visitHalfPlanes(visitor, node.left, depth + 1);
       // visitHalfPlanes(visitor, node.right, depth+1);
     }
   }
 }
Пример #3
0
  private static KDNode buildTreeSAH(
      TriangleRef[] refs, BoundingRectangle bounds, int depth, double lastCost) {
    double minCost = Double.MAX_VALUE;
    SplittingPlane minPlane = null;
    SplittingPlaneAffiliation minPlaneAffiliation = null;
    int minnleft = Integer.MAX_VALUE;
    int minnright = Integer.MAX_VALUE;

    List<TriangleEvent> events = new ArrayList<>(refs.length * 2);

    for (Dimension d : Dimension.values()) {
      for (TriangleRef ref : refs) {
        BoundingRectangle br = bounds.intersect(ref.bounds);
        if (d.getValue(br.min) == d.getValue(br.max)) {
          // The triangle is planar after clipping it to the current Voxel
          events.add(
              new TriangleEvent(
                  ref.triangle, new SplittingPlane(d.getValue(br.min), d), EventType.PLANAR));
        } else {
          events.add(
              new TriangleEvent(
                  ref.triangle, new SplittingPlane(d.getValue(br.min), d), EventType.START));
          events.add(
              new TriangleEvent(
                  ref.triangle, new SplittingPlane(d.getValue(br.max), d), EventType.END));
        }
      }

      events.sort(
          (a, b) -> {
            int i1 = (int) Math.signum(a.p.splitValue - b.p.splitValue);
            int i2 = (int) Math.signum(a.type.ord - b.type.ord);
            return i1 != 0 ? i1 : i2;
          }); // As per the order in the paper: Sort by splitValue, then by EventType: END < PLANAR
              // < START

      // Now we sweep over the event list and find the minimum cost splitting plane for this
      // dimension.
      int nleft = 0;
      int nright = refs.length;

      for (int i = 0; i < events.size(); ) {
        TriangleEvent evt = events.get(i);
        int pend = 0;
        int pplanar = 0;
        int pstart = 0;
        final SplittingPlane p = evt.p;

        // aggregate all p* for the same splitValue
        while (evt.p.splitValue == p.splitValue) {
          switch (evt.type) {
            case END:
              pend++;
              break;
            case PLANAR:
              pplanar++;
              break;
            case START:
              pstart++;
              break;
          }

          if (++i < events.size()) {
            evt = events.get(i);
          } else {
            break;
          }
        }

        // Now we got all p* values for the current splitValue.
        // Move plane *onto* p.
        nright -= pplanar;
        nright -= pend;

        Tuple2<Double, SplittingPlaneAffiliation> result =
            surfaceAreaHeuristic(bounds, p, nleft, nright, pplanar);
        if (result.v1 < minCost) {
          minCost = result.v1;
          minPlane = p;
          minPlaneAffiliation = result.v2;
          minnleft = nleft;
          minnright = nright;
          switch (minPlaneAffiliation) {
            case LEFT:
              minnleft += pplanar;
              break;
            case RIGHT:
              minnright += pplanar;
              break;
          }
        }

        // Move plane *beyond* p
        nleft += pstart;
        nleft += pplanar;

        assert nleft + nright >= refs.length;
        assert minnleft + minnright >= refs.length;
      }

      events.clear();
    }

    if (INTERSECTION_COST * refs.length < minCost || depth > 30 || lastCost <= minCost) {
      return KDNode.leaf(bounds, refs);
    }

    assert refs.length > 1;

    // We found the best splitting plane and the associated costs, all that is left to do is split.
    TriangleRef[] left = new TriangleRef[minnleft];
    TriangleRef[] right = new TriangleRef[minnright];
    int l = 0;
    int r = 0;
    BoundingRectangle lv = BoundingRectangle.EMPTY;
    BoundingRectangle rv = BoundingRectangle.EMPTY;
    for (TriangleRef ref : refs) {
      if (minPlane.dimension.getValue(ref.bounds.min) == minPlane.splitValue
          && minPlane.dimension.getValue(ref.bounds.max) == minPlane.splitValue) {
        if (minPlaneAffiliation == SplittingPlaneAffiliation.RIGHT) {
          right[r++] = ref;
          rv = rv.merge(ref.bounds);
        } else {
          left[l++] = ref;
          lv = lv.merge(ref.bounds);
        }
      } else {
        if (minPlane.dimension.getValue(ref.bounds.min) < minPlane.splitValue) {
          left[l++] = ref;
          lv = lv.merge(ref.bounds);
        }
        if (minPlane.dimension.getValue(ref.bounds.max) > minPlane.splitValue) {
          right[r++] = ref;
          rv = rv.merge(ref.bounds);
        }
        // In the remaining case where min == max == splitValue, the triangle
        // is planar (extent == 0) and will we dealt with in the other if branch
        // according to minPlaneAffiliation.
      }
    }

    lv =
        splitBoundingRect(lv.intersect(bounds), minPlane)
            .v1; // I hope this is accurate enough. don't want clip all triangles
    rv = splitBoundingRect(rv.intersect(bounds), minPlane).v2;

    assert l == left.length;
    assert r == right.length;

    KDNode leftChild = buildTreeSAH(left, lv, depth + 1, minCost);
    KDNode rightChild = buildTreeSAH(right, rv, depth + 1, minCost);

    return KDNode.inner(bounds, leftChild, rightChild, minPlane);
  }