def scale_example(): s1 = shapes.star_outline(100, 100, 4) transforms.offset(s1, (300, 0)) transforms.rotate(s1, -math.pi / 4) s2 = shapes.star_outline(100, 100, 6) transforms.offset(s2, (300, 0)) transforms.rotate(s2, -math.pi / 2) s3 = shapes.star_outline(100, 100, 9) transforms.offset(s3, (300, 0)) transforms.rotate(s3, -math.pi * 3 / 4) tl = TransformLock([s1, s2], ["scale"]) start = Group([tl, s3]) mid = copy.deepcopy(start) end = copy.deepcopy(start) formatters.Pen(3)(start) formatters.Pen(2)(mid) formatters.Pen(1)(end) transforms.scale(mid, 2) transforms.scale(end, 3) o = shapes.circle(100) return Group([start, mid, end, o])
def __init__(self, shapes, lock_transforms): Group.__init__(self, shapes=shapes) self.lock_transforms = set(lock_transforms) self.transform_map = { "scale": self.scale_to_offset, "rotate": self.rotate_to_offset, }
def test_transformlock_transforms_scale_02(): '''Locks work within groups.''' t = scale_locked_rectangle_pair() g = Group([Group([t])]) transforms.scale(g, 2) assert_scaling_preserves_diff(g) assert_scaling_changes_coords(g)
def sort1(group): # dumb sort. find closest endpoint to current endpoint, append, rinse, repeat sortedgroup = Group() original = group[:] sortedgroup.append(original[0]) del original[0] while original: p1 = sortedgroup[-1].points.xy[-1] bestvalue = 99999999999999999999 # i know this is stupid. ugh. bestindex = 0 reverseflag = False for index in range(len(original)): distance = distance_between(p1, original[index].points.xy[0]) if distance < bestvalue: bestvalue = distance bestindex = index reverseflag = False distance = distance_between(p1, original[index].points.xy[-1]) if distance < bestvalue: bestvalue = distance bestindex = index reverseflag = True if reverseflag: original[bestindex].points.xy.reverse() sortedgroup.append(original[bestindex]) del original[bestindex] return sortedgroup
def test_transformlock_transforms_rotate_02(): '''Locks work within groups.''' t = rotation_locked_rectangle_pair() g = Group([Group([t])]) transforms.offset(g, (200, 0)) assert_rotation_preserves_diff(g) assert_rotation_changes_coords(g)
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 pupd_to_paths(hpgl): # slurp in PUs and PDs, and convert to a group of simple paths # probably fragile, but works fine on the output of pstoedit -f hpgl results = Group() builder = [] for command in hpgl: if isinstance(command, PU): if builder: # must be starting a new path, so stash the last coords = get_all_coordinates(builder) results.append(Path(coords)) builder = [] builder.append(command) elif isinstance(command, PD): builder.append(command) return results
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 frame(width, height, inset): '''A frame (rectangle within a rectangle) with a width, height, and inset. - `width` is the width of the frame. - `height` is the height of the frame. - `inset` is the distance to inset the inner rectangle from the outer. ''' r1 = rectangle(width, height) r2 = rectangle(width - (inset * 2), height - (inset * 2)) return Group([r1, r2])
def rotation_example(): r1 = shapes.rectangle(100, 100) r2 = shapes.rectangle(200, 200) transforms.offset(r2, (400, 0)) r3 = shapes.rectangle(300, 300) transforms.offset(r3, (800, 0)) tl = TransformLock([r1, r2], ["rotate"]) start = Group([tl, r3]) transforms.offset(start, (500, 0)) mid = copy.deepcopy(start) end = copy.deepcopy(start) formatters.Pen(3)(start) formatters.Pen(2)(mid) formatters.Pen(1)(end) transforms.rotate(mid, math.pi / 6, (0, 0)) transforms.rotate(end, math.pi / 3, (0, 0)) o = shapes.circle(100) return Group([start, mid, end, o])
def _annotate_center(self): coord = self.shape.center label = Label('\n\rcenter: ' + str(coord), self.charwidth, self.charheight, origin = 'top-center') c = circle(20) cr = cross(50, 50) mark = Group([c, cr, label]) offset(label, coord) offset(c, coord) offset(cr, coord) return mark
def _annotate_centroid(self): coord = self.shape.centroid label = Label('\n\rcentroid: ' + str(coord), self.charwidth, self.charheight, origin = 'top-center') r = rectangle(20, 20) cr = cross(50, 50) mark = Group([r, cr, label]) offset(label, coord) offset(r, coord) offset(cr, coord) return mark
def grid(width, height, width_divisions,height_divisions): '''Rectangular grid. - `width` : ``int`` or ``float``, width of the rectangle. - `height` : ``int`` or ``float``, height of the rectangle. - `width_divisions` : ``int``, number of horizontal equidistant partitions. - `height_divisions` : ``int``, number of vertical equidistant partitions. ''' ul_x = width bl_x = ul_x ur_x = ul_x + width ul_y = height ur_y = ul_y bl_y = ul_y - height x_step_size = width / width_divisions y_step_size = height / height_divisions g = Group() ## add horizontal lines for i in range(height_divisions + 1): step_y = y_step_size * i l = line((ul_x, ul_y - step_y), (ur_x, ur_y - step_y)) g.append(l) ## add vertical lines for i in range(width_divisions + 1): step_x = x_step_size * i l = line((ul_x + step_x, ul_y), (bl_x + step_x, bl_y)) g.append(l) return g
def arrow(path, headwidth, headheight, filled=False): '''Returns an arrow shape. - `path` is a Path object. - `headwidth` is the width of the arrow head. - `headheight` is the height of the arrow head. ''' ## make arow head... r, a = xy_to_polar((path.points[-1] - path.points[-2])) head = isosceles(headwidth, headheight, filled) offset(head, (0, -headheight)) rotate(head, a - math.pi / 2, (0, 0)) offset(head, path.points[-1]) return Group([head, path])
def donut(width, height, inset, segments=100): """ A donut (ellipse within ellipse) with a width, height, inset, segments. segments is how many lines should be used to draw ellipse. More segments create a smoother ellipse, but will take longer to draw. inset is the distance to inset the inner ellipse from the outer. The donut is drawn with the current pen location as the center. offset may be used to shift this around, for example, to draw from the lower, left corner. """ e1 = ellipse(width, height, segments) e2 = ellipse(width - (inset * 2), height - (inset * 2), segments) return Group([e1, e2])
def target(outer_radius, inner_radius, circles_count, segments=36): """ Creates `circles_count` concentric circles. Can be used to create radially filled circles. """ if not outer_radius > inner_radius: raise ValueError("outer_radius must be > inner_radius.") if not circles_count > 1: raise ValueError("circles_count must be >= 2.") result = [] radius_delta = (outer_radius - inner_radius) / float(circles_count) for i in range(circles_count): result.append(circle(inner_radius + radius_delta * i, segments)) return Group(result)
def grid(width, height, width_divisions, height_divisions): '''Rectangular grid. - `width` : ``int`` or ``float``, width of the rectangle. - `height` : ``int`` or ``float``, height of the rectangle. - `width_divisions` : ``int``, number of horizontal equidistant partitions. - `height_divisions` : ``int``, number of vertical equidistant partitions. ''' ul_x = width bl_x = ul_x ur_x = ul_x + width ul_y = height ur_y = ul_y bl_y = ul_y - height x_step_size = width / width_divisions y_step_size = height / height_divisions g = Group() ## add horizontal lines for i in range(height_divisions + 1): step_y = y_step_size * i l = line((ul_x, ul_y - step_y), (ur_x, ur_y - step_y)) g.append(l) ## add vertical lines for i in range(width_divisions + 1): step_x = x_step_size * i l = line((ul_x + step_x, ul_y), (bl_x + step_x, bl_y)) g.append(l) return g
def test_shapes_group__init__01(): """A Group can be empty.""" t = Group([]) assert len(t) == 0
rads_incr = segmentation_map[segmentation_mode.lower()]() rads = start_angle arc = [] while rads < end_angle: coord = Coordinate(math.cos(rads), math.sin(rads)) coord = coord * Coordinate(width / 2.0, height / 2.0) rads += rads_incr arc.append(coord) ## NOTE: this is better than using rads <= end_angle since ## the equality does not always work as expected with floats ## due to rounding. last = Coordinate(math.cos(end_angle), math.sin(end_angle)) last *= Coordinate(width / 2.0, height / 2.0) arc.append(last) return Path(arc) ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.tools import io from chiplotle.geometry.core.group import Group gr = Group() for radius in range(100, 1000, 10): ae = arc_ellipse(radius, radius * 2, 0, math.pi / 2, 5, 'arc') gr.append(ae) io.view(gr)
def __init__(self, shapes, name): Group.__init__(self, shapes = shapes) self.name = name
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) ## RUN DEMO CODE if __name__ == "__main__": from chiplotle.geometry.core.group import Group from chiplotle.tools import io points = [ (0, 0), (1000, 1000), (-1000, 1000), (-1000, -1000), (1000, -1000), (0, 0), ] e1 = path_interpolated(points, 1) e2 = path_interpolated(points, 0.5) e3 = path_interpolated(points, 0) assert isinstance(e1, Path) io.view(Group([e1, e2, e3]))
from chiplotle.geometry.core.coordinate import Coordinate from chiplotle.geometry.transforms.transformvisitor import TransformVisitor def offset(shape, value): '''In place offsetting. - `shape` is the shape to be rotated. - `value` is the offset value. Can be a scalar or an (x, y) coordinate. ''' if isinstance(value, (list, tuple)): value = Coordinate(*value) def offset(coords, value): return coords + value t = TransformVisitor(offset) t.visit(shape, value) ## RUN DEMO CODE if __name__ == "__main__": from chiplotle.geometry.shapes.rectangle import rectangle from chiplotle.tools import io r0 = rectangle(1000, 400) r1 = rectangle(1000, 400) r2 = rectangle(1000, 400) offset(r1, (0, 1500)) offset(r2, (100, 200)) io.view(Group([r0, r1, r2]))
return result ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.geometry.core.group import Group from chiplotle.geometry.transforms.offset import offset from chiplotle.geometry.transforms.rotate import rotate from chiplotle.tools import io #one of each main spiral type s1 = spiral_archimedean(500, wrapping_constant=1) s2 = spiral_archimedean(500, wrapping_constant=2, direction="ccw") offset(s2, (0, -1000)) #these two are long, so we'll rotate them and move them to the side #of the others s3 = spiral_archimedean(1800, wrapping_constant=-1, direction="ccw") rotate(s3, math.pi * 1.5) offset(s3, (650, 400)) s4 = spiral_archimedean(1500, wrapping_constant=-2, direction="ccw") rotate(s4, math.pi * .6) offset(s4, (1000, -1100)) g = Group([s1, s2, s3, s4]) io.view(g)
point_order = [] for i in range(0, num_points): point_num = (i * jump_size) % num_points point_order.append(point_num) corners = [corners[i] for i in point_order] return Polygon(corners) ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.tools import io from chiplotle.geometry.shapes.star_crisscross import star_crisscross from chiplotle.geometry.core.group import Group from chiplotle.geometry.transforms.offset import offset gr1 = Group() for points in range(5, 26): for i in range(1, points): s = star_crisscross(1000, 1000, num_points = points, jump_size = i, find_valid_jump_size = False) offset(s, ((i - 1) * 1000, -(points - 5) * 1000)) gr1.append(s) io.view(gr1)
def __init__(self, shapes, name): Group.__init__(self, shapes=shapes) self.name = name
def test_shapes_group__init__04(): '''A Group can be initialized with another Group.''' t = Group([Group([]), Path([(1, 2), (3, 4)])]) assert len(t) == 2 assert t[0] == Group([]) assert t[1] == Path([(1, 2), (3, 4)])
def test_shapes_group__init__02(): '''A Group can take no parameters.''' t = Group() assert len(t) == 0
else: #No collision return None ## DEMO if __name__ == '__main__': from chiplotle.geometry.shapes.line import line from chiplotle.geometry.core.group import Group from chiplotle.tools import io from random import randrange #draw a bunch of lines that do not intersect no_intersections = Group() line_1 = line([randrange(0, 4000), randrange(0, 4000)], [randrange(0, 4000), randrange(0, 4000)]) no_intersections.append(line_1) while len(no_intersections) < 300: new_line = line([randrange(0, 4000), randrange(0, 4000)], [randrange(0, 4000), randrange(0, 4000)]) intersection = False for l in no_intersections: if get_line_intersection(new_line, l) != None: intersection = True break
def catmull_path(points, interpolation_count=50): '''Path with Catmull-Rom spline interpolation.''' path_points = catmull_interpolation(points, interpolation_count) return Path(path_points) ## DEMO if __name__ == '__main__': from chiplotle.tools import io from chiplotle.geometry.shapes.cross import cross from chiplotle.geometry.core.group import Group from chiplotle.geometry.transforms.offset import offset import random points = [] for i in range(10): x, y = random.randint(-100, 100), random.randint(-100, 100) points.append((x, y)) crosses = [] for point in points: c = cross(15, 15) offset(c, point) crosses.append(c) path = catmull_path(points) g = Group([path] + crosses) io.view(g)
rads_incr = segmentation_map[segmentation_mode.lower()]() rads = start_angle arc = [] while rads < end_angle: coord = Coordinate(math.cos(rads), math.sin(rads)) coord = coord * Coordinate(width / 2.0, height / 2.0) rads += rads_incr arc.append(coord) ## NOTE: this is better than using rads <= end_angle since ## the equality does not always work as expected with floats ## due to rounding. last = Coordinate(math.cos(end_angle), math.sin(end_angle)) last *= Coordinate(width / 2.0, height / 2.0) arc.append(last) return Path(arc) ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.tools import io from chiplotle.geometry.core.group import Group gr = Group() for radius in range(100, 1000, 10): ae = arc_ellipse(radius, radius * 2, 0, math.pi/2, 5, 'arc') gr.append(ae) io.view(gr)
def group(lst): return Group(lst)
def test_shapes_group__init__03(): '''A Group can be initialized with a list of Paths.''' t = Group([Path([(1, 2), (3, 4)])]) assert len(t) == 1 assert t[0] == Path([(1, 2), (3, 4)])
from chiplotle.geometry.core.coordinate import Coordinate from chiplotle.geometry.transforms.transformvisitor import TransformVisitor 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) ## RUN DEMO CODE if __name__ == "__main__": from chiplotle.geometry.shapes.rectangle import rectangle from chiplotle.tools import io r0 = rectangle(1000, 500) r1 = rectangle(1000, 500) r2 = rectangle(1000, 500) scale(r1, 5, (500, 250)) scale(r2, (10, 20)) g = Group([r0, r1, r2]) print(g.format) io.view(g)
def test_shapes_group__init__01(): '''A Group can be empty.''' t = Group([]) assert len(t) == 0
x = math.cos(theta) * math.pow(math.e, (expansion_rate * theta)) y = math.sin(theta) * math.pow(math.e, (expansion_rate * theta)) if direction == "ccw": y *= -1.0 spiral_points.append((x, y)) theta += theta_incr return Path(spiral_points) ## RUN DEMO CODE if __name__ == "__main__": from chiplotle.geometry.core.group import Group from chiplotle.geometry.shapes.line import line from chiplotle.tools import io s = spiral_logarithmic() # add some lines for scale line_right = line((0, 0), (500, 0)) line_left = line((0, 0), (-500, 0)) line_up = line((0, 0), (0, 500)) line_down = line((0, 0), (0, -500)) g = Group([s, line_right, line_left, line_up, line_down]) io.view(g)
''' Constructs an arc from a circle with the given radius, and number of segments. Arc goes from start_angle to end_angle, both of which are in radians. - `segmentation_mode` : '2PI' or 'arc'. The first segments the whole circle into the given number of segments, the second segments the arc. ''' radius = radius * 2.0 return arc_ellipse(radius, radius, start_angle, end_angle, segments, segmentation_mode) ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.tools import io gr = Group() for radius in range(100, 1000, 100): ac = arc_circle(radius, 1.0, math.pi) assert isinstance(ac, Path) gr.append(ac) io.view(gr, 'png')
else: w_multi = quarter_width h_multi = quarter_height even = True point_x = (w_multi * cos_alpha); point_y = (h_multi * sin_alpha); corners.append((point_x, point_y)) degrees += degrees_incr corners.append(corners[0]) return Polygon(corners) ## RUN DEMO CODE if __name__ == '__main__': from chiplotle.tools import io from chiplotle.geometry.shapes.star_outline import star_outline from chiplotle.geometry.core.group import Group gr1 = Group() for i in range(5, 10): st = star_outline(100 * (i * i * i), 100 * (i * i * i), num_points = i) gr1.append(st) io.view(gr1)