コード例 #1
0
ファイル: operations.py プロジェクト: tatarize/vpype
def trim(document: vp.Document, margin_x: float, margin_y: float,
         layer: Union[int, List[int]]) -> vp.Document:
    """Trim the geometries by some margin.

    This command trims the geometries by the provided X and Y margins with respect to the
    current bounding box.

    By default, `trim` acts on all layers. If one or more layer IDs are provided with the
    `--layer` option, only these layers will be affected. In this case, the bounding box is
    that of the listed layers.
    """

    layer_ids = vp.multiple_to_layer_ids(layer, document)
    bounds = document.bounds(layer_ids)

    if not bounds:
        return document

    min_x = bounds[0] + margin_x
    max_x = bounds[2] - margin_x
    min_y = bounds[1] + margin_y
    max_y = bounds[3] - margin_y
    if min_x > max_x:
        min_x = max_x = 0.5 * (min_x + max_x)
    if min_y > max_y:
        min_y = max_y = 0.5 * (min_y + max_y)

    for vid in layer_ids:
        lc = document[vid]
        lc.crop(min_x, min_y, max_x, max_y)

    return document
コード例 #2
0
ファイル: test_model.py プロジェクト: vmario89/vpype
def test_document_bounds_empty_layer():
    doc = Document()

    doc.add(LineCollection([(0, 10 + 10j)]), 1)
    doc.add(LineCollection())

    assert doc.bounds() == (0, 0, 10, 10)
コード例 #3
0
ファイル: operations.py プロジェクト: tatarize/vpype
def layout(
    document: vp.Document,
    size: Tuple[float, float],
    landscape: bool,
    margin: Optional[float],
    align: str,
    valign: str,
) -> vp.Document:
    """Layout command"""

    size = _normalize_page_size(size, landscape)

    document.page_size = size
    bounds = document.bounds()

    if bounds is None:
        # nothing to layout
        return document

    min_x, min_y, max_x, max_y = bounds
    width = max_x - min_x
    height = max_y - min_y
    if margin is not None:
        document.translate(-min_x, -min_y)
        scale = min((size[0] - 2 * margin) / width,
                    (size[1] - 2 * margin) / height)
        document.scale(scale)
        min_x = min_y = 0.0
        width *= scale
        height *= scale
    else:
        margin = 0.0

    if align == "left":
        h_offset = margin - min_x
    elif align == "right":
        h_offset = size[0] - margin - width - min_x
    else:
        h_offset = margin + (size[0] - width - 2 * margin) / 2 - min_x

    if valign == "top":
        v_offset = margin - min_y
    elif valign == "bottom":
        v_offset = size[1] - margin - height - min_y
    else:
        v_offset = margin + (size[1] - height - 2 * margin) / 2 - min_y

    document.translate(h_offset, v_offset)
    return document
コード例 #4
0
ファイル: transforms.py プロジェクト: carewdavid/vpype
def _compute_origin(
    document: vp.Document,
    layer: Optional[Union[int, List[int]]],
    origin_coords: Optional[Union[Tuple[()], Tuple[float, float]]],
) -> Tuple[Tuple[float, float], List[int], Tuple[float, float, float, float]]:
    layer_ids = vp.multiple_to_layer_ids(layer, document)
    bounds = document.bounds(layer_ids)

    if not bounds:
        logging.warning("no geometry available, cannot compute origin")
        raise ValueError

    if origin_coords is not None and len(origin_coords) == 2:
        origin = origin_coords
    else:
        origin = (
            0.5 * (bounds[0] + bounds[2]),
            0.5 * (bounds[1] + bounds[3]),
        )

    return cast(Tuple[float, float], origin), layer_ids, bounds
コード例 #5
0
def dbsample(document: Document):
    """
    Show statistics on the current geometries in JSON format.
    """
    global debug_data

    data: Dict[str, Any] = {}
    if document.is_empty():
        data["count"] = 0
    else:
        data["count"] = sum(len(lc) for lc in document.layers.values())
        data["layer_count"] = len(document.layers)
        data["length"] = document.length()
        data["pen_up_length"] = document.pen_up_length()
        data["bounds"] = document.bounds()
        data["layers"] = {
            layer_id: [as_vector(line).tolist() for line in layer]
            for layer_id, layer in document.layers.items()
        }

    debug_data.append(data)
    return document
