Skip to content

Commit

Permalink
fix type annotation & reformat by black
Browse files Browse the repository at this point in the history
  • Loading branch information
mozman committed Dec 29, 2023
1 parent f4dbb7a commit 66a6a19
Show file tree
Hide file tree
Showing 15 changed files with 172 additions and 58 deletions.
8 changes: 6 additions & 2 deletions src/ezdxf/addons/drawing/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ def draw_filled_polygon(
raise NotImplementedError

@abstractmethod
def draw_image(self, image: np.ndarray, transform: Matrix44, properties: BackendProperties) -> None:
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
raise NotImplementedError

@abstractmethod
Expand Down Expand Up @@ -187,7 +189,9 @@ def draw_filled_polygon(
raise NotImplementedError

@abstractmethod
def draw_image(self, image: np.ndarray, transform: Matrix44, properties: BackendProperties) -> None:
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
"""Draw an image with the given pixels
Args:
Expand Down
1 change: 1 addition & 0 deletions src/ezdxf/addons/drawing/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class HatchPolicy(Enum):
SHOW_SOLID: show HATCH entities as solid filling regardless of the pattern
"""

NORMAL = auto()
IGNORE = auto()
SHOW_OUTLINE = auto()
Expand Down
4 changes: 3 additions & 1 deletion src/ezdxf/addons/drawing/debug_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ def draw_filled_polygon(
) -> None:
self.collector.append(("filled_polygon", points, properties))

def draw_image(self, image: np.ndarray, transform: Matrix44, properties: BackendProperties) -> None:
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
self.collector.append(("image", image, transform, properties))

def set_background(self, color: str) -> None:
Expand Down
13 changes: 9 additions & 4 deletions src/ezdxf/addons/drawing/designer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import numpy as np
from typing_extensions import TypeAlias
import abc
import itertools

from ezdxf.colors import RGB
import ezdxf.bbox
Expand Down Expand Up @@ -120,7 +119,9 @@ def draw_text(
...

@abc.abstractmethod
def draw_image(self, image: np.ndarray, transform: Matrix44, properties: Properties) -> None:
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: Properties
) -> None:
...

@abc.abstractmethod
Expand Down Expand Up @@ -410,8 +411,12 @@ def draw_text(
properties.filling = Filling()
self._draw_filled_paths(transformed_paths, properties)

def draw_image(self, image: np.ndarray, transform: Matrix44, properties: Properties) -> None:
self.backend.draw_image(image, transform, self.get_backend_properties(properties))
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: Properties
) -> None:
self.backend.draw_image(
image, transform, self.get_backend_properties(properties)
)

def finalize(self) -> None:
self.backend.finalize()
Expand Down
9 changes: 7 additions & 2 deletions src/ezdxf/addons/drawing/dxf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
from typing import Iterable, TYPE_CHECKING, no_type_check
from functools import lru_cache
import enum

import numpy as np

from ezdxf import colors
from ezdxf.lldxf.const import VALID_DXF_LINEWEIGHTS
from ezdxf.math import Vec2, BoundingBox2d
from ezdxf.math import Vec2, BoundingBox2d, Matrix44
from ezdxf.path import to_splines_and_polylines, to_hatches
from ezdxf.layouts import BaseLayout

Expand Down Expand Up @@ -163,6 +163,11 @@ def draw_filled_polygon(
hatch.paths.add_polyline_path(points.vertices(), is_closed=True)
self.set_solid_fill(hatch, properties)

def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
pass # TODO: not implemented

def configure(self, config: Configuration) -> None:
pass

Expand Down
90 changes: 65 additions & 25 deletions src/ezdxf/addons/drawing/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
from ezdxf.addons.drawing.config import (
Configuration,
ProxyGraphicPolicy,
HatchPolicy, ImagePolicy,
HatchPolicy,
ImagePolicy,
)
from ezdxf.addons.drawing.backend import BackendInterface
from ezdxf.addons.drawing.properties import (
Expand Down Expand Up @@ -57,7 +58,8 @@
Face3d,
OLE2Frame,
Point,
Viewport, Image,
Viewport,
Image,
)
from ezdxf.entities.attrib import BaseAttrib
from ezdxf.entities.polygon import DXFPolygon
Expand All @@ -79,7 +81,7 @@
from ezdxf.lldxf import const
from ezdxf.render import hatching
from ezdxf.fonts import fonts
from ezdxf.colors import RGB
from ezdxf.colors import RGB, RGBA
from .type_hints import Color

if TYPE_CHECKING:
Expand Down Expand Up @@ -626,63 +628,97 @@ def draw_ole2frame_entity(self, entity: DXFGraphic, properties: Properties) -> N

def draw_image_entity(self, entity: DXFGraphic, properties: Properties) -> None:
image = cast(Image, entity)

if self.config.image_policy in (ImagePolicy.DISPLAY, ImagePolicy.RECT, ImagePolicy.MISSING):
image_policy = self.config.image_policy
image_def = image.image_def
assert image_def is not None

if image_policy in (
ImagePolicy.DISPLAY,
ImagePolicy.RECT,
ImagePolicy.MISSING,
):
loaded_image = None
show_filename_if_missing = True

if self.config.image_policy == ImagePolicy.RECT or not image.dxf.flags & Image.SHOW_IMAGE:
if (
image_policy == ImagePolicy.RECT
or not image.dxf.flags & Image.SHOW_IMAGE
):
loaded_image = None
show_filename_if_missing = False
elif self.config.image_policy != ImagePolicy.MISSING and self.ctx.document_dir is not None:
image_path = self.ctx.document_dir / image.image_def.dxf.filename.replace('\\', os.path.sep)
elif (
image_policy != ImagePolicy.MISSING
and self.ctx.document_dir is not None
):
assert image_def is not None
image_path = self.ctx.document_dir / image_def.dxf.filename.replace(
"\\", os.path.sep
)
with contextlib.suppress(FileNotFoundError):
loaded_image = PIL.Image.open(image_path)

if loaded_image is not None:
loaded_image = loaded_image.convert('RGBA')
color: RGB | RGBA
loaded_image = loaded_image.convert("RGBA")

if image.dxf.contrast != 50:
# note: this is only an approximation. Unclear what the exact operation AutoCAD uses
amount = image.dxf.contrast / 50
loaded_image = PIL.ImageEnhance.Contrast(loaded_image).enhance(amount)
loaded_image = PIL.ImageEnhance.Contrast(loaded_image).enhance(
amount
)

if image.dxf.fade != 0:
# note: this is only an approximation. Unclear what the exact operation AutoCAD uses
amount = image.dxf.fade / 100
color = RGB.from_hex(self.ctx.current_layout_properties.background_color)
color = RGB.from_hex(
self.ctx.current_layout_properties.background_color
)
loaded_image = _blend_image_towards(loaded_image, amount, color)

if image.dxf.brightness != 50:
# note: this is only an approximation. Unclear what the exact operation AutoCAD uses
amount = image.dxf.brightness / 50 - 1
if amount > 0:
color = (255, 255, 255, 255)
color = RGBA(255, 255, 255, 255)
else:
color = (0, 0, 0, 255)
color = RGBA(0, 0, 0, 255)
amount = -amount
loaded_image = _blend_image_towards(loaded_image, amount, color)

if not image.dxf.flags & Image.USE_TRANSPARENCY:
loaded_image.putalpha(255)

if image.transparency != 0.0:
loaded_image = _multiply_alpha(loaded_image, 1.0 - image.transparency)
loaded_image = _multiply_alpha(
loaded_image, 1.0 - image.transparency
)

if image.dxf.flags & Image.USE_CLIPPING_BOUNDARY:
loaded_image = _mask_image(loaded_image, [(p.x, p.y) for p in image.boundary_path_ocs()])
loaded_image = _mask_image(
loaded_image, [(p.x, p.y) for p in image.boundary_path_ocs()]
)

self.designer.draw_image(np.asarray(loaded_image), image.get_wcs_transform(), properties)
self.designer.draw_image(
np.asarray(loaded_image), image.get_wcs_transform(), properties
)

elif show_filename_if_missing:
# TODO: unclear what logic AutoCAD uses to determine the font size. Also text is supposed to be centered
default_cap_height = 50 # chosen empirically
self.designer.draw_text(image.image_def.dxf.filename, image.get_wcs_transform(), properties, default_cap_height)
self.designer.draw_text(
image_def.dxf.filename,
image.get_wcs_transform(),
properties,
default_cap_height,
)

self.designer.draw_solid_lines(list(pairwise(v.vec2 for v in image.boundary_path_wcs())), properties)
self.designer.draw_solid_lines(
list(pairwise(v.vec2 for v in image.boundary_path_wcs())), properties
)

elif self.config.image_policy == ImagePolicy.PROXY:
self.draw_proxy_graphic(entity.proxy_graphic, entity.doc)
self.draw_proxy_graphic(entity.proxy_graphic, entity.doc) # type: ignore

elif self.config.image_policy == ImagePolicy.IGNORE:
pass
Expand Down Expand Up @@ -918,23 +954,27 @@ def _draw_viewports(frontend: UniversalFrontend, viewports: list[Viewport]) -> N
frontend.draw_viewport(viewport)


def _blend_image_towards(image: PIL.Image.Image, amount: float, color: tuple[int, int, int, int]) -> PIL.Image.Image:
def _blend_image_towards(
image: PIL.Image.Image, amount: float, color: RGB | RGBA
) -> PIL.Image.Image:
original_alpha = image.split()[-1]
destination = PIL.Image.new('RGBA', image.size, color)
destination = PIL.Image.new("RGBA", image.size, color)
updated_image = PIL.Image.blend(image, destination, amount)
updated_image.putalpha(original_alpha)
return updated_image


def _mask_image(image: PIL.Image.Image, clip_polygon: list[tuple[float, float]]) -> PIL.Image.Image:
mask = PIL.Image.new('L', image.size, 0)
def _mask_image(
image: PIL.Image.Image, clip_polygon: list[tuple[float, float]]
) -> PIL.Image.Image:
mask = PIL.Image.new("L", image.size, 0)
PIL.ImageDraw.ImageDraw(mask).polygon(clip_polygon, outline=None, width=0, fill=1)
masked_image = np.array(image)
masked_image[:, :, 3] *= np.asarray(mask)
return PIL.Image.fromarray(masked_image, 'RGBA')
return PIL.Image.fromarray(masked_image, "RGBA")


def _multiply_alpha(image: PIL.Image.Image, amount: float) -> PIL.Image.Image:
output_image = np.array(image, dtype=np.float64)
output_image[:, :, 3] *= amount
return PIL.Image.fromarray(output_image.astype(np.uint8), 'RGBA')
return PIL.Image.fromarray(output_image.astype(np.uint8), "RGBA")
8 changes: 7 additions & 1 deletion src/ezdxf/addons/drawing/hpgl2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from __future__ import annotations
from typing import Iterable, Sequence, no_type_check
import copy
import numpy as np

from ezdxf import colors
from ezdxf.math import Vec2, BoundingBox2d
from ezdxf.math import Vec2, BoundingBox2d, Matrix44
from ezdxf.path import Command

from .type_hints import Color
Expand Down Expand Up @@ -396,6 +397,11 @@ def draw_filled_polygon(
self.add_polyline_encoded(points2d, properties)
self.fill_polygon()

def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
pass # TODO: not implemented

def configure(self, config: Configuration) -> None:
self.lineweight_policy = config.lineweight_policy
if config.min_lineweight:
Expand Down
40 changes: 29 additions & 11 deletions src/ezdxf/addons/drawing/matplotlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ def draw_filled_polygon(self, points: BkPoints2d, properties: BackendProperties)
zorder=self._get_z(),
)

def draw_image(self, image: np.ndarray, transform: Matrix44, properties: BackendProperties) -> None:
def draw_image(
self, image: np.ndarray, transform: Matrix44, properties: BackendProperties
) -> None:
height, width, depth = image.shape
assert depth == 4

Expand All @@ -208,21 +210,37 @@ def draw_image(self, image: np.ndarray, transform: Matrix44, properties: Backend
# afterward. We can use a slight hack which is that the outlines of images are drawn
# as well as the image itself, so we don't have to adjust the data limits at all here
# as the outline will take care of that
handle = AxesImage(self.ax, interpolation='antialiased')
handle = AxesImage(self.ax, interpolation="antialiased")
handle.set_data(np.flip(image, axis=0))
handle.set_zorder(self._get_z())

(
m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44,
m11,
m12,
m13,
m14,
m21,
m22,
m23,
m24,
m31,
m32,
m33,
m34,
m41,
m42,
m43,
m44,
) = transform
matplotlib_transform = Affine2D(matrix=np.array([
[m11, m21, m41],
[m12, m22, m42],
[0, 0, 1],
]))
matplotlib_transform = Affine2D(
matrix=np.array(
[
[m11, m21, m41],
[m12, m22, m42],
[0, 0, 1],
]
)
)
handle.set_transform(matplotlib_transform + self.ax.transData)
self.ax.add_image(handle)

Expand Down
4 changes: 1 addition & 3 deletions src/ezdxf/addons/drawing/mtext_complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,7 @@ def word(self, text: str, ctx: MTextContext) -> tl.ContentCell:
),
)

def fraction(
self, data: tuple[str, str, str], ctx: MTextContext
) -> tl.ContentCell:
def fraction(self, data: tuple[str, str, str], ctx: MTextContext) -> tl.ContentCell:
upr, lwr, type_ = data
if type_:
return tl.Fraction(
Expand Down
4 changes: 3 additions & 1 deletion src/ezdxf/addons/drawing/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,9 @@ def __init__(
self.pdsize = doc.header.get("$PDSIZE", 1.0)
self.pdmode = doc.header.get("$PDMODE", 0)
if doc.filename is not None:
self.document_dir = pathlib.Path(doc.filename.replace('\\', os.path.sep)).parent.resolve()
self.document_dir = pathlib.Path(
doc.filename.replace("\\", os.path.sep)
).parent.resolve()
self._setup_text_styles(doc)
try:
self.units = InsertUnits(doc.header.get("$INSUNITS", 0))
Expand Down
Loading

0 comments on commit 66a6a19

Please sign in to comment.