Source code for opengl.sanitize

#
##
##  SPDX-FileCopyrightText: © 2007-2024 Benedict Verhegghe <bverheg@gmail.com>
##  SPDX-License-Identifier: GPL-3.0-or-later
##
##  This file is part of pyFormex 3.5  (Thu Feb  8 19:11:13 CET 2024)
##  pyFormex is a tool for generating, manipulating and transforming 3D
##  geometrical models by sequences of mathematical operations.
##  Home page: https://pyformex.org
##  Project page: https://savannah.nongnu.org/projects/pyformex/
##  Development: https://gitlab.com/bverheg/pyformex
##  Distributed under the GNU General Public License version 3 or later.
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see http://www.gnu.org/licenses/.
##
"""Sanitize data before rendering.

The pyFormex drawing functions were designed to require a minimal knowledge
of OpenGL and rendering principles in general. They allow the user to specify
rendering attributes in a simple, user-friendly, sometimes even sloppy way.
This module contains some functions to sanitize the user input into the more
strict attributes required by the OpenGl rendering engine.

These functions are generally not intended for direct use by the user, but
for use in the opengl rendering functions.
"""
import numpy as np

import pyformex as pf
from pyformex import arraytools as at

### Sanitize settings ###############################################

[docs]def saneFloat(value): """Return a float value or None. If value can be converted to float, the float is returned, else None. """ try: value = float(value) except Exception: value = None return value
[docs]def saneLineStipple(stipple): """Return a sane line stipple tuple. A line stipple tuple is a tuple (factor,pattern) where pattern defines which pixels are on or off (maximum 16 bits), factor is a multiplier for each bit. """ try: stipple = [int(i) for i in stipple] except Exception: stipple = None return stipple
[docs]def saneColor(color): """Return a sane color array or None. This is like :func:`pyformex.color.colorArray` but catches the InvalidColor exception and returns the default color black instead. Examples -------- >>> saneColor('red') array([1., 0., 0.]) >>> saneColor(['red', 'green', 'blue']) array([[1. , 0. , 0. ], [0. , 1. , 0. ], [0.1, 0.1, 0.8]]) >>> saneColor(None) array([0., 0., 0.]) >>> saneColor(1) array([0., 0., 0.]) >>> saneColor('appelblauwzeegroen') array([0., 0., 0.]) """ try: return pf.color.colorArray(color) except pf.color.InvalidColor as e: if pf.verbosity(3): print("saneColor:", e) raise e return pf.color.colorArray(pf.color.black)
[docs]def saneColorSet(color=None, colormap=None, *, shape=(1,)): """Return a sane set of colors. A sane set of colors is one that guarantees correct use by the draw functions. This means either - no color (None) - a single color - at least as many colors as the shape argument specifies - a color index with at least as many colors as the shape argument specifies, and a colormap with enough colors to satisfy the index. Parameters ---------- color: colors_like One or more valid color designations. This can be anything that is valid input for saneColor. These can be the real colors (if float) or indices into a colormap (if int). colormap: list A list of colors to be used as the color map if colors are indices instead of the colors themselves. shape: tuple A tuple specifying the shape of the color array to return. The tuple should have 1 or 2 dimensions: (nelems,) or (nelems, nplex). The tuple should not contain the number of color components. Returns ------- color: array Either a float32 array with shape ``shape + (3,)`` holding 3 RGB color components, or an int32 array with shape ``shape``, holding indices into the colormap. colormap: None | array None if color returns real colors, or a float32 array of shape (ncolors, 3), where ncolors is guaranteed to be larger than the maximum index returned in color. Examples -------- No color always remains no color >>> saneColorSet(None, shape=(2,)) (None, None) Single color remains single color >>> saneColorSet('red', shape=(2,)) (array([1., 0., 0.]), None) >>> saneColorSet(pf.color.red, shape=(3,2)) (array([1., 0., 0.]), None) >>> saneColorSet(1, pf.color.palette, shape=(2,)) (array([1., 0., 0.]), None) >>> saneColorSet(1, pf.color.palette, shape=(3,2)) (array([1., 0., 0.]), None) A set (tuple, list or array) of colors is expanded to match shape >>> saneColorSet(('red',), shape=(2,)) (array([[1., 0., 0.], [1., 0., 0.]]), None) >>> saneColorSet((pf.color.red, pf.color.green), shape=(3,2)) (array([[[1., 0., 0.], [1., 0., 0.]], <BLANKLINE> [[0., 1., 0.], [0., 1., 0.]], <BLANKLINE> [[1., 0., 0.], [1., 0., 0.]]]), None) >>> saneColorSet((1,), pf.color.palette, shape=(2,)) (array([1., 0., 0.]), None) >>> saneColorSet((1, 2), pf.color.palette, shape=(3,2)) (array([[1, 1], [2, 2], [1, 1]]), array([[0.4, 0.4, 0.4], [1. , 0. , 0. ], [0. , 1. , 0. ]])) """ if color is None: return None, None if at.isInt(shape): # make sure we get a tuple shape = (shape, ) color = np.asarray(color) if pf.debugon(pf.DEBUG.DRAW): print(f"SANECOLORSET: color {color.shape}, {color.dtype} to shape {shape}") if color.dtype.kind in 'iu': # color index color = color.astype(np.int32) if colormap is None: # we need a color map: use the default colormap = pf.canvas.settings.colormap else: colormap = saneColor(colormap).reshape(-1,3) ncolors = color.max() + 1 colormap = at.resizeArray(colormap, (ncolors, colormap.shape[1])) if color.ndim == 0: # a scalar color index is converted to a single color color, colormap = colormap[color], None elif color.size == 1: # a single color index is also converted to a single color color, colormap = colormap[color.flat[0]], None else: while color.ndim < 2: color = at.addAxis(color, 1) color = at.resizeArray(color, shape) else: # direct color color = saneColor(color) colormap = None if color.ndim > 1: shape += (color.shape[-1],) while color.ndim < len(shape): color = at.addAxis(color, 1) color = at.resizeArray(color, shape) if pf.debugon(pf.DEBUG.DRAW): print(f"SANECOLORSET RESULT: {color.shape}") return color, colormap
### End