コード例 #6
0
ファイル: test_model.py プロジェクト: vmario89/vpype
def _all_document_ops(doc: Document):
    doc.bounds()
    doc.length()
    doc.segment_count()
コード例 #7
0
ファイル: test_model.py プロジェクト: vmario89/vpype
def test_document_bounds():
    doc = Document()
    doc.add(LineCollection([(-10, 10), (0, 0)]), 1)
    doc.add(LineCollection([(0, 0), (-10j, 10j)]), 2)
    assert doc.bounds() == (-10, -10, 10, 10)
コード例 #8
0
def layout(
    document: vp.Document,
    size: Tuple[float, float],
    landscape: bool,
    margin: Optional[float],
    align: str,
    valign: str,
) -> vp.Document:
    """Layout the geometries on the provided page size.

    By default, this command centers everything on the page. The horizontal and vertical
    alignment can be adjusted using the `--align`, resp. `--valign` options.

    Optionally, this command can scale the geometries to fit specified margins with the
    `--fit-to-margin` option.

    Examples:

        Fit the geometries to 3cm margins with top alignment (a generally pleasing arrangement
        for square designs on portrait-oriented pages):

            vpype read input.svg layout --fit-to-margin 3cm --valign top a4 write.svg
    """

    if landscape and size[0] < size[1]:
        size = size[::-1]

    document.page_size = size
    bounds = document.bounds()

    if bounds is None:
        # nothing to layout
        return document

    min_x, min_y, max_x, max_y = bounds
    width = max_x - min_x
    height = max_y - min_y
    if margin is not None:
        document.translate(-min_x, -min_y)
        scale = min((size[0] - 2 * margin) / width,
                    (size[1] - 2 * margin) / height)
        document.scale(scale)
        min_x = min_y = 0.0
        width *= scale
        height *= scale
    else:
        margin = 0.0

    if align == "left":
        h_offset = margin - min_x
    elif align == "right":
        h_offset = size[0] - margin - width - min_x
    else:
        h_offset = margin + (size[0] - width - 2 * margin) / 2 - min_x

    if valign == "top":
        v_offset = margin - min_y
    elif valign == "bottom":
        v_offset = size[1] - margin - height - min_y
    else:
        v_offset = margin + (size[1] - height - 2 * margin) / 2 - min_y

    document.translate(h_offset, v_offset)
    return document
コード例 #9
0
ファイル: display.py プロジェクト: stringertheory/vsketch
def display_matplotlib(
    document: vp.Document,
    page_size: Tuple[float, float] = None,
    center: bool = False,
    show_axes: bool = True,
    show_grid: bool = False,
    show_pen_up: bool = False,
    colorful: bool = False,
    unit: str = "px",
    fig_size: Tuple[float, float] = None,
) -> None:
    scale = 1 / vp.convert_length(unit)

    if fig_size:
        plt.figure(figsize=fig_size)
    plt.cla()

    # draw page
    if page_size is not None:
        w = page_size[0] * scale
        h = page_size[1] * scale
        dw = 10 * scale
        plt.fill(
            np.array([w, w + dw, w + dw, dw, dw, w]),
            np.array([dw, dw, h + dw, h + dw, h, h]),
            "k",
            alpha=0.3,
        )
        plt.plot(np.array([0, 1, 1, 0, 0]) * w,
                 np.array([0, 0, 1, 1, 0]) * h,
                 "-k",
                 lw=0.25)

    # compute offset
    offset = complex(0, 0)
    if center and page_size:
        bounds = document.bounds()
        if bounds is not None:
            offset = complex(
                (page_size[0] - (bounds[2] - bounds[0])) / 2.0 - bounds[0],
                (page_size[1] - (bounds[3] - bounds[1])) / 2.0 - bounds[1],
            )
    offset_ndarr = np.array([offset.real, offset.imag])

    # plot all layers
    color_idx = 0
    collections = {}
    for layer_id, lc in document.layers.items():
        if colorful:
            color: Union[Tuple[float, float, float],
                         List[Tuple[float, float,
                                    float]]] = (COLORS[color_idx:] +
                                                COLORS[:color_idx])
            color_idx += len(lc)
        else:
            color = COLORS[color_idx]
            color_idx += 1
        if color_idx >= len(COLORS):
            color_idx = color_idx % len(COLORS)

        # noinspection PyUnresolvedReferences
        layer_lines = matplotlib.collections.LineCollection(
            (vp.as_vector(line + offset) * scale for line in lc),
            color=color,
            lw=1,
            alpha=0.5,
            label=str(layer_id),
        )
        collections[layer_id] = [layer_lines]
        plt.gca().add_collection(layer_lines)

        if show_pen_up:
            # noinspection PyUnresolvedReferences
            pen_up_lines = matplotlib.collections.LineCollection(
                ((
                    (vp.as_vector(lc[i])[-1] + offset_ndarr) * scale,
                    (vp.as_vector(lc[i + 1])[0] + offset_ndarr) * scale,
                ) for i in range(len(lc) - 1)),
                color=(0, 0, 0),
                lw=0.5,
                alpha=0.5,
            )
            collections[layer_id].append(pen_up_lines)
            plt.gca().add_collection(pen_up_lines)

    plt.gca().invert_yaxis()
    plt.axis("equal")
    plt.margins(0, 0)

    if show_axes or show_grid:
        plt.axis("on")
        plt.xlabel(f"[{unit}]")
        plt.ylabel(f"[{unit}]")
    else:
        plt.axis("off")
    if show_grid:
        plt.grid("on")

    plt.show()
