Tuesday, September 15, 2009

Ecotect

I had a meeting today with Thida about the Ecotect results. Here is what we concluded - mostly through trial and error:

Ecotect is good for Sunpath diagrams, Insolation calculations (under Solar Access menu), and Solar Exposure, all on individual surfaces. Insolation calculations on grid points are OK, but they are different from surfaces.
  • For a surface, the radiation can only hit it from one side. In addition, the angle of incidence makes a difference - radiation at right angles to the surface has a bigger effect that radiation coming in at an acute angle.
  • For a grid, the point is idealized like a small ball. The radiation can come from all directions - so the values can be much higher. Also, the angle of incidence makes no difference.

For Lighting Analysis - light levels (lux) and daylight factors (%), Ecotect is quite inaccurate. Therefore, it is better to use Ecotect together with Radiance. Radiance can be downloaded and installed for free . You can do either grids or images.
  • For Ecotect/Radiance grids: You set up the model and the grid in Ecotect, and then use Ecotect to run the Radiance calc, and then import the results back into Ecotect to display the results.
  • For Ecotect/Radiance images: You set up the model and the camera in Ecotect, and then use Ecotect to run the Radiance calc. The image will be displayed in a special Ecotect window, where you can do various things, like false colour images etc.
So, first you need to install Radiance. This is straightforward -
  • Download the software
    http://floyd.lbl.gov/misc/download20B.html
  • Unzip it to a temporary location
  • Run setup.exe
  • Click 'Next' until you get to 'Setup type', choose 'Radiance for Windows'
  • When you get to 'Choose Radiance for Windows Installation Location', the default location is fine. Or you may want to put it on D if you do not have permission for C. However, make sure your path has no spaces.
  • Then click 'Next' all the way to the end.
  • If you get an error about a folder icon, ignore it.
To run and Ecotect/Radiance analysis:
  • Set up your model in Ecotect
  • Set up the grid in Ecotect
  • In the menu, go to Calculate > Lighting Analysis
  • Select Export to Radiance, and click Next
Here are the steps to follow from here on if you want to do a grid analysis:
  1. Choose 'Daylight Factors' (for example)
  2. Choose 'Surface and/or point analysis'
  3. Choose 'Analysis Grid' - and if you want you can also choose the 3D option
  4. Choose 'Interior Views'
  5. De-select 'Current 3d Editor view' and make sure that nothing else is selected
  6. For accuracy, 'Medium' is fine
  7. For the output and installation, first click the Fix button and find the file Rad.exe (in ...Radiance\bin\) - which is teh RAdiance program; the n tell Ecotect where to save the ARdiance files - choose a path with no spaces.
  8. Accept the advanced rendering settings as they are - click next
  9. Finally, you are ready to run radiance - click OK
A black command window will pop up, and some calculations will start. When the calculations are complete, Ecotect will pop up a RadianceImport dialog box - which is asking you if you want to import the results. So click 'Import'. In the Analysis Grid panel, under Data & Scale, you will see RAD Daylight Factors. This will display the results from the Radiance calculation.

Tuesday, September 8, 2009

Ecotect video

There is agreat talk on Ectoect by the software developer Ander Marsh. It is a very good introduction to Ecotect. It is one and a half hours long!

