#include "stdafx.h" #include "utils.h" #include "primitives.h" #include "unprojectionchecks.h" CUnprojectionChecker g_Unprojector; CUnprojectionChecker::CUnprojectionChecker() { for(int i=0;i<2;i++) for(int j=0;jn*pmode->dir)*isg; i=0; mindist = (ptri[itri^1]->pt[0]*ptri[itri]->n)*sg; // find vertex of itri^1 that is the closest to the face of itri dist = (ptri[itri^1]->pt[1]*ptri[itri]->n)*sg; i += isneg(dist-mindist); mindist = min(dist,mindist); bBest = isneg((ptri[itri^1]->pt[2]*ptri[itri]->n)*sg-mindist); (i&=bBest^1)|=bBest<<1; t.set(((ptri[itri]->pt[0]-ptri[itri^1]->pt[i])*ptri[itri]->n)*isg, pmode->dir*ptri[itri]->n).fixsign(); dir = pmode->dir*isg; dp = ptri[itri^1]->pt[i]*t.y + dir*t.x; bContact = isneg((dp-ptri[itri]->pt[0]*t.y ^ ptri[itri]->pt[1]-ptri[itri]->pt[0])*ptri[itri]->n) & isneg((dp-ptri[itri]->pt[1]*t.y ^ ptri[itri]->pt[2]-ptri[itri]->pt[1])*ptri[itri]->n) & isneg((dp-ptri[itri]->pt[2]*t.y ^ ptri[itri]->pt[0]-ptri[itri]->pt[2])*ptri[itri]->n); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,itri<<2 | i); } for(i=0;i<3;i++) { // check for edge-edge contacts edge0 = ptri1->pt[inc_mod3[i]]-ptri1->pt[i]; for(j=0;j<3;j++) { edge1 = ptri2->pt[inc_mod3[j]]-ptri2->pt[j]; dp = ptri2->pt[j]-ptri1->pt[i]; n = edge0 ^ edge1; if (n.len2() < (real)1E-8*edge0.len2()*edge1.len2()) continue; t.set(dp*n, pmode->dir*n).fixsign(); dp = dp*t.y-pmode->dir*t.x; t0.set((dp ^ edge1)*n, n.len2()*t.y); t1.set((dp ^ edge0)*n, n.len2()*t.y); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-t0.y) & isneg(fabs_tpl(t1.x*2-t1.y)-t1.y); // contact point belongs to both edges bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,i<<2 | j | 0x80); } } if (idbest==-1 || fabs_tpl(tmax.y)<1E-20) return 0; if (idbest & 0x80) { i = idbest>>2 & 3; j = idbest & 3; edge0 = ptri1->pt[inc_mod3[i]]-ptri1->pt[i]; edge1 = ptri2->pt[inc_mod3[j]]-ptri2->pt[j]; dp = ptri2->pt[j]-ptri1->pt[i]; n = edge0 ^ edge1; t0.set((dp*tmax.y-pmode->dir*tmax.x ^ edge1)*n, n.len2()*tmax.y); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*n.len2(); pcontact->pt = ptri1->pt[i] + edge0*(t0.x*t0.y) + pmode->dir*pcontact->t; pcontact->n = n*sgnnz((ptri2->n^edge1)*n); //sgnnz((ptri2->pt[dec_mod3[j]]-ptri1->pt[dec_mod3[i]])*n); pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0xA0 | j; } else { itri = idbest>>2; i = idbest & 3; pcontact->t = tmax.val(); pcontact->pt = ptri[itri^1]->pt[i] + pmode->dir*(pcontact->t*itri); pcontact->n = ptri[itri]->n*(1-(itri<<1)); pcontact->iFeature[itri] = 0x40; pcontact->iFeature[itri^1] = 0x80 | i; } return 1; } int tri_box_lin_unprojection(unprojection_mode *pmode, const triangle *ptri,int iFeature1, const box *pbox,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr pt[3],n,dir,edge0,ptbox,dp,ncontact,dp_x_edge1; vectori sgdir,sgnorm; int i,j,idir,idbest=-1,bContact,bBest,ix,iy,isgx,isgy,isg; quotient t,tmax(0,1),t0,t1; real dist,mindist; pt[0] = pbox->Basis*(ptri->pt[0]-pbox->center); pt[1] = pbox->Basis*(ptri->pt[1]-pbox->center); pt[2] = pbox->Basis*(ptri->pt[2]-pbox->center); n = pbox->Basis*ptri->n; dir = pbox->Basis*pmode->dir; sgdir.x=sgnnz(dir.x); sgdir.y=sgnnz(dir.y); sgdir.z=sgnnz(dir.z); sgnorm.x=sgnnz(n.x); sgnorm.y=sgnnz(n.y); sgnorm.z=sgnnz(n.z); // box vertex - triangle face, check only the box vertex that is the farthest from the triangle (along flipped normal) // Proof: suppose that not the farthest point is the global maximum. but then some edges from this point to the farthest // point will penetrate the triangle (since the box is convex), meaning this point is not the global maximum ptbox.Set(-pbox->size.x*sgnorm.x, -pbox->size.y*sgnorm.y, -pbox->size.z*sgnorm.z); t.set((pt[0]-ptbox)*n, -(dir*n)).fixsign(); ptbox = ptbox*t.y - dir*t.x; bContact = 1^(isneg((pt[1]-pt[0]^ptbox-pt[0]*t.y)*n) | isneg((pt[2]-pt[1]^ptbox-pt[1]*t.y)*n) | isneg((pt[0]-pt[2]^ptbox-pt[2]*t.y)*n)); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0); // triangle vertex - box face for(j=0;j<3;j++) { // i-triangle vertex, j-box face (with sign in direction of movement) i=0; mindist = pt[0][j]*sgdir[j]; // find triangle vertex that is the farthest from corresponding box face (along its inside normal) dist = pt[1][j]*sgdir[j]; i += isneg(dist-mindist); mindist = min(dist,mindist); dist = pt[2][j]*sgdir[j]; bBest = isneg(dist-mindist); (i&=bBest^1)|=bBest<<1; ix = inc_mod3[j]; iy = dec_mod3[j]; t.set(pbox->size[j]-pt[i][j]*sgdir[j], dir[j]*sgdir[j]); // check if contact point belongs to box face bContact = isneg(fabs_tpl(pt[i][ix]*t.y+dir[ix]*t.x)-pbox->size[ix]*t.y) & isneg(fabs_tpl(pt[i][iy]*t.y+dir[iy]*t.x)-pbox->size[iy]*t.y); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0x40 | (i<<2 | j)); } // triangle edge - box edge for(i=0;i<3;i++) { edge0 = pt[inc_mod3[i]]-pt[i]; for(idir=0;idir<3;idir++) { ix = inc_mod3[idir]; iy = dec_mod3[idir]; ncontact[idir]=0; ncontact[ix]=edge0[iy]; ncontact[iy]=-edge0[ix]; if (ncontact.len2() < (real)1E-8*edge0.len2()) continue; t.y = dir*ncontact; t.y *= isg = sgnnz(t.y); // choose box edge along idir that will generate maximum t (only it can be the global maximum for the entire unprojection) // Proof: there's a continuous range along dir where the edge intersects infinite box slab in direction idir. // if touch point is the global separation point, then the edge will intersect the slab, but not the box while continuing moving along dir. // but it cannot leave the box immediately after the touch point, because it doesn't touch box feature different from slab feature // (if it touches a vertex, we'll hopefully detect it as a face-vertex contact) // NOTE: if maximum t is generated by 2 box edges, and the triangle edge crosses both of them, then which one we choose doesn't matter, // and if the triangle edge crosses only one, then tri vertex-box face contact will be detected (so choosing max t edge only is safe) isgx = sgnnz(ncontact[ix])*isg; isgy = sgnnz(ncontact[iy])*isg; ptbox[idir]=-pbox->size[idir]; ptbox[ix]=pbox->size[ix]*isgx; ptbox[iy]=pbox->size[iy]*isgy; dp = ptbox-pt[i]; t.x = dp*ncontact*isg; dp = dp*t.y-dir*t.x; dp_x_edge1[idir]=0; dp_x_edge1[ix]=dp[iy]; dp_x_edge1[iy]=-dp[ix]; t0.set(dp_x_edge1*ncontact, ncontact.len2()*t.y); // check if t0 is in (0..1) and t1 is in (0..2*size) t1.set((dp^edge0)*ncontact, t0.y); bContact = t0.isin01() & isneg(fabs_tpl(t1.x-t1.y*pbox->size[idir])-t1.y*pbox->size[idir]); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0x80 | (i<<4 | idir<<2 | isgy+1 | isgx+1>>1)); } } if (idbest==-1) return 0; switch (idbest & 0xC0) { case 0: // triangle face - box vertex ptbox.Set(-pbox->size.x*sgnorm.x, -pbox->size.y*sgnorm.y, -pbox->size.z*sgnorm.z); pcontact->t = tmax.val(); pcontact->pt = pbox->center+ptbox*pbox->Basis; pcontact->n = ptri->n; pcontact->iFeature[0] = 0x40; pcontact->iFeature[1] = 1-sgnorm.z<<1 | 1-sgnorm.y | 1-sgnorm.x>>1; break; case 0x40: // triangle vertex - box face i = idbest>>2&3; j = idbest&3; pcontact->t = tmax.val(); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t; pcontact->n = pbox->Basis.GetRow(j)*-sgdir[j]; pcontact->iFeature[0] = 0x80 | i; pcontact->iFeature[1] = 0x40 | j<<1 | sgdir[j]+1>>1; break; default: // triangle edge - box edge i = idbest>>4&3; idir=idbest>>2&3; isgy=(idbest&2)-1; isgx=((idbest&1)<<1)-1; edge0 = pt[inc_mod3[i]]-pt[i]; ix = inc_mod3[idir]; iy = dec_mod3[idir]; ncontact[idir]=0; ncontact[ix]=edge0[iy]; ncontact[iy]=-edge0[ix]; isg = sgnnz(dir*ncontact); isgx = sgnnz(ncontact[ix])*isg; isgy = sgnnz(ncontact[iy])*isg; ptbox[idir]=-pbox->size[idir]; ptbox[ix]=pbox->size[ix]*isgx; ptbox[iy]=pbox->size[iy]*isgy; dp = (ptbox-pt[i])*tmax.y-dir*tmax.x; dp_x_edge1[idir]=0; dp_x_edge1[ix]=dp[iy]; dp_x_edge1[iy]=-dp[ix]; t0.set(dp_x_edge1*ncontact, ncontact.len2()*tmax.y); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*ncontact.len2(); pcontact->pt = ptri->pt[i] + (ptri->pt[inc_mod3[i]]-ptri->pt[i])*t0.x*t0.y + pmode->dir*pcontact->t; pcontact->n = (ncontact*pbox->Basis)*sgnnz((pt[i]-pt[dec_mod3[i]])*ncontact); pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x20 | idbest&0xF; } return 1; } int box_tri_lin_unprojection(unprojection_mode *pmode, const box *pbox,int iFeature1, const triangle *ptri,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = tri_box_lin_unprojection(pmode,ptri,iFeature2,pbox,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int tri_cylinder_lin_unprojection(unprojection_mode *pmode, const triangle *ptri,int iFeature1, const cylinder *pcyl,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr n,center,dp,edge,vec0,vec1,ptcyl; quotient t,t0,t1,tmax(0,1); real nlen,nlen2,a,b,c,d,r2=sqr(pcyl->r),dist,mindist; int i,j,idbest=-1,bContact,bBest; // cylinder cap edges - triangle face n = pcyl->axis*(pcyl->axis*ptri->n)-ptri->n; nlen = n.len(); i = isneg(pcyl->axis*ptri->n); // choose cap closest to the triangle face center = pcyl->center + pcyl->axis*(pcyl->hh*((i<<1)-1)); t.set(((center-ptri->pt[0])*nlen+n*pcyl->r)*ptri->n, ptri->n*pmode->dir).fixsign(); ptcyl = center*(t.y*nlen) + n*(pcyl->r*t.y) - pmode->dir*t.x; t.y *= nlen; bContact = 1^(isneg((ptri->pt[1]-ptri->pt[0]^ptcyl-ptri->pt[0]*t.y)*ptri->n) | isneg((ptri->pt[2]-ptri->pt[1]^ptcyl-ptri->pt[1]*t.y)*ptri->n) | isneg((ptri->pt[0]-ptri->pt[2]^ptcyl-ptri->pt[2]*t.y)*ptri->n)); bBest = bContact & isneg(tmax-t) & isneg(t.x-pmode->tmax*t.y); UPDATE_IDBEST(tmax,i); // triangle vertices - cylinder cap faces j = (isnonneg(pcyl->axis*pmode->dir)<<1)-1; // choose cap that lies farther along unprojection direction i=0; mindist = (ptri->pt[0]*pcyl->axis)*j; // find triangle vertex that is the closest to the cap face dist = (ptri->pt[1]*pcyl->axis)*j; i += isneg(dist-mindist); mindist = min(mindist,dist); dist = (ptri->pt[2]*pcyl->axis)*j; bBest = isneg(dist-mindist); (i&=bBest^1)|=bBest<<1; center = pcyl->center + pcyl->axis*(pcyl->hh*j); t.set((center-ptri->pt[i])*pcyl->axis, pmode->dir*pcyl->axis).fixsign(); bContact = isneg((ptri->pt[i]*t.y+pmode->dir*t.x-center*t.y).len2()-r2*t.y*t.y); bBest = bContact & isneg(tmax-t) & isneg(t.x-pmode->tmax*t.y); UPDATE_IDBEST(tmax,0x20 | (i<<1 | j+1>>1)); // triangle vertices - cylinder side for(i=0;i<3;i++) { dp = ptri->pt[i]-pcyl->center; vec0 = dp ^ pcyl->axis; vec1 = pmode->dir^pcyl->axis; a = vec1*vec1; b = vec1*vec0; c = vec0*vec0-r2; d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t.set(-b+d,a); bContact = isneg(fabs_tpl((dp*t.y+pmode->dir*t.x)*pcyl->axis) - t.y*pcyl->hh); bBest = bContact & isneg(tmax-t) & isneg(t.x-pmode->tmax*t.y); UPDATE_IDBEST(tmax,0x40 | i); } } // triangle edges - cylinder side for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; dp = ptri->pt[i]-pcyl->center; n = edge^pcyl->axis; nlen2 = n.len2(); c = dp*n; a = pmode->dir*n; b = a*c; a*=a; c=c*c-r2*nlen2; d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t.set(-b+d,a); dp = (pcyl->center-ptri->pt[i])*t.y-pmode->dir*t.x; t0.set((dp^pcyl->axis)*n, nlen2*t.y); t1.set((dp^edge)*n, nlen2*t.y); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-t0.y) & isneg(fabs_tpl(t1.x)-t1.y*pcyl->hh); bBest = bContact & isneg(tmax-t) & isneg(t.x-pmode->tmax*t.y); UPDATE_IDBEST(tmax,0x60 | i); } } // triangle edges - cylinder cap edges for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; for(j=-1;j<=1;j+=2) { center = pcyl->center+pcyl->axis*pcyl->hh*j; dp = ptri->pt[i]-center; vec0 = pcyl->axis^(dp^edge); vec1 = pcyl->axis^(pmode->dir^edge); a = vec1*vec1; b = vec0*vec1; c = vec0*vec0-r2*sqr(edge*pcyl->axis); d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t.set(-b+d,a); t0.set(-((dp*t.y+pmode->dir*t.x)*pcyl->axis), (edge*pcyl->axis)*t.y); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-fabs_tpl(t0.y)); bBest = bContact & isneg(tmax-t) & isneg(t.x-pmode->tmax*t.y); UPDATE_IDBEST(tmax,0x80 | i<<1 | j+1>>1); } } } // we also need to process degenerate cases when contacting triangle edge lies in cap plane (edge-cap edge check doesn't detect them) if (fabs_tpl(fabs_tpl(ptri->n*pcyl->axis)-1.0f)<1E-4f) { // potential triangle face - cylinder cap contact d = pmode->dir*pcyl->axis; j = sgnnz(d); center = pcyl->center + pcyl->axis*(pcyl->hh*j); t.set(((center-ptri->pt[0])*pcyl->axis)*j, fabs_tpl(d)); if (t>tmax && ttmax) { pcontact->n = pcyl->axis*-j; pcontact->iFeature[1] = 0x40 | (j+1>>1)+1; if (((ptri->pt[0]-center)*t.y+pmode->dir*t.x).len2() < r2*t.y*t.y) { // triangle's 1st vertex is inside cylinder cap pcontact->t = t.val(); pcontact->pt = ptri->pt[0] + pmode->dir*pcontact->t; pcontact->iFeature[0] = 0x80 | i; return 1; } ptcyl = center*t.y - pmode->dir*t.x; bContact = 1^(isneg((ptri->pt[1]-ptri->pt[0]^ptcyl-ptri->pt[0]*t.y)*ptri->n) | isneg((ptri->pt[2]-ptri->pt[1]^ptcyl-ptri->pt[1]*t.y)*ptri->n) | isneg((ptri->pt[0]-ptri->pt[2]^ptcyl-ptri->pt[2]*t.y)*ptri->n)); if (bContact) { // cylinder cap center is inside triangle pcontact->t = t.val(); pcontact->pt = center; pcontact->iFeature[0] = 0x40; return 1; } for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; vec0 = (ptri->pt[i]-center)*t.y+pmode->dir*t.x; vec1 = edge*t.y; a = vec1*vec1; b = vec0*vec1; c = vec0*vec0-r2*sqr(t.y); d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t0.set(-b-d,a); for(j=0;j<2;j++,t0.x+=d*2) if (isneg(fabs_tpl(t0.x*2-t0.y)-t0.y)) { pcontact->t = t.val(); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t + edge*t0.val(); pcontact->iFeature[0] = 0xA0 | i; return 1; } } } } } else for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; if (sqr(edge*pcyl->axis)axis*pmode->dir); center = pcyl->center+pcyl->axis*pcyl->hh*j; dp = center-ptri->pt[i]; t.set((dp*pcyl->axis)*edge.len2()-(edge*pcyl->axis)*(edge*dp), // calculate when the closest point on edge will touch the cap (pmode->dir*pcyl->axis)*edge.len2()-(edge*pcyl->axis)*(edge*pmode->dir)).fixsign(); if (t>tmax && ttmax) { pcontact->t = t.val(); t0.set(edge*(dp-pmode->dir*pcontact->t), edge.len2()); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t; if (fabs_tpl(t0.x*2-t0.y)pt-center)*t0.y).len2()pt += edge*(t0.val()); else if ((pcontact->pt+edge-center).len2()pt += edge; else if ((pcontact->pt-center).len2()>r2) continue; pcontact->n = pcyl->axis*-j; pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x40 | (j+1>>1)+1; return 1; } } } if (idbest==-1 || fabs_tpl(tmax.y)<1E-20) return 0; switch (idbest & 0xE0) { case 0: // triangle face - cylinder cap edge center = pcyl->center+pcyl->axis*(pcyl->hh*((idbest<<1)-1)); n = pcyl->axis*(pcyl->axis*ptri->n)-ptri->n; t0.y = 1.0/(tmax.y*nlen); pcontact->t = tmax.x*nlen*t0.y; pcontact->pt = center + n*(pcyl->r*t0.y*tmax.y); pcontact->n = ptri->n; pcontact->iFeature[0] = 0x40; pcontact->iFeature[1] = 0x20 | idbest; break; case 0x20: // triangle vertex - cylinder cap face i = idbest>>1&3; j = idbest&1; pcontact->t = tmax.val(); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t; pcontact->n = pcyl->axis*(1-(j<<1)); pcontact->iFeature[0] = 0x80 | i; pcontact->iFeature[1] = 0x40 | j+1; break; case 0x40: // triangle vertex - cylinder side i = idbest & 3; pcontact->t = tmax.val(); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t; pcontact->n = pcyl->center-pcontact->pt; pcontact->n -= pcyl->axis*(pcyl->axis*pcontact->n); pcontact->iFeature[0] = 0x80 | i; pcontact->iFeature[1] = 0x40; break; case 0x60: // triangle edge - cylinder side i = idbest&3; edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; dp = pcyl->center-ptri->pt[i]; n = edge^pcyl->axis; nlen2 = n.len2(); dp = dp*tmax.y-pmode->dir*tmax.x; t0.set((dp^pcyl->axis)*n, nlen2*tmax.y); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*nlen2; pcontact->pt = ptri->pt[i] + edge*t0.x*t0.y + pmode->dir*pcontact->t; pcontact->n = pcyl->center-pcontact->pt; pcontact->n -= pcyl->axis*(pcyl->axis*pcontact->n); pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x40; break; default: // triangle edge - cylinder cap edge i = idbest>>1&3; j = idbest&1; center = pcyl->center+pcyl->axis*pcyl->hh*((j<<1)-1); edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; dp = ptri->pt[i]-center; t0.set(-((dp*tmax.y+pmode->dir*tmax.x)*pcyl->axis), (edge*pcyl->axis)*tmax.y); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*(edge*pcyl->axis); pcontact->pt = ptri->pt[i] + edge*t0.x*t0.y + pmode->dir*pcontact->t; pcontact->n = pcontact->pt-pcyl->center; pcontact->n -= pcyl->axis*(pcyl->axis*pcontact->n); pcontact->n = edge^(pcyl->axis^pcontact->n); pcontact->n *= sgnnz((pcyl->center-pcontact->pt)*pcontact->n); pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x20 | j; } return 1; } int cylinder_tri_lin_unprojection(unprojection_mode *pmode, const cylinder *pcyl,int iFeature1, const triangle *ptri,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = tri_cylinder_lin_unprojection(pmode,ptri,iFeature2,pcyl,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int tri_sphere_lin_unprojection(unprojection_mode *pmode, const triangle *ptri,int iFeature1, const sphere *psphere,int iFeature2, contact *pcontact, geom_contact_area *parea) { quotient t,tmax(0,1),t0; vectorr center,edge,vec0,vec1,dp; real a,b,c,d,r2=sqr(psphere->r); int i,bBest,bContact,idbest=-1; // triangle face - sphere t.set((psphere->center-ptri->pt[0])*ptri->n-psphere->r, pmode->dir*ptri->n).fixsign(); center = psphere->center*t.y - pmode->dir*t.x; bContact = 1^(isneg((ptri->pt[1]-ptri->pt[0]^center-ptri->pt[0]*t.y)*ptri->n) | isneg((ptri->pt[2]-ptri->pt[1]^center-ptri->pt[1]*t.y)*ptri->n) | isneg((ptri->pt[0]-ptri->pt[2]^center-ptri->pt[2]*t.y)*ptri->n)); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0); // triangle vertices - sphere for(i=0;i<3;i++) { vec0 = ptri->pt[i]-psphere->center; vec1 = pmode->dir; a = vec1*vec1; b = vec0*vec1; c = vec0*vec0-r2; d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t.set(-b-d,a); bBest = isneg(tmax-t); UPDATE_IDBEST(tmax,0x40 | i); t.set(-b+d,a); bBest = isneg(tmax-t); UPDATE_IDBEST(tmax,0x40 | i); } } // triangle edges - sphere for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; dp = ptri->pt[i]-psphere->center; vec0 = edge ^ dp; vec1 = edge ^ pmode->dir; a = vec1*vec1; b = vec0*vec1; c = vec0*vec0-r2*edge.len2(); d = b*b-a*c; if (d>=0) { d = sqrt_tpl(d); t.set(-b-d,a); t0.set(((psphere->center-ptri->pt[i])*t.y-pmode->dir*t.x)*edge, t.y*edge.len2()); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-t0.y); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0x80 | i); t.x += d*2; t0.set(((psphere->center-ptri->pt[i])*t.y-pmode->dir*t.x)*edge, t.y*edge.len2()); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-t0.y); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0x80 | i); } } if (idbest==-1) return 0; switch (idbest & 0xC0) { case 0: // triangle face - sphere pcontact->t = tmax.val(); pcontact->pt = psphere->center - ptri->n*psphere->r; pcontact->n = ptri->n; pcontact->iFeature[0] = 0x40; pcontact->iFeature[1] = 0x40; break; case 0x40: i = idbest&3; pcontact->t = tmax.val(); pcontact->pt = ptri->pt[i]+pmode->dir*pcontact->t; pcontact->n = psphere->center-pcontact->pt; pcontact->iFeature[0] = 0x80 | i; pcontact->iFeature[1] = 0x40; break; case 0x80: i = idbest&3; edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; t0.set(((psphere->center-ptri->pt[i])*tmax.y-pmode->dir*tmax.x)*edge, tmax.y*edge.len2()); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*edge.len2(); pcontact->pt = ptri->pt[i] + edge*(t0.x*t0.y) + pmode->dir*pcontact->t; pcontact->n = psphere->center-pcontact->pt; pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x40; } return 1; } int sphere_tri_lin_unprojection(unprojection_mode *pmode, const sphere *psphere,int iFeature1, const triangle *ptri,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = tri_sphere_lin_unprojection(pmode,ptri,iFeature2,psphere,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int tri_ray_lin_unprojection(unprojection_mode *pmode, const triangle *ptri,int iFeature1, const ray *pray,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr pt,ptnew,n,dp,edge; quotient t,tmax(0,1),t0,t1; int i,bContact,bBest,idbest=-1; real nlen2; // triangle - ray ends for(i=0,pt=pray->origin; i<2; i++,pt+=pray->dir) { t.set((pt-ptri->pt[0])*ptri->n, pmode->dir*ptri->n); ptnew = pt*t.y - pmode->dir*t.x; bContact = 1^(isneg((ptri->pt[1]-ptri->pt[0]^ptnew-ptri->pt[0]*t.y)*ptri->n) | isneg((ptri->pt[2]-ptri->pt[1]^ptnew-ptri->pt[1]*t.y)*ptri->n) | isneg((ptri->pt[0]-ptri->pt[2]^ptnew-ptri->pt[2]*t.y)*ptri->n)); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,i); } // triangle edges - ray for(i=0;i<3;i++) { edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; n = edge^pray->dir; nlen2 = n.len2(); dp = pray->origin-ptri->pt[i]; t.set(n*dp, n*pmode->dir).fixsign(); dp = dp*t.y - pmode->dir*t.x; t0.set((dp^pray->dir)*n, nlen2*t.y); t1.set((dp^edge)*n, nlen2*t.y); bContact = isneg(fabs_tpl(t0.x*2-t0.y)-t0.y) & isneg(fabs_tpl(t1.x*2-t1.y)-t1.y); bBest = isneg(tmax-t); UPDATE_IDBEST(tmax,0x80 | i); } if (idbest==-1) return 0; if (idbest&0x80) { // triangle edge - ray i = idbest&3; edge = ptri->pt[inc_mod3[i]]-ptri->pt[i]; n = edge^pray->dir; nlen2 = n.len2(); dp = (pray->origin-ptri->pt[i])*tmax.y - pmode->dir*tmax.x; t0.set((dp^pray->dir)*n, nlen2*tmax.y); t0.y = 1.0/t0.y; pcontact->t = tmax.x*t0.y*nlen2; pcontact->pt = ptri->pt[i] + edge*t0.x*t0.y + pmode->dir*pcontact->t; pcontact->n = pray->dir^edge; pcontact->n *= sgnnz((ptri->pt[i]-ptri->pt[dec_mod3[i]])*pcontact->n); pcontact->iFeature[0] = 0xA0 | i; pcontact->iFeature[1] = 0x20; } else { // triangle face - ray end pcontact->t = tmax.val(); pcontact->pt = pray->origin+pray->dir*idbest; pcontact->n = ptri->n; pcontact->iFeature[0] = 0x40; pcontact->iFeature[1] = idbest; } return 1; } int ray_tri_lin_unprojection(unprojection_mode *pmode, const ray *pray,int iFeature1, const triangle *ptri,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = tri_ray_lin_unprojection(pmode,ptri,iFeature2,pray,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int tri_plane_lin_unprojection(unprojection_mode *pmode, const triangle *ptri,int iFeature1, const plane *pplane,int iFeature2, contact *pcontact, geom_contact_area *parea) { quotient t; real dist,mindist; int i,bBest; // the closest to the plane triangle vertex - plane i=0; mindist = (ptri->pt[0]-pplane->origin)*pplane->n; dist = (ptri->pt[1]-pplane->origin)*pplane->n; i += isneg(dist-mindist); mindist = min(dist,mindist); dist = (ptri->pt[2]-pplane->origin)*pplane->n; bBest = isneg(dist-mindist); (i&=bBest^1)|=bBest<<1; t.set((pplane->origin-ptri->pt[0])*pplane->n, pmode->dir*pplane->n).fixsign(); if (t.x<0) return 0; pcontact->t = t.val(); pcontact->pt = ptri->pt[i] + pmode->dir*pcontact->t; pcontact->n = -pplane->n; pcontact->iFeature[0] = 0x80 | i; pcontact->iFeature[1] = 0x40; return 1; } int plane_tri_lin_unprojection(unprojection_mode *pmode, const plane *pplane,int iFeature1, const triangle *ptri,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = tri_plane_lin_unprojection(pmode,ptri,iFeature2,pplane,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int crop_polygon_with_bound(const vector2df *ptsrc,const int *idedgesrc,int nsrc, const vector2df &sz,int icode,int iedge, vector2df *ptdst,int *idedgedst) { int ic=icode&1,i,inext,sg=icode>>1&1,istart=-1,bInside,bSwitch,n,ndst; float bound = sz[ic]*((icode&2)-1); for(i=0;i>31; if (bSwitch = isneg((ptsrc[i][ic]-bound)*(ptsrc[inext][ic]-bound))) { vector2df d = ptsrc[inext]-ptsrc[i]; bInside ^= 1; ptdst[ndst][ic] = bound; ptdst[ndst][ic^1] = ptsrc[i][ic^1]+d[ic^1]*(bound-ptsrc[i][ic])/d[ic]; idedgedst[ndst++] = idedgedst[ndst]&-bInside | iedge&~-bInside; // change edge to bound edge only for inside-outside switches } } return ndst; } void crop_segment_with_bound(vector2df *ptseg,float bound,int ic, int *piFeature0,int *piFeature1, int iedge0,int iedge1) { int imin=isneg(ptseg[1][ic]-ptseg[0][ic]),imax=imin^1; if (ptseg[imax][ic]<-bound || ptseg[imin][ic]>bound) { ptseg[0].zero(); ptseg[1].zero(); } else { vector2df d = ptseg[imax]-ptseg[imin]; float denom; if (ptseg[imin][ic]<-bound || ptseg[imax][ic]>bound) denom = 1.0f/d[ic]; if (ptseg[imin][ic] < -bound) { ptseg[imin][ic^1] = ptseg[imin][ic^1] + d[ic^1]*(-bound-ptseg[imin][ic])*denom; ptseg[imin][ic] = -bound; piFeature0[imin] = iedge0; piFeature1[imin] = iedge1; } if (ptseg[imax][ic] > bound) { ptseg[imax][ic^1] = ptseg[imin][ic^1] + d[ic^1]*(bound-ptseg[imin][ic])*denom; ptseg[imax][ic] = bound; piFeature0[imax] = iedge0 | 2>>ic; piFeature1[imax] = iedge1; } } } inline int edge_from_vtx(int ivtx0,int ivtx1) { int idir = ivtx0^ivtx1; idir = isneg(3-idir)+isneg(1-idir); return idir<<2 | ivtx0>>inc_mod3[idir]&1 | (ivtx0>>dec_mod3[idir])<<1&2; } inline int vtx_from_edges(int iedge0,int iedge1) { int idir0=iedge0>>2, idir1=iedge1>>2; return (iedge0&1)<>1&1)<>iszero(idir0-dec_mod3[idir1])&1)<size,pbox2->size }; real nlen2; int i,iz,ibox,idir0,idir1,ix,iy,ix0,iy0,ix1,iy1,bBest,sg,idbest=-1,bFindMinUnproj; vectori isg0,isg1; quotient t,tmin(pmode->tmax,1),t0,t1; matrix3x3RM &Basis21((matrix3x3RM&)axes[0]); center[0] = pbox1->Basis*(pbox2->center-pbox1->center); center[1] = pbox2->Basis*(pbox1->center-pbox2->center); dir[0] = pbox1->Basis*pmode->dir; dir[1] = pbox2->Basis*-pmode->dir; Basis21 = pbox2->Basis*pbox1->Basis.T(); bFindMinUnproj = iszero(pmode->dir.len2()); // each contact we check guarantees that boxes are fully separated at it (while also guaranteeing that no closer contact of the // selected group of features can be the global separation minimum), so the contact with the minimum unprojection length // is the global separation minimum, and we don't even need to verify that boxes are in contact at this point // (i.e. check finite versions of features in addition to infinite) // face - the closest vertex for(ibox=0;ibox<2;ibox++) { // ibox provides face, ibox^1 - vertex for(iz=0;iz<3;iz++) { if (bFindMinUnproj) dir[ibox].zero()[iz] = -sgnnz(center[ibox][iz]); ix = inc_mod3[iz]; iy = dec_mod3[iz]; origin.zero(); origin[iz] = size[ibox][iz]*(isg0.z=-sgnnz(dir[ibox][iz])); isg1.x = sgnnz(axes[0][iz]); isg1.y = sgnnz(axes[1][iz]); isg1.z = sgnnz(axes[2][iz]); pt = center[ibox] - (axes[0]*(size[ibox^1].x*isg1.x) + axes[1]*(size[ibox^1].y*isg1.y) + axes[2]*(size[ibox^1].z*isg1.z))*isg0.z; t.set(pt[iz]-origin[iz], dir[ibox][iz]).fixsign(); bBest = isneg(-t.x) & isneg(t-tmin); UPDATE_IDBEST(tmin,ibox<<5 | iz<<3 | isg1.z+1<<1|isg1.y+1|isg1.x+1>>1); } Basis21.Transpose(); } // edge - edge for(idir0=0;idir0<3;idir0++) for(idir1=0;idir1<3;idir1++) { ix0 = inc_mod3[idir0]; iy0 = dec_mod3[idir0]; ix1 = inc_mod3[idir1]; iy1 = dec_mod3[idir1]; n[idir0]=0; n[ix0]=-axes[idir1][iy0]; n[iy0]=axes[idir1][ix0]; nlen2=n.len2(); if (nlen2 < (real)1E-8) continue; if (bFindMinUnproj) dir[0] = n*-sgnnz(n*center[0]); t.y = dir[0]*n; t.y *= sg = sgnnz(t.y); // choose edges along idir0,idir1 that will generate maximum t // Proof of validity is similar to box-triangle: if boxes separate, but infinite slabs (along idir0 and idir1) do not, // then the global minimum contact should be at box feature that is not a slab feature. isg0.x = -sgnnz(n[ix0])*sg; isg0.y = -sgnnz(n[iy0])*sg; isg1.x = sgnnz(axes[ix1]*n)*sg; isg1.y = sgnnz(axes[iy1]*n)*sg; pt0[idir0]=-size[0][idir0]; pt0[ix0]=size[0][ix0]*isg0.x; pt0[iy0]=size[0][iy0]*isg0.y; pt1 = center[0] - axes[idir1]*size[1][idir1] + axes[ix1]*(size[1][ix1]*isg1.x) + axes[iy1]*(size[1][iy1]*isg1.y); dp = pt1-pt0; t.x = (dp*n)*sg; bBest = isneg(-t.x) & isneg(t-tmin); UPDATE_IDBEST(tmin,0x100 | (idir1<<2|isg1.y+1|isg1.x+1>>1)<<4 | (idir0<<2|isg0.y+1|isg0.x+1>>1)); } if (idbest==-1) return 0; if (idbest & 0x100) { // edge-edge idir0=idbest>>2&3; isg0.y=(idbest&2)-1; isg0.x=((idbest&1)<<1)-1; idbest>>=4; idir1=idbest>>2&3; isg1.y=(idbest&2)-1; isg1.x=((idbest&1)<<1)-1; ix0 = inc_mod3[idir0]; iy0 = dec_mod3[idir0]; ix1 = inc_mod3[idir1]; iy1 = dec_mod3[idir1]; n[idir0]=0; n[ix0]=-axes[idir1][iy0]; n[iy0]=axes[idir1][ix0]; nlen2 = n.len2(); if (bFindMinUnproj) { dir[0] = n*-sgnnz(n*center[0]); pmode->dir = dir[0]*pbox[0]->Basis; dir[1] = pbox[1]->Basis*-pmode->dir; } pt0[idir0]=-size[0][idir0]; pt0[ix0]=size[0][ix0]*isg0.x; pt0[iy0]=size[0][iy0]*isg0.y; pt1 = center[0] - axes[idir1]*size[1][idir1] + axes[ix1]*size[1][ix1]*isg1.x + axes[iy1]*size[1][iy1]*isg1.y; dp = pt1-pt0; dp = dp*tmin.y - dir[0]*tmin.x; t0.set((dp^axes[idir1])*n, nlen2*tmin.y); t0.y = (real)1.0/t0.y; pcontact->t = tmin.x*t0.y*nlen2; pt0[idir0] += t0.x*t0.y; pcontact->pt = pbox1->center + pt0*pbox1->Basis + pmode->dir*pcontact->t; pcontact->n = (n*pbox1->Basis)*sgnnz(n*center[0]); pcontact->iFeature[0] = 0x20 | idir0<<2|isg0.y+1|isg0.x+1>>1; pcontact->iFeature[1] = 0x20 | idir1<<2|isg1.y+1|isg1.x+1>>1; } else { // face-vertex ibox = idbest>>5; iz = idbest>>3&3; if (bFindMinUnproj) { dir[ibox].zero()[iz] = -sgnnz(center[ibox][iz]); pmode->dir = (dir[ibox]*(1-(ibox<<1)))*pbox[ibox]->Basis; dir[ibox^1] = pbox[ibox^1]->Basis*(pmode->dir*((ibox<<1)-1)); } isg0.z=-sgnnz(dir[ibox][iz]); isg1.z=(idbest>>1&2)-1; isg1.y=(idbest&2)-1; isg1.x=(idbest<<1&2)-1; pcontact->t = tmin.val(); pcontact->pt = pbox[ibox^1]->center + pmode->dir*pcontact->t*ibox - (vectorf(pbox[ibox^1]->size.x*isg1.x, pbox[ibox^1]->size.y*isg1.y, pbox[ibox^1]->size.z*isg1.z)*isg0.z)*pbox[ibox^1]->Basis; pcontact->n = pbox[ibox]->Basis.GetRow(iz)*(isg0.z*(1-(ibox<<1))); pcontact->iFeature[ibox] = 0x40 | iz | isg0.z+1>>1; pcontact->iFeature[ibox^1] = ((idbest^isg0.z>>31)^7)&7; } if (parea) { // check if the contact is face-face, and build area if it is float diff0,diff1; for(idir1=0; idir1<3; idir1++) if (fabs_tpl(axes[idir1].x)+fabs_tpl(axes[idir1].y)+fabs_tpl(axes[idir1].z)<(real)1.05) { // axes[i] is close to some coordinate axis iz = -1; // find axis coordinate that is close to one bBest = -isneg((real)0.997-fabs_tpl(axes[idir1].x)); iz = iz&~bBest; bBest = -isneg((real)0.997-fabs_tpl(axes[idir1].y)); iz = iz&~bBest|1&bBest; bBest = -isneg((real)0.997-fabs_tpl(axes[idir1].z)); iz = iz&~bBest|2&bBest; if (iz<0) continue; diff0 = fabs_tpl(center[0][iz]-dir[0][iz]*pcontact->t) - (pbox[0]->size[iz]+fabs_tpl(axes[0][iz])*pbox[1]->size.x+ fabs_tpl(axes[1][iz])*pbox[1]->size.y+fabs_tpl(axes[2][iz])*pbox[1]->size.z); diff1 = fabs_tpl(center[1][idir1]-dir[1][idir1]*pcontact->t) - (pbox[1]->size[idir1]+fabs_tpl(axes[idir1][0])*pbox[0]->size.x+ fabs_tpl(axes[idir1][1])*pbox[0]->size.y+fabs_tpl(axes[idir1][2])*pbox[0]->size.z); if (min(fabs_tpl(diff0),fabs_tpl(diff1))<(pbox[0]->size[iz]+pbox[1]->size[idir1])*0.003f && fabs_tpl(dir[0][iz])>0.01f) { // confirmed face-face contact int iprev,ivtx0,ivtx1,iface[2],idedge[2][8]; vector2df ptc2d,sz2d,axisx2d,axisy2d,pt2d[2][8]; vectorf pt3d; parea->type = geom_contact_area::polygon; parea->n1 = pbox[1]->Basis.GetRow(idir1); parea->n1 *= (isg1.z = sgnnz(parea->n1*pmode->dir)); pcontact->n = pbox[0]->Basis.GetRow(iz)*-dir[0][iz]; ix0 = inc_mod3[iz]; iy0 = dec_mod3[iz]; ix1 = inc_mod3[idir1]; iy1 = dec_mod3[idir1]; ptc2d.set(center[0][ix0]-dir[0][ix0]*pcontact->t, center[0][iy0]-dir[0][iy0]*pcontact->t); sz2d.set(pbox[0]->size[ix0], pbox[0]->size[iy0]); axisx2d.set(axes[ix1][ix0], axes[ix1][iy0]); axisy2d.set(axes[iy1][ix0], axes[iy1][iy0]); pt3d[iz] = pbox[0]->size[iz]*(isg0.z=-sgnnz(dir[0][iz])); isg0.z = -isg0.z>>31&3; isg1.z = -isg1.z>>31&3; iface[0] = 0x40 | iz<<1 | isg0.z&1; iface[1] = 0x40 | idir1<<1 | isg1.z&1; for(i=0;i<4;i++) { i = i+1&3; ix = (i^i>>1)&1; iy = (i^isg1.z)>>1&1; ivtx1 = ix<>1)&1; iy = (i^isg1.z)>>1&1; ivtx0 = ix<size[ix1]*((ix<<1)-1)) + axisy2d*(pbox[1]->size[iy1]*((iy<<1)-1)); idedge[0][i] = 1<<4 | edge_from_vtx((isg1.z&1)<npt = crop_polygon_with_bound(pt2d[0],idedge[0], 4, sz2d,0,iy0<<2|isg0.z&1, pt2d[1],idedge[1]); parea->npt = crop_polygon_with_bound(pt2d[1],idedge[1],parea->npt, sz2d,1,ix0<<2|isg0.z<<1&2, pt2d[0],idedge[0]); parea->npt = crop_polygon_with_bound(pt2d[0],idedge[0],parea->npt, sz2d,2,iy0<<2|2|isg0.z&1, pt2d[1],idedge[1]); parea->npt = crop_polygon_with_bound(pt2d[1],idedge[1],parea->npt, sz2d,3,ix0<<2|1|isg0.z<<1&2, pt2d[0],idedge[0]); for(i=0;inpt;i++) { iprev = i-1&-i>>31 | parea->npt-1&i-1>>31; pt3d[ix0] = pt2d[0][i].x; pt3d[iy0] = pt2d[0][i].y; parea->pt[i] = pt3d*pbox[0]->Basis + pbox[0]->center + pmode->dir*pcontact->t; parea->piPrim[0][i] = parea->piPrim[1][i] = 0; if ((idedge[0][iprev]^idedge[0][i])>>4) { parea->piFeature[idedge[0][iprev]>>4][i] = 0x20 | idedge[0][iprev]&0xF; parea->piFeature[idedge[0][i]>>4][i] = 0x20 | idedge[0][i]&0xF; } else { parea->piFeature[idedge[0][i]>>4][i] = vtx_from_edges(idedge[0][iprev]&0xF,idedge[0][i]&0xF); parea->piFeature[idedge[0][i]>>4^1][i] = iface[idedge[0][i]>>4^1]; } } if (parea->npt) goto gotarea; } } // check if the contact is face-edge, and build area if it is for(int idx=0;idx<9;idx++) { idir1 = isneg(2-idx)+isneg(5-idx); iz = idx-idir1*3; // idir1 = idx/3; iz = idx%3 if (fabs_tpl(axes[idir1][iz])<(real)0.02) { // check all near-zeros in Basis21 buffer ix1 = inc_mod3[idir1]; iy1 = dec_mod3[idir1]; for(ibox=0;ibox<2;ibox++) { if (fabs_tpl(fabs_tpl(center[ibox][iz]-dir[ibox][iz]*pcontact->t) - (pbox[ibox]->size[iz]+fabs_tpl(axes[ix1][iz])*pbox[ibox^1]->size[ix1]+fabs_tpl(axes[iy1][iz])*pbox[ibox^1]->size[iy1])) < min(pbox[ibox]->size[iz],pbox[1]->size[ix1]+pbox[ibox^1]->size[iy1])*0.01f) { // confirmed face(ibox.iz) - edge(ibox^1.idir1) contact vector2df pt2d[2],sz2d,axisx2d,axisy2d; vectorf ptc,pt3d; ptc = center[ibox]-dir[ibox]*pcontact->t; ix0 = inc_mod3[iz]; iy0 = dec_mod3[iz]; isg0.z = sgnnz(ptc[iz]); parea->type = geom_contact_area::polyline; parea->n1.zero()[iz] = isg0.z*((ibox<<1)-1); parea->n1 = parea->n1*pbox[ibox]->Basis; axisx2d.set(axes[ix1][ix0],axes[ix1][iy0]) *= (isg1.x = sgnnz(axes[ix1][iz])*-isg0.z); axisy2d.set(axes[iy1][ix0],axes[iy1][iy0]) *= (isg1.y = sgnnz(axes[iy1][iz])*-isg0.z); pt2d[0].set(ptc[ix0],ptc[iy0]) += axisx2d*pbox[ibox^1]->size[ix1]+axisy2d*pbox[ibox^1]->size[iy1]; sz2d.set(axes[idir1][ix0],axes[idir1][iy0]) *= pbox[ibox^1]->size[idir1]; pt2d[1] = pt2d[0]+sz2d; pt2d[0] -= sz2d; parea->piFeature[ibox][0] = parea->piFeature[ibox][1] = 0x40 | iz<<1 | 1+isg0.z>>1; parea->piFeature[ibox^1][0] = (isg1.x+1>>1)<>1)<piFeature[ibox^1][1] = parea->piFeature[ibox^1][0] | 1<>1 | 1+isg1.y; crop_segment_with_bound(pt2d,pbox[ibox]->size[ix0],0, parea->piFeature[ibox],parea->piFeature[ibox^1], 0x20|iy0<<2|1+isg0.z>>1,iedge1); crop_segment_with_bound(pt2d,pbox[ibox]->size[iy0],1, parea->piFeature[ibox],parea->piFeature[ibox^1], 0x20|ix0<<2|1+isg0.z,iedge1); if ((pt2d[0]-pt2d[1]).len2()>sqr(pmode->minPtDist)) { pt3d[iz] = pbox[ibox]->size[iz]*isg0.z; for(i=0;i<2;i++) { pt3d[ix0] = pt2d[i].x; pt3d[iy0] = pt2d[i].y; parea->pt[i] = pt3d*pbox[ibox]->Basis + pbox[ibox]->center + pmode->dir*(pcontact->t*(ibox^1)); parea->piPrim[0][i] = parea->piPrim[1][i] = 0; } parea->npt = 2; } if (parea->npt) goto gotarea; idir0=idir1;idir1=iz;iz=idir0; ix1 = inc_mod3[idir1]; iy1 = dec_mod3[idir1]; } Basis21.Transpose(); } } } gotarea: if (parea->npt) { pcontact->pt = parea->pt[0]; pcontact->iFeature[0] = parea->piFeature[0][0]; pcontact->iFeature[1] = parea->piFeature[1][0]; } } return 1; } int box_cylinder_lin_unprojection(unprojection_mode *pmode, const box *pbox,int iFeature1, const cylinder *pcyl,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr center,axis,dir,size=pbox->size,c,n,pt,vec0,vec1,dir_best(zero); real nlen,dlen,r=pcyl->r,hh=pcyl->hh,ka,kb,kc,kd,e=pmode->minPtDist; vector2d pt2d; quotient tmin(pmode->tmax,1),t,t0,tmax; int i,j,idbest=-1,bFindMinUnproj,bBest,bSeparated,icap,bContact; vectori isg,ic; center = pbox->Basis*(pcyl->center-pbox->center); axis = pbox->Basis*pcyl->axis; dir = pbox->Basis*pmode->dir; bFindMinUnproj = iszero(pmode->dir.len2()); // box.face - cyl.capedge for(ic.z=0;ic.z<3;ic.z++) { if (bFindMinUnproj) dir.zero()[ic.z] = -sgnnz(center[ic.z]); isg.z = -sgnnz(dir[ic.z]); icap = sgnnz(axis[ic.z])*-isg.z; c.z = center[ic.z]+axis[ic.z]*(hh*icap); (n = axis*axis[ic.z])[ic.z]-=1; n*=isg.z; nlen = max((real)0.001,n.len()); t.set(((c.z-size[ic.z]*isg.z)*nlen+n[ic.z]*r)*-isg.z, fabs_tpl(dir[ic.z])*nlen); bBest = isneg(-t.x) & isneg(t-tmin); UPDATE_IDBEST(tmin,ic.z<<2|isg.z+1|icap+1>>1); dir_best = dir_best*(bBest^1) + dir*bBest; } // box.vertex - cyl.cap if (bFindMinUnproj) dir = axis*-sgnnz(center*axis); icap = sgnnz(dir*axis); t.set((center*axis)*icap+hh+axis.abs()*size, fabs_tpl(dir*axis)); bBest = isneg(-t.x) & isneg(t-tmin); UPDATE_IDBEST(tmin,0x20 | icap+1>>1); dir_best = dir_best*(bBest^1) + dir*bBest; // box.vertex - cyl.side for(i=0;i<8;i++) { isg.x=(i>>1&2)-1; isg.y=(i&2)-1; isg.z=(i<<1&2)-1; pt.Set(size.x*isg.x, size.y*isg.y, size.z*isg.z) -= center; if (bFindMinUnproj) { dir = (pt-axis*(pt*axis)).normalized(); dir *= -sgnnz(dir*center); } vec0 = pt^axis; vec1 = dir^axis; ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-r*r; kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { t.set(-kb+sqrt_tpl(kd), ka); pt = pt*t.y + dir*t.x; // cyl.center->contact point vector pt -= axis*(pt*axis); // cyl.axis->contact point vector bSeparated = isneg(pt.x*isg.x-e) & isneg(pt.y*isg.y-e) & isneg(pt.z*isg.z-e); // no outgoing box edge points 'inside' the cylinder bBest = isneg(t-tmin) & bSeparated; UPDATE_IDBEST(tmin,0x40 | i); dir_best = dir_best*(bBest^1) + dir*bBest; } } // box.edge - cyl.side for(ic.z=0;ic.z<3;ic.z++) { ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; n = -cross_with_ort(axis,ic.z); nlen = n.len(); if (bFindMinUnproj) { dir = n*-sgnnz(n*center); dlen = nlen; } else dlen = 1; isg.z = sgnnz(dir*n); isg.x = -sgnnz(n[ic.x])*isg.z; isg.y = -sgnnz(n[ic.y])*isg.z; pt[ic.z]=0; pt[ic.x]=size[ic.x]*isg.x; pt[ic.y]=size[ic.y]*isg.y; t.set((r*nlen+((center-pt)*n)*isg.z)*dlen, fabs_tpl(dir*n)); bBest = isneg(-t.x) & isneg(t-tmin); UPDATE_IDBEST(tmin,0x60 | ic.z<<3 | isg.z+1<<1 | isg.y+1 | isg.x+1>>1); dir_best = dir_best*(bBest^1) + dir*bBest; } // box.edge - cyl.capedge if (bFindMinUnproj) dir = dir_best; // finding real minimizing edge-capedge unprojection involves solving 4th order eq., not worth the effort here tmax.set(0,1); j=idbest; // for edge-capedge search for max contacting t, since we don't calculate mindir anyway for(ic.z=0;ic.z<3;ic.z++) if (fabs_tpl(axis[ic.z])>0.01) { ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; for(i=0;i<8;i++) { icap=(i>>1&2)-1; isg.x=(i<<1&2)-1; isg.y=(i&2)-1; pt[ic.z]=0; pt[ic.x]=size[ic.x]*isg.x; pt[ic.y]=size[ic.y]*isg.y; pt -= center+axis*(hh*icap); // pt = edge center - cyl.cap center vec1 = axis^cross_with_ort(dir,ic.z); vec0 = axis^cross_with_ort(pt,ic.z); ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-sqr(r*axis[ic.z]); kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { t.set(-kb+sqrt_tpl(kd), ka); pt = pt*t.y+dir*t.x; t0.set(-(pt*axis), axis[ic.z]*t.y).fixsign(); bContact = isneg(fabs_tpl(t0.x)-size[ic.z]*fabs_tpl(t0.y)); (pt *= fabs_tpl(axis[ic.z]))[ic.z] += t0.x; // pt = pt+dir*t+edge*t0-c bSeparated = isneg(((pt^axis)*cross_with_ort(axis,ic.z))*axis[ic.z]*-icap); // intersection along the edge lies outside the cylinder pt = axis^pt; // tangential direction at cap point, neither positive nor flipped should point inside the box bSeparated &= isneg(pt[ic.x]*isg.x) ^ isneg(pt[ic.y]*isg.y); bBest = isneg(tmax-t) & bContact & bSeparated; UPDATE_IDBEST(tmax,0x80 | ic.z<<3|icap+1<<1|isg.y+1|isg.x+1>>1); } } } i=idbest; idbest=j; t=tmax; bBest = (iszero(t.x)^1) & isneg(t-tmin); UPDATE_IDBEST(tmin,i); if (idbest==-1) return 0; if (bFindMinUnproj) { dir = dir_best; if (fabs_tpl(dir.len2()-(real)1.0)>(real)0.001) dir.normalize(); pmode->dir = dir*pbox->Basis; } switch (idbest&0xE0) { case 0x00: // box.face - cyl.capedge ic.z=idbest>>2; isg.z=(idbest&2)-1; icap=(idbest<<1&2)-1; (n = axis*axis[ic.z])[ic.z]-=1; n*=isg.z; nlen = max((real)0.001,n.len()); pcontact->t = tmin.val(); pt = center+axis*(hh*icap)+n*(r/nlen); ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; bContact = isneg(fabs_tpl(pt[ic.x]-dir[ic.x]*pcontact->t)-size[ic.x]-e) & isneg(fabs_tpl(pt[ic.y]-dir[ic.y]*pcontact->t)-size[ic.y]-e); pcontact->pt = pt*pbox->Basis + pbox->center; pcontact->n = pbox->Basis.GetRow(ic.z)*isg.z; pcontact->iFeature[0] = 0x40 | ic.z<<1 | isg.z+1>>1; pcontact->iFeature[1] = 0x20 | icap+1>>1; break; case 0x20: // box.vertex - cyl.cap icap = (idbest<<1&2)-1; isg.Set(-sgnnz(dir.x),-sgnnz(dir.y),-sgnnz(dir.z)); pcontact->t = tmin.val(); pt = vectorr(size.x*isg.x,size.y*isg.y,size.z*isg.z) + dir*pcontact->t; bContact = isneg((pt-center-axis*(hh*icap)).len2()-sqr(r+e)); pcontact->pt = pt*pbox->Basis + pbox->center; pcontact->n = pcyl->axis*(hh*-icap); pcontact->iFeature[0] = isg.z+1<<1|isg.y+1|isg.x+1>>1; pcontact->iFeature[1] = 0x41+(icap+1>>1); break; case 0x40: // box.vertex - cyl.side isg.Set((idbest>>1&2)-1,(idbest&2)-1,(idbest<<1&2)-1); pt.Set(size.x*isg.x, size.y*isg.y, size.z*isg.z); pcontact->t = tmin.val(); pt += dir*pcontact->t; bContact = isneg(fabs_tpl((pt-center)*axis)-hh-e); pcontact->pt = pt*pbox->Basis + pbox->center; pcontact->n = pcyl->center-pcontact->pt; pcontact->n -= pcyl->axis*(pcontact->n*pcyl->axis); pcontact->iFeature[0] = isg.z+1<<1|isg.y+1|isg.x+1>>1; pcontact->iFeature[1] = 0x40; break; case 0x60: // box.edge - cyl.side ic.z = idbest>>3&3; ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; isg.Set((idbest<<1&2)-1,(idbest&2)-1,(idbest>>1&2)-1); n = -cross_with_ort(axis,ic.z); pt[ic.z]=0; pt[ic.x]=size[ic.x]*isg.x; pt[ic.y]=size[ic.y]*isg.y; t0.set(((center-pt)*tmin.y-dir*tmin.x ^ axis)*n, n.len2()*tmin.y); t0.y=(real)1.0/t0.y; pt[ic.z] = (t0.x*=t0.y); pt += dir*pcontact->t; bContact = isneg(fabs_tpl(t0.x)-size[ic.z]-e) & isneg(fabs_tpl((pt-center)*axis)-hh-e); if (!bContact) pt[ic.z]=t0.y=0; pcontact->t = tmin.x*t0.y*n.len2(); pcontact->pt = pt*pbox->Basis + pbox->center; n = pcyl->center-pcontact->pt; pcontact->n = n-pcyl->axis*(n*pcyl->axis); pcontact->iFeature[0] = 0x20 | ic.z<<2|isg.y+1|isg.x+1>>1; pcontact->iFeature[1] = 0x40; break; case 0x80: // box.edge - cyl.capedge ic.z = idbest>>3&3; ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; icap=(idbest>>1&2)-1; isg.y=(idbest&2)-1; isg.x=(idbest<<1&2)-1; pt[ic.z]=0; pt[ic.x]=size[ic.x]*isg.x; pt[ic.y]=size[ic.y]*isg.y; c = center+axis*(hh*icap); t0.set(((c-pt)*tmin.y-dir*tmin.x)*axis, axis[ic.z]*tmin.y); t0.y=(real)1.0/t0.y; pt[ic.z] += (t0.x*=t0.y); pcontact->t = tmin.x*t0.y*axis[ic.z]; vec0 = pt; pt += dir*pcontact->t; bContact = isneg(fabs_tpl(t0.x)-size[ic.z]-e); pcontact->pt = pt*pbox->Basis + pbox->center; n = cross_with_ort(pt-c ^ axis,ic.z); pcontact->n = (n*sgnnz(n*vec0))*pbox->Basis; pcontact->iFeature[0] = 0x20 | ic.z<<2|isg.y+1|isg.x+1>>1; pcontact->iFeature[1] = 0x20 | icap+1>>1; } if (!parea && pmode->bCheckContact) { static geom_contact_area dummyArea; static vectorf dummyAreaPt[16]; static int dummyAreaInt[16]; parea = &dummyArea; parea->pt = dummyAreaPt; parea->piPrim[0]=parea->piPrim[1]=parea->piFeature[0]=parea->piFeature[1] = dummyAreaInt; parea->npt = 0; parea->nmaxpt = 16; parea->minedge = 0; } if (parea) { const real tol = (real)0.02; int iCheckEdgeSide = -1; if (fabs_tpl(axis.x)+fabs_tpl(axis.y)+fabs_tpl(axis.z)<(real)1.05) { // axis is close to some coordinate axis ic.z = -1; // find axis coordinate that is close to one bBest = -isneg((real)0.998-fabs_tpl(axis.x)); ic.z = ic.z&~bBest; bBest = -isneg((real)0.998-fabs_tpl(axis.y)); ic.z = ic.z&~bBest|1&bBest; bBest = -isneg((real)0.998-fabs_tpl(axis.z)); ic.z = ic.z&~bBest|2&bBest; if (ic.z>=0) { c = center-dir*pcontact->t+axis*(hh*(icap=sgnnz(axis*dir))); ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; isg.z = -sgnnz(dir[ic.z]); dlen = sqr(max((real)0.0,fabs_tpl(c[ic.x])-size[ic.x]))+sqr(max((real)0.0,fabs_tpl(c[ic.y])-size[ic.y])); // check for box.face-cyl.cap area contact if (fabs_tpl(fabs_tpl(c[ic.z])-size[ic.z])pt[parea->npt] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; parea->piPrim[0][parea->npt] = parea->piPrim[1][parea->npt] = 0; parea->piFeature[0][parea->npt] = 0x40 | ic.z<<1 | isg.z+1>>1; parea->piFeature[1][parea->npt] = 0x20 | icap+1>>1; parea->npt += isneg(fabs_tpl(pt[ic.x])-size[ic.x]) & isneg(fabs_tpl(pt[ic.y])-size[ic.y]); nlen=pt2d.x; pt2d.x=(pt2d.x-pt2d.y)*(sqrt2/2); pt2d.y=(nlen+pt2d.y)*(sqrt2/2); // rotate by 45 degrees } // add circle-rect intersection points for(i=0;i<4;i++) if ((kd = r*r-sqr((pt[ic[i&1]] = size[ic[i&1]]*((i&2)-1))-c[ic[i&1]]))>0) for(j=0,pt[ic[i&1^1]]=c[ic[i&1^1]]-(kd=sqrt_tpl(kd)); j<2; j++,pt[ic[i&1^1]]+=kd*2) if (fabs_tpl(pt[ic[i&1^1]])pt[parea->npt] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; parea->piPrim[0][parea->npt] = parea->piPrim[1][parea->npt] = 0; parea->piFeature[0][parea->npt] = 0x20 | ic[i&1^1]<<2 | isg.z+1>>(i&1^1) | (i&2)>>(i&1); parea->piFeature[1][parea->npt++] = 0x20 | icap+1>>1; } parea->type = geom_contact_area::polygon; goto gotarea; } else // check for box.edge-cyl.side area contact later (if other area contacts fail) iCheckEdgeSide = ic.z; } } for(ic.z=0;ic.z<3;ic.z++) if (fabs_tpl(axis[ic.z])<(real)0.04) { c = center-dir*pcontact->t; ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; isg.z = -sgnnz(dir[ic.z]); vector2df pts2d[2]; // check for box.face-cyl.side area contact if (fabs_tpl(fabs_tpl(c[ic.z])-size[ic.z]-r) < min(size[ic.z],r)*tol) { pts2d[0].set(c[ic.x]-axis[ic.x]*hh, c[ic.y]-axis[ic.y]*hh); pts2d[1].set(c[ic.x]+axis[ic.x]*hh, c[ic.y]+axis[ic.y]*hh); parea->piFeature[1][0] = parea->piFeature[1][1] = 0x40; parea->piFeature[0][0] = parea->piFeature[0][1] = 0x40 | ic.z<<1|isg.z+1>>1; parea->piPrim[0][0]=parea->piPrim[0][1]=parea->piPrim[1][0]=parea->piPrim[1][1] = 0; pt[ic.z] = size[ic.z]*isg.z; crop_segment_with_bound(pts2d,size[ic.x],0, parea->piFeature[0],parea->piFeature[1], 0x20|ic.y<<2|1+isg.z>>1,0x40); crop_segment_with_bound(pts2d,size[ic.y],1, parea->piFeature[0],parea->piFeature[1], 0x20|ic.x<<2|1+isg.z,0x40); if ((pts2d[0]-pts2d[1]).len2()>e*e) { parea->type = geom_contact_area::polyline; for(i=0;i<2;i++) { pt[ic.x] = pts2d[i].x; pt[ic.y] = pts2d[i].y; parea->pt[i] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; } parea->npt = 2; goto gotarea; } } // check for box.edge-cyl.cap area contact c += axis*(hh*(icap=sgnnz(axis*dir))); pt[ic.z] = 0; for(j=0,i=1;i<4;i++) { // find the edge in direction ic.z that is the closest to the cap plane pt[ic.x]=size[ic.x]*((i<<1&2)-1); pt[ic.y]=size[ic.y]*((i&2)-1); t.x=fabs_tpl((pt-c)*axis); pt[ic.x]=size[ic.x]*((j<<1&2)-1); pt[ic.y]=size[ic.y]*((j&2)-1); t.y=fabs_tpl((pt-c)*axis); bBest = isneg(t.x-t.y); j = j&~-bBest | i&-bBest; } isg.x=(j<<1&2)-1; isg.y=(j&2)-1; pt[ic.x]=size[ic.x]*isg.x; pt[ic.y]=size[ic.y]*isg.y; dlen = sqr(c[ic.x]-pt[ic.x])+sqr(c[ic.y]-pt[ic.y]); if (fabs_tpl((c-pt)*axis)-size[ic.z]) { for(i=0;i<2;i++) { pt[ic.z] = min(size[ic.z],max(-size[ic.z],c[ic.z]+dlen*(i*2-1))); parea->pt[i] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; } parea->type = geom_contact_area::polyline; parea->piPrim[0][0]=parea->piPrim[0][1]=parea->piPrim[1][0]=parea->piPrim[1][1] = 0; parea->piFeature[0][0] = parea->piFeature[0][1] = 0x20 | ic.z<<2|isg.y+1|isg.x+1>>1; parea->piFeature[1][0] = parea->piFeature[1][1] = 0x40 | (icap+1>>1)+1; parea->npt = 2; goto gotarea; } } } // check box.edge-cyl.side area contact if ((ic.z=iCheckEdgeSide)>=0) { ic.x = inc_mod3[ic.z]; ic.y = dec_mod3[ic.z]; isg.z = -sgnnz(dir[ic.z]); c = center-dir*pcontact->t; dlen = sqr(max((real)0.0,fabs_tpl(c[ic.x])-size[ic.x]))+sqr(max((real)0.0,fabs_tpl(c[ic.y])-size[ic.y])); if (fabs_tpl(c[ic.z])-size[ic.z]type = geom_contact_area::polyline; c[ic.z] = center[ic.z]-dir[ic.z]*pcontact->t-fabs_tpl(axis[ic.z])*hh; isg.x = sgnnz(c[ic.x]); isg.y = sgnnz(c[ic.y]); pt[ic.x] = size[ic.x]*isg.x; pt[ic.y] = size[ic.y]*isg.y; pt[ic.z] = max(c[ic.z],-size[ic.z]); parea->pt[0] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; pt[ic.z] = min(c[ic.z]+fabs_tpl(axis[ic.z])*hh*2,size[ic.z]); parea->pt[1] = pt*pbox->Basis + pbox->center + pmode->dir*pcontact->t; parea->piFeature[0][0] = parea->piFeature[0][1] = 0x20 | ic.z<<2|isg.y+1|isg.x+1>>1; parea->piFeature[1][0] = parea->piFeature[1][1] = 0x40; parea->piPrim[0][0]=parea->piPrim[0][1]=parea->piPrim[1][0]=parea->piPrim[1][1] = 0; parea->npt = 2; } } gotarea: if (parea->npt) { pcontact->pt = parea->pt[0]; pcontact->iFeature[0] = parea->piFeature[0][0]; pcontact->iFeature[1] = parea->piFeature[1][0]; parea->n1 = -pcontact->n; bContact = 1; } } return bContact|(pmode->bCheckContact^1); } int cylinder_box_lin_unprojection(unprojection_mode *pmode, const cylinder *pcyl,int iFeature1, const box *pbox,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = box_cylinder_lin_unprojection(pmode,pbox,iFeature2,pcyl,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; if (parea) { for(int i=0;inpt;i++) { iFeature=parea->piFeature[0][i]; parea->piFeature[0][i]=parea->piFeature[1][i]; parea->piFeature[1][i]=iFeature; } parea->n1.Flip(); } } pmode->dir.Flip(); return res; } int box_sphere_lin_unprojection(unprojection_mode *pmode, const box *pbox,int iFeature1, const sphere *psph,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr center,size=pbox->size,vec0,vec1,dir,pt; real r=psph->r,ka,kb,kc,kd,dlen; quotient t,tmax(0,1); int i,ix,iy,iz,niz,isum,idbest=-1,bBest,bContact; vectori isg; center = pbox->Basis*(psph->center-pbox->center); dir = pbox->Basis*pmode->dir; pcontact->iFeature[1] = 0x40; if (pmode->dir.len2()==0) { // minimum unprojection dir requested for(iz=isum=niz=0;iz<3;iz++) { i = isneg(size[iz]-fabs_tpl(center[iz])); niz+=i; isum+=i*iz; } if (niz==0) { vectorr csize(fabs_tpl(center.x)*size.y*size.z, fabs_tpl(center.y)*size.x*size.z, fabs_tpl(center.z)*size.x*size.y); niz=1; isum=idxmax3((real*)&csize); } switch (niz) { case 1: iz=isum; isg.z=sgnnz(center[iz]); dir.zero()[iz]=-isg.z; pcontact->t=r-fabs_tpl(center[iz]-size[iz]*isg.z); pcontact->iFeature[0] = 0x40|iz<<1|isg.z+1>>1; break; case 2: iz=3-isum; ix=inc_mod3[iz]; iy=dec_mod3[iz]; isg.x=sgnnz(center[ix]); isg.y=sgnnz(center[iy]); pt[ix]=size[ix]*isg.x; pt[iy]=size[iy]*isg.y; pt[iz]=center[iz]; dlen=(dir=pt-center).len(); pcontact->t=r-dlen; dir/=dlen; pcontact->iFeature[0] = 0x20|iz<<2|isg.y+1|isg.x+1>>1; break; case 3: isg.x=sgnnz(center.x); isg.y=sgnnz(center.y); isg.z=sgnnz(center.z); pt.x=size.x*isg.x; pt.y=size.y*isg.y; pt.z=size.z*isg.z; dlen=(dir=pt-center).len(); pcontact->t=r-dlen; dir/=dlen; pcontact->iFeature[0] = isg.z+1<<1|isg.y+1|isg.z+1>>1; } pmode->dir = dir*pbox->Basis; pcontact->n = -pmode->dir; pcontact->pt = psph->center+pmode->dir*r; return 1; } // box face - sphere for(iz=0;iz<3;iz++) { isg.z = -sgnnz(dir[iz]); ix = inc_mod3[iz]; iy = dec_mod3[iz]; t.set(center[iz]*-isg.z+(size[iz]+r), fabs_tpl(dir[iz])); bContact = isneg(fabs_tpl(center[ix]*t.y-dir[ix]*t.x)-size[ix]*t.y) & isneg(fabs_tpl(center[iy]*t.y-dir[iy]*t.x)-size[iy]*t.y); bBest = isneg(tmax-t) & bContact; UPDATE_IDBEST(tmax,iz<<1|isg.z+1>>1); } // box edge - sphere for(iz=0;iz<3;iz++) { ix = inc_mod3[iz]; iy = dec_mod3[iz]; for(i=0;i<4;i++) { isg.x = (i<<1&2)-1; isg.y = (i&2)-1; pt[ix]=size[ix]*isg.x; pt[iy]=size[iy]*isg.y; pt[iz]=0; vec0 = cross_with_ort(center-pt,iz); vec1 = cross_with_ort(-dir,iz); ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-r*r; kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { t.set(-kb+sqrt_tpl(kd), ka); bContact = isneg(fabs_tpl(center[iz]*t.y-dir[iz]*t.x)-size[iz]*t.y); bBest = isneg(tmax-t) & bContact; UPDATE_IDBEST(tmax,0x10 | iz<<2|isg.y+1|isg.x+1>>1); } } } // box vertex - sphere for(i=0;i<8;i++) { isg.x=(i<<1&2)-1; isg.y=(i&2)-1; isg.z=(i>>1&2)-1; pt(size.x*isg.x, size.y*isg.y, size.z*isg.z); vec0=center-pt; vec1=-dir; ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-r*r; kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { t.set(-kb+sqrt_tpl(kd), ka); bBest = isneg(tmax-t); UPDATE_IDBEST(tmax,0x20 | isg.z+1<<1|isg.y+1|isg.x+1>>1); } } if (idbest==-1) return 0; pcontact->t = tmax.val(); switch (idbest&0xF0) { case 0x00: // box face - sphere iz=idbest>>1; isg.z=(idbest<<1&2)-1; pcontact->n = pbox->Basis.GetRow(iz)*isg.z; pcontact->iFeature[0] = 0x40 | iz<<1|isg.z+1>>1; break; case 0x10: // box edge - sphere iz=idbest>>2&3; ix=inc_mod3[iz]; iy=dec_mod3[iz]; isg.y=(idbest&2)-1; isg.x=(idbest<<1&2)-1; pt[ix]=size[ix]*isg.x; pt[iy]=size[iy]*isg.y; pt[iz]=center[iz]-dir[iz]*pcontact->t; pcontact->n = (center-dir*pcontact->t-pt).normalized()*pbox->Basis; pcontact->iFeature[0] = 0x20 | idbest&0xF; break; default: // box vertex - sphere isg.z=(idbest>>1&2)-1; isg.y=(idbest&2)-1; isg.x=(idbest<<1&2)-1; pt(size.x*isg.x, size.y*isg.y, size.z*isg.z); pcontact->n = (center-dir*pcontact->t-pt).normalized()*pbox->Basis; pcontact->iFeature[0] = idbest&0xF; } pcontact->pt = psph->center-pcontact->n*r; return 1; } int sphere_box_lin_unprojection(unprojection_mode *pmode, const sphere *psph,int iFeature1, const box *pbox,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = box_sphere_lin_unprojection(pmode,pbox,iFeature2,psph,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int cylinder_sphere_lin_unprojection(unprojection_mode *pmode, const cylinder *pcyl,int iFeature1, const sphere *psph,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr axis,center,dir,vec0,vec1,c,pt2d; real rs=psph->r,rc=pcyl->r,hh=pcyl->hh,ka,kb,kc,kd,roots[4]; int i,icap,bContact,bBest,idbest=-1; quotient t,tmax(0,1); axis = pcyl->axis; center = psph->center-pcyl->center; dir = pmode->dir; pcontact->iFeature[1] = 0x40; if (pmode->dir.len2()==0) { // minimum unprojection dir requested real h,r2d,dist[2]; int bOutside[2]; h = center*axis; r2d = (center-axis*h).len(); bOutside[0] = isneg(dist[0] = hh-fabs_tpl(h)); bOutside[1] = isneg(dist[1] = rc-r2d); if (bOutside[0]+bOutside[1]==0) bOutside[1] = (bOutside[0] = isneg(dist[0]-dist[1]))^1; switch (bOutside[0]|bOutside[1]<<1) { case 1: // unproject to cap icap = sgnnz(h); pmode->dir = axis*-icap; pcontact->t = rs+dist[0]; pcontact->iFeature[0] = 0x41+(icap+1>>1); break; case 2: // unproject to side pmode->dir = (-center+axis*h)/r2d; pcontact->t=rs+dist[1]; pcontact->iFeature[0] = 0x40; break; case 3: // unproject to cap edge icap = sgnnz(h); pmode->dir = axis*dist[0]*icap+(center-axis*h)*(dist[1]/r2d); pcontact->t=pmode->dir.len(); pmode->dir/=pcontact->t; pcontact->t=rs-pcontact->t; pcontact->iFeature[0] = 0x20|icap+1>>1; } pcontact->n = -pmode->dir; pcontact->pt = psph->center-pcontact->n*psph->r; return 1; } // sphere - cyl.cap face icap = -sgnnz(dir*axis); t.set((rs+hh)-(center*axis)*icap, fabs_tpl(dir*axis)); bContact = isneg((center*t.y-dir*t.x^axis).len2()-sqr(rc*t.y)) & isneg(t.x-pmode->tmax*t.y); bBest = isneg(tmax-t) & bContact; UPDATE_IDBEST(tmax,icap+1>>1); // sphere - cyl.side vec1 = -dir^axis; vec0 = center^axis; ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-sqr(rc+rs); kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { t.set(-kb+sqrt_tpl(kd), ka); bContact = isneg(fabs_tpl((center*t.y-dir*t.x)*axis)-hh) & isneg(t.x-pmode->tmax*t.y); bBest = isneg(tmax-t) & bContact; UPDATE_IDBEST(tmax,0x10); } // sphere - cyl.cap edge for(icap=-1; icap<=1; icap+=2) { c = center-axis*(hh*icap); polynomial_tpl p = (P2(-(dir*dir))+P1(2*(c*dir))+rc*rc+rs*rs-c*c).sqr()- (P2(-sqr(dir*axis))+P1(2*(c*axis)*(dir*axis))+rs*rs-sqr(c*axis))*(4*rc*rc); for(i=p.findroots(0,pmode->tmax,roots)-1;i>=0;i--) { t.set(roots[i],1); bContact = isneg(rs*rs+rc*rc-(c-dir*t.x).len2()); // checks for phantom root bBest = isneg(tmax-t) & bContact; UPDATE_IDBEST(tmax,0x20); /*if (bBest) { prim_inters inters; int cylinder_sphere_intersection(const cylinder *pcyl, const sphere *psphere, prim_inters *pinters); if (!cylinder_sphere_intersection(pcyl,psph,&inters)) bBest = 0; }*/ } /*real ht,r2dt,distt,tt; for(i=p.findroots(-4,4,roots,20)-1;i>=0;i--) { ht = (c-dir*roots[i])*axis; r2dt = sqrt_tpl(rs*rs-ht*ht); distt = (c-dir*roots[i]).len2(); tt = p.eval(roots[i]); } i = p.nroots(-4,4);*/ } if (idbest<0) return 0; pcontact->t = tmax.val(); icap = (idbest<<1&2)-1; switch (idbest&0xF0) { case 0x00: // sphere - cyl.cap face pcontact->n = pcyl->axis*icap; pcontact->iFeature[0] = 0x40|(icap+1>>1)+1; break; case 0x10: // sphere - cyl.side pcontact->n = center-dir*pcontact->t; (pcontact->n -= axis*(axis*pcontact->n)).normalize(); pcontact->iFeature[0] = 0x40; break; case 0x20: // sphere - cyl.cap edge c = center-axis*(hh*icap)-dir*pcontact->t; pt2d = (c-axis*(c*axis)).normalized()*rc; pcontact->n = (c-pt2d).normalized(); pcontact->iFeature[0] = 0x20|icap+1>>1; } pcontact->pt = psph->center-pcontact->n*psph->r; return 1; } int sphere_cylinder_lin_unprojection(unprojection_mode *pmode, const sphere *psph,int iFeature1, const cylinder *pcyl,int iFeature2, contact *pcontact, geom_contact_area *parea) { pmode->dir.Flip(); int res = cylinder_sphere_lin_unprojection(pmode,pcyl,iFeature2,psph,iFeature1,pcontact,parea); if (res) { pcontact->pt -= pmode->dir*pcontact->t; pcontact->n.Flip(); int iFeature = pcontact->iFeature[0]; pcontact->iFeature[0]=pcontact->iFeature[1]; pcontact->iFeature[1]=iFeature; } pmode->dir.Flip(); return res; } int sphere_sphere_lin_unprojection(unprojection_mode *pmode, const sphere *psph1,int iFeature1, const sphere *psph2,int iFeature2, contact *pcontact, geom_contact_area *parea) { vectorr dc,vec0,vec1; real ka,kb,kc,kd,dclen; dc = psph1->center-psph2->center; pcontact->iFeature[0] = pcontact->iFeature[1] = 0x40; if (pmode->dir.len2()==0) { // minimum unprojection dir requested dclen = dc.len(); pmode->dir = dc/dclen; pcontact->t = psph1->r+psph2->r-dclen; pcontact->n = -pmode->dir; pcontact->pt = psph2->center-pcontact->n*psph2->r; return 1; } vec1 = pmode->dir; vec0 = dc; ka=vec1*vec1; kb=vec0*vec1; kc=vec0*vec0-sqr(psph1->r+psph2->r); kd=kb*kb-ka*kc; if (isnonneg(kd) & (isneg(kb)|isneg(kb*kb-kd))) { pcontact->t = (-kb+sqrt_tpl(kd))/ka; pcontact->n = (psph2->center-(psph1->center+pmode->dir*pcontact->t)).normalized(); pcontact->pt = psph2->center-pcontact->n*psph2->r; return 1; } return 0; } int cyl_cyl_lin_unprojection(unprojection_mode *pmode, const cylinder *pcyl1,int iFeature1, const cylinder *pcyl2,int iFeature2, contact *pcontact, geom_contact_area *parea) { /*box abox; abox.center = pcyl2->center; abox.size(pcyl2->r,pcyl2->r,pcyl2->hh); abox.bOriented = 1; abox.Basis.SetRow(2,pcyl2->axis); vectorf axisx(0,0,1); axisx -= pcyl2->axis*(pcyl2->axis*axisx); if (axisx.len2()axis); abox.Basis.SetRow(0,axisx.normalize()); abox.Basis.SetRow(1,pcyl2->axis^axisx); return cylinder_box_lin_unprojection(pmode, pcyl1,iFeature1, &abox,-1, pcontact,parea);*/ const cylinder *pcyl[2] = { pcyl1,pcyl2 }; vectorr axis[2],center[2],dir,ptc,dp,dc,n,axisx,axisy,u,l,a1,dir_best(zero); vector2d c2d,dir2d,pt2d[4],ptbest2d; quotient tmax(0,1),t,t0,t1; real cosa,sina,rsina,a,b,c,d,roots[4],nlen,denom,r0,r1,dist,tbound[2]; int i,j,j1,bContact,bBest,icyl,nroots,idbest=-1,iter,bSeparated,bFindMinUnproj,bNoContact=0; vectorf ncap[2]; float ncaplen[2]; if (bFindMinUnproj = (pmode->dir.len2()<0.5f)) tmax.x = pmode->tmax; cosa = pcyl[0]->axis*pcyl[1]->axis; sina = (axisx = pcyl[0]->axis^pcyl[1]->axis).len(); axisx = sina>0.001f ? (vectorr)(axisx*(rsina=1/sina)) : (vectorr)(pcyl[0]->axis.orthogonal().normalized()); // cap edge - cap face for(icyl=0;icyl<2;icyl++) { // icyl provides cap edge, icyl^1 - cap face if (bFindMinUnproj) dir = axis[1] = pcyl[icyl^1]->axis*sgnnz(pcyl[icyl^1]->axis*(pcyl[icyl]->center-pcyl[icyl^1]->center)); else { axis[1] = pcyl[icyl^1]->axis*(sgnnz(pcyl[icyl^1]->axis*pmode->dir)*(1-icyl*2)); dir = pmode->dir; } dir *= 1-icyl*2; if (fabs_tpl(a = pcyl[icyl]->axis*axis[1]) < 0.001f) a = pcyl[icyl]->axis*(pcyl[icyl]->center-pcyl[icyl^1]->center); axis[0] = pcyl[icyl]->axis*-sgnnz(a); center[0] = axis[0]*pcyl[icyl]->hh + pcyl[icyl]->center; center[1] = axis[1]*pcyl[icyl^1]->hh + pcyl[icyl^1]->center; n = axis[0]*(axis[0]*axis[1]) - axis[1]; if (n.len2()>0.0001f) nlen = n.len(); else { // in case of degenerate n (ņo-axis cylinders) mask it to point to center, not to plane n = center[1]-center[0]; n -= pcyl[icyl]->axis*(n*pcyl[icyl]->axis); if (n.len2()>0.0001f) nlen = n.len(); else { n.zero(); nlen=1; } } ncap[icyl] = n; ncaplen[icyl] = nlen; ptc = center[0]*nlen + n*pcyl[icyl]->r; t.set((center[1]*nlen-ptc)*axis[1], (axis[1]*dir)*(1-icyl*2)*nlen).fixsign(); ptc = ptc*t.y + dir*(t.x*(1-icyl*2)*nlen); bContact = isneg((ptc - center[1]*(nlen*t.y)).len2() - sqr(pcyl[1]->r*nlen*t.y)); if (bFindMinUnproj) { bBest = isneg(t-tmax) & isneg(min(pcyl[0]->r,pcyl[1]->r)*-0.002f-t.x); dir_best = dir_best*(bBest^1) + dir*bBest; bNoContact = bNoContact&(bBest^1) | (bContact^1)&bBest; } else bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,icyl); } // cap edge - side for(icyl=0; icyl<2; icyl++) { // icyl provides edge, icyl^1 - side axisy=pcyl[icyl^1]->axis^axisx; r0=pcyl[icyl]->r; r1=pcyl[icyl^1]->r; dir=pmode->dir*(icyl*2-1); for(i=-1; i<=1; i+=2) { dc = pcyl[icyl^1]->center - pcyl[icyl]->center - pcyl[icyl]->axis*(pcyl[icyl]->hh*i); c2d.set(dc*axisx, dc*axisy); if (bFindMinUnproj) { if (sina>0.005f) { if (c2d.y*i*(1-icyl*2)<0) continue; } else if (dc.len2()>(dc+pcyl[icyl]->axis*(pcyl[icyl]->hh*i*2)).len2()) continue; vector2di sg(sgnnz(c2d.x),sgnnz(c2d.y)); pt2d[2].set(r0*sg.x, r0*sg.y*fabs_tpl(cosa)); pt2d[3].set(sg.x*fabs_tpl(cosa), sg.y); j1 = 1-sg.x*sg.y>>1; int idx[2] = {0,SINCOSTABSZ}; do { iter = idx[0]+idx[1]>>1; pt2d[0].set(g_costab[iter]*pt2d[2].x, g_sintab[iter]*pt2d[2].y); // point on ellipse pt2d[1].set(g_costab[iter]*pt2d[3].x, g_sintab[iter]*pt2d[3].y); // ellipse normal idx[isneg(pt2d[1]^c2d-pt2d[0])^j1] = iter; } while(idx[1]-idx[0]>1); dist = (pt2d[1] = pt2d[0]-c2d).len2(); if (pt2d[1]*pt2d[0]<0 && distaxis^axisx)*(pt2d[0].y*sgnnz(cosa)); bNoContact = isneg(pcyl[icyl^1]->hh-fabs_tpl(dp*pcyl[icyl^1]->axis)); tmax = r1-dist; dp -= pcyl[icyl^1]->axis*(dp*pcyl[icyl^1]->axis); dir_best = dp.normalized()*(icyl*2-1); idbest = 0x40 | icyl*2 | i+1>>1; } } else { int bBoundTouched = 0; dir2d.set(dir*axisx, dir*axisy); if (pmode->tmaxr,pcyl[1]->r) && fabs_tpl(cosa)>0.003f) { tbound[0] = 0; tbound[1] = pmode->tmax; } else { if (fabs_tpl(cosa)>0.003f) { // find t0,t1 where the circle enters the inner and outer boundaries respectively denom = 1/sqrt_tpl(sqr(dir2d.y)+sqr(dir2d.x*cosa)); // pt2d[0] - dir-ellipse touch point pt2d[0].set(dir2d.y,-dir2d.x*sqr(cosa)) *= (denom*r0); pt2d[0] *= sgnnz((pt2d[0]^dir2d)*(c2d^dir2d)); pt2d[1] = dir2d*(denom*r0*fabs_tpl(cosa)); // pt2d[1] - dir-ellipse intersection point denom = 1/sqrt_tpl(sqr(dir2d.x)+sqr(dir2d.y*cosa)); // pt2d[2] - dir.rot90cw()-ellipse touch point pt2d[2].set(dir2d.x,dir2d.y*sqr(cosa)) *= (denom*r0); pt2d[2] *= sgnnz(dir2d*pt2d[2]); denom = 1/dir2d.len2(); pt2d[2] = dir2d*((dir2d*pt2d[2])*denom); pt2d[3] = dir2d.rot90cw()*((dir2d.rot90cw()*pt2d[0])*denom)+pt2d[2]; j = 0; } else { pt2d[2].set(-r0,0); pt2d[3].set(r0,0); j = 1; } for(; j<2; j++) { t0.set(0,1); // circle-line touch point (additionally check for contact) vector2d l = pt2d[j*2+1]-pt2d[j*2]; t.set(r1*l.len()-(c2d-pt2d[j*2]^l)*sgn(dir2d^l), fabs_tpl(dir2d^l)); if (inrange(((c2d-pt2d[j*2])*t.y+dir2d*t.x)*l, (real)0,l.len2()*t.y) && t>t0) if (ttmax) { t0 = t; bBoundTouched = 1; } else t0 = pmode->tmax; // circle-line ends touch points for(j1=0; j1<2; j1++) { a = dir2d.len2(); b = (c2d-pt2d[j*2+j1])*dir2d; c = (c2d-pt2d[j*2+j1]).len2()-r1*r1; d = b*b-a*c; denom = a*t0.x+b*t0.y; if (d>0 && (denom<0 || d*sqr(t0.y)>sqr(denom))) { denom = a*pmode->tmax+b; if (denom>0 && dtmax+b)) { t0.set(-b+sqrt_tpl(d),a); bBoundTouched = 1; } else t0.set(pmode->tmax,1); } } tbound[j] = (t0.y==1 ? t0.x : t0.x/t0.y); } } if (fabs_tpl(cosa)>0.003f) { real a0=r0*r0*(1+sqr(cosa))+r1*r1, b0=sqr(r0*r0*cosa)+sqr(r1*r0)*(1+sqr(cosa)), cx2,cy2,p,q,Q,Q0,Q1; #define calcQ \ cx2 = sqr(c2d.x+dir2d.x*t.x); cy2 = sqr(c2d.y+dir2d.y*t.x); \ a = a0-cx2-cy2; b = b0-sqr(r0)*(cx2*sqr(cosa)+cy2); \ p = b-a*a*(1.0f/3); q = a*(b*(1.0f/6)-a*a*(1.0f/27))-c*0.5f; \ Q = cube(p)*(1.0f/27)+sqr(q) c = sqr(r1*r0*r0*cosa); // check Q signs on both ends, if they are the same - skip the test t.x = tbound[0]; calcQ; Q0 = Q; t.x = tbound[1]; calcQ; Q1 = Q; if (Q0*Q1<0) { iter=0; do { t.x = (tbound[0]+tbound[1])*0.5f; calcQ; tbound[isneg(Q)] = t.x; } while(++iter<16); } else continue; } else { if (!bBoundTouched) continue; tbound[0] = tbound[1]; } t.set((tbound[0]+tbound[1])*0.5f,1); c2d += dir2d*t.x; vector2di sg(sgnnz(c2d.x),sgnnz(c2d.y)); pt2d[2].set(r0*sg.x, r0*sg.y*fabs_tpl(cosa)); pt2d[3].set(sg.x*sqr(cosa), sg.y); j1 = 1-sg.x*sg.y>>1; int idx[2] = {0,SINCOSTABSZ}; do { iter = idx[0]+idx[1]>>1; pt2d[0].set(g_costab[iter]*pt2d[2].x, g_sintab[iter]*pt2d[2].y); // point on ellipse pt2d[1].set(g_costab[iter]*pt2d[3].x, g_sintab[iter]*pt2d[3].y); // ellipse normal idx[isneg(pt2d[1]^c2d-pt2d[0])^j1] = iter; } while(idx[1]-idx[0]>1); pt2d[0].set(g_costab[idx[0]]*sg.x, g_sintab[idx[0]]*sg.y) *= r0; dp = dc+dir*t.x-axisx*pt2d[0].x-(pcyl[icyl]->axis^axisx)*(pt2d[0].y*sgnnz(cosa)); bContact = isneg(fabs_tpl(dp*pcyl[icyl^1]->axis) - pcyl[icyl^1]->hh); bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0x40 | icyl*2 | i+1>>1); ptbest2d = ptbest2d*(bBest^1) + pt2d[0]*bBest; } } } // side - side dc = pcyl2->center-pcyl1->center; if (bFindMinUnproj) dir = axisx*-sgnnz(dc*axisx); else dir = pmode->dir; t.set((dc*axisx)*sgnnz(dir*axisx)-pcyl[0]->r-pcyl[1]->r, fabs_tpl(dir*axisx)); for(i=0; i<2; i++,t.x+=(pcyl[0]->r+pcyl[1]->r)*2) { dp = dc*t.y-dir*t.x; t0.set((dp^pcyl2->axis)*axisx, t.y); t1.set((dp^pcyl1->axis)*axisx, t.y); bContact = isneg(fabs_tpl(t0.x)-t0.y*pcyl1->hh*sina) & isneg(fabs_tpl(t1.x)-t1.y*pcyl2->hh*sina); if (bFindMinUnproj) { bBest = isneg(-t.x) & isneg(t-tmax); dir_best = dir_best*(bBest^1) + dir*bBest; bNoContact = bNoContact&(bBest^1) | (bContact^1)&bBest; } else bBest = bContact & isneg(tmax-t); UPDATE_IDBEST(tmax,0xC0); } // cap edge - cap edge if (sina>0.001f) { // real edge-edge min unprojection will require iterative solving, so instead use the best unprojection dir found so far dir = bFindMinUnproj ? (vectorr)dir_best : (vectorr)pmode->dir; u = (pcyl[0]->axis*cosa-pcyl[1]->axis)*rsina; l = pcyl[0]->axis^u; a1 = pcyl[1]->axis*rsina; r0 = pcyl[0]->r; r1 = pcyl[1]->r; for(i=-1; i<=1; i+=2) { axis[0] = pcyl[0]->axis*i; center[0] = pcyl[0]->center + axis[0]*pcyl[0]->hh; for(j=-1; j<=1; j+=2) { axis[1] = pcyl[1]->axis*j; center[1] = pcyl[1]->center + axis[1]*pcyl[1]->hh; dc = center[0]-center[1]; polynomial_tpl udc = P1(u*dir)+u*dc, a1dc = P1(a1*dir)+a1*dc, ldc = P1(l*dir)+l*dc; polynomial_tpl dc2 = P2(dir.len2())+P1(dir*dc)*2+dc*dc; polynomial_tpl colls = (r0*r0-a1dc.sqr())*ldc.sqr()*4 - (udc*a1dc*2+dc2+r0*r0-r1*r1).sqr(); if (colls.nroots(0,pmode->tmax)) { nroots = colls.findroots(0,pmode->tmax,roots); if (bFindMinUnproj) { for(nroots--; nroots>=0; nroots--) { t = roots[nroots]; dp = dc+dir*t.x; bSeparated = isneg(sqr(r1)-(dp+u*(a1*dp)).len2()); bSeparated &= isneg(pcyl[1]->hh*0.995f-fabs_tpl((center[0]+dir*t.x-pcyl[1]->center)*pcyl[1]->axis)); if (bSeparated) break; } bBest = (bNoContact | isneg(t-tmax)) & bSeparated; dir_best = dir_best*(bBest^1) + dir*bBest; bNoContact &= bBest^1; UPDATE_IDBEST(tmax,0x80 | i+1 | j+1>>1); } else if (nroots) { t = roots[nroots-1]; bBest = isneg(tmax-t); UPDATE_IDBEST(tmax,0x80 | i+1 | j+1>>1); } } } } } if (idbest==-1 || bNoContact && tmax>min(pcyl[0]->r,pcyl[1]->r)*0.1f) return 0; if (bFindMinUnproj) pmode->dir = dir_best.normalized(); switch (idbest & 0xC0) { case 0: // cap edge - cap face icyl = idbest; axis[1] = pcyl[icyl^1]->axis*(sgnnz(pcyl[icyl^1]->axis*pmode->dir)*(1-icyl*2)); if (fabs_tpl(a = pcyl[icyl]->axis*axis[1]) < 0.01f) a = pcyl[icyl]->axis*(pcyl[icyl]->center-pcyl[icyl^1]->center); axis[0] = pcyl[icyl]->axis*-sgnnz(a); center[0] = axis[0]*pcyl[icyl]->hh + pcyl[icyl]->center; pcontact->t = tmax.val(); pcontact->pt = center[0] + ncap[icyl]*(pcyl[icyl]->r/ncaplen[icyl]) + pmode->dir*(pcontact->t*(icyl^1)); pcontact->n = axis[1]*(icyl*2-1); pcontact->iFeature[icyl] = 0x20 | isnonneg(pcyl[icyl]->axis*axis[0]); pcontact->iFeature[icyl^1] = 0x40 | isnonneg(pcyl[icyl^1]->axis*axis[1]); break; case 0x40: // cap edge - side icyl = idbest>>1&1; i = (idbest*2&2)-1; pcontact->t = tmax.val(); pcontact->pt = pcyl[icyl]->center + pcyl[icyl]->axis*(pcyl[icyl]->hh*i) + pmode->dir*(pcontact->t*(1-icyl)) + axisx*ptbest2d.x + (pcyl[icyl]->axis^axisx)*(ptbest2d.y*sgnnz(cosa)); pcontact->n = ((pcyl[icyl^1]->center+pmode->dir*(pcontact->t*icyl))-pcontact->pt)*(1-icyl*2); pcontact->n -= pcyl[icyl^1]->axis*(pcontact->n*pcyl[icyl^1]->axis); pcontact->iFeature[icyl] = 0x20 | i+1>>1; pcontact->iFeature[icyl^1] = 0x40; break; case 0x80: // cap edge - cap edge i = (idbest&2)-1; j = (idbest*2&2)-1; pcontact->t = tmax.val(); center[0] = pcyl[0]->center + pcyl[0]->axis*(pcyl[0]->hh*i) + pmode->dir*pcontact->t; center[1] = pcyl[1]->center + pcyl[1]->axis*(pcyl[1]->hh*j); a = a1*(center[0]-center[1]); pcontact->pt = center[0] + u*a; pcontact->pt += l*(sqrt_tpl(fabs_tpl(r0*r0-a*a))*sgnnz(l*(center[1]-pcontact->pt))); pcontact->n = (pcyl[0]->axis ^ pcontact->pt-center[0]) ^ (pcyl[1]->axis ^ pcontact->pt-center[1]); pcontact->n *= sgnnz(pcontact->n*(pcyl[1]->center-pcyl[0]->center-pmode->dir*pcontact->t)); pcontact->iFeature[0] = 0x20 | i+1>>1; pcontact->iFeature[1] = 0x20 | j+1>>1; break; case 0xC0: // side - side pcontact->t = tmax.val(); dp = pcyl2->center-pcyl1->center - pmode->dir*pcontact->t; t1.set((dp^pcyl1->axis)*axisx, sina); pcontact->t = tmax.x*t0.y; pcontact->pt = pcyl2->center + pcyl2->axis*t1.val(); pcontact->n = axisx*sgnnz(axisx*(pcyl2->center-pcyl1->center)); pcontact->pt -= pcontact->n*pcyl2->r; pcontact->iFeature[0] = pcontact->iFeature[1] = 0x40; break; } if (parea) { parea->npt = 0; dc = pcyl[0]->center+pmode->dir*pcontact->t-pcyl[1]->center; if (sina<0.017f) { if (fabs_tpl(fabs_tpl(dc*pcyl[0]->axis)-(pcyl[0]->hh+pcyl[1]->hh)) < min(pcyl[0]->hh,pcyl[1]->hh)*0.05f) { // check for cap-cap area contact i = -sgnnz(pcyl[0]->axis*dc); j = sgnnz(pcyl[1]->axis*dc); center[0] = pcyl[0]->center+pmode->dir*pcontact->t+pcyl[0]->axis*(pcyl[0]->hh*i); center[1] = pcyl[1]->center+pcyl[1]->axis*(pcyl[0]->hh*j); for(icyl=0; icyl<2; icyl++) { axisy = pcyl[icyl]->axis^axisx; for(j1=0,pt2d[0].set(pcyl[icyl]->r,0); j1<8; j1++) { parea->pt[parea->npt] = center[icyl]+axisx*pt2d[0].x+axisy*pt2d[0].y; parea->piFeature[icyl^1][parea->npt] = 0x40 | ((i&-icyl | j&~-icyl)+1>>1)+1; parea->piFeature[icyl][parea->npt] = 0x20 | (i&~-icyl | j&-icyl)+1>>1; parea->npt += isneg((parea->pt[parea->npt]-center[icyl^1]).len2()-sqr(pcyl[icyl^1]->r*1.01f)); a=pt2d[0].x; pt2d[0].x=(pt2d[0].x-pt2d[0].y)*(sqrt2/2); pt2d[0].y=(a+pt2d[0].y)*(sqrt2/2); // rotate by 45 degrees } } // add circle intersection points d = (center[1]-center[0]).len2(); if (d > sqr(min(pcyl[0]->r,pcyl[1]->r)*0.01f)) { b = 1.0/(d = sqrt_tpl(d)); a = ((sqr(pcyl[0]->r)-sqr(pcyl[1]->r))*b+d)*0.5f; c = sqrt_tpl(sqr(pcyl[0]->r)-a*a); u = (center[1]-center[0])*b; l = pcyl[0]->axis^u; for(j1=-1; j1<=1; j1+=2) { parea->pt[parea->npt] = center[0]+u*a+l*(c*j1); parea->piFeature[0][parea->npt] = 0x20 | i+1>>1; parea->piFeature[1][parea->npt] = 0x20 | j+1>>1; parea->npt++; } } } if (fabs_tpl((dc-pcyl[0]->axis*(dc*pcyl[0]->axis)).len2()-sqr(pcyl[0]->r+pcyl[1]->r)) < min(pcyl[0]->r,pcyl[1]->r)*0.05f) { // check for side-side edge contact dp = (pcyl[0]->axis*(dc*pcyl[0]->axis)-dc).normalized(); for(icyl=0; icyl<2; icyl++) for(i=-1; i<=1; i+=2) if (fabs_tpl((dc*(1-icyl*2)+pcyl[icyl]->axis*(pcyl[icyl]->hh*i))*pcyl[icyl^1]->axis) < pcyl[icyl^1]->hh*1.002f) { parea->pt[parea->npt] = pcyl[icyl]->center+pmode->dir*(pcontact->t*(1-icyl))+pcyl[icyl]->axis*(pcyl[icyl]->hh*i)+ dp*(pcyl[icyl]->r*(1-icyl*2)); parea->piFeature[icyl^1][parea->npt] = 0x40; parea->piFeature[icyl][parea->npt] = 0x20 | i+1>>1; parea->npt++; } } } if (fabs_tpl(cosa)<0.017f) for(icyl=0,dc.flip(); icyl<2; icyl++,dc.flip()) if (fabs_tpl((dc^pcyl[icyl^1]->axis).len2()-sqr(pcyl[icyl]->hh+pcyl[icyl^1]->r)) < sqr(min(pcyl[icyl]->hh,pcyl[icyl^1]->r))*0.08f) { // check for icyl^1 side - icyl cap contact a=1-sqr(cosa); b=dc*pcyl[icyl^1]->axis-(dc*pcyl[icyl]->axis)*cosa; c=dc.len2()-sqr(pcyl[icyl]->axis*dc)-sqr(pcyl[icyl]->r); d=b*b-a*c; if (d>0) { i = sgnnz(dc*pcyl[icyl]->axis); j = sgnnz(d-b*b); if (d*j < sqr_signed(pcyl[icyl^1]->hh*a+fabs_tpl(b)*j)) { d = sqrt_tpl(d); a = 1/a; for(j=-1; j<=1; j+=2) if (fabs_tpl((-b+d*j)*a) < pcyl[icyl^1]->hh) { parea->pt[parea->npt] = pcyl[icyl^1]->axis*((-b+d*j)*a); parea->piFeature[icyl^1][parea->npt] = 0x20 | i; parea->piFeature[icyl][parea->npt++] = 0x40 | (j+1>>1)+1; } } for(j=-1; j<=1; j+=2) if ((dc+pcyl[icyl^1]->axis*(pcyl[icyl^1]->hh*j)^pcyl[icyl]->axis).len2()r)) { parea->pt[parea->npt] = pcyl[icyl^1]->axis*(pcyl[icyl^1]->hh*j); parea->piFeature[icyl^1][parea->npt] = 0x40; parea->piFeature[icyl][parea->npt++] = 0x20 | j+1>>1; } for(j=0; jnpt; j++) { dp = pcyl[icyl]->axis*(pcyl[icyl]->axis*(dc+parea->pt[j])-pcyl[icyl]->hh*i); parea->pt[j] += pcyl[icyl^1]->center+pmode->dir*(pcontact->t*(1-icyl))-dp; } } } if (parea->npt) parea->n1 = -pcontact->n; } return 1; }