Skip to content

Commit 77885a4

Browse files
committed
WIP
1 parent 0e8b51e commit 77885a4

File tree

2 files changed

+96
-53
lines changed

2 files changed

+96
-53
lines changed

mathics/builtin/drawing/plot.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ def eval(self, points, evaluation: Evaluation, options: dict):
137137
)
138138

139139
points = points.evaluate(evaluation)
140-
print("xxx points", points)
141140
if not isinstance(points, ListExpression):
142141
evaluation.message(class_name, "lpn", points)
143142
return
@@ -461,7 +460,6 @@ def __init__(self, expr, range_exprs, options, evaluation):
461460
# plot ranges
462461
self.ranges = []
463462
for range_expr in range_exprs:
464-
print("xxx range_expr", range_expr)
465463
if not range_expr.has_form("List", 3):
466464
self.error(expr, "invrange", range_expr)
467465
if not isinstance(range_expr.elements[0], Symbol):
@@ -754,7 +752,9 @@ class ComplexPlot3D(_Plot3D):
754752

755753
summary_text = "plots one or more complex functions as a surface"
756754
expected_args = 2
757-
options = _Plot3D.options3d
755+
options = _Plot3D.options3d | {
756+
"Mesh": "None"
757+
}
758758

759759
many_functions = True
760760
eval_function = staticmethod(eval_ComplexPlot3D)

mathics/eval/drawing/plot3d_vectorized.py

