123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- /******************************************************************************
- *
- * Copyright (C) 2006-2017 by
- * The Salk Institute for Biological Studies and
- * Pittsburgh Supercomputing Center, Carnegie Mellon University
- *
- * Use of this source code is governed by an MIT-style
- * license that can be found in the LICENSE file or at
- * https://opensource.org/licenses/MIT.
- *
- ******************************************************************************/
- #ifndef SRC4_GEOMETRY_UTILS_INC_
- #define SRC4_GEOMETRY_UTILS_INC_
- /**
- * This file is directly included into diffuse_react_event.cpp.
- * The reason why this is not a standard .cpp + .h file is to gove the compiler
- * the opportunity to inline these functions into methods of diffuse&react event.
- */
- #include <vector>
- #include "logging.h"
- #include "diffuse_react_event.h"
- #include "defines.h"
- #include "world.h"
- #include "partition.h"
- #include "geometry.h"
- #include "debug_config.h"
- #include "wall_utils.inl"
- namespace MCell {
- namespace GeometryUtils {
- static Vec2 xyz2uv(const Partition& p, const Vec3& a, const Wall& w) {
- Vec2 res;
- if (w.has_initialized_grid()) {
- const Grid& g = w.grid;
- res.u = a.x * w.unit_u.x + a.y * w.unit_u.y + a.z * w.unit_u.z -
- g.vert0.u;
- res.v = a.x * w.unit_v.x + a.y * w.unit_v.y + a.z * w.unit_v.z -
- g.vert0.v;
- }
- else {
- Vec3 pos = a - p.get_wall_vertex(w, 0);
- res.u = dot(pos, w.unit_u);
- res.v = dot(pos, w.unit_v);
- }
- return res;
- }
- /***************************************************************************
- wall_bounding_box:
- In: a wall
- vector to store one corner of the bounding box for that wall
- vector to store the opposite corner
- Out: No return value. The vectors are set to define the smallest box
- that contains the wall.
- ***************************************************************************/
- static inline void get_wall_bounding_box(
- const Vec3 w_vert[VERTICES_IN_TRIANGLE],
- Vec3& llf, Vec3& urb
- ) {
- llf.x = urb.x = w_vert[0].x;
- llf.y = urb.y = w_vert[0].y;
- llf.z = urb.z = w_vert[0].z;
- if (w_vert[1].x < llf.x)
- llf.x = w_vert[1].x;
- else if (w_vert[1].x > urb.x)
- urb.x = w_vert[1].x;
- if (w_vert[2].x < llf.x)
- llf.x = w_vert[2].x;
- else if (w_vert[2].x > urb.x)
- urb.x = w_vert[2].x;
- if (w_vert[1].y < llf.y)
- llf.y = w_vert[1].y;
- else if (w_vert[1].y > urb.y)
- urb.y = w_vert[1].y;
- if (w_vert[2].y < llf.y)
- llf.y = w_vert[2].y;
- else if (w_vert[2].y > urb.y)
- urb.y = w_vert[2].y;
- if (w_vert[1].z < llf.z)
- llf.z = w_vert[1].z;
- else if (w_vert[1].z > urb.z)
- urb.z = w_vert[1].z;
- if (w_vert[2].z < llf.z)
- llf.z = w_vert[2].z;
- else if (w_vert[2].z > urb.z)
- urb.z = w_vert[2].z;
- }
- /***************************************************************************
- distribute_wall:
- In: a wall 'w' that fully fits into partition 'p'
- Out: colliding_subparts - indices of all the subpartitions in a given partition
- where the wall is located
- ***************************************************************************/
- // original name: distribute_wall
- static void wall_subparts_collision_test(
- const Partition& p, const Wall& w,
- SubpartIndicesVector& colliding_subparts
- ) {
- Vec3 llf, urb; /* Bounding box for wall */
- pos_t leeway = 1; /* Margin of error */
- Vec3 w_vert[VERTICES_IN_TRIANGLE];
- w_vert[0] = p.get_geometry_vertex(w.vertex_indices[0]);
- w_vert[1] = p.get_geometry_vertex(w.vertex_indices[1]);
- w_vert[2] = p.get_geometry_vertex(w.vertex_indices[2]);
- get_wall_bounding_box(w_vert, llf, urb);
- // min
- if (llf.x < -leeway)
- leeway = -llf.x;
- if (llf.y < -leeway)
- leeway = -llf.y;
- if (llf.z < -leeway)
- leeway = -llf.z;
- // max
- if (urb.x > leeway)
- leeway = urb.x;
- if (urb.y > leeway)
- leeway = urb.y;
- if (urb.z > leeway)
- leeway = urb.z;
- leeway = POS_EPS + leeway * POS_EPS;
- if (p.config.use_expanded_list) {
- leeway += p.config.rxn_radius_3d;
- }
- Vec3 leeway3 = Vec3(leeway);
- llf = llf - leeway3;
- urb = urb + leeway3;
- // let's assume for now that we are placing a cube with corners llf and urb,
- // fing what are the min and max parition indices
- IVec3 min_subpart_indices, max_subpart_indices;
- p.get_subpart_3d_indices(llf, min_subpart_indices);
- p.get_subpart_3d_indices(urb, max_subpart_indices);
- #if 0
- // don't know yet what is this doing, somehow it is trying to find some subvolume
- // but don't know which one
- // probably just an optimization
- // this is needed for the following code (if enabled, fix loop afterwards)
- max_subpart_indices += IVec3(1); // max is incremented by 1 in each dimension
- cent.x = 0.33333333333 * (w_vert[0].x + w_vert[1].x + w_vert[2].x);
- cent.y = 0.33333333333 * (w_vert[0].y + w_vert[1].y + w_vert[2].y);
- cent.z = 0.33333333333 * (w_vert[0].z + w_vert[1].z + w_vert[2].z);
- for (i = x_min; i < x_max; i++) {
- if (cent.x < world->x_partitions[i])
- break;
- }
- for (j = y_min; j < y_max; j++) {
- if (cent.y < world->y_partitions[j])
- break;
- }
- for (k = z_min; k < z_max; k++) {
- if (cent.z < world->z_partitions[k])
- break;
- }
- h = (k - 1) +
- (world->nz_parts - 1) * ((j - 1) + (world->ny_parts - 1) * (i - 1));
- where_am_i = localize_wall(w, world->subvol[h].local_storage);
- if (where_am_i == NULL)
- return NULL;
- #endif
- // go through all subparts in llf-urb box and select only those that cross our wall
- for (int x = min_subpart_indices.x; x <= max_subpart_indices.x; x++) {
- for (int y = min_subpart_indices.y; y <= max_subpart_indices.y; y++) {
- for (int z = min_subpart_indices.z; z <= max_subpart_indices.z; z++) {
- Vec3 subpart_llf, subpart_urb;
- subpart_index_t subpart_index = p.get_subpart_index_from_3d_indices(x, y, z);
- p.get_subpart_llf_point(subpart_index, subpart_llf);
- p.get_subpart_urb_point_from_llf(subpart_llf, subpart_urb);
- subpart_llf = subpart_llf - leeway3;
- subpart_urb = subpart_urb + leeway3;
- if (WallUtils::wall_in_box(p, w, subpart_llf, subpart_urb) != 0) {
- colliding_subparts.push_back(subpart_index);
- }
- }
- }
- }
- }
- /***************************************************************************
- find_edge_point:
- In: here: a wall
- loc: a point in the coordinate system of that wall where we are now
- (assumed to be on or inside triangle)
- disp: a 2D displacement vector to move
- edgept: a place to store the coordinate on the edge, if we hit it
- Out: index of the edge we hit (0, 1, or 2), or 3 if the new location
- is within the wall, or 4 if we can't tell. If the result is
- 0, 1, or 2, edgept is set to the new location.
- ***************************************************************************/
- static edge_index_t find_edge_point(
- const Wall& here,
- const Vec2& loc,
- const Vec2& disp,
- Vec2& edgept
- ) {
- pos_t lxd = determinant2(loc, disp);
- pos_t lxc1 = -loc.v * here.uv_vert1_u;
- pos_t dxc1 = -disp.v * here.uv_vert1_u;
- // Make sure that the displacement vector isn't on v0v1
- pos_t f, s, t;
- if (dxc1 < -POS_EPS || dxc1 > POS_EPS) {
- f = 1 / dxc1; /* f>0 is passing outwards */
- s = -lxd * f;
- if (0 < s && s < 1 && f > 0) {
- t = -lxc1 * f;
- if (POS_EPS < t && t < 1) {
- edgept = loc + Vec2(t) * disp;
- return EDGE_INDEX_0;
- }
- else if (t > 1 + POS_EPS) {
- return EDGE_INDEX_WITHIN_WALL;
- }
- /* else can't tell if we hit this edge, assume not */
- }
- }
- pos_t lxc2 = determinant2(loc, here.uv_vert2);
- pos_t dxc2 = determinant2(disp, here.uv_vert2);
- // Make sure that the displacement vector isn't on v1v2
- if (dxc2 < -POS_EPS || dxc2 > POS_EPS) {
- f = 1 / dxc2; /* f<0 is passing outwards */
- s = 1 + lxd * f;
- if (0 < s && s < 1 && f < 0) {
- t = -lxc2 * f;
- if (POS_EPS < t && t < 1) {
- edgept = loc + Vec2(t) * disp;
- return EDGE_INDEX_2;
- }
- else if (t > 1 + POS_EPS) {
- return EDGE_INDEX_WITHIN_WALL;
- }
- /* else can't tell */
- }
- }
- f = dxc2 - dxc1;
- if (f < -POS_EPS || f > POS_EPS) {
- f = 1 / f; /* f>0 is passing outwards */
- s = -(lxd + dxc1) * f;
- if (0 < s && s < 1 && f > 0) {
- t = (here.uv_vert1_u * here.uv_vert2.v + lxc1 - lxc2) * f;
- if (POS_EPS < t && t < 1) {
- edgept = loc + Vec2(t) * disp;
- return EDGE_INDEX_1;
- }
- else if (t > 1 + POS_EPS) {
- return EDGE_INDEX_WITHIN_WALL;
- }
- /* else can't tell */
- }
- }
- return EDGE_INDEX_CANNOT_TELL; /* Couldn't tell whether we hit or not--calling function should
- pick another displacement */
- }
- /***************************************************************************
- traverse_surface:
- In: here: a wall
- loc: a point in the coordinate system of that wall
- which: which edge to travel off of
- newloc: a vector to set for the new wall
- Out: NULL if the edge is not shared, or a pointer to the wall in that
- direction if it is shared. newloc is set to loc in the coordinate system
- of the new wall (after flattening the walls along their shared edge)
- ***************************************************************************/
- static wall_index_t traverse_surface(const Wall& here, const Vec2& loc, edge_index_t which_edge,
- Vec2& newloc) {
- wall_index_t there;
- const Edge& e = here.edges[which_edge];
- if (e.forward_index == here.index) {
- /* Apply forward transform to loc */
- there = e.backward_index;
- /* rotation */
- Vec2 tmp;
- tmp.u = e.get_cos_theta() * loc.u + e.get_sin_theta() * loc.v;
- tmp.v = -e.get_sin_theta() * loc.u + e.get_cos_theta() * loc.v;
- /* translation */
- newloc = tmp + e.get_translate();
- return there;
- }
- else {
- /* Apply inverse transform to loc */
- there = e.forward_index;
- /* inverse translation */
- Vec2 tmp;
- tmp = loc - e.get_translate();
- /* inverse rotation */
- newloc.u = e.get_cos_theta() * tmp.u - e.get_sin_theta() * tmp.v;
- newloc.v = e.get_sin_theta() * tmp.u + e.get_cos_theta() * tmp.v;
- return there;
- }
- return WALL_INDEX_INVALID;
- }
- /**************************************************************************
- same_side:
- In: two points p1 and p2
- line defined by the points a and b
- Out: returns 1 if points p1 and p2 are on the same side of the line
- defined by the points a and b
- **************************************************************************/
- static int same_side(const Vec3& p1, const Vec3& p2, const Vec3& a, const Vec3& b) {
- Vec3 cp1, cp2, b_a, p1_a, p2_a;
- b_a = b - a;
- p1_a = p1 - a;
- p2_a = p2 - a;
- cp1 = cross(b_a, p1_a);
- cp2 = cross(b_a, p2_a);
- if (dot(cp1, cp2) >= 0) {
- return 1;
- }
- else {
- return 0;
- }
- }
- /************************************************************************
- point_in_triangle:
- In: point p
- triangle defined by points a,b,c
- Out: returns 1 if point p is in the triangle defined by
- points (a,b,c) or lies on edges (a,b), (b,c) or (a,c).
- Note: If point p coincides with vertices (a,b,c) we consider that p
- is in the triangle.
- ************************************************************************/
- static bool point_in_triangle(const Vec3& p, const Vec3& a, const Vec3& b, const Vec3& c) {
- if (same_side(p, a, b, c) && same_side(p, b, a, c) && same_side(p, c, a, b)) {
- return 1;
- }
- if (((!distinguishable_p(p.x, a.x, POS_EPS)) &&
- (!distinguishable_p(p.y, a.y, POS_EPS)) &&
- (!distinguishable_p(p.z, a.z, POS_EPS))) ||
- ((!distinguishable_p(p.x, b.x, POS_EPS)) &&
- (!distinguishable_p(p.y, b.y, POS_EPS)) &&
- (!distinguishable_p(p.z, b.z, POS_EPS))) ||
- ((!distinguishable_p(p.x, c.x, POS_EPS)) &&
- (!distinguishable_p(p.y, c.y, POS_EPS)) &&
- (!distinguishable_p(p.z, c.z, POS_EPS)))) {
- return 1;
- }
- return 0;
- }
- /*******************************************************************
- cross2D:
- In: 2D vectors a and b
- Out: 2D pseudo cross product Dot(Perp(a0,b)
- Note: The code adapted from "Real-Time Collision Detection" by
- Christer Ericson, p.205
- *******************************************************************/
- static pos_t cross2D(const Vec2& a, const Vec2& b) {
- return ((a.v) * (b.u) - (a.u) * (b.v));
- }
- /*********************************************************************
- point_in_triangle_2D:
- In: point p
- triangle defined by vertices a, b, c
- Out: Returns 1 if point p is inside the above defined triangle,
- and 0 otherwise.
- Note: The code adapted from "Real-Time Collision Detection" by
- Christer Ericson, p.206
- ***********************************************************************/
- static bool point_in_triangle_2D(const Vec2& p, const Vec2& a,
- const Vec2& b, const Vec2& c) {
- pos_t pab, pbc, pca;
- pab = cross2D(p - a, b - a);
- pbc = cross2D(p - b, c - b);
- /* if P left of one of AB and BC and right of the other, not inside triangle
- - (pab and pbc have different signs */
- if (((pab > 0) && (pbc < 0)) || ((pab < 0) && (pbc > 0))) {
- return false;
- }
- pca = cross2D(p - c, a - c);
- /* if P left of one of AB and CA and right of the other, not inside triangle
- * - pab and pca have different signs */
- if (((pab > 0) && (pca < 0)) || ((pab < 0) && (pca > 0))) {
- return false;
- }
- /* if P left or right of all edges, so must be in (or on) the triangle */
- return true;
- }
- /***************************************************************************
- closest_pt_point_triangle:
- In: p - point
- a,b,c - vectors defining the vertices of the triangle.
- Out: final_result - closest point on triangle ABC to a point p.
- The code is adapted from "Real-time Collision Detection" by Christer
- Ericson, ISBN 1-55860-732-3, p.141.
- ***************************************************************************/
- static void closest_pt_point_triangle(
- const Vec3& p, const Vec3& a,
- const Vec3& b, const Vec3& c,
- Vec3& final_result
- ) {
- /* Check if P in vertex region outside A */
- Vec3 ab, ac, ap;
- ab = b - a;
- ac = c - a;
- ap = p - a;
- pos_t d1, d2;
- d1 = dot(ab, ap);
- d2 = dot(ac, ap);
- if (d1 <= 0 && d2 <= 0) {
- final_result = a;
- return;
- }
- /* Check if P in vertex region outside B */
- Vec3 bp = p - b;
- pos_t d3, d4;
- d3 = dot(ab, bp);
- d4 = dot(ac, bp);
- if (d3 >= 0 && d4 <= d3) {
- final_result = b;
- return;
- }
- /* Check if P in edge region of AB, if so return projection of P onto AB */
- pos_t v, w;
- Vec3 result1;
- pos_t vc = d1 * d4 - d3 * d2;
- if (vc <= 0 && d1 >= 0 && d3 <= 0) {
- v = d1 / (d1 - d3);
- result1 = ab * Vec3(v);
- final_result = a + result1;
- return; /* barycentric coordinates (1-v,v,0) */
- }
- /* Check if P in vertex region outside C */
- Vec3 cp = p - c;
- pos_t d5, d6;
- d5 = dot(ab, cp);
- d6 = dot(ac, cp);
- if (d6 >= 0 && d5 <= d6) {
- final_result = c;
- return;
- }
- /* Check if P in edge region of AC, if so return projection of P onto AC */
- pos_t vb = d5 * d2 - d1 * d6;
- if (vb <= 0 && d2 >= 0 && d6 <= 0) {
- w = d2 / (d2 - d6);
- result1 = ac * Vec3(w);
- final_result = a + result1;
- return; /* barycentric coordinates (0, 1-w,w) */
- }
- /* Check if P in edge region of BC, if so return projection of P onto BC */
- pos_t va = d3 * d6 - d5 * d4;
- if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) {
- w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
- result1 = (c - b) * Vec3(w);
- final_result = b + result1;
- return; /*barycentric coordinates (0,1-w, w) */
- }
- /* P inside face region. Compute Q through its barycentric
- coordinates (u,v,w) */
- pos_t denom = 1 / (va + vb + vc);
- v = vb * denom;
- w = vc * denom;
- ab = ab * Vec3(v);
- ac = ac * Vec3(w);
- result1 = ab + ac;
- final_result = a + result1;
- return; /* = u*a + v*b + w*c, u = va * denom = 1 - v -w */
- }
- /***************************************************************************
- closest_interior_point:
- In: a point in 3D
- a wall
- the surface coordinates of the closest interior point on the wall
- how far away the point can be before we give up
- Out: return the distance^2 between the input point and closest point.
- Sets closest interior point.
- Note: the search distance currently isn't used. This function is just
- a wrapper for closest_pt_point_triangle. If the closest point is
- on an edge or corner, we scoot the point towards the centroid of
- the triangle so we're contained fully within the triangle.
- ***************************************************************************/
- static pos_t closest_interior_point(
- const Partition& p,
- const Vec3& pt,
- const Wall& w,
- Vec2& ip) {
- #ifdef DEBUG_CLOSEST_INTERIOR_POINT
- std::cout << "closest_interior_point: " << pt << "\n";;
- w.dump(p, "", true);
- #endif
- Vec3 v;
- const Vec3& w_vert0 = p.get_wall_vertex(w, 0);
- const Vec3& w_vert1 = p.get_wall_vertex(w, 1);
- const Vec3& w_vert2 = p.get_wall_vertex(w, 2);
- closest_pt_point_triangle(pt, w_vert0, w_vert1, w_vert2, v);
- ip = xyz2uv(p, v, w);
- /* Check to see if we're lying on an edge; if so, scoot towards centroid. */
- /* ip lies on edge of wall if cross products are zero */
- int give_up_ctr = 0;
- int give_up = 10;
- pos_t a1 = ip.u * w.uv_vert2.v - ip.v * w.uv_vert2.u;
- pos_t a2 = w.uv_vert1_u * ip.v;
- Vec2 vert_0(0);
- Vec2 vert_1(w.uv_vert1_u, 0);
- while (
- give_up_ctr < give_up
- && (!distinguishable_p(ip.v, 0, POS_EPS) ||
- !distinguishable_p(a1, 0, POS_EPS) ||
- !distinguishable_p(a1 + a2, 2 * w.area, POS_EPS) ||
- !point_in_triangle_2D(ip, vert_0, vert_1, w.uv_vert2))
- ) {
- /* Move toward centroid. It's possible for this movement to be so small
- * that we are essentially stuck in this loop, so bail out after a set
- * number of tries. The number chosen is somewhat arbitrary. In most cases,
- * one try is sufficent. */
- ip.u = (1 - 5 * POS_EPS) * ip.u +
- 5 * POS_EPS * 0.333333333333333 * (w.uv_vert1_u + w.uv_vert2.u);
- ip.v = (1 - 5 * POS_EPS) * ip.v +
- 5 * POS_EPS * 0.333333333333333 * w.uv_vert2.v;
- a1 = ip.u * w.uv_vert2.v - ip.v * w.uv_vert2.u;
- a2 = w.uv_vert1_u * ip.v;
- give_up_ctr++;
- }
- pos_t res = len3_squared(v - pt);
- #ifdef DEBUG_CLOSEST_INTERIOR_POINT
- std::cout << "res: " << res << ", ip: " << ip << "\n";
- #endif
- return res;
- }
- static bool is_point_above_plane_defined_by_wall(const Partition& p, const Wall& w, const Vec3& pos) {
- // make vector pointing from any point to our position
- Vec3 w0_pos = pos - p.get_wall_vertex(w, 0);
- // dot product with normal gives ||a|| * ||b|| * cos(phi)
- pos_t dot_prod = dot(w0_pos, w.normal);
- assert(!cmp_eq(dot_prod, (pos_t)0.0, POS_EPS) && "Checked point is on the plane");
- return dot_prod > 0;
- }
- } // namespace GeometryUtil
- } // namespace MCell
- #endif // SRC4_GEOMETRY_UTILS_INC_
|