# Moving Roof

#### Keywords

variable geometry truss(VGT); kinetic architecture; adaptive structure; structural design competition

Hayashi Kazuki

#### Location

Daiba park, Tokyo, Japan

29000㎡

2018.6-2018.8

#### Description

What will you behave if you are under a roof which moves up and down corresponding to the people underneath? We will not only stare at the movement, but also interact with the roof by capturing, sprawling, chasing, or inflating. In that sense, I believe this architecture has the potential to stimulate our hidden sensitivity. I propose the structure using adaptive trusses with actuators, called Variable Geometry Truss(VGT).

#### Mathematical approaches

To achieve this structure, we need the following information:

1. What is the target surface?
2. Are the axial forces within the range that the actuator can handle?

##### [1. What is the target surface?]

I introduced a tensor product Bezier surface to define the target surface, whose control points go upwards and downwards depending on the number of people around each point. Usually, Bezier surface is defined as a function of parameters $$u$$ and $$v$$ as
$${\bf p}(u,v)=\sum_{i=0}^{m}{\sum_{j=0}^{n}{{B_{i}^{m}(u)B_{j}^{n}(v){\bar{\bf p}}_{ij}}}}$$

Under condition that the control points are evenly spaced and their locations are all within the range of [0,1] in x and y direction, the height of any arbitrary point on the surface can be derived as a explicit function of its $$x$$ and $$y$$;
$$z(x,y)=\sum_{i=0}^{m}{\sum_{j=0}^{n}{{B_{i}^{m}(x)B_{j}^{n}(y){\bar z}_{ij}}}}$$

$${\bar z}_{ij}$$ is set corresponding to the number of people around the fixed coordinates ($${\bar x}_{ij}$$, $${\bar y}_{ij}$$) of control point $${\bar{\bf p}}_{ij}$$, obtained with a monitoring system.

To visualize the sequence of the target surface movement, the Grasshopper component “RoofWave” is developed within the framework of Grasshopper Software Development Kit(SDK). The original source codes are also provided below.

using System;
using System.Collections.Generic;
using System.Linq;
using Grasshopper.Kernel;
using Rhino.Geometry;

