calculate a perpendicular offset from a diagonal line

前端 未结 3 858
小蘑菇
小蘑菇 2020-12-09 06:10

I am writing a music display program and need to draw a \'slur\' between two notes. A slur is a curved line linking two notes - just to be clear.

相关标签:
3条回答
  • 2020-12-09 06:21

    I took legends2k excellent answer and converted to Java on Android. This might help someone save some time.

    private PointF getPerpendicularPoint(int startX, int startY, int stopX, int stopY, float distance)
    {
        PointF M = new PointF((startX + stopX) / 2, (startY + stopY) / 2);
        PointF p = new PointF(startX - stopX, startY - stopY);
        PointF n = new PointF(-p.y, p.x);
        int norm_length = (int) Math.sqrt((n.x * n.x) + (n.y * n.y));
        n.x /= norm_length;
        n.y /= norm_length;
        return new PointF(M.x + (distance * n.x), M.y + (distance * n.y));
    }
    
    0 讨论(0)
  • 2020-12-09 06:22

    Given the line segment AB, you can find the midpoint, say M, using the famous midpoint formula (A + B)/2. Now calculate the vector from B to A:

    p = <p.x, p.y> = AB

    Rotate it about the origin by 90° counter-clockwise to get the perpendicular vector

    n = <n.x, n.y> = < ‒ p.y, p.x >

    Normalise it:

    n = <n.x, n.y> / ‖n‖ where ‖n‖ = √(n.x² + n.y²) is the Euclidean Norm or length

    C = L(t) = M + t n

    Using this equation -- parametric form of a line -- you can find any number of points along the perpendicular line (in the direction of n). t is the distance of the obtained point, C, from M. When t = 0, you get M back, when t = 1, you get a point 1 unit away from M along n and so on. This also works for negative values of t, where the points obtained will be on the opposite side of AB i.e. towards the note. Since t can be a decimal number, you can play with it by changing its values to get the desired distance and direction of the obtained point from M.

    Code, since you said you're not interested in the math jargon ;)

    vec2d calculate_perp_point(vec2d A, vec2d B, float distance)
    {
       vec2d M = (A + B) / 2;
       vec2d p = A - B;
       vec2d n = (-p.y, p.x);
       int norm_length = sqrt((n.x * n.x) + (n.y * n.y));
       n.x /= norm_length;
       n.y /= norm_length;
       return (M + (distance * n));
    }
    

    This is just pseudo code, since I'm not sure of the vector math library you are using for your project.

    Boldface variables above are 2-d vectors; uppercase letters denote points and lowercase ones are vectors with no position

    0 讨论(0)
  • 2020-12-09 06:43

    And here’s a Swift version:

    func pointNormalToLine(startPoint: CGPoint, endPoint: CGPoint, distance: CGFloat) -> CGPoint {
    
        let midpoint = CGPoint(x: (startPoint.x + endPoint.x) / 2, y: (startPoint.y + endPoint.y) / 2)
        let p = CGPoint(x: startPoint.x - endPoint.x, y: startPoint.y - endPoint.y)
        var n = CGPoint(x: -p.y, y: p.x)
        let norm_length = sqrt((n.x * n.x) + (n.y * n.y))
        n.x /= norm_length
        n.y /= norm_length
        return CGPoint(x: midpoint.x + (distance * n.x), y: midpoint.y + (distance * n.y))
    }
    
    0 讨论(0)
提交回复
热议问题