# Shape Grammar

Coursework for Design Computation II

Implementation of Shape Grammar using Rhino and GH Python. A shape grammar is a set of shape rules applied in discrete steps to generate designs of shapes. The example demostrated is based on the following Shape Grammar:

1. Every square has an anchor and a heading (as a vector). These two properties define the direction of rotation of the square (clockwise or counter-clockwise).

2. A new square is generated by rotating the original square by -90 or 90 degrees, according to its direction of rotation.

3. Generate a new square based on rule 2, but move the anchor to the center of the edge ad scale the size of the new square by 2/3.

4. Eliminate a square from the existing set, randomly choose another square from the set as the next origin.

Type: design computation study
Role: code implementation
Date: 2018

Demonstration of shape growth

GH Python Code:

``````__author__ = "Vincent Mai"
__version__ = "2018.11.10"

"""
Design Computation II
Shape Grammar
"""
import random
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg

class Shape(object):
def __init__(self):
self.squares = set()
self.frontier = set()

def initialShape(self, num, size):
"""
generate initial state of the shape object by the specified
square numbers and size, each with a random coner anchor,
a random rotational direction and a random heading.
"""
gridSize = int(num**0.5)+3
centerLocs = [(x*size, y*size) for x in range(gridSize) for y in range(gridSize)]
centers = random.sample(centerLocs, num)
for center in centers:
x, y = random.choice((-1,1)), random.choice((-1, 1))
anchorDir = (x*0.5*size, y*0.5*size)
anchor = (center+anchorDir, center+anchorDir)
rotation = random.choice(('cw', 'ccw'))
# generate heading based on clockwise or counter clockwise rotation
if anchorDir == anchorDir:
heading = (0, -anchorDir) if rotation == 'cw' else (-anchorDir, 0)
else:
heading = (-anchorDir, 0) if rotation == 'cw' else (0, -anchorDir)
# initializing square objects
newSquare = Square(anchor, heading, rotation, center, size)

def grow(self):
"""
grow frontier squares by random choice of shape grammar or delete
squares from existing squares
"""
tempFrontier = set()
for square in self.frontier:
grammar = random.randint(0,20)
if grammar < 10:
elif grammar < 15:
else:
self.frontier = tempFrontier
self.squares |= self.frontier
print(len(tempFrontier))

def rule1(self, square):
"""
based on the rotation of the original square
"""
# rotate heading cw if original square is ccw, and vice versa
if square.rotation == 'cw':
else:
newCenter = (square.anchor+move, square.anchor+move)
newSquare = Square(square.anchor, newHeading, square.rotation, newCenter, square.size)
return newSquare

def rule2(self, square):
"""
adding an adjacent square scaled by 2/3 and anchored to the midpoint
of one of the edges
"""
if square.rotation == 'cw':
else:
newSize = square.size*2/3
newCenter = (newAnchor+move, newAnchor+move)

newSquare = Square(newAnchor, newHeading, square.rotation, newCenter, newSize)
return newSquare

def rule3(self):
"""
eliminate
"""
self.squares.remove(random.sample(self.squares, 1))
return random.sample(self.squares, 1)

class Square(object):
def __init__(self, anchor, heading, rotation, center, size):
self.anchor = anchor
self.rotation = rotation
self.center = center
self.size = size

if reset:
centerList = []
anchorList = []
sizeList = []
random.seed(0)
shape = Shape()
shape.initialShape(num, size)

else:
centerList = []
anchorList = []
sizeList = []

for i in range(1):
shape.grow()

for square in shape.squares:
centerList.append(rs.CreatePoint(square.center, square.center))
anchorList.append(rs.CreatePoint(square.anchor, square.anchor))