def parse_rect(e): x = float(get_attribute(e, "x")) y = float(get_attribute(e, "y")) w = float(get_attribute(e, "width")) h = float(get_attribute(e, "height")) if w < 0: print >> sys.stderr, "Error: invalid negative value for <rect> attribute width=\"%s\"" % w w = 0 if h < 0: print >> sys.stderr, "Error: invalid negative value for <rect> attribute height=\"%s\"" % h h = 0 rx = float(get_attribute(e, "rx")) ry = float(get_attribute(e, "ry")) if rx < 0: print >> sys.stderr, "Error: invalid negative value for <rect> attribute rx=\"%s\"" % rx rx = 0 if ry < 0: print >> sys.stderr, "Error: invalid negative value for <rect> attribute ry=\"%s\"" % ry ry = 0 if not rx or not ry: rx = ry = max(rx, ry) if rx > w / 2.0: rx = w / 2.0 if ry > h / 2.0: ry = h / 2.0 p = Path() p.rect(x + w / 2.0, y + h / 2.0, w, h, rx, ry) return p
def line_angle(position, angle, distance): p = Path() x1, y1 = coordinates(position.x, position.y, distance, angle) p.line(position.x, position.y, x1, y1) p.strokeColor = Color.BLACK p.strokeWidth = 1 return p
def text_on_path(text, shape, font_name, font_size, alignment, margin, baseline_offset): 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() p = Path() fm = get_font_metrics(font_name, font_size) string_width = textwidth(text, fm) dw = string_width / shape.length if alignment == "trailing": first = True for char in text: char_width = textwidth(char, fm) if first: t = (99.9 - margin) / 100.0 first = False else: t -= char_width / string_width * dw t = t % 1.0 margin = t * 100 first = True for char in text: char_width = textwidth(char, fm) if first: t = margin / 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.0000001) a = angle(pt2.x, pt2.y, pt1.x, pt1.y) tp = Text(char, -char_width, -baseline_offset) tp.align = Text.Align.LEFT tp.fontName = font_name tp.fontSize = font_size tp.translate(pt1.x, pt1.y) tp.rotate(a - 180) for contour in tp.path.contours: p.add(contour) return p
def transform(path, m, n1, n2, n3, scale=1.0, points=100, range=TWOPI): new_path = Path() for i in _range(points): pt = path.pointAt(float(i) / points) phi = i * range / points dx, dy = supercalc(m, n1, n2, n3, phi) new_path.addPoint(pt.x + dx * scale, pt.y + dy * scale) return new_path
def parse_rect(e): x = float(get_attribute(e, "x")) y = float(get_attribute(e, "y")) w = float(get_attribute(e, "width")) h = float(get_attribute(e, "height")) p = Path() p.rect(x+w/2, y+h/2, w, h) return p
def parse_line(e): x1 = float(get_attribute(e, "x1")) y1 = float(get_attribute(e, "y1")) x2 = float(get_attribute(e, "x2")) y2 = float(get_attribute(e, "y2")) p = Path() p.line(x1, y1, x2, y2) return p
def parse_rect(e): x = float(get_attribute(e, "x")) y = float(get_attribute(e, "y")) w = float(get_attribute(e, "width")) h = float(get_attribute(e, "height")) p = Path() p.rect(x + w / 2, y + h / 2, w, h) return p
def parse_circle(e): x = float(get_attribute(e, "cx")) y = float(get_attribute(e, "cy")) r = float(get_attribute(e, "r")) p = Path() p.ellipse(x, y, r*2, r*2) p.close() return p
def _flatten(geo): compound = Path() first = True for path in geo.paths: if first: compound = path first = False else: compound = compound.united(path) return compound
def parse_oval(e): x = float(get_attribute(e, "cx")) y = float(get_attribute(e, "cy")) w = float(get_attribute(e, "rx"))*2 h = float(get_attribute(e, "ry"))*2 p = Path() p.ellipse(x, y, w, h) p.close() return p
def path(position, width, height, m, n1, n2, n3, points=1000, percentage=1.0, range=TWOPI): path = Path() for i in _range(points): if i > points*percentage: continue phi = i * range / points dx, dy = supercalc(m, n1, n2, n3, phi) dx = (dx * width / 2) + position.x dy = (dy * height / 2) + position.y path.addPoint(dx, dy) return path
def delete_points(path, bounding, delete_selected=True): if path is None or bounding is None: return None new_path = Path(path, False) # cloneContours = False for old_contour in path.contours: new_contour = Contour() for point in old_contour.points: if bounding.contains(point) == delete_selected: new_contour.addPoint(Point(point.x, point.y, point.type)) new_contour.closed = old_contour.closed new_path.add(new_contour) return new_path
def makecurve(pt1, pt2, c1, c2): p = Path() p.moveto(pt1.x, pt1.y) p.curveto(c1.x, c1.y, c2.x, c2.y, pt2.x, pt2.y) p.fill = None p.stroke = Color.BLACK p.strokeWidth = 1.0 return p
def parse_circle(e): cx = float(get_attribute(e, "cx")) cy = float(get_attribute(e, "cy")) r = float(get_attribute(e, "r")) if r < 0: print >> sys.stderr, "Error: invalid negative value for <circle> attribute r=\"%s\"" % r r = 0 p = Path() p.ellipse(cx, cy, r*2, r*2) p.close() return p
def transform(path, m, n1, n2, n3, points=100, range=TWOPI): first = True for i in _range(points): pt = path.getPoints#(float(i)/points) phi = i * range / points dx, dy = supercalc(m, n1, n2, n3, phi) if first: p = Path() p.moveto(pt.x+dx, pt.y+dy) first = False else: _ctx.lineto(pt.x+dx, pt.y+dy) p.lineto(pt.x+dx, pt.y+dy) return p
def shape_intersects(distance): tx, ty = coordinates(0, 0, distance, angle) t = Transform() t.translate(tx, ty) translated_shape = t.map(shape) if use_bounding_box: b = Path() b.cornerRect(translated_shape.bounds) else: b = translated_shape # If the shape intersects it is too close (the distance is too low). if bounding_path.intersects(b): return -1 return 1
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 parse_ellipse(e): cx = float(get_attribute(e, "cx")) cy = float(get_attribute(e, "cy")) rx = float(get_attribute(e, "rx")) ry = float(get_attribute(e, "ry")) if rx < 0: print >> sys.stderr, "Error: invalid negative value for <ellipse> attribute rx=\"%s\"" % rx rx = 0 if ry < 0: print >> sys.stderr, "Error: invalid negative value for <ellipse> attribute ry=\"%s\"" % ry ry = 0 p = Path() p.ellipse(cx, cy, rx * 2, ry * 2) p.close() return p
def connect(points, closed=True): """Connects all points in a path.""" if points is None: return None if len(points) < 2: return None points = list(points) start = points[0] p = Path() p.moveto(start.x, start.y) for point in points[1:]: p.lineto(point.x, point.y) if closed: p.close() p.stroke = Color.BLACK p.strokeWidth = 1.0 return p
def star(position, points, outer, inner): p = Path() p.moveto(position.x, position.y + outer / 2) # Calculate the points of the star. for i in xrange(1, points * 2): angle = i * pi / points radius = i % 2 and inner / 2 or outer / 2 x = position.x + radius * sin(angle) y = position.y + radius * cos(angle) p.lineto(x, y) p.close() return p
def arc(position, width, height, start_angle, degrees, arc_type): """Create an arc.""" if arc_type == "chord": awt_type = Arc2D.CHORD elif arc_type == "pie": awt_type = Arc2D.PIE else: awt_type = Arc2D.OPEN p = Path(Arc2D.Double(position.x-width/2, position.y-height/2, width, height, -start_angle, -degrees, awt_type)) return p
def path(position, width, height, m, n1, n2, n3, points=1000, percentage=1.0, range=TWOPI): path = Path() for i in _range(points): if i > points * percentage: continue phi = i * range / points dx, dy = supercalc(m, n1, n2, n3, phi) dx = (dx * width / 2) + position.x dy = (dy * height / 2) + position.y path.addPoint(dx, dy) return path
def lpath(x, y, angle, angleScale, length, thicknessScale, lengthScale, full_rule): p = Path() p.rect(0, -length / 2, 2, length) segment = p.asGeometry() # Now run the simulation g = Geometry() stack = [] angleStack = [] t = Transform() t.translate(x, y) for letter in full_rule: if re.search('[a-zA-Z]', letter): # Move forward and draw newShape = t.map(segment) g.extend(newShape) 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, lengthScale / 100.0) elif letter == '!': # Multiply thickness t.scale(thicknessScale / 100.0, 1.0) elif letter == ';': # Multiply angle angle *= angleScale / 100.0 elif letter == '_': # Divide length t.scale(1.0, 1.0 / (lengthScale / 100.0)) elif letter == '?': # Divide thickness t.scale(1.0 / (thicknessScale / 100.0), 1.0) elif letter == '@': # Divide angle angle /= angleScale / 100.0 return g
def quad_curve(pt1, pt2, t, distance): t /= 100.0 cx = pt1.x + t * (pt2.x - pt1.x) cy = pt1.y + t * (pt2.y - pt1.y) a = angle(pt1.x, pt1.y, pt2.x, pt2.y) + 90 qx, qy = coordinates(cx, cy, distance, a) p = Path() p.moveto(pt1.x, pt1.y) c1x = pt1.x + 2 / 3.0 * (qx - pt1.x) c1y = pt1.y + 2 / 3.0 * (qy - pt1.y) c2x = pt2.x + 2 / 3.0 * (qx - pt2.x) c2y = pt2.y + 2 / 3.0 * (qy - pt2.y) p.curveto(c1x, c1y, c2x, c2y, pt2.x, pt2.y) p.fill = None p.stroke = Color.BLACK p.strokeWidth = 1.0 return p
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 rect(position, width, height, roundness): """Create a rectangle or rounded rectangle.""" p = Path() if roundness == Point.ZERO: p.rect(position.x, position.y, width, height) else: p.roundedRect(position.x, position.y, width, height, roundness.x, roundness.y) return p
def _str_to_path(s): # Utility function to convert a string containing a list of points to a Path # The letter M marks the start of a new curve. p = Path() for curve_str in s.strip().split("M"): curve_str = curve_str.strip() if curve_str: coords = [] for coord in curve_str.split(" "): try: coords.append(float(coord)) except: pass coords = len(coords) % 2 and coords[:-1] or coords for i in range(0, len(coords), 2): x = coords[i] y = coords[i+1] if i == 0: p.moveto(x, y) else: p.lineto(x, y) return p
def parse_circle(e): x = float(get_attribute(e, "cx")) y = float(get_attribute(e, "cy")) r = float(get_attribute(e, "r")) p = Path() p.ellipse(x, y, r * 2, r * 2) p.close() return p
def parse_oval(e): x = float(get_attribute(e, "cx")) y = float(get_attribute(e, "cy")) w = float(get_attribute(e, "rx")) * 2 h = float(get_attribute(e, "ry")) * 2 p = Path() p.ellipse(x, y, w, h) p.close() return p
def quad_curve(pt1, pt2, t, distance): t /= 100.0 cx = pt1.x + t * (pt2.x - pt1.x) cy = pt1.y + t * (pt2.y - pt1.y) a = angle(pt1.x, pt1.y, pt2.x, pt2.y) + 90 qx, qy = coordinates(cx, cy, distance, a) p = Path() p.moveto(pt1.x, pt1.y) c1x = pt1.x + 2/3.0 * (qx - pt1.x) c1y = pt1.y + 2/3.0 * (qy - pt1.y) c2x = pt2.x + 2/3.0 * (qx - pt2.x) c2y = pt2.y + 2/3.0 * (qy - pt2.y) p.curveto(c1x, c1y, c2x, c2y, pt2.x, pt2.y) p.fill = None p.stroke = Color.BLACK p.strokeWidth = 1.0 return p
def parse_circle(e): cx = float(get_attribute(e, "cx")) cy = float(get_attribute(e, "cy")) r = float(get_attribute(e, "r")) if r < 0: print >> sys.stderr, "Error: invalid negative value for <circle> attribute r=\"%s\"" % r r = 0 p = Path() p.ellipse(cx, cy, r * 2, r * 2) p.close() return p
def polygon(position, radius, sides, align): """Draw a polygon.""" p = Path() x, y, r = position.x, position.y, radius sides = max(sides, 3) a = 360.0 / sides da = 0 if align: x0, y0 = coordinates(x, y, r, 0) x1, y1 = coordinates(x, y, r, a) da = -angle(x1, y1, x0, y0) for i in xrange(sides): x1, y1 = coordinates(x, y, r, (a * i) + da) if i == 0: p.moveto(x1, y1) else: p.lineto(x1, y1) p.close() return p
def parse_polygon(e): d = get_attribute(e, "points", default="") d = d.replace(" ", ",") d = d.replace("-", ",") d = d.split(",") points = [] for x in d: if x != "": points.append(float(x)) autoclosepath = True if (e.tagName == "polyline"): autoclosepath = False p = Path() p.moveto(points[0], points[1]) for i in range(len(points) / 2): p.lineto(points[i * 2], points[i * 2 + 1]) if autoclosepath: p.close() return p
def polygon(position, radius, sides, align): """Draw a polygon.""" p = Path() x, y, r = position.x, position.y, radius sides = max(sides, 3) a = 360.0 / sides da = 0 if align: x0, y0 = coordinates(x, y, r, 0) x1, y1 = coordinates(x, y, r, a) da = -angle(x1, y1, x0, y0) for i in xrange(sides): x1, y1 = coordinates(x, y, r, (a*i) + da) if i == 0: p.moveto(x1, y1) else: p.lineto(x1, y1) p.close() return p
def parse_polygon(e): d = get_attribute(e, "points", default="") d = d.replace(" ", ",") d = d.replace("-", ",") d = d.split(",") points = [] for x in d: if x != "": points.append(float(x)) autoclosepath = True if (e.tagName == "polyline") : autoclosepath = False p = Path() p.moveto(points[0], points[1]) for i in range(len(points)/2): p.lineto(points[i*2], points[i*2+1]) if autoclosepath: p.close() return p
def _str_to_path(s): # Utility function to convert a string containing a list of points to a Path # The letter M marks the start of a new curve. p = Path() for curve_str in s.strip().split("M"): curve_str = curve_str.strip() if curve_str: coords = [] for coord in curve_str.split(" "): try: coords.append(float(coord)) except: pass coords = len(coords) % 2 and coords[:-1] or coords for i in range(0, len(coords), 2): x = coords[i] y = coords[i + 1] if i == 0: p.moveto(x, y) else: p.lineto(x, y) return p
def parse_path(e): d = get_attribute(e, "d", default="") d = re.sub(r",", r" ", d) # get rid of all commas d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from commands d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from commands d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([^\s])", r"\1 \2", d) # separate commands from points d = re.sub(r"([^\s])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from points d = re.sub(r"([0-9])([+\-])", r"\1 \2", d) # separate digits when no comma d = re.sub(r"(\.[0-9]*)(\.)", r"\1 \2", d) # separate digits when no comma d = re.sub(r"([Aa](\s+[0-9]+){3})\s+([01])\s*([01])", r"\1 \3 \4 ", d) # shorthand elliptical arc path syntax d = re.sub(r"[\s\r\t\n]+", r" ", d) d = d.strip() path = Path() pp = PathParser(d) pp.reset() while not pp.isEnd(): pp.nextCommand() command = pp.command.lower() if command == 'm': p = pp.getAsCurrentPoint() path.moveto(p.x, p.y) pp.start = pp.current while not pp.isCommandOrEnd(): p = pp.getAsCurrentPoint() path.lineto(p.x, p.y) elif command == 'l': while not pp.isCommandOrEnd(): p = pp.getAsCurrentPoint() path.lineto(p.x, p.y) elif command == 'h': while not pp.isCommandOrEnd(): newP = Point((pp.isRelativeCommand() and pp.current.x or 0) + pp.getScalar(), pp.current.y) pp.current = newP path.lineto(pp.current.x, pp.current.y) elif command == 'v': while not pp.isCommandOrEnd(): newP = Point(pp.current.x, (pp.isRelativeCommand() and pp.current.y or 0) + pp.getScalar()) pp.current = newP path.lineto(pp.current.x, pp.current.y) elif command == 'c': while not pp.isCommandOrEnd(): curr = pp.current p1 = pp.getPoint() cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() path.curveto(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y) elif command == 's': while not pp.isCommandOrEnd(): curr = pp.current p1 = pp.getReflectedControlPoint() cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() path.curveto(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y) elif command == 'q': while not pp.isCommandOrEnd(): curr = pp.current cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() cp1x = curr.x + 2 / 3.0 * (cntrl.x - curr.x) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp1y = curr.y + 2 / 3.0 * (cntrl.y - curr.y) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp2x = cp1x + 1 / 3.0 * (cp.x - curr.x) # CP2 = CP1 + 1 / 3 *(QP2-QP0) cp2y = cp1y + 1 / 3.0 * (cp.y - curr.y) # CP2 = CP1 + 1 / 3 *(QP2-QP0) g.curveto(cp1x, cp1y, cp2x, cp2y, cp.x, cp.y) elif command == 't': while not pp.isCommandOrEnd(): curr = pp.current cntrl = pp.getReflectedControlPoint() pp.control = cntrl cp = pp.getAsCurrentPoint() cp1x = curr.x + 2 / 3.0 * (cntrl.x - curr.x) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp1y = curr.y + 2 / 3.0 * (cntrl.y - curr.y) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp2x = cp1x + 1 / 3.0 * (cp.x - curr.x) # CP2 = CP1 + 1 / 3 *(QP2-QP0) cp2y = cp1y + 1 / 3.0 * (cp.y - curr.y) # CP2 = CP1 + 1 / 3 *(QP2-QP0) path.curveto(cp1x, cp1y, cp2x, cp2y, cp.x, cp.y) elif command == 'a': while not pp.isCommandOrEnd(): curr = pp.current rx = pp.getScalar() ry = pp.getScalar() rot = pp.getScalar() # * (math.pi / 180.0) large = pp.getScalar() sweep = pp.getScalar() cp = pp.getAsCurrentPoint() ex = cp.x ey = cp.y segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, curr.x, curr.y) for seg in segs: bez = segmentToBezier(*seg) path.curveto(*bez) elif command == 'z': path.close() pp.current = pp.start return path
def ellipse(position, width, height): p = Path() p.ellipse(position.x, position.y, width, height) return p
def parse_path(e): d = get_attribute(e, "d", default="") # Divide the path data string into segments. # Each segment starts with a path command, # usually followed by coordinates. segments = [] i = 0 for j in range(len(d)): commands = [ "M", "m", "Z", "z", "L", "l", "H", "h", "V", "v", "C", "c", "S", "s", "A" ] if d[j] in commands: segments.append(d[i:j].strip()) i = j segments.append(d[i:].strip()) segments.remove("") previous_command = "" # Path origin (moved by MOVETO). x0 = 0 y0 = 0 # The current point in the path. dx = 0 dy = 0 # The previous second control handle. dhx = 0 dhy = 0 path = Path() for segment in segments: command = segment[0] if command in ["Z", "z"]: path.close() else: # The command is a pen move, line or curve. # Get the coordinates. points = segment[1:].strip() points = points.replace("-", ",-") points = points.replace(" ", ",") points = re.sub(",+", ",", points) points = points.strip(",") points = [float(i) for i in points.split(",")] # Absolute MOVETO. # Move the current point to the new coordinates. if command == "M": for i in range(len(points) / 2): path.moveto(points[i * 2], points[i * 2 + 1]) dx = points[i * 2] dy = points[i * 2 + 1] x0 = dx y0 = dy # Relative MOVETO. # Offset from the current point. elif command == "m": for i in range(len(points) / 2): path.moveto(dx + points[i * 2], dy + points[i * 2 + 1]) dx += points[i * 2] dy += points[i * 2 + 1] x0 = dx y0 = dy # Absolute LINETO. # Draw a line from the current point to the new coordinate. elif command == "L": for i in range(len(points) / 2): path.lineto(points[i * 2], points[i * 2 + 1]) dx = points[i * 2] dy = points[i * 2 + 1] # Relative LINETO. # Offset from the current point. elif command == "l": for i in range(len(points) / 2): path.lineto(dx + points[i * 2], dy + points[i * 2 + 1]) dx += points[i * 2] dy += points[i * 2 + 1] # Absolute horizontal LINETO. # Only the vertical coordinate is supplied. elif command == "H": for i in range(len(points)): path.lineto(points[i], dy) dx = points[i] # Relative horizontal LINETO. # Offset from the current point. elif command == "h": for i in range(len(points)): path.lineto(dx + points[i], dy) dx += points[i] # Absolute vertical LINETO. # Only the horizontal coordinate is supplied. if command == "V": for i in range(len(points)): path.lineto(dx, points[i]) dy = points[i] # Relative vertical LINETO. # Offset from the current point. elif command == "v": for i in range(len(points)): path.lineto(dx, dy + points[i]) dy += points[i] # Absolute CURVETO. # Draw a bezier with given control handles and destination. elif command == "C": for i in range(len(points) / 6): path.curveto(points[i * 6], points[i * 6 + 1], points[i * 6 + 2], points[i * 6 + 3], points[i * 6 + 4], points[i * 6 + 5]) dhx = points[i * 6 + 2] dhy = points[i * 6 + 3] dx = points[i * 6 + 4] dy = points[i * 6 + 5] # Relative CURVETO. # Offset from the current point. elif command == "c": for i in range(len(points) / 6): path.curveto(dx + points[i * 6], dy + points[i * 6 + 1], dx + points[i * 6 + 2], dy + points[i * 6 + 3], dx + points[i * 6 + 4], dy + points[i * 6 + 5]) dhx = dx + points[i * 6 + 2] dhy = dy + points[i * 6 + 3] dx += points[i * 6 + 4] dy += points[i * 6 + 5] # Absolute reflexive CURVETO. # Only the second control handle is given, # the first is the reflexion of the previous handle. elif command == "S": for i in range(len(points) / 4): if previous_command not in ["C", "c", "S", "s"]: dhx = dx dhy = dy else: dhx = dx - dhx dhy = dy - dhy path.curveto(dx + dhx, dy + dhy, points[i * 4], points[i * 4 + 1], points[i * 4 + 2], points[i * 4 + 3]) dhx = points[i * 4] dhy = points[i * 4 + 1] dx = points[i * 4 + 2] dy = points[i * 4 + 3] # Relative reflexive CURVETO. # Offset from the current point. elif command == "s": for i in range(len(points) / 4): if previous_command not in ["C", "c", "S", "s"]: dhx = dx dhy = dy else: dhx = dx - dhx dhy = dy - dhy path.curveto(dx + dhx, dy + dhy, dx + points[i * 4], dy + points[i * 4 + 1], dx + points[i * 4 + 2], dy + points[i * 4 + 3]) dhx = dx + points[i * 4] dhy = dy + points[i * 4 + 1] dx += points[i * 4 + 2] dy += points[i * 4 + 3] # Absolute elliptical arc. elif command == "A": rx, ry, phi, large_arc_flag, sweep_flag, x2, y2 = points for p in arc.elliptical_arc_to(dx, dy, rx, ry, phi, large_arc_flag, sweep_flag, x2, y2): if len(p) == 2: path.lineto(*p) elif len(p) == 6: path.curveto(*p) dx = p[-2] dy = p[-1] previous_command = command return path
def line(point1, point2): p = Path() p.line(point1.x, point1.y, point2.x, point2.y) p.strokeColor = Color.BLACK p.strokeWidth = 1 return p
def link(shape1, shape2, orientation): if shape1 is None or shape2 is None: return None p = Path() ax, ay, aw, ah = shape1.bounds bx, by, bw, bh = shape2.bounds if orientation == "horizontal": hw = (bx - (ax + aw)) / 2 p.moveto(ax + aw, ay) p.curveto(ax + aw + hw, ay, bx - hw, by, bx, by) p.lineto(bx, by + bh) p.curveto(bx - hw, by + bh, ax + aw + hw, ay + ah, ax + aw, ay + ah) else: hh = (by - (ay + ah)) / 2 p.moveto(ax, ay + ah) p.curveto(ax, ay + ah + hh, bx, by - hh, bx, by) p.lineto(bx + bw, by) p.curveto(bx + bw, by - hh, ax + aw, ay + ah + hh, ax + aw, ay + ah) return p
def generator(): """Serve as a template for future functions that generate geometry""" p = Path() p.rect(0, 0, 100, 100) return p
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
def parse_path(e): d = get_attribute(e, "d", default="") d = re.sub(r",", r" ", d) # get rid of all commas d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from commands d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from commands d = re.sub(r"([MmZzLlHhVvCcSsQqTtAa])([^\s])", r"\1 \2", d) # separate commands from points d = re.sub(r"([^\s])([MmZzLlHhVvCcSsQqTtAa])", r"\1 \2", d) # separate commands from points d = re.sub(r"([0-9])([+\-])", r"\1 \2", d) # separate digits when no comma d = re.sub(r"(\.[0-9]*)(\.)", r"\1 \2", d) # separate digits when no comma d = re.sub(r"([Aa](\s+[0-9]+){3})\s+([01])\s*([01])", r"\1 \3 \4 ", d) # shorthand elliptical arc path syntax d = re.sub(r"[\s\r\t\n]+", r" ", d) d = d.strip() path = Path() pp = PathParser(d) pp.reset() while not pp.isEnd(): pp.nextCommand() command = pp.command.lower() if command == 'm': p = pp.getAsCurrentPoint() path.moveto(p.x, p.y) pp.start = pp.current while not pp.isCommandOrEnd(): p = pp.getAsCurrentPoint() path.lineto(p.x, p.y) elif command == 'l': while not pp.isCommandOrEnd(): p = pp.getAsCurrentPoint() path.lineto(p.x, p.y) elif command == 'h': while not pp.isCommandOrEnd(): newP = Point((pp.isRelativeCommand() and pp.current.x or 0) + pp.getScalar(), pp.current.y) pp.current = newP path.lineto(pp.current.x, pp.current.y) elif command == 'v': while not pp.isCommandOrEnd(): newP = Point(pp.current.x, (pp.isRelativeCommand() and pp.current.y or 0) + pp.getScalar()) pp.current = newP path.lineto(pp.current.x, pp.current.y) elif command == 'c': while not pp.isCommandOrEnd(): curr = pp.current p1 = pp.getPoint() cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() path.curveto(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y) elif command == 's': while not pp.isCommandOrEnd(): curr = pp.current p1 = pp.getReflectedControlPoint() cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() path.curveto(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y) elif command == 'q': while not pp.isCommandOrEnd(): curr = pp.current cntrl = pp.getAsControlPoint() cp = pp.getAsCurrentPoint() cp1x = curr.x + 2 / 3.0 * (cntrl.x - curr.x ) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp1y = curr.y + 2 / 3.0 * (cntrl.y - curr.y ) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp2x = cp1x + 1 / 3.0 * (cp.x - curr.x ) # CP2 = CP1 + 1 / 3 *(QP2-QP0) cp2y = cp1y + 1 / 3.0 * (cp.y - curr.y ) # CP2 = CP1 + 1 / 3 *(QP2-QP0) g.curveto(cp1x, cp1y, cp2x, cp2y, cp.x, cp.y) elif command == 't': while not pp.isCommandOrEnd(): curr = pp.current cntrl = pp.getReflectedControlPoint() pp.control = cntrl cp = pp.getAsCurrentPoint() cp1x = curr.x + 2 / 3.0 * (cntrl.x - curr.x ) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp1y = curr.y + 2 / 3.0 * (cntrl.y - curr.y ) # CP1 = QP0 + 2 / 3 *(QP1-QP0) cp2x = cp1x + 1 / 3.0 * (cp.x - curr.x ) # CP2 = CP1 + 1 / 3 *(QP2-QP0) cp2y = cp1y + 1 / 3.0 * (cp.y - curr.y ) # CP2 = CP1 + 1 / 3 *(QP2-QP0) path.curveto(cp1x, cp1y, cp2x, cp2y, cp.x, cp.y) elif command == 'a': while not pp.isCommandOrEnd(): curr = pp.current rx = pp.getScalar() ry = pp.getScalar() rot = pp.getScalar() # * (math.pi / 180.0) large = pp.getScalar() sweep = pp.getScalar() cp = pp.getAsCurrentPoint() ex = cp.x ey = cp.y segs = arcToSegments(ex, ey, rx, ry, large, sweep, rot, curr.x, curr.y) for seg in segs: bez = segmentToBezier(*seg) path.curveto(*bez) elif command == 'z': path.close() pp.current = pp.start return path
def cook(generations,x,y,angle,angleScale,length,thicknessScale,lengthScale,premise,rule1,rule2,rule3): #segment = self.segment #if segment is None: p = Path() p.rect(0, -length/2, 2, length) segment = p.asGeometry() # Parse all rules ruleArgs = [rule1,rule2,rule3] rules = {} #rulenum = 1 #while hasattr(cook,"rule%i" % rulenum): for full_rule in ruleArgs: #full_rule = getattr("rule%i" % rulenum) 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" % full_rule) rule_key = full_rule[0] rule_value = full_rule[2:] rules[rule_key] = rule_value #rulenum += 1 # 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 rules: tmp_rule += rules[letter] else: tmp_rule += letter full_rule = tmp_rule # Now run the simulation g = Geometry() stack = [] angleStack = [] t = Transform() t.translate(x, y) angle = angle for letter in full_rule: if re.search('[a-zA-Z]',letter): # Move forward and draw newShape = t.map(segment) g.extend(newShape) 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, lengthScale/100.0) elif letter == '!': # Multiply thickness t.scale(thicknessScale/100.0, 1.0) elif letter == ';': # Multiply angle angle *= angleScale/100.0 elif letter == '_': # Divide length t.scale(1.0, 1.0/(lengthScale/100.0)) elif letter == '?': # Divide thickness t.scale(1.0/(thicknessScale/100.0), 1.0) elif letter == '@': # Divide angle angle /= angleScale/100.0 return g