Skip to content

Commit

Permalink
replace macro-generated Illuminant and Observer with more bespoke han…
Browse files Browse the repository at this point in the history
…dwritten versions; notably, illuminant now forwards to a dyn trait, which enables use with implementations based on lookup table as well as constant value
  • Loading branch information
apparebit committed Sep 3, 2024
1 parent 04b0160 commit e2ddd89
Show file tree
Hide file tree
Showing 4 changed files with 392 additions and 146 deletions.
14 changes: 12 additions & 2 deletions prettypretty/color/spectrum/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,32 @@ from . import std_observer as std_observer


class Illuminant:
"""A standard illuminant."""
"""
A standard illuminant. Note that `at` accepts a nanometer wavelength whereas
`__getitem__` accepts a zero-based index.
"""
def label(self) -> str: ...
def is_empty(self) -> bool: ...
def start(self) -> int: ...
def end(self) -> int: ...
def len(self) -> int: ...
def at(self, wavelength: int) -> float: ...
def __len__(self) -> int: ...
def __getitem__(self, index: int) -> float: ...
def __repr__(self) -> str: ...


class Observer:
"""A standard observer, that is, a color matching function."""
"""
A standard observer, that is, a color matching function. Note that `at`
accepts a nanometer wavelength whereas `__getitem__` accepts a zero-based
index.
"""
def label(self) -> str: ...
def is_empty(self) -> bool: ...
def start(self) -> int: ...
def end(self) -> int: ...
def len(self) -> int: ...
def at(self, wavelength: int) -> list[float]: ...
def __len__(self) -> int: ...
def __getitem__(self, index: int) -> list[float]: ...
Expand All @@ -44,5 +53,6 @@ class SpectrumTraversal:


CIE_ILLUMINANT_D65: Illuminant
CIE_ILLUMINANT_E: Illuminant
CIE_OBSERVER_2DEG_1931: Observer
CIE_OBSERVER_2DEG_2015: Observer
20 changes: 15 additions & 5 deletions prettypretty/viz3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
GamutTraversalStep
)
from prettypretty.color.spectrum import ( # pyright: ignore [reportMissingModuleSource]
CIE_ILLUMINANT_D65, CIE_OBSERVER_2DEG_1931, SpectrumTraversal
CIE_ILLUMINANT_D65, CIE_ILLUMINANT_E, CIE_OBSERVER_2DEG_1931, Illuminant,
SpectrumTraversal
)


