uadapy.plotting.stippling module

uadapy.plotting.stippling.plot_stipples(distributions, resolution=256, steps=600, tau=0.1, stipple_size=4, stippling_scale=1, seed=55, ranges=None, fig=None, axs=None, distrib_labels=None, distrib_colors=None, colorblind_safe=False, show_plot=False)

Plot multi-class electrostatic halftoning (stippling) from continuous distributions.

This function produces a multi-class stippling of 2D data by implementing the electrostatic halftoning model (charged particles in an attraction field induced by image tone) with the multi-class interaction extension:

  • A tone (or “image charge”) field u_c is built for each class by rasterizing the class PDF onto a common grid; darker tone (1 - u_c) induces stronger attraction.

  • Particles (stipples) repel each other (Coulomb-like repulsion), and are attracted to dark regions (image-induced attraction).

Internally, each distribution is rasterized, the attraction field per class is precomputed, and particles are initialized by importance sampling (1 - u_c). Particle evolution then runs an explicit Euler solver with (i) per-particle step clamping for stability (max displacement per iteration in pixel units) and (ii) an optional simple plateau stop for very small stipple counts. To preserve total “charge neutrality” when the number of particles is changed, forces are rescaled by an internal factor q_scale = 1 / stippling_scale:

  • If you halve the number of particles (stippling_scale=0.5), each particle’s effective charge is doubled, keeping the global balance consistent with the model.

Parameters:
  • distributions (list[Distribution] or Distribution) – A list of uadapy.Distribution objects (or a single one) representing classes.

  • resolution (int, optional) – Side length (pixels) of the square raster grid used to build tone images u_c and their attraction fields. The solver operates in grid coordinates (pixels). Default is 256.

  • steps (int, optional) – Maximum number of Euler iterations for the particle evolution. Default is 600.

  • tau (float, optional) – Artificial time step for the explicit Euler integrator. Together with force magnitudes, this controls per-iteration displacement before clamping. Default is 0.1.

  • stipple_size (float, optional) – Matplotlib scatter size for stipples in the output figure. Default is 4.

  • stippling_scale (float, optional) – Multiplicative scale on the default particle count per class (derived from sum(1 - u_c)). Must be in [0.1, 1.0] in this implementation. Internally the solver rescales forces with q_scale = 1 / stippling_scale to preserve total charge when the count changes. Example: 0.5: half as many stipples; 2x per-particle charge (via force scaling). Default is 1.

  • seed (int, optional) – RNG seed used for particle initialization (importance sampling) and early shaking. Default is 55.

  • ranges (list of tuple or None, optional) – The ranges for the x and y axes as [(x_min, x_max), (y_min, y_max)]. If None, ranges are calculated based on the distributions.

  • fig (matplotlib.figure.Figure or None, optional) – Figure to plot into. If None, a new figure is created.

  • axs (matplotlib.axes.Axes or None, optional) – Axes to plot into. If None, a new axes (or the first axes of fig) is used.

  • distrib_labels (list[str] or None, optional) – Legend labels per class. If None, defaults to “Class i”.

  • distrib_colors (list or None, optional) – List of colors to use for each distribution. If None, Matplotlib Set2 and glasbey colors will be used.

  • colorblind_safe (bool, optional) – If True, the plot will use colors suitable for colorblind individuals. Default is False.

  • show_plot (bool, optional) – If True, display the plot. Default is False.

Returns:

  • matplotlib.figure.Figure – The figure containing the stippling plot.

  • matplotlib.axes.Axes – The axes with drawn stipples.