Skip to content

Colour scales

The colour scales of the force viewer, for every front end that colours tensions and pressures.

The Polyscope viewer (foambryo.viewing), foambryo view --colour-bars and the napari plugin colour with these functions, so one value has one colour in all of them, and a colour bar spans exactly the range the colours were clipped to:

  • tensions: coolwarm (TENSION_COLOUR_MAP: blue for low, light grey for the mean, red for high), over a range centred on the mean of the finite tensions and symmetric around it, reaching the farther of the alpha and 1 - alpha quantiles (tension_colour_range, tension_colour_fractions, tension_colours);
  • pressures: magma reversed and trimmed to its 10-90 % range (PRESSURE_COLOUR_MAP, PRESSURE_COLOUR_MAP_RANGE: no pressure is black or near-white, so none vanishes on a black or a white background), from 0 to the 1 - alpha quantile of the finite pressures over the surface's triangles (surface_pressure_colour_upper, pressure_colour_fractions, pressure_colours);
  • other per-interface quantities (a seed spread, a residual): jet between the alpha and 1 - alpha quantiles (quantile_colour_range, clipped_colour_fractions, quantity_colours);
  • undetermined values (nan): UNDETERMINED_COLOR, green, outside every map; a viewer that can draw outlines draws an undetermined interface as its outline in this green, without fill;
  • crop faces: see-through glass, CROP_FACE_COLOR at CROP_FACE_OPACITY with edges in CROP_FACE_EDGE_COLOR;
  • the backgrounds a viewer offers, by name (BACKGROUND_COLOURS: black, dark grey, light grey, white).

alpha is DEFAULT_CLIP_QUANTILE (0.05) unless given. save_colour_bars writes the bars to an image file and returns the values at their ends (ColourBarRanges). This module needs matplotlib (installed by foambryo[viewing]), but neither Polyscope nor a display. The functions are implemented in foambryo._colour_scales; this module is their public interface.

ColourBarRanges dataclass

ColourBarRanges(tension: tuple[float, float] | None, pressure: tuple[float, float] | None, alpha: float)

The values at the two ends of each colour bar, which are the viewer's clipped ranges.

Attributes:

Name Type Description
tension tuple[float, float] | None

(lowest, highest) of the tension bar (tension_colour_range: centred on the mean tension), or None when no tension is finite.

pressure tuple[float, float] | None

(0, highest) of the pressure bar, or None when no pressure was given or none is finite.

alpha float

the quantile clipped at each end.

clipped_colour_fractions

clipped_colour_fractions(values: NDArray[float64], colour_range: tuple[float, float]) -> NDArray[np.float64]

Where each value falls on a colour scale, in [0, 1]: clipped to the range, then stretched.

Parameters:

Name Type Description Default
values NDArray[float64]

the values to colour (per triangle on the viewer's surface); nan stays nan.

required
colour_range tuple[float, float]

(lowest, highest), from quantile_colour_range or tension_colour_range.

required

Returns:

Type Description
NDArray[float64]

NDArray[np.float64]: (clip(v, lowest, highest) - lowest) / (highest - lowest). A range of zero width

NDArray[float64]

gives nan (and numpy's division warning); tension_colour_fractions handles that case for tensions.

colour_bar_ranges

colour_bar_ranges(mesh: DcelData, dict_tensions: dict[tuple[int, int], float], dict_pressures: dict[int, float] | None = None, alpha: float = DEFAULT_CLIP_QUANTILE) -> ColourBarRanges

The ranges the viewer's surface colours tensions and pressures over, without drawing anything.

Parameters:

Name Type Description Default
mesh DcelData

the mesh the values belong to (its triangles weight the pressure quantile).

required
dict_tensions dict[tuple[int, int], float]

tension per interface, keyed (label_1, label_2) with label_1 < label_2.

required
dict_pressures dict[int, float] | None

pressure per material (0 = medium), or None for no pressure bar.

None
alpha float

quantile clipped at each end (the viewer's alpha). Defaults to 0.05.

DEFAULT_CLIP_QUANTILE

Returns:

Name Type Description
ColourBarRanges ColourBarRanges

the two ranges.

pressure_colour_fractions

pressure_colour_fractions(values: NDArray[float64], upper: float) -> NDArray[np.float64]

Where each pressure falls on the reversed pressure scale: 0 for the top, 1 for 0 and below.

Parameters:

Name Type Description Default
values NDArray[float64]

the pressures to colour; nan stays nan.

required
upper float

the top of the range, from surface_pressure_colour_upper.

required

Returns:

Type Description
NDArray[float64]

NDArray[np.float64]: 1 - clip(v, 0, upper) / upper, the fraction pressure_colours draws

NDArray[float64]

(on the trimmed magma, PRESSURE_COLOUR_MAP_RANGE). Dividing by upper itself, rather than by the

NDArray[float64]

largest clipped value (the same number whenever a value reaches upper, as on the surface), gives one

NDArray[float64]

pressure one colour wherever it is drawn, also on the graph view, whose cells need not reach the

NDArray[float64]

surface's top. With upper 0 (no positive pressure) the fractions are nan.

pressure_colour_upper

pressure_colour_upper(values: NDArray[float64], alpha: float) -> np.float64

The top of the range the viewer colours pressures over: the 1 - alpha quantile.

The bottom is 0: the viewer clips negative pressures to 0.

Parameters:

Name Type Description Default
values NDArray[float64]

pressures; on the viewer's surface, one per triangle (the pressure of the higher-labelled of its two materials). nan values take no part.

required
alpha float

quantile clipped at the top.

required

Returns:

Type Description
float64

np.float64: the 1 - alpha quantile of the finite values.

Raises:

Type Description
IndexError

if no value is finite (numpy's error on an empty quantile).

pressure_colours

pressure_colours(fractions: NDArray[float64]) -> NDArray[np.float64]

RGB colours of pressure fractions (pressure_colour_fractions), as every front end draws them.

A fraction x in [0, 1] is magma(lo + (hi - lo) x), (lo, hi) = PRESSURE_COLOUR_MAP_RANGE (0.1, 0.9): 0 (the top of the range) is dark purple, 1 (a pressure of 0 or below) light orange, and no pressure is black or near-white. nan is UNDETERMINED_COLOR.

quantile_colour_range

quantile_colour_range(values: NDArray[float64], alpha: float) -> tuple[np.float64, np.float64]

The range the viewer colours a per-interface quantity other than a tension over: two quantiles.

The alpha and 1 - alpha quantiles of the finite values.

Used for the per-interface quantities the viewer draws with jet (quantity_colours): a seed spread, a residual. Tensions have their own range, tension_colour_range.

Parameters:

Name Type Description Default
values NDArray[float64]

the values, for tensions one per interface; nan values take no part in the range.

required
alpha float

quantile clipped at each end, in [0, 0.5).

required

Returns:

Name Type Description
tuple tuple[float64, float64]

(lowest, highest), the two quantiles of the finite values.

Raises:

Type Description
IndexError

if no value is finite (numpy's error on an empty quantile).

quantity_colours

quantity_colours(fractions: NDArray[float64]) -> NDArray[np.float64]

RGB colours of another per-interface quantity's fractions (a seed spread, a residual): jet; nan green.

save_colour_bars

save_colour_bars(mesh: DcelData, dict_tensions: dict[tuple[int, int], float], dict_pressures: dict[int, float] | None, path: str | PathLike[str], alpha: float = DEFAULT_CLIP_QUANTILE, *, tension_label: str = 'Surface tension', pressure_label: str = 'Pressure') -> ColourBarRanges

Write the colour bars of the force viewer's surface colours to an image file.

One horizontal bar per quantity, each spanning exactly the range the viewer colours over (colour_bar_ranges), drawn with the viewer's own colour function, with its two end values written under it:

  • surface tension: coolwarm, centred on the mean of the finite tensions (one per interface; light grey), symmetric, its half-width the farther of the alpha and 1 - alpha quantiles from the mean (tension_colour_range); the viewer draws tensions beyond either end in the end's colour; the bar's middle tick is the mean;
  • pressure: magma reversed (light for low) and trimmed to its 10-90 % range, so that neither end is black or near-white, from 0 to the 1 - alpha quantile of the finite pressures over the mesh's triangles, each carrying the pressure of the higher-labelled of its two materials, as on the viewer's surface; negative pressures are drawn as 0.

A green swatch, "undetermined", is added when any value is nan, and a glass swatch, "crop face", when the mesh has crop faces. The viewer's graph view (cell nodes) colours pressures on this same scale (surface_pressure_colour_upper).

Headless: matplotlib's non-interactive canvas, no window, no Polyscope.

Parameters:

Name Type Description Default
mesh DcelData

the mesh the values belong to.

required
dict_tensions dict[tuple[int, int], float]

tension per interface ((label_1, label_2) -> tension, label_1 < label_2).

required
dict_pressures dict[int, float] | None

pressure per material (label -> pressure, 0 = medium), or None for the tension bar only.

required
path str | PathLike[str]

the image file; matplotlib chooses the format from the suffix (.png, .pdf, .svg).

required
alpha float

quantile clipped at each end, the viewer's alpha (default 0.05).

DEFAULT_CLIP_QUANTILE
tension_label str

the tension bar's title (for example with a unit). Keyword-only.

'Surface tension'
pressure_label str

the pressure bar's title. Keyword-only.

'Pressure'

Returns:

Name Type Description
ColourBarRanges ColourBarRanges

the values at the ends of each bar (pressure is None when no pressure bar was

ColourBarRanges

drawn).

Raises:

Type Description
ValueError

if no tension is finite (there is no scale to draw), or matplotlib does not know the file's suffix.

surface_face_pressures

surface_face_pressures(mesh: DcelData, dict_pressures: dict[int, float], *, scattered: bool = False) -> NDArray[np.float64]

The pressure the viewer's surface gives each triangle: that of its higher-labelled material.

Parameters:

Name Type Description Default
mesh DcelData

the mesh.

required
dict_pressures dict[int, float]

pressure per material (0 = medium).

required
scattered bool

the triangles of the scattered view (mesh.f_scattered, each carrying the pressure of the cell it was scattered with), as the viewer draws them with scattered=True. Keyword-only. Defaults to False.

False

Returns:

Type Description
NDArray[float64]

NDArray[np.float64]: one pressure per row of mesh.f (or of mesh.f_scattered).

Raises:

Type Description
KeyError

if a material of the mesh has no pressure in dict_pressures.

surface_pressure_colour_upper

surface_pressure_colour_upper(mesh: DcelData, dict_pressures: dict[int, float], alpha: float = DEFAULT_CLIP_QUANTILE, *, scattered: bool = False) -> np.float64 | None

The top of the viewer's pressure scale, for every view that colours pressures.

The one place the pressure range is computed: the viewer's surface ("Pressures Cells"), its graph view's cell nodes ("Graph Nodes") and the colour bars all take it from here, so one pressure has one colour in all three. It is the 1 - alpha quantile of the finite pressures over the triangles the surface draws (surface_face_pressures); the bottom is 0.

Parameters:

Name Type Description Default
mesh DcelData

the mesh the pressures belong to.

required
dict_pressures dict[int, float]

pressure per material (0 = medium).

required
alpha float

quantile clipped at the top.

DEFAULT_CLIP_QUANTILE
scattered bool

over the scattered view's triangles, as the surface drawn with scattered=True. Keyword-only. Defaults to False.

False

Returns:

Type Description
float64 | None

np.float64 | None: the top of the scale, or None when no pressure is finite.

tension_colour_fractions

tension_colour_fractions(values: NDArray[float64], colour_range: tuple[float, float]) -> NDArray[np.float64]

Where each tension falls on the tension map, in [0, 1] (0.5 is the mean); nan stays nan.

clipped_colour_fractions on tension_colour_range's range; when the range has no width (every tension equal), every finite tension is at the middle, 0.5, rather than undetermined.

tension_colour_range

tension_colour_range(values: NDArray[float64], alpha: float = DEFAULT_CLIP_QUANTILE, *, centre: float | None = None) -> tuple[float, float]

The range the viewers colour tensions over: centred on the mean tension, symmetric around it.

The centre is the mean of the finite tensions, which is mean_tension (1 by default) for every method that meets its gauge exactly (infer_tensions; per junction-graph component, so also over several components). The half-width is the larger of the distances from the centre to the alpha and the 1 - alpha quantiles, so that both quantiles lie within the range and a tension equal to the mean is drawn in the map's middle colour (light grey).

Parameters:

Name Type Description Default
values NDArray[float64]

the tensions, one per interface; nan values take no part.

required
alpha float

quantile at each end, in [0, 0.5) (the viewer's alpha, default 0.05).

DEFAULT_CLIP_QUANTILE
centre float | None

the centre instead of the mean (for example a known mean_tension). Keyword-only.

None

Returns:

Name Type Description
tuple tuple[float, float]

(centre - half, centre + half). With every finite tension equal, (centre, centre).

Raises:

Type Description
ValueError

if no tension is finite.

tension_colours

tension_colours(fractions: NDArray[float64]) -> NDArray[np.float64]

RGB colours of tension fractions (tension_colour_fractions): coolwarm; nan is UNDETERMINED_COLOR.