From 2a6bb6c6e0c7d5019e484ab4393941b8801d63ea Mon Sep 17 00:00:00 2001
From: Tavian Barnes <tavianator@gmail.com>
Date: Tue, 6 Apr 2010 23:47:59 -0400
Subject: Implement CSG differences in libdimension.

---
 libdimension/csg.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 139 insertions(+), 1 deletion(-)

(limited to 'libdimension')

diff --git a/libdimension/csg.c b/libdimension/csg.c
index 8ba99d4..78df625 100644
--- a/libdimension/csg.c
+++ b/libdimension/csg.c
@@ -281,6 +281,145 @@ dmnsn_new_csg_intersection(dmnsn_object *A, dmnsn_object *B)
   return NULL;
 }
 
+/* Differences */
+
+static dmnsn_intersection *
+dmnsn_csg_difference_intersection_fn(const dmnsn_object *csg, dmnsn_line line)
+{
+  const dmnsn_object **params = csg->ptr;
+
+  dmnsn_line line1 = dmnsn_matrix_line_mul(params[0]->trans_inv, line);
+  dmnsn_line line2 = dmnsn_matrix_line_mul(params[1]->trans_inv, line);
+  dmnsn_intersection *i1 = (*params[0]->intersection_fn)(params[0], line1);
+  dmnsn_intersection *i2 = (*params[1]->intersection_fn)(params[1], line2);
+
+  double oldt = 0.0;
+  while (i1) {
+    i1->ray = line;
+    i1->t += oldt;
+    oldt = i1->t;
+    i1->normal = dmnsn_vector_normalize(
+      dmnsn_vector_sub(
+        dmnsn_matrix_vector_mul(params[0]->trans, i1->normal),
+        dmnsn_matrix_vector_mul(params[0]->trans, dmnsn_zero)
+      )
+    );
+
+    if (!i1->texture)
+      i1->texture = csg->texture;
+    if (!i1->interior)
+      i1->interior = csg->interior;
+
+    dmnsn_vector point = dmnsn_line_point(i1->ray, i1->t);
+    point = dmnsn_matrix_vector_mul(params[1]->trans_inv, point);
+    if ((*params[1]->inside_fn)(params[1], point)) {
+      line1.x0 = dmnsn_line_point(line1, i1->t);
+      line1 = dmnsn_line_add_epsilon(line1);
+      dmnsn_delete_intersection(i1);
+      i1 = (*params[0]->intersection_fn)(params[0], line1);
+    } else {
+      break;
+    }
+  }
+
+  oldt = 0.0;
+  while (i2) {
+    i2->ray = line;
+    i2->t += oldt;
+    oldt = i2->t;
+    i2->normal = dmnsn_vector_normalize(
+      dmnsn_vector_sub(
+        dmnsn_matrix_vector_mul(params[1]->trans, i2->normal),
+        dmnsn_matrix_vector_mul(params[1]->trans, dmnsn_zero)
+      )
+    );
+
+    if (!i2->texture)
+      i2->texture = csg->texture;
+    if (!i2->interior)
+      i2->interior = csg->interior;
+
+    dmnsn_vector point = dmnsn_line_point(i2->ray, i2->t);
+    point = dmnsn_matrix_vector_mul(params[0]->trans_inv, point);
+    if (!(*params[0]->inside_fn)(params[0], point)) {
+      line2.x0 = dmnsn_line_point(line2, i2->t);
+      line2 = dmnsn_line_add_epsilon(line2);
+      dmnsn_delete_intersection(i2);
+      i2 = (*params[1]->intersection_fn)(params[1], line2);
+    } else {
+      break;
+    }
+  }
+
+  if (!i1)
+    return i2;
+  if (!i2)
+    return i1;
+
+  if (i1->t < i2->t) {
+    dmnsn_delete_intersection(i2);
+    return i1;
+  } else {
+    dmnsn_delete_intersection(i1);
+    return i2;
+  }
+}
+
+static bool
+dmnsn_csg_difference_inside_fn(const dmnsn_object *csg, dmnsn_vector point)
+{
+  dmnsn_object **params = csg->ptr;
+  return (*params[0]->inside_fn)(params[0], point)
+     && !(*params[1]->inside_fn)(params[1], point);
+}
+
+dmnsn_object *
+dmnsn_new_csg_difference(dmnsn_object *A, dmnsn_object *B)
+{
+  if (A && B) {
+    A->trans_inv = dmnsn_matrix_inverse(A->trans);
+    B->trans_inv = dmnsn_matrix_inverse(B->trans);
+
+    dmnsn_object *csg = dmnsn_new_object();
+    if (csg) {
+      dmnsn_object **params = malloc(2*sizeof(dmnsn_object *));
+      if (!params) {
+        dmnsn_delete_object(csg);
+        dmnsn_delete_object(B);
+        dmnsn_delete_object(A);
+        errno = ENOMEM;
+        return NULL;
+      }
+
+      params[0] = A;
+      params[1] = B;
+
+      csg->ptr             = params;
+      csg->intersection_fn = &dmnsn_csg_difference_intersection_fn;
+      csg->inside_fn       = &dmnsn_csg_difference_inside_fn;
+      csg->free_fn         = &dmnsn_csg_free_fn;
+
+      dmnsn_bounding_box Abox
+        = dmnsn_matrix_bounding_box_mul(A->trans, A->bounding_box);
+      dmnsn_bounding_box Bbox
+        = dmnsn_matrix_bounding_box_mul(B->trans, B->bounding_box);
+      csg->bounding_box.min = dmnsn_vector_min(Abox.min, Bbox.min);
+      csg->bounding_box.max = dmnsn_vector_max(Abox.max, Bbox.max);
+
+      return csg;
+    } else {
+      dmnsn_delete_object(B);
+      dmnsn_delete_object(A);
+    }
+  } else if (A) {
+    dmnsn_delete_object(B);
+  } else if (B) {
+    dmnsn_delete_object(A);
+  }
+
+  return NULL;
+}
+
 /* Merges */
 
 static dmnsn_intersection *
@@ -345,7 +484,6 @@ dmnsn_csg_merge_intersection_fn(const dmnsn_object *csg, dmnsn_line line)
       line2.x0 = dmnsn_line_point(line2, i2->t);
       line2 = dmnsn_line_add_epsilon(line2);
       dmnsn_delete_intersection(i2);
-      i2 = NULL; break;
       i2 = (*params[1]->intersection_fn)(params[1], line2);
     } else {
       break;
-- 
cgit v1.2.3