射线与圆柱的交点

射线与圆柱的交点_第1张图片

1  射线与底圆的投影P1P2

  O 点到底圆的投影 =》 射线OP1 和底面的交点

   射线OP1 的向量就是圆柱的向量C1C0(OP1 和C1C0是平行的)设圆柱的向量n = C0-C1

       P1 = O + tn

       (P1-C0)*n = 0    //连个向量垂直,点积为0

    (O + tn - C0)*n = 0  =》  t = ((C0-O)*n)/n*n;      

   同理O1 可以是射线上的单位向量点O1 = O+V, 可以求出投影点P2

   用两个射线在底面上的投影点P1,P2 求出投影射线V1 = p2-P1

2  求射线V1 与底圆的交点P0

 射线与圆柱的交点_第2张图片

 

3 求P的坐标

射线与圆柱的交点_第3张图片

  求p1p0的长度为a, 设op的长度为t,    cosO = a/t =>  t = a/cosO 

  cosO = V*V1  (向量点积) , 所以   t = Ip0-P1I/(V*V1);     

  P = O+tV,  最终计算出P坐标

下面的code: 

//用几何法求射线与圆柱的交点
public static Point intersectionPoint2(Ray ray, Cylinder cylinder);
 public static class Point {
        public final float x,y,z;
        public Point(float x,float y,float z){
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public Point translateX(float value) {
            return new Point(x,y+value,z);
        }
        public Point translateY(float value) {
            return new Point(x,y+value,z);
        }
        public Point translateZ(float value) {
            return new Point(x,y+value,z);
        }
        public Point translate(Vector vector) {
            return new Point(x+vector.x,y+vector.y,z+vector.z);
        }

        @Override
        public String toString() {
            return "{" +
                    "x=" + x +
                    ", y=" + y +
                    ", z=" + z +
                    '}';
        }
    }

    /**
     * 方向 向量
     */
    public static class Vector {
        public final float x,y,z;
        public Vector(float x, float y, float z) {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public float length() {
            return (float) Math.sqrt(x*x + y*y + z*z);
        }
        public Vector crossProduct(Vector other) {
            return new Vector(
                    (y*other.z) - (z*other.y),
                    (x*other.z) - (z*other.x),
                    (x*other.y) - (y*other.x)
            );
        }
        public float dotProduct(Vector other) {
            return x * other.x +
                    y * other.y +
                    z * other.z ;
        }
        public Vector scale(float f) {
            return new Vector(x*f, y*f, z*f);
        }

        public Vector normalize(){
            float len = length();
            float nx = x/len;
            float ny = y/len;
            float nz = z/len;
            return new Vector(nx, ny, nz);
        }

        @Override
        public String toString() {
            return "Vector{" +
                    "x=" + x +
                    ", y=" + y +
                    ", z=" + z +
                    '}';
        }
    }

    /**
     * 射线 = 起点 + 方向向量
     */
    public static class Ray {
        public final Point point;
        public final Vector vector;

        public Ray(Point point, Vector vector) {
            this.point = point;
            this.vector = vector;
        }
    }

    /**
     * 平面 = 平面切点 + 法向量
     */
    public static class Plane {
        public final Point point;
        public final Vector normal;

        public Plane(Point point,Vector normal) {
            this.point = point;
            this.normal = normal;
        }
    }


    /**
     * 圆柱
     */

    public static class Cylinder{
        public final Point upCenter;
        public final Point bottomCenter;
        public final float radius;

        public Cylinder(Point upCenter, Point bottomCenter, float radius){
            this.upCenter = upCenter;
            this.bottomCenter = bottomCenter;
            this.radius = radius;
        }

        //判断点是否在圆柱内部
        public boolean isInCylinder(Point point){
             Point middle = getMiddlePoint(upCenter, bottomCenter);                  //圆柱中心点
             Ray ray = new Ray(bottomCenter,vectorBetween(bottomCenter, upCenter));  //圆柱中心射线
             float height = ray.vector.length();                                     //圆柱高度
             float distance = distanceBetween(point, ray);                           //点到圆柱中心射线的距离

             if(distance > radius){
                 return false;                                                       //距离大于半径,点在圆柱外面
             }
             Vector normal = vectorBetween(middle, upCenter).normalize();            //圆住中心标准向量

             float hLen = normal.dotProduct(vectorBetween(middle, point));           //点到中心面的距离 (点乘投影)
             if(Math.abs(hLen) > height/2){
                 return false;                                                       //距离大于高度的一半, 在圆柱外面
             }
             return true;
        }
    }

    //返回两点的中点
    public static Point getMiddlePoint(Point near, Point far){
        return new Point((near.x + far.x)/2.0f,
                (near.y+far.y)/2.0f,
                (near.z+far.z)/2.0f);
    }

    //射线与圆柱的交点 (射线是起点在圆柱内)
    //迭代的方式求解,
    public static Point intersectionPoint(Ray ray, Cylinder cylinder){

        intersectionPoint2(ray, cylinder);
        long time = SystemClock.elapsedRealtimeNanos();
        float tolerance = 0.01f;              //计算精度
        Point nearPoint = ray.point;          //射线近点
        Point farPoint = new Point(ray.vector.x+ray.point.x, ray.vector.y+ray.point.y, ray.vector.z+ray.point.z); //射线远点
        Point middle = getMiddlePoint(nearPoint, farPoint);  //射线中点

        int times = 0;

        while (true){
            if(cylinder.isInCylinder(middle)){  //中点在圆柱内部, 交点在[near=middle, far]
                nearPoint = middle;
            }else{                              //中点在圆柱外, 交点在[near, far = middle]
                farPoint = middle;
            }
            middle = getMiddlePoint(nearPoint, farPoint);  //新的中点

            if(vectorBetween(farPoint, nearPoint).length() <= tolerance){  //精度判断
                break;
            }

            times ++;
            if(times == 40){
                break;
            }
        }

        Ray rayCylinder = new Ray(cylinder.bottomCenter,vectorBetween(cylinder.bottomCenter, cylinder.upCenter));
        float len = distanceBetween(nearPoint, rayCylinder);
        if(Math.abs((len-cylinder.radius)) > tolerance){ //交点在圆柱的上下面上, 不在圆柱侧面
            return null;
        }
        //Log.i(TAG,"intersectionPoint nearPoint="+nearPoint.toString());
        Log.i(TAG, "intersectionPoint time = "+(SystemClock.elapsedRealtimeNanos()-time));
        return nearPoint;
    }


    //用几何法求射线与圆柱的交点
    public static Point intersectionPoint2(Ray ray, Cylinder cylinder){

        //射线原点到圆柱底面的投影p1
        Point center = cylinder.bottomCenter;//getMiddlePoint(cylinder.upCenter, cylinder.bottomCenter); //圆柱中心面的,圆面中点
        Vector p2Center = vectorBetween(ray.point, center);
        Vector normal = vectorBetween(center, cylinder.upCenter).normalize(); //圆柱中心线方向向量
        // 然后就是 射点到平面点的向量 与 平面法向量的点积 / 射线方向向量 与 平面法向量的点积
        float scaleFactor = p2Center.dotProduct(normal) / normal.dotProduct(normal);
        Point p1 = ray.point.translate(normal.scale(scaleFactor));

        //射线上单位长度点到圆柱中心面的投影p2
        Point p = ray.point.translate(ray.vector.normalize().scale(1.0f)); //射线上,单位长度点
        p2Center = vectorBetween(p, center);
        scaleFactor = p2Center.dotProduct(normal) / normal.dotProduct(normal);
        Point p2 = p.translate(normal.scale(scaleFactor));


        // vP1P2  射线在圆柱底面上的投影射线向量
        // 用余弦定理,求投影射线与中心圆的交点p  a*a+b*b-2*a*b*cos(&) = c*c
        // 三角形三点: p,  p1,  center;   设P1->p的长度为t, p->center的长度为R(半径), p1->chenter的长度为a
        //t*t - 2a*cos(&)*t+a*a-R*R=0, 解方程求出t
        Vector vP1P2 = vectorBetween(p1, p2).normalize();
        Vector vP1Center = vectorBetween(p1,center);
        float lenP1Center = vP1Center.length();

        float A = 1.0f;                                           //二元一次方程的A
        float B = -vP1Center.dotProduct(vP1P2)*2;  //二元一次方程的B  - 2a*cos(&)  =》 a*cos(&) => 向量p1Center 点乘单位向量p1p2
        float C = lenP1Center*lenP1Center -cylinder.radius*cylinder.radius; //二元一次方程的C  a*a-R*R

        //解方程
        float d = B*B-4*A*C;
        if(d > 0){
            float t1 = (float) ((-B+Math.sqrt(d))/(2*A));
            float t2 = (float) ((-B-Math.sqrt(d))/(2*A));
            if(t1 > 0){
                p = p1.translate(vP1P2.scale(t1));
            }else {
                p = p1.translate(vP1P2.scale(t2));
            }
            //三点 p,intersection,p1 组成一个直角三角行,求出p1->intersection 的长度tx
            float tx = vectorBetween(p1,p).length()/(ray.vector.normalize().dotProduct(vP1P2));
            Point intersection = ray.point.translate(ray.vector.normalize().scale(tx));

            return intersection;
        }
        return null;
    }

    /**
     * 向量相减 A-B = BA
     * @param from B
     * @param to A
     * @return AB
     */
    public static Vector vectorBetween(Point from, Point to) {
        return new Vector(
                to.x-from.x,
                to.y-from.y,
                to.z-from.z);
    }

    // 包围球 与 射线 的相交测试接口
    public static boolean intersects(Ray ray, Sphere sphere) {
        return sphere.radius > distanceBetween(sphere.center, ray);
    }
    // 求出包围球中心点 与 射线的距离
    public static float distanceBetween(Point centerPoint, Ray ray) {
        // 第一个向量:开始点到球心
        Vector vStart2Center = vectorBetween(ray.point, centerPoint);
        // 第二个向量:结束点到球心
        Vector vEnd2Center = vectorBetween(
                ray.point.translate(ray.vector), // 结束点 = 开始点 + 方向向量。
                centerPoint);
        // 两个向量的叉积
        Vector crossProduct = vStart2Center.crossProduct(vEnd2Center);
        // 两个向量叉积的值大小 = 三角形面积 * 2
        float areaOf2 = crossProduct.length();
        // 求出射线的长度 = 三角形的底边长
        float lengthOfRay = ray.vector.length();
        // 高 = 面积*2 / 底边长
        float distanceFromSphereCenterToRay = areaOf2 / lengthOfRay;
        return distanceFromSphereCenterToRay;
    }
    // 射线 与 平面 相交点
    /*  p:交点  p0: 射线起点  u: 射线方向向量   p1: 平面上的点  n: 平面法向量
    p = p0 + tu
    n * (p - p1) = 0    //p 和 p1 都在平面上,所有p和p1组成的向量和法向量垂直
    n * (p0 + tu - p1) = 0   =>  n*p0 + n * tu - n * p1 = 0    =》
    t =  n * (p1 - p0) / n* u
    计算出t后, 可以算出交点 p = p0 + tu;
    */

    public static Point intersectionPoint(Ray ray, Plane tablePlane) {
        // 先找出 射点 到 平面点的 向量
        Vector rayToPlaneVector = vectorBetween(ray.point, tablePlane.point);
        // 然后就是 射点到平面点的向量 与 平面法向量的点积 / 射线方向向量 与 平面法向量的点积
        float scaleFactor = rayToPlaneVector.dotProduct(tablePlane.normal) / ray.vector.dotProduct(tablePlane.normal);
        // 根据缩放因子,缩放射线向量,射线平面交点 = 射点 + 缩放后的方向向量
        Point intersctionPoint = ray.point.translate(ray.vector.scale(scaleFactor));
        return intersctionPoint;
    }
另外code中还有一种方法是迭代法求交点
用射线上的两点(far 圆柱外, near圆柱内, 取这两点的中点middle), 
如:middle 在圆柱内, near = middle, 
如:middle 在圆柱外, far = middle,
继续取中点,通过一次次的如此迭代,可以不断的逼近交点



//射线与圆柱的交点 (射线是起点在圆柱内)
//迭代的方式求解,
public static Point intersectionPoint(Ray ray, Cylinder cylinder);

   

   

你可能感兴趣的:(java,算法,开发语言)