COMPUTER AIDED ARCHITECTURAL DESIGN
Workshop
15 Notes, Week of November 15 , 2015

SURFACES BASED UPON POLYNOMIAL EXPRESSIONS

This section picks out some polynomial expressions and the basic surfaces which they can produce.

In the examples below, polygons are described with reference to the World Coordinate System. It is an eclectic collection derived from various textbook sources; however, there is consistency in how the basic surface elements are generated. In the examples polynomial expressions are used to calculate the vertices of a number of simple four sided surface polygons. The four sided surface polygons are juxtaposed in three dimensional space so as to create larger surface areas. 

PART 1. Hyperbolic Parabolic Surface

In the illustration below, we see a polygonal mesh description of a hyperbolic paraboloid shape on the left-hand side and its rendered surface description on the right-hand side [derived from Lord, The Mathematical Description of Shape and Form, John Wiley and Sons, 1984].

hyperbolic parabolic surf hyperbolic parabolic surf

Perspective View Hyperbolic Parabolic surface


The x-coordinate and y-coordinate of the polygon vertices range over a set of values stepping in increments of 1 from -10 to +10. That is, we have -10 <= Point.X <= 10 and -10 <= Point.Y <= 10. Note the regularity of this uniform x-coordinate and y-coordinate grid mesh is apparent when seen in the plan view as depicted below. However, the z-coordinate changes non-uniformly from vertex to vertex and accounts most directly for the curvature that is apparent in the views of the surfaces above.

hyperbolic parabolic surf hyperbolic parabolic surf

Plan View Hyperbolic Parabolic surface

Sub procedure that calculates the z-coordinate of the hyperbolic paraboloid surface

The full Python script  that generates the hyperbolic paraboloid surface will be given further below. First, however, we examine the logic of some of its parts. For each of the four sided polygons above, the z-coordinate value of each vertex is the function of its x-coordinate and y-coordinate values. The general mathematical expression used to calculate the z-coordinate of the vertices is:

z = cScalar   * (pow(x, 2) - pow(y,2))

where s is a scalar coordinate, z is the z-coordinate, x is the x-coordinate, and y is the y-coordinate value of a particular given polygon vertex in the figure above, and where "pow" exponentiation function.  Note that to generate the specific surface above, cScalar = 0.1, -10 <= x < 10, and -10 <= y <= 10. Modification of the scalar s impacts the relative height the z-coordinate each polygon vertex:

In particular, examine the  function surfProc that takes a three dimensional Point and calculates its z coordinate Point.Z based on the values of Point.X and Point.Y and the scalar cScalar. That is,  graphics function surfProc takes in x, y, and cScalar,  and returns a new point named newPoint  with the new z coordinate as indicated below.

def hyperbolicParabolic(x, y, cScalar):
....#calculate hyperbolic parabolic value of z for modification of curPoint
....z = cScalar * (pow(x, 2) - pow(y, 2))
....newPoint = rh.Geometry.Point3d(x, y, z)
....return newPoint

PART 2: Calculating polygon grid faces of hyperbolic paraboloid surface

In the following figure, there are nine vertices, labelled 0 through 8,  and four associated faces determined by vertices (0, 1, 4, 3), (1, 2, 5, 4), (3, 4, 7, 6) and (4, 5, 8, 7). To generate the surface, we need a way to organize every four adjacent vertices such that they form the individual grid faces that constitute the overall surface. Thus a list of all the vertices and an indexing system that determines all the subgroups of 4 vertices forming each face are both needed. (This example is modified after a rhinoscriptsyntax help example provided by McNeel Inc.)

four faces

A python script to generate the polygon mesh surface and index into the vertices would be as follows.  Note that the "faceVertices" array contains a list of  index locations in the "vertices" array for each face of the polygon mesh. 

import rhinoscriptsyntax as rs
import Rhino as rh

#vertices of polygon mesh
pt0 = rh.Geometry.Point3d(0, 0, 0)
pt1 = rh.Geometry.Point3d(5, 0, 0)
pt2 = rh.Geometry.Point3d(10, 0, 0)
pt3 = rh.Geometry.Point3d(0, 5, 0)
pt4 = rh.Geometry.Point3d(5, 5, 0)
pt5 = rh.Geometry.Point3d(10, 5, 0)
pt6 = rh.Geometry.Point3d(0, 10, 0)
pt7 = rh.Geometry.Point3d(5, 10, 0)
pt8 = rh.Geometry.Point3d(10, 10, 0)
#create array of vertices
vertices = [pt0, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8]

#create index into vertices array to determine faces
faceVertices = []
faceVertices.append((0,1,4,3))
faceVertices.append((1,2,5,4))
faceVertices.append((3,4,7,6))
faceVertices.append((4,5,8,7))

