Shapes in depth

VisualShapes is the visual version of shape functions, including necessary operations on shapes. It encapsualtes parts of matplotlib for the purpose of instant visualization of shapes just created. In a situation where the visualization is not necessary, it is efficient for one to use the low level shape functions themselves.

Shape functions aim to simplify the creation of polygons with vertices in either 3D or 2D space, a process that is prone to error in practice. Here, we choose the yz plane as the working plane, and design some descriptions for 2D shape dimensions of wall/roofs that are commonly seen in building or/and solar panels of electricity or thermal fluids. As soon as they have been set in the working plane (2D), defined shapes can be moved to their specific 3D positions by using a helper function move().

_images/shapes.PNG

Note

All definitions are included in VisualShape3D.Shapes, and first of all, one has to import it as shown below.

>>> import VisualShape3D.Shapes as shape

Shapes

At first, the relation between dimensions and the vertices of shapes are shown as indicated below,

_images/shape1.PNG
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]])
_images/shape2.PNG
A = 0.2
shape.triangle(W,H,A) 
>>>  array([[0. , 0. , 0. ],
            [0. , 1. , 0. ],
            [0. , 0.2, 0.5]])
_images/shape3.PNG
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]])
_images/shape4.PNG
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.

_images/local_coordinates.PNG

A complex shape

For windows/doors/roofs or other walls/panels with opennings, we design a more complex shape.
_images/shapeComplex.PNG
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.

_images/shapeComplexSpecials.PNG

The following code is for verification of the design.

_images/matrix.PNG
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.

_images/shapes_rotation.png

Accordingly, the transformation matrix is

_images/matrix.PNG

Examples

Example 1

A cubic box

_images/matrix.PNG
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()
_images/matrix.PNG

Example 2

A simple house

_images/matrix.PNG
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()
_images/matrix.PNG