def reflect(shape, position, _angle, keep_original): """Mirrors and copies the geometry across an invisible axis.""" if shape is None: return None new_shape = shape.cloneAndClear() for contour in shape.contours: c = Contour() for point in contour.points: d = distance(point.x, point.y, position.x, position.y) a = angle(point.x, point.y, position.x, position.y) x, y = coordinates(position.x, position.y, d * cos(radians(a - _angle)), 180 + _angle) d = distance(point.x, point.y, x, y) a = angle(point.x, point.y, x, y) px, py = coordinates(point.x, point.y, d * 2, a) c.addPoint(Point(px, py, point.type)) if contour.closed: c.close() new_shape.add(c) if keep_original: g = Geometry() g.add(shape) g.add(new_shape) return g return new_shape
def _function(shape, *args, **kwargs): if isinstance(shape, list): return fn(shape, *args, **kwargs) elif isinstance(shape, Path): return fn([shape], *args, **kwargs)[0] elif isinstance(shape, Geometry): g = Geometry() for path in fn(shape.paths, *args, **kwargs): g.add(path) return g
def group(shapes): if shapes is None: return None g = Geometry() for shape in shapes: if isinstance(shape, Geometry): g.extend(shape) elif isinstance(shape, Path): g.add(shape) else: raise "Unable to group %ss. I can only group paths or geometry objects." % shape return g
def _function(shape, *args, **kwargs): if isinstance(shape, Path): return fn(shape, *args, **kwargs) elif isinstance(shape, Geometry): g = Geometry() for path in shape.paths: result = fn(path, *args, **kwargs) if isinstance(result, Path): g.add(result) elif isinstance(result, Geometry): g.extend(result) return g return None
def delete_paths(geo, bounding, delete_selected=True): if geo is None or bounding is None: return None new_geo = Geometry() for old_path in geo.paths: selected = False # Paths are eagerly selected: # Even if only one point is inside of the bounding volume # the path is selected. for point in old_path.points: if bounding.contains(point): selected = True if selected is delete_selected: new_geo.add(old_path.clone()) return new_geo
def _function(shape, *args, **kwargs): from java.util import List if isinstance(shape, (list, tuple, List)): return fn(shape, *args, **kwargs) elif isinstance(shape, Path): new_path = Path(shape, False) for c in shape.contours: new_path.add(Contour(fn(c.points, *args, **kwargs), c.closed)) return new_path elif isinstance(shape, Geometry): new_geo = Geometry() for path in shape.paths: new_geo.add(_map_geo_to_points(fn)(path, *args, **kwargs)) return new_geo return None
def pack(shapes, iterations, padding, seed): _seed(seed) packed_objects = [] for path in shapes: packed_objects.append(PackObject(path)) for i in xrange(1, iterations): _pack(packed_objects, damping=0.1/i, padding=padding) geo = Geometry() for po in packed_objects: print po.x, po.y p = Transform.translated(po.x, po.y).map(po.path) geo.add(p) return geo
def _function(shape, *args, **kwargs): if isinstance(shape, list): return fn(shape, *args, **kwargs) elif isinstance(shape, Contour): return fn([shape], *args, **kwargs)[0] elif isinstance(shape, Path): path = shape.cloneAndClear() for contour in fn(shape.contours, *args, **kwargs): path.add(contour) return path elif isinstance(shape, Geometry): g = Geometry() for p in shape.paths: g.add(_function(p, *args, **kwargs)) return g
def delete_paths(geo, bounding, delete_selected=True): if geo is None or bounding is None: return None new_geo = Geometry() for old_path in geo.paths: selected = False # Paths are eagerly selected: # Even if only one point is inside of the bounding volume # the path is selected. for point in old_path.points: if bounding.contains(point): selected = True break if selected is not delete_selected: new_geo.add(old_path.clone()) return new_geo
def text_on_path(shape, text, font_name="Verdana", font_size=20, position=0, offset=2.0, keep_geometry=True): if shape is None or shape.length <= 0: return None if text is None: return None text = unicode(text) if isinstance(shape, Path): shape = shape.asGeometry() g = Geometry() if keep_geometry: g.extend(shape.clone()) fm = get_font_metrics(font_name, font_size) string_width = textwidth(text, fm) dw = string_width / shape.length first = True for i, char in enumerate(text): char_width = textwidth(char, fm) if first: t = position / 100.0 first = False else: t += char_width / string_width * dw # Always loop (the other behavior is weird) t = t % 1.0 pt1 = shape.pointAt(t) pt2 = shape.pointAt(t + 0.001) a = angle(pt2.x, pt2.y, pt1.x, pt1.y) tp = Text(char, -char_width, -offset) tp.align = Text.Align.LEFT tp.fontName = font_name tp.fontSize = font_size tp.translate(pt1.x, pt1.y) tp.rotate(a - 180) g.add(tp.path) return g
def _function(_list, *args, **kwargs): if isinstance(_list, (Path, Geometry, Contour)): return fn(_list.points, *args, **kwargs) elif isinstance(_list, (list, tuple, Iterable)): if len(_list) == 0: return None first = _list[0] if isinstance(first, Point): return fn(_list, *args, **kwargs) elif isinstance(first, (Path, Geometry, Contour)): if len(_list) == 1: return fn(first.points, *args, **kwargs) else: g = Geometry() for el in _list: g.add(fn(el.points, *args, **kwargs)) return g return None
def _function(shape, *args, **kwargs): from java.util import List if isinstance(shape, (list, tuple, List)): return fn(shape, *args, **kwargs) elif isinstance(shape, Point): return fn([shape], *args, **kwargs)[0] elif isinstance(shape, Contour): new_points = fn(shape.points, *args, **kwargs) return Contour(new_points, shape.closed) elif isinstance(shape, Path): path = shape.cloneAndClear() for contour in shape.contours: path.add(_function(contour, *args, **kwargs)) return path elif isinstance(shape, Geometry): g = Geometry() for p in shape.paths: g.add(_function(p, *args, **kwargs)) return g
def import_svg(file_name, centered=False, position=Point.ZERO): """Import geometry from a SVG file.""" # We defer loading the SVG library until we need it. # This makes creating a node faster. import svg if not file_name: return None f = file(file_name, 'r') s = f.read() f.close() g = Geometry() paths = svg.parse(s, True) for path in paths: g.add(path) t = Transform() if centered: x, y, w, h = list(g.bounds) t.translate(-x - w / 2, -y - h / 2) t.translate(position) g = t.map(g) return g
def import_svg(file_name, centered=False, position=Point.ZERO): """Import geometry from a SVG file.""" # We defer loading the SVG library until we need it. # This makes creating a node faster. import svg if not file_name: return None f = file(file_name, 'r') s = f.read() f.close() g = Geometry() paths = svg.parse(s, True) for path in paths: g.add(path) t = Transform() if centered: x, y, w, h = list(g.bounds) t.translate(-x-w/2, -y-h/2) t.translate(position) g = t.map(g) return g
def angle_pack(shapes, seed, limit, maximum_radius, angle_tries=1, use_bounding_box=False): if shapes is None: return None _seed(seed) def center_and_translate(shape, tx=0, ty=0): bx, by, bw, bh = list(shape.bounds) t = Transform() t.translate(-bw / 2 - bx, -bh / 2 - by) return t.map(shape) geo = Geometry() bounding_path = Path() # Center first shape first_shape = center_and_translate(shapes[0]) geo.add(first_shape) bounding_path.cornerRect(first_shape.bounds) for shape in shapes[1:]: centered_shape = center_and_translate(shape) angles = [] for i in range(angle_tries): a = uniform(0, 360) if use_bounding_box: d = try_angle(bounding_path, centered_shape, a, limit, maximum_radius, use_bounding_box) else: d = try_angle(geo, centered_shape, a, limit, maximum_radius, use_bounding_box) angles.append([d, a]) chosen_distance, chosen_angle = sorted(angles)[0] tx, ty = coordinates(0, 0, chosen_distance, chosen_angle) t = Transform() t.translate(tx, ty) translated_shape = t.map(centered_shape) bounding_path.cornerRect(translated_shape.bounds) geo.add(translated_shape) return geo
def l_system(shape, position, generations, length, length_scale, angle, angle_scale, thickness_scale, premise, *rules): if shape is None: p = Path() p.rect(0, -length/2, 2, length) shape = p.asGeometry() # Parse all rules rule_map = {} for rule_index, full_rule in enumerate(rules): if len(full_rule) > 0: if len(full_rule) < 3 or full_rule[1] != '=': raise ValueError("Rule %s should be in the format A=FFF" % (rule_index + 1)) rule_key = full_rule[0] rule_value = full_rule[2:] rule_map[rule_key] = rule_value # Expand the rules up to the number of generations full_rule = premise for gen in xrange(int(round(generations))): tmp_rule = "" for letter in full_rule: if letter in rule_map: tmp_rule += rule_map[letter] else: tmp_rule += letter full_rule = tmp_rule # Now run the simulation g = Geometry() stack = [] angleStack = [] t = Transform() t.translate(position.x, position.y) angle = angle for letter in full_rule: if letter == 'F': # Move forward and draw transformed_shape = t.map(shape) if isinstance(transformed_shape, Geometry): g.extend(transformed_shape) elif isinstance(transformed_shape, Path): g.add(transformed_shape) t.translate(0, -length) elif letter == '+': # Rotate right t.rotate(angle) elif letter == '-': # Rotate left t.rotate(-angle) elif letter == '[': # Push state (start branch) stack.append(Transform(t)) angleStack.append(angle) elif letter == ']': # Pop state (end branch) t = stack.pop() angle = angleStack.pop() elif letter == '"': # Multiply length t.scale(1.0, length_scale / 100.0) elif letter == '!': # Multiply thickness t.scale(thickness_scale / 100.0, 1.0) elif letter == ';': # Multiply angle angle *= angle_scale / 100.0 elif letter == '_': # Divide length t.scale(1.0, 1.0/(length_scale / 100.0)) elif letter == '?': # Divide thickness t.scale(1.0/(thickness_scale / 100.0), 1.0) elif letter == '@': # Divide angle angle /= angle_scale / 100.0 return g