def transform_line(shape, xfrom, yfrom, xto, yto, angle, flip, mirror):
    shape = shape.copy()
    (shape["x1"],
     shape["y1"]) = alterpcb_core.transform_point(shape["x1"], shape["y1"],
                                                  xfrom, yfrom, xto, yto,
                                                  angle, flip, mirror)
    (shape["x2"],
     shape["y2"]) = alterpcb_core.transform_point(shape["x2"], shape["y2"],
                                                  xfrom, yfrom, xto, yto,
                                                  angle, flip, mirror)
    if flip:
        shape["layer"] = alterpcb_core.flip_layer(shape["layer"])
    return shape
def transform_text(shape, xfrom, yfrom, xto, yto, angle, flip, mirror):
	shape = shape.copy()
	(shape["x"], shape["y"]) = alterpcb_core.transform_point(shape["x"], shape["y"], xfrom, yfrom, xto, yto, angle, flip, mirror)
	if flip != mirror:
		shape["angle"] = (angle - shape["angle"]) % 360.0
		shape["mirror"] = not shape["mirror"]
	else:
		shape["angle"] = (angle + shape["angle"]) % 360.0
	if flip:
		shape["layer"] = alterpcb_core.flip_layer(shape["layer"])
	return shape
def transform_path(shape, xfrom, yfrom, xto, yto, angle, flip, mirror):
    shape = shape.copy()
    (px, py) = alterpcb_core.equalize_arrays(shape["x"], shape["y"])
    (px, py) = alterpcb_core.transform_point(px, py, xfrom, yfrom, xto, yto,
                                             angle, flip, mirror)
    (shape["x"], shape["y"]) = (list(px), list(py))
    if flip != mirror:
        if type(shape["width"]) == list:
            shape["width"] = shape["width"][::-1]
        if type(shape["space"]) == list:
            shape["space"] = shape["space"][::-1]
        shape["centerline"] = -shape["centerline"]
    if flip:
        shape["layer"] = alterpcb_core.flip_layer(shape["layer"])
    return shape
def transform_arc(shape, xfrom, yfrom, xto, yto, angle, flip, mirror):
    shape = shape.copy()
    (shape["x"],
     shape["y"]) = alterpcb_core.transform_point(shape["x"], shape["y"], xfrom,
                                                 yfrom, xto, yto, angle, flip,
                                                 mirror)
    if flip != mirror:
        d = shape["angle2"] - shape["angle1"]
        shape["angle1"] = (angle - shape["angle1"] + 180.0) % 360.0
        shape["angle2"] = shape["angle1"] - d
    else:
        d = shape["angle2"] - shape["angle1"]
        shape["angle1"] = (angle + shape["angle1"]) % 360.0
        shape["angle2"] = shape["angle1"] + d
    if flip:
        shape["layer"] = alterpcb_core.flip_layer(shape["layer"])
    return shape
def text(x=0.0, y=0.0, angle=0.0, mirror=False, layer="silk-top", text="Text", font="DejaVuSans", size=2.0, halign="left", valign="baseline", spacing=0.0, steps=8, hole=False, order=0):
	pcb = alterpcb_core.Pcb()
	
	# load font
	if font not in loaded_fonts:
		loaded_fonts[font] = freetype.Face("fonts/%s.ttf" % (font))
	face = loaded_fonts[font]
	xscale = size / face.units_per_EM
	yscale = size / face.units_per_EM
	
	# calculate total width
	total_width = 0.0
	for ch in range(len(text)):
		face.load_char(text[ch], freetype.FT_LOAD_NO_HINTING | freetype.FT_LOAD_NO_SCALE)
		if ch != 0:
			kerning = face.get_kerning(text[ch - 1], text[ch], freetype.FT_KERNING_UNSCALED)
			total_width += kerning.x * xscale + spacing
		total_width += face.glyph.advance.x * xscale
	
	# calculate start position
	if halign == "left":
		pos_x = 0.0
	elif halign == "center":
		pos_x = -total_width / 2
	elif halign == "right":
		pos_x = -total_width
	else:
		raise Exception("Unknown horizontal alignment!")
	if valign == "baseline":
		pos_y = 0.0
	elif valign == "top":
		pos_y = -face.ascender * yscale
	elif valign == "center":
		pos_y = -(face.ascender + face.descender) / 2 * yscale
	elif valign == "bottom":
		pos_y = -face.descender * yscale
	else:
		raise Exception("Unknown vertical alignment!")
	
	# convert to polygons
	for ch in range(len(text)):
		
		# load one glyph
		face.load_char(text[ch], freetype.FT_LOAD_NO_HINTING | freetype.FT_LOAD_NO_SCALE)
		outline = face.glyph.outline
		
		# move position
		if ch != 0:
			kerning = face.get_kerning(text[ch - 1], text[ch], freetype.FT_KERNING_UNSCALED)
			pos_x += kerning.x * xscale + spacing
		
		# add polygons
		start = 0
		for contour in outline.contours:
			end = contour + 1
			points = outline.points[start:end]
			tags = outline.tags[start:end]
			start = end
			
			oncurve = [bool((tags[i] >> 0) & 1) for i in range(len(points))]
			thirdorder = [bool((tags[i] >> 1) & 1) for i in range(len(points))]
			hasmode = [bool((tags[i] >> 2) & 1) for i in range(len(points))]
			mode = [(tags[i] >> 5) & 0x7 for i in range(len(points))]
			
			t = (numpy.arange(steps) + 1) / steps
			n = len(oncurve)
			px = []
			py = []
			for i in range(len(points)):
				if oncurve[i]:
					if oncurve[i - 1]:
						px.append(points[i][0])
						py.append(points[i][1])
				else:
					assert(not thirdorder[i])
					x1 = points[i - 1][0] if oncurve[i - 1] else (points[i][0] + points[i - 1][0]) / 2
					y1 = points[i - 1][1] if oncurve[i - 1] else (points[i][1] + points[i - 1][1]) / 2
					x2 = points[i + 1 - n][0] if oncurve[i + 1 - n] else (points[i][0] + points[i + 1 - n][0]) / 2
					y2 = points[i + 1 - n][1] if oncurve[i + 1 - n] else (points[i][1] + points[i + 1 - n][1]) / 2
					xx = bezier2(x1, points[i][0], x2, t)
					yy = bezier2(y1, points[i][1], y2, t)
					px.extend(xx)
					py.extend(yy)
			hh = (1 if hole else 0) + (1 if polygon_direction(px, py) == "ccw" else 0)
			px = pos_x + numpy.array(px) * xscale
			py = pos_y + numpy.array(py) * yscale
			(px, py) = alterpcb_core.transform_point(px, py, 0.0, 0.0, x, y, angle, False, mirror)
			pcb.add("polygon", layer=layer, x=px, y=py, hole=bool(hh % 2), order=order + hh // 2)
		
		# move position again
		pos_x += face.glyph.advance.x * xscale
	
	return pcb