Source code for pyturbo.helper.line2D

from typing import Union
from .ray import *
import math
import numpy as np
from .ray import ray2D
from .bezier import bezier

[docs] class line2D(): def __init__(self,pt1,pt2): x = np.array([pt1[0], pt2[0]]) y = np.array([pt1[1], pt2[1]]) self.x = x self.y = y self.p = [x[0], y[0]]; self.q=[x[1], y[1]] self.dx = x[1]-x[0] self.dy = y[1]-y[0] self.length = np.sqrt(self.dx*self.dx+self.dy*self.dy) self.angle = np.arctan2(self.dy,self.dx)
[docs] def to_bezier(self): ''' returns a bezier version of a line ''' b = bezier(self.x,self.y) return b
def ray(self): return ray2D(self.x[0],self.y[0],self.x[1]-self.x[0],self.y[1]-self.y[0])
[docs] def intersect_ray(self,r2:ray2D): """ INTERSECTRAY - CHECK IF A RAY WILL INTERSECT THE LINE """ r1 = self.ray() A = np.array([ [r1.dx, -r2.dx],[r1.dy, -r2.dy] ]) b = np.array([ [r2.x-r1.x], [r2.y-r1.y] ]) T = np.linalg.solve(A,b) t = T[0,0] u = T[1,0] # time, ray # t2 = (r1.y*r2.dx + r2.dy*r2.x - r2.y*r2.dx - r2.dy*r1.x ) / (r1.dx*r2.dy - r1.dy*r2.dx); # t1 = (r1.x+r1.dx*t2-r2.x)/r2.dx [x, y] = r1.get_point(t) [x2, y2] = r2.get_point(u) if (math.sqrt((x-x2)**2 + (y-y2)**2) > 0.001): return False if (t>1 or t<0): # if time is more than 1, the lines wont intersect return t,False if (u<0): return t,False return t,True
[docs] def add_length(self,len): """ Increase the length of the line by a percent """ self.x[1]=self.x[0]+len*self.dx self.y[1]=self.y[0]+len*self.dy self = line2D(self.x,self.y)
[docs] def __eq__(self,line2): """Check if two lines are equal Args: line2 (line2D): second line """ if (self.x == line2.x and self.y == line2.y): return True elif (self.x == np.flip(line2.x) and self.y == np.flip(line2.y)): return True else: return False
def set_length(self,len): self.x[1]=self.x[0]+len*self.dx/math.sqrt(self.dx**2+self.dy**2) self.y[1]=self.y[0]+len*self.dy/math.sqrt(self.dx**2+self.dy**2) self = line2D(self.x,self.y)
[docs] def get_point(self,t:Union[np.ndarray,float]): """Gets the point given a value between 0 and 1 Args: t (Union[np.ndarray,float]): numpy array linspace(0,1,10) or any float value between 0 and 1 Returns: Union[np.ndarray,float]: either a single value or an array of values """ x2=self.dx*t+self.x[0] y2=self.dy*t+self.y[0] return x2,y2
[docs] def get_points2(self,n:int): """Get `n` number of points along a line Args: n (int): _description_ Returns: Union[np.ndarray,float]: either a single value or an array of values """ t = np.linspace(0,1,n) x2=self.dx*t+self.x[0] y2=self.dy*t+self.y[0] return x2,y2
[docs] def get_y(self,x:Union[np.ndarray,float]): """Given an x value, output a y value Args: x (Union[np.ndarray,float]): any value Returns: Union[np.ndarray,float]: either a single value or an array of values """ return self.dy/self.dx * (x-self.x[0]) + self.y[0]
def bezier(self,npoints): t = np.linspace(0,1,npoints) [xx,yy] = self.get_point(t) b = bezier(xx,yy)
[docs] def average(self,new_line): """ averages the line with another line """ self.x = (new_line.x+self.x)/2 self.y = (new_line.y+self.y)/2 self.p = [self.x[0],self.y[0]]; self.q=[self.x[1],self.y[1]] self.dx = self.x[1]-self.x[0]; self.dy = self.y[1]-self.y[0] self.length = math.sqrt((self.x[1]-self.x[0])*(self.x[1]-self.x[0]) + (self.y[1]-self.y[0])*(self.y[1]-self.y[0])) self.angle = math.atan2(self.y[1]-self.y[0],self.x[1]-self.x[0])
def plot2D(self): _, ax1 = plt.subplots() ax1.plot(self.x, self.y,'or') ax1.plot(self.x, self.y,'-b') ax1.set_xlabel("x-label") ax1.set_ylabel("y-label") plt.show()
[docs] def intersect_check(self,line2): """ Intersect check returns true if line segment 'p1q1' and 'p2q2' intersect. """ bIntersect = False p1 = self.p; q1 = self.q p2 = line2.p; q2 = line2.q # Find the four orientations needed for general and special cases o1 = self.__orientation(p1, q1, p2) o2 = self.__orientation(p1, q1, q2) o3 = self.__orientation(p2, q2, p1) o4 = self.__orientation(p2, q2, q1) # General case if (o1 != o2 and o3 != o4): return True # Special Cases # p1, q1 and p2 are colinear and p2 lies on segment p1q1 if (o1 == 0 and self.__onSegment(p1, p2, q1)): return True # p1, q1 and p2 are colinear and q2 lies on segment p1q1 if (o2 == 0 and self.__onSegment(p1, q2, q1)): return True # p2, q2 and p1 are colinear and p1 lies on segment p2q2 if (o3 == 0 and self.__onSegment(p2, p1, q2)): return True # p2, q2 and q1 are colinear and q1 lies on segment p2q2 if (o4 == 0 and self.__onSegment(p2, q1, q2)): return True
[docs] def flip(self): """ reverses the direction of the line """ return line2D(self.q,self.p)
[docs] def line_intersect(self,line2): """ line_intersect - used to be called rayintersect but that's kind of misleading. Output changed to return the point of intersection and t1, t2 Returns [x,y,t1,t2,bIntersect] """ r1 = self.ray() r2 = line2.ray() A = [[r1.dx, -r2.dx], [r1.dy, -r2.dy]] b = [[r2.x-r1.x], [r2.y-r1.y]] T = np.linalg.solve(A,b) t1 = T[0]; t2 = T[1] [x, y] = r1.get_point(t1) [x2, y2] = r2.get_point(t2) if (math.sqrt((x-x2)**2 + (y-y2)**2) > 0.001): return False if (t1>1 or t1<0 or t2>1 or t2<0): # if time is more than 1, the lines wont intersect return False return True
def mag(self): return math.sqrt(self.dx^2+self.dy^2) def angle_between(self,line2): return math.degrees(math.acos((self.dx*line2.dx + self.dy*line2.dy)/(self.mag()*line2.mag()))) def __onSegment(self,p,q,r): """ Private Functions Given three colinear points p, q, r, the function checks if point q lies on line segment 'pr' """ if (q[0] <= max(p[0], r[0]) and q[0] >= min(p[0], r[0]) and q[1] <= max(p[1], r[1]) and q[1] >= min(p[1], r[1])): return True return False def __orientation(self,p,q,r): """ To find orientation of ordered triplet (p, q, r). The function returns following values 0 --> p, q and r are colinear 1 --> Clockwise 2 --> Counterclockwise """ orientation = 0 # See http://www.geeksforgeeks.org/orientation-3-ordered-points/ for details of below formula. val = (q[1] - p[1])*(r[0]-q[0]) - (q[0] - p[0])*(r[1] - q[1]) if (val == 0): orientation = 0; # colinear elif (val>0): orientation = 1 elif (val<0): orientation = 2 # orientation = (val > 0)? 1: 2; % clock or counterclock wise return orientation
[docs] def shrink_start(self,shrink_len): ''' Calculates the new starting point ''' self.x[0] = self.x[1]-self.dx*(self.length-shrink_len)/self.length self.y[0] = self.y[1]-self.dy*(self.length-shrink_len)/self.length self.dx = self.x[1]-self.x[0] self.dy = self.y[1]-self.y[0] self.length = math.sqrt((self.dx)*(self.dx) + (self.dy)*(self.dy)) self.p = [self.x[0],self.y[0]] self.q = [self.x[1],self.y[1]]
[docs] def shrink_end(self,shrink_len): ''' Calculates the new end point ''' self.x[1] = self.x[0]+self.dx*(self.length-shrink_len)/self.length self.y[1] = self.y[0]+self.dy*(self.length-shrink_len)/self.length self.dx = self.x[1]-self.x[0] self.dy = self.y[1]-self.y[0] self.length = math.sqrt((self.dx)*(self.dx) + (self.dy)*(self.dy)) self.p = [self.x[0],self.y[0]] self.q = [self.x[1],self.y[1]]
[docs] def fillet(self,prev_line,filletR:float): ''' Creates a fillet with the previous line, how the line terminates doesn't matter inputs: prev_line - Line2D class filletR - fillet radius returns: prev_line - previous line fillet ''' # Check the start and end points see where it intersects if (self.p == prev_line.p): # Beginning of first line and beginning of 2nd line, intersections happen at start # shrink self(first line) at the beginning # shrink prev line at the end ix = self.x[0] iy = self.y[0] self.shrink_start(filletR) prev_line.shrink_start(filletR) fillet = bezier([prev_line.x[0],ix,self.x[0]], [prev_line.y[0],iy,self.y[0]]) return prev_line,fillet elif (self.p == prev_line.q): # Beginning of first line and end of 2nd line # shrink self(first line) at the end # shrink prev line at the beginning ix = self.x[0] iy = self.y[0] self.shrink_start(filletR) prev_line.shrink_end(filletR) fillet = bezier([self.x[0],ix,prev_line.x[1]], [self.y[0],iy,prev_line.y[1]]) return prev_line,fillet elif (self.q==prev_line.p): # End of first line and beginning of 2nd line ix = self.x[1] iy = self.y[1] self.shrink_end(filletR) prev_line.shrink_start(filletR) fillet = bezier([self.x[1],ix,prev_line.x[0]], [self.y[1],iy,prev_line.y[0]]) elif (self.q==prev_line.q): # End of first line and end of 2nd line # Reverse the current line ix = self.x[1] iy = self.y[1] self.shrink_end(filletR) prev_line.shrink_end(filletR) fillet = bezier([self.x[1],ix,prev_line.x[1]], [self.y[1],iy,prev_line.y[1]]) return prev_line,fillet