skip to Main Content

I’m writing code in Swift and I want to draw CAShapeLayers with BezierPaths as it’s presented in the image below.

How can I calculate C & D points if points A & B are known, distance between A & B is known, and length between points C & D is also known? Also, B is exactly mid-way between C & D.

enter image description here

After C and D points are calculated, a line between them would be drawn using UIBezierPath like this:

let path: UIBezierPath = UIBezierPath()
path.move(to: CGPoint(x: Cx, y: Cy))
path.addLine(to: CGPoint(x: Dx, y: Dy))
layer.path = path.cgPath

Thanks for your help and time.

3

Answers


  1. Here’s the math that I would use 🙂

    First you can calculate the angle in the Cartesian plane from A to B:

    theta_AB = atan2(By-Ay, Bx-Ax)
    

    The angle of the perpendicular line CD is just theta + pi/2

    theta_CD = theta_AB + pi/2
    

    Then assume that d is the known distance between C and D.

    Then you have:

    Cx = Bx + cos(theta_CD) * d/2
    Cy = By + sin(theta_CD) * d/2
    Dx = Bx - cos(theta_CD) * d/2
    Dy = By - sin(theta_CD) * d/2
    

    So putting it all together with (hopefully) correct Swift syntax, you’d have:

    let theta_AB = atan2(By-Ay, Bx-Ax);
    let theta_CD = theta_AB + Double.pi/2.0;
    let Cx = Bx + cos(theta_CD) * d/2;
    let Cy = By + sin(theta_CD) * d/2;
    let Dx = Bx - cos(theta_CD) * d/2;
    let Dy = By - sin(theta_CD) * d/2;
    let C : CGPoint = CGPoint(x: Cx, y: Cy);
    let D : CGPoint = CGPoint(x: Dx, y: Dy);
    

    Or something like that

    Login or Signup to reply.
  2. You can figure this out with algebra and the Pythagorean theorem, or trig.

    Trig seems easier. Here is my off-the-cuff attempt to solve the problem using trig (not tested):

    Let’s call point A’s coordinates (Ax,Ay) and B’s coordinates (Bx,By)

    The angle between your line segment AB and the horizontal would be

    theta = atan2(By-Ay, Bx-Ax)
    

    The angle between line segment BD and the vertical line that drops from B would be theta + pi/2. (Swift’s trig libraries use radians, and pi/2 radians is 90°, or an angle perpendicular to the original angle.) Let’s call the perpendicular angle theta2.

    Let’s call the length of your line segment CD L. The length of line segments BD and BC will both be half of that, or L/2.

    The change in y from your point B to point D would be:

    deltaY = L/2 • cos(theta2)
    

    The change in x from your point B to point D would be

    deltaX = L/2 • sin(theta2)
    

    The coordinates of point D would be

    Dx = Bx + deltaX
    Dy = By + deltaY
    

    The coordinates of point C would be

    Cx = Bx - deltaX
    Cy = By - deltaY
    
    Login or Signup to reply.
  3. This is a math question. It really doesn’t matter which language but I will post the Swift solution.

    To figure out the angle from another point and the point with a certain angle and distance you will need those helpers:

    extension CGPoint {
    
        func angle(from location: CGPoint) -> Double {
            let deltaY = location.y - y
            let deltaX = location.x - x
            let angle = atan2(deltaY, deltaX) * 180 / .pi
            return angle
            // if you need the angle reversing y directon
            // return angle < 0 ? abs(angle) : 360 - angle
        }
    
        func moving(distance: Double, at angle: Double) -> CGPoint {
            .init(
                x: x + distance * __cospi(angle/180),
                y: y + distance * __sinpi(angle/180)
            )
        }
    }
    

    let pointA: CGPoint = .zero
    let pointB: CGPoint = .init(x: 100, y: 100)
    
    let angleAfromB = pointA.angle(from: pointB)  // 45
    let angleBfromA = pointB.angle(from: pointA)  // 225
    
    let pointE = pointA.moving(distance: 1, at: 0)     // {x 1 y 0}
    let pointF = pointA.moving(distance: 1, at: 90)    // {x 0 y 1}
    let pointG = pointA.moving(distance: 1, at: 180)   // {x -1 y 0}
    let pointH = pointA.moving(distance: 1, at: 270)   // {x 0 y -1}
    
    let path = UIBezierPath()
    path.move(to: pointA)
    path.addLine(to: pointB)
    path.close()
    path.stroke()
    
    let pointC = pointB.moving(distance: 50, at: angleAfromB + 90)
    let pointD = pointB.moving(distance: 50, at: angleAfromB - 90)
    let pathCD = UIBezierPath()
    path.move(to: pointC)
    path.addLine(to: pointD)
    path.close()
    path.stroke()
    

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search