Predefined shapes¶
What we use vertices to define polygons, either 3D or 2D, is prone to error. Here, we design some common shapes for wall/roofs in building or/and for solar panels for electricity or thermal fluids, which can be initiated extactly and conveniently by dimensions other than vertices. As stated above, the shapes are described in 2D space and can be moved to their real 3D positions by using function move() in VisualShape3D.
Note
All 2D shapes are defined in Shapes, and one has to import it as shown below before initiating any of them.
>>> import VisualShape3D.Shapes as shape
2D shapes¶
At first, we define the simple shapes by dimensions, and their vertices can be calculated as indicated,
W, H = 1.0, 0.5
shape.rectangle(W,H)
>>> array([[0. , 0. , 0. ],
[0. , 1. , 0. ],
[0. , 1. , 0.5],
[0. , 0. , 0.5]])
A = 0.2
shape.triangle(W,H,A)
>>> array([[0. , 0. , 0. ],
[0. , 1. , 0. ],
[0. , 0.2, 0.5]])
B,C = 0.5, 0.2
shape.fourSided(W,H,A,B,C)
>>> array([[0. , 0. , 0. ],
[0. , 1. , 0. ],
[0. , 0.4, 0.5],
[0. , 0.2, 0.5]])
D = 0.5
shape.fiveSided(W,H,A,B,C,D)
>>> array([[0. , 0. , 0. ],
[0. , 1. , 0. ],
[0. , 1.2, 0.5],
[0. , 0.7, 0.5],
[0. , 0.2, 0.5]])
As shown in these figures, the dimensions of these shapes produce their own vertices in counter-clockwise.loops.
A complex 2D shape¶
For windows/doors/roofs or other walls/panels with opennings, we design a more complex shape.
W,H,A,B,C,D = 1.0,0.5, 0.4, 0.2, 0.2, 0.3
shape.rectangleWithHole(W,H,A,B,C,D)
>>> array([[0. , 0. , 0. ],
[0. , 1. , 0. ],
[0. , 1. , 0.5],
[0. , 0.3, 0.5],
[0. , 0.3, 0.4],
[0. , 0.7, 0.4],
[0. , 0.7, 0.2],
[0. , 0.3, 0.2],
[0. , 0.3, 0.5],
[0. , 0. , 0.5]])
rectangleWithHole(W,H,A,B,C,D) will not always return 10 vertices, and for the following special cases, it will return vertices as few as possible.
The following code is for verification of the design.
import numpy as np
from VisualShape3D.Shapes import *
def shape2D(W,H,A,B,C,D):
ret = rectangleWithHole(W,H,A,B,C,D)
x,y,z = zip(*ret)
return np.array([y,z])
%matplotlib notebook
import matplotlib.pyplot as plt
fig, axs = plt.subplots(3,3)
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.0
C = H - B
xs,ys = shape2D(W,H,A,B,C,D)
axs[0,0].set_aspect('equal', 'datalim')
axs[0,0].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[0,0].set_axis_off()
label = f"{xs.size} vertices"
axs[0,0].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.0
C = H - B
D = 0.8
xs,ys = shape2D(W,H,A,B,C,D)
axs[0,1].set_aspect('equal', 'datalim')
axs[0,1].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[0,1].set_axis_off()
label = f"{xs.size} vertices"
axs[0,1].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.0
C = H - B
D = W - A
xs,ys = shape2D(W,H,A,B,C,D)
axs[0,2].set_aspect('equal', 'datalim')
axs[0,2].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[0,2].set_axis_off()
label = f"{xs.size} vertices"
axs[0,2].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.0
xs,ys = shape2D(W,H,A,B,C,D)
axs[1,0].set_aspect('equal', 'datalim')
axs[1,0].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[1,0].set_axis_off()
label = f"{xs.size} vertices"
axs[1,0].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.8
xs,ys = shape2D(W,H,A,B,C,D)
axs[1,1].set_aspect('equal', 'datalim')
axs[1,1].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[1,1].set_axis_off()
label = f"{xs.size} vertices"
axs[1,1].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.8
D = W - A
xs,ys = shape2D(W,H,A,B,C,D)
axs[1,2].set_aspect('equal', 'datalim')
axs[1,2].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[1,2].set_axis_off()
label = f"{xs.size} vertices"
axs[1,2].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.8
C = 0
D = 0
xs,ys = shape2D(W,H,A,B,C,D)
axs[2,0].set_aspect('equal', 'datalim')
axs[2,0].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[2,0].set_axis_off()
label = f"{xs.size} vertices"
axs[2,0].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.8
C = 0
xs,ys = shape2D(W,H,A,B,C,D)
axs[2,1].set_aspect('equal', 'datalim')
axs[2,1].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[2,1].set_axis_off()
label = f"{xs.size} vertices"
axs[2,1].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
W,H,A,B,C,D = 3,2.5,1.5,1.0,0.7,0.8
C = 0
D = W - A
xs,ys = shape2D(W,H,A,B,C,D)
axs[2,2].set_aspect('equal', 'datalim')
axs[2,2].fill(xs, ys, alpha=0.5, fc='r', ec='none')
axs[2,2].set_axis_off()
label = f"{xs.size} vertices"
axs[2,2].annotate(label, (0.1, 0.5), xycoords='axes fraction', va='center')
plt.show()
Transformation to 3D position¶
After all 2D shapes have been defined with their origin at (0,0,0), one can transform a shape to its 3D position by rotating and translating. The rotating angles and the new 3D position of origin are designated as follow.

