Speculative Restoration Design Tool

Elizabeth Singer
Design Intelligence Course
5 min readDec 21, 2023

by Elizabeth Singer and Jon Marcos

Above: Image of Generated Grasshopper Product around Context

Tool Introduction:

Understanding the foundational elements of a building’s structure and design features like window jambs, sills, and heads can provide valuable insights into speculating what the facades might have looked like. In the case of the Detroit Restoration, we chose to use these found geometries to generate or reconstruct buildings, especially in architectural styles like Art Nouveau and Beaux-Arts.

Above: 3-D Chunk of Window Profile

The goal of the Speculative Restoration App is to leverage found geometries, such as window profiles and fundamental window components like jambs, sills, and heads, and orient them on a facade. The key elements of this process involve obtaining the site boundary, extruding it, subdividing the geometry, introducing variance, and enabling this variation to dynamically influence the placement of these geometries on individual windows.

Motivation:

We began with the aim of envisioning the appearance of buildings in Detroit’s restoration process by leveraging discovered architectural data from these structures. By taking found window jambs, sills, and heads, users can speculate on the likely facades. Analyzing these geometries allowed us to hypothesize the building’s original appearance by examining their subdivisions and profiles. Our initial focus was particularly on Art Nouveau and Beaux-Arts styles, allowing for the recreation of facades with intricate details and grandeur characteristic of these architectural movements.

Use Cases:

We discovered that this approach isn’t only useful for restoration; it’s also a useful tool for world-building. Using these curve profiles facilitates the rapid generation of diverse buildings, ideal for CGI rendering. For instance, with a set of 100 curve profiles, we can swiftly create numerous building designs simply by inputting these profiles.

Process: Methodologies using Rhino, Grasshopper, and Python

A. We identified the site boundaries: Based on the rough sketch of the site boundary, the tool will round the site boundary off to the nearest whole quotient. For example, a side that is 99 feet that is getting subdivided into increments of 12 feet will be rounded to 96 feet.

import math

# Input your number here
input_number = InputNum

# Round to the nearest ten
rounded_number = round(input_number, -1)

# Output the result
print(rounded_number)

B. We generated the base geometry from the given building footprints and desired heights: The height of the building is determined by the user. For example, if a user wants a 300-foot building, that is possible.

Above: UI plugging in the values desired by the user. Besides height, other parameters are adjustable, such as panel width, panel height, subdivision width, etc.

C. These facades are subdivided based on user input such as horizontal distance between windows. Optionally, users also have the option to have alternating distances between windows, which Python allows for this alternating variance.

import Rhino.Geometry as rg
import scriptcontext as sc
import Grasshopper as gh

input_surfaces = Walls

# Define the size of each panel in feet
panel_size_u = ph # in feet
panel_size_v = pw # in feet

# Get the U and V domain of the input surface
u_domain = input_surfaces.Domain(0)
v_domain = input_surfaces.Domain(1)

# Calculate the number of divisions in the U and V directions
u_count = int((u_domain[1] - u_domain[0]) / panel_size_u)
v_count = int((v_domain[1] - v_domain[0]) / panel_size_v)

# Calculate the U and V step sizes based on the number of divisions
u_step = (u_domain[1] - u_domain[0]) / u_count
v_step = (v_domain[1] - v_domain[0]) / v_count

# Create a list to store the resulting panels
panels = []

# Iterate through the U and V directions and create panels
for i in range(u_count):
# Calculate the parameters for the sub-surface
u_start = u_domain[0] + i * u_step
u_end = u_start + u_step
# Ensure that u_end does not exceed the surface domain
u_end = min(u_end, u_domain[1])

for j in range(v_count):
# Calculate the parameters for the sub-surface
v_start = v_domain[0] + j * v_step
v_end = v_start + v_step
# Ensure that v_end does not exceed the surface domain
v_end = min(v_end, v_domain[1])

# Extract a sub-surface for each panel
panel_surface = input_surfaces.Trim(rg.Interval(u_start, u_end), rg.Interval(v_start, v_end))

# Add the panel surface to the panels list
panels.append(panel_surface)

# Create a DataTree to store the panels
panel_tree = gh.DataTree[object]()

# Add panels to the DataTree
for i, panel in enumerate(panels):
path = gh.Kernel.Data.GH_Path(i)
panel_tree.Add(panel, path)

# Output the panels in Grasshopper
a = panel_tree

D. We fixed the UVs of the surface using Rhino Common and UV Unify, as Grasshopper has the possibility of mixing those up in the surface generation process. This is essential in making sure all windows orient in the same fashion.

import Rhino.Geometry as rg

# Input Surfaces
input_surfaces = surfacestofix

# Unify UV Coordinates
unified_surfaces = []
for surface in input_surfaces:
# Extract the underlying NURBS surface
nurbs_surface = surface.ToNurbsSurface()

# Check if the surface is valid
if nurbs_surface:
try:
# Unify UV Coordinates
nurbs_surface.SetDomain(0, rg.Interval(0, 1))
nurbs_surface.SetDomain(1, rg.Interval(0, 1))
unified_surface = rg.Brep.CreateFromSurface(nurbs_surface)

# Check if the unified surface is valid
if unified_surface:
unified_surfaces.append(unified_surface)
else:
print("Error: Unable to create unified surface for input surface {}".format(surface))
except Exception as ex:
print("Error: {}".format(ex))
else:
print("Error: Unable to extract NURBS surface from input surface {}".format(surface))

# Output
output_surfaces = unified_surfaces

E. Using a series of boolean toggles, users have the option to alternate between uniform and alternating subdivisions. This allows wanted data streams to be turned on, and vice versa.

Above: Stream gate showing which components keep running, and vice versa

F. We continued by orienting found window shape profiles onto the facade subdivisions.

Above: annotations describing how the sill generation works. Similar methodologies are used in generating the jambs and heads.

G. Using Grasshopper Geometry Preview, the generative product can be visible to the user.

H. Using the Grasshopper UI+ plugin, users can skip fiddling with the back end of the script by conveniently changing the parameters in a clearer interface.

Before and after: Backend script versus simplified UI for more convenient use.

--

--