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 Shape4D.

_images/shapes.PNG

Note

All 2D shapes are defined in Shapes, and one has to import it as shown below before initiating any of them.

>>> import Shape4D.Shapes as shape

2D shapes

At first, we define the simple shapes by dimensions, and their vertices can be calculated as indicated,

_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 2D 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 Shape4D.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

Example 1

A cubic box

_images/matrix.PNG
import numpy as np
from Shape4D.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 Shape4D.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