Node ta = tet.nodeA(); Node tb = tet.nodeB(); Node tc = tet.nodeC(); Node td = tet.nodeD(); tet = face.tetLeft(); Node nodeBack = face.nodeLeft(); Tet tetBack = tet.tetNabor(node); while (tetBack!=null) { node = nodeBack; nodeBack = tet.nodeNabor(tetBack); tet = tetBack; tetBack = tet.tetNabor(node);
/** * Marks the specified tet inner or outer. * The outer box must valid. */ private synchronized void markTetInnerOrOuter(Tet tet) { assert _xminOuter<_xmaxOuter:"outer box is valid"; assert _yminOuter<_ymaxOuter:"outer box is valid"; assert _zminOuter<_zmaxOuter:"outer box is valid"; double[] po = {0.0,0.0,0.0}; double s = tet.centerSphere(po); double r = sqrt(s); double xo = po[0]; double yo = po[1]; double zo = po[2]; if (xo-r>=_xminOuter && yo-r>=_yminOuter && zo-r>=_zminOuter && xo+r<=_xmaxOuter && yo+r<=_ymaxOuter && zo+r<=_zmaxOuter) { tet.setInner(); tet.clearOuter(); } else { tet.setOuter(); tet.clearInner(); } }
Node na = tet.nodeA(); Node nb = tet.nodeB(); Node nc = tet.nodeC(); Node nd = tet.nodeD(); if (!leftOfPlane(na,nb,nc,nd)) { trace("xa="+na._x+" ya="+na._y+" za="+na._z); validate(nc); validate(nd); Tet ta = tet.tetA(); Tet tb = tet.tetB(); Tet tc = tet.tetC(); Tet td = tet.tetD(); if (ta!=null) Check.state(ta.tetNabor(tet.nodeNabor(ta))==tet,"a nabor ok"); if (tb!=null) Check.state(tb.tetNabor(tet.nodeNabor(tb))==tet,"b nabor ok"); if (tc!=null) Check.state(tc.tetNabor(tet.nodeNabor(tc))==tet,"c nabor ok"); if (td!=null) Check.state(td.tetNabor(tet.nodeNabor(td))==tet,"d nabor ok");
/** * Constructs an oriented face that references the specified nodes. * Optionally, a tet may be specified. If non-null, that tet may be * right or left of the face, but it must reference the specified nodes. * @param a a node of the face. * @param b a node of the face. * @param c a node of the face. * @param abcd a tet that references the specified nodes; null, if none. */ public Face(Node a, Node b, Node c, Tet abcd) { Node d = (abcd!=null)?otherNode(abcd,a,b,c):null; Check.argument(abcd==null || d!=null,"tet references nodes"); _a = a; _b = b; _c = c; if (d!=null) { if (nodesInOrder(abcd,a,b,c,d)) { _tetLeft = abcd; _nodeLeft = d; _tetRight = abcd.tetNabor(d); _nodeRight = (_tetRight!=null)?abcd.nodeNabor(_tetRight):null; } else { _tetRight = abcd; _nodeRight = d; _tetLeft = abcd.tetNabor(d); _nodeLeft = (_tetLeft!=null)?abcd.nodeNabor(_tetLeft):null; } } }
private void processTetNabor( double xp, double yp, double zp, double xt, double yt, double zt, TetMesh mesh, TetMesh.Tet ta, TetMesh.Node nb, TetMesh.Node nc, TetMesh.Node nd) { boolean saveFace = true; if (ta!=null && mesh.isMarked(ta)) { ta.centerSphere(_xyz); double xa = _xyz[0]-xp, ya = _xyz[1]-yp, za = _xyz[2]-zp; double xb = nb.xp()-xp, yb = nb.yp()-yp, zb = nb.zp()-zp; double xd = nd.xp()-xp, yd = nd.yp()-yp, zd = nd.zp()-zp; double xc = nc.xp()-xp, yc = nc.yp()-yp, zc = nc.zp()-zp; double xbd = xb+xd, ybd = yb+yd, zbd = zb+zd; double xdc = xd+xc, ydc = yd+yc, zdc = zd+zc; double xcb = xc+xb, ycb = yc+yb, zcb = zc+zb; double xyz = yt*za-ya*zt, yzx = zt*xa-za*xt, zxy = xt*ya-xa*yt; accumulate(nb,xbd*xyz+ybd*yzx+zbd*zxy); accumulate(nd,xdc*xyz+ydc*yzx+zdc*zxy); accumulate(nc,xcb*xyz+ycb*yzx+zcb*zxy); saveFace = false; } if (saveFace) addFace(xp,yp,zp,xt,yt,zt,nb,nc,nd); }
/** * Adds the mate of the face of the specified tet that is opposite * the specified node, unless the face is in the set, in which case * that face is removed. Sets the current face to the mate added or * the face removed. * @param tet the tet that references the nodes in the face. * @param node the other node in the tet that is not in the face. * @return true, if the mate was added; false, if the face was removed. */ boolean addMate(Tet tet, Node node) { Tet tetNabor = tet.tetNabor(node); Node nodeNabor = (tetNabor!=null)?tet.nodeNabor(tetNabor):null; if (node==tet._n0) { return add(tet._n1,tet._n2,tet._n3,nodeNabor,tetNabor); } else if (node==tet._n1) { return add(tet._n3,tet._n2,tet._n0,nodeNabor,tetNabor); } else if (node==tet._n2) { return add(tet._n3,tet._n0,tet._n1,nodeNabor,tetNabor); } else if (node==tet._n3) { return add(tet._n1,tet._n0,tet._n2,nodeNabor,tetNabor); } else { assert false:"node is referenced by tet"; return false; } }
private void processTets( double xp, double yp, double zp, TetMesh mesh, TetMesh.TetList tetList) { _faceList.clear(); int ntet = tetList.ntet(); TetMesh.Tet[] tets = tetList.tets(); for (int itet=0; itet<ntet; ++itet) { TetMesh.Tet tet = tets[itet]; TetMesh.Tet ta = tet.tetA(); TetMesh.Tet tb = tet.tetB(); TetMesh.Tet tc = tet.tetC(); TetMesh.Tet td = tet.tetD(); TetMesh.Node na = tet.nodeA(); TetMesh.Node nb = tet.nodeB(); TetMesh.Node nc = tet.nodeC(); TetMesh.Node nd = tet.nodeD(); tet.centerSphere(_xyz); double xt = _xyz[0]-xp, yt = _xyz[1]-yp, zt = _xyz[2]-zp; processTetNabor(xp,yp,zp,xt,yt,zt,mesh,ta,nb,nc,nd); processTetNabor(xp,yp,zp,xt,yt,zt,mesh,tb,nc,na,nd); processTetNabor(xp,yp,zp,xt,yt,zt,mesh,tc,nd,na,nb); processTetNabor(xp,yp,zp,xt,yt,zt,mesh,td,na,nc,nb); } } private void processTetNabor(
/** * Returns a new tet, possibly one resurrected from the dead. * Resurrection reduces the need for garbage collection of dead tets. * Assuming that the new tet will be linked into the mesh, the root * tet is set to the new tet. */ private Tet makeTet(Node n0, Node n1, Node n2, Node n3) { ++_ntet; int ndead = _deadTets.ntet(); if (ndead==0) { _troot = new Tet(n0,n1,n2,n3); } else { _troot = _deadTets.remove(ndead-1); _troot.init(n0,n1,n2,n3); } if (_ntetListeners>0) fireTetAdded(_troot); return _troot; }
private void addTet(double xp, double yp, double zp, TetMesh.Tet tet) { _mesh.mark(tet); _tetList.add(tet); addNode(tet.nodeA()); addNode(tet.nodeB()); addNode(tet.nodeC()); addNode(tet.nodeD()); TetMesh.Tet ta = tet.tetA(); TetMesh.Tet tb = tet.tetB(); TetMesh.Tet tc = tet.tetC(); TetMesh.Tet td = tet.tetD(); if (needTet(xp,yp,zp,ta)) addTet(xp,yp,zp,ta); if (needTet(xp,yp,zp,tb)) addTet(xp,yp,zp,tb); if (needTet(xp,yp,zp,tc)) addTet(xp,yp,zp,tc); if (needTet(xp,yp,zp,td)) addTet(xp,yp,zp,td); } private void addNode(TetMesh.Node node) {
/** * Validates the specified node. */ private void validate(Node node) { Check.state(node==node._prev._next,"node==node._prev._next"); Check.state(node==node._next._prev,"node==node._next._prev"); Tet tet = node.tet(); if (_troot!=null) { Check.state(tet!=null,"tet!=null"); Check.state(node==tet.nodeA() || node==tet.nodeB() || node==tet.nodeC() || node==tet.nodeD(), "node is one of tet nodes"); } }
/** * Given a tet and node on the hull, and another node, typically * inside (not on) the hull, gets the next tet on the hull that * is opposite the node on the hull. If the other node is also * on the hull, this method simply returns the specified tet. */ private Tet getNextTetOnHull(Tet tet, Node node, Node nodeOther) { for (Tet tnext=tet.tetNabor(node); tnext!=null; tnext=tet.tetNabor(node)) { node = nodeOther; nodeOther = tet.nodeNabor(tnext); tet = tnext; } return tet; }
/** * Given a node referenced by one face of the hull, gets the next * face on the hull that is opposite that node. */ private Face getNextFaceOnHull(Node node, Face face) { Tet tet = face.tetLeft(); Node next = face.nodeLeft(); for (Tet tnext=tet.tetNabor(node); tnext!=null; tnext=tet.tetNabor(node)) { node = next; next = tet.nodeNabor(tnext); tet = tnext; } return new Face(tet,node); }
private boolean needTet(double xp, double yp, double zp, TetMesh.Tet tet) { if (tet==null || _mesh.isMarked(tet)) return false; TetMesh.Node na = tet.nodeA(); TetMesh.Node nb = tet.nodeB(); TetMesh.Node nc = tet.nodeC(); TetMesh.Node nd = tet.nodeD(); double xa = na.xp(), ya = na.yp(), za = na.zp(); double xb = nb.xp(), yb = nb.yp(), zb = nb.zp(); double xc = nc.xp(), yc = nc.yp(), zc = nc.zp(); double xd = nd.xp(), yd = nd.yp(), zd = nd.zp(); return Geometry.inSphere(xa,ya,za,xb,yb,zb,xc,yc,zc,xd,yd,zd,xp,yp,zp)>0.0; }
/** * Returns the circumcenter of this tet. * @return array of circumcenter coordinates {xc,yc,zc}. */ public double[] centerSphere() { double[] c = new double[3]; centerSphere(c); return c; }
/** * Constructs a new tet. * The nodes n1, n2, and n3 must be in CCW order as viewed from n0. * Alternatively, the nodes n0, n1, and n2 must be in CW order as * viewed from n3. */ private Tet(Node n0, Node n1, Node n2, Node n3) { init(n0,n1,n2,n3); }
/** * Returns the quality of this tet. * Quality is a number between 0 and 1, inclusive. * Quality equals 1 for equilateral tets. * Quality equals 0 for degenerate tets with co-planar nodes. * @return tet quality. */ public double quality() { if (_quality<0.0) _quality = quality(_n0,_n1,_n2,_n3); return _quality; }