Search code examples
rmathgeometrycurve

Drawing a perpendicular line between a point along a line and a curve in R


I am hoping to calculate the distance between the midpoint of one line and a curve above it in R, but am mathematically dense. My curves all look something like what is shown in the code below:

Curve <- data.frame(X = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, 4.9046, 6.1424, 7.275, 8.5851, 10.0373, 11.9981, 13.7726, 15.0731,16.0664, 
18.1945, 21.2666, 24.2093, 26.7119, 28.8037, 30.7135, 32.1351,  33.1982, 34.2341, 35.7587, 37.2147, 38.4303, 39.625, 40.4596, 42.0938, 42.7428, 42.7593, 43.5085, 43.7419, 43.5989, 44.0841, NA, NA, NA), 
Y = c(NA, NA, NA, NA, NA, NA, NA, NA, NA, -9.9938, -7.4596, -4.8647, -2.2903, 0.3158, 2.9302, 5.7262, 8.7033, 11.8007, 14.9847, 16.7225, 16.7813, 15.6921, 14.2964, 11.5579, 8.2378, 5.183, 1.5938, -2.0712, -5.195, -7.1447, -9.0446, -11.1269, -13.0979, -15.3295, -17.1898, -19.4376, -21.4781,-23.8426, -25.6343, NA, NA, NA),              
fan_line = 1:42)    

I am able to draw a line between the first and last xy coordinates and find the midpoint of that line (as in the image below). From this midpoint I now need to construct a perpendicular line that extends from the midpoint up to the curve and extract the xy coordinates at this point. I'm at a loss as to how this is done.

enter image description here

All my plotting is done in ggplot if that is relevant, but principally I am interested in extracting the XY coordinates perpendicular to the midpoint shown in the image (the midpoint between the curve's first and last xy coordinates), so the values rather than the plotting itself.

I'd previously been calculating the mid x and y coordinates of the curve on the basis of the middle X-coordinate (ignoring NA values) but that's really not what I want at all.

Ultimately I am interested in obtaining a value that is the distance of the dotted line in my image, divided by the length of the perpendicular line.

Please excuse my mathematical ignorance

Edit: As Limey points out there are any number of perpendicular lines from a point. I mean vertical from the midpoint along the dotted line I've drawn.

Edit2: Sorry to be so unclear about what I'm looking for. Basically, I want to end up essentially obtaining something like the red line in the image below (forgive the Microsoft Paint job).

enter image description here


Solution

  • The approx() function allows you to find the y coordinate corresponding to a given x coordinate, for example with:

    approx(Curve$X, Curve$Y, mean(range(Curve$X, na.rm=TRUE)) )
    # $x
    # [1] 24.49435
    # 
    # $y
    # [1] 16.65724
    

    However it appears that you want to find the point on the curve which is perpendicular to the line between the endpoints at its midpoint. To do this, you can rotate the curve by the gradient of the line joining the endpoints so that the line is flat, use the approach above, and then rotate in the opposite direction. The code to do this is:

    library(lava)
    
    Ends <- Curve[Curve$X %in% range(Curve$X, na.rm=TRUE),]
    
    theta <- atan(diff(Ends$Y)/diff(Ends$X))
    
    rotCurve <- rotate2(as.matrix(Curve)[,1:2], theta)
    rotEnds <- rotate2(as.matrix(Ends)[,1:2], theta)
    
    rotPoint <- approx(rotCurve[,1], rotCurve[,2], mean(rotEnds[,1]) )
    
    Point <- rotate2(matrix(c(rotPoint$x, rotPoint$y), ncol=2), -theta)
    #         [,1]     [,2]
    # [1,] 33.39819 4.490086
    

    With a quick plot to verify it looks like your diagram

    ggplot() + geom_line(aes(x=X, y=Y), Curve) + geom_line(aes(x=X, y=Y), data=Ends, lty=2) + 
      geom_line(aes(x=c(mean(Ends$X), Point[1]), y=c(mean(Ends$Y),Point[2])), colour='red') + coord_equal()
    

    Output plot