Pysometric is a simple Python library for creating isometric 3D line art that can be rendered to SVG, typically for use with pen plotters.
It is currently intended to be used in conjunction with the vsketch generative toolkit (though this dependency may be removed in the future).
- Renders 2D rectangles and polygons in 3D isometric projection
- Allows creation of complex 3D shapes from groups of polygons (e.g. boxes, pyramids, prisms)
- Supports textures (e.g. hatch and solid fills) on any polygon
- Automatically occludes lines (hidden line removal) based on scene z-order
- Adds support for circles using the
Circleclass
- Initial release
Pysometric currently requires vsketch for rendering. You will need to install vsketch before using this library.
To get started with Pysometric, clone the repository and install it using pip:
$ git clone https://github.com/svoisen/pysometric.git
$ cd pysometric
$ pip install .Once the library is installed you can create a simple isometric scene using pysometric inside a vsketch sketch. For example, in the draw method of your sketch:
# Create a frame for the scene that is the same size as the vsketch page
frame = shapely.box(0, 0, vsk.width, vsk.height)
# Create a Scene with a single 3D box at the origin and
# a grid pitch size of 100 CSS pixels
box = Box((0, 0, 0))
scene = Scene(frame, 100, [box])
# Render the scene to ths ketch
scene.render(vsk)More examples may be found in the examples directory.
This project uses pytest. Run tests by executing the following in the root directory of the repository:
$ pytestPysometric is intended for the creation of static isometric 3D scenes as line art. It is not a true 3D system, nor does it support animation. As such, it is intentionally limited by the following constraints:
Pysometric's API is immutable. Setup your scene once, then render it. That's it. Because it does not support animation, scenes, shapes and volumes inpysometriccannot be modified after their initial creation.Pysometrichas no concept of light sources. Shadows can be emulated using hatching or fill textures on polygons, but they cannot be generated automatically for a shape or entire scene based on lighting.
A Scene defines an isometric 3D scene that can be rendered to 2D line art. All shapes to be rendered by pysometric must be part of a scene. When creating a Scene you must specify:
grid_pitch: The size of single grid coordinate unit for the 3D scene, which controls the overall scale of the scene.frame: A scene has aframethat defines its rendering boundaries, and can be anyshapelypolygon.children: A list of all of the childRenderables in the scene. The order of this list defines the order in which the objects are rendered. Items in the list are rendered from first to last, meaning that items with lower indices will occlude items with higher indices, irrespective of their position in 3D space.
The Scene has a render method that accepts a Vsketch instance as a parameter and outputs the scene to the given sketch.
All 3D coordinates should be specified as Vector3 objects defining (x, y, z) positions. Axes in pysometric are as follows:
- x-axis: The axis oriented visually left to right (at an upward diagonal)
- y-axis: The axis oriented visually from back to front (at a downward diagonal)
- z-axis: The vertical axis running from bottom to top
The base class for any object that can be added to a Scene. All Renderables have a compile method which is used during rendering to compile the object into a RenderableGeometry.
A RenderableGeometry is the output of the compile method for a Renderable, and includes information necessary for rendering the object in 2D, such as its 2D geometry, stroke thickness and stroke color (layer).
A Polygon defines a general polygon shape. It is a subclass of Renderable.
A Polygon is initialized with:
vertices: A list ofVector3objects defining the vertices of the polygon. The vertices should be specified in clockwise or counter-clockwise order.textures: Optional list ofTextureobjects to apply to the polygon.rotations: Optional list ofRotationobjects to rotate the polygon.layer: The layer for the polygon. Default is 1.
The following example creates a square polygon with vertices at (0, 0, 0), (10, 0, 0), (10, 10, 0) and (0, 10, 0):
polygon = Polygon([
(0, 0, 0),
(10, 0, 0),
(10, 10, 0),
(0, 10, 0)
])The RegularPolygon defines a symmetrical polygon with a specific number of sides. It is a subclass of Polygon and Renderable.
A RegularPolygon is initialized with:
origin: The (x, y, z) coordinates of the center of the polygon defined as aVector3.num_vertices: The number of vertices/sides for the polygon. Must be 3 or greater.radius: The radius/distance from the center to each vertex.orientation: The plane in which the polygon lies. Must be one of Plane.XY, Plane.XZ or Plane.YZ.textures: Optional list ofTextureobjects to apply to the polygon.rotations: Optional list ofRotationobjects to rotate the polygon.layer: The layer for the polygon. Default is 1.
The following example creates a hexagon with radius 10 at the origin on the plane defined by the X and Y axes.
polygon = RegularPolygon((0, 0, 0), 6, 10, Plane.XY)A Rectangle defines a rectangular polygon in 3D space.
By default, rectangles have an orientation parallel to one of the 3 possible planes defined in the Plane enumeration.
A Rectangle is initialized with:
origin: The (x, y, z) coordinates of the center of the rectangle defined as aVector3.width: The width of the rectangle.height: The height of the rectangle.orientation: The plane in which the rectangle lies. Must be one ofPlane.XY,Plane.XZorPlane.YZ.textures: Optional list ofTextureobjects to apply to the rectangle.rotations: Optional list ofRotationobjects to rotate the rectangle.layer: The layer for the rectangle. Default is 1.
The following example creates a rectangle with width 10 and height 5 centered at the origin on the XZ plane:
rectangle = Rectangle((0, 0, 0), 10, 5, Plane.XZ)A Group defines a collection of Renderable objects that should be treated as a single object. Groups allow you to organize complex shapes in your scene by combining multiple polygons and volumes.
A Group is initialized with:
children: A list ofRenderableobjects (e.g.Polygon,Rectangle,Box,Prism) that make up the group.
The following example creates a Group that renders as a cube, defined by 6 Rectangle objects (one for each side of the cube):
cube = Group([
Rectangle((0, 0, 0), 10, 10, Plane.XY), # Top face
Rectangle((0, 0, -10), 10, 10, Plane.XY), # Bottom face
Rectangle((0, -10, 0), 10, 10, Plane.YZ), # Front face
Rectangle((0, 10, 0), 10, 10, Plane.YZ), # Back face
Rectangle((-10, 0, 0), 10, 10, Plane.XZ), # Left face
Rectangle((10, 0, 0), 10, 10, Plane.XZ) # Right face
])A Box defines a rectangular cuboid in 3D space.
Boxes have a width, depth and height dimension.
A Box is initialized with:
origin: The (x, y, z) coordinates of the bottom left front corner of the box defined as a Vector3.width: The width of the box.depth: The depth of the box.height: The height of the box.top: Optional dictionary containing:textures: A list of Texture objects to apply to the top face.layer: The render layer for the top face.
left: Optional dictionary containing:- textures: A list of Texture objects to apply to the left face.
layer: The render layer for the left face.
right: Optional dictionary containing:- textures: A list of Texture objects to apply to the right face.
- layer: The render layer for the right face.
The following example creates a box with width 2, depth 3 and height 4 centered at (1, 1, 1) with:
- A hatch texture on the top face
- The left face rendered on layer 2
- A fill texture on the right face
box = Box((1, 1, 1), 2, 3, 4,
top={"textures": [HatchTexture(4)]},
left={"layer": 2},
right={"textures": [FillTexture()]}
)A Circle defines an isometric circle in 3D space.
A Circle is initialized with:
center: A Vector3 specifying the 3D center point of the circle.radius: The radius of the circle.orientation: The plane in which the circle lies. Must be one ofPlane.XY,Plane.XZorPlane.YZ.num_segments: Circles are approximated using a series of straight line segments. The higher the number of segments, the smoother the circle at the cost of higher rendering cost.textures: Optional list ofTextureobjects to apply to the circle.rotations: Optional list ofRotationobjects to rotate the circle.layer: The layer for the rectangle. Default is 1.
circle = Circle((5, 10, 15), 5, Plane.XY)Pull requests are welcome.
This project is licensed under the MIT License — see the LICENSE file for details.