Expand All @@ -20,6 +21,11 @@ def create_parser() -> argparse.ArgumentParser:
"as well as Oklrab and store the data in 'visual-gamut-xyz.ply' and "
"'visual-gamut-ok.ply' files"
)
parser.add_argument(
"--illuminant-e",
action="store_true",
help="use CIE standard illuminant E instead of D65"
)
parser.add_argument(
"--gamut", "-g",
help="plot boundary of gamut for 'sRGB', 'P3', or 'Rec2020' color space "
Expand Down Expand Up @@ -445,8 +451,9 @@ def generate(
mesh: bool = False,
alpha: float = 1.0,
sampler: None | Sampler = None,
illuminant: Illuminant = CIE_ILLUMINANT_D65,
) -> None:
traversal = SpectrumTraversal(CIE_ILLUMINANT_D65, CIE_OBSERVER_2DEG_1931)
traversal = SpectrumTraversal(illuminant, CIE_OBSERVER_2DEG_1931)
traversal.set_step_sizes(step_size)

log(f"Traversing visual gamut in {space} with step size {step_size}:")
Expand Down Expand Up @@ -560,28 +567,31 @@ def render() -> None:
log(f"step size {step_size} is not between 1 and 10.")

sampler = Sampler()
illuminant = CIE_ILLUMINANT_E if options.illuminant_e else CIE_ILLUMINANT_D65

generate(
ColorSpace.Xyz,
step_size=step_size,
gamut=gamut,
planar_gamut=options.planar_gamut,
mesh=options.mesh,
step_size=step_size,
darken=options.darken,
alpha=float(options.alpha)
alpha=float(options.alpha),
illuminant=illuminant,
)

log()

generate(
ColorSpace.Oklrab,
step_size=step_size,
gamut=gamut,
planar_gamut=options.planar_gamut,
mesh=options.mesh,
sampler=sampler,
step_size=step_size,
darken=options.darken,
alpha=float(options.alpha),
illuminant=illuminant,
)

log("\nShape of visible gamut (sampled on chroma/hue plane):\n")
Expand Down
59 changes: 31 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ pub fn color(m: &Bound<'_, PyModule>) -> PyResult<()> {
// --------------------------------------------------------------- color.gamut
let modgamut = PyModule::new_bound(m.py(), "gamut")?;
modgamut.add("__package__", modcolor_name)?;
modgamut.add_class::<crate::gamut::GamutTraversal>()?;
modgamut.add_class::<crate::gamut::GamutTraversalStep>()?;
modgamut.add_class::<gamut::GamutTraversal>()?;
modgamut.add_class::<gamut::GamutTraversalStep>()?;
m.add_submodule(&modgamut)?;

// Only change __name__ attribute after submodule has been added.
Expand All @@ -220,18 +220,21 @@ pub fn color(m: &Bound<'_, PyModule>) -> PyResult<()> {
// --------------------------------------------------------------- color.spectrum
let modspectrum = PyModule::new_bound(m.py(), "spectrum")?;
modspectrum.add("__package__", modcolor_name)?;
modspectrum.add("CIE_ILLUMINANT_D65", crate::spectrum::CIE_ILLUMINANT_D65)?;
modspectrum.add(
"CIE_OBSERVER_2DEG_1931",
crate::spectrum::CIE_OBSERVER_2DEG_1931,
"CIE_ILLUMINANT_D65",
spectrum::Illuminant::new(Box::new(spectrum::CIE_ILLUMINANT_D65)
as Box<dyn spectrum::SpectralDistribution<Value = Float> + Send>),
)?;
modspectrum.add(
"CIE_OBSERVER_2DEG_2015",
crate::spectrum::CIE_OBSERVER_2DEG_2015,
"CIE_ILLUMINANT_E",
spectrum::Illuminant::new(Box::new(spectrum::CIE_ILLUMINANT_E)
as Box<dyn spectrum::SpectralDistribution<Value = Float> + Send>),
)?;
modspectrum.add_class::<crate::spectrum::Illuminant>()?;
modspectrum.add_class::<crate::spectrum::Observer>()?;
modspectrum.add_class::<crate::spectrum::SpectrumTraversal>()?;
modspectrum.add("CIE_OBSERVER_2DEG_1931", spectrum::CIE_OBSERVER_2DEG_1931)?;
modspectrum.add("CIE_OBSERVER_2DEG_2015", spectrum::CIE_OBSERVER_2DEG_2015)?;
modspectrum.add_class::<spectrum::Illuminant>()?;
modspectrum.add_class::<spectrum::Observer>()?;
modspectrum.add_class::<spectrum::SpectrumTraversal>()?;
m.add_submodule(&modspectrum)?;

// Only change __name__ attribute after submodule has been added.
Expand All @@ -248,16 +251,16 @@ pub fn color(m: &Bound<'_, PyModule>) -> PyResult<()> {
// --------------------------------------------------------------- color.style
let modstyle = PyModule::new_bound(m.py(), "style")?;
modstyle.add("__package__", modcolor_name)?;
modstyle.add_class::<crate::style::AnsiColor>()?;
modstyle.add_class::<crate::style::Colorant>()?;
modstyle.add_class::<crate::style::EmbeddedRgb>()?;
modstyle.add_class::<crate::style::Fidelity>()?;
modstyle.add_class::<crate::style::GrayGradient>()?;
modstyle.add_class::<crate::style::Layer>()?;
modstyle.add_function(wrap_pyfunction!(crate::style::stylist, m)?)?;
modstyle.add_class::<crate::style::Style>()?;
modstyle.add_class::<crate::style::Stylist>()?;
modstyle.add_class::<crate::style::TrueColor>()?;
modstyle.add_class::<style::AnsiColor>()?;
modstyle.add_class::<style::Colorant>()?;
modstyle.add_class::<style::EmbeddedRgb>()?;
modstyle.add_class::<style::Fidelity>()?;
modstyle.add_class::<style::GrayGradient>()?;
modstyle.add_class::<style::Layer>()?;
modstyle.add_function(wrap_pyfunction!(style::stylist, m)?)?;
modstyle.add_class::<style::Style>()?;
modstyle.add_class::<style::Stylist>()?;
modstyle.add_class::<style::TrueColor>()?;
m.add_submodule(&modstyle)?;

// Only change __name__ attribute after submodule has been added.
Expand All @@ -266,21 +269,21 @@ pub fn color(m: &Bound<'_, PyModule>) -> PyResult<()> {
// -------------------------------------------------------- color.style.format
let modformat = PyModule::new_bound(m.py(), "format")?;
modformat.add("__package__", &modstyle_name)?;
modformat.add_class::<crate::style::format::AllAttributes>()?;
modformat.add_class::<crate::style::format::Attribute>()?;
modformat.add_class::<crate::style::format::AttributeIterator>()?;
modformat.add_class::<crate::style::format::Format>()?;
modformat.add_class::<style::format::AllAttributes>()?;
modformat.add_class::<style::format::Attribute>()?;
modformat.add_class::<style::format::AttributeIterator>()?;
modformat.add_class::<style::format::Format>()?;
modstyle.add_submodule(&modformat)?;

modformat.setattr("__name__", &modformat_name)?;

// --------------------------------------------------------------- color.trans
let modtrans = PyModule::new_bound(m.py(), "trans")?;
modtrans.add("__package__", modcolor_name)?;
modtrans.add_class::<crate::trans::ThemeEntry>()?;
modtrans.add_class::<crate::trans::ThemeEntryIterator>()?;
modtrans.add_class::<crate::trans::Translator>()?;
modtrans.add("VGA_COLORS", crate::trans::VGA_COLORS)?;
modtrans.add_class::<trans::ThemeEntry>()?;
modtrans.add_class::<trans::ThemeEntryIterator>()?;
modtrans.add_class::<trans::Translator>()?;
modtrans.add("VGA_COLORS", trans::VGA_COLORS)?;
m.add_submodule(&modtrans)?;

// Only change __name__ attribute after submodule has been added.
Expand Down
Loading

0 comments on commit e2ddd89

Please sign in to comment.