In order to view this talk, you need to create an account at the Autodesk University wesbite (http://au.autodesk.com/) - it is free. Note that this is not the same as the Student Community website (http://students.autodesk.com/)

Log into the AU website, and then view the talk called Practical Sustainable Design Workflows with Ecotect and Revit

Interactive console

Ctrl+Alt+Enter (while in the Eclipse/Pydev editor) can be used to open a console if there's no open console. A dialog box will pop up - select "Console for currently active editor".

Monday, September 7, 2009

Vertical paths

Here is another version of the previous post, but in this case, we get a set of paths where the next point is always higher than the previous point. This uses the sort function.

import py2rhino as p2r
import random as rnd
#===============================================================================
# Parameters
#===============================================================================
num_points = 100
#===============================================================================
# Functions
#===============================================================================
def get_z(point):
return point[2]
#===============================================================================
# Main Script
#===============================================================================
#create some random points
points = []
for i in range(num_points):
x = rnd.randint(-25, 25)
y = rnd.randint(-25, 25)
z = rnd.randint(0,200)
points.append((x,y,z))
p2r.obj.Sphere.create((x,y,z), 0.5)

#sort the points, based on the x vale
points.sort()

#split the list into 4 sub lists
points1 = points[:num_points/4]
points2 = points[num_points/4:num_points/2]
points3 = points[num_points/2:100 - num_points/4]
points4 = points[100 - num_points/4:]

#sort all these lists, based on the z value
#in this case, the sort function is passed the get_z function
#this function returns the z vale (see above) of the point
points1.sort(key=get_z)
points2.sort(key=get_z)
points3.sort(key=get_z)
points4.sort(key=get_z)

#draw 4 vertical lines through these points
for pt_num in range((num_points/4) - 1):
p2r.obj.Line.create(points1[pt_num], points1[pt_num + 1])
p2r.obj.Line.create(points2[pt_num], points2[pt_num + 1])
p2r.obj.Line.create(points3[pt_num], points3[pt_num + 1])
p2r.obj.Line.create(points4[pt_num], points4[pt_num + 1])

print "done"

This script creates some random points and then draws lines to the next closest point. The result is a bit unexpected... the line zig zags all over the place.


import py2rhino as p2r
import random as rnd
#===============================================================================
# Parameters
#===============================================================================
num_points = 100
#===============================================================================
# Main Script
#===============================================================================
#create some random points
points = []
for i in range(num_points):
x = rnd.randint(-25, 25)
y = rnd.randint(-25, 25)
z = rnd.randint(0,200)
points.append((x,y,z))
p2r.obj.Sphere.create((x,y,z), 0.5)

#create a starting point
pt1= (0,0,0)

#draw a line from to the next closest point
for pt_num in range(num_points):
pt2_num = p2r.util.point.points_closest_point(points, pt1)
pt2 = points[pt2_num]
p2r.obj.Line.create(pt1, pt2)
points.remove(pt2)
pt1 = pt2

print "done"

The site...


Here is the site or the studio... It is marked by the red star. It is right next door to the tallest building in Singapore.. the Capital Tower.

Saturday, September 5, 2009

Roof structure

Here is a script that generates some randomly placed roof structure...
No geometry is required

import py2rhino as p2r
import random as rnd
#===============================================================================
# Parameters
#===============================================================================
num_roof_pnts = (8,8)
num_columns = 10
roof_size = (100,100)
roof_height = 50
#===============================================================================
# Main Script
#===============================================================================
points = ( (roof_size[0]/-2, roof_size[1]/-2, roof_height), #x,y,z
(roof_size[0]/2, roof_size[1]/-2, roof_height),
(roof_size[0]/2, roof_size[1]/2, roof_height),
(roof_size[0]/-2, roof_size[1]/2, roof_height) )

roof = p2r.obj.NurbsSurface.create_by_corner_pnts(points)
roof.modf.rebuild((5,5), num_roof_pnts)
roof_pnts = roof.prop.pnts(True)

column_upper_pnts = []
column_lower_pnts = []

#create columns in random positions
for col_num in range(num_columns):

#create the lower and upper points
x_size = 2 * roof_size[0]/5
y_size = 2 * roof_size[1]/5
x_pos = rnd.randint(-1*x_size, x_size)
y_pos = rnd.randint(-1*y_size, y_size)
col_lower_pnt = ( x_pos, y_pos, 0 )
col_upper_pnt = (col_lower_pnt[0], col_lower_pnt[1], roof_height/2)

#save the points
column_lower_pnts.append( col_lower_pnt )
column_upper_pnts.append( col_upper_pnt )

#create the column
base = p2r.obj.Circle.create(col_lower_pnt, 2)
top = p2r.obj.Circle.create(col_upper_pnt, 1)
p2r.obj.NurbsSurface.create_by_loft( (base, top) )

for roof_pnt in roof_pnts:

#for each point on the roof, find the closest column
closest = p2r.util.point.points_closest_point(column_upper_pnts, roof_pnt)
closest_column_upper_pnt = column_upper_pnts[closest]

#get the planes that are at right angles to the centre line of the branch
center_ln = p2r.obj.Line.create(closest_column_upper_pnt, roof_pnt)
domain = center_ln.prop.domain()
base_plane = center_ln.eval.perp_frame(domain[0]) #((x,y,z), (p,q,r), (p,q,r), (p,q,r))
top_plane = center_ln.eval.perp_frame(domain[1])

#create the branch
base = p2r.obj.Circle.create(base_plane, 1)
top = p2r.obj.Circle.create(top_plane, 0.5)
p2r.obj.NurbsSurface.create_by_loft( (base, top) )

#delete the centre line - it is no longer needed
center_ln.modf.delete()

print "done"

Friday, September 4, 2009

New version of PyDev

There is a new version of PyDev - version 1.5

http://pydev.org/download.html

This version includes PyDev Extensions, which was previously a commercial plugin seperate from PyDev. This plugin also kept poping up a window where you have to click OK.

So now with the new version, you will not get this any more. As far as I know, nothing else has changed. So - to install it, here is what you do:


  • Uninstall PyDevExtensions, and PyDev
  1. Start Eclipse (if it is not yet open)
  2. Close all your files that are open in Eclipse
  3. In the menu, go to Help > About Eclipse SDK
  4. Click on Installation Details
  5. Click PyDev Extensions
  6. Click Uninstall
  7. When asked to restart Eclipse, click cancel
  8. Click on Installation Details
  9. Click PyDev
  10. Click Uninstall
  11. This time, accept when asked to restart Eclipse
  • Install the new version of PyDev
  1. Start Eclipse (if it is not yet open)
  2. In the menu, go to Help > Install new software
  3. Click the Add button
  4. In the Add Site dialog box, enter:
    Name: PyDev
    Location: http://pydev.org/updates
  5. Then click install...

If you can see you Navigator pane - don't panic.... In the menu, go to Window > Show View > Navigator, and then dock it on the left side

Thursday, September 3, 2009

Wavy surface


import py2rhino as p2r
import random

points = ((0,0,0),(100,0,0), (100,100,0), (0,100,0))

srf = p2r.obj.NurbsSurface.create_by_corner_pnts(points)
srf.modf.rebuild((4,4), (20,20))
points = srf.prop.pnts(True)

print points
points_rand = []
for pt in points:
z = random.randint(0,10)
rand_point = (pt[0], pt[1], z)
points_rand.append(rand_point)

srf2 = p2r.obj.NurbsSurface.create_by_control_pnt_grid((20,20), points_rand, (4,4))

Saw Tooth Roof Deformed



import py2rhino as p2r
#===============================================================================
# Parameters
#===============================================================================

sec1 = p2r.obj.NurbsSurface("7f72bdb7-ef4f-4b09-a7ba-6b9e3458a0b5")
sec2 = p2r.obj.NurbsSurface("4c136d1d-dbee-4a58-90fb-1371a480269d")
sec3 = p2r.obj.NurbsSurface("9f5599dd-c9a0-49a0-be23-8857372ed973")
sec4 = p2r.obj.NurbsSurface("ab8125c7-4524-4716-8cc1-ffb03b62e1b6")
sec5 = p2r.obj.NurbsSurface("44002bb6-e116-402b-b193-893b8e5902c9")
sec6 = p2r.obj.NurbsSurface("4514f287-4322-426f-b2d7-faf5e7de6977")

lower = p2r.obj.NurbsSurface("21399d5b-427a-43db-84a3-7f76b23d4b16")
upper = p2r.obj.NurbsSurface("19ce0362-2971-4536-a943-1d3185ac1416")

#===============================================================================
# Functions
#===============================================================================
def draw_line_between_crv(crv):
start = crv.prop.start_pnt()
end = crv.prop.end_pnt()
line = p2r.obj.Line.create(start, end)
return line
#------------------------------------------------------------------------------
def loft_curve(crv):
line = draw_line_between_crv(crv)
srf = p2r.obj.NurbsSurface.create_by_loft( (line, crv) )
return srf
#------------------------------------------------------------------------------
def generate_roofs():
num_sections = len(lower_secs)
num_segments = num_sections - 1

roofs = []
for sec_num in range(num_segments):
roof = p2r.obj.NurbsSurface.create_by_loft( (lower_secs[sec_num], upper_secs[sec_num + 1]) )
roofs.append(roof)

return roofs
#------------------------------------------------------------------------------
def generate_shade(lower_roof, upper_roof, num_v):
points_shade_edge = []
points_shade_edge_projected = []

u_domain_lower = lower_roof.prop.domain(0) #(0, 12.2)
max_u_param_of_lower = u_domain_lower[1]

u_domain_upper = upper_roof.prop.domain(0) #(0, 12.2)
max_u_param_of_upper = u_domain_upper[1]

v_domain_lower = lower_roof.prop.domain(1)
v_domain_upper = upper_roof.prop.domain(1)

v_range_lower = v_domain_lower[1] - v_domain_lower[0]
v_range_upper = v_domain_upper[1] - v_domain_upper[0]

v_lower_step = v_range_lower / num_v
v_upper_step = v_range_upper / num_v

for point_num in range(num_v + 1):

param_lower = (max_u_param_of_lower, v_lower_step * point_num)
param_upper = (0, v_upper_step * point_num)
param_upper_2 = (max_u_param_of_upper, v_upper_step * point_num)

point_lower = lower_roof.eval.evaluate(param_lower)
point_upper = upper_roof.eval.evaluate(param_upper)
point_upper_2 = upper_roof.eval.evaluate(param_upper_2)

dist_vec = p2r.util.vector.create(point_lower, point_upper)
dist = p2r.util.vector.length(dist_vec)

dir_vec = p2r.util.vector.create(point_upper, point_upper_2)
dir_vec = p2r.util.vector.unitize(dir_vec)
dir_vec = p2r.util.vector.scale(dir_vec, dist)

point_projected = p2r.util.point.add(point_upper, dir_vec)

points_shade_edge.append(point_upper)
points_shade_edge_projected.append(point_projected)

pl1 = p2r.obj.Polyline.create(points_shade_edge)
pl2 = p2r.obj.Polyline.create(points_shade_edge_projected)
p2r.obj.NurbsSurface.create_by_loft( (pl1, pl2) )

#------------------------------------------------------------------------------
def generate_window(lower_roof, upper_roof, num_v):

points_lower = []
points_upper = []

u_domain_lower = lower_roof.prop.domain(0) #(0, 12.2)
u_param_of_lower = u_domain_lower[1]

v_domain_lower = lower_roof.prop.domain(1)
v_domain_upper = upper_roof.prop.domain(1)

v_range_lower = v_domain_lower[1] - v_domain_lower[0]
v_range_upper = v_domain_upper[1] - v_domain_upper[0]

v_lower_step = v_range_lower / num_v
v_upper_step = v_range_upper / num_v

for point_num in range(num_v + 1):

param_lower = (u_param_of_lower, v_lower_step * point_num)
param_upper = (0, v_upper_step * point_num)

point_lower = lower_roof.eval.evaluate(param_lower)
points_lower.append(point_lower)

point_upper = upper_roof.eval.evaluate(param_upper)
points_upper.append(point_upper)

pl1 = p2r.obj.Polyline.create(points_lower)
pl2 = p2r.obj.Polyline.create(points_upper)
p2r.obj.NurbsSurface.create_by_loft( (pl1, pl2) )

#------------------------------------------------------------------------------
def generate_windows(roofs, num_v):

windows = []
for roof_num in range(len(roofs) - 1):
lower_roof = roofs[roof_num]
upper_roof = roofs[roof_num + 1]
generate_window(lower_roof,upper_roof, num_v)
generate_shade(lower_roof,upper_roof, num_v)

start_window = loft_curve(lower_secs[0])
end_window = loft_curve(upper_secs[-1])

windows.append(start_window)
windows.append(end_window)

return windows
#------------------------------------------------------------------------------
def generate_panels(roof, num_u, num_v):

domain_u = roof.prop.domain(0)
u_range = domain_u[1] - domain_u[0]
u_step = u_range / num_u

domain_v = roof.prop.domain(1)
v_range = domain_v[1] - domain_v[0]
v_step = v_range / num_v

for v_panel_num in range(num_v):
v_param = v_panel_num * v_step

for u_panel_num in range(num_u):

u_param = u_panel_num * u_step

#parameters
param_1 = (u_param, v_param)
param_2 = (param_1[0]+u_step, param_1[1])
param_3 = (param_1[0]+u_step, param_1[1]+v_step)
param_4 = (param_1[0] , param_1[1]+v_step)

#points
p1 = roof.eval.evaluate(param_1)
p2 = roof.eval.evaluate(param_2)
p3 = roof.eval.evaluate(param_3)
p4 = roof.eval.evaluate(param_4)

#draw a panel
p2r.obj.NurbsSurface.create_by_corner_pnts( (p1, p2, p3, p4) )
#------------------------------------------------------------------------------
def generate_all_panels(roofs, num_u, num_v):
for roof in roofs:
generate_panels(roof, num_u, num_v)
roof.modf.delete()

#===============================================================================
# Main Script
#===============================================================================
sec_list = (sec1, sec2, sec3, sec4, sec5, sec6)

lower_secs = []
for sec in sec_list:
sec_crv = lower.func.intersect_2_srfs(sec, create=True)
lower_secs.append(sec_crv[0])

upper_secs = []
for sec in sec_list:
sec_crv = upper.func.intersect_2_srfs(sec, create=True)
upper_secs.append(sec_crv[0])

roofs = generate_roofs()
windows = generate_windows(roofs, 6)

generate_all_panels(roofs, 2, 6)


print "done"

Thursday, August 27, 2009

Dividing a curve


import py2rhino as p2r

#------------------------------------------------------------------------------
#globals
points = ((0,0,0), (10,0,0), (5,5,0))
#------------------------------------------------------------------------------
elp = p2r.obj.Ellipse.create_by_3pt(points[0], points[1], points[2])

#------------------------------------------------------------------------------
#METHOD 1
domain = elp.prop.domain()
domain_range = domain[1] - domain[0]
step = domain_range / 10.0

for i in range(10):
param_t = step * i
pnt = elp.eval.evaluate(param_t)
p2r.obj.Sphere.create(pnt, 0.3)

#------------------------------------------------------------------------------
#METHOD 2
points = elp.func.divide_crv(10)

for pnt in points:
p2r.obj.Sphere.create(pnt, 0.3)

print "done"

Grid on a surface


import py2rhino as p2r

#------------------------------------------------------------------------------
#globals
building_height = 50
num_floors = 10
num_panels = 8
points = ((0,0,0), (10,0,0), (5,5,0))
#------------------------------------------------------------------------------
def get_step_size():
domain_u = srf.prop.domain(0)
domain_v = srf.prop.domain(1)

u_range = domain_u[1] - domain_u[0]
v_range = domain_v[1] - domain_v[0]

u_step = u_range / num_floors
v_step = v_range / num_panels

return u_step, v_step

def draw_panels(u_param):
for panel_num in range(num_panels):
v_param = panel_num * v_step

#get the main parameter
param_1 = (u_param, v_param)

#get 3 neighbours
param_2 = (u_param + u_step, v_param)
param_3 = (u_param + u_step, v_param + v_step)
param_4 = (u_param, v_param + v_step)

#get the 4 points by evaluating the srf
point_1 = srf.eval.evaluate(param_1)
point_2 = srf.eval.evaluate(param_2)
point_3 = srf.eval.evaluate(param_3)
point_4 = srf.eval.evaluate(param_4)

#draw the panel
p2r.obj.NurbsSurface.create_by_corner_pnts((point_1, point_2, point_3, point_4))
#------------------------------------------------------------------------------
elp = p2r.obj.Ellipse.create_by_3pt(points[0], points[1], points[2])
srf = p2r.obj.NurbsSurface.create_by_extrude_crv_straight(elp, (0,0,0), (0,0,building_height))

u_step , v_step = get_step_size()

for floor_num in range(num_floors):
u_param = floor_num * u_step

#for each floor, draw a set of facade panels
draw_panels(u_param)

print "done"

To do

trim command - there is no trim command, which is unexpected.
delete command - now found under mdfy

cylinder creation - ...
ellipse creation - ...

Using functions


import py2rhino as p2r
print p2r._version
#------------------------------------------------------------------------------
#global variables
points = ((0,0,0), (0,10,0), (10,10,0))

#------------------------------------------------------------------------------
def create_base():
return p2r.obj.Polyline.create(points)

def extrude_base(base):
start = (0,0,0)
end = (0,0,10)
return p2r.obj.NurbsSurface.create_by_extrude_crv_straight(base, start, end)

def copy_and_rotate_srf(srf):
srf_copy = srf.dupl.copy_move_by_vec((0,0,10))
srf_copy.trfm.rotate( (0,0,0), 5, (0,0,1))
return srf_copy

#------------------------------------------------------------------------------
base = create_base()
srf = extrude_base(base)

for i in range(10):
srf = copy_and_rotate_srf(srf)

Booleans with solids


import py2rhino as p2r
print p2r._version

#------------------------------------------------------------------------------
floor_height = 3

#------------------------------------------------------------------------------
def create_box(floor_num):
z1 = floor_height * floor_num
z2 = z1 + 0.5

p1 = (0,0,z1)
p2 = (30,0,z1)
p3 = (30,30,z1)
p4 = (0,30,z1)

p5 = (0,0,z2)
p6 = (30,0,z2)
p7 = (30,30,z2)
p8 = (0,30,z2)

corner_points = (p1,p2,p3,p4,p5,p6,p7,p8)
return p2r.obj.Box.create(corner_points)
#------------------------------------------------------------------------------
sph = p2r.obj.Sphere.create((15,15,15), 12)

boxes = []
for floor_num in range(10):
box = create_box(floor_num)
box = sph.func.boolean_intersection(box, False)
boxes.append(box)
sph = p2r.obj.Sphere.create((0,0,0), 5)

print sph.func.boolean_difference( box )

Deform a sphere

This one needs to be update with getting the bounding box... (coming soon)


import py2rhino as p2r
print p2r._version

p1 = (0,0,0)
p2 = (10,0,0)
p3 = (10,10,0)
p4 = (0,10,0)

p5 = (0,0,10)
p6 = (10,0,10)
p7 = (10,10,30)
p8 = (0,10,30)

corner_points = (p1,p2,p3,p4,p5,p6,p7,p8)

box = p2r.obj.Box.create(corner_points)

sph = p2r.obj.Sphere.create((5,5,5), 5)

sph.defm.box_morph(corner_points, True)

Deforming a surface


import py2rhino as p2r

srf = p2r.obj.NurbsSurface("98599908-7024-4864-b9e9-a5cf20cd5804")
control_points = srf.prop.pnts()

cir = p2r.obj.Circle("25309c07-d705-4041-bd87-aba1389d79e6")
cen = cir.prop.center_pnt()

new_control_points = []
for cp in control_points:

#get the distance from the circle to the control point
vec = p2r.util.vector.create(cen, cp)
dist = p2r.util.vector.length(vec)

#move the control point and save it in the list
moved_point = (cp[0], cp[1], dist)
new_control_points.append(moved_point)

#print the points
for i in new_control_points:
print i

#create a new surface
dim = srf.prop.pnt_count()
p2r.obj.NurbsSurface.create_by_control_pnt_grid(dim, new_control_points, (1,5))

Deforming a polyline


import py2rhino as p2r

p1 = (0,10,0)
p2 = (-10,0,0)
p3 = (10,0,0)
p4 = (0,-10,0)

v1 = (-2,0,2)
v2 = (0,0,2)
v3 = (0,0,2)
v4 = (2,0,2)

lines = []

for counter in range(10):

line = p2r.obj.Polyline.create( (p1,p2,p3,p4) )
lines.append(line)

#p1 = (p1[0] + v1[0], p1[1] + v1[1], p1[2] + v1[2])
p1 = p2r.util.vector.add(p1, v1)
p2 = p2r.util.vector.add(p2, v2)
p3 = p2r.util.vector.add(p3, v3)
p4 = p2r.util.vector.add(p4, v4)

#now do something with your lines

print "done"

Monday, August 24, 2009

Ecotect + Rhino

Here is an example of scripting between rhino and Ecotect.


import py2rhino as p2r
import py2ecotect as p2e
print p2r._version
import math

#===============================================================================
# Parameters
#===============================================================================
crv_centre = p2r.obj.NurbsCurve("54cd3b4b-40d0-4602-8774-75412d2e1c20")
crv_radius = p2r.obj.NurbsCurve("a822cc3e-67fb-4df3-b7bc-3c40080302a9")
floor_height = 5
facade_inset = 5
wall_panels = 5
#===============================================================================
# Functions
#===============================================================================
def mid_point(point_1, point_2):
vector = p2r.util.vector.create(point_2, point_1)
half_vector = p2r.util.vector.divide(vector, 2.0)
return p2r.util.vector.add(point_1, half_vector)


#===============================================================================
# Main script
#===============================================================================
#get the start and end point of the centre curve
crv_s = crv_centre.prop.start_pnt()
crv_e = crv_centre.prop.end_pnt()

#sub divide the curves
points_centre = crv_centre.func.contour_pnts((0,0,crv_s[2]), (0,0,crv_e[2]), floor_height)
points_radius = crv_radius.func.contour_pnts((0,0,crv_s[2]), (0,0,crv_e[2]), floor_height)

#draw circles and save them in the list
circles = []
for point_counter in range(len(points_centre)):

#get the points out of the list
centre_pnt = points_centre[point_counter]
radius_pnt = points_radius[point_counter]

#create the circle
cir = p2r.obj.Circle.create((centre_pnt), radius_pnt[0])
circles.append(cir)

#draw a surface for each floor
centre_points = []
panels = []
corners = []
for cir_num in range(len(circles) - 1):

#get this circle and the next circle
cir1 = circles[cir_num]
cir2 = circles[cir_num + 1]

#add the center point to the data list
centre_points.append( ( cir1.prop.center_pnt(), cir2.prop.center_pnt() ) )

#split the circles into points
points1 = list(cir1.func.divide_crv(wall_panels))
points2 = list(cir2.func.divide_crv(wall_panels))

#add one more to the end
points1.append(points1[0])
points2.append(points2[0])

#my lists for storing data
panel_data = []
corner_data = []

for wall_panel_counter in range(wall_panels):

#create the points of the vertical facade lines
line1_pt1 = points1[wall_panel_counter]
line1_pt2 = points2[wall_panel_counter]
line2_pt1 = points1[wall_panel_counter + 1]
line2_pt2 = points2[wall_panel_counter + 1]

#draw in ecotect
points = (line1_pt1, line1_pt2, line2_pt2, line2_pt1)
panel = p2e.obj.Wall.create(points)

#add panels to the data list
panel_data.append(panel)

#add corner points to the data list
corner_data.append( (line1_pt1, line1_pt2, line2_pt2, line2_pt1) )

#deal with the circular problem
panel_data.append(panel_data[0])
corner_data.append(corner_data[0])

panels.append(panel_data)
corners.append(corner_data)

"""
for i in corners:
print "next"
for j in i:
print "\t", j"""

raw_input("Continue:")


#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# DO THE SIMULATION
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


#get the maximum and minimum insolation value
max_insolation = panels[0][0].prop.attr_1
min_insolation = panels[0][0].prop.attr_1
for floor in panels:
for panel in floor:
insolation = panel.prop.attr_1
if insolation > max_insolation:
max_insolation = insolation
if insolation < min_insolation:
min_insolation = insolation
insolation_range = max_insolation - min_insolation

#now create the rhino geometry
num_floors = len(centre_points)
for floor_num in range(num_floors):

#get centre points
cir1_center_pnt = centre_points[floor_num][0]
cir2_center_pnt = centre_points[floor_num][1]
center_pnt = mid_point(cir1_center_pnt, cir2_center_pnt)

p2r.obj.Sphere.create(center_pnt, 0.2)

facade_sections = []

#for each panel
for panel_num in range(wall_panels):

this_panel = panels[floor_num][panel_num]
next_panel = panels[floor_num][panel_num + 1]
this_corners = corners[floor_num][panel_num]

#get the two points between these panels
pnt2 = this_corners[2]
pnt1 = this_corners[3]
mid_pnt = mid_point(pnt1, pnt2)

#get the vector to move points
vec = p2r.util.vector.create(center_pnt, mid_pnt)
vec = p2r.util.vector.unitize(vec)

#get the vector scale factor based on insolation levels
insolation_1 = this_panel.prop.attr_1
insolation_2 = next_panel.prop.attr_1
av_insolation = (insolation_1 + insolation_2) /2.0
scale_factor = ((av_insolation - min_insolation) / insolation_range) * facade_inset

#scale the vector
vec = p2r.util.vector.scale(vec, scale_factor)

#move the mid point
moved_pnt = p2r.util.vector.add(mid_pnt, vec)

#create a section through the facade
pline= p2r.obj.Polyline.create((pnt1, moved_pnt, pnt2))
facade_sections.append(pline)

#join up all the facade sections with a loft
p2r.obj.NurbsSurface.create_by_loft(facade_sections, closed = True)

print "done"

Thursday, August 20, 2009

Liyi Tower

Here is the twisted tower example:

import py2rhino as p2r
print p2r._version

#===============================================================================
# Parameters
#===============================================================================
pline = p2r.obj.Polyline("8132d141-be8d-4abe-84a3-1dbf37bd0960")
rot_cir = p2r.obj.Circle("b85bb2ea-6790-45a1-b16d-3604bc579637")
floor_height = 4
rot_angle = 1
num_floors = 50
#===============================================================================
# Main script
#===============================================================================

#create the surface and cap it
srf = p2r.obj.NurbsSurface.create_by_extrude_crv_straight(pline, (0,0,0), (0,0,floor_height))
srf.func.cap_planar_holes()

#get the centre of rotation
rot_pnt = rot_cir.prop.center_pnt()

#create floors
for floor in range(num_floors):

#copy
srf2 = srf.dupl.copy_move_by_vec((0,0,floor_height * floor))

#rotate
srf2.trfm.rotate(rot_pnt, rot_angle * floor, (0,0,1), False)

Cheng Hui Tower

Here is the tower example:



import py2rhino as p2r
print p2r._version

#===============================================================================
# Parameters
#===============================================================================
crv_centre = p2r.obj.NurbsCurve("e48e4900-47fa-44f4-b84a-4d7089520e09")
crv_radius = p2r.obj.NurbsCurve("0d528bea-63a5-4213-a669-2a0ba4cb2d9c")
floor_height = 4
facade_curve = 3
wall_panels = 9
#===============================================================================
# Main script
#===============================================================================
#get the start and end point of the centre curve
crv_s = crv_centre.prop.start_pnt()
crv_e = crv_centre.prop.end_pnt()

#sub divide the curves
points_centre = crv_centre.func.contour_pnts((0,0,crv_s[2]), (0,0,crv_e[2]), floor_height)
points_radius = crv_radius.func.contour_pnts((0,0,crv_s[2]), (0,0,crv_e[2]), floor_height)

#draw circles and save them in the list
circles = []
for point_counter in range(len(points_centre)):

#get the points out of the list
centre_pnt = points_centre[point_counter]
radius_pnt = points_radius[point_counter]

#create the circle
cir = p2r.obj.Circle.create((centre_pnt), radius_pnt[0])
circles.append(cir)

#draw a surface for each floor
surfaces = []
for cir_num in range(len(circles) - 1):

#get this circle and the next circle
cir1 = circles[cir_num]
cir2 = circles[cir_num + 1]

#create the average of the two centre points
cen_pnt1 = cir1.prop.center_pnt()
cen_pnt2 = cir2.prop.center_pnt()
cen_pnt_mid = ( (cen_pnt1[0] + cen_pnt2[0]) / 2.0, (cen_pnt1[1] + cen_pnt2[1]) / 2.0, (cen_pnt1[2] + cen_pnt2[2]) / 2.0 )

#get the average of the twp radii, and subtract the offset
rad_pnt1 = cir1.prop.radius()
rad_pnt2 = cir1.prop.radius()
rad_pnt_mid = ((rad_pnt1 + rad_pnt2) / 2.0) - facade_curve

#create the circle
cir_mid = p2r.obj.Circle.create((cen_pnt_mid), rad_pnt_mid)

#split the circles into points
points1 = list(cir1.func.divide_crv(wall_panels))
points2 = list(cir2.func.divide_crv(wall_panels))
points_mid = list(cir_mid.func.divide_crv(wall_panels))

#add one ore to the end
points1.append(points1[0])
points2.append(points2[0])
points_mid.append(points_mid[0])

for wall_panel_counter in range(wall_panels):

#create the first polyline
pt1 = points1[wall_panel_counter]
pt2 = points2[wall_panel_counter]
pt_mid = points_mid[wall_panel_counter]
pline1 = p2r.obj.Polyline.create((pt1, pt_mid, pt2))

#create the second polyline
pt1 = points1[wall_panel_counter + 1]
pt2 = points2[wall_panel_counter + 1]
pt_mid = points_mid[wall_panel_counter + 1]
pline2 = p2r.obj.Polyline.create((pt1, pt_mid, pt2 ))

#loft the two circles
srf = p2r.obj.NurbsSurface.create_by_loft((pline1, pline2))
surfaces.append(srf)

print "done"

py2rhino - modules

The graphic objects, as well as some other entities have been defined as classes since this works well for them. However, for some other functions, defining everything in a class would be over the top. So, in these cases, the functions are simply placed inside some python modules.

For example, in py2rhino there is a package called util. This contains a module named 'vector' and 'point', both of which contain a useful set of functions to work with vectors and points. For example you can get the cross product of two vectors like this

import py2rhino as p2r
v1 = (3,8,1)
v2 = (4,4,9)
v3 = p2r.util.vector.cross_product(v1, v2)

In order to avoid having to do so much typing, I tend to import these modules separately, like this:

import py2rhino.util.vector as v
v1 = (3,8,1)
v2 = (4,4,9)
v3 = v.cross_product(v1, v2)

Some other useful functions in the vector module are unitize (which forces the vector to be 1 unit long), and scale (which scales the vector by a certain amount).

py2rhino - object methods

Once you have create an instance of a graphic object, you can then start to do stuff with this object using the methods belonging to those objects. Since the list of things that you can do is so long, the the methods have been grouped into sub-namespaces. The main object namespaces are as follows:
  • dupl: duplicate - make copies of an object or parts of an object
  • modf: modify - change the object in some way
  • eval: evaluate - evaluate an object for a given parameter
  • trfm: transform - transform an object, for example move or rotate
  • defm: deform - deform an object, for example shear
  • test: test - perform various test on an object
  • prop: properties - get or set object properties
  • stat: state - the state of the object, for example locking and hiding object
  • func: function - various other functions that can be performed with the object
To create some examples, lets first create a curve:
import py2rhino as p2r
points = ((0,-10,0), (0,0,0),(2,0,0),(3,4,0),(7,9,0))
crv = p2r.obj.NurbsCurve.create_by_pnts(points, degree=3)

Here is a duplication example - rebuilding the curve. Note that the method returns a boolean indicating if it worked or not.
success = crv.dupl.rebuild(4, 20)

Here is a transformation example- moving the curve. Note that the method returns a boolean indicating if it worked or not.
success = crv.trfm.move((0,0,0),(0,0,20))

Here is an evaluation example - getting the tanget at a particular parameter along the curve:
tangent = crv.eval.tangent(0.5)

Here is a deformation example - shearing the object.
tangent = crv.defm.shear((0,0,0), (0,10,0), 45, True)

Here is a test example - testing is a curve is planar - i.e. lies on a plane. All these methods return a boolean value.
tangent = crv.test.is_planar()

Here is a properties example - getting the start point of the curve.
start_point = crv.prop.start_pnt()

Here is a state example - locking the curve.
success = crv.stat.lock(30)

Here is a functions example - dividing a curve to create a series of point along the curve.
points = crv.func.divide_crv(30)

py2rhino - object creation

The py2rhino connection is a set of classes that allow you to control Rhino from Python through a programming interface called COM. The functions that we can execute via this interface are the same as the Rhinoscript functions.

When scripting with py2rhino, the first thing you need to do is to import it, like this:

import py2rhino as p2r

Note that the p2r bit is optional - this just means that 'p2r' is an alias for 'py2rhino', which saves a lot of typing. You can check which version of py2rhino you have installed as follows:

print "py2rhino version number: ", p2r._version

The py2rhino library has been created using an object-orientated approach. This means that instead of simply having a very long list of functions, in py2rhino you also have classes, and each class has a set of functions (called methods) associated with it.

All the graphic objects that you can create in Rhino, like curves, surfaces, and meshes, are represented by classes. The graphic object classes are all found in the 'p2r.obj' namespace. If you want to draw a line, you need to create an instance of the Line class. This is done by calling one of the class's factory methods whose names start with 'create'. For example, here is how to create a line:

start_point = (0,0,0)
end_point = (20,10,0)
line = p2r.obj.Line.create(start_point, end_point)

The list of graphic object classes includes the following:
  • Curves: NurbsCurve, Line, Polyline, Circle, Arc, Ellipse, EllipticalArc, PolyCurve
  • Surfaces: NurbsSurface, PlaneSurface, Cone, Cylinder, Sphere, Torus, PolySurface
  • Meshes:Mesh, PlanarMesh
Each of these classes will have a set of 'create' factory methods that can be used for instantiating the class. In addition, you can also create a instance by using the default constructor and passing in the rhino id. This can be done line this:

line = p2r.obj.Line("3a357c2a-652c-4826-9938-eb3bb5b7dc62")

Of course, the id that you pass in must be for an object that really exists in Rhino, and also that it really is a line. In Rhino, if you want to get the id of an object, click the 'Details' button in Object Properties.

Here are some more examples of creating various graphic objects:

points = ((0,-10,0), (0,0,0),(2,0,0),(3,4,0),(7,9,0))
pl = p2r.obj.Polyline.create(points)
crv = p2r.obj.NurbsCurve.create_by_pnts(points, degree=3)
srf = p2r.obj.NurbsSurface.create_by_extrude_crv_straight(crv, (0,0,0), (0,0,10))

When you select a particular create command in the code editor, there will be a help window that will pop up with some documentation. Read this carefully in order to understand what the required and optional parameters are.

Monday, August 17, 2009

The worm

Here is an example of the worm script - it creates a worm like lofted surface from two curves. One curve specifies the centre line, and the other curve specifies the radius of arcs generated for lofting.

This works with version 0.1.4 of py2rhino.

Remember that you will need to insert your own curve ids into the script.



import py2rhino as p2r
import py2rhino.util.vector as v
#===============================================================================
# Parameters
#===============================================================================
#global variables
total_arcs = 15

#these ids will change - get the from your rhino file
radius_crv_id = "0b20eb2b-3229-4c54-97d3-c90c81f73f83"
center_crv_id = "b19973bf-9dc9-42f5-9e8d-b530e51cff23"

#===============================================================================
# Functions
#===============================================================================
#function to draw an arc
def draw_arc(crv, arc_num, step, radius):

#get the parameter
parameter = arc_num * step

#get the xyz point and the tangent vector at this parameter
origin = crv.eval.evaluate(parameter)
t = crv.eval.tangent(parameter)

#get the vector at right angles to the tangent and the z axis
y_axis = v.cross_product(t, (0,0,1))

#create three point using vector geometry
pt1 = v.add(origin, v.scale(y_axis, radius))
pt2 = v.add(origin, v.scale(v.reverse(y_axis), radius))
pt3 = v.add(origin, (0,0,radius))

#create the arc
arc = p2r.obj.Arc.create_by_3pt(pt1, pt2, pt3)

#return the arc
return arc
#------------------------------------------------------------------------------
#function to get the radius
def get_radius(crv, arc_num, step):

#get the parameter
parameter = arc_num * step

#get the xyz point at this parameter
pt = crv.eval.evaluate(parameter)

#draw a sphere just to see where this point is (optional)
p2r.obj.Sphere.create(pt, 0.3)

#return the y value of the point - this will be the height
return pt[1]
#------------------------------------------------------------------------------
#function to get the step size
def get_step_size(crv):

#get the step size by dividing the domain by the total_arcs
d = crv.prop.domain()
interval = d[1] - d[0]

#divide the domain interval by the total number of steps
#the number of steps is one less than the number of arcs
step = interval/(total_arcs - 1)

#return the size of the step
return step
#===============================================================================
# The main Script
#===============================================================================

#create two NurbsCurve objects
radius_crv = p2r.obj.NurbsCurve(radius_crv_id)
center_crv = p2r.obj.NurbsCurve(center_crv_id)


#get the step size of each curve
radius_step = get_step_size(radius_crv)
centre_step = get_step_size(center_crv)


#create an empty list to store the arcs that will be created
arcs = []

#loop to create arcs
arc_num = 0
for i in range(total_arcs):
radius = get_radius(radius_crv, radius_step, total_arcs)
arc = draw_arc(center_crv, arc_num, centre_step, radius)
arcs.append(arc)
arc_num = arc_num + 1

#create the surface by lofting the arcs
srf = p2r.obj.NurbsSurface.create_by_loft(arcs)


print "done"

Wednesday, August 5, 2009

Python documentation

There is lots of online help for Python.

The standard Python docs are a good place to start. Although we are using version 2.5, we will use the documentation for 2.6. The 2.6 docs are formatted in a better way, so it makes our life easier:
As well as this, there are also lots more Python tutorials for complete beginners. Here is a list of the most popular ones:

Getting started with Python

This week we are going to start our Python scripting. We are kicking of with a three day workshop. Today is focusing on the basics of Python scripting. I will go through all the steps below to set up your computer, since many of you will need to do this at home or on your laptop.

The first thing we will need to do is to get Python installed! (This is already installed in the labs.)
  • To install Python 2.5 (not Python 2.6 or 3.0):
  1. Download msi file
  2. Double click the file that you downloaded.
Then we need to install an IDE, which is an Integrated Development Environment. We will be using Eclipse. (This is already installed in the labs.)
  • To install Eclipse Galileo:
  1. Download zip file
  2. Unzip the file anywhere on your hard disk - e.g. in C: drive
  3. To start Eclipse, double click the eclipse.exe file
The we will need to install a couple of plugins so that Eclipse can be used for writing Python scripts. Installing plugins in Eclipse is very easy -
  • To install PyDev
  1. Start Eclipse
  2. In the menu, go to Help > Install new software
  3. Click the Add button
  4. In the Add Site dialog box, enter:
    Name: PyDev
    Location: http://pydev.sourceforge.net/updates/
  5. Then click install...
  • To install PyDev Extensions
  1. Start eclipse
  2. In the menu, go to Help > Install new software
  3. Click the Add button
  4. In the Add Site dialog box, enter:
    Name: PyDev
    Location: http://www.fabioz.com/pydev/updates
  5. Then click install...
Now we will now need to configure a new Eclipse workspace and project. First you need to tell Eclipse where Python is installed. Then you need to create a new project. And finally, you need to tell Python where the new project is by adding it to the PYTHONPATH.
  • To tell Eclipse where Python is:
  1. Start eclipse
  2. In the menu, go to Window > Preferences
  3. In the preferences dialog box, navigate to 'Pydev > Interpreter - Python'
  4. Click the 'New' button
  5. In the select Interpreter dialog box:
    Interpreter Name: Python25
    Interpreter Executable: click 'Browse' and navigate to find Python.exe
    C:\Python25\python.exe
  6. Click 'OK' and 'OK' again
  • To create a new project
  1. Start eclipse
  2. In the menu, go to File > New > Pydev Project
  3. In the dialog box:
    Project name: type any name
    Use default: tick to use your default workspace
    Choose Project: Python
    Grammar Version: 2.5
    Interpreter: Python25
    Create default 'src'...": untick this - it is not needed
  4. Click Next and Finish
  • To tell the Python where your project is:
  1. In the Pydev Package Explorer, find your project folder
  2. Right click the folder and select 'Properties'
  3. Click on 'PyDev - PYTHONPATH'
  4. Click the 'Add source folder' button
  5. Click 'OK'
Now the project should all be set up and ready to start coding. First test that everything is working by writing a little 'Hello World' script. Then, in order to communicate with Windows, you will need to install pywin32. And finally, you will need to set up py2rhino and py2ecotect.
  • To test that everything is working:
  1. In the Pydev Package Explorer, find your project folder
  2. right click the folder and select 'New > Pydev Module'
  3. In the dialog box:
    Source Folder: leave it as it is
    Package: leave it as it is, or add a name if you want a sub folder
    Name: enter the name of your script, ege. 'test'
  4. Click 'Finish'
  5. In the script file, type ' print "Hello World" '
  6. Click the run button (or go to Run > Run)
  7. Check that you see 'Hello World' in the output console
  • To install pywin32 (can only be installed on windows):
  1. Download the pywin32 file fpr Python25:
    http://sourceforge.net/projects/pywin32/files/pywin32/Build%20214/pywin32-214.win32-py2.5.exe/download
  2. Run the exe by double clicking the file
  • To install py2rhino and py2ecotect:
  1. Go to the download page for design-automation:
    http://code.google.com/p/design-automation/downloads/list
  2. Download the latest version of py2rhino orpy2ecotect
  3. In Windows explorer, navigate to the Python sitpackages folder
    C:\Python25\Lib\site-packages
  4. Delete any older versions by deleting the py2rhino or py2ecotect folders
  5. Then unzip the py2rhino or py2ecotect into the site-packages folder
  6. Now go back to eclipse and refresh eclipse as follows:
    In the menu, go to Window > Preferences
    In the preferences dialog box, go to PyDev > Interpreter - Python
    Click the 'Apply' button
  • Now test that you can draw something in Rhino
  1. Start Rhino
  2. Create a script file in eclipse
  3. Type:

    import py2rhino as p2r
    p2r.obj.Line.create( (0,0,0), (5,5,0) )
    print "done"

  4. Click the run button (or go to Run > Run)
  5. Check that you see a line in Rhino and the 'done' is printed to the output console