stippler 0.2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stippler-0.2.0.0/LICENSE.txt +26 -0
- stippler-0.2.0.0/PKG-INFO +210 -0
- stippler-0.2.0.0/README.md +185 -0
- stippler-0.2.0.0/pyproject.toml +46 -0
- stippler-0.2.0.0/setup.cfg +4 -0
- stippler-0.2.0.0/src/stippler/__init__.py +54 -0
- stippler-0.2.0.0/src/stippler/classic.py +278 -0
- stippler-0.2.0.0/src/stippler/illustration.py +311 -0
- stippler-0.2.0.0/src/stippler/pipeline.py +679 -0
- stippler-0.2.0.0/src/stippler/voronoi.py +253 -0
- stippler-0.2.0.0/src/stippler.egg-info/PKG-INFO +210 -0
- stippler-0.2.0.0/src/stippler.egg-info/SOURCES.txt +14 -0
- stippler-0.2.0.0/src/stippler.egg-info/dependency_links.txt +1 -0
- stippler-0.2.0.0/src/stippler.egg-info/entry_points.txt +2 -0
- stippler-0.2.0.0/src/stippler.egg-info/requires.txt +5 -0
- stippler-0.2.0.0/src/stippler.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Copyright (c) 2017 Nicolas P. Rougier
|
|
2
|
+
Copyright (c) 2026 Max Benjamin Eschenbach
|
|
3
|
+
(Python 3.9 port, hand-drawn pipeline extensions, and Grasshopper integration)
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions
|
|
8
|
+
are met:
|
|
9
|
+
1. Redistributions of source code must retain the above copyright
|
|
10
|
+
notice, this list of conditions and the following disclaimer.
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
12
|
+
notice, this list of conditions and the following disclaimer in the
|
|
13
|
+
documentation and/or other materials provided with the distribution.
|
|
14
|
+
3. The name of the author may not be used to endorse or promote products
|
|
15
|
+
derived from this software without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
18
|
+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
19
|
+
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
20
|
+
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
21
|
+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
22
|
+
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
23
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
24
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
25
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
26
|
+
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stippler
|
|
3
|
+
Version: 0.2.0.0
|
|
4
|
+
Summary: Hand-drawn weighted Voronoi stippling pipeline (Python 3.9.10 / Rhino CPython compatible)
|
|
5
|
+
Author: Nicolas P. Rougier
|
|
6
|
+
Maintainer: Max Benjamin Eschenbach
|
|
7
|
+
License-Expression: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/fstwn/stippler
|
|
9
|
+
Project-URL: Repository, https://github.com/fstwn/stippler
|
|
10
|
+
Project-URL: Documentation, https://github.com/fstwn/stippler#readme
|
|
11
|
+
Project-URL: Bug Tracker, https://github.com/fstwn/stippler/issues
|
|
12
|
+
Keywords: stippling,voronoi,halftone,rhino,grasshopper,npar
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE.txt
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: scipy
|
|
21
|
+
Requires-Dist: Pillow
|
|
22
|
+
Requires-Dist: tqdm
|
|
23
|
+
Requires-Dist: matplotlib
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Rhino Grasshopper-Compatible Stippling Processor
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
This is a replication of the following article:
|
|
34
|
+
|
|
35
|
+
*Weighted Voronoi Stippling*, Adrian Secord. In: Proceedings of the 2nd
|
|
36
|
+
International Symposium on Non-photorealistic Animation and
|
|
37
|
+
Rendering. NPAR ’02. ACM, 2002, pp. 37– 43.
|
|
38
|
+
|
|
39
|
+
where the author introduced a *techniques for generating stipple drawings from
|
|
40
|
+
grayscale images using weighted centroidal Voronoi diagrams* as in *the
|
|
41
|
+
traditional artistic technique of stippling that places small dots of ink onto
|
|
42
|
+
paper such that their density give the impression of tone*.
|
|
43
|
+
|
|
44
|
+
## Authors and credits
|
|
45
|
+
|
|
46
|
+
* **Original replication** — Nicolas P. Rougier (BSD license, 2017), replicating
|
|
47
|
+
Adrian Secord, *Weighted Voronoi Stippling*, NPAR 2002.
|
|
48
|
+
* **Python 3.9.10 port, revised hand-drawn stippling pipeline, Lu et al.
|
|
49
|
+
scientific-illustration extensions, and Grasshopper / Rhino integration** —
|
|
50
|
+
Max Benjamin Eschenbach.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## Pre-requisites
|
|
54
|
+
|
|
55
|
+
The original replication was written and tested on OSX 10.12 (Sierra) using
|
|
56
|
+
Python 3.6, Numpy 1.12, Scipy 0.18, Matplotlib 2.0 and tqdm 4.10.
|
|
57
|
+
|
|
58
|
+
This revision ports and extends that code for **Python 3.9.10**, matching the
|
|
59
|
+
Rhino 8 / Grasshopper CPython runtime. The port replaces the removed
|
|
60
|
+
`scipy.misc.imread` with a small Pillow-based loader; the compute core remains
|
|
61
|
+
plain numpy / `scipy.spatial`. It has been verified with:
|
|
62
|
+
|
|
63
|
+
* Python 3.9.10
|
|
64
|
+
* Numpy 2.0
|
|
65
|
+
* Scipy 1.13
|
|
66
|
+
* Pillow 11.3
|
|
67
|
+
* Matplotlib 3.9
|
|
68
|
+
* tqdm 4.x
|
|
69
|
+
|
|
70
|
+
Create the environment with conda:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
conda create -n stippler -c conda-forge python=3.9.10 numpy scipy matplotlib pillow tqdm
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Original data is in the data directory and you can also obtain it from
|
|
77
|
+
[Adrian Secord homepage](http://cs.nyu.edu/~ajsecord/npar2002/StipplingOriginals.zip).
|
|
78
|
+
|
|
79
|
+
## Installation
|
|
80
|
+
|
|
81
|
+
Install from PyPI:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
pip install stippler
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Or from a local clone (editable install for development):
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
pip install -e .
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Or directly from GitHub:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
pip install "git+https://github.com/fstwn/stippler.git@main"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This installs the dependencies (numpy, scipy, Pillow, tqdm, matplotlib), the
|
|
100
|
+
importable package `stippler`, and a `stippler` console command.
|
|
101
|
+
|
|
102
|
+
## Usage (classic stippler)
|
|
103
|
+
|
|
104
|
+
The original replication CLI is preserved as the `stippler.classic` module
|
|
105
|
+
(run it with `python -m stippler.classic ...`):
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
usage: python -m stippler.classic
|
|
109
|
+
[--n_iter n] [--n_point n] [--save] [--force]
|
|
110
|
+
[--pointsize min,max] [--figsize w,h]
|
|
111
|
+
[--display] [--interactive] file
|
|
112
|
+
|
|
113
|
+
Weighted Vororonoi Stippler
|
|
114
|
+
|
|
115
|
+
positional arguments:
|
|
116
|
+
file Density image filename
|
|
117
|
+
|
|
118
|
+
optional arguments:
|
|
119
|
+
-h, --help show this help message and exit
|
|
120
|
+
--n_iter n Maximum number of iterations
|
|
121
|
+
--n_point n Number of points
|
|
122
|
+
--pointsize (min,max) (min,max)
|
|
123
|
+
Point mix/max size for final display
|
|
124
|
+
--figsize w,h Figure size
|
|
125
|
+
--force Force recomputation
|
|
126
|
+
--save Save computed points
|
|
127
|
+
--display Display final result
|
|
128
|
+
--interactive Display intermediate results (slower)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Hand-drawn pipeline (`stippler` command)
|
|
132
|
+
|
|
133
|
+
The `stippler` console command (module `stippler.pipeline`) wraps the relaxation
|
|
134
|
+
in a grayscale-image → stipple-output pipeline and adds three controls that make
|
|
135
|
+
the result read as *hand drawn* rather than machine generated. The compute core
|
|
136
|
+
depends only on numpy, scipy and Pillow (Rhino-friendly); matplotlib is used
|
|
137
|
+
only for preview rendering.
|
|
138
|
+
|
|
139
|
+
1. **Early-stopped relaxation** — fewer Lloyd iterations means the points never
|
|
140
|
+
settle into the regular hexagonal lattice, so spacing stays organically
|
|
141
|
+
uneven. This is the single biggest dial.
|
|
142
|
+
* `--n_iter n` — hard cap on iterations (lower = looser).
|
|
143
|
+
* `--epsilon d` — optional: stop when mean point movement drops below `d`.
|
|
144
|
+
|
|
145
|
+
To control **contrast** (denser blacks / cleaner whites), use `--gamma g`. It
|
|
146
|
+
applies a power curve `d ** g` to the density that drives point placement,
|
|
147
|
+
relaxation and dot size, so `g > 1` (e.g. `2.0`–`2.5`) concentrates the same
|
|
148
|
+
`n_point` dots into the dark areas and thins the light ones; `g < 1` flattens
|
|
149
|
+
the tonal range; `g = 1` is the original linear mapping. (`--threshold` is a
|
|
150
|
+
blunter, hard cutoff that forces lighter greys to pure white.)
|
|
151
|
+
2. **Varying dot size** — a min/max radius spread plus per-dot random jitter
|
|
152
|
+
breaks the constant-radius "machine stipple" giveaway.
|
|
153
|
+
* `--pointsize min max` — radius range (density drives the base size).
|
|
154
|
+
* `--size_jitter f` — per-dot multiplicative radius noise, e.g. `0.15`.
|
|
155
|
+
3. **Imperfect placement & edges** — Gaussian positional jitter plus wobbly,
|
|
156
|
+
non-circular dot outlines.
|
|
157
|
+
* `--position_jitter s` — Gaussian noise std on final positions.
|
|
158
|
+
* `--edge_noise f` — dot-edge wobble as a fraction of radius, e.g. `0.08`.
|
|
159
|
+
* `--edge_segments n` — vertices per dot outline.
|
|
160
|
+
|
|
161
|
+
Output format follows the `--out` extension (`.png`, `.pdf`, `.svg`).
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
stippler data/boots.jpg --n_point 12000 --n_iter 8 \
|
|
165
|
+
--pointsize 0.8 3.0 --size_jitter 0.2 --position_jitter 0.5 \
|
|
166
|
+
--edge_noise 0.09 --seed 3 --out data/boots-stipple.png
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The pipeline can also be imported as a library: `stippler.stipple(...)` returns
|
|
170
|
+
a `StippleResult` carrying `points`, `radii` and per-dot `polygons` (handy for
|
|
171
|
+
feeding geometry into Rhino later), which `render_matplotlib`, `render_svg` and
|
|
172
|
+
`save_points` consume.
|
|
173
|
+
|
|
174
|
+
## Scientific-illustration pipeline (Lu et al.)
|
|
175
|
+
|
|
176
|
+
The hand-drawn pipeline is further extended with optional controls adapted from
|
|
177
|
+
Lu et al., *Non-Photorealistic Volume Rendering Using Stippling Techniques*
|
|
178
|
+
([`resources/vis_stipple.pdf`](resources/vis_stipple.pdf)). Each feature is
|
|
179
|
+
independently toggled via `IllustrationParams` (library) or the CLI
|
|
180
|
+
`illustration` argument group (`stippler --help`):
|
|
181
|
+
|
|
182
|
+
* **Boundary** — boost stipple density on high-gradient edges.
|
|
183
|
+
* **Silhouette density** — concentrate dots on view-facing silhouette regions
|
|
184
|
+
(uses an optional normal map).
|
|
185
|
+
* **Interior** — sparse stipples in flat, low-gradient areas.
|
|
186
|
+
* **Lighting** — modulate density from inferred or supplied normals.
|
|
187
|
+
* **Depth attenuation** — thin stipples in far regions (requires a depth map).
|
|
188
|
+
* **Gradient size** — scale dot radius by local gradient magnitude.
|
|
189
|
+
* **Silhouette curves** — extract and draw feature-line strokes over the dots.
|
|
190
|
+
|
|
191
|
+
Normal and depth maps should be aligned with the beauty-pass image. In Rhino,
|
|
192
|
+
the Grasshopper component can capture these automatically when the options that
|
|
193
|
+
need them are enabled.
|
|
194
|
+
|
|
195
|
+
## Grasshopper / Rhino integration
|
|
196
|
+
|
|
197
|
+
Max Benjamin Eschenbach integrated the revised pipeline into Grasshopper for
|
|
198
|
+
Rhino 8 CPython 3.9.10:
|
|
199
|
+
|
|
200
|
+
* **`grasshopper_userobjects/STIPPLER_StippledViewCapture.ghuser`** — drop-in
|
|
201
|
+
user object that captures the active viewport and writes a `_stippled` output
|
|
202
|
+
(PNG, SVG, or PDF), including hand-drawn controls and the Lu et al. options
|
|
203
|
+
above.
|
|
204
|
+
* **`grasshopper_userobjects_src/`** — Python 3 script source and input/output
|
|
205
|
+
reference. Re-export the user object from Grasshopper after editing the script
|
|
206
|
+
(right-click → **Save User Object…**).
|
|
207
|
+
|
|
208
|
+
Install the `stippler` package into the Rhino CPython environment the component
|
|
209
|
+
uses (see Installation above), then paste or sync the script into a GH Python 3
|
|
210
|
+
component.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
|
|
2
|
+
# Rhino Grasshopper-Compatible Stippling Processor
|
|
3
|
+
|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
This is a replication of the following article:
|
|
9
|
+
|
|
10
|
+
*Weighted Voronoi Stippling*, Adrian Secord. In: Proceedings of the 2nd
|
|
11
|
+
International Symposium on Non-photorealistic Animation and
|
|
12
|
+
Rendering. NPAR ’02. ACM, 2002, pp. 37– 43.
|
|
13
|
+
|
|
14
|
+
where the author introduced a *techniques for generating stipple drawings from
|
|
15
|
+
grayscale images using weighted centroidal Voronoi diagrams* as in *the
|
|
16
|
+
traditional artistic technique of stippling that places small dots of ink onto
|
|
17
|
+
paper such that their density give the impression of tone*.
|
|
18
|
+
|
|
19
|
+
## Authors and credits
|
|
20
|
+
|
|
21
|
+
* **Original replication** — Nicolas P. Rougier (BSD license, 2017), replicating
|
|
22
|
+
Adrian Secord, *Weighted Voronoi Stippling*, NPAR 2002.
|
|
23
|
+
* **Python 3.9.10 port, revised hand-drawn stippling pipeline, Lu et al.
|
|
24
|
+
scientific-illustration extensions, and Grasshopper / Rhino integration** —
|
|
25
|
+
Max Benjamin Eschenbach.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Pre-requisites
|
|
29
|
+
|
|
30
|
+
The original replication was written and tested on OSX 10.12 (Sierra) using
|
|
31
|
+
Python 3.6, Numpy 1.12, Scipy 0.18, Matplotlib 2.0 and tqdm 4.10.
|
|
32
|
+
|
|
33
|
+
This revision ports and extends that code for **Python 3.9.10**, matching the
|
|
34
|
+
Rhino 8 / Grasshopper CPython runtime. The port replaces the removed
|
|
35
|
+
`scipy.misc.imread` with a small Pillow-based loader; the compute core remains
|
|
36
|
+
plain numpy / `scipy.spatial`. It has been verified with:
|
|
37
|
+
|
|
38
|
+
* Python 3.9.10
|
|
39
|
+
* Numpy 2.0
|
|
40
|
+
* Scipy 1.13
|
|
41
|
+
* Pillow 11.3
|
|
42
|
+
* Matplotlib 3.9
|
|
43
|
+
* tqdm 4.x
|
|
44
|
+
|
|
45
|
+
Create the environment with conda:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
conda create -n stippler -c conda-forge python=3.9.10 numpy scipy matplotlib pillow tqdm
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Original data is in the data directory and you can also obtain it from
|
|
52
|
+
[Adrian Secord homepage](http://cs.nyu.edu/~ajsecord/npar2002/StipplingOriginals.zip).
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
Install from PyPI:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
pip install stippler
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Or from a local clone (editable install for development):
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
pip install -e .
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or directly from GitHub:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pip install "git+https://github.com/fstwn/stippler.git@main"
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
This installs the dependencies (numpy, scipy, Pillow, tqdm, matplotlib), the
|
|
75
|
+
importable package `stippler`, and a `stippler` console command.
|
|
76
|
+
|
|
77
|
+
## Usage (classic stippler)
|
|
78
|
+
|
|
79
|
+
The original replication CLI is preserved as the `stippler.classic` module
|
|
80
|
+
(run it with `python -m stippler.classic ...`):
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
usage: python -m stippler.classic
|
|
84
|
+
[--n_iter n] [--n_point n] [--save] [--force]
|
|
85
|
+
[--pointsize min,max] [--figsize w,h]
|
|
86
|
+
[--display] [--interactive] file
|
|
87
|
+
|
|
88
|
+
Weighted Vororonoi Stippler
|
|
89
|
+
|
|
90
|
+
positional arguments:
|
|
91
|
+
file Density image filename
|
|
92
|
+
|
|
93
|
+
optional arguments:
|
|
94
|
+
-h, --help show this help message and exit
|
|
95
|
+
--n_iter n Maximum number of iterations
|
|
96
|
+
--n_point n Number of points
|
|
97
|
+
--pointsize (min,max) (min,max)
|
|
98
|
+
Point mix/max size for final display
|
|
99
|
+
--figsize w,h Figure size
|
|
100
|
+
--force Force recomputation
|
|
101
|
+
--save Save computed points
|
|
102
|
+
--display Display final result
|
|
103
|
+
--interactive Display intermediate results (slower)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Hand-drawn pipeline (`stippler` command)
|
|
107
|
+
|
|
108
|
+
The `stippler` console command (module `stippler.pipeline`) wraps the relaxation
|
|
109
|
+
in a grayscale-image → stipple-output pipeline and adds three controls that make
|
|
110
|
+
the result read as *hand drawn* rather than machine generated. The compute core
|
|
111
|
+
depends only on numpy, scipy and Pillow (Rhino-friendly); matplotlib is used
|
|
112
|
+
only for preview rendering.
|
|
113
|
+
|
|
114
|
+
1. **Early-stopped relaxation** — fewer Lloyd iterations means the points never
|
|
115
|
+
settle into the regular hexagonal lattice, so spacing stays organically
|
|
116
|
+
uneven. This is the single biggest dial.
|
|
117
|
+
* `--n_iter n` — hard cap on iterations (lower = looser).
|
|
118
|
+
* `--epsilon d` — optional: stop when mean point movement drops below `d`.
|
|
119
|
+
|
|
120
|
+
To control **contrast** (denser blacks / cleaner whites), use `--gamma g`. It
|
|
121
|
+
applies a power curve `d ** g` to the density that drives point placement,
|
|
122
|
+
relaxation and dot size, so `g > 1` (e.g. `2.0`–`2.5`) concentrates the same
|
|
123
|
+
`n_point` dots into the dark areas and thins the light ones; `g < 1` flattens
|
|
124
|
+
the tonal range; `g = 1` is the original linear mapping. (`--threshold` is a
|
|
125
|
+
blunter, hard cutoff that forces lighter greys to pure white.)
|
|
126
|
+
2. **Varying dot size** — a min/max radius spread plus per-dot random jitter
|
|
127
|
+
breaks the constant-radius "machine stipple" giveaway.
|
|
128
|
+
* `--pointsize min max` — radius range (density drives the base size).
|
|
129
|
+
* `--size_jitter f` — per-dot multiplicative radius noise, e.g. `0.15`.
|
|
130
|
+
3. **Imperfect placement & edges** — Gaussian positional jitter plus wobbly,
|
|
131
|
+
non-circular dot outlines.
|
|
132
|
+
* `--position_jitter s` — Gaussian noise std on final positions.
|
|
133
|
+
* `--edge_noise f` — dot-edge wobble as a fraction of radius, e.g. `0.08`.
|
|
134
|
+
* `--edge_segments n` — vertices per dot outline.
|
|
135
|
+
|
|
136
|
+
Output format follows the `--out` extension (`.png`, `.pdf`, `.svg`).
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
stippler data/boots.jpg --n_point 12000 --n_iter 8 \
|
|
140
|
+
--pointsize 0.8 3.0 --size_jitter 0.2 --position_jitter 0.5 \
|
|
141
|
+
--edge_noise 0.09 --seed 3 --out data/boots-stipple.png
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The pipeline can also be imported as a library: `stippler.stipple(...)` returns
|
|
145
|
+
a `StippleResult` carrying `points`, `radii` and per-dot `polygons` (handy for
|
|
146
|
+
feeding geometry into Rhino later), which `render_matplotlib`, `render_svg` and
|
|
147
|
+
`save_points` consume.
|
|
148
|
+
|
|
149
|
+
## Scientific-illustration pipeline (Lu et al.)
|
|
150
|
+
|
|
151
|
+
The hand-drawn pipeline is further extended with optional controls adapted from
|
|
152
|
+
Lu et al., *Non-Photorealistic Volume Rendering Using Stippling Techniques*
|
|
153
|
+
([`resources/vis_stipple.pdf`](resources/vis_stipple.pdf)). Each feature is
|
|
154
|
+
independently toggled via `IllustrationParams` (library) or the CLI
|
|
155
|
+
`illustration` argument group (`stippler --help`):
|
|
156
|
+
|
|
157
|
+
* **Boundary** — boost stipple density on high-gradient edges.
|
|
158
|
+
* **Silhouette density** — concentrate dots on view-facing silhouette regions
|
|
159
|
+
(uses an optional normal map).
|
|
160
|
+
* **Interior** — sparse stipples in flat, low-gradient areas.
|
|
161
|
+
* **Lighting** — modulate density from inferred or supplied normals.
|
|
162
|
+
* **Depth attenuation** — thin stipples in far regions (requires a depth map).
|
|
163
|
+
* **Gradient size** — scale dot radius by local gradient magnitude.
|
|
164
|
+
* **Silhouette curves** — extract and draw feature-line strokes over the dots.
|
|
165
|
+
|
|
166
|
+
Normal and depth maps should be aligned with the beauty-pass image. In Rhino,
|
|
167
|
+
the Grasshopper component can capture these automatically when the options that
|
|
168
|
+
need them are enabled.
|
|
169
|
+
|
|
170
|
+
## Grasshopper / Rhino integration
|
|
171
|
+
|
|
172
|
+
Max Benjamin Eschenbach integrated the revised pipeline into Grasshopper for
|
|
173
|
+
Rhino 8 CPython 3.9.10:
|
|
174
|
+
|
|
175
|
+
* **`grasshopper_userobjects/STIPPLER_StippledViewCapture.ghuser`** — drop-in
|
|
176
|
+
user object that captures the active viewport and writes a `_stippled` output
|
|
177
|
+
(PNG, SVG, or PDF), including hand-drawn controls and the Lu et al. options
|
|
178
|
+
above.
|
|
179
|
+
* **`grasshopper_userobjects_src/`** — Python 3 script source and input/output
|
|
180
|
+
reference. Re-export the user object from Grasshopper after editing the script
|
|
181
|
+
(right-click → **Save User Object…**).
|
|
182
|
+
|
|
183
|
+
Install the `stippler` package into the Rhino CPython environment the component
|
|
184
|
+
uses (see Installation above), then paste or sync the script into a GH Python 3
|
|
185
|
+
component.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "stippler"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Hand-drawn weighted Voronoi stippling pipeline (Python 3.9.10 / Rhino CPython compatible)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
license-files = ["LICENSE.txt"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Nicolas P. Rougier" },
|
|
15
|
+
]
|
|
16
|
+
maintainers = [
|
|
17
|
+
{ name = "Max Benjamin Eschenbach" },
|
|
18
|
+
]
|
|
19
|
+
keywords = ["stippling", "voronoi", "halftone", "rhino", "grasshopper", "npar"]
|
|
20
|
+
classifiers = [
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Topic :: Multimedia :: Graphics",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"numpy",
|
|
27
|
+
"scipy",
|
|
28
|
+
"Pillow",
|
|
29
|
+
"tqdm",
|
|
30
|
+
"matplotlib",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/fstwn/stippler"
|
|
35
|
+
Repository = "https://github.com/fstwn/stippler"
|
|
36
|
+
Documentation = "https://github.com/fstwn/stippler#readme"
|
|
37
|
+
"Bug Tracker" = "https://github.com/fstwn/stippler/issues"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
stippler = "stippler.pipeline:main"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools.packages.find]
|
|
43
|
+
where = ["src"]
|
|
44
|
+
|
|
45
|
+
[tool.setuptools.dynamic]
|
|
46
|
+
version = { attr = "stippler.__version__" }
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Hand-drawn weighted Voronoi stippling.
|
|
2
|
+
|
|
3
|
+
Grayscale image -> stipple geometry/output, with controls for early-stopped
|
|
4
|
+
relaxation, varying dot size, positional jitter, imperfect dot edges and
|
|
5
|
+
contrast (gamma). Optional Lu et al. scientific-illustration enhancements
|
|
6
|
+
(boundary/silhouette density, interior sparsity, lighting, depth, silhouette
|
|
7
|
+
curves) are toggled via :class:`IllustrationParams`. Targets Python 3.9.10 for
|
|
8
|
+
Rhino 8 CPython compatibility.
|
|
9
|
+
|
|
10
|
+
The high-level entry point is :func:`stipple`, which returns a
|
|
11
|
+
:class:`StippleResult` carrying the points, radii, per-dot polygons and
|
|
12
|
+
optional silhouette curves. Render helpers (:func:`render_matplotlib`,
|
|
13
|
+
:func:`render_svg`, :func:`save_points`) turn that into files.
|
|
14
|
+
"""
|
|
15
|
+
from . import voronoi
|
|
16
|
+
from .illustration import IllustrationParams
|
|
17
|
+
from .pipeline import (
|
|
18
|
+
StippleResult,
|
|
19
|
+
stipple,
|
|
20
|
+
load_density,
|
|
21
|
+
prepare_density,
|
|
22
|
+
normalize,
|
|
23
|
+
initialization,
|
|
24
|
+
relax,
|
|
25
|
+
assign_radii,
|
|
26
|
+
apply_position_jitter,
|
|
27
|
+
dot_polygons,
|
|
28
|
+
render_matplotlib,
|
|
29
|
+
render_svg,
|
|
30
|
+
save_points,
|
|
31
|
+
main,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
__version__ = "0.2.0.0"
|
|
35
|
+
|
|
36
|
+
__all__ = [
|
|
37
|
+
"StippleResult",
|
|
38
|
+
"stipple",
|
|
39
|
+
"IllustrationParams",
|
|
40
|
+
"load_density",
|
|
41
|
+
"prepare_density",
|
|
42
|
+
"normalize",
|
|
43
|
+
"initialization",
|
|
44
|
+
"relax",
|
|
45
|
+
"assign_radii",
|
|
46
|
+
"apply_position_jitter",
|
|
47
|
+
"dot_polygons",
|
|
48
|
+
"render_matplotlib",
|
|
49
|
+
"render_svg",
|
|
50
|
+
"save_points",
|
|
51
|
+
"main",
|
|
52
|
+
"voronoi",
|
|
53
|
+
"__version__",
|
|
54
|
+
]
|