1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
# Copyright (c) 2023, Manfred Moitzi
# License: MIT License
from typing import Iterator
import pathlib
import random
import ezdxf
from ezdxf.addons.drawing import (
RenderContext,
Frontend,
recorder,
config,
layout,
pymupdf,
)
from ezdxf.math import BoundingBox2d
# ------------------------------------------------------------------------------
# This example renders the DXF file in rows by cols tiles including filtering
# the DXF entities outside the rendering area.
#
# This example is a reimplementation of the "render_model_space_as_tiles.py" example
# nut uses the new RecorderBackend introduced in ezdxf v1.1.
# The RecorderBackend stores the output of the Frontend as compact objects based on
# numpy arrays. The recordings can be replayed on any other backend, including the
# older Matplotlib- and PyQt backends. The player class provides fast bounding box
# detection, inplace transformation and rectangular content cropping.
#
# docs: https://ezdxf.mozman.at/docs/addons/drawing.html
# ------------------------------------------------------------------------------
CWD = pathlib.Path("~/Desktop/Outbox").expanduser()
if not CWD.exists():
CWD = pathlib.Path(".")
COLORS = list(range(1, 7))
WIDTH = 400
HEIGHT = 400
def random_points(count: int, width: float, height: float):
for _ in range(count):
yield width * random.random(), height * random.random()
def create_content(msp):
for s, e in zip(
random_points(100, WIDTH, HEIGHT), random_points(100, WIDTH, HEIGHT)
):
msp.add_line(s, e, dxfattribs={"color": random.choice(COLORS)})
def render_areas(extents, grid=(2, 2)) -> Iterator[BoundingBox2d]:
"""Returns a bounding box for each tile to render."""
rows, cols = grid
tile_width = extents.size.x / cols
tile_height = extents.size.y / rows
for row in range(rows):
for col in range(cols):
x_min = extents.extmin.x + col * tile_width
y_min = extents.extmin.y + row * tile_height
# BoundingBox2d ignores the z-axis!
yield BoundingBox2d(
[(x_min, y_min), (x_min + tile_width, y_min + tile_height)]
)
def main(rows: int, cols: int):
doc = ezdxf.new()
msp = doc.modelspace()
create_content(msp)
ctx = RenderContext(doc)
# record the output of the Frontend class
recorder_backend = recorder.Recorder()
Frontend(
ctx,
recorder_backend,
config=config.Configuration(background_policy=config.BackgroundPolicy.WHITE),
).draw_layout(msp)
# the main player has access to the original recordings
main_player = recorder_backend.player()
# basic layout settings for full image and all tiles
settings = layout.Settings(fit_page=False)
# export full image
# always copy the main player when the content is rendered multiple times!
full_player = main_player.copy()
# create the output backend
png_backend = pymupdf.PyMuPdfBackend()
full_player.replay(png_backend)
# export full image as png file, auto-detect image size
page = layout.Page(0, 0, layout.Units.mm)
(CWD / "full_image.png").write_bytes(
png_backend.get_pixmap_bytes(page, settings=settings)
)
# calculating tile size:
extents = full_player.bbox()
image_size = extents.size
tile_size_x = image_size.x / cols
tile_size_y = image_size.y / rows
# export image as tiles
for tile, render_area in enumerate(render_areas(extents, (rows, cols))):
# copy the content of the main player!!!
tile_player = main_player.copy()
# crop content to the size of the tile
tile_player.crop_rect(render_area.extmin, render_area.extmax, distance=0.1)
# create a new output backend for each tile
png_backend = pymupdf.PyMuPdfBackend()
tile_player.replay(png_backend)
filename = f"tile-{tile:02d}.png"
print(f'saving tile #{tile} to "{filename}"')
# export tile as png file
page = layout.Page(tile_size_x, tile_size_y, layout.Units.mm)
(CWD / filename).write_bytes(
png_backend.get_pixmap_bytes(
page, fmt="png", settings=settings, render_box=render_area
)
)
if __name__ == "__main__":
# create 3x3 tiles
main(3, 3)
|