def interactive_define_ellipse(plotter): ''' Interactive routine to define center and radii of an ellipse. ''' from chiplotle.hpgl.compound.rectangle import Ellipse points = [] print "Interactive define Rectangle:" print "Move pen to lower, left corner and press enter." input = raw_input() lower_left = Coordinate(plotter.actual_position[0].x, plotter.actual_position[0].y) print "lower_left:" print lower_left print "Move pen to upper, right corner and press enter." input = raw_input() upper_right = Coordinate(plotter.actual_position[0].x, plotter.actual_position[0].y) print "upper_right:" print upper_right rectangle = Rectangle([lower_left], upper_right.x, upper_right.y) return rectangle
def ruler(start_coord, end_coord, units, min_tick_height, symmetric=False): """ A measuring ruler. - `units` is a list of units on which to put marks, from smaller to larger. e.g., (10, 20, 40). - `min_tick_height` is the height of the marks for the smallest units. The hight of the other units are multiples of this. - `symmetric` set to True to draw the tick lines symmetrically around the invisible center-line. """ start_coord = Coordinate(*start_coord) end_coord = Coordinate(*end_coord) length = (end_coord - start_coord).magnitude angle = (end_coord - start_coord).angle result = [] for i, unit in enumerate(units): ticks = int(math.ceil(length / unit)) for t in range(ticks): tick_height = min_tick_height * (i + 1) if symmetric: x1, y1 = unit * t, tick_height / 2 x2, y2 = unit * t, -tick_height / 2 else: x1, y1 = unit * t, 0 x2, y2 = unit * t, -tick_height tick = line((x1, y1), (x2, y2)) result.append(tick) g = Group(result) rotate(g, angle, (0, 0)) offset(g, start_coord) return g
def radial_ruler(radius, start_angle, end_angle, units, min_tick_height, symmetric=True): length = end_angle - start_angle result = [] for i, unit in enumerate(units): ticks = int(math.ceil(length / unit)) for t in range(ticks): tick_height = min_tick_height * (i + 1) if symmetric: r1, a1 = tick_height / 2, unit * t + start_angle r2, a2 = -tick_height / 2, unit * t + start_angle else: r1, a1 = 0, unit * t + start_angle r2, a2 = -tick_height, unit * t + start_angle xy1 = p2c(Coordinate(r1, a1) + Coordinate(radius, 0)) xy2 = p2c(Coordinate(r2, a2) + Coordinate(radius, 0)) tick = line(xy1, xy2) result.append(tick) return Group(result)
def get_bounding_box(arg): '''Returns the pair of coordinate pairs outlining the bounding box of the given HPGL drawing.''' if not isinstance(arg, (list, tuple)): raise TypeError('arg must be list or tuple') min_x = min_y = 1000000.0 max_x = max_y = -1000000.0 coords = get_all_coordinates(arg) if len(coords) == 0: return None for c in coords: ## x... if c.x > max_x: max_x = c.x if c.x < min_x: min_x = c.x ## y... if c.y > max_y: max_y = c.y if c.y < min_y: min_y = c.y return (Coordinate(min_x, min_y), Coordinate(max_x, max_y))
def instantiate_virtual_plotter( left_bottom=Coordinate(0, 0), right_top=Coordinate(10320, 7920), type=None): ''' Instantiates a virtual plotter with 8.5x11" (ANSI A) paper. If you have a default plotter defined in your config.py file we will use that plotter definition file (ignoring the serial port setting). ''' which_plotter = type if type is None: map = get_config_value('serial_port_to_plotter_map') ## if user has set fixed port to plotter mapping... if map is not None: for k, v in map.items(): which_plotter = v else: which_plotter = "Plotter" ser = VirtualSerialPort(left_bottom, right_top) plotter = instantiate_plotter_from_id(ser, which_plotter) print "\nInstantiated plotter %s:" % plotter coords = plotter.margins.soft.all_coordinates print " Drawing limits: (left %s; bottom %s; right %s; top %s)" % coords print " Buffer Size: %s" % plotter.buffer_size return plotter
def minmax(self): """Returns the minimum and maximum coordinates.""" if len(self._data) == 0: return None coords = [list(c) for c in self._data] mx = np.max(coords, 0).tolist() mn = np.min(coords, 0).tolist() return (Coordinate(*mn), Coordinate(*mx))
def cross(width, height): '''Draws a cross shape. - `width` is the length of the horizontal line. - `height` is the length of the vertical line.. ''' l1 = Path([Coordinate(-width / 2.0, 0), Coordinate(width / 2.0, 0)]) l2 = Path([Coordinate(0, -height / 2.0), Coordinate(0, height / 2.0)]) return Group([l1, l2])
def test_path_radd_03(): """A Coordinate and a Path can be added.""" a = Path([(1, 2), (3, 4)]) t = Coordinate(1, 2) + a assert t is not a assert isinstance(t, Path) assert t == Path([(2, 4), (4, 6)])
def test_path_add_03(): """A Path and a Coordinate can be added.""" a = Path([(1, 2), (3, 4)]) t = a + Coordinate(1, 2) assert t is not a assert isinstance(t, Path) assert t == Path([(2, 4), (4, 6)])
def supershape( width, height, m, n1, n2, n3, point_count=100, percentage=1.0, a=1.0, b=1.0, travel=None, ): """Supershape, generated using the superformula first proposed by Johan Gielis. - `points_count` is the total number of points to compute. - `travel` is the length of the outline drawn in radians. 3.1416 * 2 is a complete cycle. """ travel = travel or (math.pi * 2) ## compute points... phis = [i * travel / point_count for i in range(int(point_count * percentage))] points = [superformula(a, b, m, n1, n2, n3, x) for x in phis] ## scale and transpose... path = [] for x, y in points: x *= width y *= height path.append(Coordinate(x, y)) return Path(path)
def test_path_rsub_03(): """A Coordinate can substract a Path.""" a = Path([(1, 2), (3, 4)]) t = Coordinate(1, 2) - a assert t is not a assert isinstance(t, Path) assert t == Path([(0, 0), (-2, -2)])
def convert_relatives_to_absolutes(lst): if not isinstance(lst, (list, tuple)): raise TypeError("`lst` must be a list or tuple.") lst = pens_updown_to_papr(lst) result = [] last_position = Coordinate(0, 0) for e in lst: ## if has absolute position, keep track of last point... if is_primitive_absolute(e): if isinstance(e.xy, CoordinateArray): last_position = e.xy[-1] else: last_position = e.xy ## handle each HPGL command type... if isinstance(e, PR): command = pr_to_pa(e, last_position) last_position = command.xy[-1] elif isinstance(e, ER): command = EA(last_position + e.xy) last_position = command.xy elif isinstance(e, RR): command = RA(last_position + e.xy) last_position = command.xy elif isinstance(e, AR): command = AA(last_position + e.xy, e.angle, e.chordtolerance) last_position = command.xy else: command = e result.append(command) return result
def center(self): """"center" is defined as being half way between the top/bottom and left/right-most points. This will be different from the centroid, which takes the distribution of the points into consideration. """ return sum(self.minmax, Coordinate(0, 0)) / 2.0
def __init__(self, left_bottom, right_top): left_bottom = Coordinate(*left_bottom) right_top = Coordinate(*right_top) #print "I am a virtual serial port!" self._received_commands_string = "" self._next_query_value = '' self.commanded_x = 0 self.commanded_y = 0 #pen_status: 0 == up, 1 == down self.pen_status = 0 self.left = left_bottom.x self.right = right_top.x self.bottom = left_bottom.y self.top = right_top.y self.buffer_size = maxint self.portstr = "VirtualSerialPort"
def test_path_sub_03(): """A Path can substract a Coordinate.""" a = Path([(1, 2), (3, 4)]) t = a - Coordinate(1, 2) assert t is not a assert isinstance(t, Path) assert t == Path([(0, 0), (2, 2)])
def interactive_define_polygon_simple(plotter): ''' Interactive routine to define points in a PolygonSimple object. ''' from chiplotle.hpgl.compound.polygon_simple import PolygonSimple points = [] print "Interactive define PolygonSimple:" print "Move pen to each point and press enter. Press x when finished adding points." print "The final point (a duplicate of first point) will be added automatically." while True: input = raw_input() if input is 'x': break point = Coordinate(plotter.actual_position[0].x, plotter.actual_position[0].y) points.append(point) print "added:" print point poly = PolygonSimple([0, 0], points) return poly
def xy_to_polar(args): """Converts cartesian to polar coordinates. Argument may be two coordinates x, y, a tuple (x, y), or a Coordinate(x, y). Returns an (r, a) tuple, where `r` is the magnitude, `a` is the angle in radians. """ x, y = tuple(Coordinate(*args)) r = math.sqrt(x ** 2 + y ** 2) x = x or 1E-10 a = math.atan(y / x) if x >= 0: if y >= 0: pass else: a += 2 * math.pi else: a += math.pi # if y >= 0: # a += math.pi # else: # a = math.pi / 2 * 3 - a return r, a
def cumsum(self): """Returns the cumulative sum.""" dimensions = len(self[0]) result = [Coordinate(*([0] * dimensions))] for coord in self: result.append(result[-1] + coord) return type(self)(result)
def path_interpolated(points, curvature, interpolation_count=50): """Returns a Path with bezier interpolation between the given `points`. The interpolation is computed so that the resulting path touches the given points. - `points` the key points from which to interpolate. - `curvature` the smoothness of the curve [0, 1]. - `interpolation_count` is the number of points to add by interpolation, per segment. """ if not (0 <= curvature <= 1): raise ValueError("`curvature` must be between 0 and 1 inclusive.") if curvature == 0: return Path(points) ## else we have a curve... points = CoordinateArray(points) curvature = 4 + (1.0 - curvature) * 40 bi = [0, -0.25] a = [Coordinate(0, 0), (points[2] - points[0] - Coordinate(0, 0)) / 4.0] ## compute bi and a... for i in range(2, len(points) - 1): bi.append(-1 / (curvature + bi[i - 1])) a.append(-(points[i + 1] - points[i - 1] - a[i - 1]) * bi[i]) ## compute dxy... dxy = [Coordinate(0, 0)] for i in reversed(list(range(len(points) - 1))): dxy.insert(0, a[i] + dxy[0] * bi[i]) ## compute interpolated points... plot_points = [] for i in range(len(points) - 1): control_points = [ points[i], points[i] + dxy[i], points[i + 1] - dxy[i + 1], points[i + 1], ] plot_points += bezier_interpolation(control_points, interpolation_count, 1)[:-1] return Path(plot_points)
def digitized_point(self): """Returns last digitized point. Returns a tuple [Coordinate(x, y), pen status]""" response = self._send_query(self._hpgl.OD()).split(b",") return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip(b"\r")), ]
def actual_position(self): """Output the actual position of the plotter pen. Returns a tuple (Coordinate(x, y), pen status)""" response = self._send_query(self._hpgl.OA()).split(b",") return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip(b"\r")), ]
def __setitem__(self, i, arg): if isinstance(i, int): if not isinstance(arg, Coordinate): raise TypeError self._data[i] = arg else: arg = [Coordinate(*list(coord)) for coord in arg] self._data[i.start:i.stop] = arg
def commanded_position(self): '''Output the commanded position of the plotter pen. Returns a tuple [Coordinate(x, y), pen status]''' response = self._send_query(self._hpgl.OC()).split(',') return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip('\r')) ]
def commanded_position(self): """Output the commanded position of the plotter pen. Returns a tuple [Coordinate(x, y), pen status]""" response = self._send_query(self._hpgl.OC()).split(b",") return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip(b"\r")), ]
def actual_position(self): '''Output the actual position of the plotter pen. Returns a tuple (Coordinate(x, y), pen status)''' response = self._send_query(self._hpgl.OA()).split(',') return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip('\r')) ]
def digitized_point(self): '''Returns last digitized point. Returns a tuple [Coordinate(x, y), pen status]''' response = self._send_query(self._hpgl.OD()).split(',') return [ Coordinate(eval(response[0]), eval(response[1])), eval(response[2].strip('\r')) ]
def rotate_2d(xy, angle, pivot=(0, 0)): '''2D rotation. - `xy` is an (x, y) coordinate pair or a list of coordinate pairs. - `angle` is the angle of rotation in radians. - `pivot` the point around which to rotate `xy`. Returns a Coordinate or a CoordinateArray. ''' try: xy = Coordinate(*xy) pivot = Coordinate(*pivot) result = rotate_coordinate_2d(xy, angle, pivot) except: xy = CoordinateArray(xy) pivot = Coordinate(*pivot) result = rotate_coordinatearray_2d(xy, angle, pivot) return result
def rotate_coordinate_2d(xy, angle, pivot): '''Coordinate 2D rotation. - `xy` is an (x, y) coordinate pair. - `angle` is the angle of rotation in radians. - `pivot` the point around which to rotate `xy`. Returns a Coordinate. ''' pivot = Coordinate(*list(pivot)) ## rotate counter-clockwise... angle = -angle #cp = Coordinate(xy) xy -= pivot x = xy.x * math.cos(angle) + xy.y * math.sin(angle) y = -xy.x * math.sin(angle) + xy.y * math.cos(angle) result = Coordinate(x, y) + pivot return result
def get_centroid(arg): '''Returns the centroid of the given Chiplotle-HPGL shapes.''' arg = get_all_coordinates(arg) ## convert into a set to remove duplicate coordinates and to ## avoid giving more weight to these duplicate points... arg = set(arg) result = Coordinate(0, 0) for c in arg: result += c return result / len(arg)
def scale(shape, value, pivot=Coordinate(0, 0)): '''In place scaling. - `shape` is the shape to be rotated. - `value` is the scaling value. Can be a scalar or an (x, y) coordinate. - `pivot` is the Coordinate around which the shape will be scaled. ''' from chiplotle.tools.geometrytools.scale import scale t = TransformVisitor(scale) t.visit(shape, value, pivot)