求过圆心直线与圆的两个交点

主要是注意所使用的数据类型。

之前用的是float,出现了一些意外,而且花费了我不少时间来反复验证、推导,

做了很多的无用功,而且,反复推导得出来的计算步骤并没有什么不牢靠的地方。

然后计算得到的结果却是让人如此之不省心,梗的我闷得慌。

今天上午发来了一贴,多位朋友各抒己见,

总算是让我发现了一些不足的地方,首当其冲的是一个变量弄错了,

导致大批的计算失准。

后来修正了这个bug以后,还是会出现计算不精确的地方。

再后来便将涉及的所有成员变量由float 纠正为 double 类型,

计算精度果然得到了提高,失准的地方再次被干掉。

这次给自己的教训就是:

涉及到精度比较高的数值运算的时候,还是得统统用 double。

之前还以为 float 已经比较不错,能够满足基本的需求了,

经过这次我总算是懂了,double的存在离我并不遥远。

这个问题堵了我比较久了,大概也有快10个月了,当时没解决就规避之没去用了,

今天能够解决这个遗留已久的问题,真是让人心情愉快!

下面贴出 Objective-C 和 Java 的相关代码:

Objective-C 部分(核心代码摘录)

/** 已知两点,求过该两点的直线表达式~ */
- (BYLine) getLine:(b2Vec2)p1 anotherPoint:(b2Vec2)p2 {
	BYLine line;
	if((p1.x - p2.x) != 0) {
        line.kExists = true;
		line.k = (p1.y - p2.y) / (p1.x - p2.x);
		line.b = p1.y - line.k * p1.x;
	} else {
		line.kExists = false;
		line.extraX = p1.x;
	}
	return line;
}
/** 已知一点和直线斜率,求该直线的表达式~ */
- (BYLine) getLine:(b2Vec2)point kParam:(double)kParam {
	BYLine line;
    line.kExists = true;
	line.k = kParam;
	line.b = point.y - kParam * point.x;
	return line;
}
- (double) getDistanceBetween2Points:(b2Vec2)p0 anotherPoint:(b2Vec2)p1 {
	return sqrt(pow(p0.y - p1.y, 2) + pow(p0.x - p1.x, 2));
}
/** 获取一条直线上距离某点一定距离的两个点~ */
- (b2Vec2*) get2Points:(BYLine)ln p:(b2Vec2)point pw:(double)pathWidth {
    b2Vec2* target = new b2Vec2[2];
    double circleRadius = pathWidth / 2;
    
    if(ln.k != 0) {
        // 斜率存在且不为 0~
        double kOfNewLine = -1 / ln.k;
        BYLine newLine = [self getLine:point kParam:kOfNewLine];
        
        // 经过数学运算,得出二元一次方程组的表达式
        double A = pow(newLine.k, 2) + 1;
        double B = 2 * (newLine.k * newLine.b - newLine.k * point.y - point.x);
        double C = pow(point.x, 2) + pow((newLine.b - point.y), 2) - pow(circleRadius, 2);
        double delta = pow(B, 2) - 4 * A * C;
        
        if(delta < 0) {    // 经实践检验有一定几率走入该分支,必须做特殊化处理~
            NSLog(@"竟然会无解,他妈的怎么回事儿啊!");
            target[0] = b2Vec2(point.x, point.y - circleRadius);
            target[1] = b2Vec2(point.x, point.y + circleRadius);
        } else {
            double x1 = (-B + sqrt(delta)) / (2 * A);
            double y1 = newLine.k * x1 + newLine.b;
            target[0] = b2Vec2(x1, y1);
            
            double x2 = (-B - sqrt(delta)) / (2 * A);
            double y2 = newLine.k * x2 + newLine.b;
            target[1] = b2Vec2(x2, y2);
        }
    } else {
        // 斜率存在且为 0~
        target[0] = b2Vec2(point.x, point.y - circleRadius);
        target[1] = b2Vec2(point.x, point.y + circleRadius);
    }
    NSLog(@"离中心点的距离为:%f", [self getDistanceBetween2Points:target[0] anotherPoint:point]);
    return target;
}
// 绘制触摸点到移动点的轨迹,1个像素~
- (void) drawTouchPath {
    if(_mouseDown) {
        // 已知(2等分,用分数表示~)
        b2Vec2 pStart = _touchSegment.p1;
        b2Vec2 pEnd = _touchSegment.p2;
        
        // 推出
        b2Vec2 pMiddle = b2Vec2((pStart.x + pEnd.x) / 2, (pStart.y + pEnd.y) / 2);
        float pathLength = [self getDistanceBetween2Points:pStart anotherPoint:pEnd];
        
        // 设置触摸轨迹的宽度~
        float pathWidth = pathLength / 3.0f;
        if(pathWidth > TOUCH_PATH_MAX_WIDTH) {
            pathWidth = TOUCH_PATH_MAX_WIDTH;
        }
        
        b2Vec2* result;
        BYLine expFunc = [self getLine:pStart anotherPoint:pEnd];
        if(expFunc.kExists) {   // 斜率存在~
            result = [self get2Points:expFunc p:pMiddle pw:pathWidth];
        } else {                // 斜率不存在~
            result = new b2Vec2[2];
            result[0] = b2Vec2(pMiddle.x - pathWidth / 2, pMiddle.y);
            result[1] = b2Vec2(pMiddle.x + pathWidth / 2, pMiddle.y);
        }
        
        b2Vec2 finalResult[5];
        finalResult[0] = pStart;
        finalResult[1] = result[0];
        finalResult[2] = pEnd;
        finalResult[3] = result[1];
        finalResult[4] = pStart;
 
        // 绘制白色内容物~
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
        glVertexPointer(2, GL_FLOAT, 0, finalResult);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 5);
    }
}

Java 部分(部件齐全,能直接拿来跑的)

package org.bruce.vertices.controller.geometry;
 
/**
 * @author BruceYang
 * 对点的抽象~
 */
public class CGPoint {
	public double x;
	public double y;
	
	public CGPoint() {
		
	}
	public CGPoint(double x, double y) {
		this.x = x;
		this.y = y;
	}
	
	@Override
	public String toString() {
		return "x=" + this.x + ", y=" + this.y;
	}
}
package org.bruce.vertices.controller.geometry;
 
 
/**
 * @author BruceYang
 * 这个是对通用一次直线方程 A*x + B*y + C = 0 的封装~
 * 本来封装的是斜截式,不过发现当斜率k不存在的时候会比较麻烦,因此该用一般式
 * 再个就是接着用一般式的演变方式 x + B/A*y + C/A = 0,但是考虑到可能存在x == 0 的情况,因此又舍弃~
 * 
 * 娘的,一般式还是他妈的无济于事啊,改回斜截式,多提供两个成员变量:
 * 一个boolean表示k是否存在,一个额外的float表示k不存在的时候直线方程 x=***, *** 等于多少~
 */
public class CGLine {
	// 特别声明为public类型,免得到时候访问的时候麻烦,到时候直接点就行了
	private boolean kExists;	// 大部分情况下 k 都应该是存在的,因此提供一个 true 的默认值~
 
	public double k = 77885.201314f;
	public double b = 13145.207788f;
	public double extraX = 52077.881314f;
	
	
	/**
	 * 这是当 k 存在时的构造方法~
	 * @param k
	 * @param b
	 */
	public CGLine(double k, double b) {
		this.kExists = true;
		this.k = k;
		this.b = b;
	}
	
	/**
	 * 已知两点,求直线的方程~
	 * @param p1
	 * @param p2
	 */
	public CGLine(CGPoint p1, CGPoint p2) {
		if((p1.x - p2.x) != 0) {
			CGDbg.println("y = k*x + b, k exits!!");
			this.kExists = true;
			this.k = (p1.y - p2.y)/(p1.x - p2.x);
			this.b = (p1.y - p1.x * k);
		} else {
			CGDbg.println("y = k*x + b, k doesn't exists!!");
			// 如果走进这个分支,表示直线垂直于x轴,斜率不存在,保留k的默认值~
			this.kExists = false;
			this.extraX = p1.x;
		}
		CGDbg.print("过p1("+p1.x+", " +p1.y + "), p2("+p2.x+", "+p2.y+")两点的直线方程表达式为: ");
		if(kExists) {
			CGDbg.println("y = " + k + "*x + " + b);
		} else {
			CGDbg.println("x = " + extraX + "(垂直于x轴!)");
		}
	}
	
	/**
	 * 点斜式~
	 * @param p	某点
	 * @param k	过该点的直线的斜率
	 */
	public CGLine(double k, CGPoint p) {
		/**
		 * (y-y') = k*(x-x')
		 * 变形成斜截式为:
		 * y = k*x + y' - k*x'
		 * k = k, b = y'-k*x'
		 */
		this.kExists = true;
		this.k = k;
		this.b = p.y - k * p.x;
	}
	
	/**
	 * 这是当 k 不存在时的构造方法~
	 * @param extraX
	 */
	public CGLine(double extraX) {
		this.kExists = false;
		this.extraX = extraX;
	}
	
	@Override
	public String toString() {
		return "Line.toString()方法被调用,y = k*x + b斜截式, k=" + this.k + 
				", b=" + this.b + 
				", kExists=" + this.kExists + 
				", extraX=" + this.extraX;
	}
	
	public boolean iskExists() {
		return kExists;
	}
	public void setkExists(boolean kExists) {
		this.kExists = kExists;
	}
}


--------------------- 
作者:yang3wei 
来源:CSDN 
原文:https://blog.csdn.net/yang3wei/article/details/7521298 
版权声明:本文为博主原创文章,转载请附上博文链接!

猜你喜欢

转载自blog.csdn.net/qingzhuyuxian/article/details/83444317