@Override
    public boolean shadow_hit(Ray ray, FloatRef tmin) {
        if (!bbox.hit(ray))
            return false;

        double x1 = ray.getOrigin().getX();
        double y1 = ray.getOrigin().getY();
        double z1 = ray.getOrigin().getZ();

        double d1 = ray.getDirection().getX();
        double d2 = ray.getDirection().getY();
        double d3 = ray.getDirection().getZ();

        double coeffs[] = new double[5];    // coefficient array for the quartic equation
        double roots[] = new double[4];    // solution array for the quartic equation

        // define the coefficients of the quartic equation
        double sum_d_sqrd = d1 * d1 + d2 * d2 + d3 * d3;
        double e = x1 * x1 + y1 * y1 + z1 * z1 - a * a - b * b;
        double f = x1 * d1 + y1 * d2 + z1 * d3;
        double four_a_sqrd = 4.0 * a * a;

        coeffs[0] = e * e - four_a_sqrd * (b * b - y1 * y1);    // constant term
        coeffs[1] = 4.0 * f * e + 2.0 * four_a_sqrd * y1 * d2;
        coeffs[2] = 2.0 * sum_d_sqrd * e + 4.0 * f * f + four_a_sqrd * d2 * d2;
        coeffs[3] = 4.0 * sum_d_sqrd * f;
        coeffs[4] = sum_d_sqrd * sum_d_sqrd;                    // coefficient of t^4

        // find roots of the quartic equation
        int num_real_roots = Solvers.solveQuartic(coeffs, roots);

        boolean intersected = false;
        double t = Double.MAX_VALUE;

        if (num_real_roots == 0)  // ray misses the torus
            return false;

        // find the smallest root greater than kEpsilon, if any
        // the roots array is not sorted
        for (int j = 0; j < num_real_roots; j++)
            if (roots[j] > K_EPSILON) {
                intersected = true;
                if (roots[j] < t)
                    t = roots[j];
            }

        if (!intersected)
            return false;

        tmin.value = (float) t;
        return true;
    }