Source code for noisemaker.constants

"""Constants used in Noisemaker"""

from __future__ import annotations

import json
import os
from enum import Enum, EnumMeta
from typing import TYPE_CHECKING, Any, Type, TypeVar, cast

# Load constants
_SHARE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "share"))
_CONSTANTS_FILE = os.path.join(_SHARE_DIR, "constants.json")

with open(_CONSTANTS_FILE) as f:
    _CONSTANTS = json.load(f)


def _get_enum_members(name: str) -> dict[str, int]:
    return cast(dict[str, int], _CONSTANTS[name])


if TYPE_CHECKING:
    T = TypeVar("T", bound=Enum)

    class DynamicEnumMeta(EnumMeta):
        def __getattr__(cls, name: str) -> Any:
            raise AttributeError(name)


[docs] class DistanceMetricMixin: """ Specify the distance metric used in various operations, such as Voronoi cells, derivatives, and sobel operators. """
[docs] @classmethod def all(cls) -> list[DistanceMetric]: """ Get all distance metrics except none. Returns: List of all non-none distance metrics """ return [m for m in cast(Type["DistanceMetric"], cls) if m.name != "none"]
[docs] @classmethod def absolute_members(cls) -> list[DistanceMetric]: """ Get all distance metrics that require absolute inputs. Returns: List of absolute distance metrics (euclidean, manhattan, chebyshev, octagram) """ return [m for m in cast(Type["DistanceMetric"], cls) if cls.is_absolute(m)]
[docs] @classmethod def is_absolute(cls, member: DistanceMetric) -> bool: """ Check if a distance metric requires absolute inputs. Args: member: Distance metric to check Returns: True if metric requires absolute inputs """ return member.name != "none" and member.value < cast(Type["DistanceMetric"], cls)["triangular"].value
[docs] @classmethod def signed_members(cls) -> list[DistanceMetric]: """ Get all distance metrics that require signed inputs. Returns: List of signed distance metrics (triangular, hexagram, sdf) """ return [m for m in cast(Type["DistanceMetric"], cls) if cls.is_signed(m)]
[docs] @classmethod def is_signed(cls, member: DistanceMetric) -> bool: """ Check if a distance metric requires signed inputs. Args: member: Distance metric to check Returns: True if metric requires signed inputs """ return member.name != "none" and not cls.is_absolute(member)
if TYPE_CHECKING:
[docs] class DistanceMetric(DistanceMetricMixin, Enum, metaclass=DynamicEnumMeta): pass
else: DistanceMetric = Enum("DistanceMetric", _get_enum_members("DistanceMetric"), type=DistanceMetricMixin)
[docs] class InterpolationTypeMixin: """ Specify the spline point count for interpolation operations. """ pass
if TYPE_CHECKING:
[docs] class InterpolationType(InterpolationTypeMixin, Enum, metaclass=DynamicEnumMeta): pass
else: InterpolationType = Enum("InterpolationType", _get_enum_members("InterpolationType"), type=InterpolationTypeMixin)
[docs] class PointDistributionMixin: """ Point cloud distribution, used by Voronoi and DLA """
[docs] @classmethod def grid_members(cls) -> list[PointDistribution]: """ Get all grid-based point distributions. Returns: List of grid-based point distribution types """ return [m for m in cast(Type["PointDistribution"], cls) if cls.is_grid(m)]
[docs] @classmethod def circular_members(cls) -> list[PointDistribution]: """ Get all circular point distributions. Returns: List of circular point distribution types """ return [m for m in cast(Type["PointDistribution"], cls) if cls.is_circular(m)]
[docs] @classmethod def is_grid(cls, member: PointDistribution) -> bool: """ Check if a point distribution is grid-based. Args: member: Point distribution type to check Returns: True if the distribution is grid-based """ enum_cls = cast(Type["PointDistribution"], cls) return bool(member.value >= enum_cls["square"].value and member.value < enum_cls["spiral"].value)
[docs] @classmethod def is_circular(cls, member: PointDistribution) -> bool: """ Check if a point distribution is circular. Args: member: Point distribution type to check Returns: True if the distribution is circular """ return bool(member.value >= cast(Type["PointDistribution"], cls)["circular"].value)
if TYPE_CHECKING:
[docs] class PointDistribution(PointDistributionMixin, Enum, metaclass=DynamicEnumMeta): pass
else: PointDistribution = Enum("PointDistribution", _get_enum_members("PointDistribution"), type=PointDistributionMixin)
[docs] class ValueDistributionMixin: """ Specify the value distribution function for basic noise. .. code-block:: python image = basic(freq, [height, width, channels], distrib=ValueDistribution.simplex) """
[docs] @classmethod def is_noise(cls, member) -> bool: """ Check if a value distribution is noise-based. Args: member: Value distribution type to check Returns: True if the distribution is noise-based """ return bool(member and member.value < 5)
[docs] @classmethod def is_center_distance(cls, member) -> bool: """ Check if a value distribution is center distance-based. Args: member: Value distribution type to check Returns: True if the distribution is center distance-based """ return bool(member and (member.value >= 20) and (member.value < 40))
[docs] @classmethod def is_native_size(cls, member) -> bool: """The noise type is generated at full-size, rather than upsampled.""" return cls.is_center_distance(member)
if TYPE_CHECKING:
[docs] class ValueDistribution(ValueDistributionMixin, Enum, metaclass=DynamicEnumMeta): pass
else: ValueDistribution = Enum("ValueDistribution", _get_enum_members("ValueDistribution"), type=ValueDistributionMixin)
[docs] class ValueMaskMixin: """ """
[docs] @classmethod def conv2d_members(cls) -> list[ValueMask]: """ Get all conv2d-based value masks. Returns: List of conv2d value mask types """ return [m for m in cast(Type["ValueMask"], cls) if cls.is_conv2d(m)]
[docs] @classmethod def is_conv2d(cls, member: ValueMask) -> bool: """ Check if a value mask is conv2d-based. Args: member: Value mask type to check Returns: True if the mask is conv2d-based """ return bool(member.name.startswith("conv2d"))
[docs] @classmethod def grid_members(cls) -> list[ValueMask]: """ Get all grid-based value masks. Returns: List of grid-based value mask types """ return [m for m in cast(Type["ValueMask"], cls) if cls.is_grid(m)]
[docs] @classmethod def is_grid(cls, member: ValueMask) -> bool: """ Check if a value mask is grid-based. Args: member: Value mask type to check Returns: True if the mask is grid-based """ return bool(member.value < cast(Type["ValueMask"], cls)["alphanum_0"].value)
[docs] @classmethod def rgb_members(cls) -> list[ValueMask]: """ Get all RGB value masks. Returns: List of RGB value mask types """ return [m for m in cast(Type["ValueMask"], cls) if cls.is_rgb(m)]
[docs] @classmethod def is_rgb(cls, member: ValueMask) -> bool: """ Check if a value mask is RGB-based. Args: member: Value mask type to check Returns: True if the mask is RGB-based """ enum_cls = cast(Type["ValueMask"], cls) return bool(member.value >= enum_cls["rgb"].value and member.value < enum_cls["sparse"].value)
[docs] @classmethod def nonprocedural_members(cls) -> list[ValueMask]: """ Get all non-procedural value masks. Returns: List of non-procedural value mask types """ return [m for m in cast(Type["ValueMask"], cls) if not cls.is_procedural(m)]
[docs] @classmethod def procedural_members(cls) -> list[ValueMask]: """ Get all procedural value masks. Returns: List of procedural value mask types """ return [m for m in cast(Type["ValueMask"], cls) if cls.is_procedural(m)]
[docs] @classmethod def is_procedural(cls, member: ValueMask) -> bool: """ Check if a value mask is procedural. Args: member: Value mask type to check Returns: True if the mask is procedural """ return bool(member.value >= cast(Type["ValueMask"], cls)["sparse"].value)
[docs] @classmethod def glyph_members(cls) -> list[ValueMask]: """ Get all glyph-based value masks. Returns: List of glyph-based value mask types """ enum_cls = cast(Type["ValueMask"], cls) return [ m for m in enum_cls if (m.value >= enum_cls["invaders"].value and m.value <= enum_cls["tromino"].value) or (m.value >= enum_cls["lcd"].value and m.value <= enum_cls["arecibo_dna"].value) or m.value == enum_cls["emoji"].value or m.value == enum_cls["bank_ocr"].value ]
[docs] @classmethod def is_glyph(cls, member: ValueMask) -> bool: """ Check if a value mask is glyph-based. Args: member: Value mask type to check Returns: True if the mask is glyph-based """ return member in cls.glyph_members()
if TYPE_CHECKING:
[docs] class ValueMask(ValueMaskMixin, Enum, metaclass=DynamicEnumMeta): pass
else: ValueMask = Enum("ValueMask", _get_enum_members("ValueMask"), type=ValueMaskMixin)
[docs] class VoronoiDiagramTypeMixin: """ Specify the artistic rendering function used for Voronoi diagrams. """
[docs] @classmethod def flow_members(cls) -> list[VoronoiDiagramType]: """ Get all flow-based Voronoi diagram types. Returns: List of flow Voronoi diagram types """ enum_cls = cast(Type["VoronoiDiagramType"], cls) return [enum_cls["flow"], enum_cls["color_flow"]]
[docs] @classmethod def is_flow_member(cls, member: VoronoiDiagramType) -> bool: """ Check if a Voronoi diagram type is flow-based. Args: member: Voronoi diagram type to check Returns: True if the diagram type is flow-based """ return member in cls.flow_members()
if TYPE_CHECKING:
[docs] class VoronoiDiagramType(VoronoiDiagramTypeMixin, Enum, metaclass=DynamicEnumMeta): pass
else: VoronoiDiagramType = Enum("VoronoiDiagramType", _get_enum_members("VoronoiDiagramType"), type=VoronoiDiagramTypeMixin)
[docs] class WormBehaviorMixin: """ Specify the type of heading bias for worms to follow. .. code-block:: python image = worms(image, behavior=WormBehavior.unruly) """
[docs] @classmethod def all(cls) -> list[WormBehavior]: """ Get all worm behaviors except none. Returns: List of all non-none worm behaviors """ return [m for m in cast(Type["WormBehavior"], cls) if m.name != "none"]
if TYPE_CHECKING:
[docs] class WormBehavior(WormBehaviorMixin, Enum, metaclass=DynamicEnumMeta): pass
else: WormBehavior = Enum("WormBehavior", _get_enum_members("WormBehavior"), type=WormBehaviorMixin)
[docs] class OctaveBlendingMixin: """Specify the mode for flattening octaves.""" pass
if TYPE_CHECKING:
[docs] class OctaveBlending(OctaveBlendingMixin, Enum, metaclass=DynamicEnumMeta): pass
else: OctaveBlending = Enum("OctaveBlending", _get_enum_members("OctaveBlending"), type=OctaveBlendingMixin)
[docs] class ColorSpaceMixin: """ """
[docs] @classmethod def is_color(cls, m: ColorSpace) -> bool: """ Check if a color space has color channels. Args: m: Color space to check Returns: True if the color space has color channels (not grayscale) """ return bool(m and m.value > 1)
[docs] @classmethod def color_members(cls) -> list[ColorSpace]: """ Get all color spaces with color channels. Returns: List of color space types with color channels """ return [m for m in cast(Type["ColorSpace"], cls) if cls.is_color(m)]
if TYPE_CHECKING:
[docs] class ColorSpace(ColorSpaceMixin, Enum, metaclass=DynamicEnumMeta): pass
else: ColorSpace = Enum("ColorSpace", _get_enum_members("ColorSpace"), type=ColorSpaceMixin)