Geometry Puzzler (part 2: intersecting segments)

Mike Brenner mikeb at mitre.org
Mon Nov 6 16:34:14 EST 2000


Mike Fletcher > 2) Given two line segments, how can I find the
coordinates of their intersection, if there is one? (easy for right
angles, but I will often NOT have right angles.)

# This python software demonstrates how to compute the
# intersection of two line segments with no warranty.
# It is followed by a tiny test that shows how to call it. 
# It seems like all that kind of simple Algebra/Geometry
# should be available as part of the basic tools of the trade. 

# Most of it is just object oriented expression 
# that could be filtered out, 
# if this was the only thing that was being done 
# with the segments being intersected.

# It first computes the intersection of the lines on which the segment
lies, 
# if any, and then determines if that intersection lies on the first
segment, 
# thus there is no need to actually compute the second parameter.


import math

line_threshold=0.00001; # Closer than this, two lines are considered to
be the same line.

def tell(message):
  sys.stdout.softspace=0
  print(message);
         
def telln(message):
  print message;

def close(left, right, threshold):
  return abs(left-right)<=threshold;
  
class points2D:
  'Points === <x,y>'
  x_meters=0.0;
  y_meters=0.0;
  def __init__(point,x_meters,y_meters): # Point constructor,
    point.x_meters=float(x_meters)
    point.y_meters=float(y_meters)
  def x_of(point): # Extracts the x coordinate of a point
    return point.x_meters;
  def y_of(point):
    return point.y_meters; # Extracts the y coordinate of a point
  def __add__(left,right): # Adds two points
    assert(right.is_point2D());
    a=left.x_of()+right.x_of();
    b=left.y_of()+right.y_of();
    return points2D(a,b); 
  def __sub__(left,right): # Subtracts two points
    assert(right.is_point2D());
    a=left.x_of()-right.x_of();
    b=left.y_of()-right.y_of();
    return points2D(a,b); 
  def close(left,right,thresh): # Determines if left and right are close
to each other.
    assert(right.is_point2D());
    return close(left.x_of(),right.x_of(),thresh) and \
           close(left.y_of(),right.y_of(),thresh)
  def distance_squared (left, right): # Square of the distance between
two points.
    assert(right.is_point2D());
    return (right.x_of()-left.x_of())**2 + \
           (right.y_of()-left.y_of())**2
  def distance (left, right): # Distance between two points.
    assert(right.is_point2D())
    return math.sqrt(left.distance_squared(right));   
  def image(point): # Displayable image of a point.
    x,y=point.x_of(),point.y_of()
    return "["+`x`+", "+`y`+"]"
  def is_point2D(point): # Type test.
    return 1;
  def slope(left,right,threshold=0.00001): # Slope of a line.
    assert(right.is_point2D());
    denominator=right.x_of()-left.x_of();
    numerator  =right.y_of()-left.y_of();
    if close(denominator,0.0,threshold):
      telln("       slope left="+left.image())
      telln("            right="+right.image())
      raise slope_is_infinite_line_almost_vertical;
    return numerator/float(denominator);
  def times_scalar (point,scalar): # Scalar vector multiplication.
    a=scalar*point.x_of();
    b=scalar*point.y_of();
    return points2D(a,b);
  def y_intercept(point,slope): # The y_intercept of a nonvertical line.
    return point.y_of()-slope*point.x_of();
  
class lines:
  'Lines === <left_point,right_point>'
  left_point=points2D(0.0,0.0);
  right_point=points2D(0.0,0.0);
  def __init__(line,left_point,right_point): # Line constructor.
    assert(left_point.is_point2D())
    assert(right_point.is_point2D())
    if left_point.distance_squared(right_point)< line_threshold:
      raise lines_require_two_distinct_points
    line.left_point=left_point
    line.right_point=right_point
  def left_of(line): # Extracts the left point of a line.
    return line.left_point
  def right_of(line): # Extracts the right point of a line.
    return line.right_point
  def image(line): # The displayable image of a line.
    my_slope=line.left_of().slope(line.right_of())
    return "[" + line.left_of().image() + "," + \
           line.right_of().image() + \
           " slope=" + `my_slope` + \
           " y_int=" + `line.left_of().y_intercept(my_slope)` + "]";
  def intersection_lambdas (left_line, right_line,infinite):
    # Computes the intersection of two lines as parameters, of which the
first is used.
    # This is designed to work on all 2-D lines including vertical
lines.
    # t1 R1 + (1-t1)L1 = p = t2 R2 + (1-t2) L2 gives:
    # (R1-L1)t1 + (L2-R2) t2 + (L1-L2) = 0     gives simultaneously:
    # e t1 + c t2 + a = 0
    # f t1 + d t2 + b = 0
    assert(right_line.is_line());
    a=left_line.left_of().x_of()  - right_line.left_of().x_of();
    b=left_line.left_of().y_of()  - right_line.left_of().y_of();
    c=right_line.left_of().x_of() - right_line.right_of().x_of();
    d=right_line.left_of().y_of() - right_line.right_of().y_of();
    e=left_line.right_of().x_of() - left_line.left_of().x_of();
    f=left_line.right_of().y_of() - left_line.left_of().y_of();
    denominator=d*e-c*f;
    if close(denominator,0.0,line_threshold):
      return [infinite,infinite];
    else:  
      numerator=a*f-b*e;
      lambda2 = numerator/float(denominator);
    if   not close(e,0.0,line_threshold):  
      lambda1 = (-a-c*lambda2)/e; 
    elif not close(f,0.0,line_threshold):        
      lambda1=(-b-d*lambda2)/f
    else: 
      return [infinite,infinite];
    return [lambda1, lambda2];
  def is_line(line): # Type check.
    return 1;  
  def parameter_of(line,point): # Converts a point to parameter form.
    # Returns the parameter of a point on a line. Assumes that the point
is on the line.
    assert(point.is_point2D());
    numerator  =point-line.left_of();
    denominator=line.right_of()-line.left_of();
    if denominator.close(points2D(0.0,0.0),line_threshold):
      raise bad_line;
    if not close(denominator.x_of(),0.0,line_threshold): 
      return numerator.x_of()/denominator.x_of();
    else:
      return numerator.y_of()/denominator.y_of();
  def parameter2Point(line,llambda): # Converts a parameter to point
form.
    difference = line.right_of()-line.left_of();
    return line.left_of()+difference.times_scalar(llambda);
  def print_segment_intersection(line1,line2):
    t1,t2=line1.intersection_lambdas(line2,20000000.0)
    print "-----------------------------------------------"
    print "Line 1="+line1.image()
    print "Line 2="+line2.image()
    print "Intersection point for parameter +`t1` is " +
line1.parameter2Point(t1).image()
    print "The intersection point is on the segment: "
+["no","yes"][0.0<=t1<=1.0]
  
  
def linetest():
  P1=points2D(5,0)
  P2=points2D(0,5)
  L1=lines(P1,P2)
  P3=points2D(0,0)
  P4=points2D(1,1)
  L2=lines(P3,P4)
  L1.print_segment_intersection(L2)

linetest()





More information about the Python-list mailing list