Those of you that know me IRL, are aware that I work as a civil engineer in Canada -- typically on municipal type projects for sewers, pump stations, water treatment, and roadway design. That sort of stuff. Either on the ground, or under it (generally).
I've found myself recently working very very iteraviely on some concept designs in AutoDesks Civil3D (very powerful, modern day engineering tool) -- but it felt tedious, and clunky, and involved a fair amount of going back and forth w/ spreadsheets to review / tweak / confirm designs.
Currently, I'm very much in the beginning phases of understanding Blenders python API that they've put together to allow devs / designers to create / manipulate objects -- and I gotta say that I'm pretty impressed with the quality of documentation that they've put together. Something that I could learn from for my own projects and scripting adventures -- same with @exhaust.
Here's the gist of my simple script that I hacked together as a bit of a proof of concept:
from mathutils import *
from math import *
import bpy
D = bpy.data
# Find Site Surface
meshes = D.meshes
mesh = 0
for object in meshes:
if object.name == "Surface":
mesh = object
# Find Lots
lots = []
ROWs = []
for poly in mesh.polygons:
if poly.material_index == 1:
lots.append(poly)
else:
ROWs.append(poly)
print("Found a bunch of lots!")
# print(lots)
# Find lot edges shared w/ ROWs
row_verts = []
for face in ROWs:
for vertex in face.vertices:
if vertex not in row_verts:
row_verts.append(vertex)
print(row_verts)
for lot in lots:
print("Checking Lot No. " + str(lot.index))
counter = 0
row_pts = []
lot_verts = list(lot.vertices)
set_lot = set(lot_verts)
set_row = set(row_verts)
lot_ROW_pts = list((set_row-(set_row-set_lot)))
print(lot_ROW_pts)
if len(lot_ROW_pts) == 2:
frontage_vector = (mesh.vertices[lot_ROW_pts[0]].co - mesh.vertices[lot_ROW_pts[1]].co)
print("Frontage Vector:", frontage_vector)
frontage_vector = frontage_vector.normalized()
print("IP#1:",mesh.vertices[lot_ROW_pts[0]].co)
print("IP#2:",mesh.vertices[lot_ROW_pts[1]].co)
print("Frontage Unit Vector:", frontage_vector)
san_service_location = Vector(
(
(mesh.vertices[lot_ROW_pts[0]].co.x+mesh.vertices[lot_ROW_pts[1]].co.x)/2,
(mesh.vertices[lot_ROW_pts[0]].co.y+mesh.vertices[lot_ROW_pts[1]].co.y)/2,
(mesh.vertices[lot_ROW_pts[0]].co.z+mesh.vertices[lot_ROW_pts[1]].co.z)/2
)
)
print("SAN:",san_service_location)
wat_service_location = san_service_location+frontage_vector*2
print("WAT:",wat_service_location)
bpy.ops.mesh.primitive_cube_add(location=san_service_location)
bpy.context.active_object.name = "SAN Service"
bpy.ops.mesh.primitive_ico_sphere_add(location=wat_service_location)
bpy.context.active_object.name = "WAT Service"
My "proof of concept" script above, generally works as follows:
material_index == 1), it adds it to the list of private properties;material_index == 0), it adds it to the list of public ROWs;Vector() object.
| MMCD S6 | MMCD W2b |
|---|---|
| Standard detail used in BC depicting how sanitary service connections should be constructed | Standard detail used in BC depicting how water service connections should be constructed |
I'm pretty fired up that this seems to work nicely, and quickly.
Next few steps should be:
Long term goals:
Any other civil engineers out here on the chain? I'd be keen to hear your thoughts on what you think may be an effective way to automate 'preliminary design' tasks like this. I think it'll be VERY complicated to script something for detailed design -- but for the early stage "options discovery", I think there's quite a bit of potential.
And yes -- I know that the python code above is like very inefficient. If you've got some cool suggestions for how to improve, I'd be keen to hear it!