static inline bool findInflections(qreal a, qreal b, qreal c, qreal *t1 , qreal *t2, qreal *tCups) { qreal r1 = 0, r2 = 0; short rootsCount = quadraticRoots(a, b, c, &r1, &r2); if (rootsCount >= 1) { if (r1 < r2) { *t1 = r1; *t2 = r2; } else { *t1 = r2; *t2 = r1; } if (!qFuzzyIsNull(a)) *tCups = qreal(0.5) * (-b / a); else *tCups = 2; return true; } return false; }
RayResult* NonhierSphere::findIntersections(const Ray& ray) { // Will use quadratic solver. double A = ray.dir.length2(); double B = 2 * ray.dir.dot(ray.pos - m_pos); double C = (ray.pos - m_pos).length2() - m_radius*m_radius; double roots[2]; size_t num_roots = quadraticRoots(A, B, C, roots); std::vector<Intersection> intersections; const double EPSILON = 0.01; if (num_roots > 0) { double t = roots[0]; if (num_roots == 2 && roots[1] > EPSILON && roots[1] < t) { t = roots[1]; } if (t > EPSILON) { const Point3D p = ray.pos + t*ray.dir; Vector3D normal = p - m_pos; normal.normalize(); //std::cout << "INTERSECTION for ray " << ray << " with params " << t << ", " << p << std::endl; intersections.push_back(Intersection( p, normal, NULL )); } } return new RayResult(intersections, 1); }
int verticalIntersect(double axisIntercept) { double D = quad[2].x; // f double E = quad[1].x; // e double F = quad[0].x; // d D += F - 2 * E; // D = d - 2*e + f E -= F; // E = -(d - e) F -= axisIntercept; return quadraticRoots(D, E, F, intersections.fT[0]); }
// from SkGeometry.cpp (and Numeric Solutions, 5.6) int cubicRoots(double A, double B, double C, double D, double t[3]) { if (approximately_zero(A)) { // we're just a quadratic return quadraticRoots(B, C, D, t); } double a, b, c; { double invA = 1 / A; a = B * invA; b = C * invA; c = D * invA; } double a2 = a * a; double Q = (a2 - b * 3) / 9; double R = (2 * a2 * a - 9 * a * b + 27 * c) / 54; double Q3 = Q * Q * Q; double R2MinusQ3 = R * R - Q3; double adiv3 = a / 3; double* roots = t; double r; if (R2MinusQ3 < 0) // we have 3 real roots { double theta = acos(R / sqrt(Q3)); double neg2RootQ = -2 * sqrt(Q); r = neg2RootQ * cos(theta / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * cos((theta + 2 * PI) / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; r = neg2RootQ * cos((theta - 2 * PI) / 3) - adiv3; if (is_unit_interval(r)) *roots++ = r; } else // we have 1 real root { double A = fabs(R) + sqrt(R2MinusQ3); A = cube_root(A); if (R > 0) { A = -A; } if (A != 0) { A += Q / A; } r = A - adiv3; if (is_unit_interval(r)) *roots++ = r; } return (int)(roots - t); }
int intersect() { /* solve by rotating line+quad so line is horizontal, then finding the roots set up matrix to rotate quad to x-axis |cos(a) -sin(a)| |sin(a) cos(a)| note that cos(a) = A(djacent) / Hypoteneuse sin(a) = O(pposite) / Hypoteneuse since we are computing Ts, we can ignore hypoteneuse, the scale factor: | A -O | | O A | A = line[1].x - line[0].x (adjacent side of the right triangle) O = line[1].y - line[0].y (opposite side of the right triangle) for each of the three points (e.g. n = 0 to 2) quad[n].y' = (quad[n].y - line[0].y) * A - (quad[n].x - line[0].x) * O */ double adj = line[1].x - line[0].x; double opp = line[1].y - line[0].y; double r[3]; for (int n = 0; n < 3; ++n) { r[n] = (quad[n].y - line[0].y) * adj - (quad[n].x - line[0].x) * opp; } double A = r[2]; double B = r[1]; double C = r[0]; A += C - 2 * B; // A = a - 2*b + c B -= C; // B = -(b - c) int roots = quadraticRoots(A, B, C, intersections.fT[0]); for (int index = 0; index < roots; ) { double lineT = findLineT(intersections.fT[0][index]); if (lineIntersects(lineT, index, roots)) { ++index; } } return roots; }
/* Coefficients : x^4 + C[0] x^3 + C[1] x^2 + C[2] x + C[3] */ size_t quarticRoots( double a, double b, double c, double d, double roots[4] ) { double h,h1,h2,H, g,g1,g2,G, n, m, en, em, y; double cubic[3]; /* Cubic and quadratic coefficients */ int i, nr; /* Find the a real root of a certain cubic */ cubic[0] = -2.0*b; cubic[1] = b*b + a*c - 4*d; cubic[2] = c*c - a*b*c + a*a*d; nr = cubicRoots( cubic[0], cubic[1], cubic[2], roots ); if( nr == 1 ) { y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[0] ); } else { if( b < 0 && d < 0 ) { y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[2] ); } else { y = PolishRoot( 3, cubic[0], cubic[1], cubic[2], 0.0, roots[0] ); } } g1 = a/2.0; h1 = (b-y)/2.0; if( y < 0 ) { n = a*a - 4*y; if( n <= 0 ) { return 0; } g2 = sqrt(n); if( g2 == 0 ) { return 0; } h2 = (a*((b-y)/2.0) - c) / g2; g2 /= 2.0; } else if( y > 0 && d > 0 && b < 0 ) { m = (b-y)*(b-y) - 4*d; if( m <= 0 ) { return 0; } h2 = sqrt(m); if( h2 == 0 ) { return 0; } g2 = (a*h1 - c) / h2; h2 /= 2.0; } else { n = a*a - 4*y; m = (b-y)*(b-y) - 4*d; en = b*b + 2.*fabs(b*y) + y*y + 4*fabs(d); em = a*a + 4.*fabs(y); if( m*en > n*em ) { /* use m */ if( m <= 0 ) { return 0; } h2 = sqrt(m); if( h2 == 0 ) { return 0; } g2 = (a*h1 - c) / h2; h2 /= 2.0; } else { /* use n */ if (n <= 0) { return 0; } g2 = sqrt(n); if (g2 == 0) { return 0; } h2 = (a*( (b-y)/2.0) - c) / g2; g2 /= 2.0; } } if( SIGN(g1) == SIGN(g2) ) { G = g1 + g2; g = (G==0) ? g1-g2 : y/G; } else { g = g1 - g2; G = (g == 0) ? g1+g2 : y/g; } if( SIGN(h1) == SIGN(h2) ) { H = h1+h2; h = (H == 0) ? h1-h2 : d/H; } else { h = h1 - h2; H = (h == 0) ? h1+h2 : d/h; } nr = quadraticRoots( 1.0, G, H, roots ); nr += quadraticRoots( 1.0, g, h, roots+nr ); for( i=0; i < nr; ++i ) { roots[i] = PolishRoot( 4, a, b, c, d, roots[i] ); } /* remove non-roots */ // fprintf(stderr,"REMOVE NONROOTS %d\n",nr); for ( i=0; i < nr; i++ ) { double r; r = (((roots[i]+a)*roots[i]+b)*roots[i]+c)*roots[i]+d; // fprintf(stderr,"root %d is %g\n",i,r); if ( fabs(r)>1e-4 ) { roots[i] = roots[nr-1]; nr--; i--; } } return nr; }
HitInfo Cone::intersects(Point3D origin, Vector3D dir) const { HitInfo info; double A = dir[0] * dir[0] + dir[2] * dir[2] - dir[1] * dir[1]; double B = 2 * (origin[0] * dir[0] + origin[2] * dir[2] - (origin[1] - 1) * dir[1]); double C = origin[0] * origin[0] + origin[2] * origin[2] - (origin[1] - 1.0) * (origin[1] - 1.0); double roots[2]; int num_roots = quadraticRoots(A, B, C, roots); if (dir[1] != 0.0) { Hit hit; double t3 = (0.0 - origin[1]) / dir[1]; Point3D p = origin + t3 * dir; if (t3 > 0.0 && (p - Point3D(0.0, 0.0, 0.0)).length() <= 1.0) { hit.normal = Vector3D(0.0, -1.0, 0.0); hit.intersection = p; hit.t = t3; info.hits.push_back(hit); } } if (num_roots == 1 && roots[0] > 0.0) { // one hit on the cone Point3D p = origin + roots[0] * dir; if (p[1] >= 0.0 && p[1] <= 1.0) { Hit hit; hit.normal = Vector3D(p[0], 0.5, p[2]); hit.intersection = p; hit.t = roots[0]; info.hits = Hit::insert_hit_in_order(info.hits, hit); } } else if (num_roots == 2) { // two hits on cone Point3D p0 = origin + roots[0] * dir; if (roots[0] > 0.0 && p0[1] >= 0.0 && p0[1] <= 1.0) { Hit h; h.t = roots[0]; h.intersection = p0; h.normal = Vector3D(p0[0], 0.0, p0[2]); info.hits = Hit::insert_hit_in_order(info.hits, h); } Point3D p1 = origin + roots[1] * dir; if (roots[1] > 0.0 && p1[1] >= 0.0 && p1[1] <= 1.0) { Hit h; h.t = roots[1]; h.intersection = p1; h.normal = Vector3D(p1[0], 0.0, p1[2]); info.hits = Hit::insert_hit_in_order(info.hits, h); } } std::vector<Hit> final_hits; for (unsigned int i = 0; i < info.hits.size(); i++) { double len = (info.hits.at(i).intersection - origin).length(); if (len > EPSILON) { Vector3D Vn = Vector3D(0.0, 1.0, 0.0); Vector3D Ve = Vector3D(1.0, 0.0, 0.0); Vector3D Vp = info.hits.at(i).intersection - Point3D(0.0, 0.5, 0.0); Vp.normalize(); double phi = acos(-Vn.dot(Vp)); double theta = (acos(Vp.dot(Ve)) / sin(phi)) / (2 * M_PI); double u = (Vp.dot(Vn.cross(Ve)) > 0) ? theta : 1 - theta; double v = phi / M_PI; if (u < 0) { u = 0.0; } if (u > 1.0) { u = 1.0; } if (v < 0.0) { v = 0.0; } if (v > 1.0) { v = 1.0; } info.hits.at(i).tex_coords[0] = u; info.hits.at(i).tex_coords[1] = v; info.hits.at(i).in_shadow = (len > EPSILON) && (dir.length() > len); final_hits.push_back(info.hits.at(i)); } } info.hits = final_hits; if (info.hits.size() == 2) { info.lines.push_back(std::make_pair(info.hits.at(0), info.hits.at(1))); info.hits.clear(); } return info; }
HitInfo Primitive::hits_sphere(Point3D origin, Vector3D dir, Point3D pos, double radius) { HitInfo info; Vector3D center_to_origin = origin - pos; double A = dir.dot(dir); double B = 2 * dir.dot(center_to_origin); double C = center_to_origin.dot(center_to_origin) - (radius * radius); double roots[2]; int num_roots = quadraticRoots(A, B, C, roots); // if D has no roots, then it doesn't intersect. if (num_roots < 1) { return info; } else if (num_roots == 1 && roots[0] > 0) { Hit h; h.intersection = origin + roots[0] * dir; info.hits.push_back(h); } else { double t1 = roots[0]; double t2 = roots[1]; if (t1 <= 0.0 && t2 <= 0.0) { return info; } if (t1 >= 0.0) { Hit h; h.intersection = origin + t1 * dir; h.t = t1; info.hits = Hit::insert_hit_in_order(info.hits, h); } if (t2 >= 0.0) { Hit h; h.intersection = origin + t2 * dir; h.t = t2; info.hits = Hit::insert_hit_in_order(info.hits, h); } } std::vector<Hit> final_hits; for (unsigned int i = 0; i < info.hits.size(); i++) { double len = (info.hits.at(i).intersection - origin).length(); if (len > EPSILON) { info.hits.at(i).in_shadow = (len > EPSILON) && (dir.length() > len); info.hits.at(i).normal = info.hits.at(i).intersection - pos; Vector3D Vn = Vector3D(0.0, 1.0, 0.0); Vector3D Ve = Vector3D(1.0, 0.0, 0.0); Vector3D Vp = info.hits.at(i).intersection - pos; Vp.normalize(); ////////////////// //// double phi = acos(-Vn.dot(Vp)); // double theta = (acos(Vp.dot(Ve)) / sin(phi)) / (2 * M_PI); // double u = (Vp.dot(Vn.cross(Ve)) > 0) ? theta : 1 - theta; /////correct? double theta = atan2(-Vp[2], Vp[0]); double phi = acos(-Vp[1] / radius); /////////// double u = (theta + M_PI) / (2 * M_PI); double v = phi / M_PI; /* if (u < 0) { u = 0.0; } if (u > 1.0) { u = 1.0; } if (v < 0.0) { v = 0.0; } if (v > 1.0) { v = 1.0; } */ //std::cout <<"uv: " << u << " " << v << std::endl; info.hits.at(i).tex_coords[0] = u; info.hits.at(i).tex_coords[1] = v; info.hits.at(i).pu = Vector3D(-radius*sin(theta)*sin(phi), 0, -radius*cos(theta)*sin(phi)); info.hits.at(i).pv = Vector3D(radius*cos(theta)*cos(phi), radius*sin(phi), -radius*sin(theta)*cos(phi)); final_hits.push_back(info.hits.at(i)); } } info.hits = final_hits; if (info.hits.size() == 2) { info.lines.push_back(std::make_pair(info.hits.at(0), info.hits.at(1))); info.hits.clear(); } return info; }