Skip to main content

vcti-shader-fringe

The fringe colormap shader feature: the fragment-stage math that turns a value into a colour.

Overview

A fringe plot colours a 3D model by the values at its nodes and elements. Drawing one means mapping each fragment's value to an RGBA colour — and that last step is what vcti-shader-fringe is.

A colormap here is a list of bands. Each band covers a half-open value range [lower, upper) and carries the colours across it. Two ideas fall out of that and keep the lookup small:

  • A band whose two colours are equal is a constant band. There is no separate discrete mode — a ramp between one colour and itself is that colour.
  • Below-range, above-range and no-value are ordinary bands too, appended to cover the rest of the number line, so the lookup has no special case for them.

A colormap also carries fallback colours, for the ways a value can have no band to come from: it is NaN, no band covers it, or the band that does is misconfigured. Each points at a different fix, so each gets its own colour.

There are two variants, and the data chooses which. A continuous result — a displacement magnitude, a von Mises stress — is a float, interpolates across a band, and uses Colormap. A discrete category — a material id, a part number — is an exact integer, takes one colour per band, and uses DiscreteColormap. Colouring an id through the float path works until it passes 2²⁴, where neighbouring ids collapse onto the same number and take the same colour; the integer path is exact to 2³¹.

Around that math the package declares two things — the specs saying what the math needs supplied, and the ShaderDefinition saying what the feature is — and ships the same lookup written in Python. Nothing here compiles or runs a shader; a build step does that, using what this package declares.

The Python version is there for two reasons. The tests run it and the compiled shader over the same values and check the colours come out identical — that is how the shader is verified. And whatever draws the legend can call it directly, so the colour blocks in the legend always match the colours on the model.

Installation

pip install vcti-shader-fringe

Requires Python 3.12, 3.13, or 3.14, matching vcti-shader-base, and so does the test extra. Only the gl extra — the GL binding the shader tests need to execute anything — is narrower in practice: on 3.14 it builds moderngl's glcontext from source for want of a cp314 wheel, so those tests are run on 3.12 or 3.13.

In requirements.txt

vcti-shader-fringe>=1.0.0

In pyproject.toml dependencies

dependencies = [
    "vcti-shader-fringe>=1.0.0",
]

Quick Start

What the feature is

from vcti.shader.fringe import DEFINITION, SLANG_DIR, fragment_uniforms

DEFINITION.id            # 'fringe'
DEFINITION.role          # StageRole.FRAGMENT
DEFINITION.slang_modules # ('colormap.slang',) — a shader does `import colormap;`
SLANG_DIR                # pass to the compiler as an import search path

[u.name for u in fragment_uniforms()]
# ['u_bandBounds', 'u_numBands', 'u_lowerColors', 'u_upperColors',
#  'u_interpModes', 'u_interpSteps', 'u_nanColor']

A continuous colormap

linear_bands() turns bounds and a palette into ramped bands; with_edge_bands() frames them so the list covers the whole number line:

from vcti.shader.fringe import Colormap, linear_bands, with_edge_bands

PALETTE = [(0.0, 0.26, 0.62, 1.0), (0.65, 0.84, 0.85, 1.0), (0.6, 0.0, 0.0, 1.0)]
GREY = (0.83, 0.83, 0.83, 1.0)

colormap = Colormap(
    with_edge_bands(
        linear_bands([0.0, 1.0, 2.0], PALETTE),   # one colour per bound
        below_color=PALETTE[0],
        above_color=PALETTE[-1],
        no_value_color=GREY,
        no_value_lower=1e30,
    )
)
colormap.validate()   # raises ValueError on a gap, overlap, or bad log bound

constant_bands() gives banded contours instead — one colour per band rather than per bound. A constant band is simply one whose two colours are equal, so there is no separate mode to select.

A discrete colormap

from vcti.shader.fringe import DiscreteBand, DiscreteColormap, category_bands

materials = DiscreteColormap(category_bands([STEEL, ALUMINIUM, COPPER]))

by_id = DiscreteColormap((                 # raw solver ids, in ranges
    DiscreteBand(1_000_000, 2_000_000, STEEL),
    DiscreteBand(2_000_000, 3_000_000, ALUMINIUM),
))