Lines changed: 93 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""
2-
Vectorized evaluation routines for Plot3D and DensityPlot, which share a good bit of code.
3-
4-
TODO: fill out eval_DensityPlot
2+
Vectorized evaluation routines for Plot3D, DensityPlot, ComplexPlot, and ComplexPlot3D,
3+
which share a good bit of code.
54
"""
65

76
import math
87

98
import numpy as np
109

10+
from mathics.builtin.colors.color_internals import convert_color
1111
from mathics.core.evaluation import Evaluation
1212
from mathics.core.symbols import strip_context
1313
from mathics.core.systemsymbols import SymbolNone, SymbolRGBColor
@@ -17,7 +17,7 @@
1717
from .util import GraphicsGenerator
1818

1919

20-
def make_plot(plot_options, evaluation: Evaluation, dim: int, is_complex: bool):
20+
def make_plot(plot_options, evaluation: Evaluation, dim: int, is_complex: bool, emit):
2121
graphics = GraphicsGenerator(dim)
2222

2323
# pull out plot options
@@ -36,17 +36,6 @@ def make_plot(plot_options, evaluation: Evaluation, dim: int, is_complex: bool):
3636
if plot_options.mesh is SymbolNone:
3737
nmesh = 0
3838

39-
# color-blind friendly palette from https://davidmathlogic.com/colorblind
40-
palette = [
41-
(255, 176, 0), # orange
42-
(100, 143, 255), # blue
43-
(220, 38, 127), # red
44-
(50, 150, 140), # green
45-
(120, 94, 240), # purple
46-
(254, 97, 0), # dark orange
47-
(0, 114, 178), # dark blue
48-
]
49-
5039
# compile the functions
5140
with Timer("compile"):
5241
compiled_functions = [
@@ -90,7 +79,8 @@ def compute_over_grid(nx, ny):
9079
# TODO: check that imag is all 0?
9180
# TODO: needed this for Hypergeometric - look into that
9281
# assert np.all(np.isreal(zs)), "array contains complex values"
93-
zs = np.real(zs)
82+
if not is_complex:
83+
zs = np.real(zs)
9484

9585
# if it's a constant, make it a full array
9686
if isinstance(zs, (float, int, complex)):
@@ -110,34 +100,9 @@ def compute_over_grid(nx, ny):
110100
# transpose and flatten to ((nx-1)*(ny-1), 4) array, suitable for use in GraphicsComplex
111101
quads = quads.T.reshape(-1, 4)
112102

113-
# Plot3D
114-
if dim == 3:
115-
# choose a color
116-
rgb = palette[i % len(palette)]
117-
rgb = [c / 255.0 for c in rgb]
118-
# graphics.add_color(SymbolRGBColor, rgb)
119-
graphics.add_directives([SymbolRGBColor, *rgb])
120-
121-
# add a GraphicsComplex displaying a surface for this function
122-
graphics.add_complex(xyzs, lines=None, polys=quads)
123-
124-
# DensityPlot
125-
elif dim == 2:
126-
# Fixed palette for now
127-
# TODO: accept color options
128-
with Timer("compute colors"):
129-
zs = xyzs[:, 2]
130-
z_min, z_max = min(zs), max(zs)
131-
zs = zs[:, np.newaxis] # allow broadcasting
132-
c_min, c_max = [0.5, 0, 0.1], [1.0, 0.9, 0.5]
133-
c_min, c_max = (
134-
np.full((len(zs), 3), c_min),
135-
np.full((len(zs), 3), c_max),
136-
)
137-
colors = ((zs - z_min) * c_max + (z_max - zs) * c_min) / (z_max - z_min)
138-
139-
# flatten the points and add the quads
140-
graphics.add_complex(xyzs[:, 0:2], lines=None, polys=quads, colors=colors)
103+
# pass the xyzs and quads back to the caller to add colors and emit quads as appropriate
104+
emit(graphics, i, xyzs, quads)
105+
141106

142107
# If requested by the Mesh attribute create a mesh of lines covering the surfaces
143108
# For now only for Plot3D
@@ -153,9 +118,9 @@ def compute_over_grid(nx, ny):
153118
# Each mesh line has high res (nx or ny) so it follows
154119
# the contours of the surface.
155120
for xyzs, inxs in compute_over_grid(nx, nmesh):
156-
graphics.add_complex(xyzs, lines=inxs, polys=None)
121+
graphics.add_complex(xyzs.astype(float), lines=inxs, polys=None)
157122
for xyzs, inxs in compute_over_grid(nmesh, ny):
158-
graphics.add_complex(xyzs, lines=inxs.T, polys=None)
123+
graphics.add_complex(xyzs.astype(float), lines=inxs.T, polys=None)
159124

160125
return graphics
161126

@@ -165,28 +130,106 @@ def eval_Plot3D(
165130
plot_options,
166131
evaluation: Evaluation,
167132
):
168-
return make_plot(plot_options, evaluation, dim=3, is_complex=False)
133+
def emit(graphics, i, xyzs, quads):
134+
135+
# color-blind friendly palette from https://davidmathlogic.com/colorblind
136+
palette = [
137+
(255, 176, 0), # orange
138+
(100, 143, 255), # blue
139+
(220, 38, 127), # red
140+
(50, 150, 140), # green
141+
(120, 94, 240), # purple
142+
(254, 97, 0), # dark orange
143+
(0, 114, 178), # dark blue
144+
]
145+
146+
# choose a color
147+
rgb = palette[i % len(palette)]
148+
rgb = [c / 255.0 for c in rgb]
149+
# graphics.add_color(SymbolRGBColor, rgb)
150+
graphics.add_directives([SymbolRGBColor, *rgb])
151+
152+
# add a GraphicsComplex displaying a surface for this function
153+
graphics.add_complex(xyzs, lines=None, polys=quads)
154+
155+
156+
return make_plot(plot_options, evaluation, dim=3, is_complex=False, emit=emit)
169157

170158

171159
@Timer("eval_DensityPlot")
172160
def eval_DensityPlot(
173161
plot_options,
174162
evaluation: Evaluation,
175163
):
176-
return make_plot(plot_options, evaluation, dim=2, is_complex=False)
164+
def emit(graphics, i, xyzs, quads):
165+
166+
# Fixed palette for now
167+
# TODO: accept color options
168+
with Timer("compute colors"):
169+
zs = xyzs[:, 2]
170+
z_min, z_max = min(zs), max(zs)
171+
zs = zs[:, np.newaxis] # allow broadcasting
172+
c_min, c_max = [0.5, 0, 0.1], [1.0, 0.9, 0.5]
173+
c_min, c_max = (
174+
np.full((len(zs), 3), c_min),
175+
np.full((len(zs), 3), c_max),
176+
)
177+
colors = ((zs - z_min) * c_max + (z_max - zs) * c_min) / (z_max - z_min)
178+
179+
# flatten the points and add the quads
180+
graphics.add_complex(xyzs[:, 0:2], lines=None, polys=quads, colors=colors)
181+
182+
return make_plot(plot_options, evaluation, dim=2, is_complex=False, emit=emit)
183+
184+
185+
@Timer("complex colors")
186+
def complex_colors(zs, s=None):
187+
188+
# hue depends on phase
189+
h = np.angle(zs, deg=True) / 360
190+
191+
# saturation depends on magnitude
192+
if s is None:
193+
zabs = abs(zs)
194+
zabs = -np.log(zabs)
195+
zmin, zmax = min(zabs), max(zabs)
196+
s = (zabs - zmin) / (zmax - zmin)
197+
else:
198+
s = np.full(zs.shape, s)
199+
200+
# brightness is constant
201+
b = np.full(zs.shape, 1.0)
202+
203+
# convert to rgb
204+
hsb = np.array([h, s, b]).T
205+
rgb = convert_color(hsb, "HSB", "RGB", False)
206+
207+
return rgb
177208

178209

179210
@Timer("eval_ComplexPlot3D")
180211
def eval_ComplexPlot3D(
181212
plot_options,
182213
evaluation: Evaluation,
183214
):
184-
return None
215+
def emit(graphics, i, xyzs, quads):
216+
zs = xyzs[:,2]
217+
rgb = complex_colors(zs, s=0.8)
218+
xyzs[:,2] = abs(zs)
219+
graphics.add_complex(xyzs.astype(float), lines=None, polys=quads, colors=rgb)
220+
221+
return make_plot(plot_options, evaluation, dim=3, is_complex=True, emit=emit)
222+
185223

186224

187225
@Timer("eval_ComplexPlot")
188226
def eval_ComplexPlot(
189227
plot_options,
190228
evaluation: Evaluation,
191229
):
192-
return make_plot(plot_options, evaluation, dim=2, is_complex=True)
230+
def emit(graphics, i, xyzs, quads):
231+
# flatten the points and add the quads
232+
rgb = complex_colors(xyzs[:,2])
233+
graphics.add_complex(xyzs[:, 0:2].astype(float), lines=None, polys=quads, colors=rgb)
234+
235+
return make_plot(plot_options, evaluation, dim=2, is_complex=True, emit=emit)

0 commit comments

Comments
 (0)