svg_path_editor.path_shade ========================== .. py:module:: svg_path_editor.path_shade Attributes ---------- .. autoapisummary:: svg_path_editor.path_shade.WEBP svg_path_editor.path_shade.PNG Classes ------- .. autoapisummary:: svg_path_editor.path_shade.ImageFormat svg_path_editor.path_shade.WebpFormat svg_path_editor.path_shade.PngFormat svg_path_editor.path_shade.PathShading Functions --------- .. autoapisummary:: svg_path_editor.path_shade.lambert_from_angle svg_path_editor.path_shade.lambert_shading_base64 svg_path_editor.path_shade.shade_path Module Contents --------------- .. py:function:: lambert_from_angle(normal, *, z_light = 1) Lambertian diffuse intensity from a 2D surface normal. The 2D normal is interpreted in the :math:`xy`-plane, with an implicit :math:`z`-component 1: .. math:: \mathbf{n} = (n_x, n_y, 1). The light position is .. math:: \mathbf{L}_\text{pos} = (0, 1, z_{\text{light}}), and the light direction is .. math:: \hat{\mathbf{L}} = \frac{\mathbf{L}_\text{pos}}{\|\mathbf{L}_\text{pos}\|}. The Lambert intensity is .. math:: I = \max(0, \hat{\mathbf{n}} \cdot \hat{\mathbf{L}}), where :math:`\hat{\mathbf{n}}` is the normalized surface normal. :param normal: 2D surface normal in the :math:`xy`-plane. :param z_light: :math:`z` height of the light source. :return: Lambertian intensity in :math:`[0, 1]`. .. py:class:: ImageFormat Bases: :py:obj:`Protocol` Abstract RGBA image encoder. :ivar media_type: MIME type of the encoded image. :ivar extension: File extension (without dot). .. py:attribute:: media_type :type: ClassVar[str] .. py:attribute:: extension :type: ClassVar[str] .. py:method:: encode(data) Encode an RGBA image. :param data: ``(H, W, 4)`` uint8 array in RGBA order. :return: Encoded image bytes. .. py:class:: WebpFormat Lossless WebP encoder. Uses :func:`imagecodecs.webp_encode(lossless=True)`. .. py:attribute:: media_type :type: ClassVar[str] :value: 'image/webp' .. py:attribute:: extension :type: ClassVar[str] :value: 'webp' .. py:method:: encode(data) .. py:class:: PngFormat PNG encoder. Uses :func:`imagecodecs.png_encode(level=9)`. .. py:attribute:: media_type :type: ClassVar[str] :value: 'image/png' .. py:attribute:: extension :type: ClassVar[str] :value: 'png' .. py:method:: encode(data) .. py:data:: WEBP .. py:data:: PNG .. py:function:: lambert_shading_base64(*, r, phi, locally_convex, resolution, z_light = 1.0, t = 0.5, format = WEBP, seed = None) Render a Lambert–shaded elliptical cone; return encoded bytes and base64 URI. The cone radii are :math:`r = (r_x, r_y)` in image space. For a point :math:`(x, y)` on the grid .. math:: (x, y) \in \left[-\frac{1}{r_x}, \frac{1}{r_x}\right] \times \left[-\frac{1}{r_y}, \frac{1}{r_y}\right], the unnormalized normal is .. math:: \mathbf{n}(x, y) = \left(\frac{x}{r_x^2}, \frac{y}{r_y^2}, 1\right). The Lambert term is .. math:: I(x, y) = \max\left(0, \hat{\mathbf{n}}(x, y)\cdot\hat{\mathbf{L}}\right), where :math:`\hat{\mathbf{L}}` is the unit vector from the light position :math:`(0, s, z_{\text{light}})` with :math:`s = -1` if ``locally_convex`` else :math:`+1`, rotated in the :math:`xy`-plane by :math:`-\varphi` degrees. Grayscale is binary (0 or 255) according to :math:`I > t`. Alpha is a symmetric remap of :math:`I` around :math:`t`: .. math:: \alpha(I) = \begin{cases} \dfrac{I - t}{1 - t}, & I \geq t,\\ \dfrac{t - I}{t}, & I < t. \end{cases} Before 8-bit quantization, uniform noise in :math:`[0, 1]` is added to alpha for dithering. The final RGBA image is encoded with ``format`` and returned both as raw bytes and as a ``data:...;base64,...`` URI. :param r: Ellipse radii in :math:`x` and :math:`y`. :param phi: Rotation in degrees of the light direction in image space (clockwise in screen coordinates). :param locally_convex: If true, the base light position is :math:`(0, -1, z_{\text{light}})`, otherwise :math:`(0, 1, z_{\text{light}})`. :param resolution: Pixels per SVG unit. Image size is :math:`\lceil 2 r_x \, \mathrm{resolution} \rceil \times \lceil 2 r_y \, \mathrm{resolution} \rceil`. :param z_light: :math:`z` height of the light source. :param t: Neutral Lambert intensity in :math:`[0, 1]` (threshold). :param format: Image format to use. :param seed: RNG seed for alpha dithering; ``None`` is non-deterministic. :return: ``(img_bytes, img_data_uri)``; the URI is suitable for SVG ``href``. .. py:class:: PathShading SVG fragments for shaded bevels. :ivar defs_body: Elements for a document-wide ```` (e.g. shared ```` or ```` definitions). :ivar body: Per-path drawing elements (e.g. ````, ````, ````) that reference :attr:`defs_body`. .. py:attribute:: defs_body :type: list[str] .. py:attribute:: body :type: list[str] .. py:function:: shade_path(svg, *, d, resolution, z_light = 1, threshold = 0.5, max_opacity = 1, format = WEBP, shade_offset = 0, clip_offset = 0, seed = None, prec = None) Per-bevel Lambert shading for an SVG path. The path is decomposed into bevel regions via :func:`svg_path_editor.path_offset.bevel_path`. * Flat bevel polygons are shaded analytically with :func:`lambert_from_angle`. * Curved bevel arcs are shaded using a small Lambert RGBA texture from :func:`lambert_shading_base64`, referenced by ```` / ```` and clipped to the arc geometry. For each bevel polygon: * One ```` is emitted with ``fill="white"`` or ``fill="black"`` depending on whether the intensity is above or below ``threshold``. * Opacity is a symmetric remap of the intensity around ``threshold`` in :math:`[0, 1]`, scaled by ``max_opacity``. For each bevel arc: * A Lambert cone texture is generated (or reused from a cache) for ``(r.x, r.y, phi, locally_convex)``. * A base ```` at the origin with size :math:`2 r_x \times 2 r_y` is placed in :attr:`PathShading.defs_body` once per unique key. * For each occurrence, a ```` with the bevel geometry and a ```` of the base image are emitted into :attr:`PathShading.body`, translated so the image origin matches the lower-left corner of the ellipse bounding box, and rotated by ``phi`` around the ellipse center if non-zero. * ```` carries opacity ``max_opacity`` when it is less than 1. To integrate into an SVG, place all ``defs_body`` entries inside a single document-level ```` and insert ``body`` where the path should render. :param svg: Input path to bevel and shade. :param d: Offset distance for :func:`bevel_path`. :param resolution: Pixels per SVG unit for generated textures. :param z_light: :math:`z` height of the light source. :param threshold: Neutral Lambert intensity in :math:`[0, 1]`, shared between flat bevels and textures. :param max_opacity: Global opacity scale in :math:`[0, 1]`. :param format: Texture format, e.g. :data:`WEBP` or :data:`PNG`. :param shade_offset: Starting index for generated shade IDs. :param clip_offset: Starting index for generated clip-path IDs. :param seed: RNG seed for alpha dithering in :func:`lambert_shading_base64`; ``None`` for non-deterministic. :param prec: Precision/geometry mode passed to :func:`bevel_path`. ``"auto"`` and ``"auto-intersections"`` select automatic strategies; ``None`` uses the default. :return: :class:`PathShading` with SVG fragments for defs and body.