如何在这些条件下计算两个圆的第三组坐标的交点?

时间:2017-08-29 18:47:17

标签: swift xcode math uibezierpath

小圆圈可左右移动任意数量 我必须在哪里计算红点的坐标 它的位置是,如果它们相交。我只在这种情况下计算。我必须找到交点,并确保它是红点上的交点,而不是它下面的交点,所以总是那个Y值较高的交点。

我已经解决了三角形和蓝点的所有距离。 如何计算红点?

enter image description here

如果您想查看我当前的代码以帮助调试它,请尝试此操作。

我的游乐场测试:

//: Playground - noun: a place where people can play

import UIKit
infix operator **

let pretendWidth: CGFloat = 374
let pretendHeight: CGFloat = 7

// Testing scenario is pretendWidth..<(pretendWidth + (pretendHeight / 2))
let spacer: CGFloat = 0.5

extension CGFloat {


    public static func **(base: CGFloat, exp: CGFloat) -> CGFloat {
        return CGFloat(pow(Double(base), Double(exp)))
    }

}

class BottomBarGradientNode: UIView {

    override func draw(_ rect: CGRect) {
        guard let context = UIGraphicsGetCurrentContext() else { return }
        context.saveGState()
        context.clip(to: bounds)

        // Gradient Creation
        let locations: [CGFloat] = [0, 1]
        let components: [CGFloat] = [0.2706, 0.6863, 0.8902, 1, 0, 0.8745, 0.7294, 1]
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let gradient: CGGradient = CGGradient(colorSpace: colorSpace, colorComponents: components, locations: locations, count: 2)!
        let startPoint = CGPoint(x: bounds.maxX, y: bounds.maxY)
        let endPoint = CGPoint(x: bounds.minX, y: bounds.minY)


        let halfHeight = bounds.height / 2
        let path = UIBezierPath()
        let startPointForPath = CGPoint(x: bounds.width - halfHeight, y: 0)
        path.move(to: startPointForPath)
        let firstCenterPoint = CGPoint(x: bounds.width - halfHeight, y: halfHeight)
        let secondCenterPoint = CGPoint(x: pretendWidth - bounds.height, y: 0)
        Computation: if bounds.width > (pretendWidth + halfHeight) {
            path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
        } else if bounds.width > pretendWidth {
//
// ------------------------------------------------------------------------------------------------------------------------------------
//            Though this looks like a complicated geometry problem, this is really best done as an ugly algebra problem.
//            We want the coordinates of the red dot: (x,y)
//            We know the red dot is on the big circle and since that circle is not moving I'm going to call it's center (0,0) thus:
//            x^2 + y^2 = 49
//            We also know that the red dot is on the little circle, it has a moving center but we know that the y value for that
//            center is always -3.5. so we'll let the x-value of that center be t:
//            (x-t)^2 + (y-3.5)^2 = (3.5)^2
//            which expands to:
//            x^2 - 2xt + t^2 + y^2 -7y + (3.5)^2 = (3.5)^2
//            which when we plug in our other equation simplifies to:
//            y = (1/7)(-2tx + 49 + t^2)
//            plugging that back into the first equation gives:
//            x^2 + ((1/7)(-2tx + 49 + t^2))^2 = 49
//            which is terrible to look out but turns out to be a quadratic equation in x, so from this point you'd just simplify
//            and plug it into the quadratic formula. Pick the value of x that is smaller in magnitude (be careful about negatives
//            here). Then plug that x back into the first equation to solve for y.
// ------------------------------------------------------------------------------------------------------------------------------------
//
            let boundsHeightSquared = bounds.height ** 2
            let distanceFromOtherCenter = firstCenterPoint.x - secondCenterPoint.x

            // x^2 + ((1/7)(-2tx + 49 + t^2))^2 = 49 <<<< translates to VVVVVV
            //
            // ((4/49)t^2 + 1)(x^2) + (-4t - (4t^3/49))(x) + (2t^2 + (t^4)/49) = 0
            // ^^^^^^^^^^^^^^^        ^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^
            //     value1(a)               value2(b)             value3(c)
            let value1 = ((4 * (distanceFromOtherCenter ** 2)) / boundsHeightSquared) + 1
            let value2 = (-4 * distanceFromOtherCenter) - ((4 * (distanceFromOtherCenter ** 3)) / boundsHeightSquared)
            let value3 = (2 * (distanceFromOtherCenter ** 2)) + ((distanceFromOtherCenter ** 4) / boundsHeightSquared)

            let (first, second) = getQuadraticValues(a: value1, b: value2, c: value3)
            // guarentee positive values
            var areBothGreaterThanZero: Bool = false
            var chosenX: CGFloat!
            if first < 0 { chosenX = second }
            else if second < 0 { chosenX = first }
            else { chosenX = first < second ? first : second; areBothGreaterThanZero = true }

            // y = (1/7)(-2tx + 49 + t^2)
            var chosenY = (1 / bounds.height) * ((-2 * distanceFromOtherCenter * chosenX) + boundsHeightSquared - (distanceFromOtherCenter ** 2))

            // last check on weird values
            if chosenY < 0 && areBothGreaterThanZero {
                chosenX = first < second ? first : second
                chosenY = (1 / bounds.height) * ((-2 * distanceFromOtherCenter * chosenX) + boundsHeightSquared - (distanceFromOtherCenter ** 2))
            }

            // Computatation failed. Show full segment.
            if chosenY < 0 {
                print("Computation Failed")
                path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: 0, endAngle: CGFloat.pi / 2, clockwise: true)
                break Computation
            }

            // true point
            let intersectingPoint = CGPoint(x: chosenX + secondCenterPoint.x, y: chosenY)

            // c^2 = a^2 + b^2 - 2abCOS(C)
            // (a^2 + b^2 - c^2) / 2ab = COS(C)
            let topPoint = CGPoint(x: firstCenterPoint.x, y: 0)
            // compute c (distance)
            let firstDistanceBetweenPoints = getDistanceBetweenTwoPoints(firstPoint: intersectingPoint, secondPoint: topPoint)
            // where a and b are halfHeight
            let firstCosC = getCosC(a: halfHeight, b: halfHeight, c: firstDistanceBetweenPoints)
            let firstAngle = acos(firstCosC)
            path.addArc(withCenter: firstCenterPoint, radius: halfHeight, startAngle: CGFloat.pi * 1.5, endAngle: CGFloat.pi * 1.5 + firstAngle, clockwise: true)

            // c^2 = a^2 + b^2 - 2abCOS(C)
            // (a^2 + b^2 - c^2) / 2ab = COS(C)
            let lastPoint = CGPoint(x: pretendWidth, y: 0)
            // compute c (distance)
            let secondDistanceBetweenPoints = getDistanceBetweenTwoPoints(firstPoint: lastPoint, secondPoint: intersectingPoint)
            // where a and b are bounds.height
            let secondCosC = getCosC(a: bounds.height, b: bounds.height, c: secondDistanceBetweenPoints)
            let secondAngle = acos(secondCosC)
            path.addArc(withCenter: secondCenterPoint, radius: bounds.height, startAngle: secondAngle, endAngle: CGFloat.pi / 2, clockwise: true)
        } else {
            path.addArc(withCenter: firstCenterPoint, radius: halfHeight, startAngle: CGFloat.pi * 1.5, endAngle: CGFloat.pi / 2, clockwise: true)
        }
        path.addLine(to: CGPoint(x: bounds.height, y: bounds.height))
        let finalCenterPoint = CGPoint(x: bounds.height, y: 0)
        path.addArc(withCenter: finalCenterPoint, radius: bounds.height, startAngle: CGFloat.pi / 2, endAngle: CGFloat.pi, clockwise: true)
        path.addLine(to: startPointForPath)
        path.close()
        path.addClip()
        context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: .drawsAfterEndLocation)
        context.restoreGState()
    }

}

func getDistanceBetweenTwoPoints(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
    let diffX = (firstPoint.x - secondPoint.x) ** 2
    let diffY = (firstPoint.y - secondPoint.y) ** 2
    return sqrt(diffX + diffY)
}

func getSlopeBetweenTwoPoints(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
    let diffY = firstPoint.y - secondPoint.y
    let diffX = firstPoint.x - secondPoint.x
    return diffY / diffX
}

func getHypotenuse(firstDistance: CGFloat, secondDistance: CGFloat) -> CGFloat {
    return sqrt((firstDistance ** 2) + (secondDistance ** 2))
}

func getQuadraticValues(a: CGFloat, b: CGFloat, c: CGFloat) -> (CGFloat, CGFloat) {
    let first = (-b + sqrt((b ** 2) - (4 * a * c))) / (2 * a)
    let second = (-b - sqrt((b ** 2) - (4 * a * c))) / (2 * a)
    return (first, second)
}

func getCosC(a: CGFloat, b: CGFloat, c: CGFloat) -> CGFloat {
    // (a^2 + b^2 - c^2) / 2ab = COS(C)
    return ((a ** 2) + (b ** 2) - (c ** 2)) / (2 * a * b)
}

// Testing scenario is pretendWidth..<(pretendWidth + (height / 2))
let bounds = CGRect(x: 0, y: 0, width: pretendWidth + spacer, height: pretendHeight)
let bar = BottomBarGradientNode(frame: bounds)

1 个答案:

答案 0 :(得分:1)

找到两个交叉点,然后选择合适的交点。或者根据 y 坐标制定解决方案,然后在那里选择更高的解决方案来计算 x 坐标。

圆1的等式是( x 2 + y 2 )+ a <子> 1 X + b'/ EM> <子> 1 ý + ç <子> 1 = 0。以这种形式写下两个圆,然后从另一个中减去一个方程。二次项将取消,剩下的等式描述圆的radical axis ax + by + c = 0其中 a = a 1 - a 2 等等。解决 x = - ( by + c )/ a 。将此术语插入圆的原始方程之一,并且在 y 中有一个二次方程式。

现在 y 中的二次方程的形式为 py 2 + qy + r = 0并有解决方案 - q ±sqrt( q 2 -4 pr )/ 2 p 的。查看 p 的符号,然后在平方根前面选择相同的符号,以获得具有更大 y 值的解决方案。将其插回到激进轴的等式中以计算 x 坐标。

如果没有交集, q 2 -4 pr &lt; 0,你的解决方案将变得复杂。如果 a = 0,您的激进轴是水平的,因此您无法通过 y 值对其进行参数化,并通过 y 值选择解决方案没有任何意义。