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
3 求P的坐标
求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);