Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .venv/Lib/site-packages/sklearn/datasets/tests/data/openml/id_561/api-v1-jdl-dn-cpu-l-2-dv-1.json.gz +3 -0
- .venv/Lib/site-packages/sklearn/datasets/tests/data/openml/id_61/data-v1-dl-61.arff.gz +3 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/plot.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/series.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/textplot.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/__pycache__/utils.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/__pycache__/base_backend.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__init__.py +5 -0
- .venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__pycache__/matplotlib.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/textbackend/__init__.py +3 -0
- .venv/Lib/site-packages/sympy/plotting/backends/textbackend/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/textbackend/__pycache__/text.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/backends/textbackend/text.py +24 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/__init__.py +12 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/interval_arithmetic.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/interval_membership.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/lib_interval.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py +413 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/interval_membership.py +78 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/lib_interval.py +452 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/tests/__init__.py +0 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py +415 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py +150 -0
- .venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_intervalmath.py +213 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/__init__.py +138 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/__pycache__/__init__.cpython-39.pyc +0 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/color_scheme.py +336 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/managed_window.py +106 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot.py +464 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_axes.py +251 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_camera.py +124 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_controller.py +218 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_curve.py +82 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_interval.py +181 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_mode.py +400 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_mode_base.py +378 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_modes.py +209 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_object.py +17 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_rotation.py +68 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_surface.py +102 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/plot_window.py +144 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/tests/__init__.py +0 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/tests/test_plotting.py +88 -0
- .venv/Lib/site-packages/sympy/plotting/pygletplot/util.py +188 -0
.venv/Lib/site-packages/sklearn/datasets/tests/data/openml/id_561/api-v1-jdl-dn-cpu-l-2-dv-1.json.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0703b0ae20b9ff75087dc601640ee58f1c2ad6768858ea21a245151da9ba8e4c
|
| 3 |
+
size 301
|
.venv/Lib/site-packages/sklearn/datasets/tests/data/openml/id_61/data-v1-dl-61.arff.gz
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:afe4736924606638984e573235191025d419c545d31dc8874c96b72f5ec5db73
|
| 3 |
+
size 2342
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (603 Bytes). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/plot.cpython-39.pyc
ADDED
|
Binary file (37.1 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/plot_implicit.cpython-39.pyc
ADDED
|
Binary file (7.45 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/plotgrid.cpython-39.pyc
ADDED
|
Binary file (6.6 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/series.cpython-39.pyc
ADDED
|
Binary file (78.1 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/textplot.cpython-39.pyc
ADDED
|
Binary file (4.75 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/__pycache__/utils.cpython-39.pyc
ADDED
|
Binary file (11.3 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (195 Bytes). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/__pycache__/base_backend.cpython-39.pyc
ADDED
|
Binary file (15.1 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.backends.matplotlibbackend.matplotlib import (
|
| 2 |
+
MatplotlibBackend, _matplotlib_list
|
| 3 |
+
)
|
| 4 |
+
|
| 5 |
+
__all__ = ["MatplotlibBackend", "_matplotlib_list"]
|
.venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (363 Bytes). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/matplotlibbackend/__pycache__/matplotlib.cpython-39.pyc
ADDED
|
Binary file (8.53 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/textbackend/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.backends.textbackend.text import TextBackend
|
| 2 |
+
|
| 3 |
+
__all__ = ["TextBackend"]
|
.venv/Lib/site-packages/sympy/plotting/backends/textbackend/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (305 Bytes). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/textbackend/__pycache__/text.cpython-39.pyc
ADDED
|
Binary file (1.33 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/backends/textbackend/text.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sympy.plotting.backends.base_backend as base_backend
|
| 2 |
+
from sympy.plotting.series import LineOver1DRangeSeries
|
| 3 |
+
from sympy.plotting.textplot import textplot
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class TextBackend(base_backend.Plot):
|
| 7 |
+
def __init__(self, *args, **kwargs):
|
| 8 |
+
super().__init__(*args, **kwargs)
|
| 9 |
+
|
| 10 |
+
def show(self):
|
| 11 |
+
if not base_backend._show:
|
| 12 |
+
return
|
| 13 |
+
if len(self._series) != 1:
|
| 14 |
+
raise ValueError(
|
| 15 |
+
'The TextBackend supports only one graph per Plot.')
|
| 16 |
+
elif not isinstance(self._series[0], LineOver1DRangeSeries):
|
| 17 |
+
raise ValueError(
|
| 18 |
+
'The TextBackend supports only expressions over a 1D range')
|
| 19 |
+
else:
|
| 20 |
+
ser = self._series[0]
|
| 21 |
+
textplot(ser.expr, ser.start, ser.end)
|
| 22 |
+
|
| 23 |
+
def close(self):
|
| 24 |
+
pass
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .interval_arithmetic import interval
|
| 2 |
+
from .lib_interval import (Abs, exp, log, log10, sin, cos, tan, sqrt,
|
| 3 |
+
imin, imax, sinh, cosh, tanh, acosh, asinh, atanh,
|
| 4 |
+
asin, acos, atan, ceil, floor, And, Or)
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
'interval',
|
| 8 |
+
|
| 9 |
+
'Abs', 'exp', 'log', 'log10', 'sin', 'cos', 'tan', 'sqrt', 'imin', 'imax',
|
| 10 |
+
'sinh', 'cosh', 'tanh', 'acosh', 'asinh', 'atanh', 'asin', 'acos', 'atan',
|
| 11 |
+
'ceil', 'floor', 'And', 'Or',
|
| 12 |
+
]
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (759 Bytes). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/interval_arithmetic.cpython-39.pyc
ADDED
|
Binary file (9.58 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/interval_membership.cpython-39.pyc
ADDED
|
Binary file (3.18 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/__pycache__/lib_interval.cpython-39.pyc
ADDED
|
Binary file (11 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/interval_arithmetic.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Interval Arithmetic for plotting.
|
| 3 |
+
This module does not implement interval arithmetic accurately and
|
| 4 |
+
hence cannot be used for purposes other than plotting. If you want
|
| 5 |
+
to use interval arithmetic, use mpmath's interval arithmetic.
|
| 6 |
+
|
| 7 |
+
The module implements interval arithmetic using numpy and
|
| 8 |
+
python floating points. The rounding up and down is not handled
|
| 9 |
+
and hence this is not an accurate implementation of interval
|
| 10 |
+
arithmetic.
|
| 11 |
+
|
| 12 |
+
The module uses numpy for speed which cannot be achieved with mpmath.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
# Q: Why use numpy? Why not simply use mpmath's interval arithmetic?
|
| 16 |
+
# A: mpmath's interval arithmetic simulates a floating point unit
|
| 17 |
+
# and hence is slow, while numpy evaluations are orders of magnitude
|
| 18 |
+
# faster.
|
| 19 |
+
|
| 20 |
+
# Q: Why create a separate class for intervals? Why not use SymPy's
|
| 21 |
+
# Interval Sets?
|
| 22 |
+
# A: The functionalities that will be required for plotting is quite
|
| 23 |
+
# different from what Interval Sets implement.
|
| 24 |
+
|
| 25 |
+
# Q: Why is rounding up and down according to IEEE754 not handled?
|
| 26 |
+
# A: It is not possible to do it in both numpy and python. An external
|
| 27 |
+
# library has to used, which defeats the whole purpose i.e., speed. Also
|
| 28 |
+
# rounding is handled for very few functions in those libraries.
|
| 29 |
+
|
| 30 |
+
# Q Will my plots be affected?
|
| 31 |
+
# A It will not affect most of the plots. The interval arithmetic
|
| 32 |
+
# module based suffers the same problems as that of floating point
|
| 33 |
+
# arithmetic.
|
| 34 |
+
|
| 35 |
+
from sympy.core.numbers import int_valued
|
| 36 |
+
from sympy.core.logic import fuzzy_and
|
| 37 |
+
from sympy.simplify.simplify import nsimplify
|
| 38 |
+
|
| 39 |
+
from .interval_membership import intervalMembership
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class interval:
|
| 43 |
+
""" Represents an interval containing floating points as start and
|
| 44 |
+
end of the interval
|
| 45 |
+
The is_valid variable tracks whether the interval obtained as the
|
| 46 |
+
result of the function is in the domain and is continuous.
|
| 47 |
+
- True: Represents the interval result of a function is continuous and
|
| 48 |
+
in the domain of the function.
|
| 49 |
+
- False: The interval argument of the function was not in the domain of
|
| 50 |
+
the function, hence the is_valid of the result interval is False
|
| 51 |
+
- None: The function was not continuous over the interval or
|
| 52 |
+
the function's argument interval is partly in the domain of the
|
| 53 |
+
function
|
| 54 |
+
|
| 55 |
+
A comparison between an interval and a real number, or a
|
| 56 |
+
comparison between two intervals may return ``intervalMembership``
|
| 57 |
+
of two 3-valued logic values.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
+
def __init__(self, *args, is_valid=True, **kwargs):
|
| 61 |
+
self.is_valid = is_valid
|
| 62 |
+
if len(args) == 1:
|
| 63 |
+
if isinstance(args[0], interval):
|
| 64 |
+
self.start, self.end = args[0].start, args[0].end
|
| 65 |
+
else:
|
| 66 |
+
self.start = float(args[0])
|
| 67 |
+
self.end = float(args[0])
|
| 68 |
+
elif len(args) == 2:
|
| 69 |
+
if args[0] < args[1]:
|
| 70 |
+
self.start = float(args[0])
|
| 71 |
+
self.end = float(args[1])
|
| 72 |
+
else:
|
| 73 |
+
self.start = float(args[1])
|
| 74 |
+
self.end = float(args[0])
|
| 75 |
+
|
| 76 |
+
else:
|
| 77 |
+
raise ValueError("interval takes a maximum of two float values "
|
| 78 |
+
"as arguments")
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def mid(self):
|
| 82 |
+
return (self.start + self.end) / 2.0
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def width(self):
|
| 86 |
+
return self.end - self.start
|
| 87 |
+
|
| 88 |
+
def __repr__(self):
|
| 89 |
+
return "interval(%f, %f)" % (self.start, self.end)
|
| 90 |
+
|
| 91 |
+
def __str__(self):
|
| 92 |
+
return "[%f, %f]" % (self.start, self.end)
|
| 93 |
+
|
| 94 |
+
def __lt__(self, other):
|
| 95 |
+
if isinstance(other, (int, float)):
|
| 96 |
+
if self.end < other:
|
| 97 |
+
return intervalMembership(True, self.is_valid)
|
| 98 |
+
elif self.start > other:
|
| 99 |
+
return intervalMembership(False, self.is_valid)
|
| 100 |
+
else:
|
| 101 |
+
return intervalMembership(None, self.is_valid)
|
| 102 |
+
|
| 103 |
+
elif isinstance(other, interval):
|
| 104 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 105 |
+
if self.end < other. start:
|
| 106 |
+
return intervalMembership(True, valid)
|
| 107 |
+
if self.start > other.end:
|
| 108 |
+
return intervalMembership(False, valid)
|
| 109 |
+
return intervalMembership(None, valid)
|
| 110 |
+
else:
|
| 111 |
+
return NotImplemented
|
| 112 |
+
|
| 113 |
+
def __gt__(self, other):
|
| 114 |
+
if isinstance(other, (int, float)):
|
| 115 |
+
if self.start > other:
|
| 116 |
+
return intervalMembership(True, self.is_valid)
|
| 117 |
+
elif self.end < other:
|
| 118 |
+
return intervalMembership(False, self.is_valid)
|
| 119 |
+
else:
|
| 120 |
+
return intervalMembership(None, self.is_valid)
|
| 121 |
+
elif isinstance(other, interval):
|
| 122 |
+
return other.__lt__(self)
|
| 123 |
+
else:
|
| 124 |
+
return NotImplemented
|
| 125 |
+
|
| 126 |
+
def __eq__(self, other):
|
| 127 |
+
if isinstance(other, (int, float)):
|
| 128 |
+
if self.start == other and self.end == other:
|
| 129 |
+
return intervalMembership(True, self.is_valid)
|
| 130 |
+
if other in self:
|
| 131 |
+
return intervalMembership(None, self.is_valid)
|
| 132 |
+
else:
|
| 133 |
+
return intervalMembership(False, self.is_valid)
|
| 134 |
+
|
| 135 |
+
if isinstance(other, interval):
|
| 136 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 137 |
+
if self.start == other.start and self.end == other.end:
|
| 138 |
+
return intervalMembership(True, valid)
|
| 139 |
+
elif self.__lt__(other)[0] is not None:
|
| 140 |
+
return intervalMembership(False, valid)
|
| 141 |
+
else:
|
| 142 |
+
return intervalMembership(None, valid)
|
| 143 |
+
else:
|
| 144 |
+
return NotImplemented
|
| 145 |
+
|
| 146 |
+
def __ne__(self, other):
|
| 147 |
+
if isinstance(other, (int, float)):
|
| 148 |
+
if self.start == other and self.end == other:
|
| 149 |
+
return intervalMembership(False, self.is_valid)
|
| 150 |
+
if other in self:
|
| 151 |
+
return intervalMembership(None, self.is_valid)
|
| 152 |
+
else:
|
| 153 |
+
return intervalMembership(True, self.is_valid)
|
| 154 |
+
|
| 155 |
+
if isinstance(other, interval):
|
| 156 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 157 |
+
if self.start == other.start and self.end == other.end:
|
| 158 |
+
return intervalMembership(False, valid)
|
| 159 |
+
if not self.__lt__(other)[0] is None:
|
| 160 |
+
return intervalMembership(True, valid)
|
| 161 |
+
return intervalMembership(None, valid)
|
| 162 |
+
else:
|
| 163 |
+
return NotImplemented
|
| 164 |
+
|
| 165 |
+
def __le__(self, other):
|
| 166 |
+
if isinstance(other, (int, float)):
|
| 167 |
+
if self.end <= other:
|
| 168 |
+
return intervalMembership(True, self.is_valid)
|
| 169 |
+
if self.start > other:
|
| 170 |
+
return intervalMembership(False, self.is_valid)
|
| 171 |
+
else:
|
| 172 |
+
return intervalMembership(None, self.is_valid)
|
| 173 |
+
|
| 174 |
+
if isinstance(other, interval):
|
| 175 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 176 |
+
if self.end <= other.start:
|
| 177 |
+
return intervalMembership(True, valid)
|
| 178 |
+
if self.start > other.end:
|
| 179 |
+
return intervalMembership(False, valid)
|
| 180 |
+
return intervalMembership(None, valid)
|
| 181 |
+
else:
|
| 182 |
+
return NotImplemented
|
| 183 |
+
|
| 184 |
+
def __ge__(self, other):
|
| 185 |
+
if isinstance(other, (int, float)):
|
| 186 |
+
if self.start >= other:
|
| 187 |
+
return intervalMembership(True, self.is_valid)
|
| 188 |
+
elif self.end < other:
|
| 189 |
+
return intervalMembership(False, self.is_valid)
|
| 190 |
+
else:
|
| 191 |
+
return intervalMembership(None, self.is_valid)
|
| 192 |
+
elif isinstance(other, interval):
|
| 193 |
+
return other.__le__(self)
|
| 194 |
+
|
| 195 |
+
def __add__(self, other):
|
| 196 |
+
if isinstance(other, (int, float)):
|
| 197 |
+
if self.is_valid:
|
| 198 |
+
return interval(self.start + other, self.end + other)
|
| 199 |
+
else:
|
| 200 |
+
start = self.start + other
|
| 201 |
+
end = self.end + other
|
| 202 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 203 |
+
|
| 204 |
+
elif isinstance(other, interval):
|
| 205 |
+
start = self.start + other.start
|
| 206 |
+
end = self.end + other.end
|
| 207 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 208 |
+
return interval(start, end, is_valid=valid)
|
| 209 |
+
else:
|
| 210 |
+
return NotImplemented
|
| 211 |
+
|
| 212 |
+
__radd__ = __add__
|
| 213 |
+
|
| 214 |
+
def __sub__(self, other):
|
| 215 |
+
if isinstance(other, (int, float)):
|
| 216 |
+
start = self.start - other
|
| 217 |
+
end = self.end - other
|
| 218 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 219 |
+
|
| 220 |
+
elif isinstance(other, interval):
|
| 221 |
+
start = self.start - other.end
|
| 222 |
+
end = self.end - other.start
|
| 223 |
+
valid = fuzzy_and([self.is_valid, other.is_valid])
|
| 224 |
+
return interval(start, end, is_valid=valid)
|
| 225 |
+
else:
|
| 226 |
+
return NotImplemented
|
| 227 |
+
|
| 228 |
+
def __rsub__(self, other):
|
| 229 |
+
if isinstance(other, (int, float)):
|
| 230 |
+
start = other - self.end
|
| 231 |
+
end = other - self.start
|
| 232 |
+
return interval(start, end, is_valid=self.is_valid)
|
| 233 |
+
elif isinstance(other, interval):
|
| 234 |
+
return other.__sub__(self)
|
| 235 |
+
else:
|
| 236 |
+
return NotImplemented
|
| 237 |
+
|
| 238 |
+
def __neg__(self):
|
| 239 |
+
if self.is_valid:
|
| 240 |
+
return interval(-self.end, -self.start)
|
| 241 |
+
else:
|
| 242 |
+
return interval(-self.end, -self.start, is_valid=self.is_valid)
|
| 243 |
+
|
| 244 |
+
def __mul__(self, other):
|
| 245 |
+
if isinstance(other, interval):
|
| 246 |
+
if self.is_valid is False or other.is_valid is False:
|
| 247 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 248 |
+
elif self.is_valid is None or other.is_valid is None:
|
| 249 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 250 |
+
else:
|
| 251 |
+
inters = []
|
| 252 |
+
inters.append(self.start * other.start)
|
| 253 |
+
inters.append(self.end * other.start)
|
| 254 |
+
inters.append(self.start * other.end)
|
| 255 |
+
inters.append(self.end * other.end)
|
| 256 |
+
start = min(inters)
|
| 257 |
+
end = max(inters)
|
| 258 |
+
return interval(start, end)
|
| 259 |
+
elif isinstance(other, (int, float)):
|
| 260 |
+
return interval(self.start*other, self.end*other, is_valid=self.is_valid)
|
| 261 |
+
else:
|
| 262 |
+
return NotImplemented
|
| 263 |
+
|
| 264 |
+
__rmul__ = __mul__
|
| 265 |
+
|
| 266 |
+
def __contains__(self, other):
|
| 267 |
+
if isinstance(other, (int, float)):
|
| 268 |
+
return self.start <= other and self.end >= other
|
| 269 |
+
else:
|
| 270 |
+
return self.start <= other.start and other.end <= self.end
|
| 271 |
+
|
| 272 |
+
def __rtruediv__(self, other):
|
| 273 |
+
if isinstance(other, (int, float)):
|
| 274 |
+
other = interval(other)
|
| 275 |
+
return other.__truediv__(self)
|
| 276 |
+
elif isinstance(other, interval):
|
| 277 |
+
return other.__truediv__(self)
|
| 278 |
+
else:
|
| 279 |
+
return NotImplemented
|
| 280 |
+
|
| 281 |
+
def __truediv__(self, other):
|
| 282 |
+
# Both None and False are handled
|
| 283 |
+
if not self.is_valid:
|
| 284 |
+
# Don't divide as the value is not valid
|
| 285 |
+
return interval(-float('inf'), float('inf'), is_valid=self.is_valid)
|
| 286 |
+
if isinstance(other, (int, float)):
|
| 287 |
+
if other == 0:
|
| 288 |
+
# Divide by zero encountered. valid nowhere
|
| 289 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 290 |
+
else:
|
| 291 |
+
return interval(self.start / other, self.end / other)
|
| 292 |
+
|
| 293 |
+
elif isinstance(other, interval):
|
| 294 |
+
if other.is_valid is False or self.is_valid is False:
|
| 295 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 296 |
+
elif other.is_valid is None or self.is_valid is None:
|
| 297 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 298 |
+
else:
|
| 299 |
+
# denominator contains both signs, i.e. being divided by zero
|
| 300 |
+
# return the whole real line with is_valid = None
|
| 301 |
+
if 0 in other:
|
| 302 |
+
return interval(-float('inf'), float('inf'), is_valid=None)
|
| 303 |
+
|
| 304 |
+
# denominator negative
|
| 305 |
+
this = self
|
| 306 |
+
if other.end < 0:
|
| 307 |
+
this = -this
|
| 308 |
+
other = -other
|
| 309 |
+
|
| 310 |
+
# denominator positive
|
| 311 |
+
inters = []
|
| 312 |
+
inters.append(this.start / other.start)
|
| 313 |
+
inters.append(this.end / other.start)
|
| 314 |
+
inters.append(this.start / other.end)
|
| 315 |
+
inters.append(this.end / other.end)
|
| 316 |
+
start = max(inters)
|
| 317 |
+
end = min(inters)
|
| 318 |
+
return interval(start, end)
|
| 319 |
+
else:
|
| 320 |
+
return NotImplemented
|
| 321 |
+
|
| 322 |
+
def __pow__(self, other):
|
| 323 |
+
# Implements only power to an integer.
|
| 324 |
+
from .lib_interval import exp, log
|
| 325 |
+
if not self.is_valid:
|
| 326 |
+
return self
|
| 327 |
+
if isinstance(other, interval):
|
| 328 |
+
return exp(other * log(self))
|
| 329 |
+
elif isinstance(other, (float, int)):
|
| 330 |
+
if other < 0:
|
| 331 |
+
return 1 / self.__pow__(abs(other))
|
| 332 |
+
else:
|
| 333 |
+
if int_valued(other):
|
| 334 |
+
return _pow_int(self, other)
|
| 335 |
+
else:
|
| 336 |
+
return _pow_float(self, other)
|
| 337 |
+
else:
|
| 338 |
+
return NotImplemented
|
| 339 |
+
|
| 340 |
+
def __rpow__(self, other):
|
| 341 |
+
if isinstance(other, (float, int)):
|
| 342 |
+
if not self.is_valid:
|
| 343 |
+
#Don't do anything
|
| 344 |
+
return self
|
| 345 |
+
elif other < 0:
|
| 346 |
+
if self.width > 0:
|
| 347 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 348 |
+
else:
|
| 349 |
+
power_rational = nsimplify(self.start)
|
| 350 |
+
num, denom = power_rational.as_numer_denom()
|
| 351 |
+
if denom % 2 == 0:
|
| 352 |
+
return interval(-float('inf'), float('inf'),
|
| 353 |
+
is_valid=False)
|
| 354 |
+
else:
|
| 355 |
+
start = -abs(other)**self.start
|
| 356 |
+
end = start
|
| 357 |
+
return interval(start, end)
|
| 358 |
+
else:
|
| 359 |
+
return interval(other**self.start, other**self.end)
|
| 360 |
+
elif isinstance(other, interval):
|
| 361 |
+
return other.__pow__(self)
|
| 362 |
+
else:
|
| 363 |
+
return NotImplemented
|
| 364 |
+
|
| 365 |
+
def __hash__(self):
|
| 366 |
+
return hash((self.is_valid, self.start, self.end))
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
def _pow_float(inter, power):
|
| 370 |
+
"""Evaluates an interval raised to a floating point."""
|
| 371 |
+
power_rational = nsimplify(power)
|
| 372 |
+
num, denom = power_rational.as_numer_denom()
|
| 373 |
+
if num % 2 == 0:
|
| 374 |
+
start = abs(inter.start)**power
|
| 375 |
+
end = abs(inter.end)**power
|
| 376 |
+
if start < 0:
|
| 377 |
+
ret = interval(0, max(start, end))
|
| 378 |
+
else:
|
| 379 |
+
ret = interval(start, end)
|
| 380 |
+
return ret
|
| 381 |
+
elif denom % 2 == 0:
|
| 382 |
+
if inter.end < 0:
|
| 383 |
+
return interval(-float('inf'), float('inf'), is_valid=False)
|
| 384 |
+
elif inter.start < 0:
|
| 385 |
+
return interval(0, inter.end**power, is_valid=None)
|
| 386 |
+
else:
|
| 387 |
+
return interval(inter.start**power, inter.end**power)
|
| 388 |
+
else:
|
| 389 |
+
if inter.start < 0:
|
| 390 |
+
start = -abs(inter.start)**power
|
| 391 |
+
else:
|
| 392 |
+
start = inter.start**power
|
| 393 |
+
|
| 394 |
+
if inter.end < 0:
|
| 395 |
+
end = -abs(inter.end)**power
|
| 396 |
+
else:
|
| 397 |
+
end = inter.end**power
|
| 398 |
+
|
| 399 |
+
return interval(start, end, is_valid=inter.is_valid)
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
def _pow_int(inter, power):
|
| 403 |
+
"""Evaluates an interval raised to an integer power"""
|
| 404 |
+
power = int(power)
|
| 405 |
+
if power & 1:
|
| 406 |
+
return interval(inter.start**power, inter.end**power)
|
| 407 |
+
else:
|
| 408 |
+
if inter.start < 0 and inter.end > 0:
|
| 409 |
+
start = 0
|
| 410 |
+
end = max(inter.start**power, inter.end**power)
|
| 411 |
+
return interval(start, end)
|
| 412 |
+
else:
|
| 413 |
+
return interval(inter.start**power, inter.end**power)
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/interval_membership.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.logic import fuzzy_and, fuzzy_or, fuzzy_not, fuzzy_xor
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class intervalMembership:
|
| 5 |
+
"""Represents a boolean expression returned by the comparison of
|
| 6 |
+
the interval object.
|
| 7 |
+
|
| 8 |
+
Parameters
|
| 9 |
+
==========
|
| 10 |
+
|
| 11 |
+
(a, b) : (bool, bool)
|
| 12 |
+
The first value determines the comparison as follows:
|
| 13 |
+
- True: If the comparison is True throughout the intervals.
|
| 14 |
+
- False: If the comparison is False throughout the intervals.
|
| 15 |
+
- None: If the comparison is True for some part of the intervals.
|
| 16 |
+
|
| 17 |
+
The second value is determined as follows:
|
| 18 |
+
- True: If both the intervals in comparison are valid.
|
| 19 |
+
- False: If at least one of the intervals is False, else
|
| 20 |
+
- None
|
| 21 |
+
"""
|
| 22 |
+
def __init__(self, a, b):
|
| 23 |
+
self._wrapped = (a, b)
|
| 24 |
+
|
| 25 |
+
def __getitem__(self, i):
|
| 26 |
+
try:
|
| 27 |
+
return self._wrapped[i]
|
| 28 |
+
except IndexError:
|
| 29 |
+
raise IndexError(
|
| 30 |
+
"{} must be a valid indexing for the 2-tuple."
|
| 31 |
+
.format(i))
|
| 32 |
+
|
| 33 |
+
def __len__(self):
|
| 34 |
+
return 2
|
| 35 |
+
|
| 36 |
+
def __iter__(self):
|
| 37 |
+
return iter(self._wrapped)
|
| 38 |
+
|
| 39 |
+
def __str__(self):
|
| 40 |
+
return "intervalMembership({}, {})".format(*self)
|
| 41 |
+
__repr__ = __str__
|
| 42 |
+
|
| 43 |
+
def __and__(self, other):
|
| 44 |
+
if not isinstance(other, intervalMembership):
|
| 45 |
+
raise ValueError(
|
| 46 |
+
"The comparison is not supported for {}.".format(other))
|
| 47 |
+
|
| 48 |
+
a1, b1 = self
|
| 49 |
+
a2, b2 = other
|
| 50 |
+
return intervalMembership(fuzzy_and([a1, a2]), fuzzy_and([b1, b2]))
|
| 51 |
+
|
| 52 |
+
def __or__(self, other):
|
| 53 |
+
if not isinstance(other, intervalMembership):
|
| 54 |
+
raise ValueError(
|
| 55 |
+
"The comparison is not supported for {}.".format(other))
|
| 56 |
+
|
| 57 |
+
a1, b1 = self
|
| 58 |
+
a2, b2 = other
|
| 59 |
+
return intervalMembership(fuzzy_or([a1, a2]), fuzzy_and([b1, b2]))
|
| 60 |
+
|
| 61 |
+
def __invert__(self):
|
| 62 |
+
a, b = self
|
| 63 |
+
return intervalMembership(fuzzy_not(a), b)
|
| 64 |
+
|
| 65 |
+
def __xor__(self, other):
|
| 66 |
+
if not isinstance(other, intervalMembership):
|
| 67 |
+
raise ValueError(
|
| 68 |
+
"The comparison is not supported for {}.".format(other))
|
| 69 |
+
|
| 70 |
+
a1, b1 = self
|
| 71 |
+
a2, b2 = other
|
| 72 |
+
return intervalMembership(fuzzy_xor([a1, a2]), fuzzy_and([b1, b2]))
|
| 73 |
+
|
| 74 |
+
def __eq__(self, other):
|
| 75 |
+
return self._wrapped == other
|
| 76 |
+
|
| 77 |
+
def __ne__(self, other):
|
| 78 |
+
return self._wrapped != other
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/lib_interval.py
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
""" The module contains implemented functions for interval arithmetic."""
|
| 2 |
+
from functools import reduce
|
| 3 |
+
|
| 4 |
+
from sympy.plotting.intervalmath import interval
|
| 5 |
+
from sympy.external import import_module
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def Abs(x):
|
| 9 |
+
if isinstance(x, (int, float)):
|
| 10 |
+
return interval(abs(x))
|
| 11 |
+
elif isinstance(x, interval):
|
| 12 |
+
if x.start < 0 and x.end > 0:
|
| 13 |
+
return interval(0, max(abs(x.start), abs(x.end)), is_valid=x.is_valid)
|
| 14 |
+
else:
|
| 15 |
+
return interval(abs(x.start), abs(x.end))
|
| 16 |
+
else:
|
| 17 |
+
raise NotImplementedError
|
| 18 |
+
|
| 19 |
+
#Monotonic
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def exp(x):
|
| 23 |
+
"""evaluates the exponential of an interval"""
|
| 24 |
+
np = import_module('numpy')
|
| 25 |
+
if isinstance(x, (int, float)):
|
| 26 |
+
return interval(np.exp(x), np.exp(x))
|
| 27 |
+
elif isinstance(x, interval):
|
| 28 |
+
return interval(np.exp(x.start), np.exp(x.end), is_valid=x.is_valid)
|
| 29 |
+
else:
|
| 30 |
+
raise NotImplementedError
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
#Monotonic
|
| 34 |
+
def log(x):
|
| 35 |
+
"""evaluates the natural logarithm of an interval"""
|
| 36 |
+
np = import_module('numpy')
|
| 37 |
+
if isinstance(x, (int, float)):
|
| 38 |
+
if x <= 0:
|
| 39 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 40 |
+
else:
|
| 41 |
+
return interval(np.log(x))
|
| 42 |
+
elif isinstance(x, interval):
|
| 43 |
+
if not x.is_valid:
|
| 44 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 45 |
+
elif x.end <= 0:
|
| 46 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 47 |
+
elif x.start <= 0:
|
| 48 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 49 |
+
|
| 50 |
+
return interval(np.log(x.start), np.log(x.end))
|
| 51 |
+
else:
|
| 52 |
+
raise NotImplementedError
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
#Monotonic
|
| 56 |
+
def log10(x):
|
| 57 |
+
"""evaluates the logarithm to the base 10 of an interval"""
|
| 58 |
+
np = import_module('numpy')
|
| 59 |
+
if isinstance(x, (int, float)):
|
| 60 |
+
if x <= 0:
|
| 61 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 62 |
+
else:
|
| 63 |
+
return interval(np.log10(x))
|
| 64 |
+
elif isinstance(x, interval):
|
| 65 |
+
if not x.is_valid:
|
| 66 |
+
return interval(-np.inf, np.inf, is_valid=x.is_valid)
|
| 67 |
+
elif x.end <= 0:
|
| 68 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 69 |
+
elif x.start <= 0:
|
| 70 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 71 |
+
return interval(np.log10(x.start), np.log10(x.end))
|
| 72 |
+
else:
|
| 73 |
+
raise NotImplementedError
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
#Monotonic
|
| 77 |
+
def atan(x):
|
| 78 |
+
"""evaluates the tan inverse of an interval"""
|
| 79 |
+
np = import_module('numpy')
|
| 80 |
+
if isinstance(x, (int, float)):
|
| 81 |
+
return interval(np.arctan(x))
|
| 82 |
+
elif isinstance(x, interval):
|
| 83 |
+
start = np.arctan(x.start)
|
| 84 |
+
end = np.arctan(x.end)
|
| 85 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 86 |
+
else:
|
| 87 |
+
raise NotImplementedError
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
#periodic
|
| 91 |
+
def sin(x):
|
| 92 |
+
"""evaluates the sine of an interval"""
|
| 93 |
+
np = import_module('numpy')
|
| 94 |
+
if isinstance(x, (int, float)):
|
| 95 |
+
return interval(np.sin(x))
|
| 96 |
+
elif isinstance(x, interval):
|
| 97 |
+
if not x.is_valid:
|
| 98 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 99 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 100 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 101 |
+
start = min(np.sin(x.start), np.sin(x.end))
|
| 102 |
+
end = max(np.sin(x.start), np.sin(x.end))
|
| 103 |
+
if nb - na > 4:
|
| 104 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 105 |
+
elif na == nb:
|
| 106 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 107 |
+
else:
|
| 108 |
+
if (na - 1) // 4 != (nb - 1) // 4:
|
| 109 |
+
#sin has max
|
| 110 |
+
end = 1
|
| 111 |
+
if (na - 3) // 4 != (nb - 3) // 4:
|
| 112 |
+
#sin has min
|
| 113 |
+
start = -1
|
| 114 |
+
return interval(start, end)
|
| 115 |
+
else:
|
| 116 |
+
raise NotImplementedError
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
#periodic
|
| 120 |
+
def cos(x):
|
| 121 |
+
"""Evaluates the cos of an interval"""
|
| 122 |
+
np = import_module('numpy')
|
| 123 |
+
if isinstance(x, (int, float)):
|
| 124 |
+
return interval(np.sin(x))
|
| 125 |
+
elif isinstance(x, interval):
|
| 126 |
+
if not (np.isfinite(x.start) and np.isfinite(x.end)):
|
| 127 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 128 |
+
na, __ = divmod(x.start, np.pi / 2.0)
|
| 129 |
+
nb, __ = divmod(x.end, np.pi / 2.0)
|
| 130 |
+
start = min(np.cos(x.start), np.cos(x.end))
|
| 131 |
+
end = max(np.cos(x.start), np.cos(x.end))
|
| 132 |
+
if nb - na > 4:
|
| 133 |
+
#differ more than 2*pi
|
| 134 |
+
return interval(-1, 1, is_valid=x.is_valid)
|
| 135 |
+
elif na == nb:
|
| 136 |
+
#in the same quadarant
|
| 137 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 138 |
+
else:
|
| 139 |
+
if (na) // 4 != (nb) // 4:
|
| 140 |
+
#cos has max
|
| 141 |
+
end = 1
|
| 142 |
+
if (na - 2) // 4 != (nb - 2) // 4:
|
| 143 |
+
#cos has min
|
| 144 |
+
start = -1
|
| 145 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 146 |
+
else:
|
| 147 |
+
raise NotImplementedError
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def tan(x):
|
| 151 |
+
"""Evaluates the tan of an interval"""
|
| 152 |
+
return sin(x) / cos(x)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
#Monotonic
|
| 156 |
+
def sqrt(x):
|
| 157 |
+
"""Evaluates the square root of an interval"""
|
| 158 |
+
np = import_module('numpy')
|
| 159 |
+
if isinstance(x, (int, float)):
|
| 160 |
+
if x > 0:
|
| 161 |
+
return interval(np.sqrt(x))
|
| 162 |
+
else:
|
| 163 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 164 |
+
elif isinstance(x, interval):
|
| 165 |
+
#Outside the domain
|
| 166 |
+
if x.end < 0:
|
| 167 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 168 |
+
#Partially outside the domain
|
| 169 |
+
elif x.start < 0:
|
| 170 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 171 |
+
else:
|
| 172 |
+
return interval(np.sqrt(x.start), np.sqrt(x.end),
|
| 173 |
+
is_valid=x.is_valid)
|
| 174 |
+
else:
|
| 175 |
+
raise NotImplementedError
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def imin(*args):
|
| 179 |
+
"""Evaluates the minimum of a list of intervals"""
|
| 180 |
+
np = import_module('numpy')
|
| 181 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 182 |
+
return NotImplementedError
|
| 183 |
+
else:
|
| 184 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 185 |
+
or a.is_valid]
|
| 186 |
+
if len(new_args) == 0:
|
| 187 |
+
if all(a.is_valid is False for a in args):
|
| 188 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 189 |
+
else:
|
| 190 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 191 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 192 |
+
for a in new_args]
|
| 193 |
+
|
| 194 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 195 |
+
for a in new_args]
|
| 196 |
+
return interval(min(start_array), min(end_array))
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def imax(*args):
|
| 200 |
+
"""Evaluates the maximum of a list of intervals"""
|
| 201 |
+
np = import_module('numpy')
|
| 202 |
+
if not all(isinstance(arg, (int, float, interval)) for arg in args):
|
| 203 |
+
return NotImplementedError
|
| 204 |
+
else:
|
| 205 |
+
new_args = [a for a in args if isinstance(a, (int, float))
|
| 206 |
+
or a.is_valid]
|
| 207 |
+
if len(new_args) == 0:
|
| 208 |
+
if all(a.is_valid is False for a in args):
|
| 209 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 210 |
+
else:
|
| 211 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 212 |
+
start_array = [a if isinstance(a, (int, float)) else a.start
|
| 213 |
+
for a in new_args]
|
| 214 |
+
|
| 215 |
+
end_array = [a if isinstance(a, (int, float)) else a.end
|
| 216 |
+
for a in new_args]
|
| 217 |
+
|
| 218 |
+
return interval(max(start_array), max(end_array))
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
#Monotonic
|
| 222 |
+
def sinh(x):
|
| 223 |
+
"""Evaluates the hyperbolic sine of an interval"""
|
| 224 |
+
np = import_module('numpy')
|
| 225 |
+
if isinstance(x, (int, float)):
|
| 226 |
+
return interval(np.sinh(x), np.sinh(x))
|
| 227 |
+
elif isinstance(x, interval):
|
| 228 |
+
return interval(np.sinh(x.start), np.sinh(x.end), is_valid=x.is_valid)
|
| 229 |
+
else:
|
| 230 |
+
raise NotImplementedError
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def cosh(x):
|
| 234 |
+
"""Evaluates the hyperbolic cos of an interval"""
|
| 235 |
+
np = import_module('numpy')
|
| 236 |
+
if isinstance(x, (int, float)):
|
| 237 |
+
return interval(np.cosh(x), np.cosh(x))
|
| 238 |
+
elif isinstance(x, interval):
|
| 239 |
+
#both signs
|
| 240 |
+
if x.start < 0 and x.end > 0:
|
| 241 |
+
end = max(np.cosh(x.start), np.cosh(x.end))
|
| 242 |
+
return interval(1, end, is_valid=x.is_valid)
|
| 243 |
+
else:
|
| 244 |
+
#Monotonic
|
| 245 |
+
start = np.cosh(x.start)
|
| 246 |
+
end = np.cosh(x.end)
|
| 247 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 248 |
+
else:
|
| 249 |
+
raise NotImplementedError
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
#Monotonic
|
| 253 |
+
def tanh(x):
|
| 254 |
+
"""Evaluates the hyperbolic tan of an interval"""
|
| 255 |
+
np = import_module('numpy')
|
| 256 |
+
if isinstance(x, (int, float)):
|
| 257 |
+
return interval(np.tanh(x), np.tanh(x))
|
| 258 |
+
elif isinstance(x, interval):
|
| 259 |
+
return interval(np.tanh(x.start), np.tanh(x.end), is_valid=x.is_valid)
|
| 260 |
+
else:
|
| 261 |
+
raise NotImplementedError
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def asin(x):
|
| 265 |
+
"""Evaluates the inverse sine of an interval"""
|
| 266 |
+
np = import_module('numpy')
|
| 267 |
+
if isinstance(x, (int, float)):
|
| 268 |
+
#Outside the domain
|
| 269 |
+
if abs(x) > 1:
|
| 270 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 271 |
+
else:
|
| 272 |
+
return interval(np.arcsin(x), np.arcsin(x))
|
| 273 |
+
elif isinstance(x, interval):
|
| 274 |
+
#Outside the domain
|
| 275 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 276 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 277 |
+
#Partially outside the domain
|
| 278 |
+
elif x.start < -1 or x.end > 1:
|
| 279 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 280 |
+
else:
|
| 281 |
+
start = np.arcsin(x.start)
|
| 282 |
+
end = np.arcsin(x.end)
|
| 283 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
def acos(x):
|
| 287 |
+
"""Evaluates the inverse cos of an interval"""
|
| 288 |
+
np = import_module('numpy')
|
| 289 |
+
if isinstance(x, (int, float)):
|
| 290 |
+
if abs(x) > 1:
|
| 291 |
+
#Outside the domain
|
| 292 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 293 |
+
else:
|
| 294 |
+
return interval(np.arccos(x), np.arccos(x))
|
| 295 |
+
elif isinstance(x, interval):
|
| 296 |
+
#Outside the domain
|
| 297 |
+
if x.is_valid is False or x.start > 1 or x.end < -1:
|
| 298 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 299 |
+
#Partially outside the domain
|
| 300 |
+
elif x.start < -1 or x.end > 1:
|
| 301 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 302 |
+
else:
|
| 303 |
+
start = np.arccos(x.start)
|
| 304 |
+
end = np.arccos(x.end)
|
| 305 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def ceil(x):
|
| 309 |
+
"""Evaluates the ceiling of an interval"""
|
| 310 |
+
np = import_module('numpy')
|
| 311 |
+
if isinstance(x, (int, float)):
|
| 312 |
+
return interval(np.ceil(x))
|
| 313 |
+
elif isinstance(x, interval):
|
| 314 |
+
if x.is_valid is False:
|
| 315 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 316 |
+
else:
|
| 317 |
+
start = np.ceil(x.start)
|
| 318 |
+
end = np.ceil(x.end)
|
| 319 |
+
#Continuous over the interval
|
| 320 |
+
if start == end:
|
| 321 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 322 |
+
else:
|
| 323 |
+
#Not continuous over the interval
|
| 324 |
+
return interval(start, end, is_valid=None)
|
| 325 |
+
else:
|
| 326 |
+
return NotImplementedError
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def floor(x):
|
| 330 |
+
"""Evaluates the floor of an interval"""
|
| 331 |
+
np = import_module('numpy')
|
| 332 |
+
if isinstance(x, (int, float)):
|
| 333 |
+
return interval(np.floor(x))
|
| 334 |
+
elif isinstance(x, interval):
|
| 335 |
+
if x.is_valid is False:
|
| 336 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 337 |
+
else:
|
| 338 |
+
start = np.floor(x.start)
|
| 339 |
+
end = np.floor(x.end)
|
| 340 |
+
#continuous over the argument
|
| 341 |
+
if start == end:
|
| 342 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 343 |
+
else:
|
| 344 |
+
#not continuous over the interval
|
| 345 |
+
return interval(start, end, is_valid=None)
|
| 346 |
+
else:
|
| 347 |
+
return NotImplementedError
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def acosh(x):
|
| 351 |
+
"""Evaluates the inverse hyperbolic cosine of an interval"""
|
| 352 |
+
np = import_module('numpy')
|
| 353 |
+
if isinstance(x, (int, float)):
|
| 354 |
+
#Outside the domain
|
| 355 |
+
if x < 1:
|
| 356 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 357 |
+
else:
|
| 358 |
+
return interval(np.arccosh(x))
|
| 359 |
+
elif isinstance(x, interval):
|
| 360 |
+
#Outside the domain
|
| 361 |
+
if x.end < 1:
|
| 362 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 363 |
+
#Partly outside the domain
|
| 364 |
+
elif x.start < 1:
|
| 365 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 366 |
+
else:
|
| 367 |
+
start = np.arccosh(x.start)
|
| 368 |
+
end = np.arccosh(x.end)
|
| 369 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 370 |
+
else:
|
| 371 |
+
return NotImplementedError
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
#Monotonic
|
| 375 |
+
def asinh(x):
|
| 376 |
+
"""Evaluates the inverse hyperbolic sine of an interval"""
|
| 377 |
+
np = import_module('numpy')
|
| 378 |
+
if isinstance(x, (int, float)):
|
| 379 |
+
return interval(np.arcsinh(x))
|
| 380 |
+
elif isinstance(x, interval):
|
| 381 |
+
start = np.arcsinh(x.start)
|
| 382 |
+
end = np.arcsinh(x.end)
|
| 383 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 384 |
+
else:
|
| 385 |
+
return NotImplementedError
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def atanh(x):
|
| 389 |
+
"""Evaluates the inverse hyperbolic tangent of an interval"""
|
| 390 |
+
np = import_module('numpy')
|
| 391 |
+
if isinstance(x, (int, float)):
|
| 392 |
+
#Outside the domain
|
| 393 |
+
if abs(x) >= 1:
|
| 394 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 395 |
+
else:
|
| 396 |
+
return interval(np.arctanh(x))
|
| 397 |
+
elif isinstance(x, interval):
|
| 398 |
+
#outside the domain
|
| 399 |
+
if x.is_valid is False or x.start >= 1 or x.end <= -1:
|
| 400 |
+
return interval(-np.inf, np.inf, is_valid=False)
|
| 401 |
+
#partly outside the domain
|
| 402 |
+
elif x.start <= -1 or x.end >= 1:
|
| 403 |
+
return interval(-np.inf, np.inf, is_valid=None)
|
| 404 |
+
else:
|
| 405 |
+
start = np.arctanh(x.start)
|
| 406 |
+
end = np.arctanh(x.end)
|
| 407 |
+
return interval(start, end, is_valid=x.is_valid)
|
| 408 |
+
else:
|
| 409 |
+
return NotImplementedError
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
#Three valued logic for interval plotting.
|
| 413 |
+
|
| 414 |
+
def And(*args):
|
| 415 |
+
"""Defines the three valued ``And`` behaviour for a 2-tuple of
|
| 416 |
+
three valued logic values"""
|
| 417 |
+
def reduce_and(cmp_intervala, cmp_intervalb):
|
| 418 |
+
if cmp_intervala[0] is False or cmp_intervalb[0] is False:
|
| 419 |
+
first = False
|
| 420 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 421 |
+
first = None
|
| 422 |
+
else:
|
| 423 |
+
first = True
|
| 424 |
+
if cmp_intervala[1] is False or cmp_intervalb[1] is False:
|
| 425 |
+
second = False
|
| 426 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 427 |
+
second = None
|
| 428 |
+
else:
|
| 429 |
+
second = True
|
| 430 |
+
return (first, second)
|
| 431 |
+
return reduce(reduce_and, args)
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
def Or(*args):
|
| 435 |
+
"""Defines the three valued ``Or`` behaviour for a 2-tuple of
|
| 436 |
+
three valued logic values"""
|
| 437 |
+
def reduce_or(cmp_intervala, cmp_intervalb):
|
| 438 |
+
if cmp_intervala[0] is True or cmp_intervalb[0] is True:
|
| 439 |
+
first = True
|
| 440 |
+
elif cmp_intervala[0] is None or cmp_intervalb[0] is None:
|
| 441 |
+
first = None
|
| 442 |
+
else:
|
| 443 |
+
first = False
|
| 444 |
+
|
| 445 |
+
if cmp_intervala[1] is True or cmp_intervalb[1] is True:
|
| 446 |
+
second = True
|
| 447 |
+
elif cmp_intervala[1] is None or cmp_intervalb[1] is None:
|
| 448 |
+
second = None
|
| 449 |
+
else:
|
| 450 |
+
second = False
|
| 451 |
+
return (first, second)
|
| 452 |
+
return reduce(reduce_or, args)
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/tests/__init__.py
ADDED
|
File without changes
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_interval_functions.py
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.external import import_module
|
| 2 |
+
from sympy.plotting.intervalmath import (
|
| 3 |
+
Abs, acos, acosh, And, asin, asinh, atan, atanh, ceil, cos, cosh,
|
| 4 |
+
exp, floor, imax, imin, interval, log, log10, Or, sin, sinh, sqrt,
|
| 5 |
+
tan, tanh,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
np = import_module('numpy')
|
| 9 |
+
if not np:
|
| 10 |
+
disabled = True
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
#requires Numpy. Hence included in interval_functions
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_interval_pow():
|
| 17 |
+
a = 2**interval(1, 2) == interval(2, 4)
|
| 18 |
+
assert a == (True, True)
|
| 19 |
+
a = interval(1, 2)**interval(1, 2) == interval(1, 4)
|
| 20 |
+
assert a == (True, True)
|
| 21 |
+
a = interval(-1, 1)**interval(0.5, 2)
|
| 22 |
+
assert a.is_valid is None
|
| 23 |
+
a = interval(-2, -1) ** interval(1, 2)
|
| 24 |
+
assert a.is_valid is False
|
| 25 |
+
a = interval(-2, -1) ** (1.0 / 2)
|
| 26 |
+
assert a.is_valid is False
|
| 27 |
+
a = interval(-1, 1)**(1.0 / 2)
|
| 28 |
+
assert a.is_valid is None
|
| 29 |
+
a = interval(-1, 1)**(1.0 / 3) == interval(-1, 1)
|
| 30 |
+
assert a == (True, True)
|
| 31 |
+
a = interval(-1, 1)**2 == interval(0, 1)
|
| 32 |
+
assert a == (True, True)
|
| 33 |
+
a = interval(-1, 1) ** (1.0 / 29) == interval(-1, 1)
|
| 34 |
+
assert a == (True, True)
|
| 35 |
+
a = -2**interval(1, 1) == interval(-2, -2)
|
| 36 |
+
assert a == (True, True)
|
| 37 |
+
|
| 38 |
+
a = interval(1, 2, is_valid=False)**2
|
| 39 |
+
assert a.is_valid is False
|
| 40 |
+
|
| 41 |
+
a = (-3)**interval(1, 2)
|
| 42 |
+
assert a.is_valid is False
|
| 43 |
+
a = (-4)**interval(0.5, 0.5)
|
| 44 |
+
assert a.is_valid is False
|
| 45 |
+
assert ((-3)**interval(1, 1) == interval(-3, -3)) == (True, True)
|
| 46 |
+
|
| 47 |
+
a = interval(8, 64)**(2.0 / 3)
|
| 48 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 49 |
+
assert abs(a.end - 16) < 1e-10
|
| 50 |
+
a = interval(-8, 64)**(2.0 / 3)
|
| 51 |
+
assert abs(a.start - 4) < 1e-10 # eps
|
| 52 |
+
assert abs(a.end - 16) < 1e-10
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def test_exp():
|
| 56 |
+
a = exp(interval(-np.inf, 0))
|
| 57 |
+
assert a.start == np.exp(-np.inf)
|
| 58 |
+
assert a.end == np.exp(0)
|
| 59 |
+
a = exp(interval(1, 2))
|
| 60 |
+
assert a.start == np.exp(1)
|
| 61 |
+
assert a.end == np.exp(2)
|
| 62 |
+
a = exp(1)
|
| 63 |
+
assert a.start == np.exp(1)
|
| 64 |
+
assert a.end == np.exp(1)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def test_log():
|
| 68 |
+
a = log(interval(1, 2))
|
| 69 |
+
assert a.start == 0
|
| 70 |
+
assert a.end == np.log(2)
|
| 71 |
+
a = log(interval(-1, 1))
|
| 72 |
+
assert a.is_valid is None
|
| 73 |
+
a = log(interval(-3, -1))
|
| 74 |
+
assert a.is_valid is False
|
| 75 |
+
a = log(-3)
|
| 76 |
+
assert a.is_valid is False
|
| 77 |
+
a = log(2)
|
| 78 |
+
assert a.start == np.log(2)
|
| 79 |
+
assert a.end == np.log(2)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_log10():
|
| 83 |
+
a = log10(interval(1, 2))
|
| 84 |
+
assert a.start == 0
|
| 85 |
+
assert a.end == np.log10(2)
|
| 86 |
+
a = log10(interval(-1, 1))
|
| 87 |
+
assert a.is_valid is None
|
| 88 |
+
a = log10(interval(-3, -1))
|
| 89 |
+
assert a.is_valid is False
|
| 90 |
+
a = log10(-3)
|
| 91 |
+
assert a.is_valid is False
|
| 92 |
+
a = log10(2)
|
| 93 |
+
assert a.start == np.log10(2)
|
| 94 |
+
assert a.end == np.log10(2)
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def test_atan():
|
| 98 |
+
a = atan(interval(0, 1))
|
| 99 |
+
assert a.start == np.arctan(0)
|
| 100 |
+
assert a.end == np.arctan(1)
|
| 101 |
+
a = atan(1)
|
| 102 |
+
assert a.start == np.arctan(1)
|
| 103 |
+
assert a.end == np.arctan(1)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_sin():
|
| 107 |
+
a = sin(interval(0, np.pi / 4))
|
| 108 |
+
assert a.start == np.sin(0)
|
| 109 |
+
assert a.end == np.sin(np.pi / 4)
|
| 110 |
+
|
| 111 |
+
a = sin(interval(-np.pi / 4, np.pi / 4))
|
| 112 |
+
assert a.start == np.sin(-np.pi / 4)
|
| 113 |
+
assert a.end == np.sin(np.pi / 4)
|
| 114 |
+
|
| 115 |
+
a = sin(interval(np.pi / 4, 3 * np.pi / 4))
|
| 116 |
+
assert a.start == np.sin(np.pi / 4)
|
| 117 |
+
assert a.end == 1
|
| 118 |
+
|
| 119 |
+
a = sin(interval(7 * np.pi / 6, 7 * np.pi / 4))
|
| 120 |
+
assert a.start == -1
|
| 121 |
+
assert a.end == np.sin(7 * np.pi / 6)
|
| 122 |
+
|
| 123 |
+
a = sin(interval(0, 3 * np.pi))
|
| 124 |
+
assert a.start == -1
|
| 125 |
+
assert a.end == 1
|
| 126 |
+
|
| 127 |
+
a = sin(interval(np.pi / 3, 7 * np.pi / 4))
|
| 128 |
+
assert a.start == -1
|
| 129 |
+
assert a.end == 1
|
| 130 |
+
|
| 131 |
+
a = sin(np.pi / 4)
|
| 132 |
+
assert a.start == np.sin(np.pi / 4)
|
| 133 |
+
assert a.end == np.sin(np.pi / 4)
|
| 134 |
+
|
| 135 |
+
a = sin(interval(1, 2, is_valid=False))
|
| 136 |
+
assert a.is_valid is False
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_cos():
|
| 140 |
+
a = cos(interval(0, np.pi / 4))
|
| 141 |
+
assert a.start == np.cos(np.pi / 4)
|
| 142 |
+
assert a.end == 1
|
| 143 |
+
|
| 144 |
+
a = cos(interval(-np.pi / 4, np.pi / 4))
|
| 145 |
+
assert a.start == np.cos(-np.pi / 4)
|
| 146 |
+
assert a.end == 1
|
| 147 |
+
|
| 148 |
+
a = cos(interval(np.pi / 4, 3 * np.pi / 4))
|
| 149 |
+
assert a.start == np.cos(3 * np.pi / 4)
|
| 150 |
+
assert a.end == np.cos(np.pi / 4)
|
| 151 |
+
|
| 152 |
+
a = cos(interval(3 * np.pi / 4, 5 * np.pi / 4))
|
| 153 |
+
assert a.start == -1
|
| 154 |
+
assert a.end == np.cos(3 * np.pi / 4)
|
| 155 |
+
|
| 156 |
+
a = cos(interval(0, 3 * np.pi))
|
| 157 |
+
assert a.start == -1
|
| 158 |
+
assert a.end == 1
|
| 159 |
+
|
| 160 |
+
a = cos(interval(- np.pi / 3, 5 * np.pi / 4))
|
| 161 |
+
assert a.start == -1
|
| 162 |
+
assert a.end == 1
|
| 163 |
+
|
| 164 |
+
a = cos(interval(1, 2, is_valid=False))
|
| 165 |
+
assert a.is_valid is False
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_tan():
|
| 169 |
+
a = tan(interval(0, np.pi / 4))
|
| 170 |
+
assert a.start == 0
|
| 171 |
+
# must match lib_interval definition of tan:
|
| 172 |
+
assert a.end == np.sin(np.pi / 4)/np.cos(np.pi / 4)
|
| 173 |
+
|
| 174 |
+
a = tan(interval(np.pi / 4, 3 * np.pi / 4))
|
| 175 |
+
#discontinuity
|
| 176 |
+
assert a.is_valid is None
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_sqrt():
|
| 180 |
+
a = sqrt(interval(1, 4))
|
| 181 |
+
assert a.start == 1
|
| 182 |
+
assert a.end == 2
|
| 183 |
+
|
| 184 |
+
a = sqrt(interval(0.01, 1))
|
| 185 |
+
assert a.start == np.sqrt(0.01)
|
| 186 |
+
assert a.end == 1
|
| 187 |
+
|
| 188 |
+
a = sqrt(interval(-1, 1))
|
| 189 |
+
assert a.is_valid is None
|
| 190 |
+
|
| 191 |
+
a = sqrt(interval(-3, -1))
|
| 192 |
+
assert a.is_valid is False
|
| 193 |
+
|
| 194 |
+
a = sqrt(4)
|
| 195 |
+
assert (a == interval(2, 2)) == (True, True)
|
| 196 |
+
|
| 197 |
+
a = sqrt(-3)
|
| 198 |
+
assert a.is_valid is False
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def test_imin():
|
| 202 |
+
a = imin(interval(1, 3), interval(2, 5), interval(-1, 3))
|
| 203 |
+
assert a.start == -1
|
| 204 |
+
assert a.end == 3
|
| 205 |
+
|
| 206 |
+
a = imin(-2, interval(1, 4))
|
| 207 |
+
assert a.start == -2
|
| 208 |
+
assert a.end == -2
|
| 209 |
+
|
| 210 |
+
a = imin(5, interval(3, 4), interval(-2, 2, is_valid=False))
|
| 211 |
+
assert a.start == 3
|
| 212 |
+
assert a.end == 4
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
def test_imax():
|
| 216 |
+
a = imax(interval(-2, 2), interval(2, 7), interval(-3, 9))
|
| 217 |
+
assert a.start == 2
|
| 218 |
+
assert a.end == 9
|
| 219 |
+
|
| 220 |
+
a = imax(8, interval(1, 4))
|
| 221 |
+
assert a.start == 8
|
| 222 |
+
assert a.end == 8
|
| 223 |
+
|
| 224 |
+
a = imax(interval(1, 2), interval(3, 4), interval(-2, 2, is_valid=False))
|
| 225 |
+
assert a.start == 3
|
| 226 |
+
assert a.end == 4
|
| 227 |
+
|
| 228 |
+
|
| 229 |
+
def test_sinh():
|
| 230 |
+
a = sinh(interval(-1, 1))
|
| 231 |
+
assert a.start == np.sinh(-1)
|
| 232 |
+
assert a.end == np.sinh(1)
|
| 233 |
+
|
| 234 |
+
a = sinh(1)
|
| 235 |
+
assert a.start == np.sinh(1)
|
| 236 |
+
assert a.end == np.sinh(1)
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def test_cosh():
|
| 240 |
+
a = cosh(interval(1, 2))
|
| 241 |
+
assert a.start == np.cosh(1)
|
| 242 |
+
assert a.end == np.cosh(2)
|
| 243 |
+
a = cosh(interval(-2, -1))
|
| 244 |
+
assert a.start == np.cosh(-1)
|
| 245 |
+
assert a.end == np.cosh(-2)
|
| 246 |
+
|
| 247 |
+
a = cosh(interval(-2, 1))
|
| 248 |
+
assert a.start == 1
|
| 249 |
+
assert a.end == np.cosh(-2)
|
| 250 |
+
|
| 251 |
+
a = cosh(1)
|
| 252 |
+
assert a.start == np.cosh(1)
|
| 253 |
+
assert a.end == np.cosh(1)
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def test_tanh():
|
| 257 |
+
a = tanh(interval(-3, 3))
|
| 258 |
+
assert a.start == np.tanh(-3)
|
| 259 |
+
assert a.end == np.tanh(3)
|
| 260 |
+
|
| 261 |
+
a = tanh(3)
|
| 262 |
+
assert a.start == np.tanh(3)
|
| 263 |
+
assert a.end == np.tanh(3)
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
def test_asin():
|
| 267 |
+
a = asin(interval(-0.5, 0.5))
|
| 268 |
+
assert a.start == np.arcsin(-0.5)
|
| 269 |
+
assert a.end == np.arcsin(0.5)
|
| 270 |
+
|
| 271 |
+
a = asin(interval(-1.5, 1.5))
|
| 272 |
+
assert a.is_valid is None
|
| 273 |
+
a = asin(interval(-2, -1.5))
|
| 274 |
+
assert a.is_valid is False
|
| 275 |
+
|
| 276 |
+
a = asin(interval(0, 2))
|
| 277 |
+
assert a.is_valid is None
|
| 278 |
+
|
| 279 |
+
a = asin(interval(2, 5))
|
| 280 |
+
assert a.is_valid is False
|
| 281 |
+
|
| 282 |
+
a = asin(0.5)
|
| 283 |
+
assert a.start == np.arcsin(0.5)
|
| 284 |
+
assert a.end == np.arcsin(0.5)
|
| 285 |
+
|
| 286 |
+
a = asin(1.5)
|
| 287 |
+
assert a.is_valid is False
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def test_acos():
|
| 291 |
+
a = acos(interval(-0.5, 0.5))
|
| 292 |
+
assert a.start == np.arccos(0.5)
|
| 293 |
+
assert a.end == np.arccos(-0.5)
|
| 294 |
+
|
| 295 |
+
a = acos(interval(-1.5, 1.5))
|
| 296 |
+
assert a.is_valid is None
|
| 297 |
+
a = acos(interval(-2, -1.5))
|
| 298 |
+
assert a.is_valid is False
|
| 299 |
+
|
| 300 |
+
a = acos(interval(0, 2))
|
| 301 |
+
assert a.is_valid is None
|
| 302 |
+
|
| 303 |
+
a = acos(interval(2, 5))
|
| 304 |
+
assert a.is_valid is False
|
| 305 |
+
|
| 306 |
+
a = acos(0.5)
|
| 307 |
+
assert a.start == np.arccos(0.5)
|
| 308 |
+
assert a.end == np.arccos(0.5)
|
| 309 |
+
|
| 310 |
+
a = acos(1.5)
|
| 311 |
+
assert a.is_valid is False
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def test_ceil():
|
| 315 |
+
a = ceil(interval(0.2, 0.5))
|
| 316 |
+
assert a.start == 1
|
| 317 |
+
assert a.end == 1
|
| 318 |
+
|
| 319 |
+
a = ceil(interval(0.5, 1.5))
|
| 320 |
+
assert a.start == 1
|
| 321 |
+
assert a.end == 2
|
| 322 |
+
assert a.is_valid is None
|
| 323 |
+
|
| 324 |
+
a = ceil(interval(-5, 5))
|
| 325 |
+
assert a.is_valid is None
|
| 326 |
+
|
| 327 |
+
a = ceil(5.4)
|
| 328 |
+
assert a.start == 6
|
| 329 |
+
assert a.end == 6
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
def test_floor():
|
| 333 |
+
a = floor(interval(0.2, 0.5))
|
| 334 |
+
assert a.start == 0
|
| 335 |
+
assert a.end == 0
|
| 336 |
+
|
| 337 |
+
a = floor(interval(0.5, 1.5))
|
| 338 |
+
assert a.start == 0
|
| 339 |
+
assert a.end == 1
|
| 340 |
+
assert a.is_valid is None
|
| 341 |
+
|
| 342 |
+
a = floor(interval(-5, 5))
|
| 343 |
+
assert a.is_valid is None
|
| 344 |
+
|
| 345 |
+
a = floor(5.4)
|
| 346 |
+
assert a.start == 5
|
| 347 |
+
assert a.end == 5
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def test_asinh():
|
| 351 |
+
a = asinh(interval(1, 2))
|
| 352 |
+
assert a.start == np.arcsinh(1)
|
| 353 |
+
assert a.end == np.arcsinh(2)
|
| 354 |
+
|
| 355 |
+
a = asinh(0.5)
|
| 356 |
+
assert a.start == np.arcsinh(0.5)
|
| 357 |
+
assert a.end == np.arcsinh(0.5)
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
def test_acosh():
|
| 361 |
+
a = acosh(interval(3, 5))
|
| 362 |
+
assert a.start == np.arccosh(3)
|
| 363 |
+
assert a.end == np.arccosh(5)
|
| 364 |
+
|
| 365 |
+
a = acosh(interval(0, 3))
|
| 366 |
+
assert a.is_valid is None
|
| 367 |
+
a = acosh(interval(-3, 0.5))
|
| 368 |
+
assert a.is_valid is False
|
| 369 |
+
|
| 370 |
+
a = acosh(0.5)
|
| 371 |
+
assert a.is_valid is False
|
| 372 |
+
|
| 373 |
+
a = acosh(2)
|
| 374 |
+
assert a.start == np.arccosh(2)
|
| 375 |
+
assert a.end == np.arccosh(2)
|
| 376 |
+
|
| 377 |
+
|
| 378 |
+
def test_atanh():
|
| 379 |
+
a = atanh(interval(-0.5, 0.5))
|
| 380 |
+
assert a.start == np.arctanh(-0.5)
|
| 381 |
+
assert a.end == np.arctanh(0.5)
|
| 382 |
+
|
| 383 |
+
a = atanh(interval(0, 3))
|
| 384 |
+
assert a.is_valid is None
|
| 385 |
+
|
| 386 |
+
a = atanh(interval(-3, -2))
|
| 387 |
+
assert a.is_valid is False
|
| 388 |
+
|
| 389 |
+
a = atanh(0.5)
|
| 390 |
+
assert a.start == np.arctanh(0.5)
|
| 391 |
+
assert a.end == np.arctanh(0.5)
|
| 392 |
+
|
| 393 |
+
a = atanh(1.5)
|
| 394 |
+
assert a.is_valid is False
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def test_Abs():
|
| 398 |
+
assert (Abs(interval(-0.5, 0.5)) == interval(0, 0.5)) == (True, True)
|
| 399 |
+
assert (Abs(interval(-3, -2)) == interval(2, 3)) == (True, True)
|
| 400 |
+
assert (Abs(-3) == interval(3, 3)) == (True, True)
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def test_And():
|
| 404 |
+
args = [(True, True), (True, False), (True, None)]
|
| 405 |
+
assert And(*args) == (True, False)
|
| 406 |
+
|
| 407 |
+
args = [(False, True), (None, None), (True, True)]
|
| 408 |
+
assert And(*args) == (False, None)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def test_Or():
|
| 412 |
+
args = [(True, True), (True, False), (False, None)]
|
| 413 |
+
assert Or(*args) == (True, True)
|
| 414 |
+
args = [(None, None), (False, None), (False, False)]
|
| 415 |
+
assert Or(*args) == (None, None)
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_interval_membership.py
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.symbol import Symbol
|
| 2 |
+
from sympy.plotting.intervalmath import interval
|
| 3 |
+
from sympy.plotting.intervalmath.interval_membership import intervalMembership
|
| 4 |
+
from sympy.plotting.experimental_lambdify import experimental_lambdify
|
| 5 |
+
from sympy.testing.pytest import raises
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_creation():
|
| 9 |
+
assert intervalMembership(True, True)
|
| 10 |
+
raises(TypeError, lambda: intervalMembership(True))
|
| 11 |
+
raises(TypeError, lambda: intervalMembership(True, True, True))
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_getitem():
|
| 15 |
+
a = intervalMembership(True, False)
|
| 16 |
+
assert a[0] is True
|
| 17 |
+
assert a[1] is False
|
| 18 |
+
raises(IndexError, lambda: a[2])
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_str():
|
| 22 |
+
a = intervalMembership(True, False)
|
| 23 |
+
assert str(a) == 'intervalMembership(True, False)'
|
| 24 |
+
assert repr(a) == 'intervalMembership(True, False)'
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_equivalence():
|
| 28 |
+
a = intervalMembership(True, True)
|
| 29 |
+
b = intervalMembership(True, False)
|
| 30 |
+
assert (a == b) is False
|
| 31 |
+
assert (a != b) is True
|
| 32 |
+
|
| 33 |
+
a = intervalMembership(True, False)
|
| 34 |
+
b = intervalMembership(True, False)
|
| 35 |
+
assert (a == b) is True
|
| 36 |
+
assert (a != b) is False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_not():
|
| 40 |
+
x = Symbol('x')
|
| 41 |
+
|
| 42 |
+
r1 = x > -1
|
| 43 |
+
r2 = x <= -1
|
| 44 |
+
|
| 45 |
+
i = interval
|
| 46 |
+
|
| 47 |
+
f1 = experimental_lambdify((x,), r1)
|
| 48 |
+
f2 = experimental_lambdify((x,), r2)
|
| 49 |
+
|
| 50 |
+
tt = i(-0.1, 0.1, is_valid=True)
|
| 51 |
+
tn = i(-0.1, 0.1, is_valid=None)
|
| 52 |
+
tf = i(-0.1, 0.1, is_valid=False)
|
| 53 |
+
|
| 54 |
+
assert f1(tt) == ~f2(tt)
|
| 55 |
+
assert f1(tn) == ~f2(tn)
|
| 56 |
+
assert f1(tf) == ~f2(tf)
|
| 57 |
+
|
| 58 |
+
nt = i(0.9, 1.1, is_valid=True)
|
| 59 |
+
nn = i(0.9, 1.1, is_valid=None)
|
| 60 |
+
nf = i(0.9, 1.1, is_valid=False)
|
| 61 |
+
|
| 62 |
+
assert f1(nt) == ~f2(nt)
|
| 63 |
+
assert f1(nn) == ~f2(nn)
|
| 64 |
+
assert f1(nf) == ~f2(nf)
|
| 65 |
+
|
| 66 |
+
ft = i(1.9, 2.1, is_valid=True)
|
| 67 |
+
fn = i(1.9, 2.1, is_valid=None)
|
| 68 |
+
ff = i(1.9, 2.1, is_valid=False)
|
| 69 |
+
|
| 70 |
+
assert f1(ft) == ~f2(ft)
|
| 71 |
+
assert f1(fn) == ~f2(fn)
|
| 72 |
+
assert f1(ff) == ~f2(ff)
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_boolean():
|
| 76 |
+
# There can be 9*9 test cases in full mapping of the cartesian product.
|
| 77 |
+
# But we only consider 3*3 cases for simplicity.
|
| 78 |
+
s = [
|
| 79 |
+
intervalMembership(False, False),
|
| 80 |
+
intervalMembership(None, None),
|
| 81 |
+
intervalMembership(True, True)
|
| 82 |
+
]
|
| 83 |
+
|
| 84 |
+
# Reduced tests for 'And'
|
| 85 |
+
a1 = [
|
| 86 |
+
intervalMembership(False, False),
|
| 87 |
+
intervalMembership(False, False),
|
| 88 |
+
intervalMembership(False, False),
|
| 89 |
+
intervalMembership(False, False),
|
| 90 |
+
intervalMembership(None, None),
|
| 91 |
+
intervalMembership(None, None),
|
| 92 |
+
intervalMembership(False, False),
|
| 93 |
+
intervalMembership(None, None),
|
| 94 |
+
intervalMembership(True, True)
|
| 95 |
+
]
|
| 96 |
+
a1_iter = iter(a1)
|
| 97 |
+
for i in range(len(s)):
|
| 98 |
+
for j in range(len(s)):
|
| 99 |
+
assert s[i] & s[j] == next(a1_iter)
|
| 100 |
+
|
| 101 |
+
# Reduced tests for 'Or'
|
| 102 |
+
a1 = [
|
| 103 |
+
intervalMembership(False, False),
|
| 104 |
+
intervalMembership(None, False),
|
| 105 |
+
intervalMembership(True, False),
|
| 106 |
+
intervalMembership(None, False),
|
| 107 |
+
intervalMembership(None, None),
|
| 108 |
+
intervalMembership(True, None),
|
| 109 |
+
intervalMembership(True, False),
|
| 110 |
+
intervalMembership(True, None),
|
| 111 |
+
intervalMembership(True, True)
|
| 112 |
+
]
|
| 113 |
+
a1_iter = iter(a1)
|
| 114 |
+
for i in range(len(s)):
|
| 115 |
+
for j in range(len(s)):
|
| 116 |
+
assert s[i] | s[j] == next(a1_iter)
|
| 117 |
+
|
| 118 |
+
# Reduced tests for 'Xor'
|
| 119 |
+
a1 = [
|
| 120 |
+
intervalMembership(False, False),
|
| 121 |
+
intervalMembership(None, False),
|
| 122 |
+
intervalMembership(True, False),
|
| 123 |
+
intervalMembership(None, False),
|
| 124 |
+
intervalMembership(None, None),
|
| 125 |
+
intervalMembership(None, None),
|
| 126 |
+
intervalMembership(True, False),
|
| 127 |
+
intervalMembership(None, None),
|
| 128 |
+
intervalMembership(False, True)
|
| 129 |
+
]
|
| 130 |
+
a1_iter = iter(a1)
|
| 131 |
+
for i in range(len(s)):
|
| 132 |
+
for j in range(len(s)):
|
| 133 |
+
assert s[i] ^ s[j] == next(a1_iter)
|
| 134 |
+
|
| 135 |
+
# Reduced tests for 'Not'
|
| 136 |
+
a1 = [
|
| 137 |
+
intervalMembership(True, False),
|
| 138 |
+
intervalMembership(None, None),
|
| 139 |
+
intervalMembership(False, True)
|
| 140 |
+
]
|
| 141 |
+
a1_iter = iter(a1)
|
| 142 |
+
for i in range(len(s)):
|
| 143 |
+
assert ~s[i] == next(a1_iter)
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_boolean_errors():
|
| 147 |
+
a = intervalMembership(True, True)
|
| 148 |
+
raises(ValueError, lambda: a & 1)
|
| 149 |
+
raises(ValueError, lambda: a | 1)
|
| 150 |
+
raises(ValueError, lambda: a ^ 1)
|
.venv/Lib/site-packages/sympy/plotting/intervalmath/tests/test_intervalmath.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.plotting.intervalmath import interval
|
| 2 |
+
from sympy.testing.pytest import raises
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def test_interval():
|
| 6 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=True)) == (True, True)
|
| 7 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=False)) == (True, False)
|
| 8 |
+
assert (interval(1, 1) == interval(1, 1, is_valid=None)) == (True, None)
|
| 9 |
+
assert (interval(1, 1.5) == interval(1, 2)) == (None, True)
|
| 10 |
+
assert (interval(0, 1) == interval(2, 3)) == (False, True)
|
| 11 |
+
assert (interval(0, 1) == interval(1, 2)) == (None, True)
|
| 12 |
+
assert (interval(1, 2) != interval(1, 2)) == (False, True)
|
| 13 |
+
assert (interval(1, 3) != interval(2, 3)) == (None, True)
|
| 14 |
+
assert (interval(1, 3) != interval(-5, -3)) == (True, True)
|
| 15 |
+
assert (
|
| 16 |
+
interval(1, 3, is_valid=False) != interval(-5, -3)) == (True, False)
|
| 17 |
+
assert (interval(1, 3, is_valid=None) != interval(-5, 3)) == (None, None)
|
| 18 |
+
assert (interval(4, 4) != 4) == (False, True)
|
| 19 |
+
assert (interval(1, 1) == 1) == (True, True)
|
| 20 |
+
assert (interval(1, 3, is_valid=False) == interval(1, 3)) == (True, False)
|
| 21 |
+
assert (interval(1, 3, is_valid=None) == interval(1, 3)) == (True, None)
|
| 22 |
+
inter = interval(-5, 5)
|
| 23 |
+
assert (interval(inter) == interval(-5, 5)) == (True, True)
|
| 24 |
+
assert inter.width == 10
|
| 25 |
+
assert 0 in inter
|
| 26 |
+
assert -5 in inter
|
| 27 |
+
assert 5 in inter
|
| 28 |
+
assert interval(0, 3) in inter
|
| 29 |
+
assert interval(-6, 2) not in inter
|
| 30 |
+
assert -5.05 not in inter
|
| 31 |
+
assert 5.3 not in inter
|
| 32 |
+
interb = interval(-float('inf'), float('inf'))
|
| 33 |
+
assert 0 in inter
|
| 34 |
+
assert inter in interb
|
| 35 |
+
assert interval(0, float('inf')) in interb
|
| 36 |
+
assert interval(-float('inf'), 5) in interb
|
| 37 |
+
assert interval(-1e50, 1e50) in interb
|
| 38 |
+
assert (
|
| 39 |
+
-interval(-1, -2, is_valid=False) == interval(1, 2)) == (True, False)
|
| 40 |
+
raises(ValueError, lambda: interval(1, 2, 3))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_interval_add():
|
| 44 |
+
assert (interval(1, 2) + interval(2, 3) == interval(3, 5)) == (True, True)
|
| 45 |
+
assert (1 + interval(1, 2) == interval(2, 3)) == (True, True)
|
| 46 |
+
assert (interval(1, 2) + 1 == interval(2, 3)) == (True, True)
|
| 47 |
+
compare = (1 + interval(0, float('inf')) == interval(1, float('inf')))
|
| 48 |
+
assert compare == (True, True)
|
| 49 |
+
a = 1 + interval(2, 5, is_valid=False)
|
| 50 |
+
assert a.is_valid is False
|
| 51 |
+
a = 1 + interval(2, 5, is_valid=None)
|
| 52 |
+
assert a.is_valid is None
|
| 53 |
+
a = interval(2, 5, is_valid=False) + interval(3, 5, is_valid=None)
|
| 54 |
+
assert a.is_valid is False
|
| 55 |
+
a = interval(3, 5) + interval(-1, 1, is_valid=None)
|
| 56 |
+
assert a.is_valid is None
|
| 57 |
+
a = interval(2, 5, is_valid=False) + 1
|
| 58 |
+
assert a.is_valid is False
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_interval_sub():
|
| 62 |
+
assert (interval(1, 2) - interval(1, 5) == interval(-4, 1)) == (True, True)
|
| 63 |
+
assert (interval(1, 2) - 1 == interval(0, 1)) == (True, True)
|
| 64 |
+
assert (1 - interval(1, 2) == interval(-1, 0)) == (True, True)
|
| 65 |
+
a = 1 - interval(1, 2, is_valid=False)
|
| 66 |
+
assert a.is_valid is False
|
| 67 |
+
a = interval(1, 4, is_valid=None) - 1
|
| 68 |
+
assert a.is_valid is None
|
| 69 |
+
a = interval(1, 3, is_valid=False) - interval(1, 3)
|
| 70 |
+
assert a.is_valid is False
|
| 71 |
+
a = interval(1, 3, is_valid=None) - interval(1, 3)
|
| 72 |
+
assert a.is_valid is None
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_interval_inequality():
|
| 76 |
+
assert (interval(1, 2) < interval(3, 4)) == (True, True)
|
| 77 |
+
assert (interval(1, 2) < interval(2, 4)) == (None, True)
|
| 78 |
+
assert (interval(1, 2) < interval(-2, 0)) == (False, True)
|
| 79 |
+
assert (interval(1, 2) <= interval(2, 4)) == (True, True)
|
| 80 |
+
assert (interval(1, 2) <= interval(1.5, 6)) == (None, True)
|
| 81 |
+
assert (interval(2, 3) <= interval(1, 2)) == (None, True)
|
| 82 |
+
assert (interval(2, 3) <= interval(1, 1.5)) == (False, True)
|
| 83 |
+
assert (
|
| 84 |
+
interval(1, 2, is_valid=False) <= interval(-2, 0)) == (False, False)
|
| 85 |
+
assert (interval(1, 2, is_valid=None) <= interval(-2, 0)) == (False, None)
|
| 86 |
+
assert (interval(1, 2) <= 1.5) == (None, True)
|
| 87 |
+
assert (interval(1, 2) <= 3) == (True, True)
|
| 88 |
+
assert (interval(1, 2) <= 0) == (False, True)
|
| 89 |
+
assert (interval(5, 8) > interval(2, 3)) == (True, True)
|
| 90 |
+
assert (interval(2, 5) > interval(1, 3)) == (None, True)
|
| 91 |
+
assert (interval(2, 3) > interval(3.1, 5)) == (False, True)
|
| 92 |
+
|
| 93 |
+
assert (interval(-1, 1) == 0) == (None, True)
|
| 94 |
+
assert (interval(-1, 1) == 2) == (False, True)
|
| 95 |
+
assert (interval(-1, 1) != 0) == (None, True)
|
| 96 |
+
assert (interval(-1, 1) != 2) == (True, True)
|
| 97 |
+
|
| 98 |
+
assert (interval(3, 5) > 2) == (True, True)
|
| 99 |
+
assert (interval(3, 5) < 2) == (False, True)
|
| 100 |
+
assert (interval(1, 5) < 2) == (None, True)
|
| 101 |
+
assert (interval(1, 5) > 2) == (None, True)
|
| 102 |
+
assert (interval(0, 1) > 2) == (False, True)
|
| 103 |
+
assert (interval(1, 2) >= interval(0, 1)) == (True, True)
|
| 104 |
+
assert (interval(1, 2) >= interval(0, 1.5)) == (None, True)
|
| 105 |
+
assert (interval(1, 2) >= interval(3, 4)) == (False, True)
|
| 106 |
+
assert (interval(1, 2) >= 0) == (True, True)
|
| 107 |
+
assert (interval(1, 2) >= 1.2) == (None, True)
|
| 108 |
+
assert (interval(1, 2) >= 3) == (False, True)
|
| 109 |
+
assert (2 > interval(0, 1)) == (True, True)
|
| 110 |
+
a = interval(-1, 1, is_valid=False) < interval(2, 5, is_valid=None)
|
| 111 |
+
assert a == (True, False)
|
| 112 |
+
a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=False)
|
| 113 |
+
assert a == (True, False)
|
| 114 |
+
a = interval(-1, 1, is_valid=None) < interval(2, 5, is_valid=None)
|
| 115 |
+
assert a == (True, None)
|
| 116 |
+
a = interval(-1, 1, is_valid=False) > interval(-5, -2, is_valid=None)
|
| 117 |
+
assert a == (True, False)
|
| 118 |
+
a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=False)
|
| 119 |
+
assert a == (True, False)
|
| 120 |
+
a = interval(-1, 1, is_valid=None) > interval(-5, -2, is_valid=None)
|
| 121 |
+
assert a == (True, None)
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
def test_interval_mul():
|
| 125 |
+
assert (
|
| 126 |
+
interval(1, 5) * interval(2, 10) == interval(2, 50)) == (True, True)
|
| 127 |
+
a = interval(-1, 1) * interval(2, 10) == interval(-10, 10)
|
| 128 |
+
assert a == (True, True)
|
| 129 |
+
|
| 130 |
+
a = interval(-1, 1) * interval(-5, 3) == interval(-5, 5)
|
| 131 |
+
assert a == (True, True)
|
| 132 |
+
|
| 133 |
+
assert (interval(1, 3) * 2 == interval(2, 6)) == (True, True)
|
| 134 |
+
assert (3 * interval(-1, 2) == interval(-3, 6)) == (True, True)
|
| 135 |
+
|
| 136 |
+
a = 3 * interval(1, 2, is_valid=False)
|
| 137 |
+
assert a.is_valid is False
|
| 138 |
+
|
| 139 |
+
a = 3 * interval(1, 2, is_valid=None)
|
| 140 |
+
assert a.is_valid is None
|
| 141 |
+
|
| 142 |
+
a = interval(1, 5, is_valid=False) * interval(1, 2, is_valid=None)
|
| 143 |
+
assert a.is_valid is False
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def test_interval_div():
|
| 147 |
+
div = interval(1, 2, is_valid=False) / 3
|
| 148 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=False)
|
| 149 |
+
|
| 150 |
+
div = interval(1, 2, is_valid=None) / 3
|
| 151 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=None)
|
| 152 |
+
|
| 153 |
+
div = 3 / interval(1, 2, is_valid=None)
|
| 154 |
+
assert div == interval(-float('inf'), float('inf'), is_valid=None)
|
| 155 |
+
a = interval(1, 2) / 0
|
| 156 |
+
assert a.is_valid is False
|
| 157 |
+
a = interval(0.5, 1) / interval(-1, 0)
|
| 158 |
+
assert a.is_valid is None
|
| 159 |
+
a = interval(0, 1) / interval(0, 1)
|
| 160 |
+
assert a.is_valid is None
|
| 161 |
+
|
| 162 |
+
a = interval(-1, 1) / interval(-1, 1)
|
| 163 |
+
assert a.is_valid is None
|
| 164 |
+
|
| 165 |
+
a = interval(-1, 2) / interval(0.5, 1) == interval(-2.0, 4.0)
|
| 166 |
+
assert a == (True, True)
|
| 167 |
+
a = interval(0, 1) / interval(0.5, 1) == interval(0.0, 2.0)
|
| 168 |
+
assert a == (True, True)
|
| 169 |
+
a = interval(-1, 0) / interval(0.5, 1) == interval(-2.0, 0.0)
|
| 170 |
+
assert a == (True, True)
|
| 171 |
+
a = interval(-0.5, -0.25) / interval(0.5, 1) == interval(-1.0, -0.25)
|
| 172 |
+
assert a == (True, True)
|
| 173 |
+
a = interval(0.5, 1) / interval(0.5, 1) == interval(0.5, 2.0)
|
| 174 |
+
assert a == (True, True)
|
| 175 |
+
a = interval(0.5, 4) / interval(0.5, 1) == interval(0.5, 8.0)
|
| 176 |
+
assert a == (True, True)
|
| 177 |
+
a = interval(-1, -0.5) / interval(0.5, 1) == interval(-2.0, -0.5)
|
| 178 |
+
assert a == (True, True)
|
| 179 |
+
a = interval(-4, -0.5) / interval(0.5, 1) == interval(-8.0, -0.5)
|
| 180 |
+
assert a == (True, True)
|
| 181 |
+
a = interval(-1, 2) / interval(-2, -0.5) == interval(-4.0, 2.0)
|
| 182 |
+
assert a == (True, True)
|
| 183 |
+
a = interval(0, 1) / interval(-2, -0.5) == interval(-2.0, 0.0)
|
| 184 |
+
assert a == (True, True)
|
| 185 |
+
a = interval(-1, 0) / interval(-2, -0.5) == interval(0.0, 2.0)
|
| 186 |
+
assert a == (True, True)
|
| 187 |
+
a = interval(-0.5, -0.25) / interval(-2, -0.5) == interval(0.125, 1.0)
|
| 188 |
+
assert a == (True, True)
|
| 189 |
+
a = interval(0.5, 1) / interval(-2, -0.5) == interval(-2.0, -0.25)
|
| 190 |
+
assert a == (True, True)
|
| 191 |
+
a = interval(0.5, 4) / interval(-2, -0.5) == interval(-8.0, -0.25)
|
| 192 |
+
assert a == (True, True)
|
| 193 |
+
a = interval(-1, -0.5) / interval(-2, -0.5) == interval(0.25, 2.0)
|
| 194 |
+
assert a == (True, True)
|
| 195 |
+
a = interval(-4, -0.5) / interval(-2, -0.5) == interval(0.25, 8.0)
|
| 196 |
+
assert a == (True, True)
|
| 197 |
+
a = interval(-5, 5, is_valid=False) / 2
|
| 198 |
+
assert a.is_valid is False
|
| 199 |
+
|
| 200 |
+
def test_hashable():
|
| 201 |
+
'''
|
| 202 |
+
test that interval objects are hashable.
|
| 203 |
+
this is required in order to be able to put them into the cache, which
|
| 204 |
+
appears to be necessary for plotting in py3k. For details, see:
|
| 205 |
+
|
| 206 |
+
https://github.com/sympy/sympy/pull/2101
|
| 207 |
+
https://github.com/sympy/sympy/issues/6533
|
| 208 |
+
'''
|
| 209 |
+
hash(interval(1, 1))
|
| 210 |
+
hash(interval(1, 1, is_valid=True))
|
| 211 |
+
hash(interval(-4, -0.5))
|
| 212 |
+
hash(interval(-2, -0.5))
|
| 213 |
+
hash(interval(0.25, 8.0))
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/__init__.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Plotting module that can plot 2D and 3D functions
|
| 2 |
+
"""
|
| 3 |
+
|
| 4 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 5 |
+
|
| 6 |
+
@doctest_depends_on(modules=('pyglet',))
|
| 7 |
+
def PygletPlot(*args, **kwargs):
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
Plot Examples
|
| 11 |
+
=============
|
| 12 |
+
|
| 13 |
+
See examples/advanced/pyglet_plotting.py for many more examples.
|
| 14 |
+
|
| 15 |
+
>>> from sympy.plotting.pygletplot import PygletPlot as Plot
|
| 16 |
+
>>> from sympy.abc import x, y, z
|
| 17 |
+
|
| 18 |
+
>>> Plot(x*y**3-y*x**3)
|
| 19 |
+
[0]: -x**3*y + x*y**3, 'mode=cartesian'
|
| 20 |
+
|
| 21 |
+
>>> p = Plot()
|
| 22 |
+
>>> p[1] = x*y
|
| 23 |
+
>>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4)
|
| 24 |
+
|
| 25 |
+
>>> p = Plot()
|
| 26 |
+
>>> p[1] = x**2+y**2
|
| 27 |
+
>>> p[2] = -x**2-y**2
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
Variable Intervals
|
| 31 |
+
==================
|
| 32 |
+
|
| 33 |
+
The basic format is [var, min, max, steps], but the
|
| 34 |
+
syntax is flexible and arguments left out are taken
|
| 35 |
+
from the defaults for the current coordinate mode:
|
| 36 |
+
|
| 37 |
+
>>> Plot(x**2) # implies [x,-5,5,100]
|
| 38 |
+
[0]: x**2, 'mode=cartesian'
|
| 39 |
+
|
| 40 |
+
>>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40]
|
| 41 |
+
[0]: x**2, 'mode=cartesian'
|
| 42 |
+
>>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100]
|
| 43 |
+
[0]: x**2 - y**2, 'mode=cartesian'
|
| 44 |
+
>>> Plot(x**2, [x,-13,13,100])
|
| 45 |
+
[0]: x**2, 'mode=cartesian'
|
| 46 |
+
>>> Plot(x**2, [-13,13]) # [x,-13,13,100]
|
| 47 |
+
[0]: x**2, 'mode=cartesian'
|
| 48 |
+
>>> Plot(x**2, [x,-13,13]) # [x,-13,13,100]
|
| 49 |
+
[0]: x**2, 'mode=cartesian'
|
| 50 |
+
>>> Plot(1*x, [], [x], mode='cylindrical')
|
| 51 |
+
... # [unbound_theta,0,2*Pi,40], [x,-1,1,20]
|
| 52 |
+
[0]: x, 'mode=cartesian'
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
Coordinate Modes
|
| 56 |
+
================
|
| 57 |
+
|
| 58 |
+
Plot supports several curvilinear coordinate modes, and
|
| 59 |
+
they independent for each plotted function. You can specify
|
| 60 |
+
a coordinate mode explicitly with the 'mode' named argument,
|
| 61 |
+
but it can be automatically determined for Cartesian or
|
| 62 |
+
parametric plots, and therefore must only be specified for
|
| 63 |
+
polar, cylindrical, and spherical modes.
|
| 64 |
+
|
| 65 |
+
Specifically, Plot(function arguments) and Plot[n] =
|
| 66 |
+
(function arguments) will interpret your arguments as a
|
| 67 |
+
Cartesian plot if you provide one function and a parametric
|
| 68 |
+
plot if you provide two or three functions. Similarly, the
|
| 69 |
+
arguments will be interpreted as a curve if one variable is
|
| 70 |
+
used, and a surface if two are used.
|
| 71 |
+
|
| 72 |
+
Supported mode names by number of variables:
|
| 73 |
+
|
| 74 |
+
1: parametric, cartesian, polar
|
| 75 |
+
2: parametric, cartesian, cylindrical = polar, spherical
|
| 76 |
+
|
| 77 |
+
>>> Plot(1, mode='spherical')
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
Calculator-like Interface
|
| 81 |
+
=========================
|
| 82 |
+
|
| 83 |
+
>>> p = Plot(visible=False)
|
| 84 |
+
>>> f = x**2
|
| 85 |
+
>>> p[1] = f
|
| 86 |
+
>>> p[2] = f.diff(x)
|
| 87 |
+
>>> p[3] = f.diff(x).diff(x)
|
| 88 |
+
>>> p
|
| 89 |
+
[1]: x**2, 'mode=cartesian'
|
| 90 |
+
[2]: 2*x, 'mode=cartesian'
|
| 91 |
+
[3]: 2, 'mode=cartesian'
|
| 92 |
+
>>> p.show()
|
| 93 |
+
>>> p.clear()
|
| 94 |
+
>>> p
|
| 95 |
+
<blank plot>
|
| 96 |
+
>>> p[1] = x**2+y**2
|
| 97 |
+
>>> p[1].style = 'solid'
|
| 98 |
+
>>> p[2] = -x**2-y**2
|
| 99 |
+
>>> p[2].style = 'wireframe'
|
| 100 |
+
>>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4)
|
| 101 |
+
>>> p[1].style = 'both'
|
| 102 |
+
>>> p[2].style = 'both'
|
| 103 |
+
>>> p.close()
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
Plot Window Keyboard Controls
|
| 107 |
+
=============================
|
| 108 |
+
|
| 109 |
+
Screen Rotation:
|
| 110 |
+
X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2
|
| 111 |
+
Z axis Q,E, Numpad 7,9
|
| 112 |
+
|
| 113 |
+
Model Rotation:
|
| 114 |
+
Z axis Z,C, Numpad 1,3
|
| 115 |
+
|
| 116 |
+
Zoom: R,F, PgUp,PgDn, Numpad +,-
|
| 117 |
+
|
| 118 |
+
Reset Camera: X, Numpad 5
|
| 119 |
+
|
| 120 |
+
Camera Presets:
|
| 121 |
+
XY F1
|
| 122 |
+
XZ F2
|
| 123 |
+
YZ F3
|
| 124 |
+
Perspective F4
|
| 125 |
+
|
| 126 |
+
Sensitivity Modifier: SHIFT
|
| 127 |
+
|
| 128 |
+
Axes Toggle:
|
| 129 |
+
Visible F5
|
| 130 |
+
Colors F6
|
| 131 |
+
|
| 132 |
+
Close Window: ESCAPE
|
| 133 |
+
|
| 134 |
+
=============================
|
| 135 |
+
"""
|
| 136 |
+
|
| 137 |
+
from sympy.plotting.pygletplot.plot import PygletPlot
|
| 138 |
+
return PygletPlot(*args, **kwargs)
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/__pycache__/__init__.cpython-39.pyc
ADDED
|
Binary file (4 kB). View file
|
|
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/color_scheme.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.basic import Basic
|
| 2 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 3 |
+
from sympy.utilities.lambdify import lambdify
|
| 4 |
+
from .util import interpolate, rinterpolate, create_bounds, update_bounds
|
| 5 |
+
from sympy.utilities.iterables import sift
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class ColorGradient:
|
| 9 |
+
colors = [0.4, 0.4, 0.4], [0.9, 0.9, 0.9]
|
| 10 |
+
intervals = 0.0, 1.0
|
| 11 |
+
|
| 12 |
+
def __init__(self, *args):
|
| 13 |
+
if len(args) == 2:
|
| 14 |
+
self.colors = list(args)
|
| 15 |
+
self.intervals = [0.0, 1.0]
|
| 16 |
+
elif len(args) > 0:
|
| 17 |
+
if len(args) % 2 != 0:
|
| 18 |
+
raise ValueError("len(args) should be even")
|
| 19 |
+
self.colors = [args[i] for i in range(1, len(args), 2)]
|
| 20 |
+
self.intervals = [args[i] for i in range(0, len(args), 2)]
|
| 21 |
+
assert len(self.colors) == len(self.intervals)
|
| 22 |
+
|
| 23 |
+
def copy(self):
|
| 24 |
+
c = ColorGradient()
|
| 25 |
+
c.colors = [e[::] for e in self.colors]
|
| 26 |
+
c.intervals = self.intervals[::]
|
| 27 |
+
return c
|
| 28 |
+
|
| 29 |
+
def _find_interval(self, v):
|
| 30 |
+
m = len(self.intervals)
|
| 31 |
+
i = 0
|
| 32 |
+
while i < m - 1 and self.intervals[i] <= v:
|
| 33 |
+
i += 1
|
| 34 |
+
return i
|
| 35 |
+
|
| 36 |
+
def _interpolate_axis(self, axis, v):
|
| 37 |
+
i = self._find_interval(v)
|
| 38 |
+
v = rinterpolate(self.intervals[i - 1], self.intervals[i], v)
|
| 39 |
+
return interpolate(self.colors[i - 1][axis], self.colors[i][axis], v)
|
| 40 |
+
|
| 41 |
+
def __call__(self, r, g, b):
|
| 42 |
+
c = self._interpolate_axis
|
| 43 |
+
return c(0, r), c(1, g), c(2, b)
|
| 44 |
+
|
| 45 |
+
default_color_schemes = {} # defined at the bottom of this file
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class ColorScheme:
|
| 49 |
+
|
| 50 |
+
def __init__(self, *args, **kwargs):
|
| 51 |
+
self.args = args
|
| 52 |
+
self.f, self.gradient = None, ColorGradient()
|
| 53 |
+
|
| 54 |
+
if len(args) == 1 and not isinstance(args[0], Basic) and callable(args[0]):
|
| 55 |
+
self.f = args[0]
|
| 56 |
+
elif len(args) == 1 and isinstance(args[0], str):
|
| 57 |
+
if args[0] in default_color_schemes:
|
| 58 |
+
cs = default_color_schemes[args[0]]
|
| 59 |
+
self.f, self.gradient = cs.f, cs.gradient.copy()
|
| 60 |
+
else:
|
| 61 |
+
self.f = lambdify('x,y,z,u,v', args[0])
|
| 62 |
+
else:
|
| 63 |
+
self.f, self.gradient = self._interpret_args(args)
|
| 64 |
+
self._test_color_function()
|
| 65 |
+
if not isinstance(self.gradient, ColorGradient):
|
| 66 |
+
raise ValueError("Color gradient not properly initialized. "
|
| 67 |
+
"(Not a ColorGradient instance.)")
|
| 68 |
+
|
| 69 |
+
def _interpret_args(self, args):
|
| 70 |
+
f, gradient = None, self.gradient
|
| 71 |
+
atoms, lists = self._sort_args(args)
|
| 72 |
+
s = self._pop_symbol_list(lists)
|
| 73 |
+
s = self._fill_in_vars(s)
|
| 74 |
+
|
| 75 |
+
# prepare the error message for lambdification failure
|
| 76 |
+
f_str = ', '.join(str(fa) for fa in atoms)
|
| 77 |
+
s_str = (str(sa) for sa in s)
|
| 78 |
+
s_str = ', '.join(sa for sa in s_str if sa.find('unbound') < 0)
|
| 79 |
+
f_error = ValueError("Could not interpret arguments "
|
| 80 |
+
"%s as functions of %s." % (f_str, s_str))
|
| 81 |
+
|
| 82 |
+
# try to lambdify args
|
| 83 |
+
if len(atoms) == 1:
|
| 84 |
+
fv = atoms[0]
|
| 85 |
+
try:
|
| 86 |
+
f = lambdify(s, [fv, fv, fv])
|
| 87 |
+
except TypeError:
|
| 88 |
+
raise f_error
|
| 89 |
+
|
| 90 |
+
elif len(atoms) == 3:
|
| 91 |
+
fr, fg, fb = atoms
|
| 92 |
+
try:
|
| 93 |
+
f = lambdify(s, [fr, fg, fb])
|
| 94 |
+
except TypeError:
|
| 95 |
+
raise f_error
|
| 96 |
+
|
| 97 |
+
else:
|
| 98 |
+
raise ValueError("A ColorScheme must provide 1 or 3 "
|
| 99 |
+
"functions in x, y, z, u, and/or v.")
|
| 100 |
+
|
| 101 |
+
# try to intrepret any given color information
|
| 102 |
+
if len(lists) == 0:
|
| 103 |
+
gargs = []
|
| 104 |
+
|
| 105 |
+
elif len(lists) == 1:
|
| 106 |
+
gargs = lists[0]
|
| 107 |
+
|
| 108 |
+
elif len(lists) == 2:
|
| 109 |
+
try:
|
| 110 |
+
(r1, g1, b1), (r2, g2, b2) = lists
|
| 111 |
+
except TypeError:
|
| 112 |
+
raise ValueError("If two color arguments are given, "
|
| 113 |
+
"they must be given in the format "
|
| 114 |
+
"(r1, g1, b1), (r2, g2, b2).")
|
| 115 |
+
gargs = lists
|
| 116 |
+
|
| 117 |
+
elif len(lists) == 3:
|
| 118 |
+
try:
|
| 119 |
+
(r1, r2), (g1, g2), (b1, b2) = lists
|
| 120 |
+
except Exception:
|
| 121 |
+
raise ValueError("If three color arguments are given, "
|
| 122 |
+
"they must be given in the format "
|
| 123 |
+
"(r1, r2), (g1, g2), (b1, b2). To create "
|
| 124 |
+
"a multi-step gradient, use the syntax "
|
| 125 |
+
"[0, colorStart, step1, color1, ..., 1, "
|
| 126 |
+
"colorEnd].")
|
| 127 |
+
gargs = [[r1, g1, b1], [r2, g2, b2]]
|
| 128 |
+
|
| 129 |
+
else:
|
| 130 |
+
raise ValueError("Don't know what to do with collection "
|
| 131 |
+
"arguments %s." % (', '.join(str(l) for l in lists)))
|
| 132 |
+
|
| 133 |
+
if gargs:
|
| 134 |
+
try:
|
| 135 |
+
gradient = ColorGradient(*gargs)
|
| 136 |
+
except Exception as ex:
|
| 137 |
+
raise ValueError(("Could not initialize a gradient "
|
| 138 |
+
"with arguments %s. Inner "
|
| 139 |
+
"exception: %s") % (gargs, str(ex)))
|
| 140 |
+
|
| 141 |
+
return f, gradient
|
| 142 |
+
|
| 143 |
+
def _pop_symbol_list(self, lists):
|
| 144 |
+
symbol_lists = []
|
| 145 |
+
for l in lists:
|
| 146 |
+
mark = True
|
| 147 |
+
for s in l:
|
| 148 |
+
if s is not None and not isinstance(s, Symbol):
|
| 149 |
+
mark = False
|
| 150 |
+
break
|
| 151 |
+
if mark:
|
| 152 |
+
lists.remove(l)
|
| 153 |
+
symbol_lists.append(l)
|
| 154 |
+
if len(symbol_lists) == 1:
|
| 155 |
+
return symbol_lists[0]
|
| 156 |
+
elif len(symbol_lists) == 0:
|
| 157 |
+
return []
|
| 158 |
+
else:
|
| 159 |
+
raise ValueError("Only one list of Symbols "
|
| 160 |
+
"can be given for a color scheme.")
|
| 161 |
+
|
| 162 |
+
def _fill_in_vars(self, args):
|
| 163 |
+
defaults = symbols('x,y,z,u,v')
|
| 164 |
+
v_error = ValueError("Could not find what to plot.")
|
| 165 |
+
if len(args) == 0:
|
| 166 |
+
return defaults
|
| 167 |
+
if not isinstance(args, (tuple, list)):
|
| 168 |
+
raise v_error
|
| 169 |
+
if len(args) == 0:
|
| 170 |
+
return defaults
|
| 171 |
+
for s in args:
|
| 172 |
+
if s is not None and not isinstance(s, Symbol):
|
| 173 |
+
raise v_error
|
| 174 |
+
# when vars are given explicitly, any vars
|
| 175 |
+
# not given are marked 'unbound' as to not
|
| 176 |
+
# be accidentally used in an expression
|
| 177 |
+
vars = [Symbol('unbound%i' % (i)) for i in range(1, 6)]
|
| 178 |
+
# interpret as t
|
| 179 |
+
if len(args) == 1:
|
| 180 |
+
vars[3] = args[0]
|
| 181 |
+
# interpret as u,v
|
| 182 |
+
elif len(args) == 2:
|
| 183 |
+
if args[0] is not None:
|
| 184 |
+
vars[3] = args[0]
|
| 185 |
+
if args[1] is not None:
|
| 186 |
+
vars[4] = args[1]
|
| 187 |
+
# interpret as x,y,z
|
| 188 |
+
elif len(args) >= 3:
|
| 189 |
+
# allow some of x,y,z to be
|
| 190 |
+
# left unbound if not given
|
| 191 |
+
if args[0] is not None:
|
| 192 |
+
vars[0] = args[0]
|
| 193 |
+
if args[1] is not None:
|
| 194 |
+
vars[1] = args[1]
|
| 195 |
+
if args[2] is not None:
|
| 196 |
+
vars[2] = args[2]
|
| 197 |
+
# interpret the rest as t
|
| 198 |
+
if len(args) >= 4:
|
| 199 |
+
vars[3] = args[3]
|
| 200 |
+
# ...or u,v
|
| 201 |
+
if len(args) >= 5:
|
| 202 |
+
vars[4] = args[4]
|
| 203 |
+
return vars
|
| 204 |
+
|
| 205 |
+
def _sort_args(self, args):
|
| 206 |
+
lists, atoms = sift(args,
|
| 207 |
+
lambda a: isinstance(a, (tuple, list)), binary=True)
|
| 208 |
+
return atoms, lists
|
| 209 |
+
|
| 210 |
+
def _test_color_function(self):
|
| 211 |
+
if not callable(self.f):
|
| 212 |
+
raise ValueError("Color function is not callable.")
|
| 213 |
+
try:
|
| 214 |
+
result = self.f(0, 0, 0, 0, 0)
|
| 215 |
+
if len(result) != 3:
|
| 216 |
+
raise ValueError("length should be equal to 3")
|
| 217 |
+
except TypeError:
|
| 218 |
+
raise ValueError("Color function needs to accept x,y,z,u,v, "
|
| 219 |
+
"as arguments even if it doesn't use all of them.")
|
| 220 |
+
except AssertionError:
|
| 221 |
+
raise ValueError("Color function needs to return 3-tuple r,g,b.")
|
| 222 |
+
except Exception:
|
| 223 |
+
pass # color function probably not valid at 0,0,0,0,0
|
| 224 |
+
|
| 225 |
+
def __call__(self, x, y, z, u, v):
|
| 226 |
+
try:
|
| 227 |
+
return self.f(x, y, z, u, v)
|
| 228 |
+
except Exception:
|
| 229 |
+
return None
|
| 230 |
+
|
| 231 |
+
def apply_to_curve(self, verts, u_set, set_len=None, inc_pos=None):
|
| 232 |
+
"""
|
| 233 |
+
Apply this color scheme to a
|
| 234 |
+
set of vertices over a single
|
| 235 |
+
independent variable u.
|
| 236 |
+
"""
|
| 237 |
+
bounds = create_bounds()
|
| 238 |
+
cverts = []
|
| 239 |
+
if callable(set_len):
|
| 240 |
+
set_len(len(u_set)*2)
|
| 241 |
+
# calculate f() = r,g,b for each vert
|
| 242 |
+
# and find the min and max for r,g,b
|
| 243 |
+
for _u in range(len(u_set)):
|
| 244 |
+
if verts[_u] is None:
|
| 245 |
+
cverts.append(None)
|
| 246 |
+
else:
|
| 247 |
+
x, y, z = verts[_u]
|
| 248 |
+
u, v = u_set[_u], None
|
| 249 |
+
c = self(x, y, z, u, v)
|
| 250 |
+
if c is not None:
|
| 251 |
+
c = list(c)
|
| 252 |
+
update_bounds(bounds, c)
|
| 253 |
+
cverts.append(c)
|
| 254 |
+
if callable(inc_pos):
|
| 255 |
+
inc_pos()
|
| 256 |
+
# scale and apply gradient
|
| 257 |
+
for _u in range(len(u_set)):
|
| 258 |
+
if cverts[_u] is not None:
|
| 259 |
+
for _c in range(3):
|
| 260 |
+
# scale from [f_min, f_max] to [0,1]
|
| 261 |
+
cverts[_u][_c] = rinterpolate(bounds[_c][0], bounds[_c][1],
|
| 262 |
+
cverts[_u][_c])
|
| 263 |
+
# apply gradient
|
| 264 |
+
cverts[_u] = self.gradient(*cverts[_u])
|
| 265 |
+
if callable(inc_pos):
|
| 266 |
+
inc_pos()
|
| 267 |
+
return cverts
|
| 268 |
+
|
| 269 |
+
def apply_to_surface(self, verts, u_set, v_set, set_len=None, inc_pos=None):
|
| 270 |
+
"""
|
| 271 |
+
Apply this color scheme to a
|
| 272 |
+
set of vertices over two
|
| 273 |
+
independent variables u and v.
|
| 274 |
+
"""
|
| 275 |
+
bounds = create_bounds()
|
| 276 |
+
cverts = []
|
| 277 |
+
if callable(set_len):
|
| 278 |
+
set_len(len(u_set)*len(v_set)*2)
|
| 279 |
+
# calculate f() = r,g,b for each vert
|
| 280 |
+
# and find the min and max for r,g,b
|
| 281 |
+
for _u in range(len(u_set)):
|
| 282 |
+
column = []
|
| 283 |
+
for _v in range(len(v_set)):
|
| 284 |
+
if verts[_u][_v] is None:
|
| 285 |
+
column.append(None)
|
| 286 |
+
else:
|
| 287 |
+
x, y, z = verts[_u][_v]
|
| 288 |
+
u, v = u_set[_u], v_set[_v]
|
| 289 |
+
c = self(x, y, z, u, v)
|
| 290 |
+
if c is not None:
|
| 291 |
+
c = list(c)
|
| 292 |
+
update_bounds(bounds, c)
|
| 293 |
+
column.append(c)
|
| 294 |
+
if callable(inc_pos):
|
| 295 |
+
inc_pos()
|
| 296 |
+
cverts.append(column)
|
| 297 |
+
# scale and apply gradient
|
| 298 |
+
for _u in range(len(u_set)):
|
| 299 |
+
for _v in range(len(v_set)):
|
| 300 |
+
if cverts[_u][_v] is not None:
|
| 301 |
+
# scale from [f_min, f_max] to [0,1]
|
| 302 |
+
for _c in range(3):
|
| 303 |
+
cverts[_u][_v][_c] = rinterpolate(bounds[_c][0],
|
| 304 |
+
bounds[_c][1], cverts[_u][_v][_c])
|
| 305 |
+
# apply gradient
|
| 306 |
+
cverts[_u][_v] = self.gradient(*cverts[_u][_v])
|
| 307 |
+
if callable(inc_pos):
|
| 308 |
+
inc_pos()
|
| 309 |
+
return cverts
|
| 310 |
+
|
| 311 |
+
def str_base(self):
|
| 312 |
+
return ", ".join(str(a) for a in self.args)
|
| 313 |
+
|
| 314 |
+
def __repr__(self):
|
| 315 |
+
return "%s" % (self.str_base())
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
x, y, z, t, u, v = symbols('x,y,z,t,u,v')
|
| 319 |
+
|
| 320 |
+
default_color_schemes['rainbow'] = ColorScheme(z, y, x)
|
| 321 |
+
default_color_schemes['zfade'] = ColorScheme(z, (0.4, 0.4, 0.97),
|
| 322 |
+
(0.97, 0.4, 0.4), (None, None, z))
|
| 323 |
+
default_color_schemes['zfade3'] = ColorScheme(z, (None, None, z),
|
| 324 |
+
[0.00, (0.2, 0.2, 1.0),
|
| 325 |
+
0.35, (0.2, 0.8, 0.4),
|
| 326 |
+
0.50, (0.3, 0.9, 0.3),
|
| 327 |
+
0.65, (0.4, 0.8, 0.2),
|
| 328 |
+
1.00, (1.0, 0.2, 0.2)])
|
| 329 |
+
|
| 330 |
+
default_color_schemes['zfade4'] = ColorScheme(z, (None, None, z),
|
| 331 |
+
[0.0, (0.3, 0.3, 1.0),
|
| 332 |
+
0.30, (0.3, 1.0, 0.3),
|
| 333 |
+
0.55, (0.95, 1.0, 0.2),
|
| 334 |
+
0.65, (1.0, 0.95, 0.2),
|
| 335 |
+
0.85, (1.0, 0.7, 0.2),
|
| 336 |
+
1.0, (1.0, 0.3, 0.2)])
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/managed_window.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pyglet.window import Window
|
| 2 |
+
from pyglet.clock import Clock
|
| 3 |
+
|
| 4 |
+
from threading import Thread, Lock
|
| 5 |
+
|
| 6 |
+
gl_lock = Lock()
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ManagedWindow(Window):
|
| 10 |
+
"""
|
| 11 |
+
A pyglet window with an event loop which executes automatically
|
| 12 |
+
in a separate thread. Behavior is added by creating a subclass
|
| 13 |
+
which overrides setup, update, and/or draw.
|
| 14 |
+
"""
|
| 15 |
+
fps_limit = 30
|
| 16 |
+
default_win_args = {"width": 600,
|
| 17 |
+
"height": 500,
|
| 18 |
+
"vsync": False,
|
| 19 |
+
"resizable": True}
|
| 20 |
+
|
| 21 |
+
def __init__(self, **win_args):
|
| 22 |
+
"""
|
| 23 |
+
It is best not to override this function in the child
|
| 24 |
+
class, unless you need to take additional arguments.
|
| 25 |
+
Do any OpenGL initialization calls in setup().
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# check if this is run from the doctester
|
| 29 |
+
if win_args.get('runfromdoctester', False):
|
| 30 |
+
return
|
| 31 |
+
|
| 32 |
+
self.win_args = dict(self.default_win_args, **win_args)
|
| 33 |
+
self.Thread = Thread(target=self.__event_loop__)
|
| 34 |
+
self.Thread.start()
|
| 35 |
+
|
| 36 |
+
def __event_loop__(self, **win_args):
|
| 37 |
+
"""
|
| 38 |
+
The event loop thread function. Do not override or call
|
| 39 |
+
directly (it is called by __init__).
|
| 40 |
+
"""
|
| 41 |
+
gl_lock.acquire()
|
| 42 |
+
try:
|
| 43 |
+
try:
|
| 44 |
+
super().__init__(**self.win_args)
|
| 45 |
+
self.switch_to()
|
| 46 |
+
self.setup()
|
| 47 |
+
except Exception as e:
|
| 48 |
+
print("Window initialization failed: %s" % (str(e)))
|
| 49 |
+
self.has_exit = True
|
| 50 |
+
finally:
|
| 51 |
+
gl_lock.release()
|
| 52 |
+
|
| 53 |
+
clock = Clock()
|
| 54 |
+
clock.fps_limit = self.fps_limit
|
| 55 |
+
while not self.has_exit:
|
| 56 |
+
dt = clock.tick()
|
| 57 |
+
gl_lock.acquire()
|
| 58 |
+
try:
|
| 59 |
+
try:
|
| 60 |
+
self.switch_to()
|
| 61 |
+
self.dispatch_events()
|
| 62 |
+
self.clear()
|
| 63 |
+
self.update(dt)
|
| 64 |
+
self.draw()
|
| 65 |
+
self.flip()
|
| 66 |
+
except Exception as e:
|
| 67 |
+
print("Uncaught exception in event loop: %s" % str(e))
|
| 68 |
+
self.has_exit = True
|
| 69 |
+
finally:
|
| 70 |
+
gl_lock.release()
|
| 71 |
+
super().close()
|
| 72 |
+
|
| 73 |
+
def close(self):
|
| 74 |
+
"""
|
| 75 |
+
Closes the window.
|
| 76 |
+
"""
|
| 77 |
+
self.has_exit = True
|
| 78 |
+
|
| 79 |
+
def setup(self):
|
| 80 |
+
"""
|
| 81 |
+
Called once before the event loop begins.
|
| 82 |
+
Override this method in a child class. This
|
| 83 |
+
is the best place to put things like OpenGL
|
| 84 |
+
initialization calls.
|
| 85 |
+
"""
|
| 86 |
+
pass
|
| 87 |
+
|
| 88 |
+
def update(self, dt):
|
| 89 |
+
"""
|
| 90 |
+
Called before draw during each iteration of
|
| 91 |
+
the event loop. dt is the elapsed time in
|
| 92 |
+
seconds since the last update. OpenGL rendering
|
| 93 |
+
calls are best put in draw() rather than here.
|
| 94 |
+
"""
|
| 95 |
+
pass
|
| 96 |
+
|
| 97 |
+
def draw(self):
|
| 98 |
+
"""
|
| 99 |
+
Called after update during each iteration of
|
| 100 |
+
the event loop. Put OpenGL rendering calls
|
| 101 |
+
here.
|
| 102 |
+
"""
|
| 103 |
+
pass
|
| 104 |
+
|
| 105 |
+
if __name__ == '__main__':
|
| 106 |
+
ManagedWindow()
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot.py
ADDED
|
@@ -0,0 +1,464 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from threading import RLock
|
| 2 |
+
|
| 3 |
+
# it is sufficient to import "pyglet" here once
|
| 4 |
+
try:
|
| 5 |
+
import pyglet.gl as pgl
|
| 6 |
+
except ImportError:
|
| 7 |
+
raise ImportError("pyglet is required for plotting.\n "
|
| 8 |
+
"visit https://pyglet.org/")
|
| 9 |
+
|
| 10 |
+
from sympy.core.numbers import Integer
|
| 11 |
+
from sympy.external.gmpy import SYMPY_INTS
|
| 12 |
+
from sympy.geometry.entity import GeometryEntity
|
| 13 |
+
from sympy.plotting.pygletplot.plot_axes import PlotAxes
|
| 14 |
+
from sympy.plotting.pygletplot.plot_mode import PlotMode
|
| 15 |
+
from sympy.plotting.pygletplot.plot_object import PlotObject
|
| 16 |
+
from sympy.plotting.pygletplot.plot_window import PlotWindow
|
| 17 |
+
from sympy.plotting.pygletplot.util import parse_option_string
|
| 18 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 19 |
+
from sympy.utilities.iterables import is_sequence
|
| 20 |
+
|
| 21 |
+
from time import sleep
|
| 22 |
+
from os import getcwd, listdir
|
| 23 |
+
|
| 24 |
+
import ctypes
|
| 25 |
+
|
| 26 |
+
@doctest_depends_on(modules=('pyglet',))
|
| 27 |
+
class PygletPlot:
|
| 28 |
+
"""
|
| 29 |
+
Plot Examples
|
| 30 |
+
=============
|
| 31 |
+
|
| 32 |
+
See examples/advanced/pyglet_plotting.py for many more examples.
|
| 33 |
+
|
| 34 |
+
>>> from sympy.plotting.pygletplot import PygletPlot as Plot
|
| 35 |
+
>>> from sympy.abc import x, y, z
|
| 36 |
+
|
| 37 |
+
>>> Plot(x*y**3-y*x**3)
|
| 38 |
+
[0]: -x**3*y + x*y**3, 'mode=cartesian'
|
| 39 |
+
|
| 40 |
+
>>> p = Plot()
|
| 41 |
+
>>> p[1] = x*y
|
| 42 |
+
>>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4)
|
| 43 |
+
|
| 44 |
+
>>> p = Plot()
|
| 45 |
+
>>> p[1] = x**2+y**2
|
| 46 |
+
>>> p[2] = -x**2-y**2
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
Variable Intervals
|
| 50 |
+
==================
|
| 51 |
+
|
| 52 |
+
The basic format is [var, min, max, steps], but the
|
| 53 |
+
syntax is flexible and arguments left out are taken
|
| 54 |
+
from the defaults for the current coordinate mode:
|
| 55 |
+
|
| 56 |
+
>>> Plot(x**2) # implies [x,-5,5,100]
|
| 57 |
+
[0]: x**2, 'mode=cartesian'
|
| 58 |
+
>>> Plot(x**2, [], []) # [x,-1,1,40], [y,-1,1,40]
|
| 59 |
+
[0]: x**2, 'mode=cartesian'
|
| 60 |
+
>>> Plot(x**2-y**2, [100], [100]) # [x,-1,1,100], [y,-1,1,100]
|
| 61 |
+
[0]: x**2 - y**2, 'mode=cartesian'
|
| 62 |
+
>>> Plot(x**2, [x,-13,13,100])
|
| 63 |
+
[0]: x**2, 'mode=cartesian'
|
| 64 |
+
>>> Plot(x**2, [-13,13]) # [x,-13,13,100]
|
| 65 |
+
[0]: x**2, 'mode=cartesian'
|
| 66 |
+
>>> Plot(x**2, [x,-13,13]) # [x,-13,13,10]
|
| 67 |
+
[0]: x**2, 'mode=cartesian'
|
| 68 |
+
>>> Plot(1*x, [], [x], mode='cylindrical')
|
| 69 |
+
... # [unbound_theta,0,2*Pi,40], [x,-1,1,20]
|
| 70 |
+
[0]: x, 'mode=cartesian'
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
Coordinate Modes
|
| 74 |
+
================
|
| 75 |
+
|
| 76 |
+
Plot supports several curvilinear coordinate modes, and
|
| 77 |
+
they independent for each plotted function. You can specify
|
| 78 |
+
a coordinate mode explicitly with the 'mode' named argument,
|
| 79 |
+
but it can be automatically determined for Cartesian or
|
| 80 |
+
parametric plots, and therefore must only be specified for
|
| 81 |
+
polar, cylindrical, and spherical modes.
|
| 82 |
+
|
| 83 |
+
Specifically, Plot(function arguments) and Plot[n] =
|
| 84 |
+
(function arguments) will interpret your arguments as a
|
| 85 |
+
Cartesian plot if you provide one function and a parametric
|
| 86 |
+
plot if you provide two or three functions. Similarly, the
|
| 87 |
+
arguments will be interpreted as a curve if one variable is
|
| 88 |
+
used, and a surface if two are used.
|
| 89 |
+
|
| 90 |
+
Supported mode names by number of variables:
|
| 91 |
+
|
| 92 |
+
1: parametric, cartesian, polar
|
| 93 |
+
2: parametric, cartesian, cylindrical = polar, spherical
|
| 94 |
+
|
| 95 |
+
>>> Plot(1, mode='spherical')
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
Calculator-like Interface
|
| 99 |
+
=========================
|
| 100 |
+
|
| 101 |
+
>>> p = Plot(visible=False)
|
| 102 |
+
>>> f = x**2
|
| 103 |
+
>>> p[1] = f
|
| 104 |
+
>>> p[2] = f.diff(x)
|
| 105 |
+
>>> p[3] = f.diff(x).diff(x)
|
| 106 |
+
>>> p
|
| 107 |
+
[1]: x**2, 'mode=cartesian'
|
| 108 |
+
[2]: 2*x, 'mode=cartesian'
|
| 109 |
+
[3]: 2, 'mode=cartesian'
|
| 110 |
+
>>> p.show()
|
| 111 |
+
>>> p.clear()
|
| 112 |
+
>>> p
|
| 113 |
+
<blank plot>
|
| 114 |
+
>>> p[1] = x**2+y**2
|
| 115 |
+
>>> p[1].style = 'solid'
|
| 116 |
+
>>> p[2] = -x**2-y**2
|
| 117 |
+
>>> p[2].style = 'wireframe'
|
| 118 |
+
>>> p[1].color = z, (0.4,0.4,0.9), (0.9,0.4,0.4)
|
| 119 |
+
>>> p[1].style = 'both'
|
| 120 |
+
>>> p[2].style = 'both'
|
| 121 |
+
>>> p.close()
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
Plot Window Keyboard Controls
|
| 125 |
+
=============================
|
| 126 |
+
|
| 127 |
+
Screen Rotation:
|
| 128 |
+
X,Y axis Arrow Keys, A,S,D,W, Numpad 4,6,8,2
|
| 129 |
+
Z axis Q,E, Numpad 7,9
|
| 130 |
+
|
| 131 |
+
Model Rotation:
|
| 132 |
+
Z axis Z,C, Numpad 1,3
|
| 133 |
+
|
| 134 |
+
Zoom: R,F, PgUp,PgDn, Numpad +,-
|
| 135 |
+
|
| 136 |
+
Reset Camera: X, Numpad 5
|
| 137 |
+
|
| 138 |
+
Camera Presets:
|
| 139 |
+
XY F1
|
| 140 |
+
XZ F2
|
| 141 |
+
YZ F3
|
| 142 |
+
Perspective F4
|
| 143 |
+
|
| 144 |
+
Sensitivity Modifier: SHIFT
|
| 145 |
+
|
| 146 |
+
Axes Toggle:
|
| 147 |
+
Visible F5
|
| 148 |
+
Colors F6
|
| 149 |
+
|
| 150 |
+
Close Window: ESCAPE
|
| 151 |
+
|
| 152 |
+
=============================
|
| 153 |
+
|
| 154 |
+
"""
|
| 155 |
+
|
| 156 |
+
@doctest_depends_on(modules=('pyglet',))
|
| 157 |
+
def __init__(self, *fargs, **win_args):
|
| 158 |
+
"""
|
| 159 |
+
Positional Arguments
|
| 160 |
+
====================
|
| 161 |
+
|
| 162 |
+
Any given positional arguments are used to
|
| 163 |
+
initialize a plot function at index 1. In
|
| 164 |
+
other words...
|
| 165 |
+
|
| 166 |
+
>>> from sympy.plotting.pygletplot import PygletPlot as Plot
|
| 167 |
+
>>> from sympy.abc import x
|
| 168 |
+
>>> p = Plot(x**2, visible=False)
|
| 169 |
+
|
| 170 |
+
...is equivalent to...
|
| 171 |
+
|
| 172 |
+
>>> p = Plot(visible=False)
|
| 173 |
+
>>> p[1] = x**2
|
| 174 |
+
|
| 175 |
+
Note that in earlier versions of the plotting
|
| 176 |
+
module, you were able to specify multiple
|
| 177 |
+
functions in the initializer. This functionality
|
| 178 |
+
has been dropped in favor of better automatic
|
| 179 |
+
plot plot_mode detection.
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
Named Arguments
|
| 183 |
+
===============
|
| 184 |
+
|
| 185 |
+
axes
|
| 186 |
+
An option string of the form
|
| 187 |
+
"key1=value1; key2 = value2" which
|
| 188 |
+
can use the following options:
|
| 189 |
+
|
| 190 |
+
style = ordinate
|
| 191 |
+
none OR frame OR box OR ordinate
|
| 192 |
+
|
| 193 |
+
stride = 0.25
|
| 194 |
+
val OR (val_x, val_y, val_z)
|
| 195 |
+
|
| 196 |
+
overlay = True (draw on top of plot)
|
| 197 |
+
True OR False
|
| 198 |
+
|
| 199 |
+
colored = False (False uses Black,
|
| 200 |
+
True uses colors
|
| 201 |
+
R,G,B = X,Y,Z)
|
| 202 |
+
True OR False
|
| 203 |
+
|
| 204 |
+
label_axes = False (display axis names
|
| 205 |
+
at endpoints)
|
| 206 |
+
True OR False
|
| 207 |
+
|
| 208 |
+
visible = True (show immediately
|
| 209 |
+
True OR False
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
The following named arguments are passed as
|
| 213 |
+
arguments to window initialization:
|
| 214 |
+
|
| 215 |
+
antialiasing = True
|
| 216 |
+
True OR False
|
| 217 |
+
|
| 218 |
+
ortho = False
|
| 219 |
+
True OR False
|
| 220 |
+
|
| 221 |
+
invert_mouse_zoom = False
|
| 222 |
+
True OR False
|
| 223 |
+
|
| 224 |
+
"""
|
| 225 |
+
# Register the plot modes
|
| 226 |
+
from . import plot_modes # noqa
|
| 227 |
+
|
| 228 |
+
self._win_args = win_args
|
| 229 |
+
self._window = None
|
| 230 |
+
|
| 231 |
+
self._render_lock = RLock()
|
| 232 |
+
|
| 233 |
+
self._functions = {}
|
| 234 |
+
self._pobjects = []
|
| 235 |
+
self._screenshot = ScreenShot(self)
|
| 236 |
+
|
| 237 |
+
axe_options = parse_option_string(win_args.pop('axes', ''))
|
| 238 |
+
self.axes = PlotAxes(**axe_options)
|
| 239 |
+
self._pobjects.append(self.axes)
|
| 240 |
+
|
| 241 |
+
self[0] = fargs
|
| 242 |
+
if win_args.get('visible', True):
|
| 243 |
+
self.show()
|
| 244 |
+
|
| 245 |
+
## Window Interfaces
|
| 246 |
+
|
| 247 |
+
def show(self):
|
| 248 |
+
"""
|
| 249 |
+
Creates and displays a plot window, or activates it
|
| 250 |
+
(gives it focus) if it has already been created.
|
| 251 |
+
"""
|
| 252 |
+
if self._window and not self._window.has_exit:
|
| 253 |
+
self._window.activate()
|
| 254 |
+
else:
|
| 255 |
+
self._win_args['visible'] = True
|
| 256 |
+
self.axes.reset_resources()
|
| 257 |
+
|
| 258 |
+
#if hasattr(self, '_doctest_depends_on'):
|
| 259 |
+
# self._win_args['runfromdoctester'] = True
|
| 260 |
+
|
| 261 |
+
self._window = PlotWindow(self, **self._win_args)
|
| 262 |
+
|
| 263 |
+
def close(self):
|
| 264 |
+
"""
|
| 265 |
+
Closes the plot window.
|
| 266 |
+
"""
|
| 267 |
+
if self._window:
|
| 268 |
+
self._window.close()
|
| 269 |
+
|
| 270 |
+
def saveimage(self, outfile=None, format='', size=(600, 500)):
|
| 271 |
+
"""
|
| 272 |
+
Saves a screen capture of the plot window to an
|
| 273 |
+
image file.
|
| 274 |
+
|
| 275 |
+
If outfile is given, it can either be a path
|
| 276 |
+
or a file object. Otherwise a png image will
|
| 277 |
+
be saved to the current working directory.
|
| 278 |
+
If the format is omitted, it is determined from
|
| 279 |
+
the filename extension.
|
| 280 |
+
"""
|
| 281 |
+
self._screenshot.save(outfile, format, size)
|
| 282 |
+
|
| 283 |
+
## Function List Interfaces
|
| 284 |
+
|
| 285 |
+
def clear(self):
|
| 286 |
+
"""
|
| 287 |
+
Clears the function list of this plot.
|
| 288 |
+
"""
|
| 289 |
+
self._render_lock.acquire()
|
| 290 |
+
self._functions = {}
|
| 291 |
+
self.adjust_all_bounds()
|
| 292 |
+
self._render_lock.release()
|
| 293 |
+
|
| 294 |
+
def __getitem__(self, i):
|
| 295 |
+
"""
|
| 296 |
+
Returns the function at position i in the
|
| 297 |
+
function list.
|
| 298 |
+
"""
|
| 299 |
+
return self._functions[i]
|
| 300 |
+
|
| 301 |
+
def __setitem__(self, i, args):
|
| 302 |
+
"""
|
| 303 |
+
Parses and adds a PlotMode to the function
|
| 304 |
+
list.
|
| 305 |
+
"""
|
| 306 |
+
if not (isinstance(i, (SYMPY_INTS, Integer)) and i >= 0):
|
| 307 |
+
raise ValueError("Function index must "
|
| 308 |
+
"be an integer >= 0.")
|
| 309 |
+
|
| 310 |
+
if isinstance(args, PlotObject):
|
| 311 |
+
f = args
|
| 312 |
+
else:
|
| 313 |
+
if (not is_sequence(args)) or isinstance(args, GeometryEntity):
|
| 314 |
+
args = [args]
|
| 315 |
+
if len(args) == 0:
|
| 316 |
+
return # no arguments given
|
| 317 |
+
kwargs = {"bounds_callback": self.adjust_all_bounds}
|
| 318 |
+
f = PlotMode(*args, **kwargs)
|
| 319 |
+
|
| 320 |
+
if f:
|
| 321 |
+
self._render_lock.acquire()
|
| 322 |
+
self._functions[i] = f
|
| 323 |
+
self._render_lock.release()
|
| 324 |
+
else:
|
| 325 |
+
raise ValueError("Failed to parse '%s'."
|
| 326 |
+
% ', '.join(str(a) for a in args))
|
| 327 |
+
|
| 328 |
+
def __delitem__(self, i):
|
| 329 |
+
"""
|
| 330 |
+
Removes the function in the function list at
|
| 331 |
+
position i.
|
| 332 |
+
"""
|
| 333 |
+
self._render_lock.acquire()
|
| 334 |
+
del self._functions[i]
|
| 335 |
+
self.adjust_all_bounds()
|
| 336 |
+
self._render_lock.release()
|
| 337 |
+
|
| 338 |
+
def firstavailableindex(self):
|
| 339 |
+
"""
|
| 340 |
+
Returns the first unused index in the function list.
|
| 341 |
+
"""
|
| 342 |
+
i = 0
|
| 343 |
+
self._render_lock.acquire()
|
| 344 |
+
while i in self._functions:
|
| 345 |
+
i += 1
|
| 346 |
+
self._render_lock.release()
|
| 347 |
+
return i
|
| 348 |
+
|
| 349 |
+
def append(self, *args):
|
| 350 |
+
"""
|
| 351 |
+
Parses and adds a PlotMode to the function
|
| 352 |
+
list at the first available index.
|
| 353 |
+
"""
|
| 354 |
+
self.__setitem__(self.firstavailableindex(), args)
|
| 355 |
+
|
| 356 |
+
def __len__(self):
|
| 357 |
+
"""
|
| 358 |
+
Returns the number of functions in the function list.
|
| 359 |
+
"""
|
| 360 |
+
return len(self._functions)
|
| 361 |
+
|
| 362 |
+
def __iter__(self):
|
| 363 |
+
"""
|
| 364 |
+
Allows iteration of the function list.
|
| 365 |
+
"""
|
| 366 |
+
return self._functions.itervalues()
|
| 367 |
+
|
| 368 |
+
def __repr__(self):
|
| 369 |
+
return str(self)
|
| 370 |
+
|
| 371 |
+
def __str__(self):
|
| 372 |
+
"""
|
| 373 |
+
Returns a string containing a new-line separated
|
| 374 |
+
list of the functions in the function list.
|
| 375 |
+
"""
|
| 376 |
+
s = ""
|
| 377 |
+
if len(self._functions) == 0:
|
| 378 |
+
s += "<blank plot>"
|
| 379 |
+
else:
|
| 380 |
+
self._render_lock.acquire()
|
| 381 |
+
s += "\n".join(["%s[%i]: %s" % ("", i, str(self._functions[i]))
|
| 382 |
+
for i in self._functions])
|
| 383 |
+
self._render_lock.release()
|
| 384 |
+
return s
|
| 385 |
+
|
| 386 |
+
def adjust_all_bounds(self):
|
| 387 |
+
self._render_lock.acquire()
|
| 388 |
+
self.axes.reset_bounding_box()
|
| 389 |
+
for f in self._functions:
|
| 390 |
+
self.axes.adjust_bounds(self._functions[f].bounds)
|
| 391 |
+
self._render_lock.release()
|
| 392 |
+
|
| 393 |
+
def wait_for_calculations(self):
|
| 394 |
+
sleep(0)
|
| 395 |
+
self._render_lock.acquire()
|
| 396 |
+
for f in self._functions:
|
| 397 |
+
a = self._functions[f]._get_calculating_verts
|
| 398 |
+
b = self._functions[f]._get_calculating_cverts
|
| 399 |
+
while a() or b():
|
| 400 |
+
sleep(0)
|
| 401 |
+
self._render_lock.release()
|
| 402 |
+
|
| 403 |
+
class ScreenShot:
|
| 404 |
+
def __init__(self, plot):
|
| 405 |
+
self._plot = plot
|
| 406 |
+
self.screenshot_requested = False
|
| 407 |
+
self.outfile = None
|
| 408 |
+
self.format = ''
|
| 409 |
+
self.invisibleMode = False
|
| 410 |
+
self.flag = 0
|
| 411 |
+
|
| 412 |
+
def __bool__(self):
|
| 413 |
+
return self.screenshot_requested
|
| 414 |
+
|
| 415 |
+
def _execute_saving(self):
|
| 416 |
+
if self.flag < 3:
|
| 417 |
+
self.flag += 1
|
| 418 |
+
return
|
| 419 |
+
|
| 420 |
+
size_x, size_y = self._plot._window.get_size()
|
| 421 |
+
size = size_x*size_y*4*ctypes.sizeof(ctypes.c_ubyte)
|
| 422 |
+
image = ctypes.create_string_buffer(size)
|
| 423 |
+
pgl.glReadPixels(0, 0, size_x, size_y, pgl.GL_RGBA, pgl.GL_UNSIGNED_BYTE, image)
|
| 424 |
+
from PIL import Image
|
| 425 |
+
im = Image.frombuffer('RGBA', (size_x, size_y),
|
| 426 |
+
image.raw, 'raw', 'RGBA', 0, 1)
|
| 427 |
+
im.transpose(Image.FLIP_TOP_BOTTOM).save(self.outfile, self.format)
|
| 428 |
+
|
| 429 |
+
self.flag = 0
|
| 430 |
+
self.screenshot_requested = False
|
| 431 |
+
if self.invisibleMode:
|
| 432 |
+
self._plot._window.close()
|
| 433 |
+
|
| 434 |
+
def save(self, outfile=None, format='', size=(600, 500)):
|
| 435 |
+
self.outfile = outfile
|
| 436 |
+
self.format = format
|
| 437 |
+
self.size = size
|
| 438 |
+
self.screenshot_requested = True
|
| 439 |
+
|
| 440 |
+
if not self._plot._window or self._plot._window.has_exit:
|
| 441 |
+
self._plot._win_args['visible'] = False
|
| 442 |
+
|
| 443 |
+
self._plot._win_args['width'] = size[0]
|
| 444 |
+
self._plot._win_args['height'] = size[1]
|
| 445 |
+
|
| 446 |
+
self._plot.axes.reset_resources()
|
| 447 |
+
self._plot._window = PlotWindow(self._plot, **self._plot._win_args)
|
| 448 |
+
self.invisibleMode = True
|
| 449 |
+
|
| 450 |
+
if self.outfile is None:
|
| 451 |
+
self.outfile = self._create_unique_path()
|
| 452 |
+
print(self.outfile)
|
| 453 |
+
|
| 454 |
+
def _create_unique_path(self):
|
| 455 |
+
cwd = getcwd()
|
| 456 |
+
l = listdir(cwd)
|
| 457 |
+
path = ''
|
| 458 |
+
i = 0
|
| 459 |
+
while True:
|
| 460 |
+
if not 'plot_%s.png' % i in l:
|
| 461 |
+
path = cwd + '/plot_%s.png' % i
|
| 462 |
+
break
|
| 463 |
+
i += 1
|
| 464 |
+
return path
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_axes.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyglet.gl as pgl
|
| 2 |
+
from pyglet import font
|
| 3 |
+
|
| 4 |
+
from sympy.core import S
|
| 5 |
+
from sympy.plotting.pygletplot.plot_object import PlotObject
|
| 6 |
+
from sympy.plotting.pygletplot.util import billboard_matrix, dot_product, \
|
| 7 |
+
get_direction_vectors, strided_range, vec_mag, vec_sub
|
| 8 |
+
from sympy.utilities.iterables import is_sequence
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PlotAxes(PlotObject):
|
| 12 |
+
|
| 13 |
+
def __init__(self, *args,
|
| 14 |
+
style='', none=None, frame=None, box=None, ordinate=None,
|
| 15 |
+
stride=0.25,
|
| 16 |
+
visible='', overlay='', colored='', label_axes='', label_ticks='',
|
| 17 |
+
tick_length=0.1,
|
| 18 |
+
font_face='Arial', font_size=28,
|
| 19 |
+
**kwargs):
|
| 20 |
+
# initialize style parameter
|
| 21 |
+
style = style.lower()
|
| 22 |
+
|
| 23 |
+
# allow alias kwargs to override style kwarg
|
| 24 |
+
if none is not None:
|
| 25 |
+
style = 'none'
|
| 26 |
+
if frame is not None:
|
| 27 |
+
style = 'frame'
|
| 28 |
+
if box is not None:
|
| 29 |
+
style = 'box'
|
| 30 |
+
if ordinate is not None:
|
| 31 |
+
style = 'ordinate'
|
| 32 |
+
|
| 33 |
+
if style in ['', 'ordinate']:
|
| 34 |
+
self._render_object = PlotAxesOrdinate(self)
|
| 35 |
+
elif style in ['frame', 'box']:
|
| 36 |
+
self._render_object = PlotAxesFrame(self)
|
| 37 |
+
elif style in ['none']:
|
| 38 |
+
self._render_object = None
|
| 39 |
+
else:
|
| 40 |
+
raise ValueError(("Unrecognized axes style %s.") % (style))
|
| 41 |
+
|
| 42 |
+
# initialize stride parameter
|
| 43 |
+
try:
|
| 44 |
+
stride = eval(stride)
|
| 45 |
+
except TypeError:
|
| 46 |
+
pass
|
| 47 |
+
if is_sequence(stride):
|
| 48 |
+
if len(stride) != 3:
|
| 49 |
+
raise ValueError("length should be equal to 3")
|
| 50 |
+
self._stride = stride
|
| 51 |
+
else:
|
| 52 |
+
self._stride = [stride, stride, stride]
|
| 53 |
+
self._tick_length = float(tick_length)
|
| 54 |
+
|
| 55 |
+
# setup bounding box and ticks
|
| 56 |
+
self._origin = [0, 0, 0]
|
| 57 |
+
self.reset_bounding_box()
|
| 58 |
+
|
| 59 |
+
def flexible_boolean(input, default):
|
| 60 |
+
if input in [True, False]:
|
| 61 |
+
return input
|
| 62 |
+
if input in ('f', 'F', 'false', 'False'):
|
| 63 |
+
return False
|
| 64 |
+
if input in ('t', 'T', 'true', 'True'):
|
| 65 |
+
return True
|
| 66 |
+
return default
|
| 67 |
+
|
| 68 |
+
# initialize remaining parameters
|
| 69 |
+
self.visible = flexible_boolean(kwargs, True)
|
| 70 |
+
self._overlay = flexible_boolean(overlay, True)
|
| 71 |
+
self._colored = flexible_boolean(colored, False)
|
| 72 |
+
self._label_axes = flexible_boolean(label_axes, False)
|
| 73 |
+
self._label_ticks = flexible_boolean(label_ticks, True)
|
| 74 |
+
|
| 75 |
+
# setup label font
|
| 76 |
+
self.font_face = font_face
|
| 77 |
+
self.font_size = font_size
|
| 78 |
+
|
| 79 |
+
# this is also used to reinit the
|
| 80 |
+
# font on window close/reopen
|
| 81 |
+
self.reset_resources()
|
| 82 |
+
|
| 83 |
+
def reset_resources(self):
|
| 84 |
+
self.label_font = None
|
| 85 |
+
|
| 86 |
+
def reset_bounding_box(self):
|
| 87 |
+
self._bounding_box = [[None, None], [None, None], [None, None]]
|
| 88 |
+
self._axis_ticks = [[], [], []]
|
| 89 |
+
|
| 90 |
+
def draw(self):
|
| 91 |
+
if self._render_object:
|
| 92 |
+
pgl.glPushAttrib(pgl.GL_ENABLE_BIT | pgl.GL_POLYGON_BIT | pgl.GL_DEPTH_BUFFER_BIT)
|
| 93 |
+
if self._overlay:
|
| 94 |
+
pgl.glDisable(pgl.GL_DEPTH_TEST)
|
| 95 |
+
self._render_object.draw()
|
| 96 |
+
pgl.glPopAttrib()
|
| 97 |
+
|
| 98 |
+
def adjust_bounds(self, child_bounds):
|
| 99 |
+
b = self._bounding_box
|
| 100 |
+
c = child_bounds
|
| 101 |
+
for i in range(3):
|
| 102 |
+
if abs(c[i][0]) is S.Infinity or abs(c[i][1]) is S.Infinity:
|
| 103 |
+
continue
|
| 104 |
+
b[i][0] = c[i][0] if b[i][0] is None else min([b[i][0], c[i][0]])
|
| 105 |
+
b[i][1] = c[i][1] if b[i][1] is None else max([b[i][1], c[i][1]])
|
| 106 |
+
self._bounding_box = b
|
| 107 |
+
self._recalculate_axis_ticks(i)
|
| 108 |
+
|
| 109 |
+
def _recalculate_axis_ticks(self, axis):
|
| 110 |
+
b = self._bounding_box
|
| 111 |
+
if b[axis][0] is None or b[axis][1] is None:
|
| 112 |
+
self._axis_ticks[axis] = []
|
| 113 |
+
else:
|
| 114 |
+
self._axis_ticks[axis] = strided_range(b[axis][0], b[axis][1],
|
| 115 |
+
self._stride[axis])
|
| 116 |
+
|
| 117 |
+
def toggle_visible(self):
|
| 118 |
+
self.visible = not self.visible
|
| 119 |
+
|
| 120 |
+
def toggle_colors(self):
|
| 121 |
+
self._colored = not self._colored
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
class PlotAxesBase(PlotObject):
|
| 125 |
+
|
| 126 |
+
def __init__(self, parent_axes):
|
| 127 |
+
self._p = parent_axes
|
| 128 |
+
|
| 129 |
+
def draw(self):
|
| 130 |
+
color = [([0.2, 0.1, 0.3], [0.2, 0.1, 0.3], [0.2, 0.1, 0.3]),
|
| 131 |
+
([0.9, 0.3, 0.5], [0.5, 1.0, 0.5], [0.3, 0.3, 0.9])][self._p._colored]
|
| 132 |
+
self.draw_background(color)
|
| 133 |
+
self.draw_axis(2, color[2])
|
| 134 |
+
self.draw_axis(1, color[1])
|
| 135 |
+
self.draw_axis(0, color[0])
|
| 136 |
+
|
| 137 |
+
def draw_background(self, color):
|
| 138 |
+
pass # optional
|
| 139 |
+
|
| 140 |
+
def draw_axis(self, axis, color):
|
| 141 |
+
raise NotImplementedError()
|
| 142 |
+
|
| 143 |
+
def draw_text(self, text, position, color, scale=1.0):
|
| 144 |
+
if len(color) == 3:
|
| 145 |
+
color = (color[0], color[1], color[2], 1.0)
|
| 146 |
+
|
| 147 |
+
if self._p.label_font is None:
|
| 148 |
+
self._p.label_font = font.load(self._p.font_face,
|
| 149 |
+
self._p.font_size,
|
| 150 |
+
bold=True, italic=False)
|
| 151 |
+
|
| 152 |
+
label = font.Text(self._p.label_font, text,
|
| 153 |
+
color=color,
|
| 154 |
+
valign=font.Text.BASELINE,
|
| 155 |
+
halign=font.Text.CENTER)
|
| 156 |
+
|
| 157 |
+
pgl.glPushMatrix()
|
| 158 |
+
pgl.glTranslatef(*position)
|
| 159 |
+
billboard_matrix()
|
| 160 |
+
scale_factor = 0.005 * scale
|
| 161 |
+
pgl.glScalef(scale_factor, scale_factor, scale_factor)
|
| 162 |
+
pgl.glColor4f(0, 0, 0, 0)
|
| 163 |
+
label.draw()
|
| 164 |
+
pgl.glPopMatrix()
|
| 165 |
+
|
| 166 |
+
def draw_line(self, v, color):
|
| 167 |
+
o = self._p._origin
|
| 168 |
+
pgl.glBegin(pgl.GL_LINES)
|
| 169 |
+
pgl.glColor3f(*color)
|
| 170 |
+
pgl.glVertex3f(v[0][0] + o[0], v[0][1] + o[1], v[0][2] + o[2])
|
| 171 |
+
pgl.glVertex3f(v[1][0] + o[0], v[1][1] + o[1], v[1][2] + o[2])
|
| 172 |
+
pgl.glEnd()
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class PlotAxesOrdinate(PlotAxesBase):
|
| 176 |
+
|
| 177 |
+
def __init__(self, parent_axes):
|
| 178 |
+
super().__init__(parent_axes)
|
| 179 |
+
|
| 180 |
+
def draw_axis(self, axis, color):
|
| 181 |
+
ticks = self._p._axis_ticks[axis]
|
| 182 |
+
radius = self._p._tick_length / 2.0
|
| 183 |
+
if len(ticks) < 2:
|
| 184 |
+
return
|
| 185 |
+
|
| 186 |
+
# calculate the vector for this axis
|
| 187 |
+
axis_lines = [[0, 0, 0], [0, 0, 0]]
|
| 188 |
+
axis_lines[0][axis], axis_lines[1][axis] = ticks[0], ticks[-1]
|
| 189 |
+
axis_vector = vec_sub(axis_lines[1], axis_lines[0])
|
| 190 |
+
|
| 191 |
+
# calculate angle to the z direction vector
|
| 192 |
+
pos_z = get_direction_vectors()[2]
|
| 193 |
+
d = abs(dot_product(axis_vector, pos_z))
|
| 194 |
+
d = d / vec_mag(axis_vector)
|
| 195 |
+
|
| 196 |
+
# don't draw labels if we're looking down the axis
|
| 197 |
+
labels_visible = abs(d - 1.0) > 0.02
|
| 198 |
+
|
| 199 |
+
# draw the ticks and labels
|
| 200 |
+
for tick in ticks:
|
| 201 |
+
self.draw_tick_line(axis, color, radius, tick, labels_visible)
|
| 202 |
+
|
| 203 |
+
# draw the axis line and labels
|
| 204 |
+
self.draw_axis_line(axis, color, ticks[0], ticks[-1], labels_visible)
|
| 205 |
+
|
| 206 |
+
def draw_axis_line(self, axis, color, a_min, a_max, labels_visible):
|
| 207 |
+
axis_line = [[0, 0, 0], [0, 0, 0]]
|
| 208 |
+
axis_line[0][axis], axis_line[1][axis] = a_min, a_max
|
| 209 |
+
self.draw_line(axis_line, color)
|
| 210 |
+
if labels_visible:
|
| 211 |
+
self.draw_axis_line_labels(axis, color, axis_line)
|
| 212 |
+
|
| 213 |
+
def draw_axis_line_labels(self, axis, color, axis_line):
|
| 214 |
+
if not self._p._label_axes:
|
| 215 |
+
return
|
| 216 |
+
axis_labels = [axis_line[0][::], axis_line[1][::]]
|
| 217 |
+
axis_labels[0][axis] -= 0.3
|
| 218 |
+
axis_labels[1][axis] += 0.3
|
| 219 |
+
a_str = ['X', 'Y', 'Z'][axis]
|
| 220 |
+
self.draw_text("-" + a_str, axis_labels[0], color)
|
| 221 |
+
self.draw_text("+" + a_str, axis_labels[1], color)
|
| 222 |
+
|
| 223 |
+
def draw_tick_line(self, axis, color, radius, tick, labels_visible):
|
| 224 |
+
tick_axis = {0: 1, 1: 0, 2: 1}[axis]
|
| 225 |
+
tick_line = [[0, 0, 0], [0, 0, 0]]
|
| 226 |
+
tick_line[0][axis] = tick_line[1][axis] = tick
|
| 227 |
+
tick_line[0][tick_axis], tick_line[1][tick_axis] = -radius, radius
|
| 228 |
+
self.draw_line(tick_line, color)
|
| 229 |
+
if labels_visible:
|
| 230 |
+
self.draw_tick_line_label(axis, color, radius, tick)
|
| 231 |
+
|
| 232 |
+
def draw_tick_line_label(self, axis, color, radius, tick):
|
| 233 |
+
if not self._p._label_axes:
|
| 234 |
+
return
|
| 235 |
+
tick_label_vector = [0, 0, 0]
|
| 236 |
+
tick_label_vector[axis] = tick
|
| 237 |
+
tick_label_vector[{0: 1, 1: 0, 2: 1}[axis]] = [-1, 1, 1][
|
| 238 |
+
axis] * radius * 3.5
|
| 239 |
+
self.draw_text(str(tick), tick_label_vector, color, scale=0.5)
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
class PlotAxesFrame(PlotAxesBase):
|
| 243 |
+
|
| 244 |
+
def __init__(self, parent_axes):
|
| 245 |
+
super().__init__(parent_axes)
|
| 246 |
+
|
| 247 |
+
def draw_background(self, color):
|
| 248 |
+
pass
|
| 249 |
+
|
| 250 |
+
def draw_axis(self, axis, color):
|
| 251 |
+
raise NotImplementedError()
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_camera.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyglet.gl as pgl
|
| 2 |
+
from sympy.plotting.pygletplot.plot_rotation import get_spherical_rotatation
|
| 3 |
+
from sympy.plotting.pygletplot.util import get_model_matrix, model_to_screen, \
|
| 4 |
+
screen_to_model, vec_subs
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PlotCamera:
|
| 8 |
+
|
| 9 |
+
min_dist = 0.05
|
| 10 |
+
max_dist = 500.0
|
| 11 |
+
|
| 12 |
+
min_ortho_dist = 100.0
|
| 13 |
+
max_ortho_dist = 10000.0
|
| 14 |
+
|
| 15 |
+
_default_dist = 6.0
|
| 16 |
+
_default_ortho_dist = 600.0
|
| 17 |
+
|
| 18 |
+
rot_presets = {
|
| 19 |
+
'xy': (0, 0, 0),
|
| 20 |
+
'xz': (-90, 0, 0),
|
| 21 |
+
'yz': (0, 90, 0),
|
| 22 |
+
'perspective': (-45, 0, -45)
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
def __init__(self, window, ortho=False):
|
| 26 |
+
self.window = window
|
| 27 |
+
self.axes = self.window.plot.axes
|
| 28 |
+
self.ortho = ortho
|
| 29 |
+
self.reset()
|
| 30 |
+
|
| 31 |
+
def init_rot_matrix(self):
|
| 32 |
+
pgl.glPushMatrix()
|
| 33 |
+
pgl.glLoadIdentity()
|
| 34 |
+
self._rot = get_model_matrix()
|
| 35 |
+
pgl.glPopMatrix()
|
| 36 |
+
|
| 37 |
+
def set_rot_preset(self, preset_name):
|
| 38 |
+
self.init_rot_matrix()
|
| 39 |
+
if preset_name not in self.rot_presets:
|
| 40 |
+
raise ValueError(
|
| 41 |
+
"%s is not a valid rotation preset." % preset_name)
|
| 42 |
+
r = self.rot_presets[preset_name]
|
| 43 |
+
self.euler_rotate(r[0], 1, 0, 0)
|
| 44 |
+
self.euler_rotate(r[1], 0, 1, 0)
|
| 45 |
+
self.euler_rotate(r[2], 0, 0, 1)
|
| 46 |
+
|
| 47 |
+
def reset(self):
|
| 48 |
+
self._dist = 0.0
|
| 49 |
+
self._x, self._y = 0.0, 0.0
|
| 50 |
+
self._rot = None
|
| 51 |
+
if self.ortho:
|
| 52 |
+
self._dist = self._default_ortho_dist
|
| 53 |
+
else:
|
| 54 |
+
self._dist = self._default_dist
|
| 55 |
+
self.init_rot_matrix()
|
| 56 |
+
|
| 57 |
+
def mult_rot_matrix(self, rot):
|
| 58 |
+
pgl.glPushMatrix()
|
| 59 |
+
pgl.glLoadMatrixf(rot)
|
| 60 |
+
pgl.glMultMatrixf(self._rot)
|
| 61 |
+
self._rot = get_model_matrix()
|
| 62 |
+
pgl.glPopMatrix()
|
| 63 |
+
|
| 64 |
+
def setup_projection(self):
|
| 65 |
+
pgl.glMatrixMode(pgl.GL_PROJECTION)
|
| 66 |
+
pgl.glLoadIdentity()
|
| 67 |
+
if self.ortho:
|
| 68 |
+
# yep, this is pseudo ortho (don't tell anyone)
|
| 69 |
+
pgl.gluPerspective(
|
| 70 |
+
0.3, float(self.window.width)/float(self.window.height),
|
| 71 |
+
self.min_ortho_dist - 0.01, self.max_ortho_dist + 0.01)
|
| 72 |
+
else:
|
| 73 |
+
pgl.gluPerspective(
|
| 74 |
+
30.0, float(self.window.width)/float(self.window.height),
|
| 75 |
+
self.min_dist - 0.01, self.max_dist + 0.01)
|
| 76 |
+
pgl.glMatrixMode(pgl.GL_MODELVIEW)
|
| 77 |
+
|
| 78 |
+
def _get_scale(self):
|
| 79 |
+
return 1.0, 1.0, 1.0
|
| 80 |
+
|
| 81 |
+
def apply_transformation(self):
|
| 82 |
+
pgl.glLoadIdentity()
|
| 83 |
+
pgl.glTranslatef(self._x, self._y, -self._dist)
|
| 84 |
+
if self._rot is not None:
|
| 85 |
+
pgl.glMultMatrixf(self._rot)
|
| 86 |
+
pgl.glScalef(*self._get_scale())
|
| 87 |
+
|
| 88 |
+
def spherical_rotate(self, p1, p2, sensitivity=1.0):
|
| 89 |
+
mat = get_spherical_rotatation(p1, p2, self.window.width,
|
| 90 |
+
self.window.height, sensitivity)
|
| 91 |
+
if mat is not None:
|
| 92 |
+
self.mult_rot_matrix(mat)
|
| 93 |
+
|
| 94 |
+
def euler_rotate(self, angle, x, y, z):
|
| 95 |
+
pgl.glPushMatrix()
|
| 96 |
+
pgl.glLoadMatrixf(self._rot)
|
| 97 |
+
pgl.glRotatef(angle, x, y, z)
|
| 98 |
+
self._rot = get_model_matrix()
|
| 99 |
+
pgl.glPopMatrix()
|
| 100 |
+
|
| 101 |
+
def zoom_relative(self, clicks, sensitivity):
|
| 102 |
+
|
| 103 |
+
if self.ortho:
|
| 104 |
+
dist_d = clicks * sensitivity * 50.0
|
| 105 |
+
min_dist = self.min_ortho_dist
|
| 106 |
+
max_dist = self.max_ortho_dist
|
| 107 |
+
else:
|
| 108 |
+
dist_d = clicks * sensitivity
|
| 109 |
+
min_dist = self.min_dist
|
| 110 |
+
max_dist = self.max_dist
|
| 111 |
+
|
| 112 |
+
new_dist = (self._dist - dist_d)
|
| 113 |
+
if (clicks < 0 and new_dist < max_dist) or new_dist > min_dist:
|
| 114 |
+
self._dist = new_dist
|
| 115 |
+
|
| 116 |
+
def mouse_translate(self, x, y, dx, dy):
|
| 117 |
+
pgl.glPushMatrix()
|
| 118 |
+
pgl.glLoadIdentity()
|
| 119 |
+
pgl.glTranslatef(0, 0, -self._dist)
|
| 120 |
+
z = model_to_screen(0, 0, 0)[2]
|
| 121 |
+
d = vec_subs(screen_to_model(x, y, z), screen_to_model(x - dx, y - dy, z))
|
| 122 |
+
pgl.glPopMatrix()
|
| 123 |
+
self._x += d[0]
|
| 124 |
+
self._y += d[1]
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_controller.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pyglet.window import key
|
| 2 |
+
from pyglet.window.mouse import LEFT, RIGHT, MIDDLE
|
| 3 |
+
from sympy.plotting.pygletplot.util import get_direction_vectors, get_basis_vectors
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class PlotController:
|
| 7 |
+
|
| 8 |
+
normal_mouse_sensitivity = 4.0
|
| 9 |
+
modified_mouse_sensitivity = 1.0
|
| 10 |
+
|
| 11 |
+
normal_key_sensitivity = 160.0
|
| 12 |
+
modified_key_sensitivity = 40.0
|
| 13 |
+
|
| 14 |
+
keymap = {
|
| 15 |
+
key.LEFT: 'left',
|
| 16 |
+
key.A: 'left',
|
| 17 |
+
key.NUM_4: 'left',
|
| 18 |
+
|
| 19 |
+
key.RIGHT: 'right',
|
| 20 |
+
key.D: 'right',
|
| 21 |
+
key.NUM_6: 'right',
|
| 22 |
+
|
| 23 |
+
key.UP: 'up',
|
| 24 |
+
key.W: 'up',
|
| 25 |
+
key.NUM_8: 'up',
|
| 26 |
+
|
| 27 |
+
key.DOWN: 'down',
|
| 28 |
+
key.S: 'down',
|
| 29 |
+
key.NUM_2: 'down',
|
| 30 |
+
|
| 31 |
+
key.Z: 'rotate_z_neg',
|
| 32 |
+
key.NUM_1: 'rotate_z_neg',
|
| 33 |
+
|
| 34 |
+
key.C: 'rotate_z_pos',
|
| 35 |
+
key.NUM_3: 'rotate_z_pos',
|
| 36 |
+
|
| 37 |
+
key.Q: 'spin_left',
|
| 38 |
+
key.NUM_7: 'spin_left',
|
| 39 |
+
key.E: 'spin_right',
|
| 40 |
+
key.NUM_9: 'spin_right',
|
| 41 |
+
|
| 42 |
+
key.X: 'reset_camera',
|
| 43 |
+
key.NUM_5: 'reset_camera',
|
| 44 |
+
|
| 45 |
+
key.NUM_ADD: 'zoom_in',
|
| 46 |
+
key.PAGEUP: 'zoom_in',
|
| 47 |
+
key.R: 'zoom_in',
|
| 48 |
+
|
| 49 |
+
key.NUM_SUBTRACT: 'zoom_out',
|
| 50 |
+
key.PAGEDOWN: 'zoom_out',
|
| 51 |
+
key.F: 'zoom_out',
|
| 52 |
+
|
| 53 |
+
key.RSHIFT: 'modify_sensitivity',
|
| 54 |
+
key.LSHIFT: 'modify_sensitivity',
|
| 55 |
+
|
| 56 |
+
key.F1: 'rot_preset_xy',
|
| 57 |
+
key.F2: 'rot_preset_xz',
|
| 58 |
+
key.F3: 'rot_preset_yz',
|
| 59 |
+
key.F4: 'rot_preset_perspective',
|
| 60 |
+
|
| 61 |
+
key.F5: 'toggle_axes',
|
| 62 |
+
key.F6: 'toggle_axe_colors',
|
| 63 |
+
|
| 64 |
+
key.F8: 'save_image'
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
def __init__(self, window, *, invert_mouse_zoom=False, **kwargs):
|
| 68 |
+
self.invert_mouse_zoom = invert_mouse_zoom
|
| 69 |
+
self.window = window
|
| 70 |
+
self.camera = window.camera
|
| 71 |
+
self.action = {
|
| 72 |
+
# Rotation around the view Y (up) vector
|
| 73 |
+
'left': False,
|
| 74 |
+
'right': False,
|
| 75 |
+
# Rotation around the view X vector
|
| 76 |
+
'up': False,
|
| 77 |
+
'down': False,
|
| 78 |
+
# Rotation around the view Z vector
|
| 79 |
+
'spin_left': False,
|
| 80 |
+
'spin_right': False,
|
| 81 |
+
# Rotation around the model Z vector
|
| 82 |
+
'rotate_z_neg': False,
|
| 83 |
+
'rotate_z_pos': False,
|
| 84 |
+
# Reset to the default rotation
|
| 85 |
+
'reset_camera': False,
|
| 86 |
+
# Performs camera z-translation
|
| 87 |
+
'zoom_in': False,
|
| 88 |
+
'zoom_out': False,
|
| 89 |
+
# Use alternative sensitivity (speed)
|
| 90 |
+
'modify_sensitivity': False,
|
| 91 |
+
# Rotation presets
|
| 92 |
+
'rot_preset_xy': False,
|
| 93 |
+
'rot_preset_xz': False,
|
| 94 |
+
'rot_preset_yz': False,
|
| 95 |
+
'rot_preset_perspective': False,
|
| 96 |
+
# axes
|
| 97 |
+
'toggle_axes': False,
|
| 98 |
+
'toggle_axe_colors': False,
|
| 99 |
+
# screenshot
|
| 100 |
+
'save_image': False
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
def update(self, dt):
|
| 104 |
+
z = 0
|
| 105 |
+
if self.action['zoom_out']:
|
| 106 |
+
z -= 1
|
| 107 |
+
if self.action['zoom_in']:
|
| 108 |
+
z += 1
|
| 109 |
+
if z != 0:
|
| 110 |
+
self.camera.zoom_relative(z/10.0, self.get_key_sensitivity()/10.0)
|
| 111 |
+
|
| 112 |
+
dx, dy, dz = 0, 0, 0
|
| 113 |
+
if self.action['left']:
|
| 114 |
+
dx -= 1
|
| 115 |
+
if self.action['right']:
|
| 116 |
+
dx += 1
|
| 117 |
+
if self.action['up']:
|
| 118 |
+
dy -= 1
|
| 119 |
+
if self.action['down']:
|
| 120 |
+
dy += 1
|
| 121 |
+
if self.action['spin_left']:
|
| 122 |
+
dz += 1
|
| 123 |
+
if self.action['spin_right']:
|
| 124 |
+
dz -= 1
|
| 125 |
+
|
| 126 |
+
if not self.is_2D():
|
| 127 |
+
if dx != 0:
|
| 128 |
+
self.camera.euler_rotate(dx*dt*self.get_key_sensitivity(),
|
| 129 |
+
*(get_direction_vectors()[1]))
|
| 130 |
+
if dy != 0:
|
| 131 |
+
self.camera.euler_rotate(dy*dt*self.get_key_sensitivity(),
|
| 132 |
+
*(get_direction_vectors()[0]))
|
| 133 |
+
if dz != 0:
|
| 134 |
+
self.camera.euler_rotate(dz*dt*self.get_key_sensitivity(),
|
| 135 |
+
*(get_direction_vectors()[2]))
|
| 136 |
+
else:
|
| 137 |
+
self.camera.mouse_translate(0, 0, dx*dt*self.get_key_sensitivity(),
|
| 138 |
+
-dy*dt*self.get_key_sensitivity())
|
| 139 |
+
|
| 140 |
+
rz = 0
|
| 141 |
+
if self.action['rotate_z_neg'] and not self.is_2D():
|
| 142 |
+
rz -= 1
|
| 143 |
+
if self.action['rotate_z_pos'] and not self.is_2D():
|
| 144 |
+
rz += 1
|
| 145 |
+
|
| 146 |
+
if rz != 0:
|
| 147 |
+
self.camera.euler_rotate(rz*dt*self.get_key_sensitivity(),
|
| 148 |
+
*(get_basis_vectors()[2]))
|
| 149 |
+
|
| 150 |
+
if self.action['reset_camera']:
|
| 151 |
+
self.camera.reset()
|
| 152 |
+
|
| 153 |
+
if self.action['rot_preset_xy']:
|
| 154 |
+
self.camera.set_rot_preset('xy')
|
| 155 |
+
if self.action['rot_preset_xz']:
|
| 156 |
+
self.camera.set_rot_preset('xz')
|
| 157 |
+
if self.action['rot_preset_yz']:
|
| 158 |
+
self.camera.set_rot_preset('yz')
|
| 159 |
+
if self.action['rot_preset_perspective']:
|
| 160 |
+
self.camera.set_rot_preset('perspective')
|
| 161 |
+
|
| 162 |
+
if self.action['toggle_axes']:
|
| 163 |
+
self.action['toggle_axes'] = False
|
| 164 |
+
self.camera.axes.toggle_visible()
|
| 165 |
+
|
| 166 |
+
if self.action['toggle_axe_colors']:
|
| 167 |
+
self.action['toggle_axe_colors'] = False
|
| 168 |
+
self.camera.axes.toggle_colors()
|
| 169 |
+
|
| 170 |
+
if self.action['save_image']:
|
| 171 |
+
self.action['save_image'] = False
|
| 172 |
+
self.window.plot.saveimage()
|
| 173 |
+
|
| 174 |
+
return True
|
| 175 |
+
|
| 176 |
+
def get_mouse_sensitivity(self):
|
| 177 |
+
if self.action['modify_sensitivity']:
|
| 178 |
+
return self.modified_mouse_sensitivity
|
| 179 |
+
else:
|
| 180 |
+
return self.normal_mouse_sensitivity
|
| 181 |
+
|
| 182 |
+
def get_key_sensitivity(self):
|
| 183 |
+
if self.action['modify_sensitivity']:
|
| 184 |
+
return self.modified_key_sensitivity
|
| 185 |
+
else:
|
| 186 |
+
return self.normal_key_sensitivity
|
| 187 |
+
|
| 188 |
+
def on_key_press(self, symbol, modifiers):
|
| 189 |
+
if symbol in self.keymap:
|
| 190 |
+
self.action[self.keymap[symbol]] = True
|
| 191 |
+
|
| 192 |
+
def on_key_release(self, symbol, modifiers):
|
| 193 |
+
if symbol in self.keymap:
|
| 194 |
+
self.action[self.keymap[symbol]] = False
|
| 195 |
+
|
| 196 |
+
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
| 197 |
+
if buttons & LEFT:
|
| 198 |
+
if self.is_2D():
|
| 199 |
+
self.camera.mouse_translate(x, y, dx, dy)
|
| 200 |
+
else:
|
| 201 |
+
self.camera.spherical_rotate((x - dx, y - dy), (x, y),
|
| 202 |
+
self.get_mouse_sensitivity())
|
| 203 |
+
if buttons & MIDDLE:
|
| 204 |
+
self.camera.zoom_relative([1, -1][self.invert_mouse_zoom]*dy,
|
| 205 |
+
self.get_mouse_sensitivity()/20.0)
|
| 206 |
+
if buttons & RIGHT:
|
| 207 |
+
self.camera.mouse_translate(x, y, dx, dy)
|
| 208 |
+
|
| 209 |
+
def on_mouse_scroll(self, x, y, dx, dy):
|
| 210 |
+
self.camera.zoom_relative([1, -1][self.invert_mouse_zoom]*dy,
|
| 211 |
+
self.get_mouse_sensitivity())
|
| 212 |
+
|
| 213 |
+
def is_2D(self):
|
| 214 |
+
functions = self.window.plot._functions
|
| 215 |
+
for i in functions:
|
| 216 |
+
if len(functions[i].i_vars) > 1 or len(functions[i].d_vars) > 2:
|
| 217 |
+
return False
|
| 218 |
+
return True
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_curve.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyglet.gl as pgl
|
| 2 |
+
from sympy.core import S
|
| 3 |
+
from sympy.plotting.pygletplot.plot_mode_base import PlotModeBase
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class PlotCurve(PlotModeBase):
|
| 7 |
+
|
| 8 |
+
style_override = 'wireframe'
|
| 9 |
+
|
| 10 |
+
def _on_calculate_verts(self):
|
| 11 |
+
self.t_interval = self.intervals[0]
|
| 12 |
+
self.t_set = list(self.t_interval.frange())
|
| 13 |
+
self.bounds = [[S.Infinity, S.NegativeInfinity, 0],
|
| 14 |
+
[S.Infinity, S.NegativeInfinity, 0],
|
| 15 |
+
[S.Infinity, S.NegativeInfinity, 0]]
|
| 16 |
+
evaluate = self._get_evaluator()
|
| 17 |
+
|
| 18 |
+
self._calculating_verts_pos = 0.0
|
| 19 |
+
self._calculating_verts_len = float(self.t_interval.v_len)
|
| 20 |
+
|
| 21 |
+
self.verts = []
|
| 22 |
+
b = self.bounds
|
| 23 |
+
for t in self.t_set:
|
| 24 |
+
try:
|
| 25 |
+
_e = evaluate(t) # calculate vertex
|
| 26 |
+
except (NameError, ZeroDivisionError):
|
| 27 |
+
_e = None
|
| 28 |
+
if _e is not None: # update bounding box
|
| 29 |
+
for axis in range(3):
|
| 30 |
+
b[axis][0] = min([b[axis][0], _e[axis]])
|
| 31 |
+
b[axis][1] = max([b[axis][1], _e[axis]])
|
| 32 |
+
self.verts.append(_e)
|
| 33 |
+
self._calculating_verts_pos += 1.0
|
| 34 |
+
|
| 35 |
+
for axis in range(3):
|
| 36 |
+
b[axis][2] = b[axis][1] - b[axis][0]
|
| 37 |
+
if b[axis][2] == 0.0:
|
| 38 |
+
b[axis][2] = 1.0
|
| 39 |
+
|
| 40 |
+
self.push_wireframe(self.draw_verts(False))
|
| 41 |
+
|
| 42 |
+
def _on_calculate_cverts(self):
|
| 43 |
+
if not self.verts or not self.color:
|
| 44 |
+
return
|
| 45 |
+
|
| 46 |
+
def set_work_len(n):
|
| 47 |
+
self._calculating_cverts_len = float(n)
|
| 48 |
+
|
| 49 |
+
def inc_work_pos():
|
| 50 |
+
self._calculating_cverts_pos += 1.0
|
| 51 |
+
set_work_len(1)
|
| 52 |
+
self._calculating_cverts_pos = 0
|
| 53 |
+
self.cverts = self.color.apply_to_curve(self.verts,
|
| 54 |
+
self.t_set,
|
| 55 |
+
set_len=set_work_len,
|
| 56 |
+
inc_pos=inc_work_pos)
|
| 57 |
+
self.push_wireframe(self.draw_verts(True))
|
| 58 |
+
|
| 59 |
+
def calculate_one_cvert(self, t):
|
| 60 |
+
vert = self.verts[t]
|
| 61 |
+
return self.color(vert[0], vert[1], vert[2],
|
| 62 |
+
self.t_set[t], None)
|
| 63 |
+
|
| 64 |
+
def draw_verts(self, use_cverts):
|
| 65 |
+
def f():
|
| 66 |
+
pgl.glBegin(pgl.GL_LINE_STRIP)
|
| 67 |
+
for t in range(len(self.t_set)):
|
| 68 |
+
p = self.verts[t]
|
| 69 |
+
if p is None:
|
| 70 |
+
pgl.glEnd()
|
| 71 |
+
pgl.glBegin(pgl.GL_LINE_STRIP)
|
| 72 |
+
continue
|
| 73 |
+
if use_cverts:
|
| 74 |
+
c = self.cverts[t]
|
| 75 |
+
if c is None:
|
| 76 |
+
c = (0, 0, 0)
|
| 77 |
+
pgl.glColor3f(*c)
|
| 78 |
+
else:
|
| 79 |
+
pgl.glColor3f(*self.default_wireframe_color)
|
| 80 |
+
pgl.glVertex3f(*p)
|
| 81 |
+
pgl.glEnd()
|
| 82 |
+
return f
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_interval.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core.singleton import S
|
| 2 |
+
from sympy.core.symbol import Symbol
|
| 3 |
+
from sympy.core.sympify import sympify
|
| 4 |
+
from sympy.core.numbers import Integer
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PlotInterval:
|
| 8 |
+
"""
|
| 9 |
+
"""
|
| 10 |
+
_v, _v_min, _v_max, _v_steps = None, None, None, None
|
| 11 |
+
|
| 12 |
+
def require_all_args(f):
|
| 13 |
+
def check(self, *args, **kwargs):
|
| 14 |
+
for g in [self._v, self._v_min, self._v_max, self._v_steps]:
|
| 15 |
+
if g is None:
|
| 16 |
+
raise ValueError("PlotInterval is incomplete.")
|
| 17 |
+
return f(self, *args, **kwargs)
|
| 18 |
+
return check
|
| 19 |
+
|
| 20 |
+
def __init__(self, *args):
|
| 21 |
+
if len(args) == 1:
|
| 22 |
+
if isinstance(args[0], PlotInterval):
|
| 23 |
+
self.fill_from(args[0])
|
| 24 |
+
return
|
| 25 |
+
elif isinstance(args[0], str):
|
| 26 |
+
try:
|
| 27 |
+
args = eval(args[0])
|
| 28 |
+
except TypeError:
|
| 29 |
+
s_eval_error = "Could not interpret string %s."
|
| 30 |
+
raise ValueError(s_eval_error % (args[0]))
|
| 31 |
+
elif isinstance(args[0], (tuple, list)):
|
| 32 |
+
args = args[0]
|
| 33 |
+
else:
|
| 34 |
+
raise ValueError("Not an interval.")
|
| 35 |
+
if not isinstance(args, (tuple, list)) or len(args) > 4:
|
| 36 |
+
f_error = "PlotInterval must be a tuple or list of length 4 or less."
|
| 37 |
+
raise ValueError(f_error)
|
| 38 |
+
|
| 39 |
+
args = list(args)
|
| 40 |
+
if len(args) > 0 and (args[0] is None or isinstance(args[0], Symbol)):
|
| 41 |
+
self.v = args.pop(0)
|
| 42 |
+
if len(args) in [2, 3]:
|
| 43 |
+
self.v_min = args.pop(0)
|
| 44 |
+
self.v_max = args.pop(0)
|
| 45 |
+
if len(args) == 1:
|
| 46 |
+
self.v_steps = args.pop(0)
|
| 47 |
+
elif len(args) == 1:
|
| 48 |
+
self.v_steps = args.pop(0)
|
| 49 |
+
|
| 50 |
+
def get_v(self):
|
| 51 |
+
return self._v
|
| 52 |
+
|
| 53 |
+
def set_v(self, v):
|
| 54 |
+
if v is None:
|
| 55 |
+
self._v = None
|
| 56 |
+
return
|
| 57 |
+
if not isinstance(v, Symbol):
|
| 58 |
+
raise ValueError("v must be a SymPy Symbol.")
|
| 59 |
+
self._v = v
|
| 60 |
+
|
| 61 |
+
def get_v_min(self):
|
| 62 |
+
return self._v_min
|
| 63 |
+
|
| 64 |
+
def set_v_min(self, v_min):
|
| 65 |
+
if v_min is None:
|
| 66 |
+
self._v_min = None
|
| 67 |
+
return
|
| 68 |
+
try:
|
| 69 |
+
self._v_min = sympify(v_min)
|
| 70 |
+
float(self._v_min.evalf())
|
| 71 |
+
except TypeError:
|
| 72 |
+
raise ValueError("v_min could not be interpreted as a number.")
|
| 73 |
+
|
| 74 |
+
def get_v_max(self):
|
| 75 |
+
return self._v_max
|
| 76 |
+
|
| 77 |
+
def set_v_max(self, v_max):
|
| 78 |
+
if v_max is None:
|
| 79 |
+
self._v_max = None
|
| 80 |
+
return
|
| 81 |
+
try:
|
| 82 |
+
self._v_max = sympify(v_max)
|
| 83 |
+
float(self._v_max.evalf())
|
| 84 |
+
except TypeError:
|
| 85 |
+
raise ValueError("v_max could not be interpreted as a number.")
|
| 86 |
+
|
| 87 |
+
def get_v_steps(self):
|
| 88 |
+
return self._v_steps
|
| 89 |
+
|
| 90 |
+
def set_v_steps(self, v_steps):
|
| 91 |
+
if v_steps is None:
|
| 92 |
+
self._v_steps = None
|
| 93 |
+
return
|
| 94 |
+
if isinstance(v_steps, int):
|
| 95 |
+
v_steps = Integer(v_steps)
|
| 96 |
+
elif not isinstance(v_steps, Integer):
|
| 97 |
+
raise ValueError("v_steps must be an int or SymPy Integer.")
|
| 98 |
+
if v_steps <= S.Zero:
|
| 99 |
+
raise ValueError("v_steps must be positive.")
|
| 100 |
+
self._v_steps = v_steps
|
| 101 |
+
|
| 102 |
+
@require_all_args
|
| 103 |
+
def get_v_len(self):
|
| 104 |
+
return self.v_steps + 1
|
| 105 |
+
|
| 106 |
+
v = property(get_v, set_v)
|
| 107 |
+
v_min = property(get_v_min, set_v_min)
|
| 108 |
+
v_max = property(get_v_max, set_v_max)
|
| 109 |
+
v_steps = property(get_v_steps, set_v_steps)
|
| 110 |
+
v_len = property(get_v_len)
|
| 111 |
+
|
| 112 |
+
def fill_from(self, b):
|
| 113 |
+
if b.v is not None:
|
| 114 |
+
self.v = b.v
|
| 115 |
+
if b.v_min is not None:
|
| 116 |
+
self.v_min = b.v_min
|
| 117 |
+
if b.v_max is not None:
|
| 118 |
+
self.v_max = b.v_max
|
| 119 |
+
if b.v_steps is not None:
|
| 120 |
+
self.v_steps = b.v_steps
|
| 121 |
+
|
| 122 |
+
@staticmethod
|
| 123 |
+
def try_parse(*args):
|
| 124 |
+
"""
|
| 125 |
+
Returns a PlotInterval if args can be interpreted
|
| 126 |
+
as such, otherwise None.
|
| 127 |
+
"""
|
| 128 |
+
if len(args) == 1 and isinstance(args[0], PlotInterval):
|
| 129 |
+
return args[0]
|
| 130 |
+
try:
|
| 131 |
+
return PlotInterval(*args)
|
| 132 |
+
except ValueError:
|
| 133 |
+
return None
|
| 134 |
+
|
| 135 |
+
def _str_base(self):
|
| 136 |
+
return ",".join([str(self.v), str(self.v_min),
|
| 137 |
+
str(self.v_max), str(self.v_steps)])
|
| 138 |
+
|
| 139 |
+
def __repr__(self):
|
| 140 |
+
"""
|
| 141 |
+
A string representing the interval in class constructor form.
|
| 142 |
+
"""
|
| 143 |
+
return "PlotInterval(%s)" % (self._str_base())
|
| 144 |
+
|
| 145 |
+
def __str__(self):
|
| 146 |
+
"""
|
| 147 |
+
A string representing the interval in list form.
|
| 148 |
+
"""
|
| 149 |
+
return "[%s]" % (self._str_base())
|
| 150 |
+
|
| 151 |
+
@require_all_args
|
| 152 |
+
def assert_complete(self):
|
| 153 |
+
pass
|
| 154 |
+
|
| 155 |
+
@require_all_args
|
| 156 |
+
def vrange(self):
|
| 157 |
+
"""
|
| 158 |
+
Yields v_steps+1 SymPy numbers ranging from
|
| 159 |
+
v_min to v_max.
|
| 160 |
+
"""
|
| 161 |
+
d = (self.v_max - self.v_min) / self.v_steps
|
| 162 |
+
for i in range(self.v_steps + 1):
|
| 163 |
+
a = self.v_min + (d * Integer(i))
|
| 164 |
+
yield a
|
| 165 |
+
|
| 166 |
+
@require_all_args
|
| 167 |
+
def vrange2(self):
|
| 168 |
+
"""
|
| 169 |
+
Yields v_steps pairs of SymPy numbers ranging from
|
| 170 |
+
(v_min, v_min + step) to (v_max - step, v_max).
|
| 171 |
+
"""
|
| 172 |
+
d = (self.v_max - self.v_min) / self.v_steps
|
| 173 |
+
a = self.v_min + (d * S.Zero)
|
| 174 |
+
for i in range(self.v_steps):
|
| 175 |
+
b = self.v_min + (d * Integer(i + 1))
|
| 176 |
+
yield a, b
|
| 177 |
+
a = b
|
| 178 |
+
|
| 179 |
+
def frange(self):
|
| 180 |
+
for i in self.vrange():
|
| 181 |
+
yield float(i.evalf())
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_mode.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .plot_interval import PlotInterval
|
| 2 |
+
from .plot_object import PlotObject
|
| 3 |
+
from .util import parse_option_string
|
| 4 |
+
from sympy.core.symbol import Symbol
|
| 5 |
+
from sympy.core.sympify import sympify
|
| 6 |
+
from sympy.geometry.entity import GeometryEntity
|
| 7 |
+
from sympy.utilities.iterables import is_sequence
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class PlotMode(PlotObject):
|
| 11 |
+
"""
|
| 12 |
+
Grandparent class for plotting
|
| 13 |
+
modes. Serves as interface for
|
| 14 |
+
registration, lookup, and init
|
| 15 |
+
of modes.
|
| 16 |
+
|
| 17 |
+
To create a new plot mode,
|
| 18 |
+
inherit from PlotModeBase
|
| 19 |
+
or one of its children, such
|
| 20 |
+
as PlotSurface or PlotCurve.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
## Class-level attributes
|
| 24 |
+
## used to register and lookup
|
| 25 |
+
## plot modes. See PlotModeBase
|
| 26 |
+
## for descriptions and usage.
|
| 27 |
+
|
| 28 |
+
i_vars, d_vars = '', ''
|
| 29 |
+
intervals = []
|
| 30 |
+
aliases = []
|
| 31 |
+
is_default = False
|
| 32 |
+
|
| 33 |
+
## Draw is the only method here which
|
| 34 |
+
## is meant to be overridden in child
|
| 35 |
+
## classes, and PlotModeBase provides
|
| 36 |
+
## a base implementation.
|
| 37 |
+
def draw(self):
|
| 38 |
+
raise NotImplementedError()
|
| 39 |
+
|
| 40 |
+
## Everything else in this file has to
|
| 41 |
+
## do with registration and retrieval
|
| 42 |
+
## of plot modes. This is where I've
|
| 43 |
+
## hidden much of the ugliness of automatic
|
| 44 |
+
## plot mode divination...
|
| 45 |
+
|
| 46 |
+
## Plot mode registry data structures
|
| 47 |
+
_mode_alias_list = []
|
| 48 |
+
_mode_map = {
|
| 49 |
+
1: {1: {}, 2: {}},
|
| 50 |
+
2: {1: {}, 2: {}},
|
| 51 |
+
3: {1: {}, 2: {}},
|
| 52 |
+
} # [d][i][alias_str]: class
|
| 53 |
+
_mode_default_map = {
|
| 54 |
+
1: {},
|
| 55 |
+
2: {},
|
| 56 |
+
3: {},
|
| 57 |
+
} # [d][i]: class
|
| 58 |
+
_i_var_max, _d_var_max = 2, 3
|
| 59 |
+
|
| 60 |
+
def __new__(cls, *args, **kwargs):
|
| 61 |
+
"""
|
| 62 |
+
This is the function which interprets
|
| 63 |
+
arguments given to Plot.__init__ and
|
| 64 |
+
Plot.__setattr__. Returns an initialized
|
| 65 |
+
instance of the appropriate child class.
|
| 66 |
+
"""
|
| 67 |
+
|
| 68 |
+
newargs, newkwargs = PlotMode._extract_options(args, kwargs)
|
| 69 |
+
mode_arg = newkwargs.get('mode', '')
|
| 70 |
+
|
| 71 |
+
# Interpret the arguments
|
| 72 |
+
d_vars, intervals = PlotMode._interpret_args(newargs)
|
| 73 |
+
i_vars = PlotMode._find_i_vars(d_vars, intervals)
|
| 74 |
+
i, d = max([len(i_vars), len(intervals)]), len(d_vars)
|
| 75 |
+
|
| 76 |
+
# Find the appropriate mode
|
| 77 |
+
subcls = PlotMode._get_mode(mode_arg, i, d)
|
| 78 |
+
|
| 79 |
+
# Create the object
|
| 80 |
+
o = object.__new__(subcls)
|
| 81 |
+
|
| 82 |
+
# Do some setup for the mode instance
|
| 83 |
+
o.d_vars = d_vars
|
| 84 |
+
o._fill_i_vars(i_vars)
|
| 85 |
+
o._fill_intervals(intervals)
|
| 86 |
+
o.options = newkwargs
|
| 87 |
+
|
| 88 |
+
return o
|
| 89 |
+
|
| 90 |
+
@staticmethod
|
| 91 |
+
def _get_mode(mode_arg, i_var_count, d_var_count):
|
| 92 |
+
"""
|
| 93 |
+
Tries to return an appropriate mode class.
|
| 94 |
+
Intended to be called only by __new__.
|
| 95 |
+
|
| 96 |
+
mode_arg
|
| 97 |
+
Can be a string or a class. If it is a
|
| 98 |
+
PlotMode subclass, it is simply returned.
|
| 99 |
+
If it is a string, it can an alias for
|
| 100 |
+
a mode or an empty string. In the latter
|
| 101 |
+
case, we try to find a default mode for
|
| 102 |
+
the i_var_count and d_var_count.
|
| 103 |
+
|
| 104 |
+
i_var_count
|
| 105 |
+
The number of independent variables
|
| 106 |
+
needed to evaluate the d_vars.
|
| 107 |
+
|
| 108 |
+
d_var_count
|
| 109 |
+
The number of dependent variables;
|
| 110 |
+
usually the number of functions to
|
| 111 |
+
be evaluated in plotting.
|
| 112 |
+
|
| 113 |
+
For example, a Cartesian function y = f(x) has
|
| 114 |
+
one i_var (x) and one d_var (y). A parametric
|
| 115 |
+
form x,y,z = f(u,v), f(u,v), f(u,v) has two
|
| 116 |
+
two i_vars (u,v) and three d_vars (x,y,z).
|
| 117 |
+
"""
|
| 118 |
+
# if the mode_arg is simply a PlotMode class,
|
| 119 |
+
# check that the mode supports the numbers
|
| 120 |
+
# of independent and dependent vars, then
|
| 121 |
+
# return it
|
| 122 |
+
try:
|
| 123 |
+
m = None
|
| 124 |
+
if issubclass(mode_arg, PlotMode):
|
| 125 |
+
m = mode_arg
|
| 126 |
+
except TypeError:
|
| 127 |
+
pass
|
| 128 |
+
if m:
|
| 129 |
+
if not m._was_initialized:
|
| 130 |
+
raise ValueError(("To use unregistered plot mode %s "
|
| 131 |
+
"you must first call %s._init_mode().")
|
| 132 |
+
% (m.__name__, m.__name__))
|
| 133 |
+
if d_var_count != m.d_var_count:
|
| 134 |
+
raise ValueError(("%s can only plot functions "
|
| 135 |
+
"with %i dependent variables.")
|
| 136 |
+
% (m.__name__,
|
| 137 |
+
m.d_var_count))
|
| 138 |
+
if i_var_count > m.i_var_count:
|
| 139 |
+
raise ValueError(("%s cannot plot functions "
|
| 140 |
+
"with more than %i independent "
|
| 141 |
+
"variables.")
|
| 142 |
+
% (m.__name__,
|
| 143 |
+
m.i_var_count))
|
| 144 |
+
return m
|
| 145 |
+
# If it is a string, there are two possibilities.
|
| 146 |
+
if isinstance(mode_arg, str):
|
| 147 |
+
i, d = i_var_count, d_var_count
|
| 148 |
+
if i > PlotMode._i_var_max:
|
| 149 |
+
raise ValueError(var_count_error(True, True))
|
| 150 |
+
if d > PlotMode._d_var_max:
|
| 151 |
+
raise ValueError(var_count_error(False, True))
|
| 152 |
+
# If the string is '', try to find a suitable
|
| 153 |
+
# default mode
|
| 154 |
+
if not mode_arg:
|
| 155 |
+
return PlotMode._get_default_mode(i, d)
|
| 156 |
+
# Otherwise, interpret the string as a mode
|
| 157 |
+
# alias (e.g. 'cartesian', 'parametric', etc)
|
| 158 |
+
else:
|
| 159 |
+
return PlotMode._get_aliased_mode(mode_arg, i, d)
|
| 160 |
+
else:
|
| 161 |
+
raise ValueError("PlotMode argument must be "
|
| 162 |
+
"a class or a string")
|
| 163 |
+
|
| 164 |
+
@staticmethod
|
| 165 |
+
def _get_default_mode(i, d, i_vars=-1):
|
| 166 |
+
if i_vars == -1:
|
| 167 |
+
i_vars = i
|
| 168 |
+
try:
|
| 169 |
+
return PlotMode._mode_default_map[d][i]
|
| 170 |
+
except KeyError:
|
| 171 |
+
# Keep looking for modes in higher i var counts
|
| 172 |
+
# which support the given d var count until we
|
| 173 |
+
# reach the max i_var count.
|
| 174 |
+
if i < PlotMode._i_var_max:
|
| 175 |
+
return PlotMode._get_default_mode(i + 1, d, i_vars)
|
| 176 |
+
else:
|
| 177 |
+
raise ValueError(("Couldn't find a default mode "
|
| 178 |
+
"for %i independent and %i "
|
| 179 |
+
"dependent variables.") % (i_vars, d))
|
| 180 |
+
|
| 181 |
+
@staticmethod
|
| 182 |
+
def _get_aliased_mode(alias, i, d, i_vars=-1):
|
| 183 |
+
if i_vars == -1:
|
| 184 |
+
i_vars = i
|
| 185 |
+
if alias not in PlotMode._mode_alias_list:
|
| 186 |
+
raise ValueError(("Couldn't find a mode called"
|
| 187 |
+
" %s. Known modes: %s.")
|
| 188 |
+
% (alias, ", ".join(PlotMode._mode_alias_list)))
|
| 189 |
+
try:
|
| 190 |
+
return PlotMode._mode_map[d][i][alias]
|
| 191 |
+
except TypeError:
|
| 192 |
+
# Keep looking for modes in higher i var counts
|
| 193 |
+
# which support the given d var count and alias
|
| 194 |
+
# until we reach the max i_var count.
|
| 195 |
+
if i < PlotMode._i_var_max:
|
| 196 |
+
return PlotMode._get_aliased_mode(alias, i + 1, d, i_vars)
|
| 197 |
+
else:
|
| 198 |
+
raise ValueError(("Couldn't find a %s mode "
|
| 199 |
+
"for %i independent and %i "
|
| 200 |
+
"dependent variables.")
|
| 201 |
+
% (alias, i_vars, d))
|
| 202 |
+
|
| 203 |
+
@classmethod
|
| 204 |
+
def _register(cls):
|
| 205 |
+
"""
|
| 206 |
+
Called once for each user-usable plot mode.
|
| 207 |
+
For Cartesian2D, it is invoked after the
|
| 208 |
+
class definition: Cartesian2D._register()
|
| 209 |
+
"""
|
| 210 |
+
name = cls.__name__
|
| 211 |
+
cls._init_mode()
|
| 212 |
+
|
| 213 |
+
try:
|
| 214 |
+
i, d = cls.i_var_count, cls.d_var_count
|
| 215 |
+
# Add the mode to _mode_map under all
|
| 216 |
+
# given aliases
|
| 217 |
+
for a in cls.aliases:
|
| 218 |
+
if a not in PlotMode._mode_alias_list:
|
| 219 |
+
# Also track valid aliases, so
|
| 220 |
+
# we can quickly know when given
|
| 221 |
+
# an invalid one in _get_mode.
|
| 222 |
+
PlotMode._mode_alias_list.append(a)
|
| 223 |
+
PlotMode._mode_map[d][i][a] = cls
|
| 224 |
+
if cls.is_default:
|
| 225 |
+
# If this mode was marked as the
|
| 226 |
+
# default for this d,i combination,
|
| 227 |
+
# also set that.
|
| 228 |
+
PlotMode._mode_default_map[d][i] = cls
|
| 229 |
+
|
| 230 |
+
except Exception as e:
|
| 231 |
+
raise RuntimeError(("Failed to register "
|
| 232 |
+
"plot mode %s. Reason: %s")
|
| 233 |
+
% (name, (str(e))))
|
| 234 |
+
|
| 235 |
+
@classmethod
|
| 236 |
+
def _init_mode(cls):
|
| 237 |
+
"""
|
| 238 |
+
Initializes the plot mode based on
|
| 239 |
+
the 'mode-specific parameters' above.
|
| 240 |
+
Only intended to be called by
|
| 241 |
+
PlotMode._register(). To use a mode without
|
| 242 |
+
registering it, you can directly call
|
| 243 |
+
ModeSubclass._init_mode().
|
| 244 |
+
"""
|
| 245 |
+
def symbols_list(symbol_str):
|
| 246 |
+
return [Symbol(s) for s in symbol_str]
|
| 247 |
+
|
| 248 |
+
# Convert the vars strs into
|
| 249 |
+
# lists of symbols.
|
| 250 |
+
cls.i_vars = symbols_list(cls.i_vars)
|
| 251 |
+
cls.d_vars = symbols_list(cls.d_vars)
|
| 252 |
+
|
| 253 |
+
# Var count is used often, calculate
|
| 254 |
+
# it once here
|
| 255 |
+
cls.i_var_count = len(cls.i_vars)
|
| 256 |
+
cls.d_var_count = len(cls.d_vars)
|
| 257 |
+
|
| 258 |
+
if cls.i_var_count > PlotMode._i_var_max:
|
| 259 |
+
raise ValueError(var_count_error(True, False))
|
| 260 |
+
if cls.d_var_count > PlotMode._d_var_max:
|
| 261 |
+
raise ValueError(var_count_error(False, False))
|
| 262 |
+
|
| 263 |
+
# Try to use first alias as primary_alias
|
| 264 |
+
if len(cls.aliases) > 0:
|
| 265 |
+
cls.primary_alias = cls.aliases[0]
|
| 266 |
+
else:
|
| 267 |
+
cls.primary_alias = cls.__name__
|
| 268 |
+
|
| 269 |
+
di = cls.intervals
|
| 270 |
+
if len(di) != cls.i_var_count:
|
| 271 |
+
raise ValueError("Plot mode must provide a "
|
| 272 |
+
"default interval for each i_var.")
|
| 273 |
+
for i in range(cls.i_var_count):
|
| 274 |
+
# default intervals must be given [min,max,steps]
|
| 275 |
+
# (no var, but they must be in the same order as i_vars)
|
| 276 |
+
if len(di[i]) != 3:
|
| 277 |
+
raise ValueError("length should be equal to 3")
|
| 278 |
+
|
| 279 |
+
# Initialize an incomplete interval,
|
| 280 |
+
# to later be filled with a var when
|
| 281 |
+
# the mode is instantiated.
|
| 282 |
+
di[i] = PlotInterval(None, *di[i])
|
| 283 |
+
|
| 284 |
+
# To prevent people from using modes
|
| 285 |
+
# without these required fields set up.
|
| 286 |
+
cls._was_initialized = True
|
| 287 |
+
|
| 288 |
+
_was_initialized = False
|
| 289 |
+
|
| 290 |
+
## Initializer Helper Methods
|
| 291 |
+
|
| 292 |
+
@staticmethod
|
| 293 |
+
def _find_i_vars(functions, intervals):
|
| 294 |
+
i_vars = []
|
| 295 |
+
|
| 296 |
+
# First, collect i_vars in the
|
| 297 |
+
# order they are given in any
|
| 298 |
+
# intervals.
|
| 299 |
+
for i in intervals:
|
| 300 |
+
if i.v is None:
|
| 301 |
+
continue
|
| 302 |
+
elif i.v in i_vars:
|
| 303 |
+
raise ValueError(("Multiple intervals given "
|
| 304 |
+
"for %s.") % (str(i.v)))
|
| 305 |
+
i_vars.append(i.v)
|
| 306 |
+
|
| 307 |
+
# Then, find any remaining
|
| 308 |
+
# i_vars in given functions
|
| 309 |
+
# (aka d_vars)
|
| 310 |
+
for f in functions:
|
| 311 |
+
for a in f.free_symbols:
|
| 312 |
+
if a not in i_vars:
|
| 313 |
+
i_vars.append(a)
|
| 314 |
+
|
| 315 |
+
return i_vars
|
| 316 |
+
|
| 317 |
+
def _fill_i_vars(self, i_vars):
|
| 318 |
+
# copy default i_vars
|
| 319 |
+
self.i_vars = [Symbol(str(i)) for i in self.i_vars]
|
| 320 |
+
# replace with given i_vars
|
| 321 |
+
for i in range(len(i_vars)):
|
| 322 |
+
self.i_vars[i] = i_vars[i]
|
| 323 |
+
|
| 324 |
+
def _fill_intervals(self, intervals):
|
| 325 |
+
# copy default intervals
|
| 326 |
+
self.intervals = [PlotInterval(i) for i in self.intervals]
|
| 327 |
+
# track i_vars used so far
|
| 328 |
+
v_used = []
|
| 329 |
+
# fill copy of default
|
| 330 |
+
# intervals with given info
|
| 331 |
+
for i in range(len(intervals)):
|
| 332 |
+
self.intervals[i].fill_from(intervals[i])
|
| 333 |
+
if self.intervals[i].v is not None:
|
| 334 |
+
v_used.append(self.intervals[i].v)
|
| 335 |
+
# Find any orphan intervals and
|
| 336 |
+
# assign them i_vars
|
| 337 |
+
for i in range(len(self.intervals)):
|
| 338 |
+
if self.intervals[i].v is None:
|
| 339 |
+
u = [v for v in self.i_vars if v not in v_used]
|
| 340 |
+
if len(u) == 0:
|
| 341 |
+
raise ValueError("length should not be equal to 0")
|
| 342 |
+
self.intervals[i].v = u[0]
|
| 343 |
+
v_used.append(u[0])
|
| 344 |
+
|
| 345 |
+
@staticmethod
|
| 346 |
+
def _interpret_args(args):
|
| 347 |
+
interval_wrong_order = "PlotInterval %s was given before any function(s)."
|
| 348 |
+
interpret_error = "Could not interpret %s as a function or interval."
|
| 349 |
+
|
| 350 |
+
functions, intervals = [], []
|
| 351 |
+
if isinstance(args[0], GeometryEntity):
|
| 352 |
+
for coords in list(args[0].arbitrary_point()):
|
| 353 |
+
functions.append(coords)
|
| 354 |
+
intervals.append(PlotInterval.try_parse(args[0].plot_interval()))
|
| 355 |
+
else:
|
| 356 |
+
for a in args:
|
| 357 |
+
i = PlotInterval.try_parse(a)
|
| 358 |
+
if i is not None:
|
| 359 |
+
if len(functions) == 0:
|
| 360 |
+
raise ValueError(interval_wrong_order % (str(i)))
|
| 361 |
+
else:
|
| 362 |
+
intervals.append(i)
|
| 363 |
+
else:
|
| 364 |
+
if is_sequence(a, include=str):
|
| 365 |
+
raise ValueError(interpret_error % (str(a)))
|
| 366 |
+
try:
|
| 367 |
+
f = sympify(a)
|
| 368 |
+
functions.append(f)
|
| 369 |
+
except TypeError:
|
| 370 |
+
raise ValueError(interpret_error % str(a))
|
| 371 |
+
|
| 372 |
+
return functions, intervals
|
| 373 |
+
|
| 374 |
+
@staticmethod
|
| 375 |
+
def _extract_options(args, kwargs):
|
| 376 |
+
newkwargs, newargs = {}, []
|
| 377 |
+
for a in args:
|
| 378 |
+
if isinstance(a, str):
|
| 379 |
+
newkwargs = dict(newkwargs, **parse_option_string(a))
|
| 380 |
+
else:
|
| 381 |
+
newargs.append(a)
|
| 382 |
+
newkwargs = dict(newkwargs, **kwargs)
|
| 383 |
+
return newargs, newkwargs
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def var_count_error(is_independent, is_plotting):
|
| 387 |
+
"""
|
| 388 |
+
Used to format an error message which differs
|
| 389 |
+
slightly in 4 places.
|
| 390 |
+
"""
|
| 391 |
+
if is_plotting:
|
| 392 |
+
v = "Plotting"
|
| 393 |
+
else:
|
| 394 |
+
v = "Registering plot modes"
|
| 395 |
+
if is_independent:
|
| 396 |
+
n, s = PlotMode._i_var_max, "independent"
|
| 397 |
+
else:
|
| 398 |
+
n, s = PlotMode._d_var_max, "dependent"
|
| 399 |
+
return ("%s with more than %i %s variables "
|
| 400 |
+
"is not supported.") % (v, n, s)
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_mode_base.py
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyglet.gl as pgl
|
| 2 |
+
from sympy.core import S
|
| 3 |
+
from sympy.plotting.pygletplot.color_scheme import ColorScheme
|
| 4 |
+
from sympy.plotting.pygletplot.plot_mode import PlotMode
|
| 5 |
+
from sympy.utilities.iterables import is_sequence
|
| 6 |
+
from time import sleep
|
| 7 |
+
from threading import Thread, Event, RLock
|
| 8 |
+
import warnings
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PlotModeBase(PlotMode):
|
| 12 |
+
"""
|
| 13 |
+
Intended parent class for plotting
|
| 14 |
+
modes. Provides base functionality
|
| 15 |
+
in conjunction with its parent,
|
| 16 |
+
PlotMode.
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
##
|
| 20 |
+
## Class-Level Attributes
|
| 21 |
+
##
|
| 22 |
+
|
| 23 |
+
"""
|
| 24 |
+
The following attributes are meant
|
| 25 |
+
to be set at the class level, and serve
|
| 26 |
+
as parameters to the plot mode registry
|
| 27 |
+
(in PlotMode). See plot_modes.py for
|
| 28 |
+
concrete examples.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
"""
|
| 32 |
+
i_vars
|
| 33 |
+
'x' for Cartesian2D
|
| 34 |
+
'xy' for Cartesian3D
|
| 35 |
+
etc.
|
| 36 |
+
|
| 37 |
+
d_vars
|
| 38 |
+
'y' for Cartesian2D
|
| 39 |
+
'r' for Polar
|
| 40 |
+
etc.
|
| 41 |
+
"""
|
| 42 |
+
i_vars, d_vars = '', ''
|
| 43 |
+
|
| 44 |
+
"""
|
| 45 |
+
intervals
|
| 46 |
+
Default intervals for each i_var, and in the
|
| 47 |
+
same order. Specified [min, max, steps].
|
| 48 |
+
No variable can be given (it is bound later).
|
| 49 |
+
"""
|
| 50 |
+
intervals = []
|
| 51 |
+
|
| 52 |
+
"""
|
| 53 |
+
aliases
|
| 54 |
+
A list of strings which can be used to
|
| 55 |
+
access this mode.
|
| 56 |
+
'cartesian' for Cartesian2D and Cartesian3D
|
| 57 |
+
'polar' for Polar
|
| 58 |
+
'cylindrical', 'polar' for Cylindrical
|
| 59 |
+
|
| 60 |
+
Note that _init_mode chooses the first alias
|
| 61 |
+
in the list as the mode's primary_alias, which
|
| 62 |
+
will be displayed to the end user in certain
|
| 63 |
+
contexts.
|
| 64 |
+
"""
|
| 65 |
+
aliases = []
|
| 66 |
+
|
| 67 |
+
"""
|
| 68 |
+
is_default
|
| 69 |
+
Whether to set this mode as the default
|
| 70 |
+
for arguments passed to PlotMode() containing
|
| 71 |
+
the same number of d_vars as this mode and
|
| 72 |
+
at most the same number of i_vars.
|
| 73 |
+
"""
|
| 74 |
+
is_default = False
|
| 75 |
+
|
| 76 |
+
"""
|
| 77 |
+
All of the above attributes are defined in PlotMode.
|
| 78 |
+
The following ones are specific to PlotModeBase.
|
| 79 |
+
"""
|
| 80 |
+
|
| 81 |
+
"""
|
| 82 |
+
A list of the render styles. Do not modify.
|
| 83 |
+
"""
|
| 84 |
+
styles = {'wireframe': 1, 'solid': 2, 'both': 3}
|
| 85 |
+
|
| 86 |
+
"""
|
| 87 |
+
style_override
|
| 88 |
+
Always use this style if not blank.
|
| 89 |
+
"""
|
| 90 |
+
style_override = ''
|
| 91 |
+
|
| 92 |
+
"""
|
| 93 |
+
default_wireframe_color
|
| 94 |
+
default_solid_color
|
| 95 |
+
Can be used when color is None or being calculated.
|
| 96 |
+
Used by PlotCurve and PlotSurface, but not anywhere
|
| 97 |
+
in PlotModeBase.
|
| 98 |
+
"""
|
| 99 |
+
|
| 100 |
+
default_wireframe_color = (0.85, 0.85, 0.85)
|
| 101 |
+
default_solid_color = (0.6, 0.6, 0.9)
|
| 102 |
+
default_rot_preset = 'xy'
|
| 103 |
+
|
| 104 |
+
##
|
| 105 |
+
## Instance-Level Attributes
|
| 106 |
+
##
|
| 107 |
+
|
| 108 |
+
## 'Abstract' member functions
|
| 109 |
+
def _get_evaluator(self):
|
| 110 |
+
if self.use_lambda_eval:
|
| 111 |
+
try:
|
| 112 |
+
e = self._get_lambda_evaluator()
|
| 113 |
+
return e
|
| 114 |
+
except Exception:
|
| 115 |
+
warnings.warn("\nWarning: creating lambda evaluator failed. "
|
| 116 |
+
"Falling back on SymPy subs evaluator.")
|
| 117 |
+
return self._get_sympy_evaluator()
|
| 118 |
+
|
| 119 |
+
def _get_sympy_evaluator(self):
|
| 120 |
+
raise NotImplementedError()
|
| 121 |
+
|
| 122 |
+
def _get_lambda_evaluator(self):
|
| 123 |
+
raise NotImplementedError()
|
| 124 |
+
|
| 125 |
+
def _on_calculate_verts(self):
|
| 126 |
+
raise NotImplementedError()
|
| 127 |
+
|
| 128 |
+
def _on_calculate_cverts(self):
|
| 129 |
+
raise NotImplementedError()
|
| 130 |
+
|
| 131 |
+
## Base member functions
|
| 132 |
+
def __init__(self, *args, bounds_callback=None, **kwargs):
|
| 133 |
+
self.verts = []
|
| 134 |
+
self.cverts = []
|
| 135 |
+
self.bounds = [[S.Infinity, S.NegativeInfinity, 0],
|
| 136 |
+
[S.Infinity, S.NegativeInfinity, 0],
|
| 137 |
+
[S.Infinity, S.NegativeInfinity, 0]]
|
| 138 |
+
self.cbounds = [[S.Infinity, S.NegativeInfinity, 0],
|
| 139 |
+
[S.Infinity, S.NegativeInfinity, 0],
|
| 140 |
+
[S.Infinity, S.NegativeInfinity, 0]]
|
| 141 |
+
|
| 142 |
+
self._draw_lock = RLock()
|
| 143 |
+
|
| 144 |
+
self._calculating_verts = Event()
|
| 145 |
+
self._calculating_cverts = Event()
|
| 146 |
+
self._calculating_verts_pos = 0.0
|
| 147 |
+
self._calculating_verts_len = 0.0
|
| 148 |
+
self._calculating_cverts_pos = 0.0
|
| 149 |
+
self._calculating_cverts_len = 0.0
|
| 150 |
+
|
| 151 |
+
self._max_render_stack_size = 3
|
| 152 |
+
self._draw_wireframe = [-1]
|
| 153 |
+
self._draw_solid = [-1]
|
| 154 |
+
|
| 155 |
+
self._style = None
|
| 156 |
+
self._color = None
|
| 157 |
+
|
| 158 |
+
self.predraw = []
|
| 159 |
+
self.postdraw = []
|
| 160 |
+
|
| 161 |
+
self.use_lambda_eval = self.options.pop('use_sympy_eval', None) is None
|
| 162 |
+
self.style = self.options.pop('style', '')
|
| 163 |
+
self.color = self.options.pop('color', 'rainbow')
|
| 164 |
+
self.bounds_callback = bounds_callback
|
| 165 |
+
|
| 166 |
+
self._on_calculate()
|
| 167 |
+
|
| 168 |
+
def synchronized(f):
|
| 169 |
+
def w(self, *args, **kwargs):
|
| 170 |
+
self._draw_lock.acquire()
|
| 171 |
+
try:
|
| 172 |
+
r = f(self, *args, **kwargs)
|
| 173 |
+
return r
|
| 174 |
+
finally:
|
| 175 |
+
self._draw_lock.release()
|
| 176 |
+
return w
|
| 177 |
+
|
| 178 |
+
@synchronized
|
| 179 |
+
def push_wireframe(self, function):
|
| 180 |
+
"""
|
| 181 |
+
Push a function which performs gl commands
|
| 182 |
+
used to build a display list. (The list is
|
| 183 |
+
built outside of the function)
|
| 184 |
+
"""
|
| 185 |
+
assert callable(function)
|
| 186 |
+
self._draw_wireframe.append(function)
|
| 187 |
+
if len(self._draw_wireframe) > self._max_render_stack_size:
|
| 188 |
+
del self._draw_wireframe[1] # leave marker element
|
| 189 |
+
|
| 190 |
+
@synchronized
|
| 191 |
+
def push_solid(self, function):
|
| 192 |
+
"""
|
| 193 |
+
Push a function which performs gl commands
|
| 194 |
+
used to build a display list. (The list is
|
| 195 |
+
built outside of the function)
|
| 196 |
+
"""
|
| 197 |
+
assert callable(function)
|
| 198 |
+
self._draw_solid.append(function)
|
| 199 |
+
if len(self._draw_solid) > self._max_render_stack_size:
|
| 200 |
+
del self._draw_solid[1] # leave marker element
|
| 201 |
+
|
| 202 |
+
def _create_display_list(self, function):
|
| 203 |
+
dl = pgl.glGenLists(1)
|
| 204 |
+
pgl.glNewList(dl, pgl.GL_COMPILE)
|
| 205 |
+
function()
|
| 206 |
+
pgl.glEndList()
|
| 207 |
+
return dl
|
| 208 |
+
|
| 209 |
+
def _render_stack_top(self, render_stack):
|
| 210 |
+
top = render_stack[-1]
|
| 211 |
+
if top == -1:
|
| 212 |
+
return -1 # nothing to display
|
| 213 |
+
elif callable(top):
|
| 214 |
+
dl = self._create_display_list(top)
|
| 215 |
+
render_stack[-1] = (dl, top)
|
| 216 |
+
return dl # display newly added list
|
| 217 |
+
elif len(top) == 2:
|
| 218 |
+
if pgl.GL_TRUE == pgl.glIsList(top[0]):
|
| 219 |
+
return top[0] # display stored list
|
| 220 |
+
dl = self._create_display_list(top[1])
|
| 221 |
+
render_stack[-1] = (dl, top[1])
|
| 222 |
+
return dl # display regenerated list
|
| 223 |
+
|
| 224 |
+
def _draw_solid_display_list(self, dl):
|
| 225 |
+
pgl.glPushAttrib(pgl.GL_ENABLE_BIT | pgl.GL_POLYGON_BIT)
|
| 226 |
+
pgl.glPolygonMode(pgl.GL_FRONT_AND_BACK, pgl.GL_FILL)
|
| 227 |
+
pgl.glCallList(dl)
|
| 228 |
+
pgl.glPopAttrib()
|
| 229 |
+
|
| 230 |
+
def _draw_wireframe_display_list(self, dl):
|
| 231 |
+
pgl.glPushAttrib(pgl.GL_ENABLE_BIT | pgl.GL_POLYGON_BIT)
|
| 232 |
+
pgl.glPolygonMode(pgl.GL_FRONT_AND_BACK, pgl.GL_LINE)
|
| 233 |
+
pgl.glEnable(pgl.GL_POLYGON_OFFSET_LINE)
|
| 234 |
+
pgl.glPolygonOffset(-0.005, -50.0)
|
| 235 |
+
pgl.glCallList(dl)
|
| 236 |
+
pgl.glPopAttrib()
|
| 237 |
+
|
| 238 |
+
@synchronized
|
| 239 |
+
def draw(self):
|
| 240 |
+
for f in self.predraw:
|
| 241 |
+
if callable(f):
|
| 242 |
+
f()
|
| 243 |
+
if self.style_override:
|
| 244 |
+
style = self.styles[self.style_override]
|
| 245 |
+
else:
|
| 246 |
+
style = self.styles[self._style]
|
| 247 |
+
# Draw solid component if style includes solid
|
| 248 |
+
if style & 2:
|
| 249 |
+
dl = self._render_stack_top(self._draw_solid)
|
| 250 |
+
if dl > 0 and pgl.GL_TRUE == pgl.glIsList(dl):
|
| 251 |
+
self._draw_solid_display_list(dl)
|
| 252 |
+
# Draw wireframe component if style includes wireframe
|
| 253 |
+
if style & 1:
|
| 254 |
+
dl = self._render_stack_top(self._draw_wireframe)
|
| 255 |
+
if dl > 0 and pgl.GL_TRUE == pgl.glIsList(dl):
|
| 256 |
+
self._draw_wireframe_display_list(dl)
|
| 257 |
+
for f in self.postdraw:
|
| 258 |
+
if callable(f):
|
| 259 |
+
f()
|
| 260 |
+
|
| 261 |
+
def _on_change_color(self, color):
|
| 262 |
+
Thread(target=self._calculate_cverts).start()
|
| 263 |
+
|
| 264 |
+
def _on_calculate(self):
|
| 265 |
+
Thread(target=self._calculate_all).start()
|
| 266 |
+
|
| 267 |
+
def _calculate_all(self):
|
| 268 |
+
self._calculate_verts()
|
| 269 |
+
self._calculate_cverts()
|
| 270 |
+
|
| 271 |
+
def _calculate_verts(self):
|
| 272 |
+
if self._calculating_verts.is_set():
|
| 273 |
+
return
|
| 274 |
+
self._calculating_verts.set()
|
| 275 |
+
try:
|
| 276 |
+
self._on_calculate_verts()
|
| 277 |
+
finally:
|
| 278 |
+
self._calculating_verts.clear()
|
| 279 |
+
if callable(self.bounds_callback):
|
| 280 |
+
self.bounds_callback()
|
| 281 |
+
|
| 282 |
+
def _calculate_cverts(self):
|
| 283 |
+
if self._calculating_verts.is_set():
|
| 284 |
+
return
|
| 285 |
+
while self._calculating_cverts.is_set():
|
| 286 |
+
sleep(0) # wait for previous calculation
|
| 287 |
+
self._calculating_cverts.set()
|
| 288 |
+
try:
|
| 289 |
+
self._on_calculate_cverts()
|
| 290 |
+
finally:
|
| 291 |
+
self._calculating_cverts.clear()
|
| 292 |
+
|
| 293 |
+
def _get_calculating_verts(self):
|
| 294 |
+
return self._calculating_verts.is_set()
|
| 295 |
+
|
| 296 |
+
def _get_calculating_verts_pos(self):
|
| 297 |
+
return self._calculating_verts_pos
|
| 298 |
+
|
| 299 |
+
def _get_calculating_verts_len(self):
|
| 300 |
+
return self._calculating_verts_len
|
| 301 |
+
|
| 302 |
+
def _get_calculating_cverts(self):
|
| 303 |
+
return self._calculating_cverts.is_set()
|
| 304 |
+
|
| 305 |
+
def _get_calculating_cverts_pos(self):
|
| 306 |
+
return self._calculating_cverts_pos
|
| 307 |
+
|
| 308 |
+
def _get_calculating_cverts_len(self):
|
| 309 |
+
return self._calculating_cverts_len
|
| 310 |
+
|
| 311 |
+
## Property handlers
|
| 312 |
+
def _get_style(self):
|
| 313 |
+
return self._style
|
| 314 |
+
|
| 315 |
+
@synchronized
|
| 316 |
+
def _set_style(self, v):
|
| 317 |
+
if v is None:
|
| 318 |
+
return
|
| 319 |
+
if v == '':
|
| 320 |
+
step_max = 0
|
| 321 |
+
for i in self.intervals:
|
| 322 |
+
if i.v_steps is None:
|
| 323 |
+
continue
|
| 324 |
+
step_max = max([step_max, int(i.v_steps)])
|
| 325 |
+
v = ['both', 'solid'][step_max > 40]
|
| 326 |
+
if v not in self.styles:
|
| 327 |
+
raise ValueError("v should be there in self.styles")
|
| 328 |
+
if v == self._style:
|
| 329 |
+
return
|
| 330 |
+
self._style = v
|
| 331 |
+
|
| 332 |
+
def _get_color(self):
|
| 333 |
+
return self._color
|
| 334 |
+
|
| 335 |
+
@synchronized
|
| 336 |
+
def _set_color(self, v):
|
| 337 |
+
try:
|
| 338 |
+
if v is not None:
|
| 339 |
+
if is_sequence(v):
|
| 340 |
+
v = ColorScheme(*v)
|
| 341 |
+
else:
|
| 342 |
+
v = ColorScheme(v)
|
| 343 |
+
if repr(v) == repr(self._color):
|
| 344 |
+
return
|
| 345 |
+
self._on_change_color(v)
|
| 346 |
+
self._color = v
|
| 347 |
+
except Exception as e:
|
| 348 |
+
raise RuntimeError("Color change failed. "
|
| 349 |
+
"Reason: %s" % (str(e)))
|
| 350 |
+
|
| 351 |
+
style = property(_get_style, _set_style)
|
| 352 |
+
color = property(_get_color, _set_color)
|
| 353 |
+
|
| 354 |
+
calculating_verts = property(_get_calculating_verts)
|
| 355 |
+
calculating_verts_pos = property(_get_calculating_verts_pos)
|
| 356 |
+
calculating_verts_len = property(_get_calculating_verts_len)
|
| 357 |
+
|
| 358 |
+
calculating_cverts = property(_get_calculating_cverts)
|
| 359 |
+
calculating_cverts_pos = property(_get_calculating_cverts_pos)
|
| 360 |
+
calculating_cverts_len = property(_get_calculating_cverts_len)
|
| 361 |
+
|
| 362 |
+
## String representations
|
| 363 |
+
|
| 364 |
+
def __str__(self):
|
| 365 |
+
f = ", ".join(str(d) for d in self.d_vars)
|
| 366 |
+
o = "'mode=%s'" % (self.primary_alias)
|
| 367 |
+
return ", ".join([f, o])
|
| 368 |
+
|
| 369 |
+
def __repr__(self):
|
| 370 |
+
f = ", ".join(str(d) for d in self.d_vars)
|
| 371 |
+
i = ", ".join(str(i) for i in self.intervals)
|
| 372 |
+
d = [('mode', self.primary_alias),
|
| 373 |
+
('color', str(self.color)),
|
| 374 |
+
('style', str(self.style))]
|
| 375 |
+
|
| 376 |
+
o = "'%s'" % ("; ".join("%s=%s" % (k, v)
|
| 377 |
+
for k, v in d if v != 'None'))
|
| 378 |
+
return ", ".join([f, i, o])
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_modes.py
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.utilities.lambdify import lambdify
|
| 2 |
+
from sympy.core.numbers import pi
|
| 3 |
+
from sympy.functions import sin, cos
|
| 4 |
+
from sympy.plotting.pygletplot.plot_curve import PlotCurve
|
| 5 |
+
from sympy.plotting.pygletplot.plot_surface import PlotSurface
|
| 6 |
+
|
| 7 |
+
from math import sin as p_sin
|
| 8 |
+
from math import cos as p_cos
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def float_vec3(f):
|
| 12 |
+
def inner(*args):
|
| 13 |
+
v = f(*args)
|
| 14 |
+
return float(v[0]), float(v[1]), float(v[2])
|
| 15 |
+
return inner
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class Cartesian2D(PlotCurve):
|
| 19 |
+
i_vars, d_vars = 'x', 'y'
|
| 20 |
+
intervals = [[-5, 5, 100]]
|
| 21 |
+
aliases = ['cartesian']
|
| 22 |
+
is_default = True
|
| 23 |
+
|
| 24 |
+
def _get_sympy_evaluator(self):
|
| 25 |
+
fy = self.d_vars[0]
|
| 26 |
+
x = self.t_interval.v
|
| 27 |
+
|
| 28 |
+
@float_vec3
|
| 29 |
+
def e(_x):
|
| 30 |
+
return (_x, fy.subs(x, _x), 0.0)
|
| 31 |
+
return e
|
| 32 |
+
|
| 33 |
+
def _get_lambda_evaluator(self):
|
| 34 |
+
fy = self.d_vars[0]
|
| 35 |
+
x = self.t_interval.v
|
| 36 |
+
return lambdify([x], [x, fy, 0.0])
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Cartesian3D(PlotSurface):
|
| 40 |
+
i_vars, d_vars = 'xy', 'z'
|
| 41 |
+
intervals = [[-1, 1, 40], [-1, 1, 40]]
|
| 42 |
+
aliases = ['cartesian', 'monge']
|
| 43 |
+
is_default = True
|
| 44 |
+
|
| 45 |
+
def _get_sympy_evaluator(self):
|
| 46 |
+
fz = self.d_vars[0]
|
| 47 |
+
x = self.u_interval.v
|
| 48 |
+
y = self.v_interval.v
|
| 49 |
+
|
| 50 |
+
@float_vec3
|
| 51 |
+
def e(_x, _y):
|
| 52 |
+
return (_x, _y, fz.subs(x, _x).subs(y, _y))
|
| 53 |
+
return e
|
| 54 |
+
|
| 55 |
+
def _get_lambda_evaluator(self):
|
| 56 |
+
fz = self.d_vars[0]
|
| 57 |
+
x = self.u_interval.v
|
| 58 |
+
y = self.v_interval.v
|
| 59 |
+
return lambdify([x, y], [x, y, fz])
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
class ParametricCurve2D(PlotCurve):
|
| 63 |
+
i_vars, d_vars = 't', 'xy'
|
| 64 |
+
intervals = [[0, 2*pi, 100]]
|
| 65 |
+
aliases = ['parametric']
|
| 66 |
+
is_default = True
|
| 67 |
+
|
| 68 |
+
def _get_sympy_evaluator(self):
|
| 69 |
+
fx, fy = self.d_vars
|
| 70 |
+
t = self.t_interval.v
|
| 71 |
+
|
| 72 |
+
@float_vec3
|
| 73 |
+
def e(_t):
|
| 74 |
+
return (fx.subs(t, _t), fy.subs(t, _t), 0.0)
|
| 75 |
+
return e
|
| 76 |
+
|
| 77 |
+
def _get_lambda_evaluator(self):
|
| 78 |
+
fx, fy = self.d_vars
|
| 79 |
+
t = self.t_interval.v
|
| 80 |
+
return lambdify([t], [fx, fy, 0.0])
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class ParametricCurve3D(PlotCurve):
|
| 84 |
+
i_vars, d_vars = 't', 'xyz'
|
| 85 |
+
intervals = [[0, 2*pi, 100]]
|
| 86 |
+
aliases = ['parametric']
|
| 87 |
+
is_default = True
|
| 88 |
+
|
| 89 |
+
def _get_sympy_evaluator(self):
|
| 90 |
+
fx, fy, fz = self.d_vars
|
| 91 |
+
t = self.t_interval.v
|
| 92 |
+
|
| 93 |
+
@float_vec3
|
| 94 |
+
def e(_t):
|
| 95 |
+
return (fx.subs(t, _t), fy.subs(t, _t), fz.subs(t, _t))
|
| 96 |
+
return e
|
| 97 |
+
|
| 98 |
+
def _get_lambda_evaluator(self):
|
| 99 |
+
fx, fy, fz = self.d_vars
|
| 100 |
+
t = self.t_interval.v
|
| 101 |
+
return lambdify([t], [fx, fy, fz])
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
class ParametricSurface(PlotSurface):
|
| 105 |
+
i_vars, d_vars = 'uv', 'xyz'
|
| 106 |
+
intervals = [[-1, 1, 40], [-1, 1, 40]]
|
| 107 |
+
aliases = ['parametric']
|
| 108 |
+
is_default = True
|
| 109 |
+
|
| 110 |
+
def _get_sympy_evaluator(self):
|
| 111 |
+
fx, fy, fz = self.d_vars
|
| 112 |
+
u = self.u_interval.v
|
| 113 |
+
v = self.v_interval.v
|
| 114 |
+
|
| 115 |
+
@float_vec3
|
| 116 |
+
def e(_u, _v):
|
| 117 |
+
return (fx.subs(u, _u).subs(v, _v),
|
| 118 |
+
fy.subs(u, _u).subs(v, _v),
|
| 119 |
+
fz.subs(u, _u).subs(v, _v))
|
| 120 |
+
return e
|
| 121 |
+
|
| 122 |
+
def _get_lambda_evaluator(self):
|
| 123 |
+
fx, fy, fz = self.d_vars
|
| 124 |
+
u = self.u_interval.v
|
| 125 |
+
v = self.v_interval.v
|
| 126 |
+
return lambdify([u, v], [fx, fy, fz])
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
class Polar(PlotCurve):
|
| 130 |
+
i_vars, d_vars = 't', 'r'
|
| 131 |
+
intervals = [[0, 2*pi, 100]]
|
| 132 |
+
aliases = ['polar']
|
| 133 |
+
is_default = False
|
| 134 |
+
|
| 135 |
+
def _get_sympy_evaluator(self):
|
| 136 |
+
fr = self.d_vars[0]
|
| 137 |
+
t = self.t_interval.v
|
| 138 |
+
|
| 139 |
+
def e(_t):
|
| 140 |
+
_r = float(fr.subs(t, _t))
|
| 141 |
+
return (_r*p_cos(_t), _r*p_sin(_t), 0.0)
|
| 142 |
+
return e
|
| 143 |
+
|
| 144 |
+
def _get_lambda_evaluator(self):
|
| 145 |
+
fr = self.d_vars[0]
|
| 146 |
+
t = self.t_interval.v
|
| 147 |
+
fx, fy = fr*cos(t), fr*sin(t)
|
| 148 |
+
return lambdify([t], [fx, fy, 0.0])
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
class Cylindrical(PlotSurface):
|
| 152 |
+
i_vars, d_vars = 'th', 'r'
|
| 153 |
+
intervals = [[0, 2*pi, 40], [-1, 1, 20]]
|
| 154 |
+
aliases = ['cylindrical', 'polar']
|
| 155 |
+
is_default = False
|
| 156 |
+
|
| 157 |
+
def _get_sympy_evaluator(self):
|
| 158 |
+
fr = self.d_vars[0]
|
| 159 |
+
t = self.u_interval.v
|
| 160 |
+
h = self.v_interval.v
|
| 161 |
+
|
| 162 |
+
def e(_t, _h):
|
| 163 |
+
_r = float(fr.subs(t, _t).subs(h, _h))
|
| 164 |
+
return (_r*p_cos(_t), _r*p_sin(_t), _h)
|
| 165 |
+
return e
|
| 166 |
+
|
| 167 |
+
def _get_lambda_evaluator(self):
|
| 168 |
+
fr = self.d_vars[0]
|
| 169 |
+
t = self.u_interval.v
|
| 170 |
+
h = self.v_interval.v
|
| 171 |
+
fx, fy = fr*cos(t), fr*sin(t)
|
| 172 |
+
return lambdify([t, h], [fx, fy, h])
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class Spherical(PlotSurface):
|
| 176 |
+
i_vars, d_vars = 'tp', 'r'
|
| 177 |
+
intervals = [[0, 2*pi, 40], [0, pi, 20]]
|
| 178 |
+
aliases = ['spherical']
|
| 179 |
+
is_default = False
|
| 180 |
+
|
| 181 |
+
def _get_sympy_evaluator(self):
|
| 182 |
+
fr = self.d_vars[0]
|
| 183 |
+
t = self.u_interval.v
|
| 184 |
+
p = self.v_interval.v
|
| 185 |
+
|
| 186 |
+
def e(_t, _p):
|
| 187 |
+
_r = float(fr.subs(t, _t).subs(p, _p))
|
| 188 |
+
return (_r*p_cos(_t)*p_sin(_p),
|
| 189 |
+
_r*p_sin(_t)*p_sin(_p),
|
| 190 |
+
_r*p_cos(_p))
|
| 191 |
+
return e
|
| 192 |
+
|
| 193 |
+
def _get_lambda_evaluator(self):
|
| 194 |
+
fr = self.d_vars[0]
|
| 195 |
+
t = self.u_interval.v
|
| 196 |
+
p = self.v_interval.v
|
| 197 |
+
fx = fr * cos(t) * sin(p)
|
| 198 |
+
fy = fr * sin(t) * sin(p)
|
| 199 |
+
fz = fr * cos(p)
|
| 200 |
+
return lambdify([t, p], [fx, fy, fz])
|
| 201 |
+
|
| 202 |
+
Cartesian2D._register()
|
| 203 |
+
Cartesian3D._register()
|
| 204 |
+
ParametricCurve2D._register()
|
| 205 |
+
ParametricCurve3D._register()
|
| 206 |
+
ParametricSurface._register()
|
| 207 |
+
Polar._register()
|
| 208 |
+
Cylindrical._register()
|
| 209 |
+
Spherical._register()
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_object.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class PlotObject:
|
| 2 |
+
"""
|
| 3 |
+
Base class for objects which can be displayed in
|
| 4 |
+
a Plot.
|
| 5 |
+
"""
|
| 6 |
+
visible = True
|
| 7 |
+
|
| 8 |
+
def _draw(self):
|
| 9 |
+
if self.visible:
|
| 10 |
+
self.draw()
|
| 11 |
+
|
| 12 |
+
def draw(self):
|
| 13 |
+
"""
|
| 14 |
+
OpenGL rendering code for the plot object.
|
| 15 |
+
Override in base class.
|
| 16 |
+
"""
|
| 17 |
+
pass
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_rotation.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
try:
|
| 2 |
+
from ctypes import c_float
|
| 3 |
+
except ImportError:
|
| 4 |
+
pass
|
| 5 |
+
|
| 6 |
+
import pyglet.gl as pgl
|
| 7 |
+
from math import sqrt as _sqrt, acos as _acos
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def cross(a, b):
|
| 11 |
+
return (a[1] * b[2] - a[2] * b[1],
|
| 12 |
+
a[2] * b[0] - a[0] * b[2],
|
| 13 |
+
a[0] * b[1] - a[1] * b[0])
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def dot(a, b):
|
| 17 |
+
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def mag(a):
|
| 21 |
+
return _sqrt(a[0]**2 + a[1]**2 + a[2]**2)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def norm(a):
|
| 25 |
+
m = mag(a)
|
| 26 |
+
return (a[0] / m, a[1] / m, a[2] / m)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def get_sphere_mapping(x, y, width, height):
|
| 30 |
+
x = min([max([x, 0]), width])
|
| 31 |
+
y = min([max([y, 0]), height])
|
| 32 |
+
|
| 33 |
+
sr = _sqrt((width/2)**2 + (height/2)**2)
|
| 34 |
+
sx = ((x - width / 2) / sr)
|
| 35 |
+
sy = ((y - height / 2) / sr)
|
| 36 |
+
|
| 37 |
+
sz = 1.0 - sx**2 - sy**2
|
| 38 |
+
|
| 39 |
+
if sz > 0.0:
|
| 40 |
+
sz = _sqrt(sz)
|
| 41 |
+
return (sx, sy, sz)
|
| 42 |
+
else:
|
| 43 |
+
sz = 0
|
| 44 |
+
return norm((sx, sy, sz))
|
| 45 |
+
|
| 46 |
+
rad2deg = 180.0 / 3.141592
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def get_spherical_rotatation(p1, p2, width, height, theta_multiplier):
|
| 50 |
+
v1 = get_sphere_mapping(p1[0], p1[1], width, height)
|
| 51 |
+
v2 = get_sphere_mapping(p2[0], p2[1], width, height)
|
| 52 |
+
|
| 53 |
+
d = min(max([dot(v1, v2), -1]), 1)
|
| 54 |
+
|
| 55 |
+
if abs(d - 1.0) < 0.000001:
|
| 56 |
+
return None
|
| 57 |
+
|
| 58 |
+
raxis = norm( cross(v1, v2) )
|
| 59 |
+
rtheta = theta_multiplier * rad2deg * _acos(d)
|
| 60 |
+
|
| 61 |
+
pgl.glPushMatrix()
|
| 62 |
+
pgl.glLoadIdentity()
|
| 63 |
+
pgl.glRotatef(rtheta, *raxis)
|
| 64 |
+
mat = (c_float*16)()
|
| 65 |
+
pgl.glGetFloatv(pgl.GL_MODELVIEW_MATRIX, mat)
|
| 66 |
+
pgl.glPopMatrix()
|
| 67 |
+
|
| 68 |
+
return mat
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_surface.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import pyglet.gl as pgl
|
| 2 |
+
|
| 3 |
+
from sympy.core import S
|
| 4 |
+
from sympy.plotting.pygletplot.plot_mode_base import PlotModeBase
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class PlotSurface(PlotModeBase):
|
| 8 |
+
|
| 9 |
+
default_rot_preset = 'perspective'
|
| 10 |
+
|
| 11 |
+
def _on_calculate_verts(self):
|
| 12 |
+
self.u_interval = self.intervals[0]
|
| 13 |
+
self.u_set = list(self.u_interval.frange())
|
| 14 |
+
self.v_interval = self.intervals[1]
|
| 15 |
+
self.v_set = list(self.v_interval.frange())
|
| 16 |
+
self.bounds = [[S.Infinity, S.NegativeInfinity, 0],
|
| 17 |
+
[S.Infinity, S.NegativeInfinity, 0],
|
| 18 |
+
[S.Infinity, S.NegativeInfinity, 0]]
|
| 19 |
+
evaluate = self._get_evaluator()
|
| 20 |
+
|
| 21 |
+
self._calculating_verts_pos = 0.0
|
| 22 |
+
self._calculating_verts_len = float(
|
| 23 |
+
self.u_interval.v_len*self.v_interval.v_len)
|
| 24 |
+
|
| 25 |
+
verts = []
|
| 26 |
+
b = self.bounds
|
| 27 |
+
for u in self.u_set:
|
| 28 |
+
column = []
|
| 29 |
+
for v in self.v_set:
|
| 30 |
+
try:
|
| 31 |
+
_e = evaluate(u, v) # calculate vertex
|
| 32 |
+
except ZeroDivisionError:
|
| 33 |
+
_e = None
|
| 34 |
+
if _e is not None: # update bounding box
|
| 35 |
+
for axis in range(3):
|
| 36 |
+
b[axis][0] = min([b[axis][0], _e[axis]])
|
| 37 |
+
b[axis][1] = max([b[axis][1], _e[axis]])
|
| 38 |
+
column.append(_e)
|
| 39 |
+
self._calculating_verts_pos += 1.0
|
| 40 |
+
|
| 41 |
+
verts.append(column)
|
| 42 |
+
for axis in range(3):
|
| 43 |
+
b[axis][2] = b[axis][1] - b[axis][0]
|
| 44 |
+
if b[axis][2] == 0.0:
|
| 45 |
+
b[axis][2] = 1.0
|
| 46 |
+
|
| 47 |
+
self.verts = verts
|
| 48 |
+
self.push_wireframe(self.draw_verts(False, False))
|
| 49 |
+
self.push_solid(self.draw_verts(False, True))
|
| 50 |
+
|
| 51 |
+
def _on_calculate_cverts(self):
|
| 52 |
+
if not self.verts or not self.color:
|
| 53 |
+
return
|
| 54 |
+
|
| 55 |
+
def set_work_len(n):
|
| 56 |
+
self._calculating_cverts_len = float(n)
|
| 57 |
+
|
| 58 |
+
def inc_work_pos():
|
| 59 |
+
self._calculating_cverts_pos += 1.0
|
| 60 |
+
set_work_len(1)
|
| 61 |
+
self._calculating_cverts_pos = 0
|
| 62 |
+
self.cverts = self.color.apply_to_surface(self.verts,
|
| 63 |
+
self.u_set,
|
| 64 |
+
self.v_set,
|
| 65 |
+
set_len=set_work_len,
|
| 66 |
+
inc_pos=inc_work_pos)
|
| 67 |
+
self.push_solid(self.draw_verts(True, True))
|
| 68 |
+
|
| 69 |
+
def calculate_one_cvert(self, u, v):
|
| 70 |
+
vert = self.verts[u][v]
|
| 71 |
+
return self.color(vert[0], vert[1], vert[2],
|
| 72 |
+
self.u_set[u], self.v_set[v])
|
| 73 |
+
|
| 74 |
+
def draw_verts(self, use_cverts, use_solid_color):
|
| 75 |
+
def f():
|
| 76 |
+
for u in range(1, len(self.u_set)):
|
| 77 |
+
pgl.glBegin(pgl.GL_QUAD_STRIP)
|
| 78 |
+
for v in range(len(self.v_set)):
|
| 79 |
+
pa = self.verts[u - 1][v]
|
| 80 |
+
pb = self.verts[u][v]
|
| 81 |
+
if pa is None or pb is None:
|
| 82 |
+
pgl.glEnd()
|
| 83 |
+
pgl.glBegin(pgl.GL_QUAD_STRIP)
|
| 84 |
+
continue
|
| 85 |
+
if use_cverts:
|
| 86 |
+
ca = self.cverts[u - 1][v]
|
| 87 |
+
cb = self.cverts[u][v]
|
| 88 |
+
if ca is None:
|
| 89 |
+
ca = (0, 0, 0)
|
| 90 |
+
if cb is None:
|
| 91 |
+
cb = (0, 0, 0)
|
| 92 |
+
else:
|
| 93 |
+
if use_solid_color:
|
| 94 |
+
ca = cb = self.default_solid_color
|
| 95 |
+
else:
|
| 96 |
+
ca = cb = self.default_wireframe_color
|
| 97 |
+
pgl.glColor3f(*ca)
|
| 98 |
+
pgl.glVertex3f(*pa)
|
| 99 |
+
pgl.glColor3f(*cb)
|
| 100 |
+
pgl.glVertex3f(*pb)
|
| 101 |
+
pgl.glEnd()
|
| 102 |
+
return f
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/plot_window.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from time import perf_counter
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
import pyglet.gl as pgl
|
| 5 |
+
|
| 6 |
+
from sympy.plotting.pygletplot.managed_window import ManagedWindow
|
| 7 |
+
from sympy.plotting.pygletplot.plot_camera import PlotCamera
|
| 8 |
+
from sympy.plotting.pygletplot.plot_controller import PlotController
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class PlotWindow(ManagedWindow):
|
| 12 |
+
|
| 13 |
+
def __init__(self, plot, antialiasing=True, ortho=False,
|
| 14 |
+
invert_mouse_zoom=False, linewidth=1.5, caption="SymPy Plot",
|
| 15 |
+
**kwargs):
|
| 16 |
+
"""
|
| 17 |
+
Named Arguments
|
| 18 |
+
===============
|
| 19 |
+
|
| 20 |
+
antialiasing = True
|
| 21 |
+
True OR False
|
| 22 |
+
ortho = False
|
| 23 |
+
True OR False
|
| 24 |
+
invert_mouse_zoom = False
|
| 25 |
+
True OR False
|
| 26 |
+
"""
|
| 27 |
+
self.plot = plot
|
| 28 |
+
|
| 29 |
+
self.camera = None
|
| 30 |
+
self._calculating = False
|
| 31 |
+
|
| 32 |
+
self.antialiasing = antialiasing
|
| 33 |
+
self.ortho = ortho
|
| 34 |
+
self.invert_mouse_zoom = invert_mouse_zoom
|
| 35 |
+
self.linewidth = linewidth
|
| 36 |
+
self.title = caption
|
| 37 |
+
self.last_caption_update = 0
|
| 38 |
+
self.caption_update_interval = 0.2
|
| 39 |
+
self.drawing_first_object = True
|
| 40 |
+
|
| 41 |
+
super().__init__(**kwargs)
|
| 42 |
+
|
| 43 |
+
def setup(self):
|
| 44 |
+
self.camera = PlotCamera(self, ortho=self.ortho)
|
| 45 |
+
self.controller = PlotController(self,
|
| 46 |
+
invert_mouse_zoom=self.invert_mouse_zoom)
|
| 47 |
+
self.push_handlers(self.controller)
|
| 48 |
+
|
| 49 |
+
pgl.glClearColor(1.0, 1.0, 1.0, 0.0)
|
| 50 |
+
pgl.glClearDepth(1.0)
|
| 51 |
+
|
| 52 |
+
pgl.glDepthFunc(pgl.GL_LESS)
|
| 53 |
+
pgl.glEnable(pgl.GL_DEPTH_TEST)
|
| 54 |
+
|
| 55 |
+
pgl.glEnable(pgl.GL_LINE_SMOOTH)
|
| 56 |
+
pgl.glShadeModel(pgl.GL_SMOOTH)
|
| 57 |
+
pgl.glLineWidth(self.linewidth)
|
| 58 |
+
|
| 59 |
+
pgl.glEnable(pgl.GL_BLEND)
|
| 60 |
+
pgl.glBlendFunc(pgl.GL_SRC_ALPHA, pgl.GL_ONE_MINUS_SRC_ALPHA)
|
| 61 |
+
|
| 62 |
+
if self.antialiasing:
|
| 63 |
+
pgl.glHint(pgl.GL_LINE_SMOOTH_HINT, pgl.GL_NICEST)
|
| 64 |
+
pgl.glHint(pgl.GL_POLYGON_SMOOTH_HINT, pgl.GL_NICEST)
|
| 65 |
+
|
| 66 |
+
self.camera.setup_projection()
|
| 67 |
+
|
| 68 |
+
def on_resize(self, w, h):
|
| 69 |
+
super().on_resize(w, h)
|
| 70 |
+
if self.camera is not None:
|
| 71 |
+
self.camera.setup_projection()
|
| 72 |
+
|
| 73 |
+
def update(self, dt):
|
| 74 |
+
self.controller.update(dt)
|
| 75 |
+
|
| 76 |
+
def draw(self):
|
| 77 |
+
self.plot._render_lock.acquire()
|
| 78 |
+
self.camera.apply_transformation()
|
| 79 |
+
|
| 80 |
+
calc_verts_pos, calc_verts_len = 0, 0
|
| 81 |
+
calc_cverts_pos, calc_cverts_len = 0, 0
|
| 82 |
+
|
| 83 |
+
should_update_caption = (perf_counter() - self.last_caption_update >
|
| 84 |
+
self.caption_update_interval)
|
| 85 |
+
|
| 86 |
+
if len(self.plot._functions.values()) == 0:
|
| 87 |
+
self.drawing_first_object = True
|
| 88 |
+
|
| 89 |
+
iterfunctions = iter(self.plot._functions.values())
|
| 90 |
+
|
| 91 |
+
for r in iterfunctions:
|
| 92 |
+
if self.drawing_first_object:
|
| 93 |
+
self.camera.set_rot_preset(r.default_rot_preset)
|
| 94 |
+
self.drawing_first_object = False
|
| 95 |
+
|
| 96 |
+
pgl.glPushMatrix()
|
| 97 |
+
r._draw()
|
| 98 |
+
pgl.glPopMatrix()
|
| 99 |
+
|
| 100 |
+
# might as well do this while we are
|
| 101 |
+
# iterating and have the lock rather
|
| 102 |
+
# than locking and iterating twice
|
| 103 |
+
# per frame:
|
| 104 |
+
|
| 105 |
+
if should_update_caption:
|
| 106 |
+
try:
|
| 107 |
+
if r.calculating_verts:
|
| 108 |
+
calc_verts_pos += r.calculating_verts_pos
|
| 109 |
+
calc_verts_len += r.calculating_verts_len
|
| 110 |
+
if r.calculating_cverts:
|
| 111 |
+
calc_cverts_pos += r.calculating_cverts_pos
|
| 112 |
+
calc_cverts_len += r.calculating_cverts_len
|
| 113 |
+
except ValueError:
|
| 114 |
+
pass
|
| 115 |
+
|
| 116 |
+
for r in self.plot._pobjects:
|
| 117 |
+
pgl.glPushMatrix()
|
| 118 |
+
r._draw()
|
| 119 |
+
pgl.glPopMatrix()
|
| 120 |
+
|
| 121 |
+
if should_update_caption:
|
| 122 |
+
self.update_caption(calc_verts_pos, calc_verts_len,
|
| 123 |
+
calc_cverts_pos, calc_cverts_len)
|
| 124 |
+
self.last_caption_update = perf_counter()
|
| 125 |
+
|
| 126 |
+
if self.plot._screenshot:
|
| 127 |
+
self.plot._screenshot._execute_saving()
|
| 128 |
+
|
| 129 |
+
self.plot._render_lock.release()
|
| 130 |
+
|
| 131 |
+
def update_caption(self, calc_verts_pos, calc_verts_len,
|
| 132 |
+
calc_cverts_pos, calc_cverts_len):
|
| 133 |
+
caption = self.title
|
| 134 |
+
if calc_verts_len or calc_cverts_len:
|
| 135 |
+
caption += " (calculating"
|
| 136 |
+
if calc_verts_len > 0:
|
| 137 |
+
p = (calc_verts_pos / calc_verts_len) * 100
|
| 138 |
+
caption += " vertices %i%%" % (p)
|
| 139 |
+
if calc_cverts_len > 0:
|
| 140 |
+
p = (calc_cverts_pos / calc_cverts_len) * 100
|
| 141 |
+
caption += " colors %i%%" % (p)
|
| 142 |
+
caption += ")"
|
| 143 |
+
if self.caption != caption:
|
| 144 |
+
self.set_caption(caption)
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/tests/__init__.py
ADDED
|
File without changes
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/tests/test_plotting.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.external.importtools import import_module
|
| 2 |
+
|
| 3 |
+
disabled = False
|
| 4 |
+
|
| 5 |
+
# if pyglet.gl fails to import, e.g. opengl is missing, we disable the tests
|
| 6 |
+
pyglet_gl = import_module("pyglet.gl", catch=(OSError,))
|
| 7 |
+
pyglet_window = import_module("pyglet.window", catch=(OSError,))
|
| 8 |
+
if not pyglet_gl or not pyglet_window:
|
| 9 |
+
disabled = True
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
from sympy.core.symbol import symbols
|
| 13 |
+
from sympy.functions.elementary.exponential import log
|
| 14 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 15 |
+
x, y, z = symbols('x, y, z')
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def test_plot_2d():
|
| 19 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 20 |
+
p = PygletPlot(x, [x, -5, 5, 4], visible=False)
|
| 21 |
+
p.wait_for_calculations()
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def test_plot_2d_discontinuous():
|
| 25 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 26 |
+
p = PygletPlot(1/x, [x, -1, 1, 2], visible=False)
|
| 27 |
+
p.wait_for_calculations()
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_plot_3d():
|
| 31 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 32 |
+
p = PygletPlot(x*y, [x, -5, 5, 5], [y, -5, 5, 5], visible=False)
|
| 33 |
+
p.wait_for_calculations()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def test_plot_3d_discontinuous():
|
| 37 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 38 |
+
p = PygletPlot(1/x, [x, -3, 3, 6], [y, -1, 1, 1], visible=False)
|
| 39 |
+
p.wait_for_calculations()
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_plot_2d_polar():
|
| 43 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 44 |
+
p = PygletPlot(1/x, [x, -1, 1, 4], 'mode=polar', visible=False)
|
| 45 |
+
p.wait_for_calculations()
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def test_plot_3d_cylinder():
|
| 49 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 50 |
+
p = PygletPlot(
|
| 51 |
+
1/y, [x, 0, 6.282, 4], [y, -1, 1, 4], 'mode=polar;style=solid',
|
| 52 |
+
visible=False)
|
| 53 |
+
p.wait_for_calculations()
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def test_plot_3d_spherical():
|
| 57 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 58 |
+
p = PygletPlot(
|
| 59 |
+
1, [x, 0, 6.282, 4], [y, 0, 3.141,
|
| 60 |
+
4], 'mode=spherical;style=wireframe',
|
| 61 |
+
visible=False)
|
| 62 |
+
p.wait_for_calculations()
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def test_plot_2d_parametric():
|
| 66 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 67 |
+
p = PygletPlot(sin(x), cos(x), [x, 0, 6.282, 4], visible=False)
|
| 68 |
+
p.wait_for_calculations()
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def test_plot_3d_parametric():
|
| 72 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 73 |
+
p = PygletPlot(sin(x), cos(x), x/5.0, [x, 0, 6.282, 4], visible=False)
|
| 74 |
+
p.wait_for_calculations()
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def _test_plot_log():
|
| 78 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 79 |
+
p = PygletPlot(log(x), [x, 0, 6.282, 4], 'mode=polar', visible=False)
|
| 80 |
+
p.wait_for_calculations()
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def test_plot_integral():
|
| 84 |
+
# Make sure it doesn't treat x as an independent variable
|
| 85 |
+
from sympy.plotting.pygletplot import PygletPlot
|
| 86 |
+
from sympy.integrals.integrals import Integral
|
| 87 |
+
p = PygletPlot(Integral(z*x, (x, 1, z), (z, 1, y)), visible=False)
|
| 88 |
+
p.wait_for_calculations()
|
.venv/Lib/site-packages/sympy/plotting/pygletplot/util.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
try:
|
| 2 |
+
from ctypes import c_float, c_int, c_double
|
| 3 |
+
except ImportError:
|
| 4 |
+
pass
|
| 5 |
+
|
| 6 |
+
import pyglet.gl as pgl
|
| 7 |
+
from sympy.core import S
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def get_model_matrix(array_type=c_float, glGetMethod=pgl.glGetFloatv):
|
| 11 |
+
"""
|
| 12 |
+
Returns the current modelview matrix.
|
| 13 |
+
"""
|
| 14 |
+
m = (array_type*16)()
|
| 15 |
+
glGetMethod(pgl.GL_MODELVIEW_MATRIX, m)
|
| 16 |
+
return m
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def get_projection_matrix(array_type=c_float, glGetMethod=pgl.glGetFloatv):
|
| 20 |
+
"""
|
| 21 |
+
Returns the current modelview matrix.
|
| 22 |
+
"""
|
| 23 |
+
m = (array_type*16)()
|
| 24 |
+
glGetMethod(pgl.GL_PROJECTION_MATRIX, m)
|
| 25 |
+
return m
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def get_viewport():
|
| 29 |
+
"""
|
| 30 |
+
Returns the current viewport.
|
| 31 |
+
"""
|
| 32 |
+
m = (c_int*4)()
|
| 33 |
+
pgl.glGetIntegerv(pgl.GL_VIEWPORT, m)
|
| 34 |
+
return m
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def get_direction_vectors():
|
| 38 |
+
m = get_model_matrix()
|
| 39 |
+
return ((m[0], m[4], m[8]),
|
| 40 |
+
(m[1], m[5], m[9]),
|
| 41 |
+
(m[2], m[6], m[10]))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def get_view_direction_vectors():
|
| 45 |
+
m = get_model_matrix()
|
| 46 |
+
return ((m[0], m[1], m[2]),
|
| 47 |
+
(m[4], m[5], m[6]),
|
| 48 |
+
(m[8], m[9], m[10]))
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def get_basis_vectors():
|
| 52 |
+
return ((1, 0, 0), (0, 1, 0), (0, 0, 1))
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def screen_to_model(x, y, z):
|
| 56 |
+
m = get_model_matrix(c_double, pgl.glGetDoublev)
|
| 57 |
+
p = get_projection_matrix(c_double, pgl.glGetDoublev)
|
| 58 |
+
w = get_viewport()
|
| 59 |
+
mx, my, mz = c_double(), c_double(), c_double()
|
| 60 |
+
pgl.gluUnProject(x, y, z, m, p, w, mx, my, mz)
|
| 61 |
+
return float(mx.value), float(my.value), float(mz.value)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def model_to_screen(x, y, z):
|
| 65 |
+
m = get_model_matrix(c_double, pgl.glGetDoublev)
|
| 66 |
+
p = get_projection_matrix(c_double, pgl.glGetDoublev)
|
| 67 |
+
w = get_viewport()
|
| 68 |
+
mx, my, mz = c_double(), c_double(), c_double()
|
| 69 |
+
pgl.gluProject(x, y, z, m, p, w, mx, my, mz)
|
| 70 |
+
return float(mx.value), float(my.value), float(mz.value)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def vec_subs(a, b):
|
| 74 |
+
return tuple(a[i] - b[i] for i in range(len(a)))
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def billboard_matrix():
|
| 78 |
+
"""
|
| 79 |
+
Removes rotational components of
|
| 80 |
+
current matrix so that primitives
|
| 81 |
+
are always drawn facing the viewer.
|
| 82 |
+
|
| 83 |
+
|1|0|0|x|
|
| 84 |
+
|0|1|0|x|
|
| 85 |
+
|0|0|1|x| (x means left unchanged)
|
| 86 |
+
|x|x|x|x|
|
| 87 |
+
"""
|
| 88 |
+
m = get_model_matrix()
|
| 89 |
+
# XXX: for i in range(11): m[i] = i ?
|
| 90 |
+
m[0] = 1
|
| 91 |
+
m[1] = 0
|
| 92 |
+
m[2] = 0
|
| 93 |
+
m[4] = 0
|
| 94 |
+
m[5] = 1
|
| 95 |
+
m[6] = 0
|
| 96 |
+
m[8] = 0
|
| 97 |
+
m[9] = 0
|
| 98 |
+
m[10] = 1
|
| 99 |
+
pgl.glLoadMatrixf(m)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def create_bounds():
|
| 103 |
+
return [[S.Infinity, S.NegativeInfinity, 0],
|
| 104 |
+
[S.Infinity, S.NegativeInfinity, 0],
|
| 105 |
+
[S.Infinity, S.NegativeInfinity, 0]]
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def update_bounds(b, v):
|
| 109 |
+
if v is None:
|
| 110 |
+
return
|
| 111 |
+
for axis in range(3):
|
| 112 |
+
b[axis][0] = min([b[axis][0], v[axis]])
|
| 113 |
+
b[axis][1] = max([b[axis][1], v[axis]])
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def interpolate(a_min, a_max, a_ratio):
|
| 117 |
+
return a_min + a_ratio * (a_max - a_min)
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def rinterpolate(a_min, a_max, a_value):
|
| 121 |
+
a_range = a_max - a_min
|
| 122 |
+
if a_max == a_min:
|
| 123 |
+
a_range = 1.0
|
| 124 |
+
return (a_value - a_min) / float(a_range)
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def interpolate_color(color1, color2, ratio):
|
| 128 |
+
return tuple(interpolate(color1[i], color2[i], ratio) for i in range(3))
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def scale_value(v, v_min, v_len):
|
| 132 |
+
return (v - v_min) / v_len
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def scale_value_list(flist):
|
| 136 |
+
v_min, v_max = min(flist), max(flist)
|
| 137 |
+
v_len = v_max - v_min
|
| 138 |
+
return [scale_value(f, v_min, v_len) for f in flist]
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def strided_range(r_min, r_max, stride, max_steps=50):
|
| 142 |
+
o_min, o_max = r_min, r_max
|
| 143 |
+
if abs(r_min - r_max) < 0.001:
|
| 144 |
+
return []
|
| 145 |
+
try:
|
| 146 |
+
range(int(r_min - r_max))
|
| 147 |
+
except (TypeError, OverflowError):
|
| 148 |
+
return []
|
| 149 |
+
if r_min > r_max:
|
| 150 |
+
raise ValueError("r_min cannot be greater than r_max")
|
| 151 |
+
r_min_s = (r_min % stride)
|
| 152 |
+
r_max_s = stride - (r_max % stride)
|
| 153 |
+
if abs(r_max_s - stride) < 0.001:
|
| 154 |
+
r_max_s = 0.0
|
| 155 |
+
r_min -= r_min_s
|
| 156 |
+
r_max += r_max_s
|
| 157 |
+
r_steps = int((r_max - r_min)/stride)
|
| 158 |
+
if max_steps and r_steps > max_steps:
|
| 159 |
+
return strided_range(o_min, o_max, stride*2)
|
| 160 |
+
return [r_min] + [r_min + e*stride for e in range(1, r_steps + 1)] + [r_max]
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def parse_option_string(s):
|
| 164 |
+
if not isinstance(s, str):
|
| 165 |
+
return None
|
| 166 |
+
options = {}
|
| 167 |
+
for token in s.split(';'):
|
| 168 |
+
pieces = token.split('=')
|
| 169 |
+
if len(pieces) == 1:
|
| 170 |
+
option, value = pieces[0], ""
|
| 171 |
+
elif len(pieces) == 2:
|
| 172 |
+
option, value = pieces
|
| 173 |
+
else:
|
| 174 |
+
raise ValueError("Plot option string '%s' is malformed." % (s))
|
| 175 |
+
options[option.strip()] = value.strip()
|
| 176 |
+
return options
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def dot_product(v1, v2):
|
| 180 |
+
return sum(v1[i]*v2[i] for i in range(3))
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def vec_sub(v1, v2):
|
| 184 |
+
return tuple(v1[i] - v2[i] for i in range(3))
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def vec_mag(v):
|
| 188 |
+
return sum(v[i]**2 for i in range(3))**(0.5)
|