Exemplo n.º 1
0
 public TriangleRef(Triangle triangle) {
   this(triangle, BoundingRectangle.fromPoints(triangle));
   assert triangle != null;
 }
Exemplo n.º 2
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);
  }
Exemplo n.º 3
0
 private static double surfaceArea(BoundingRectangle v) {
   Point2D e = v.extent();
   return 2 * (e.getX() + e.getY());
 }