a = rs.AddMesh( vertices, faceVertices ) #output variable a
b = vertices #output variable b

Similarly, the following function contains two while loops that handle the work of creating faces  for some range of values in Point.X and Point.Y. In the outer loop, -depth/2.0 <= Point.Y= <depth/2.0, and in the inner loop -width/2.0 <= Point.X  <= width/2.0.   Within the inner loop, we calculate the z-coordinate value of each polygon vertex point with a call to the sub procedure hyperbolicParabolic. 


def makePolyMesh(centerPt, numRows, numCols, width, depth, cScalar):
....ptList = []
....faceList = []
....xSpace = width/(numCols - 1)
....ySpace = depth/(numRows - 1)
....for ii in range(0, numRows, 1): # outer loop handles values of y
........for i in range(0, numCols, 1): # inner loop handles values of x
............x = (centerPt.X - width/2.0) + i * xSpace
............y = (centerPt.Y - depth/2.0) + ii * ySpace
............curPoint = hyperbolicParabolic(x, y, cScalar)
............ptList.append(curPoint) # build the array of vertices
............if ((ii < numRows - 1) and (i < numCols - 1)):
............faceList.append((ii * numCols + i, ii * numCols + i + 1, (ii + 1) * numCols + i + 1, (ii + 1) * numCols + i)) # build index of faces corresponding to the array of vertices
....mesh = rs.AddMesh(ptList, faceList)
....return mesh

Note that  also build up an array consisting of sets of face vertices with the statement:

faceList.append((ii * numCols + i, ii * numCols + i + 1, (ii + 1) * numCols + i + 1, (ii + 1) * numCols + i))

Putting the functions together wihtin a complete Python script.

1. Create a Rhino drawing with a point at the origin at the x-y plane.

point at origin

2. Create a Grasshopper Python component with the following input parameters.

CenterPt set to the point3d from step 1.

width as a floating point variable in the range of 0.1 to 720.0
depth as a floating point variable in the range of 0.1 to 720.0
cScalar as a floating point variable in the range of -20.0  to 20.0
numRows as an integer variable in the range of 2  to 100
numCols as an integer variable in the range of 2  to 100

Python parameters

3. Create the following script within the Python component.

script

4. With parameters setup in part 3, baking the output of the component yields the following figure.

hyperbolic parabolic mesh

5. Varying the cScalar parameter setup in part 3 gives some of the following shapes (the view has been rotated to help see the resulting geometry).

cscalarImage1 cscalarImageq cscalarImage3 cscalarImage4
cScalar = 0.1 cScalar = 0.25 cScalar = 0.5 cScalar =1

PART 3. Subsituting Alternative Polynomial Expressions

1. The key to the shape of the polygon mesh in the function hyperbolicParabolic is the expression z = cScalar * (pow(x, 2) - pow(y, 2)). If we replace this function with one containing alternative values of z, it results in the following table.Note that for the hyperbolic parabolic mesh, the eliptical parabolic mesh, and the simple saddle shape, the width and height variables should be set to no greater than 10, and cScalar no greater than -1 to 1.  For  the sine function shapes, the width and depth should be no less than 360 degrees, cScalar should be set to a minimum of 30, and numRows and numCols both be set to 100. The basic Python script can be download from this link: generalMesh.gh.


#Hyperbolic Parabolic Mesh
z = cScalar * (pow(x, 2) - pow(y, 2))

hyperbolic parabolic mesh
#Eliptical Parabolic Mesh
z = cScalar * (pow(x, 2) + pow(y, 2))
eliptical parabolic mesh
#Simple Saddle
z = cScalar * (x * y)
simple saddle
#Sine Surface
frequency = 2
z = cScalar * abs(math.sin(math.radians(x * frequency)))
sin surface
#Sine Surface in X and Y
frequency = 2   
z = cScalar * abs(math.sin(math.radians(x * frequency)) + math.sin(math.radians(y * frequency)))
sin in x and y
#Concentric Sine Wave
dist = math.sqrt(pow(x,2) + pow(y,2))
frequency = 3
z = cScalar * abs(math.sin(math.radians(frequency * dist)) + math.sin(math.radians(frequency * dist)))
concentric sin wave

For example, in the case of a concentric sin surface, the script could be writting as follows:

concentric sin wav

Summary

Within this workshop we have developed a few basic scripts for representing three-dimensional surfaces . The principal building block of these surface elements has been a four sided polygon. We examined how such four side polygons could be arrayed together so as to create various kinds of polynomial based surfaces. The examples also demonstrate the power of encapsulating the basic parameters of a particular architectural composition within a few variables. In the case of polynomial related surfaces, we were able to succinctly describe the range of the surface (i.e., the variables width, depth), the scale of its curvature (i.e., the variable sScalar) and the resolution of its curvature (i.e., the variables numRows and NumCols).