def _formatArc(self, arc, se: sexpr.SexprBuilder):
        se.startGroup("fp_arc", newline=True, indent=False)

        start = arc["start"]
        end = arc["end"]

        fp_arc = [
            {
                "start": [start["x"], start["y"]]
            },
            {
                "end": [end["x"], end["y"]]
            },
            {
                "angle": arc["angle"]
            },
            {
                "layer": arc["layer"]
            },
            {
                "width": arc["width"]
            },
        ]

        se.addItems(fp_arc, newline=False)
        se.endGroup(newline=False)
    def _formatCircle(self, circle, se: sexpr.SexprBuilder):
        se.startGroup("fp_circle", newline=True, indent=False)

        center = circle["center"]
        end = circle["end"]

        fp_circle = [
            {
                "center": [center["x"], center["y"]]
            },
            {
                "end": [end["x"], end["y"]]
            },
            {
                "layer": circle["layer"]
            },
            {
                "width": circle["width"]
            },
        ]

        se.addItems(fp_circle, newline=False)
        se.endGroup(newline=False)
    def _formatRect(self, line, se: sexpr.SexprBuilder):
        se.startGroup("fp_rect", newline=True, indent=False)

        start = line["start"]
        end = line["end"]

        fp_line = [
            {
                "start": [start["x"], start["y"]]
            },
            {
                "end": [end["x"], end["y"]]
            },
            {
                "layer": line["layer"]
            },
            {
                "width": line["width"]
            },
        ]

        se.addItems(fp_line, newline=False)
        se.endGroup(newline=False)
    def _formatModel(self, model, se: sexpr.SexprBuilder):
        se.startGroup("model", newline=True, indent=False)

        se.addItems(model["file"], newline=False)
        """
          at
          scale
          rotate
        """

        at = model["pos"]
        sc = model["scale"]
        ro = model["rotate"]

        se.addItems({"at": {
            "xyz": [at["x"], at["y"], at["z"]]
        }},
                    newline=True,
                    indent=True)
        se.addItems({"scale": {
            "xyz": [sc["x"], sc["y"], sc["z"]]
        }},
                    newline=True,
                    indent=False)
        se.addItems({"rotate": {
            "xyz": [ro["x"], ro["y"], ro["z"]]
        }},
                    newline=True)

        se.endGroup(newline=True)
    def _formatPad(self, pad, se: sexpr.SexprBuilder):
        pos = pad["pos"]

        se.startGroup("pad", newline=True, indent=False)

        fp_pad = [pad["number"], pad["type"], pad["shape"]]

        at = [pos["x"], pos["y"]]

        rot = pos.get("orientation", 0)
        if rot:
            at.append(rot)

        fp_pad.append({"at": at})

        fp_pad.append({"size": [pad["size"]["x"], pad["size"]["y"]]})

        # Drill?
        _drill = pad.get("drill", None)

        if _drill:
            d = []
            if _drill["shape"] == "oval":
                d.append("oval")

            if _drill["size"]:
                d.append(_drill["size"]["x"])

                # Oval drill requires x,y pair
                if _drill["shape"] == "oval":
                    d.append(_drill["size"]["y"])

            if _drill["offset"]:
                o = [_drill["offset"]["x"], _drill["offset"]["y"]]
                d.append({"offset": o})

            fp_pad.append({"drill": d})

        # Layers
        fp_pad.append({"layers": pad["layers"]})

        se.addItems(fp_pad, newline=False)

        extras = []

        # die length
        if pad["die_length"]:
            extras.append({"die_length": pad["die_length"]})

        # rect_delta
        if pad["rect_delta"]:
            extras.append({"rect_delta": pad["rect_delta"]})

        # clearances zones settings
        # clearance
        if pad["clearance"]:
            extras.append({"clearance": pad["clearance"]})
        # solder mask margin
        if pad["solder_mask_margin"]:
            extras.append({"solder_mask_margin": pad["solder_mask_margin"]})
        # solder paste margin
        if pad["solder_paste_margin"]:
            extras.append({"solder_paste_margin": pad["solder_paste_margin"]})
        # solder paste margin ratio
        if pad["solder_paste_margin_ratio"]:
            extras.append({
                "solder_paste_margin_ratio":
                pad["solder_paste_margin_ratio"]
            })

        # copper zones settings
        # zone connect
        if pad["zone_connect"]:
            extras.append({"zone_connect": pad["zone_connect"]})
        # thermal width
        if pad["thermal_width"]:
            extras.append({"thermal_width": pad["thermal_width"]})
        # thermal gap
        if pad["thermal_gap"]:
            extras.append({"thermal_gap": pad["thermal_gap"]})

        # TODO: properly format custom pad shapes

        if extras:
            se.addItems(extras, newline=True, indent=True)
            se.unIndent()

        se.endGroup(newline=False)
    def _formatPoly(self, poly, se: sexpr.SexprBuilder):
        se.startGroup("fp_poly", newline=True, indent=False)

        se.startGroup("pts", newline=False, indent=True)
        pos = 0
        for pt in poly["points"]:
            se.addItem([{
                "xy": [pt["x"], pt["y"]]
            }],
                       newline=(pos != 0),
                       indent=(pos == 1))
            pos += 1
        if pos > 1:
            se.unIndent()
        se.endGroup(newline=False)

        fp_poly = [{"layer": poly["layer"]}, {"width": poly["width"]}]
        se.addItems(fp_poly, newline=False)
        se.endGroup(newline=False)
    def _formatText(self, text_type, text, se: sexpr.SexprBuilder) -> None:
        """
        Text is formatted like thus:
        (fp_text <type> <value> (at <x> <y> <R>*) (layer <layer>)
          (effects (font (size <sx> <sy>) (thickness <t>)))
        )
        """

        # Text
        se.startGroup("fp_text")

        # Extract position informat
        tp = text["pos"]
        pos = [tp["x"], tp["y"]]
        rot = tp.get("orientation", 0)
        if rot not in [0, None]:
            pos.append(rot)

        se.addItems(
            [
                text_type, text[text_type], {
                    "at": pos
                }, {
                    "layer": text["layer"]
                }
            ],
            newline=False,
        )

        tf = text["font"]

        font = [{
            "font": [
                {
                    "size": [tf["height"], tf["width"]]
                },
                {
                    "thickness": tf["thickness"]
                },
            ]
        }]
        italic = tf.get("italic", None)
        if italic:
            font.append(italic)

        se.startGroup("effects", indent=True)
        se.addItems(font, newline=False)
        se.endGroup(False)
        se.endGroup(True)