コード例 #10
0
ファイル: display.py プロジェクト: stringertheory/vsketch
def display_ipython(
    document: vp.Document,
    page_size: Optional[Tuple[float, float]],
    center: bool = False,
    show_pen_up: bool = False,
    color_mode: str = "layer",
) -> None:
    """Implements a SVG previsualisation with pan/zoom support for IPython.

    If page_size is provided, a page is displayed and the sketch is laid out on it. Otherwise
    the sketch is displayed using its intrinsic boundaries.
    """
    if "IPython" not in sys.modules:
        raise RuntimeError("IPython display cannot be used outside of IPython")

    svg_io = io.StringIO()
    vp.write_svg(
        svg_io,
        document,
        page_size if page_size is not None else (0, 0),
        center,
        show_pen_up=show_pen_up,
        color_mode=color_mode,
    )

    MARGIN = 10

    if page_size is None:
        bounds = document.bounds()
        if bounds:
            svg_width = bounds[2] - bounds[0]
            svg_height = bounds[3] - bounds[1]
        else:
            svg_width = 0
            svg_height = 0
    else:
        svg_width = page_size[0]
        svg_height = page_size[1]

    page_boundaries = f"""
        <polygon points="{svg_width},{MARGIN}
            {svg_width + MARGIN},{MARGIN}
            {svg_width + MARGIN},{svg_height + MARGIN}
            {MARGIN},{svg_height + MARGIN}
            {MARGIN},{svg_height}
            {svg_width},{svg_height}"
            style="fill:black;stroke:none;opacity:0.3;" />
        <rect width="{svg_width}" height="{svg_height}"
            style="fill:none;stroke-width:1;stroke:rgb(0,0,0)" />
    """

    svg_margin = MARGIN if page_size is not None else 0
    svg_id = f"svg_display_{random.randint(0, 10000)}"

    IPython.display.display_html(
        f"""<div id="container" style="width: 80%; height: {svg_height + svg_margin}px;">
            <svg id="{svg_id}" width="{svg_width + svg_margin}px"
                    height={svg_height + svg_margin}
                    viewBox="0 0 {svg_width + svg_margin} {svg_height + svg_margin}">
                {page_boundaries if page_size is not None else ""}
                {svg_io.getvalue()}
            </svg>
        </div>
        <script src="{get_svg_pan_zoom_url()}"></script>
        <script>
            svgPanZoom('#{svg_id}', {{
                zoomEnabled: true,
                controlIconsEnabled: true,
                center: true,
                zoomScaleSensitivity: 0.3,
                contain: true,
            }});
          </script>
        """,
        raw=True,
    )