Accordingly, the transformation matrix is
Example 1
A cubic box
import numpy as np
from VisualShape3D.Shapes import *
def box3D(L,W,H):
rectLW = rectangle(L,W)
rectWH = rectangle(W,H)
rectLH = rectangle(L,H)
bottom = move(shape = rectLW,to = (0,0,0), by = (0,-90) )
top = move(shape = rectLW,to = (W,0,H), by = (0, 90) )
front = move(shape = rectLH,to = (W,0,0), by = (0,0) )
back = move(shape = rectLH,to = (0,L,0), by = (180,0) )
left = move(shape = rectWH,to = (0,0,0),by = (-90, 0))
right = move(shape = rectWH,to = (W,L,0),by = ( 90, 0))
polygons = np.array([front,right,back,left,top,bottom])
return polygons
%matplotlib notebook
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
polygons = box3D(1,0.7,0.5)
fig = plt.figure()
ax = Axes3D(fig)
box = Poly3DCollection(polygons)
box.set_facecolor('brown')
box.set_edgecolor('yellow')
ax.add_collection3d(box)
ax.axis('off')
plt.show()
Example 2
A simple house
import numpy as np
from VisualShape3D.Shapes import *
def house3D(L,W,H,A,B,C,D):
rectLW = rectangle(L,W)
rectWH = rectangle(W,H)
rectLH = rectangle(L,H)
rectLH_window = rectangleWithHole(L,H,A,B,C,D)
bottom = move(shape = rectLW,to = (0,0,0), by = (0,-90) )
top = move(shape = rectLW,to = (W,0,H), by = (0, 90) )
front = move(shape = rectLH_window,to = (W,0,0), by = (0,0) )
back = move(shape = rectLH,to = (0,L,0), by = (180,0) )
left = move(shape = rectWH,to = (0,0,0),by = (-90, 0))
right = move(shape = rectWH,to = (W,L,0),by = ( 90, 0))
polygons = np.array([front,right,back,left,top,bottom])
return polygons
%matplotlib notebook
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import matplotlib.pyplot as plt
L,W,H,A,B,C,D = 3,3,2.5,1.5,1.0,1.0,0.7
box = Poly3DCollection(house3D(L,W,H,A,B,C,D))
fig = plt.figure()
ax = Axes3D(fig)
box.set_facecolor('brown')
box.set_edgecolor('yellow')
ax.add_collection3d(box)
ax.axis('off')
plt.show()