Real Cylinder3D::closestPoint(const Point3D& pt,Point3D& closest) const { Real h = dot(pt-center,axis); if(h < 0) { Circle3D c; getBase(c); return c.closestPoint(pt,closest); } else if (h > height) { Circle3D c; getCap(c); return c.closestPoint(pt,closest); } else { Vector3 vperp = (pt-center) - h*axis; Real rv = vperp.norm(); if(rv <= radius) { //inside cylinder closest = pt; return 0; } else { closest = center + axis*h + vperp*(radius/rv); return rv - radius; } } }
bool Cylinder3D::intersects(const Line3D& line,Real* tmin,Real* tmax) const { Real axistmin,axistmax; //quick reject - infinite cylinder Vector3 src=line.source-center; const Vector3& dir=line.direction; assert(FuzzyEquals(axis.normSquared(),One)); //quadratic equation Real a,b,c; Real dv,sv; dv = dir.dot(axis); sv = src.dot(axis); a=dir.normSquared()-Sqr(dv); b=Two*(dir.dot(src)-sv*dv); c=src.normSquared()-Sqr(sv)-Sqr(radius); int nroots=quadratic(a,b,c,axistmin,axistmax); //TODO: if the line is contained within the cylinder, ignore this if(nroots == 0) return false; else if(nroots == 1) axistmax=axistmin; else if(nroots == 2) { if(axistmin > axistmax) std::swap(axistmin,axistmax); } else if(nroots == -1) return false; else return false; //what case is this? //projection of intersection pts on the cyl axis Real axisumin,axisumax; Vector3 temp; line.eval(axistmin,temp); axisumin = axis.dot(temp-center); line.eval(axistmax,temp); axisumax = axis.dot(temp-center); //now check the caps Real tc; Circle3D cir; if(axisumin < 0) { //hits a cap first if(dv > 0) //line points along axis getBase(cir); else //line points against axis getCap(cir); if(!cir.intersects(line,&tc)) return false; axistmin = tc; } if(axisumin > height) { //hits a cap last if(dv > 0) //line points along axis getCap(cir); else //line points against axis getBase(cir); if(!cir.intersects(line,&tc)) return false; axistmax = tc; } //if(axistmin > axistmax) return false; if(tmin) *tmin=axistmin; if(tmax) *tmax=axistmax; return true; }
bool Circle3D::boundaryIntersects(const Sphere3D& s) const { //slice the ball along the plane of the circle, check for circle-disk intersection Plane3D p; Circle3D cs; getPlane(p); if(!cs.setIntersection(s,p)) return false; return Sphere3D::ballSphereIntersect(cs.center,cs.radius,center,radius); }
void Cylinder3D::getAABB(AABB3D& aabb) const { Circle3D c; getBase(c); c.getAABB(aabb); if(axis.x > 0) aabb.bmax.x += axis.x*height; else aabb.bmin.x -= axis.x*height; if(axis.y > 0) aabb.bmax.y += axis.y*height; else aabb.bmin.y -= axis.y*height; if(axis.z > 0) aabb.bmax.z += axis.z*height; else aabb.bmin.z -= axis.z*height; }
bool BallCircleCollision(const Sphere3D& a, const Circle3D& b) { return b.boundaryIntersects(a); }
bool TransformWidget::Hover(int x,int y,Camera::Viewport& viewport,double& distance) { Real globalScale = 1.0; if(scaleToScreen) { float sx,sy,sz; viewport.project(T.t,sx,sy,sz); globalScale = sz/viewport.scale; } distance = Inf; int oldHoverItem = hoverItem; hoverItem = -1; Ray3D r; viewport.getClickSource(x,y,r.source); viewport.getClickVector(x,y,r.direction); //check origin if(enableTranslation && enableOriginTranslation) { Sphere3D s; s.center = T.t; s.radius = originRadius*globalScale; Real tmin,tmax; if(s.intersects(r,&tmin,&tmax)) { distance = tmin; hoverItem = 0; } } //check translation axes for(int i=0;i<3;i++) { if(!enableTranslation) break; if(!enableTranslationAxes[i]) continue; Line3D axisLine; axisLine.source = T.t; axisLine.direction = Vector3(T.R.col(i)); Real t,u; axisLine.closestPoint(r,t,u); t = Clamp(t,0.0,axisLength*globalScale); u = Clamp(u,0.0,Inf); Vector3 paxis,pray; axisLine.eval(t,paxis); r.eval(u,pray); if(paxis.distanceSquared(pray) <= Sqr(axisRadius*globalScale)) { if(u < distance) { distance = u; hoverItem = 1+i; } } } if(enableRotation) { //check rotation rings Circle3D c; c.center = T.t; for(int i=0;i<3;i++) { if(!enableRotationAxes[i]) continue; c.axis = Vector3(T.R.col(i)); c.radius = ringOuterRadius*globalScale; Real t; if(c.intersects(r,&t) && t >= 0) { c.radius = ringInnerRadius*globalScale; if(!c.intersects(r,NULL)) { if(t < distance) { distance = t; hoverItem = i+4; } } } } } if(enableRotation && enableOuterRingRotation) { //check outer ring Circle3D c; c.center = T.t; viewport.getViewVector(c.axis); c.radius = (ringOuterRadius+arrowHeight)*globalScale; Real t; if(c.intersects(r,&t) && t >= 0) { c.radius = (ringInnerRadius+arrowHeight)*globalScale; if(!c.intersects(r,NULL)) { if(t < distance) { distance = t; hoverItem = 7; } } } clickAxis = c.axis; } if(hoverItem != oldHoverItem) Refresh(); r.eval(distance,hoverPos); return hoverItem != -1; }