/* ---------------------------------------------------------------------- LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator https://www.lammps.org/, Sandia National Laboratories LAMMPS development team: developers@lammps.org Copyright (2003) Sandia Corporation. Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains certain rights in this software. This software is distributed under the GNU General Public License. See the README file in the top-level LAMMPS directory. ------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- Contributing author: Pieter in 't Veld (SNL) ------------------------------------------------------------------------- */ #include "region_prism.h" #include "domain.h" #include "error.h" #include "input.h" #include "math_extra.h" #include "update.h" #include "variable.h" #include using namespace LAMMPS_NS; static constexpr double BIG = 1.0e20; /* ---------------------------------------------------------------------- */ RegPrism::RegPrism(LAMMPS *lmp, int narg, char **arg) : Region(lmp, narg, arg), xlostr(nullptr), ylostr(nullptr), zlostr(nullptr), xhistr(nullptr), yhistr(nullptr), zhistr(nullptr), xystr(nullptr), xzstr(nullptr), yzstr(nullptr) { options(narg - 11, &arg[11]); xlostyle = CONSTANT; if (strcmp(arg[2], "INF") == 0 || strcmp(arg[2], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[2], "INF") == 0) xlo = -BIG; else xlo = domain->boxlo[0]; } else if (utils::strmatch(arg[2], "^v_")) { xlostr = utils::strdup(arg[2] + 2); xlo = 0.0; xlostyle = VARIABLE; varshape = 1; } else xlo = xscale * utils::numeric(FLERR, arg[2], false, lmp); xhistyle = CONSTANT; if (strcmp(arg[3], "INF") == 0 || strcmp(arg[3], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[3], "INF") == 0) xhi = BIG; else xhi = domain->boxhi[0]; } else if (utils::strmatch(arg[3], "^v_")) { xhistr = utils::strdup(arg[3] + 2); xhi = 0.0; xhistyle = VARIABLE; varshape = 1; } else xhi = xscale * utils::numeric(FLERR, arg[3], false, lmp); ylostyle = CONSTANT; if (strcmp(arg[4], "INF") == 0 || strcmp(arg[4], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[4], "INF") == 0) ylo = -BIG; else ylo = domain->boxlo[1]; } else if (utils::strmatch(arg[4], "^v_")) { ylostr = utils::strdup(arg[4] + 2); ylo = 0.0; ylostyle = VARIABLE; varshape = 1; } else ylo = yscale * utils::numeric(FLERR, arg[4], false, lmp); yhistyle = CONSTANT; if (strcmp(arg[5], "INF") == 0 || strcmp(arg[5], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[5], "INF") == 0) yhi = BIG; else yhi = domain->boxhi[1]; } else if (utils::strmatch(arg[5], "^v_")) { yhistr = utils::strdup(arg[5] + 2); yhi = 0.0; yhistyle = VARIABLE; varshape = 1; } else yhi = yscale * utils::numeric(FLERR, arg[5], false, lmp); zlostyle = CONSTANT; if (strcmp(arg[6], "INF") == 0 || strcmp(arg[6], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[6], "INF") == 0) zlo = -BIG; else zlo = domain->boxlo[2]; } else if (utils::strmatch(arg[6], "^v_")) { zlostr = utils::strdup(arg[6] + 2); zlo = 0.0; zlostyle = VARIABLE; varshape = 1; } else zlo = zscale * utils::numeric(FLERR, arg[6], false, lmp); zhistyle = CONSTANT; if (strcmp(arg[7], "INF") == 0 || strcmp(arg[7], "EDGE") == 0) { if (domain->box_exist == 0) error->all(FLERR, "Cannot use region INF or EDGE when box does not exist"); if (strcmp(arg[7], "INF") == 0) zhi = BIG; else zhi = domain->boxhi[2]; } else if (utils::strmatch(arg[7], "^v_")) { zhistr = utils::strdup(arg[7] + 2); zhi = 0.0; zhistyle = VARIABLE; varshape = 1; } else zhi = zscale * utils::numeric(FLERR, arg[7], false, lmp); if (utils::strmatch(arg[8], "^v_")) { xystr = utils::strdup(arg[8] + 2); xy = 0.0; xystyle = VARIABLE; varshape = 1; } else { xy = xscale * utils::numeric(FLERR, arg[8], false, lmp); xystyle = CONSTANT; } if (utils::strmatch(arg[9], "^v_")) { xzstr = utils::strdup(arg[9] + 2); xz = 0.0; xzstyle = VARIABLE; varshape = 1; } else { xz = xscale * utils::numeric(FLERR, arg[9], false, lmp); xzstyle = CONSTANT; } if (utils::strmatch(arg[10], "^v_")) { yzstr = utils::strdup(arg[10] + 2); yz = 0.0; yzstyle = VARIABLE; varshape = 1; } else { yz = yscale * utils::numeric(FLERR, arg[10], false, lmp); yzstyle = CONSTANT; } if (varshape) { variable_check(); RegPrism::shape_update(); } // error check // prism cannot be 0 thickness in any dim, else inverse blows up // non-zero tilt values cannot be used if either dim is INF on both ends if (xlo >= xhi) error->all(FLERR, "Illegal region prism xlo: {} >= xhi: {}", xlo, xhi); if (ylo >= yhi) error->all(FLERR, "Illegal region prism ylo: {} >= yhi: {}", ylo, yhi); if (zlo >= zhi) error->all(FLERR, "Illegal region prism zlo: {} >= zhi: {}", zlo, zhi); if (xy != 0.0 && xlo == -BIG && xhi == BIG) error->all(FLERR, "Illegal region prism non-zero xy tilt with infinite x size"); if (xy != 0.0 && ylo == -BIG && yhi == BIG) error->all(FLERR, "Illegal region prism non-zero xy tilt with infinite y size"); if (xz != 0.0 && xlo == -BIG && xhi == BIG) error->all(FLERR, "Illegal region prism non-zero xz tilt with infinite x size"); if (xz != 0.0 && zlo == -BIG && zhi == BIG) error->all(FLERR, "Illegal region prism non-zero xz tilt with infinite z size"); if (yz != 0.0 && ylo == -BIG && yhi == BIG) error->all(FLERR, "Illegal region prism non-zero yz tilt with infinite y size"); if (yz != 0.0 && zlo == -BIG && zhi == BIG) error->all(FLERR, "Illegal region prism non-zero yz tilt with infinite z size"); // extent of prism if (interior) { bboxflag = 1; extent_xlo = MIN(xlo, xlo + xy); extent_xlo = MIN(extent_xlo, extent_xlo + xz); extent_ylo = MIN(ylo, ylo + yz); extent_zlo = zlo; extent_xhi = MAX(xhi, xhi + xy); extent_xhi = MAX(extent_xhi, extent_xhi + xz); extent_yhi = MAX(yhi, yhi + yz); extent_zhi = zhi; } else bboxflag = 0; // particle could be close to all 6 planes // particle can only touch 3 planes cmax = 6; contact = new Contact[cmax]; if (interior) tmax = 3; else tmax = 1; // h = transformation matrix from tilt coords (0-1) to box coords (xyz) // columns of h are edge vectors of tilted box // hinv = transformation matrix from box coords to tilt coords // both h and hinv are upper triangular // since 1st edge of prism is along x-axis // and bottom face of prism is in xy plane h[0][0] = xhi - xlo; h[0][1] = xy; h[0][2] = xz; h[1][1] = yhi - ylo; h[1][2] = yz; h[2][2] = zhi - zlo; hinv[0][0] = 1.0 / h[0][0]; hinv[0][1] = -h[0][1] / (h[0][0] * h[1][1]); hinv[0][2] = (h[0][1] * h[1][2] - h[0][2] * h[1][1]) / (h[0][0] * h[1][1] * h[2][2]); hinv[1][1] = 1.0 / h[1][1]; hinv[1][2] = -h[1][2] / (h[1][1] * h[2][2]); hinv[2][2] = 1.0 / h[2][2]; // corners = 8 corner points of prism // order = x varies fastest, then y, finally z // clo/chi = lo and hi corner pts of prism a[0] = xhi - xlo; a[1] = 0.0; a[2] = 0.0; b[0] = xy; b[1] = yhi - ylo; b[2] = 0.0; c[0] = xz; c[1] = yz; c[2] = zhi - zlo; clo[0] = corners[0][0] = xlo; clo[1] = corners[0][1] = ylo; clo[2] = corners[0][2] = zlo; corners[1][0] = xlo + a[0]; corners[1][1] = ylo + a[1]; corners[1][2] = zlo + a[2]; corners[2][0] = xlo + b[0]; corners[2][1] = ylo + b[1]; corners[2][2] = zlo + b[2]; corners[3][0] = xlo + a[0] + b[0]; corners[3][1] = ylo + a[1] + b[1]; corners[3][2] = zlo + a[2] + b[2]; corners[4][0] = xlo + c[0]; corners[4][1] = ylo + c[1]; corners[4][2] = zlo + c[2]; corners[5][0] = xlo + a[0] + c[0]; corners[5][1] = ylo + a[1] + c[1]; corners[5][2] = zlo + a[2] + c[2]; corners[6][0] = xlo + b[0] + c[0]; corners[6][1] = ylo + b[1] + c[1]; corners[6][2] = zlo + b[2] + c[2]; chi[0] = corners[7][0] = xlo + a[0] + b[0] + c[0]; chi[1] = corners[7][1] = ylo + a[1] + b[1] + c[1]; chi[2] = corners[7][2] = zlo + a[2] + b[2] + c[2]; // face = 6 inward-facing unit normals to prism faces // order = xy plane, xz plane, yz plane MathExtra::cross3(a, b, face[0]); MathExtra::cross3(b, a, face[1]); MathExtra::cross3(c, a, face[2]); MathExtra::cross3(a, c, face[3]); MathExtra::cross3(b, c, face[4]); MathExtra::cross3(c, b, face[5]); // remap open face indices to be consistent if (openflag) { int temp[6]; for (int i = 0; i < 6; i++) temp[i] = open_faces[i]; open_faces[0] = temp[4]; open_faces[1] = temp[5]; open_faces[2] = temp[2]; open_faces[3] = temp[3]; open_faces[4] = temp[0]; open_faces[5] = temp[1]; } for (int i = 0; i < 6; i++) MathExtra::norm3(face[i]); // tri = 3 vertices (0-7) in each of 12 triangles on 6 faces // verts in each tri are ordered so that right-hand rule gives inward norm // order = xy plane, xz plane, yz plane tri[0][0] = 0; tri[0][1] = 1; tri[0][2] = 3; tri[1][0] = 0; tri[1][1] = 3; tri[1][2] = 2; tri[2][0] = 4; tri[2][1] = 7; tri[2][2] = 5; tri[3][0] = 4; tri[3][1] = 6; tri[3][2] = 7; tri[4][0] = 0; tri[4][1] = 4; tri[4][2] = 5; tri[5][0] = 0; tri[5][1] = 5; tri[5][2] = 1; tri[6][0] = 2; tri[6][1] = 7; tri[6][2] = 6; tri[7][0] = 2; tri[7][1] = 3; tri[7][2] = 7; tri[8][0] = 2; tri[8][1] = 6; tri[8][2] = 4; tri[9][0] = 2; tri[9][1] = 4; tri[9][2] = 0; tri[10][0] = 1; tri[10][1] = 5; tri[10][2] = 7; tri[11][0] = 1; tri[11][1] = 7; tri[11][2] = 3; } /* ---------------------------------------------------------------------- */ RegPrism::~RegPrism() { delete[] xlostr; delete[] ylostr; delete[] zlostr; delete[] xhistr; delete[] yhistr; delete[] zhistr; delete[] xystr; delete[] xzstr; delete[] yzstr; delete[] contact; } /* ---------------------------------------------------------------------- */ void RegPrism::init() { Region::init(); if (varshape) variable_check(); } /* ---------------------------------------------------------------------- inside = 1 if x,y,z is inside or on surface inside = 0 if x,y,z is outside and not on surface abc = Hinv * (xyz - xyz/lo) abc = tilt coords (0-1) Hinv = transformation matrix from box coords to tilt coords xyz = box coords xyz/lo = lower-left corner of prism ------------------------------------------------------------------------- */ int RegPrism::inside(double x, double y, double z) { double a = hinv[0][0] * (x - xlo) + hinv[0][1] * (y - ylo) + hinv[0][2] * (z - zlo); double b = hinv[1][1] * (y - ylo) + hinv[1][2] * (z - zlo); double c = hinv[2][2] * (z - zlo); if (a >= 0.0 && a <= 1.0 && b >= 0.0 && b <= 1.0 && c >= 0.0 && c <= 1.0) return 1; return 0; } /* ---------------------------------------------------------------------- contact if 0 <= x < cutoff from one or more inner surfaces of prism can be one contact for each of 6 faces no contact if outside (possible if called from union/intersect) delxyz = vector from nearest point on prism to x ------------------------------------------------------------------------- */ int RegPrism::surface_interior(double *x, double cutoff) { int i; double dot; double *corner; // x is exterior to prism for (i = 0; i < 6; i++) { if (i % 2) corner = chi; else corner = clo; dot = (x[0] - corner[0]) * face[i][0] + (x[1] - corner[1]) * face[i][1] + (x[2] - corner[2]) * face[i][2]; if (dot < 0.0) return 0; } // x is interior to prism or on its surface int n = 0; for (i = 0; i < 6; i++) { if (open_faces[i]) continue; if (i % 2) corner = chi; else corner = clo; dot = (x[0] - corner[0]) * face[i][0] + (x[1] - corner[1]) * face[i][1] + (x[2] - corner[2]) * face[i][2]; if (dot < cutoff) { contact[n].r = dot; contact[n].delx = dot * face[i][0]; contact[n].dely = dot * face[i][1]; contact[n].delz = dot * face[i][2]; contact[n].radius = 0; contact[n].iwall = i; n++; } } return n; } /* ---------------------------------------------------------------------- one contact if 0 <= x < cutoff from outer surface of prism no contact if inside (possible if called from union/intersect) delxyz = vector from nearest point on prism to x ------------------------------------------------------------------------- */ int RegPrism::surface_exterior(double *x, double cutoff) { int i; double dot; double *corner; double xp, yp, zp; // x is far enough from prism that there is no contact for (i = 0; i < 6; i++) { if (i % 2) corner = chi; else corner = clo; dot = (x[0] - corner[0]) * face[i][0] + (x[1] - corner[1]) * face[i][1] + (x[2] - corner[2]) * face[i][2]; if (dot <= -cutoff) return 0; } // x is interior to prism for (i = 0; i < 6; i++) { if (i % 2) corner = chi; else corner = clo; dot = (x[0] - corner[0]) * face[i][0] + (x[1] - corner[1]) * face[i][1] + (x[2] - corner[2]) * face[i][2]; if (dot <= 0.0) break; } if (i == 6) return 0; // x is exterior to prism or on its surface // xp,yp,zp = point on surface of prism that x is closest to // could be edge or corner pt of prism // do not add contact point if r >= cutoff find_nearest(x, xp, yp, zp); add_contact(0, x, xp, yp, zp); contact[0].radius = 0; contact[0].iwall = 0; if (contact[0].r < cutoff) return 1; return 0; } /* ---------------------------------------------------------------------- change region shape via variable evaluation ------------------------------------------------------------------------- */ void RegPrism::shape_update() { if (xlostyle == VARIABLE) xlo = xscale * input->variable->compute_equal(xlovar); if (xhistyle == VARIABLE) xhi = xscale * input->variable->compute_equal(xhivar); if (ylostyle == VARIABLE) ylo = yscale * input->variable->compute_equal(ylovar); if (yhistyle == VARIABLE) yhi = yscale * input->variable->compute_equal(yhivar); if (zlostyle == VARIABLE) zlo = zscale * input->variable->compute_equal(zlovar); if (zhistyle == VARIABLE) zhi = zscale * input->variable->compute_equal(zhivar); if (xystyle == VARIABLE) xy = xscale * input->variable->compute_equal(xyvar); if (xzstyle == VARIABLE) xz = xscale * input->variable->compute_equal(xzvar); if (yzstyle == VARIABLE) yz = yscale * input->variable->compute_equal(yzvar); // error check // prism cannot be 0 thickness in any dim, else inverse blows up // non-zero tilt values cannot be used if either dim is INF on both ends if (xlo >= xhi) error->all(FLERR, "Illegal region prism xlo: {} >= xhi: {}", xlo, xhi); if (ylo >= yhi) error->all(FLERR, "Illegal region prism ylo: {} >= yhi: {}", ylo, yhi); if (zlo >= zhi) error->all(FLERR, "Illegal region prism zlo: {} >= zhi: {}", zlo, zhi); if (xy != 0.0 && xlo == -BIG && xhi == BIG) error->all(FLERR, "Illegal region prism non-zero xy tilt with infinite x size"); if (xy != 0.0 && ylo == -BIG && yhi == BIG) error->all(FLERR, "Illegal region prism non-zero xy tilt with infinite y size"); if (xz != 0.0 && xlo == -BIG && xhi == BIG) error->all(FLERR, "Illegal region prism non-zero xz tilt with infinite x size"); if (xz != 0.0 && zlo == -BIG && zhi == BIG) error->all(FLERR, "Illegal region prism non-zero xz tilt with infinite z size"); if (yz != 0.0 && ylo == -BIG && yhi == BIG) error->all(FLERR, "Illegal region prism non-zero yz tilt with infinite y size"); if (yz != 0.0 && zlo == -BIG && zhi == BIG) error->all(FLERR, "Illegal region prism non-zero yz tilt with infinite z size"); // extent of prism if (interior) { extent_xlo = MIN(xlo, xlo + xy); extent_xlo = MIN(extent_xlo, extent_xlo + xz); extent_ylo = MIN(ylo, ylo + yz); extent_zlo = zlo; extent_xhi = MAX(xhi, xhi + xy); extent_xhi = MAX(extent_xhi, extent_xhi + xz); extent_yhi = MAX(yhi, yhi + yz); extent_zhi = zhi; } // h = transformation matrix from tilt coords (0-1) to box coords (xyz) h[0][0] = xhi - xlo; h[0][1] = xy; h[0][2] = xz; h[1][1] = yhi - ylo; h[1][2] = yz; h[2][2] = zhi - zlo; hinv[0][0] = 1.0 / h[0][0]; hinv[0][1] = -h[0][1] / (h[0][0] * h[1][1]); hinv[0][2] = (h[0][1] * h[1][2] - h[0][2] * h[1][1]) / (h[0][0] * h[1][1] * h[2][2]); hinv[1][1] = 1.0 / h[1][1]; hinv[1][2] = -h[1][2] / (h[1][1] * h[2][2]); hinv[2][2] = 1.0 / h[2][2]; // corners = 8 corner points of prism a[0] = xhi - xlo; a[1] = 0.0; a[2] = 0.0; b[0] = xy; b[1] = yhi - ylo; b[2] = 0.0; c[0] = xz; c[1] = yz; c[2] = zhi - zlo; clo[0] = corners[0][0] = xlo; clo[1] = corners[0][1] = ylo; clo[2] = corners[0][2] = zlo; corners[1][0] = xlo + a[0]; corners[1][1] = ylo + a[1]; corners[1][2] = zlo + a[2]; corners[2][0] = xlo + b[0]; corners[2][1] = ylo + b[1]; corners[2][2] = zlo + b[2]; corners[3][0] = xlo + a[0] + b[0]; corners[3][1] = ylo + a[1] + b[1]; corners[3][2] = zlo + a[2] + b[2]; corners[4][0] = xlo + c[0]; corners[4][1] = ylo + c[1]; corners[4][2] = zlo + c[2]; corners[5][0] = xlo + a[0] + c[0]; corners[5][1] = ylo + a[1] + c[1]; corners[5][2] = zlo + a[2] + c[2]; corners[6][0] = xlo + b[0] + c[0]; corners[6][1] = ylo + b[1] + c[1]; corners[6][2] = zlo + b[2] + c[2]; chi[0] = corners[7][0] = xlo + a[0] + b[0] + c[0]; chi[1] = corners[7][1] = ylo + a[1] + b[1] + c[1]; chi[2] = corners[7][2] = zlo + a[2] + b[2] + c[2]; // face = 6 inward-facing unit normals to prism faces MathExtra::cross3(a, b, face[0]); MathExtra::cross3(b, a, face[1]); MathExtra::cross3(c, a, face[2]); MathExtra::cross3(a, c, face[3]); MathExtra::cross3(b, c, face[4]); MathExtra::cross3(c, b, face[5]); for (int i = 0; i < 6; i++) MathExtra::norm3(face[i]); } /* ---------------------------------------------------------------------- error check on existence of variable ------------------------------------------------------------------------- */ void RegPrism::variable_check() { if (xlostyle == VARIABLE) { xlovar = input->variable->find(xlostr); if (xlovar < 0) error->all(FLERR, "Variable {} for region prism does not exist", xlostr); if (!input->variable->equalstyle(xlovar)) error->all(FLERR, "Variable {} for region prism is invalid style", xlostr); } if (xhistyle == VARIABLE) { xhivar = input->variable->find(xhistr); if (xhivar < 0) error->all(FLERR, "Variable {} for region prism does not exist", xhistr); if (!input->variable->equalstyle(xhivar)) error->all(FLERR, "Variable {} for region prism is invalid style", xhistr); } if (ylostyle == VARIABLE) { ylovar = input->variable->find(ylostr); if (ylovar < 0) error->all(FLERR, "Variable {} for region prism does not exist", ylostr); if (!input->variable->equalstyle(ylovar)) error->all(FLERR, "Variable {} for region prism is invalid style", ylostr); } if (yhistyle == VARIABLE) { yhivar = input->variable->find(yhistr); if (yhivar < 0) error->all(FLERR, "Variable {} for region prism does not exist", yhistr); if (!input->variable->equalstyle(yhivar)) error->all(FLERR, "Variable {} for region prism is invalid style", yhistr); } if (zlostyle == VARIABLE) { zlovar = input->variable->find(zlostr); if (zlovar < 0) error->all(FLERR, "Variable {} for region prism does not exist", zlostr); if (!input->variable->equalstyle(zlovar)) error->all(FLERR, "Variable {} for region prism is invalid style", zlostr); } if (zhistyle == VARIABLE) { zhivar = input->variable->find(zhistr); if (zhivar < 0) error->all(FLERR, "Variable {} for region prism does not exist", zhistr); if (!input->variable->equalstyle(zhivar)) error->all(FLERR, "Variable {} for region prism is invalid style", zhistr); } if (xystyle == VARIABLE) { xyvar = input->variable->find(xystr); if (xyvar < 0) error->all(FLERR, "Variable {} for region prism does not exist", xystr); if (!input->variable->equalstyle(xyvar)) error->all(FLERR, "Variable {} for region prism is invalid style", xystr); } if (xzstyle == VARIABLE) { xzvar = input->variable->find(xzstr); if (xzvar < 0) error->all(FLERR, "Variable {} for region prism does not exist", xzstr); if (!input->variable->equalstyle(xzvar)) error->all(FLERR, "Variable {} for region prism is invalid style", xzstr); } if (yzstyle == VARIABLE) { yzvar = input->variable->find(yzstr); if (yzvar < 0) error->all(FLERR, "Variable {} for region prism does not exist", yzstr); if (!input->variable->equalstyle(yzvar)) error->all(FLERR, "Variable {} for region prism is invalid style", yzstr); } } /* ---------------------------------------------------------------------- x is exterior to prism or on its surface return (xp,yp,zp) = nearest pt to x that is on surface of prism ------------------------------------------------------------------------- */ void RegPrism::find_nearest(double *x, double &xp, double &yp, double &zp) { int i, j, k, iface; double xproj[3], xline[3], nearest[3]; double dot; // generate successive xnear points, one nearest to x is (xp,yp,zp) // loop over 6 faces and 2 triangles in each face // xproj = x projected to plane of triangle // if xproj is inside or on triangle boundary, that is xnear // else: loop over 3 edges of triangle // compute distance to edge line // xnear = nearest point on line to xproj, bounded by segment end pts double distsq = BIG; for (int itri = 0; itri < 12; itri++) { iface = itri / 2; if (open_faces[iface]) continue; i = tri[itri][0]; j = tri[itri][1]; k = tri[itri][2]; dot = (x[0] - corners[i][0]) * face[iface][0] + (x[1] - corners[i][1]) * face[iface][1] + (x[2] - corners[i][2]) * face[iface][2]; xproj[0] = x[0] - dot * face[iface][0]; xproj[1] = x[1] - dot * face[iface][1]; xproj[2] = x[2] - dot * face[iface][2]; if (inside_tri(xproj, corners[i], corners[j], corners[k], face[iface])) { distsq = closest(x, xproj, nearest, distsq); } else { point_on_line_segment(corners[i], corners[j], xproj, xline); distsq = closest(x, xline, nearest, distsq); point_on_line_segment(corners[j], corners[k], xproj, xline); distsq = closest(x, xline, nearest, distsq); point_on_line_segment(corners[i], corners[k], xproj, xline); distsq = closest(x, xline, nearest, distsq); } } xp = nearest[0]; yp = nearest[1]; zp = nearest[2]; } /* ---------------------------------------------------------------------- test if x is inside triangle with vertices v1,v2,v3 norm = normal to triangle, defined by right-hand rule for v1,v2,v3 ordering edge = edge vector of triangle pvec = vector from vertex to x xproduct = cross product of edge with pvec if xproduct dot norm < 0.0 for any of 3 edges, then x is outside triangle ------------------------------------------------------------------------- */ int RegPrism::inside_tri(double *x, double *v1, double *v2, double *v3, double *norm) { double edge[3], pvec[3], xproduct[3]; MathExtra::sub3(v2, v1, edge); MathExtra::sub3(x, v1, pvec); MathExtra::cross3(edge, pvec, xproduct); if (MathExtra::dot3(xproduct, norm) < 0.0) return 0; MathExtra::sub3(v3, v2, edge); MathExtra::sub3(x, v2, pvec); MathExtra::cross3(edge, pvec, xproduct); if (MathExtra::dot3(xproduct, norm) < 0.0) return 0; MathExtra::sub3(v1, v3, edge); MathExtra::sub3(x, v3, pvec); MathExtra::cross3(edge, pvec, xproduct); if (MathExtra::dot3(xproduct, norm) < 0.0) return 0; return 1; } /* ---------------------------------------------------------------------- */ double RegPrism::closest(double *x, double *near, double *nearest, double dsq) { double delx = x[0] - near[0]; double dely = x[1] - near[1]; double delz = x[2] - near[2]; double rsq = delx * delx + dely * dely + delz * delz; if (rsq >= dsq) return dsq; nearest[0] = near[0]; nearest[1] = near[1]; nearest[2] = near[2]; return rsq; }