namespace VariableGeometryTruss
{
public enum component_mode { Initialize, Exploit };
public class VariableGeometryTrussComponent : GH_Component
{
List<Point3d> pt = null;
Line[] mb = null;
int nk = 0;
int nm = 0;
int[] istart = null;
int[] iend = null;
List<int> free = new List<int>();
List<int> fix = new List<int>();
double[] L_original = null;
int nfree = 0;
string directory = Grasshopper.Folders.DefaultAssemblyFolder + @"\VGT";
public component_mode mode = component_mode.Initialize;
bezier B = new bezier();
int nsrf = 0;
double[] srf_xmax = null;
double[] srf_xmin = null;
double[] srf_ymax = null;
double[] srf_ymin = null;
double[] srf_xrange = null;
double[] srf_yrange = null;
List<Surface> input_srf = null;
List<Point3d[,]> c_p = null;
List<Point3d[,]> scaled_c_p = new List<Point3d[,]>();
const double e = 1.0e-5;

public VariableGeometryTrussComponent()
: base("VariableGeometryTruss", "VGT",
"Variable geometry truss(VGT) controller for a single layer latticed shell.",
"Extra", "Kazuki")
{
}

/// <summary>
/// Create an attribute to setup optimization triggered by double-clicking on the icon.
/// </summary>
public override void CreateAttributes()
{
base.m_attributes = new CustomComponentAttributes(this);
}

/// <summary>
/// Add mode display below the component.
/// </summary>
{
switch (mode)
{
case component_mode.Initialize:
Message = "Initialize";
break;

case component_mode.Exploit:
Message = "Exploit";
break;
default:
break;
}
}

/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
{
pManager.AddPointParameter("Point","P","Nodal locations of truss with initial position.",GH_ParamAccess.list);
pManager.AddIntegerParameter("Fix", "F", "Point indices to fix z coordinates", GH_ParamAccess.list);
pManager.AddSurfaceParameter("Target","T","Target surface that VGT aims at.",GH_ParamAccess.list);
}

/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
{
pManager.AddPointParameter("Point", "P", "Nodal locations of truss.", GH_ParamAccess.list);
pManager.AddLineParameter("Member", "M", "Extensible bar elements with actuator.", GH_ParamAccess.list);
}

/// <summary>
/// This is the method that actually does the work.
/// </summary>
/// <param name="DA">The DA object can be used to retrieve data from input parameters and
/// to store data in output parameters.</param>
protected override void SolveInstance(IGH_DataAccess DA)
{
if(mode == component_mode.Initialize)
{
pt = new List<Point3d>();
List<Line> mb_original = new List<Line>();
input_srf = new List<Surface>();

if (!DA.GetDataList(0, pt)) { return; }
if (!DA.GetDataList(1, mb_original)) { return; }
if (!DA.GetDataList(2, fix)) { return; }
if (!DA.GetDataList(3, input_srf)) { return; }

nm = mb_original.Count;

istart = new int[nm];
iend = new int[nm];
L_original = new double[nm];
for (int i = 0; i < nm; i++)
{
istart[i] = Rhino.Collections.Point3dList.ClosestIndexInList(pt, mb_original[i].From);
iend[i] = Rhino.Collections.Point3dList.ClosestIndexInList(pt, mb_original[i].To);
L_original[i] = mb_original[i].Length;
}

nk = pt.Count;

free = new List<int>();
for (int i = 0; i < nk; i++)
{
}

nfree = free.Count();
nsrf = input_srf.Count();

srf_xmax = new double[nsrf];
srf_xmin = new double[nsrf];
srf_ymax = new double[nsrf];
srf_ymin = new double[nsrf];

for (int k = 0; k < nsrf; k++)
{
srf_xmax[k] = double.NegativeInfinity;
srf_xmin[k] = double.PositiveInfinity;
srf_ymax[k] = double.NegativeInfinity;
srf_ymin[k] = double.PositiveInfinity;
}

srf_xrange = new double[nsrf];
srf_yrange = new double[nsrf];

c_p = new List<Point3d[,]>();
scaled_c_p = new List<Point3d[,]>();

for (int k = 0; k < nsrf; k++)
{
var target_srf = input_srf[k].ToNurbsSurface();
int nu = target_srf.OrderU;
int nv = target_srf.OrderV;

for (int i = 0; i < nu; i++)
{
for (int j = 0; j < nv; j++)
{
c_p[k][i, j] = target_srf.Points.GetControlPoint(i, j).Location;
if (c_p[k][i, j].X > srf_xmax[k]) { srf_xmax[k] = c_p[k][i, j].X; }
if (c_p[k][i, j].X < srf_xmin[k]) { srf_xmin[k] = c_p[k][i, j].X; }
if (c_p[k][i, j].Y > srf_ymax[k]) { srf_ymax[k] = c_p[k][i, j].Y; }
if (c_p[k][i, j].Y < srf_ymin[k]) { srf_ymin[k] = c_p[k][i, j].Y; }
}
}

srf_xrange[k] = srf_xmax[k] - srf_xmin[k];
srf_yrange[k] = srf_ymax[k] - srf_ymin[k];

for (int i = 0; i < nu; i++)
{
for (int j = 0; j < nv; j++)
{
scaled_c_p[k][i, j] = new Point3d((c_p[k][i, j].X - srf_xmin[k]) / srf_xrange[k], (c_p[k][i, j].Y - srf_ymin[k]) / srf_yrange[k], c_p[k][i, j].Z);
}
}
}
}

else
{
input_srf = new List<Surface>();
if (!DA.GetDataList(3, input_srf)) { return; }

c_p = new List<Point3d[,]>();
scaled_c_p = new List<Point3d[,]>();

for (int k = 0; k < nsrf; k++)
{
var target_srf = input_srf[k].ToNurbsSurface();
int nu = target_srf.OrderU;
int nv = target_srf.OrderV;

for (int i = 0; i < nu; i++)
{
for (int j = 0; j < nv; j++)
{
c_p[k][i, j] = target_srf.Points.GetControlPoint(i, j).Location;
}
}

for (int i = 0; i < nu; i++)
{
for (int j = 0; j < nv; j++)
{
scaled_c_p[k][i, j] = new Point3d((c_p[k][i, j].X - srf_xmin[k]) / srf_xrange[k], (c_p[k][i, j].Y - srf_ymin[k]) / srf_yrange[k], c_p[k][i, j].Z);
}
}

}

for (int i=0;i<nfree;i++)
{
for (int k=0; k<nsrf; k++)
{
if (srf_xmin[k]-e < pt[free[i]].X && pt[free[i]].X < srf_xmax[k]+e && srf_ymin[k]-e < pt[free[i]].Y && pt[free[i]].Y < srf_ymax[k]+e)
{
pt[free[i]] = new Point3d(pt[free[i]].X, pt[free[i]].Y, B.z_plot((pt[free[i]].X - srf_xmin[k]) / srf_xrange[k], (pt[free[i]].Y - srf_ymin[k]) / srf_yrange[k], scaled_c_p[k]));
break;
}
}
}

mb = new Line[nm];
for (int j = 0; j < nm; j++)
{
mb[j] = new Line(pt[istart[j]], pt[iend[j]]);
if (mb[j].Length > L_original[j] * 1.3)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, string.Format("member {0} is too long.", j));
}
if (mb[j].Length < L_original[j] * 0.7)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, string.Format("member {0} is too short.", j));
}
}

