Skip to content

Commit d821224

Browse files
gselzerCopilot
andauthored
Anti aliasing - always a boolean (#51)
* Change Points.antialias to boolean VisPy uses an int and pygfx uses a boolean. Until someone wants that int for VisPy let's just use a boolean for simplicity. * Add line antialiasing * Remove example cruft * Pygfx lines: pass aa in ctor * Text antialiasing * Break up adaptor line tests into multiple * Add an antialiasing example * Update tests/adaptors/_vispy/test_line.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 67989f9 commit d821224

19 files changed

Lines changed: 230 additions & 58 deletions

File tree

examples/antialiasing.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
"""Demonstrates antialiasing for Lines, Points, and Text.
2+
3+
Three nodes are shown — a diagonal line, a disc point, and a text label —
4+
each supporting the ``antialias`` property.
5+
6+
**Interaction**
7+
- Left-click anywhere in the view to toggle antialiasing on all nodes.
8+
The status text updates to reflect the current state.
9+
10+
Antialiasing is most noticeable on the diagonal line (jagged stairstepping
11+
appears on its edges without AA) and on the curved edge of the disc.
12+
"""
13+
14+
import cmap
15+
import numpy as np
16+
17+
import scenex as snx
18+
from scenex.app.events import Event, MousePressEvent
19+
from scenex.utils import projections
20+
21+
# --- Diagonal line on the left side ---
22+
# A steep diagonal makes aliasing (stairstepping) clearly visible.
23+
line = snx.Line(
24+
vertices=np.array([[-0.7, -0.5, 0], [0.1, 0.6, 0]], dtype=np.float32),
25+
color=snx.UniformColor(color=cmap.Color("white")),
26+
width=2.0,
27+
antialias=True,
28+
)
29+
30+
# --- Single disc point on the right side ---
31+
point = snx.Points(
32+
vertices=np.array([[0.65, 0.0, 0]], dtype=np.float32),
33+
size=30,
34+
scaling="fixed",
35+
face_color=snx.UniformColor(color=cmap.Color("orange")),
36+
edge_color=snx.UniformColor(color=cmap.Color("white")),
37+
edge_width=2.0,
38+
antialias=True,
39+
)
40+
41+
# --- Status label at the bottom ---
42+
status_text = snx.Text(
43+
text="Antialiasing: ON (click to toggle)",
44+
color=cmap.Color("yellow"),
45+
size=14,
46+
antialias=True,
47+
transform=snx.Transform().translated((0, -0.8, 0)),
48+
)
49+
50+
view = snx.View(
51+
scene=snx.Scene(children=[line, point, status_text]),
52+
camera=snx.Camera(),
53+
)
54+
55+
56+
def _toggle_antialias(event: Event) -> bool:
57+
if isinstance(event, MousePressEvent):
58+
# Toggle the antialiasing state for all nodes and update the status text.
59+
new_aa = not line.antialias
60+
line.antialias = new_aa
61+
point.antialias = new_aa
62+
status_text.antialias = new_aa
63+
state = "ON" if new_aa else "OFF"
64+
status_text.text = f"Antialiasing: {state} (click to toggle)"
65+
return True
66+
return False
67+
68+
69+
view.set_event_filter(_toggle_antialias)
70+
71+
snx.show(view)
72+
view.camera.projection = projections.orthographic(2, 2, 1e5)
73+
snx.run()

examples/basic_line.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def _view_event_filter(event: Event) -> bool:
6060
elif isinstance(event, MousePressEvent):
6161
if event.buttons & MouseButton.LEFT:
6262
if intersections := event.world_ray.intersections(view.scene):
63-
# Find mesh intersection
63+
# Find line intersection
6464
for node, _distance in intersections:
6565
if isinstance(node, snx.Line):
6666
pressed = True

examples/basic_points.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,6 @@ def _on_view_event(event: Event) -> bool:
8484

8585
view.set_event_filter(_on_view_event)
8686

87-
snx.use("pygfx")
88-
# snx.use("vispy")
89-
9087
# Show and position camera
9188
snx.show(view)
9289
view.camera.projection = projections.orthographic(2, 2, 1e5)

src/scenex/adaptors/_base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def _snx_set_symbol(self, arg: str, /) -> None: ...
162162
@abstractmethod
163163
def _snx_set_scaling(self, arg: model.ScalingMode, /) -> None: ...
164164
@abstractmethod
165-
def _snx_set_antialias(self, arg: float, /) -> None: ...
165+
def _snx_set_antialias(self, arg: bool, /) -> None: ...
166166

167167

168168
class MeshAdaptor(NodeAdaptor[TMesh, TNative]):
@@ -185,6 +185,8 @@ def _snx_set_vertices(self, arg: NDArray) -> None: ...
185185
def _snx_set_color(self, arg: model.ColorModel, /) -> None: ...
186186
@abstractmethod
187187
def _snx_set_width(self, arg: float, /) -> None: ...
188+
@abstractmethod
189+
def _snx_set_antialias(self, arg: bool, /) -> None: ...
188190

189191

190192
class TextAdaptor(NodeAdaptor[TText, TNative]):
@@ -196,6 +198,8 @@ def _snx_set_text(self, arg: str, /) -> None: ...
196198
def _snx_set_color(self, arg: model.Color, /) -> None: ...
197199
@abstractmethod
198200
def _snx_set_size(self, arg: int, /) -> None: ...
201+
@abstractmethod
202+
def _snx_set_antialias(self, arg: bool, /) -> None: ...
199203

200204

201205
class CanvasAdaptor(SupportsVisibility[TCanvas, TNative]):

src/scenex/adaptors/_pygfx/_line.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def __init__(self, line: model.Line, **backend_kwargs: Any) -> None:
2626
self._model = line
2727
self._material = pygfx.LineMaterial(
2828
thickness=line.width,
29+
aa=line.antialias,
2930
# This value has model render order win for coplanar objects
3031
depth_compare="<=",
3132
)
@@ -65,3 +66,6 @@ def _snx_set_color(self, arg: ColorModel) -> None:
6566

6667
def _snx_set_width(self, arg: float) -> None:
6768
self._material.thickness = arg
69+
70+
def _snx_set_antialias(self, arg: bool) -> None:
71+
self._material.aa = arg

src/scenex/adaptors/_pygfx/_points.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, points: model.Points, **backend_kwargs: Any) -> None:
3737
self._material = pygfx.PointsMarkerMaterial(
3838
size=points.size, # pyright: ignore[reportArgumentType]
3939
size_space=SPACE_MAP[points.scaling],
40-
aa=points.antialias > 0,
40+
aa=points.antialias,
4141
edge_width=points.edge_width,
4242
opacity=points.opacity,
4343
# This value has model render order win for coplanar objects
@@ -102,8 +102,8 @@ def _snx_set_symbol(self, symbol: str) -> None: ...
102102
def _snx_set_scaling(self, scaling: model.ScalingMode) -> None:
103103
self._material.size_space = SPACE_MAP[scaling]
104104

105-
def _snx_set_antialias(self, antialias: float) -> None:
106-
self._material.aa = antialias > 0
105+
def _snx_set_antialias(self, antialias: bool) -> None:
106+
self._material.aa = antialias
107107

108108
def _snx_set_opacity(self, arg: float) -> None:
109109
self._material.opacity = arg

src/scenex/adaptors/_pygfx/_text.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def __init__(self, text: TextModel, **backend_kwargs: Any) -> None:
2828
color=text.color.hex,
2929
# This value has model render order win for coplanar objects
3030
depth_compare="<=",
31+
aa=text.antialias,
3132
)
3233
# create a pygfx Text visual
3334
self._pygfx_node = pygfx.Text(
@@ -45,3 +46,6 @@ def _snx_set_color(self, arg: cmap.Color) -> None:
4546

4647
def _snx_set_size(self, arg: int) -> None:
4748
self._pygfx_node.font_size = arg
49+
50+
def _snx_set_antialias(self, arg: bool) -> None:
51+
self._material.aa = arg

src/scenex/adaptors/_vispy/_line.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(self, line: model.Line, **backend_kwargs: Any) -> None:
3030
self._vispy_node = vispy.scene.Line(
3131
pos=np.asarray(line.vertices, dtype=np.float32),
3232
width=int(line.width),
33-
antialias=line.antialias > 0,
33+
antialias=line.antialias,
3434
)
3535
self._snx_set_color(line.color)
3636

@@ -45,3 +45,6 @@ def _snx_set_color(self, arg: snx.ColorModel) -> None:
4545

4646
def _snx_set_width(self, arg: float) -> None:
4747
self._vispy_node.set_data(width=arg)
48+
49+
def _snx_set_antialias(self, arg: bool) -> None:
50+
self._vispy_node.antialias = arg

src/scenex/adaptors/_vispy/_points.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ def _snx_set_scaling(self, scaling: model.ScalingMode) -> None:
5656
self._vispy_node.scaling = scaling
5757
self._update_vispy_data()
5858

59-
def _snx_set_antialias(self, antialias: float) -> None:
60-
self._vispy_node.antialias = antialias
59+
def _snx_set_antialias(self, antialias: bool) -> None:
60+
self._vispy_node.antialias = 1.0 if antialias else 0.0
6161

6262
def _snx_set_opacity(self, arg: float) -> None:
6363
self._vispy_node.alpha = arg

src/scenex/adaptors/_vispy/_text.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ def _snx_set_size(self, arg: int) -> None:
4949
if self._vispy_node.font_size != font_size:
5050
self._vispy_node.font_size = font_size
5151

52+
def _snx_set_antialias(self, arg: bool) -> None:
53+
# vispy's TextVisual uses SDF-based rendering and cannot be tuned; antialiasing
54+
# is always applied internally.
55+
# TODO: Consider logging something here? There are bigger questions around how
56+
# scenex should handle something that a backend doesn't support. For now let's
57+
# be quiet.
58+
pass
59+
5260
def _snx_set_blending(self, arg: BlendMode) -> None:
5361
# Blending text makes it look very blocky
5462
pass

0 commit comments

Comments
 (0)