Roadmap#
Todo
What is the role of layers ? -> Color and penwidth.
Be able to call Axonometry.show() not only at end but alsoduring script as well. Currently the drawing is centered when calling show and at the next call geometries will not overlap. -> Done
Inherit the compas.datastructure.Tree or its implementation compas.scene.Scene to collect and organize the geometry (import and export to json are possible then !) instaed of a single list. The vpype.Document is recording the geometries in parallel and used to save SVG files with the drawing operations in order. Make my geometry inherit compas.data.Data ? -> Can be done by using a compas.scene.Scene object.
Use compas_viewer to see the geometry and vpype to render plotter paths. -> vpype_viewer is OK.
Add standard axonometries in the Config TOML.
When adding a line to a plane, check if points already exist in plane (i.e. in objects from a previous projection). -> Done
Make auxilary line points on the same reference plane. -> Done.
When projecting to axonometry, make auxilary plane selection a choice. -> Done.
Intersection: Tricky but solvable. The intersection is a compas Point, once found, project it onto the two lines projected lines with the trihedron vector. And recalculate the correct user coordinates from these points with the distance from reference plane axes.
Should auxilary plane be avoided if object is perpendicular ? -> Yes, done.
Handle closing vpype.execute(“show”)/vpype_viewer command.
.. glossary::Implementation: start with a complete axonometry and break it down into parts and how they are implemented ?
Do not add new point/line in plane if these already exists ! -> Done
Use
vpype_viewerinstead ofvpype_cli.execute()to display paths.Check if not redundant pairing with
pair_projections_lines()Check that geometries are added to the drawing object in the right order.
Implement SVG import for each ReferencePlane.
When points of a new line are equal, the plane becomes a tuple. Why ? For now solved by forcing non-equal points.
Make line widths dependent on page_size.
Use directly circle radius config to define points visibility, None or 0 is a no points parameter.
Make drawing traces a separate function with 2 points as parameters.
In Plane.objects add clever subcollections in order to fine grain the selection of pre generated elements: ex. “parallel to y”, “perpendicular to x”, etc.
make two document objects: one chronologic and one in layers per line type.
Import obj files as wireframes by parsing the lines.
Make random point/line a static class method -> Done
Make static method to create points with .xy(). .yz(), .zx(). -> Done
Add the vpype-vectrace plugin to import png etc. as well.
Saving GIF files with the help of
pillow.Improve performance for GIF generation. Maybe with Queue object ?
New parameter for add_*: when in XYZ one can also just add to all three reference planes without a projection in the axonometry plane.
For into_surface or into_line projections: if distance is ommitted, the surface/line starts from coordinate plane.
Make function to stream SVG data so it can be directly used by the vpype cli.
Add ‘invisible’ mode in which axes are not added to the drawing.
Change angles name to left/right_inclination where it’s about axonometric notation.
Write error message like context error for Axo- or RefPlane calling draw_* and project methods with wrong parameters. Later implement context managers. c.f. <https://stackoverflow.com/a/60367552>
SNIPPETS#
Make something like this for the Point, Line, Surface Objects:
class Line:
"""To be implemented soon."""
def __init__(self, start: Point, end: Point) -> None: # noqa: D107
assert is_coplanar([start, end]), "Points are not in the same plane."
self.plane = None # set by parent
self.projections = {"xy": None, "yz": None, "zx": None, "xyz": []}
self.start = start
self.end = end
self.key = {start.key, end.key}.pop()
self._data: CLine | None = None # Use a private attribute to store data
@property
def __data__(self):
return {"start": self.start.data.__data__, "end": self.end.data.__data__}
@property
def data(self):
"""Getter for the line's data."""
if self._data is None:
# If _data is not set, create it based on start and end points
self._data = CLine(CPoint(*self.start.data), CPoint(*self.end.data))
return self._data
@data.setter
def data(self, value):
"""Setter for the line's data."""
if isinstance(value, CLine):
# Update both start and end points to match the new line data
self.start.data = value.point1
self.end.data = value.point2
self._data = value # Store the new value in the private attribute
else:
raise ValueError("Invalid value for line data")
Milestones#
Need to have#
Import existing SVG files and place them in one of the three reference planes.
Project lines and surfaces
Good to have#
auto-execute code (with vsketch)
Considerations#
Future development would focus on separating the geometry part and the axonometry part. That is opne can produce a sequence of events: create lines, project etc. And finally only add them to a certain Axonometry, which controls how the geometry will be rendered by putting each geometry in its right place.
First Principles#
- 3D CGI matrix transformations use 4 Homogeneous Coordinates - we have 2D projections & use 3 Cartesian coordinates. The geometries have a data attribute which basically are compas objects in the view plane coordinate system (= axonometric picture plane = view plane). Objects defined on the axonometric picture plane hold also a complete set of world coordinates (intended for the user). The particularity are an addition of three coordinate sub-systems. Objects can be partially defined in these reference plane coordinate systems. (There’s no local coordinate space)
The fundamental problem is the same as in computer graphics: determine the position of a given point in space, on our screen/paper.
To play with projections quickly, the operations transform points to lines, lines to surfaces, surfaces to solids.
Prefer class methods for operations on a single Point|Line|Surface(polyline); use functions for operations on two or more objects.
Replace the observer design pattern with an operative data structure: tree (I think)
the graphical primitive most often handled by the user is the line: points are most often used to project orthogonal lines from them.
Fewer but more intuitive operations give more possibilities; think about Affordances: a limited set of operations can enhance intuitive operations and expand the use cases.
Most operations leave traces and are recorded in order: making the drawing on a plotter re-enacts this order of construction.
Projective/geometric consistencies are not sought or enforced: projection operations are propagated, but these traces do not have to be consistent with existing or future geometry. Every object needs a minimum of projectional traces to be defined, but all the views are not necessarily updated.
A drawing is never finished, but only the beginning: the aim is not to generate full, glossy drawings in one click.
Lines are internally infinite, but rendered as segments.
compas is used for geometry, vpype for i/o; everything created is stored in a vpype document instance
Use uv for package management. (Previously: venv and conda)
Python version 3.12, not above
Default paper size is A1, units are in mm (converted to CSS pixels for rendering in scale)
Prefer radians (internally)
Three ways of adding/projecting geometry:
From the axonometry object:
Axonometry.add_*(<geometry>). A Point(x,y,z) is added in the axonometric space.Therefore, on can also add objects directly in a reference plane:
Axonometry.<reference_plane_key>.add_*(<geometry>). Axonometry.xy.add_point(Point(x,y)), etc.One can also manipulate objects by their own methods (i.e. their parents are the planes they are in):
Point().project(<parameters>)
Projecting an object always returns a new object
ruff, reuse
point data (and geometry in general) is only created when it is added to a plane: that way, one can compute the data manually and then add the point without transformation to a plane (check: if not point.data -> create/transform data).
Geometry cannot be “moved” around, each operation makes new geaometry.
Projections on refrence planes can only exist once.