DA.SetDataList(0, pt);
DA.SetDataList(1, mb);
}
}

/// <summary>
/// Provides an Icon for every component that will be visible in the User Interface.
/// Icons need to be 24x24 pixels.
/// </summary>
protected override System.Drawing.Bitmap Icon
{
get
{
return null;
}
}

/// <summary>
/// Each component must have a unique Guid to identify it.
/// It is vital this Guid doesn't change otherwise old ghx files
/// </summary>
public override Guid ComponentGuid
{
get { return new Guid("508852b3-6bda-402e-b6c2-92b02cc8aa92"); }
}
}
}

using System;
using Grasshopper.Kernel;

namespace VariableGeometryTruss
{
public class CustomComponentAttributes : Grasshopper.Kernel.Attributes.GH_ComponentAttributes
{
// custom attribute to override double click mouse event on component and open a WPF window

public CustomComponentAttributes(IGH_Component component)
: base(component)
{
GhComponent = (VariableGeometryTrussComponent)component;
}

VariableGeometryTrussComponent GhComponent;
public override Grasshopper.GUI.Canvas.GH_ObjectResponse RespondToMouseDoubleClick(Grasshopper.GUI.Canvas.GH_Canvas sender, Grasshopper.GUI.GH_CanvasMouseEvent e)
{
if(GhComponent.mode == component_mode.Initialize)
{
GhComponent.mode = component_mode.Exploit;
}

else
{
GhComponent.mode = component_mode.Initialize;
}

Grasshopper.Instances.ActiveCanvas.Document.NewSolution(true);
return base.RespondToMouseDoubleClick(sender, e);
}
}
}

using System;
using Rhino.Geometry;

namespace VariableGeometryTruss
{
class bezier
{
private double bernstein(double t, int n, int i)
{
double bernstein = 0.0;
if (i == 0 | i > n)
{ return bernstein; }
int cn = 1;
int ci = 1;
int cni = 1;
for (int k = 2; k < n; k++)
{ cn *= k; }
for (int k = 1; k < i; k++)
{
if (i == 1) { break; }
ci *= k;
}
for (int k = 1; k < n - i + 1; k++)
{
if (n == i) { break; }
cni *= k;
}
bernstein = Math.Pow(t, (i - 1)) * Math.Pow((1 - t), (n - i)) * cn / (ci * cni);
return bernstein;
}

public double z_plot(double x, double y, Point3d[,] c_p)
{
double z = 0.0;
int nu = c_p.GetUpperBound(0) + 1;
int nv = c_p.GetUpperBound(1) + 1;
for (int i = 0; i<nu; i++)
{
double bu = bernstein(x, nu, i + 1);
for (int j=0;j<nv;j++)
{
double bv = bernstein(y, nv, j + 1);
z += c_p[i, j].Z * bu * bv;
}
}
return z;
}
}
}

##### [2. Are the axial forces within the range that the actuator can handle?]

Linear structural analysis is conducted for various geometry of VGT to confirm that every axial stresses are within the range of allowable stress. Let $$\bf u$$ denote a vector of displacement about all degrees of freedom. $$\bf u$$ can be obtained by solving the following stiffness equation:

$${\bf Ku}={\bf p}$$

where $${\bf K}$$ is a global stiffness matrix and $$\bf p$$ is a load vector. After obtaining elongation of $$i$$ th member $$d_i$$ from $$\bf u$$, axial stress of $$i$$ th member $$\sigma_i$$ is given by

$$\sigma_i = \frac{Ed_i}{L_i}$$

where $$L_i$$ is member length of member $$i$$ and $$E$$ is Young’s modulus, respectively.

Tags: