Is there any way to graph a continuous line with a slope calculated from coordinate pairs?

101 Views Asked by At

I am brand new at coding and I am currently working on creating a program that calculates the slope-intercept form of a line and information like the x-intercept and y-intercept of that line originating from the values of any two random coordinate pairs for example (x1,y1), (x2,y2) = (1,2), (3,4) what this presents as in matplotlib, when plotted is a line segment with a calculated slope (happens to be 1 here y = 1x + 1) that doesn't cross any axis'. I would like to draw another line over it with the same slope as the line segment that is continuous, to show where the line segment would cross the x and y-axis, but I want to do this for any combination of random coordinate pairs input by the user. I would also like to set the graph to load with the origin of the graph (0,0) at the bottom left in the frame when it is produced and not have the graph centered around my line segment when it is produced. Any help is much appreciated.

import numpy as np
import math
x1 = float(input("Enter a x coordinate for x1: "))
y1 = float(input("Enter a y coordinate for y1: "))
x2 = float(input("Enter a x coordinate for x2: "))
y2 = float(input("Enter a y coordinate for y2: "))
m = (y2-y1)/(x2-x1)
d = math.sqrt((x2 - x1)**2 + (y2-y1)**2)
slope_multiplied_by_negative_x1 = m * (-1 * x1)
b = float(y1) + float(slope_multiplied_by_negative_x1)
b_absolute = abs(b)
result = "y = " + (str(m) + "x " if m != 0 else "")
if m == 0:
    sign = ""
elif b == 0:
    sign = ""
elif b > 0:
    sign = "+ "
else:
    sign = "- "
try: X_intercept = float((-1 * b)/m)
except ZeroDivisionError:
    X_intercept = 'n/a'
print(result + sign + ("" if b == 0 else (str(b_absolute))))
print("X intercept: " + ("0.0" if X_intercept == 0 else str(X_intercept)))
print("Y intercept: " + str(b))
print("Distance between (x1,y1) and (x2,y2): " + str(d))
x = [x1,x2]
y=[y1,y2]
t = np.arange(0.0, 2.0, 0.01)
fig, ax = plt.subplots()
plt.plot(x, y, color='c', linestyle='-', marker='o')
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.grid()
plt.show()
2

There are 2 best solutions below

0
On

Segment with end points (x1,y1), (x2,y2) belongs to line with equation

(x - x1) / (x2 - x1) = (y - y1) / (y2 - y1)

At first we need to check extra cases of vertical and horizontal lines:

if x1 == x2:
    x_intercept = x1
    y_intercept does not exist 
    use screen window border coordinates to draw line
    (x1,0)-(x1,height)  

if y1 == y2:
    y_intercept = y1
    x_intercept does not exist
    use screen window border coordinates to draw line
    (0,y1)-(width,y1)  

Otherwise, to find intersections with axes, we can just substitute y=0 or x=0 to this equation.

x_intercept = x1 - y1 * (x2 - x1) / (y2 - y1)
y_intercept = y1 - x1 * (y2 - y1) / (x2 - x1)
draw line
(0, y_intercept) - (x_intercept, 0)  

P.S. Note that you rarely need slope in computational geometry - there is a lot of more general approaches for line definition (described one, parametric method, theta-rho method)

1
On

Here is an approach without changing the original code too much. It tests for horizontal and vertical lines, and gives a result when start and end points are equal.

The point (0,0) is enforced to be in the plot by drawing an invisible dot there. Lines are added between each of the endpoints and each of the intercepts. This will also enforce both (if they exist) to be in the plot area. You'll notice this will draw the same segments multiple times, but it avoids too many tests to know which segments are needed. The order of drawing makes sure the segment between (x1,y1) and (x2,y2) is on top.

Numpy is removed as it is not needed here, but it would be useful for other types of curves. Conversions to float are usually not needed in the calculations, as Python3 automatically converts integers to floats for divisions.

Some extensions that could be considered:

  • testing floats for being equal to avoid division by 0 can be dangerous for floats resulting from large calculations; sometimes floats are almost-but-not-exact equal and division by their difference results in overflow (or undesired large numbers)
  • to extend the lines until they touch the borders, you'll need to solve equations to find intersections with each of the four borders and reject those intersections that fall outside the other borders
  • alternatively, you could draw a very long line and afterwards reapply the x and y limits

Here is some code that can serve as a base for further experimentation:

import matplotlib.pyplot as plt
import math

x1 = float(input("Enter a x coordinate for x1: "))
y1 = float(input("Enter a y coordinate for y1: "))
x2 = float(input("Enter a x coordinate for x2: "))
y2 = float(input("Enter a y coordinate for y2: "))

d = math.sqrt((x2 - x1)**2 + (y2-y1)**2)
if x2 != x1:
    m = (y2-y1)/(x2-x1)
    slope_multiplied_by_negative_x1 = m * (- x1)
    b = y1 + slope_multiplied_by_negative_x1
    result = "y = " + (str(m) + "x" if m != 0 else "")
    if m != 0:
        X_intercept = (-1 * b) / m
    else:
        X_intercept = 'n/a'
    if m == 0:
        sign = ""
    elif b < 0:
        sign = " - "
    else:
        sign = " + "
    result = result + ("" if b == 0 else sign + str(abs(b)))
elif y1 != y2:
    result = "x = " + str(x1)
    X_intercept = x1
    b = 'n/a'
else:
    result = "(x, y) = (" + str(x1) + ", " + str(y1) + ')'
    X_intercept = 'n/a' if x1 != 0 else x1
    b = 'n/a' if y1 != 0 else y1
print(result)
print("X intercept: " + ("0.0" if X_intercept == 0 else str(X_intercept)))
print("Y intercept: " + str(b))
print("Distance between (x1,y1) and (x2,y2): " + str(d))

fig, ax = plt.subplots()
plt.plot(0, 0, color='k', marker='')  # just to make sure the plot contains 0,0
if X_intercept != 'n/a':
    plt.plot([X_intercept, x1], [0, y1], color='m', marker='o', linewidth=0.5)
    plt.plot([X_intercept, x2], [0, y2], color='m', marker='', linewidth=0.5)
if b != 'n/a':
    plt.plot([0, x1], [b, y1], color='m', marker='o', linewidth=0.5)
    plt.plot([0, x2], [b, y2], color='m', marker='', linewidth=0.5)
plt.plot([x1, x2], [y1, y2], color='c', linestyle='-', marker='o')
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
ax.grid()
plt.show()

enter image description here

Here is an approach to draw a long line. zorder=-1 is used to force this line behind the other lines and dots. We call this at the end (just before plt.show) so matplotlib can automatically calculate the limits to fit all the earlier stuff.

xlims = plt.xlim()  # save the current limits
ylims = plt.ylim()
dx = (x2 - x1) * 100
dy = (y2 - y1) * 100
plt.plot([x1 - dx, x2 + dx], [y1 - dy, y2 + dy], color='k', linestyle='-', marker='', linewidth=0.5, zorder=-1)
plt.xlim(xlims)  # reapply the limits
plt.ylim(ylims)