def curve_length(curve, t0=0, t1=1): """ Computes the total length of a curve. Args: curve: list of Points, or parametric function of points, to be computed from t0 to t1. """ # TODO possible bug: if the curve is a loop, it will return 0 (BAD) if isinstance(curve, list): # assuming curve is a list of points scale = (curve[-1] - curve[0]).norm() if scale > 0: coords = np.array([[point.x, point.y] for point in curve]).T dp = np.diff(coords, axis=-1) ds = np.sqrt((dp**2).sum(axis=0)) return ds.sum() else: return 0 else: # assuming curve is a function. curve_func = curve scale = (curve_func(t1) - curve_func(t0)).norm() if scale > 0: coords = lambda t: np.array([curve_func(t).x, curve_func(t).y]) _, sampled_coords = sample_function( coords, [t0, t1], tol=0.0001 / scale, min_points=100) # 1000 times more precise than the scale dp = np.diff(sampled_coords, axis=-1) ds = np.sqrt((dp**2).sum(axis=0)) return ds.sum() else: return 0
def bezier_optimal(P0, P3, *args, **kwargs): """ If inside KLayout, return computed list of KLayout points. """ P0 = _Point(P0.x, P0.y) P3 = _Point(P3.x, P3.y) scale = (P3 - P0).norm() # rough length. # if scale > 1000: # if in nanometers, convert to microns # scale /= 1000 # This function returns a np.array of Points. # We need to convert to array of Point coordinates new_bezier_line = _bezier_optimal_pure(P0, P3, *args, **kwargs) bezier_point_coordinates = lambda t: np.array( [new_bezier_line(t).x, new_bezier_line(t).y]) t_sampled, bezier_point_coordinates_sampled = sample_function( bezier_point_coordinates, [0, 1], tol=0.005 / scale) # tol about 5 nm # The following adds two points right after the first and before the last point # to guarantee that the first edge of the path goes out in the direction # of the 'port'. insert_at = np.argmax(0.001 / scale < t_sampled) t_sampled = np.insert(t_sampled, insert_at, 0.001 / scale) bezier_point_coordinates_sampled = np.insert( bezier_point_coordinates_sampled, insert_at, bezier_point_coordinates(0.001 / scale), axis=1, ) # add a point right after the first one insert_at = np.argmax(1 - 0.001 / scale < t_sampled) # t_sampled = np.insert(t_sampled, insert_at, 1 - 0.001 / scale) bezier_point_coordinates_sampled = np.insert( bezier_point_coordinates_sampled, insert_at, bezier_point_coordinates(1 - 0.001 / scale), axis=1, ) # add a point right before the last one # bezier_point_coordinates_sampled = \ # np.append(bezier_point_coordinates_sampled, np.atleast_2d(bezier_point_coordinates(1 + .001 / scale)).T, # axis=1) # finish the waveguide a little bit after return [ pya.DPoint(x, y) for (x, y) in zip(*(bezier_point_coordinates_sampled)) ]
def get_points(self): from math import atan2, pi P1, C, P2 = self.P1, self.C, self.P2 r = (P2 - C).norm() theta_start = atan2((P1 - C).y, (P1 - C).x) theta_end = atan2((P2 - C).y, (P2 - C).x) if self.ccw: theta_end = (theta_end - theta_start) % (2 * pi) + theta_start else: theta_start = (theta_start - theta_end) % (2 * pi) + theta_end theta_start, theta_end = theta_end, theta_start arc_function = lambda t: np.array([r * np.cos(t), r * np.sin(t)]) # in the function below, theta_start must be smaller than theta_end t, coords = sample_function(arc_function, [theta_start, theta_end], tol=0.002 / r) # This yields a better polygon # The idea is to place a point right after the first one, to # make sure the arc starts in the right direction insert_at = np.argmax(theta_start + 0.001 <= t) t = np.insert(t, insert_at, theta_start + 0.001) coords = np.insert(coords, insert_at, arc_function(theta_start + 0.001), axis=1) insert_at = np.argmax(theta_end - 0.001 <= t) coords = np.insert(coords, insert_at, arc_function(theta_end - 0.001), axis=1) # finish the waveguide a little bit after # create original waveguide poligon prior to clipping and rotation dpoints_list = [C + kdb.DPoint(x, y) for x, y in zip(*coords)] if not self.ccw: dpoints_list = list(reversed(dpoints_list)) return dpoints_list