PyVRML97 2.3.4b3__tar.gz → 2.4.0a1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/MANIFEST.in +1 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PKG-INFO +16 -3
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PyVRML97.egg-info/PKG-INFO +16 -3
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PyVRML97.egg-info/SOURCES.txt +26 -3
- pyvrml97-2.4.0a1/PyVRML97.egg-info/requires.txt +3 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/README.rst +7 -0
- pyvrml97-2.4.0a1/RELEASE-NOTES-2.3.5.md +104 -0
- pyvrml97-2.4.0a1/pyproject.toml +112 -0
- pyvrml97-2.4.0a1/requirements.txt +8 -0
- pyvrml97-2.4.0a1/tests/test_arrays.py +171 -0
- pyvrml97-2.4.0a1/tests/test_basefield.py +335 -0
- pyvrml97-2.4.0a1/tests/test_cache.py +237 -0
- pyvrml97-2.4.0a1/tests/test_copying.py +166 -0
- pyvrml97-2.4.0a1/tests/test_csscolors.py +71 -0
- pyvrml97-2.4.0a1/tests/test_field_assignment.py +186 -0
- pyvrml97-2.4.0a1/tests/test_field_coercion.py +181 -0
- pyvrml97-2.4.0a1/tests/test_field_declaration.py +467 -0
- pyvrml97-2.4.0a1/tests/test_fieldtype_edges.py +341 -0
- pyvrml97-2.4.0a1/tests/test_fieldtypes.py +144 -0
- pyvrml97-2.4.0a1/tests/test_linearise.py +596 -0
- pyvrml97-2.4.0a1/tests/test_matrix_primitives.py +235 -0
- pyvrml97-2.4.0a1/tests/test_node_behaviour.py +268 -0
- pyvrml97-2.4.0a1/tests/test_node_definitions.py +173 -0
- pyvrml97-2.4.0a1/tests/test_nodepath.py +201 -0
- pyvrml97-2.4.0a1/tests/test_olist.py +156 -0
- pyvrml97-2.4.0a1/tests/test_parseprocessor.py +276 -0
- pyvrml97-2.4.0a1/tests/test_parsing_values.py +365 -0
- pyvrml97-2.4.0a1/tests/test_routes_and_protofunctions.py +274 -0
- pyvrml97-2.4.0a1/tests/test_scenegraph.py +249 -0
- pyvrml97-2.4.0a1/tests/test_smallmodules.py +156 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/tests/test_tostring.py +2 -10
- pyvrml97-2.4.0a1/tests/test_transform_composition.py +186 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/tests/test_transformmatrix.py +12 -9
- pyvrml97-2.4.0a1/tests/test_vrml200x_parser.py +119 -0
- pyvrml97-2.4.0a1/tests/test_vrmlstr.py +187 -0
- pyvrml97-2.4.0a1/tests/test_weakcollections.py +280 -0
- pyvrml97-2.4.0a1/tox.ini +44 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/__init__.py +1 -1
- pyvrml97-2.4.0a1/vrml/arrays.py +133 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/cache.py +47 -42
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/copier.py +14 -6
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/csscolors.py +9 -22
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/event.py +8 -4
- pyvrml97-2.4.0a1/vrml/field.py +569 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/fieldtypes.py +216 -226
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/node.py +196 -91
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/nodepath.py +20 -14
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/olist.py +52 -41
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/protofunctions.py +24 -23
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/protonamespace.py +13 -5
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/route.py +53 -22
- pyvrml97-2.4.0a1/vrml/vrml200x/__init__.py +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml200x/parser.py +20 -8
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/_transformmatrix.py +26 -23
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/_transformmatrix_accel.py +5 -4
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/basenamespaces.py +2 -3
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/basenodes.py +35 -35
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/linearise.py +144 -101
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/nodepath.py +55 -42
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/nodetypes.py +9 -8
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/nurbs.py +3 -3
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/parseprocessor.py +87 -52
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/parser.py +14 -6
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/scenegraph.py +50 -41
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/script.py +3 -2
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/shaders.py +14 -14
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/transformmatrix.py +60 -51
- pyvrml97-2.4.0a1/vrml/vrml97/tree.py +117 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/weaklist.py +73 -56
- pyvrml97-2.4.0a1/vrml/weaktuple.py +167 -0
- pyvrml97-2.3.4b3/PyVRML97.egg-info/requires.txt +0 -3
- pyvrml97-2.3.4b3/pyproject.toml +0 -48
- pyvrml97-2.3.4b3/requirements.txt +0 -5
- pyvrml97-2.3.4b3/tests/test_fieldtypes.py +0 -20
- pyvrml97-2.3.4b3/tests/test_nodepath.py +0 -80
- pyvrml97-2.3.4b3/tests/test_olist.py +0 -43
- pyvrml97-2.3.4b3/tox.ini +0 -13
- pyvrml97-2.3.4b3/vrml/_bytes.py +0 -85
- pyvrml97-2.3.4b3/vrml/arrays.py +0 -86
- pyvrml97-2.3.4b3/vrml/field.py +0 -476
- pyvrml97-2.3.4b3/vrml/weakkeydictfix.py +0 -36
- pyvrml97-2.3.4b3/vrml/weaktuple.py +0 -138
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PyVRML97.egg-info/dependency_links.txt +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PyVRML97.egg-info/not-zip-safe +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/PyVRML97.egg-info/top_level.txt +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/license.txt +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/setup.cfg +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/test-requirements.txt +0 -0
- /pyvrml97-2.3.4b3/vrml/vrml200x/__init__.py → /pyvrml97-2.4.0a1/vrml/py.typed +0 -0
- {pyvrml97-2.3.4b3 → pyvrml97-2.4.0a1}/vrml/vrml97/__init__.py +0 -0
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyVRML97
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0a1
|
|
4
4
|
Summary: VRML97 Scenegraph model for Python
|
|
5
5
|
Author-email: "Mike C. Fletcher" <mcfletch@vrplumber.com>
|
|
6
6
|
License: BSD-style, see license.txt for details
|
|
7
7
|
Project-URL: Homepage, http://pyopengl.sourceforge.net/context/
|
|
8
8
|
Keywords: VRML,VRML97,scenegraph
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
-
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
17
|
Classifier: Programming Language :: C
|
|
12
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
19
|
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
@@ -16,7 +22,7 @@ Requires-Python: >=3.9
|
|
|
16
22
|
Description-Content-Type: text/x-rst
|
|
17
23
|
Requires-Dist: numpy
|
|
18
24
|
Requires-Dist: PyDispatcher
|
|
19
|
-
Requires-Dist: simpleparse
|
|
25
|
+
Requires-Dist: simpleparse>=3.0.0a3
|
|
20
26
|
|
|
21
27
|
PyVRML97 Scenegraph Model
|
|
22
28
|
==========================
|
|
@@ -35,6 +41,13 @@ optimize the rendering process.
|
|
|
35
41
|
.. _`PyDispatcher`: https://pypi.python.org/pypi/PyDispatcher
|
|
36
42
|
.. _`Numpy`: https://pypi.python.org/pypi/numpy
|
|
37
43
|
|
|
44
|
+
`RELEASE-NOTES-2.3.5.md`_ says what changed for a program using this release,
|
|
45
|
+
including the two behaviour changes a caller may need to act on:
|
|
46
|
+
``transformmatrix.orthoMatrix`` is transposed, and ``Node.copy()`` copies the
|
|
47
|
+
whole subtree.
|
|
48
|
+
|
|
49
|
+
.. _`RELEASE-NOTES-2.3.5.md`: RELEASE-NOTES-2.3.5.md
|
|
50
|
+
|
|
38
51
|
Installation
|
|
39
52
|
-------------
|
|
40
53
|
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PyVRML97
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0a1
|
|
4
4
|
Summary: VRML97 Scenegraph model for Python
|
|
5
5
|
Author-email: "Mike C. Fletcher" <mcfletch@vrplumber.com>
|
|
6
6
|
License: BSD-style, see license.txt for details
|
|
7
7
|
Project-URL: Homepage, http://pyopengl.sourceforge.net/context/
|
|
8
8
|
Keywords: VRML,VRML97,scenegraph
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
-
Classifier: Programming Language :: Python
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
11
17
|
Classifier: Programming Language :: C
|
|
12
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
13
19
|
Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering
|
|
@@ -16,7 +22,7 @@ Requires-Python: >=3.9
|
|
|
16
22
|
Description-Content-Type: text/x-rst
|
|
17
23
|
Requires-Dist: numpy
|
|
18
24
|
Requires-Dist: PyDispatcher
|
|
19
|
-
Requires-Dist: simpleparse
|
|
25
|
+
Requires-Dist: simpleparse>=3.0.0a3
|
|
20
26
|
|
|
21
27
|
PyVRML97 Scenegraph Model
|
|
22
28
|
==========================
|
|
@@ -35,6 +41,13 @@ optimize the rendering process.
|
|
|
35
41
|
.. _`PyDispatcher`: https://pypi.python.org/pypi/PyDispatcher
|
|
36
42
|
.. _`Numpy`: https://pypi.python.org/pypi/numpy
|
|
37
43
|
|
|
44
|
+
`RELEASE-NOTES-2.3.5.md`_ says what changed for a program using this release,
|
|
45
|
+
including the two behaviour changes a caller may need to act on:
|
|
46
|
+
``transformmatrix.orthoMatrix`` is transposed, and ``Node.copy()`` copies the
|
|
47
|
+
whole subtree.
|
|
48
|
+
|
|
49
|
+
.. _`RELEASE-NOTES-2.3.5.md`: RELEASE-NOTES-2.3.5.md
|
|
50
|
+
|
|
38
51
|
Installation
|
|
39
52
|
-------------
|
|
40
53
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
MANIFEST.in
|
|
2
2
|
README.rst
|
|
3
|
+
RELEASE-NOTES-2.3.5.md
|
|
3
4
|
license.txt
|
|
4
5
|
pyproject.toml
|
|
5
6
|
requirements.txt
|
|
@@ -11,13 +12,34 @@ PyVRML97.egg-info/dependency_links.txt
|
|
|
11
12
|
PyVRML97.egg-info/not-zip-safe
|
|
12
13
|
PyVRML97.egg-info/requires.txt
|
|
13
14
|
PyVRML97.egg-info/top_level.txt
|
|
15
|
+
tests/test_arrays.py
|
|
16
|
+
tests/test_basefield.py
|
|
17
|
+
tests/test_cache.py
|
|
18
|
+
tests/test_copying.py
|
|
19
|
+
tests/test_csscolors.py
|
|
20
|
+
tests/test_field_assignment.py
|
|
21
|
+
tests/test_field_coercion.py
|
|
22
|
+
tests/test_field_declaration.py
|
|
23
|
+
tests/test_fieldtype_edges.py
|
|
14
24
|
tests/test_fieldtypes.py
|
|
25
|
+
tests/test_linearise.py
|
|
26
|
+
tests/test_matrix_primitives.py
|
|
27
|
+
tests/test_node_behaviour.py
|
|
28
|
+
tests/test_node_definitions.py
|
|
15
29
|
tests/test_nodepath.py
|
|
16
30
|
tests/test_olist.py
|
|
31
|
+
tests/test_parseprocessor.py
|
|
32
|
+
tests/test_parsing_values.py
|
|
33
|
+
tests/test_routes_and_protofunctions.py
|
|
34
|
+
tests/test_scenegraph.py
|
|
35
|
+
tests/test_smallmodules.py
|
|
17
36
|
tests/test_tostring.py
|
|
37
|
+
tests/test_transform_composition.py
|
|
18
38
|
tests/test_transformmatrix.py
|
|
39
|
+
tests/test_vrml200x_parser.py
|
|
40
|
+
tests/test_vrmlstr.py
|
|
41
|
+
tests/test_weakcollections.py
|
|
19
42
|
vrml/__init__.py
|
|
20
|
-
vrml/_bytes.py
|
|
21
43
|
vrml/arrays.py
|
|
22
44
|
vrml/cache.py
|
|
23
45
|
vrml/copier.py
|
|
@@ -30,8 +52,8 @@ vrml/nodepath.py
|
|
|
30
52
|
vrml/olist.py
|
|
31
53
|
vrml/protofunctions.py
|
|
32
54
|
vrml/protonamespace.py
|
|
55
|
+
vrml/py.typed
|
|
33
56
|
vrml/route.py
|
|
34
|
-
vrml/weakkeydictfix.py
|
|
35
57
|
vrml/weaklist.py
|
|
36
58
|
vrml/weaktuple.py
|
|
37
59
|
vrml/vrml200x/__init__.py
|
|
@@ -50,4 +72,5 @@ vrml/vrml97/parser.py
|
|
|
50
72
|
vrml/vrml97/scenegraph.py
|
|
51
73
|
vrml/vrml97/script.py
|
|
52
74
|
vrml/vrml97/shaders.py
|
|
53
|
-
vrml/vrml97/transformmatrix.py
|
|
75
|
+
vrml/vrml97/transformmatrix.py
|
|
76
|
+
vrml/vrml97/tree.py
|
|
@@ -15,6 +15,13 @@ optimize the rendering process.
|
|
|
15
15
|
.. _`PyDispatcher`: https://pypi.python.org/pypi/PyDispatcher
|
|
16
16
|
.. _`Numpy`: https://pypi.python.org/pypi/numpy
|
|
17
17
|
|
|
18
|
+
`RELEASE-NOTES-2.3.5.md`_ says what changed for a program using this release,
|
|
19
|
+
including the two behaviour changes a caller may need to act on:
|
|
20
|
+
``transformmatrix.orthoMatrix`` is transposed, and ``Node.copy()`` copies the
|
|
21
|
+
whole subtree.
|
|
22
|
+
|
|
23
|
+
.. _`RELEASE-NOTES-2.3.5.md`: RELEASE-NOTES-2.3.5.md
|
|
24
|
+
|
|
18
25
|
Installation
|
|
19
26
|
-------------
|
|
20
27
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# PyVRML97 2.3.5
|
|
2
|
+
|
|
3
|
+
## What changed for a program using this
|
|
4
|
+
|
|
5
|
+
### `transformmatrix.orthoMatrix` is transposed
|
|
6
|
+
|
|
7
|
+
It answers the row-vector form now, matching `perspectiveMatrix`, the three
|
|
8
|
+
primitives beside it, and the module's own instructions: a point goes through
|
|
9
|
+
a matrix here as `dot( point, matrix )`. The previous form put the offset in
|
|
10
|
+
the last column, so a point through it landed the translation in `w`; a
|
|
11
|
+
centred box came out right and an off-centre one did not.
|
|
12
|
+
|
|
13
|
+
A caller that fed the result straight to `glLoadMatrixf` was relying on GL's
|
|
14
|
+
column-major upload to transpose it back. That caller now wants
|
|
15
|
+
`matrix.T` — or, better, one of the core-profile paths that take a row-vector
|
|
16
|
+
matrix.
|
|
17
|
+
|
|
18
|
+
### `Node.copy()` copies the whole subtree
|
|
19
|
+
|
|
20
|
+
A node's copy shared the nodes its fields pointed at, so writing to the copy's
|
|
21
|
+
child was writing to the original's. Each instance of a PROTO is a copy of the
|
|
22
|
+
prototype's body, so this was also why two instances of one prototype drew the
|
|
23
|
+
same thing: `Wheel { radius 2 }` beside `Wheel { radius 5 }` gave two wheels of
|
|
24
|
+
radius 5. A node reached twice in the original is still one node in the copy,
|
|
25
|
+
which is what DEF/USE and a prototype's IS wiring need.
|
|
26
|
+
|
|
27
|
+
### `NULL` compares and hashes as a value
|
|
28
|
+
|
|
29
|
+
`NullNode.__eq__` answered a three-way comparison — 0 for equal, which reads as
|
|
30
|
+
false, and -1 for anything else, which reads as true. `NULL == 42` was true and
|
|
31
|
+
`NULL == NullNode()` was false, and the class was unhashable. It now equals
|
|
32
|
+
another NULL, differs from anything that is not a node, and can be a dictionary
|
|
33
|
+
key.
|
|
34
|
+
|
|
35
|
+
### A field left at its default is no longer written
|
|
36
|
+
|
|
37
|
+
`arrays.safeCompare` is what the lineariser asks about every field it is about
|
|
38
|
+
to write, and it answered two questions wrongly. Two arrays were "the same"
|
|
39
|
+
when they shared a single element, and a list compared as a different type from
|
|
40
|
+
an array — which is the pair the question is usually about, since a node
|
|
41
|
+
declares its default as a list of numbers and stores its value as an array. So
|
|
42
|
+
no vector field ever looked like its default, and every one was written out.
|
|
43
|
+
|
|
44
|
+
A `Transform` with a translation set was written with all eight of its fields;
|
|
45
|
+
it is written with the one now. Files are shorter and read back the same. NULL
|
|
46
|
+
node fields go the same way, now that `NULL` compares as a value.
|
|
47
|
+
|
|
48
|
+
A program that read the output looking for a field it had not set will not find
|
|
49
|
+
it. That is what the format says, and what a file written by anything else
|
|
50
|
+
looks like.
|
|
51
|
+
|
|
52
|
+
### `del someNode.children[0]` removes the child
|
|
53
|
+
|
|
54
|
+
It announced the removal and left the child in the list. Only the slice form
|
|
55
|
+
deleted anything.
|
|
56
|
+
|
|
57
|
+
### A node appended to a scene learns which scene it is in
|
|
58
|
+
|
|
59
|
+
`scene.children.append(node)` left the node without a scene root, so `getProto`
|
|
60
|
+
and the DEF names did not reach it; only assignment to the whole field passed
|
|
61
|
+
the root down. Both do now.
|
|
62
|
+
|
|
63
|
+
## What a file can say that it could not
|
|
64
|
+
|
|
65
|
+
- **Every registered field type can be read.** `SFVec4f`, `SFVec2d`, `SFVec3d`,
|
|
66
|
+
`SFVec4d`, `SFUInt32`, `SFArray32`, `MFVec4f`, `MFVec2d`, `MFVec3d`,
|
|
67
|
+
`MFVec4d` and the four `MFMatrix` types had no reader; a file naming one
|
|
68
|
+
raised. The four `SFMatrix` types were not registered at all, so a PROTO
|
|
69
|
+
could not declare one.
|
|
70
|
+
- **A file that is a header and nothing else parses**, as does one whose last
|
|
71
|
+
line is a comment.
|
|
72
|
+
- **A PROTO declared without a body can be written out.**
|
|
73
|
+
|
|
74
|
+
## What the writer gets right
|
|
75
|
+
|
|
76
|
+
- An **MFString** field is written as a list of strings rather than as a list of
|
|
77
|
+
nodes. A node with a non-empty MFString — `Text`, `WorldInfo`, `Anchor`,
|
|
78
|
+
`ImageTexture`, `Script`, `Inline` — was written with an opening bracket that
|
|
79
|
+
was never closed and its next field swallowed inside it.
|
|
80
|
+
- An **EXTERNPROTO** reads back: its interface is written without values, which
|
|
81
|
+
is what VRML97 has room for, and `externalURL` is no longer written as one of
|
|
82
|
+
the fields the prototype declares.
|
|
83
|
+
- A **Script** is written as `DEF x Script` rather than `DEF x Script`, and
|
|
84
|
+
closed with a comment naming `x`.
|
|
85
|
+
- `Lineariser.linear( scene, skipProtos=... )` leaves the named prototypes out.
|
|
86
|
+
The argument was accepted and dropped. Name them by name, by the prototype
|
|
87
|
+
itself, or as the keys of a mapping such as a scene graph's `protoTypes`;
|
|
88
|
+
their instances are still written, which is how a caller writes a fragment
|
|
89
|
+
for a file that already declares them.
|
|
90
|
+
|
|
91
|
+
## Smaller things
|
|
92
|
+
|
|
93
|
+
- `protofunctions.getField` answers the field for a name that is not an
|
|
94
|
+
attribute — `' DEF'`, or an event stored under a name a program cannot write.
|
|
95
|
+
It answered the name itself.
|
|
96
|
+
- `str(node)` says the node's DEF name.
|
|
97
|
+
- Setting a vector field from one number works: `transform.scale = 2` raised.
|
|
98
|
+
- `MFString` refuses a value it cannot read with a ValueError naming the field,
|
|
99
|
+
as its sibling types do.
|
|
100
|
+
- `field.PyBaseField` names the Python implementation of the field primitives
|
|
101
|
+
whichever build is installed, so the two can be compared. Where the
|
|
102
|
+
accelerator is installed, `field.BaseField` is the compiled class as before.
|
|
103
|
+
- `vrml.arrays` drops what it carried for Numeric. `typeCode` reads
|
|
104
|
+
`dtype.char`; the `typecode()` fallbacks are gone.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
# PyVRML97 (the root package) is pure Python - no C extensions - so it needs
|
|
3
|
+
# nothing but setuptools to build. Cython/numpy build-deps live only in
|
|
4
|
+
# accelerate/pyproject.toml, where the extensions actually are. setuptools>=61
|
|
5
|
+
# is required to read the PEP 621 [project] table below.
|
|
6
|
+
requires = ["setuptools>=61.0"]
|
|
7
|
+
build-backend = "setuptools.build_meta"
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "PyVRML97"
|
|
11
|
+
dynamic = ["version"]
|
|
12
|
+
description = "VRML97 Scenegraph model for Python"
|
|
13
|
+
requires-python = ">=3.9"
|
|
14
|
+
readme = "README.rst"
|
|
15
|
+
license = { text = "BSD-style, see license.txt for details" }
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "Mike C. Fletcher", email = "mcfletch@vrplumber.com" },
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"numpy",
|
|
21
|
+
"PyDispatcher",
|
|
22
|
+
# 3.0.0a3 or newer: the first release with wheels for a current Python.
|
|
23
|
+
# 2.2.4 has none past 3.11 and its mxTextTools C extension does not compile
|
|
24
|
+
# against a current GCC, so an install that resolves to it fails to build.
|
|
25
|
+
# Named as a pre-release so an installer does not skip it.
|
|
26
|
+
"simpleparse >= 3.0.0a3",
|
|
27
|
+
]
|
|
28
|
+
keywords = [
|
|
29
|
+
"VRML",
|
|
30
|
+
"VRML97",
|
|
31
|
+
"scenegraph",
|
|
32
|
+
]
|
|
33
|
+
classifiers = [
|
|
34
|
+
"License :: OSI Approved :: BSD License",
|
|
35
|
+
"Programming Language :: Python :: 3",
|
|
36
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
37
|
+
"Programming Language :: Python :: 3.9",
|
|
38
|
+
"Programming Language :: Python :: 3.10",
|
|
39
|
+
"Programming Language :: Python :: 3.11",
|
|
40
|
+
"Programming Language :: Python :: 3.12",
|
|
41
|
+
"Programming Language :: Python :: 3.13",
|
|
42
|
+
"Programming Language :: C",
|
|
43
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
44
|
+
"Topic :: Multimedia :: Graphics :: 3D Rendering",
|
|
45
|
+
"Intended Audience :: Developers",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[project.urls]
|
|
49
|
+
Homepage = "http://pyopengl.sourceforge.net/context/"
|
|
50
|
+
|
|
51
|
+
[tool.setuptools]
|
|
52
|
+
zip-safe = false
|
|
53
|
+
|
|
54
|
+
[tool.setuptools.packages.find]
|
|
55
|
+
include = ["vrml*"]
|
|
56
|
+
|
|
57
|
+
[tool.setuptools.package-data]
|
|
58
|
+
# `py.typed` is what tells a checker this package's exports are real. Without
|
|
59
|
+
# it `vrml.arrays` -- the abstraction OpenGLContext and its kin reach numpy
|
|
60
|
+
# through -- resolves to nothing, and every `arrays.dot`, `arrays.zeros` and
|
|
61
|
+
# their fellows becomes an error in code that is correct.
|
|
62
|
+
vrml = ["py.typed"]
|
|
63
|
+
|
|
64
|
+
[tool.setuptools.dynamic]
|
|
65
|
+
version = { attr = "vrml.__version__" }
|
|
66
|
+
|
|
67
|
+
# uv decides whether its cached metadata for a source tree is still current
|
|
68
|
+
# from pyproject.toml, setup.py and setup.cfg alone. This project's version
|
|
69
|
+
# lives in vrml/__init__.py, so name that file too: without it a version bump
|
|
70
|
+
# leaves uv answering with the previous number, and a dependent requiring the
|
|
71
|
+
# new one resolves against PyPI rather than this checkout.
|
|
72
|
+
[tool.uv]
|
|
73
|
+
cache-keys = [{ file = "pyproject.toml" }, { file = "vrml/__init__.py" }]
|
|
74
|
+
|
|
75
|
+
# Named explicitly rather than left to ruff's default, which has widened
|
|
76
|
+
# between releases: a gate whose meaning moves when a tool is upgraded is not a
|
|
77
|
+
# gate. `E,W,F,B` is what the other pre-2010 projects here hold to.
|
|
78
|
+
[tool.ruff.lint]
|
|
79
|
+
select = ["E", "W", "F", "B"]
|
|
80
|
+
# `_name` as well as `_`: an underscore prefix is how Python says a name is
|
|
81
|
+
# bound but deliberately unused.
|
|
82
|
+
dummy-variable-rgx = "^(_[a-zA-Z0-9_]*|err)$"
|
|
83
|
+
ignore = [
|
|
84
|
+
'E501', # long lines, kept readable by hand
|
|
85
|
+
# The scenegraph's namespaces are assembled with `from x import *`, which
|
|
86
|
+
# is how the VRML97 node types are gathered; the names are there at run
|
|
87
|
+
# time. What holds the modules to importing is the suite, which imports
|
|
88
|
+
# them.
|
|
89
|
+
'F403',
|
|
90
|
+
'F405',
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
# This package ships `py.typed`, so what it declares is what every consumer's
|
|
95
|
+
# checker believes -- `vrml.arrays` is the abstraction OpenGLContext and its
|
|
96
|
+
# kin reach numpy through. Publishing types that nothing holds the package to
|
|
97
|
+
# is worse than publishing none.
|
|
98
|
+
python_version = "3.12"
|
|
99
|
+
files = ["vrml"]
|
|
100
|
+
ignore_missing_imports = true
|
|
101
|
+
warn_unused_ignores = true
|
|
102
|
+
# Every function body, not only the annotated ones. Nothing here was annotated,
|
|
103
|
+
# so without this the gate read the module-level declarations and skipped all
|
|
104
|
+
# 326 bodies -- which is where `str.replace` was called with one argument, where
|
|
105
|
+
# `WeakList.get` called a method Python 2 had, and where a property assigned to
|
|
106
|
+
# itself. A gate that cannot see inside a function is not checking the package.
|
|
107
|
+
check_untyped_defs = true
|
|
108
|
+
# Every one of the 331 definitions carries its types now, so a new one that does
|
|
109
|
+
# not is a gap the gate reports rather than a body it quietly stops checking.
|
|
110
|
+
# This is what keeps the setting above a floor rather than the whole of it.
|
|
111
|
+
disallow_untyped_defs = true
|
|
112
|
+
disallow_incomplete_defs = true
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
--prefer-binary
|
|
2
|
+
# 3.0.0a3 or newer: it is the first release with wheels for a current
|
|
3
|
+
# Python, and 2.2.4's mxTextTools C extension does not compile against a
|
|
4
|
+
# current GCC. Named as a pre-release so an installer does not skip it.
|
|
5
|
+
simpleparse >= 3.0.0a3
|
|
6
|
+
numpy >= 2.0.1; python_version >= '3.10'
|
|
7
|
+
numpy; python_version < '3.10'
|
|
8
|
+
PyDispatcher
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""The array abstraction the scenegraph reaches numpy through.
|
|
2
|
+
|
|
3
|
+
`vrml.arrays` is what let the package move from Numeric to numpy without every
|
|
4
|
+
node changing, and OpenGLContext and the projects built on it reach numpy
|
|
5
|
+
through it in turn. So what it exports is what a great deal of code sees under
|
|
6
|
+
those names, and a name that shadows a builtin with something that is not a
|
|
7
|
+
drop-in for it is a trap laid across all of that.
|
|
8
|
+
|
|
9
|
+
The reductions -- `sum`, `min`, `max`, `any`, `abs` -- shadow builtins
|
|
10
|
+
deliberately: taking the numpy one is the point, and each answers what the
|
|
11
|
+
builtin does for a scalar. `bool` is the exception, and the reason this file
|
|
12
|
+
names them one at a time rather than asserting a rule about the whole set.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import builtins
|
|
16
|
+
import math
|
|
17
|
+
|
|
18
|
+
import unittest
|
|
19
|
+
|
|
20
|
+
from vrml import arrays
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestWhatItShadows(unittest.TestCase):
|
|
24
|
+
def test_bool_is_the_builtin(self):
|
|
25
|
+
"""`numpy.bool` is a scalar type, not a drop-in for the builtin.
|
|
26
|
+
|
|
27
|
+
`isinstance(True, numpy.bool)` is False and `numpy.bool(1) is not
|
|
28
|
+
True`, so a module reading `bool` out of here got a name that fails
|
|
29
|
+
both of the things `bool` is used for.
|
|
30
|
+
"""
|
|
31
|
+
self.assertIs(getattr(arrays, 'bool', builtins.bool), builtins.bool)
|
|
32
|
+
self.assertNotIn('bool', arrays.__all__)
|
|
33
|
+
|
|
34
|
+
def test_it_exports_no_other_type_that_shadows_a_builtin(self):
|
|
35
|
+
"""A shadowing *function* is the abstraction; a shadowing *type* is not.
|
|
36
|
+
|
|
37
|
+
`sum` and the rest answer for a scalar what the builtin does. A type
|
|
38
|
+
does not: it is what `isinstance` is asked about and what an
|
|
39
|
+
annotation names.
|
|
40
|
+
"""
|
|
41
|
+
shadowing_types = [
|
|
42
|
+
name for name in arrays.__all__
|
|
43
|
+
if isinstance(getattr(arrays, name, None), type)
|
|
44
|
+
and hasattr(builtins, name)
|
|
45
|
+
and getattr(arrays, name) is not getattr(builtins, name)
|
|
46
|
+
]
|
|
47
|
+
self.assertEqual(shadowing_types, [])
|
|
48
|
+
|
|
49
|
+
def test_the_reductions_are_still_numpy(self):
|
|
50
|
+
"""Removing one shadow must not take the abstraction with it."""
|
|
51
|
+
for name in ('sum', 'min', 'max', 'any', 'abs'):
|
|
52
|
+
self.assertIn(name, arrays.__all__)
|
|
53
|
+
self.assertIsNot(getattr(arrays, name), getattr(builtins, name), name)
|
|
54
|
+
|
|
55
|
+
def test_log_is_still_not_exported(self):
|
|
56
|
+
"""`math.log` and `numpy.log` differ, so neither is offered here."""
|
|
57
|
+
self.assertNotIn('log', arrays.__all__)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestSafeCompare(unittest.TestCase):
|
|
61
|
+
"""Comparing two field values, either of which may be an array."""
|
|
62
|
+
|
|
63
|
+
def test_two_equal_arrays_compare_equal(self):
|
|
64
|
+
first = arrays.array([1.0, 2.0, 3.0], 'f')
|
|
65
|
+
second = arrays.array([1.0, 2.0, 3.0], 'f')
|
|
66
|
+
self.assertTrue(arrays.safeCompare(first, second))
|
|
67
|
+
|
|
68
|
+
def test_two_different_arrays_do_not(self):
|
|
69
|
+
first = arrays.array([1.0, 2.0, 3.0], 'f')
|
|
70
|
+
second = arrays.array([9.0, 9.0, 9.0], 'f')
|
|
71
|
+
self.assertFalse(arrays.safeCompare(first, second))
|
|
72
|
+
|
|
73
|
+
def test_arrays_alike_in_one_place_are_not_equal(self):
|
|
74
|
+
"""Every element has to match, not merely one of them: the
|
|
75
|
+
lineariser asks this to decide whether a field is still at its
|
|
76
|
+
default, and a translation of (1,0,0) shares two with (0,0,0)."""
|
|
77
|
+
first = arrays.array([0.0, 0.0, 0.0], 'f')
|
|
78
|
+
second = arrays.array([1.0, 0.0, 0.0], 'f')
|
|
79
|
+
self.assertFalse(arrays.safeCompare(first, second))
|
|
80
|
+
|
|
81
|
+
def test_arrays_of_different_lengths_are_not_equal(self):
|
|
82
|
+
self.assertFalse(arrays.safeCompare(arrays.array([1.0, 2.0], 'f'),
|
|
83
|
+
arrays.array([1.0], 'f')))
|
|
84
|
+
|
|
85
|
+
def test_a_sequence_equals_the_array_of_the_same_numbers(self):
|
|
86
|
+
"""A node declares its default as a list and stores its value as an
|
|
87
|
+
array, and the two are the same value."""
|
|
88
|
+
self.assertTrue(arrays.safeCompare([0.0, 0.0, 0.0],
|
|
89
|
+
arrays.array([0.0, 0.0, 0.0], 'f')))
|
|
90
|
+
|
|
91
|
+
def test_a_sequence_of_other_numbers_does_not(self):
|
|
92
|
+
self.assertFalse(arrays.safeCompare([0.0, 0.0, 0.0],
|
|
93
|
+
arrays.array([1.0, 0.0, 0.0], 'f')))
|
|
94
|
+
|
|
95
|
+
def test_two_equal_sequences_compare_equal(self):
|
|
96
|
+
self.assertTrue(arrays.safeCompare([1.0, 2.0], (1.0, 2.0)))
|
|
97
|
+
|
|
98
|
+
def test_two_empty_sequences_compare_equal(self):
|
|
99
|
+
self.assertTrue(arrays.safeCompare([], arrays.array([], 'f')))
|
|
100
|
+
|
|
101
|
+
def test_a_list_of_objects_compares_by_identity(self):
|
|
102
|
+
"""Which is what an MFNode's value is: the same nodes or not."""
|
|
103
|
+
held = [object(), object()]
|
|
104
|
+
self.assertTrue(arrays.safeCompare(held, list(held)))
|
|
105
|
+
self.assertFalse(arrays.safeCompare(held, [held[0], object()]))
|
|
106
|
+
|
|
107
|
+
def test_a_ragged_sequence_falls_back_to_comparing_it(self):
|
|
108
|
+
"""numpy refuses to make an array of rows of different lengths, and
|
|
109
|
+
the two lists still have an answer."""
|
|
110
|
+
ragged = [[1, 2], [3]]
|
|
111
|
+
self.assertTrue(arrays.safeCompare(ragged, [[1, 2], [3]]))
|
|
112
|
+
self.assertFalse(arrays.safeCompare(ragged, [[1, 2], [4]]))
|
|
113
|
+
|
|
114
|
+
def test_something_neither_kind_falls_back_to_comparing_it(self):
|
|
115
|
+
self.assertTrue(arrays.safeCompare({'a': 1}, {'a': 1}))
|
|
116
|
+
self.assertFalse(arrays.safeCompare({'a': 1}, {'a': 2}))
|
|
117
|
+
|
|
118
|
+
def test_it_answers_a_real_bool(self):
|
|
119
|
+
"""A caller writing `if x is True` or storing the answer wants one."""
|
|
120
|
+
first = arrays.array([1.0], 'f')
|
|
121
|
+
answer = arrays.safeCompare(first, arrays.array([1.0], 'f'))
|
|
122
|
+
self.assertIs(type(answer), builtins.bool)
|
|
123
|
+
|
|
124
|
+
def test_none_compares_equal_to_none(self):
|
|
125
|
+
self.assertTrue(arrays.safeCompare(None, None))
|
|
126
|
+
|
|
127
|
+
def test_none_and_a_value_do_not_compare_equal(self):
|
|
128
|
+
self.assertFalse(arrays.safeCompare(None, 3))
|
|
129
|
+
self.assertFalse(arrays.safeCompare(3, None))
|
|
130
|
+
|
|
131
|
+
def test_two_equal_scalars_compare_equal(self):
|
|
132
|
+
self.assertTrue(arrays.safeCompare(3, 3))
|
|
133
|
+
self.assertTrue(arrays.safeCompare(3.5, 3.5))
|
|
134
|
+
self.assertTrue(arrays.safeCompare('a', 'a'))
|
|
135
|
+
|
|
136
|
+
def test_values_of_different_types_do_not(self):
|
|
137
|
+
self.assertFalse(arrays.safeCompare(arrays.array([1.0], 'f'), 'a'))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class TestTypeCode(unittest.TestCase):
|
|
141
|
+
def test_it_answers_the_arrays_own_code(self):
|
|
142
|
+
self.assertEqual(arrays.typeCode(arrays.array([1.0], 'f')), 'f')
|
|
143
|
+
self.assertEqual(arrays.typeCode(arrays.array([1.0], 'd')), 'd')
|
|
144
|
+
|
|
145
|
+
def test_it_wants_an_array_rather_than_a_sequence(self):
|
|
146
|
+
"""It reads the code off the value; a list carries none."""
|
|
147
|
+
with self.assertRaises(AttributeError):
|
|
148
|
+
arrays.typeCode([1.0, 2.0])
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class TestContiguous(unittest.TestCase):
|
|
152
|
+
def test_a_contiguous_array_is_answered_as_it_is(self):
|
|
153
|
+
original = arrays.array([1.0, 2.0, 3.0], 'f')
|
|
154
|
+
self.assertTrue(arrays.contiguous(original).flags['C_CONTIGUOUS'])
|
|
155
|
+
|
|
156
|
+
def test_a_sliced_array_is_made_contiguous(self):
|
|
157
|
+
sliced = arrays.array([[1.0, 2.0], [3.0, 4.0]], 'f')[:, 0]
|
|
158
|
+
self.assertTrue(arrays.contiguous(sliced).flags['C_CONTIGUOUS'])
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class TestWhichImplementation(unittest.TestCase):
|
|
162
|
+
def test_it_names_the_one_it_is_using(self):
|
|
163
|
+
self.assertEqual(arrays.implementation_name, 'numpy')
|
|
164
|
+
|
|
165
|
+
def test_the_trig_is_the_array_kind(self):
|
|
166
|
+
"""`math.sin` keeps a float32 float32; the array one widens it."""
|
|
167
|
+
self.assertIsNot(arrays.sin, math.sin)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == '__main__':
|
|
171
|
+
unittest.main()
|