Looking up a colour

fringe_color() is the Python version of the shipped Slang lookup:

from vcti.shader.fringe import fringe_color

fringe_color(5.0, colormap)             # halfway through the linear band
fringe_color(31.6, colormap)            # the geometric midpoint of the log band
fringe_color(-1.0, colormap)            # below the range -> BLUE
fringe_color(3.402823466e38, colormap)  # the no-value sentinel -> GREY
fringe_color(float("nan"), colormap)    # not a number -> the NaN colour

Handing it to the GPU

colormap.uniforms()
# {'u_bandBounds': [(-inf, 0.0), (0.0, 10.0), ...], 'u_numBands': 5, ...}

Arrays are padded to MAX_BANDS; the shader reads only the first u_numBands.


Key API

Name What it is
DEFINITION the ShaderDefinition the feature declares itself with
SLANG_DIR the installed slang/ directory — an import search path
fragment_uniforms(kind) the uniforms a shader declares, per variant
fragment_inputs(kind) the vertex attribute the integer variant reads
fragment_outputs() the fragColor output the feature writes
ValueType FLOAT or INT — which variant a shader is built for
Band one half-open range, its two colours, and its interpolation mode
Colormap a band list, three fallback colours, and an optional step count
DiscreteBand one half-open range of integer categories and its colour
DiscreteColormap a band list and the colour for a category none holds
Colormap.validate() raises on a gap, an overlap, or a log band reaching zero
Colormap.uniforms() the {uniform: value} mapping, padded to MAX_BANDS
linear_bands() bounds + one colour each → ramped bands
constant_bands() bounds + one colour per band → flat bands
category_bands() one band per consecutive integer category
with_edge_bands() frames authored bands with below/above/no-value
fringe_color(value, colormap) the colour a fragment takes — same result as the shader
fringe_color_int(value, colormap) the same, for integer categories
band_color(), band_parameter() the same, for a single band already in hand
InterpMode LINEAR or LOG
INTERP_MODES the same as a name-to-number map, as published on the spec
MAX_BANDS band-array capacity, shared with the Slang module

colormap.slang publishes applyFringe, applyFringeInt and fringeBandColor. All take every value as an argument — nothing in the module reads a uniform, so a shipped shader and the test probes run the same code from different sources.


Dependencies

  • vcti-shader-base — the ShaderDefinition record and the spec types, itself zero-dependency.

Nothing else at runtime. vcti-shader-compiler and numpy are test-only: the tests run the shader on a GPU and compare it against the Python version, but declaring the feature needs neither.


Documentation

If you want to… Read
Get started using the package Quick Start above
Build the colormaps a viewer actually ships docs/patterns.md
Understand the colour model and the decisions behind it docs/design.md
Navigate or modify the source docs/source-guide.md

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vcti_shader_fringe-1.0.1.tar.gz (31.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vcti_shader_fringe-1.0.1-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file vcti_shader_fringe-1.0.1.tar.gz.

File metadata

  • Download URL: vcti_shader_fringe-1.0.1.tar.gz
  • Upload date:
  • Size: 31.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for vcti_shader_fringe-1.0.1.tar.gz
Algorithm Hash digest
SHA256 faa7e37fa2623adab56d090b9dca7bd90ba88a3f51d6d311ddc93aaed94b104f
MD5 97b5a3e20e6f51d5e91bbe1ce458c16c
BLAKE2b-256 9aea6aa786f4014afb7fa1bfca5e61a33dbbc17fd5f04528213adf16e6d2cf68

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_shader_fringe-1.0.1.tar.gz:

Publisher: release.yml on vcollab/vcti-python-shader-fringe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vcti_shader_fringe-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for vcti_shader_fringe-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 36efcb0b8da0f18418899291a9818c176591b07280453f6ee7bc7a154a5761ca
MD5 2d74a0b93fd9dbccecaebe780db32bf3
BLAKE2b-256 7b318b86877c97c9dd8cbb12c0ccd5045eb171922479a1767f434a970ddad4ec

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_shader_fringe-1.0.1-py3-none-any.whl:

Publisher: release.yml on vcollab/vcti-python-shader-fringe

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page