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 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 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 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 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_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 _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 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__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)])
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 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 noisify(coords, value): try: x, y = value except TypeError: x = y = value result = [ ] for point in coords: x_wiggle = random.randrange(-x, x) y_wiggle = random.randrange(-y, y) xy = point + Coordinate(x_wiggle, y_wiggle) result.append(xy) return CoordinateArray(result) t = TransformVisitor(noisify) t.visit(shape, value) ## RUN DEMO CODE if __name__ == "__main__": from chiplotle.geometry.shapes.circle import circle from chiplotle.tools import io c1 = circle(1000, 100) c2 = circle(800, 100) noise(c1, 90) c1 += Coordinate(1000, 1000) g = Group([c1, c2]) noise(g, 60) io.view(g)
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)
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)
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)
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 group(lst): return Group(lst)
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]) ## go... r = rotation_example() s = scale_example() io.view(Group([s, r]))
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__01(): '''A Group can be empty.''' t = Group([]) assert len(t) == 0
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]))