Fereshteh Shahmiri

PhD in Computer Science - School of Interactive Computing - Georgia Institute of Technology

 

Technology Square Research Building
85 Fifth Street NW
Atlanta, GA 30308

© 2017 by Fereshteh Shahmiri. All rights reserved.

Morphing

Spiral Curve along a Circle

 

By using the Function and the Point Polar component a spiral along a given 3D curve can be generated. The grasshopper file needs to have at least two numeric controllers for number of segments and total number of rotations. Also, the sampling points from the given curve for constructing the spiral should be evenly distributed. By adding another set of parameters this curve can be modified.

The concept of this modeling is related to landscape furniture design.

Rose Curves Modeling

 

By using the equation for the rose curve and the Function component with three input variables (x,y,z) a series of 2D and 3D Rose curves can be derived.

The following equation is used for constructing points on the rose curve and is based on polar coordinate system, where a point is defined by 𝑃(𝛾, 𝜃).

Equation for Rose Curve Point :        𝛾 = sin (𝜅𝜃)   (𝜅 =𝑛 /𝑑)

 

Based on Rose Curve modeling, a turing tower can be modeled in grasshopper. And in accordance with different amounts for n and d , different forms for tower can be generated.

 

download .ghx file

 

Remapping HexGrid Surface

 

By starting from a basic HexGrid component in grasshopper, a hexagonal pattern on any given surface will be created. We should construct the parametric relationship for the hexagonal pattern by examining the HexGrid component. We could then reuse this relationship to generated the hexagonal grid on any untrimmed surface. The relationship should be parametric both ways. If the original hexagonal pattern be changed, then the mapped pattern on the surface should be updated accordingly. If the surface be deformed, then the mapping of the hexagonal pattern should be adjusted. This parametric relationship expresses how a hexagonal pattern can be generated on a surface by specifying the underlying UV parameters.

 

Experimenting with Mobious Strip

 

A Mobius strip is an example of moving a line segment ‘g’ along a circle ’ c’.  As moving the line segment ‘g’ along ‘c’, ‘g’ is continuously rotated around ‘c’ and remain orthogonal to the same intersecting point . When ‘g’ returns to the starting position it should turn the rotation angle,   u = p

Self - Intersecting Mobius Strip

 

For every point on this Mobius Strip surface:

 

X = sin(2*u)*(-­‐2+v*sin(u/2))

Y = cos(2*u)*(-­‐2+v*sin(u/2))

Z = v*cos(u/2)where0

(0<u<2π, ­‐1<v<1)

 

Based on simple Mobius strip definition, Function components with Surface from Points component are combined to generate the surface. In next step an unrolled strip is created, which has the same length of the Mobius strip. The radius for these trimmed openings, circles, has a ratio to the length of this strip:

 

r = (length / numofOpenings) / c

 

where c is the control factor that changes the size of the openings

Mesh Refinement via Edge Midpoint Insertion

 

Instead of using the mesh component directly, 4Point Surface component is used for this exercise. Each 4Point Surface is treated as a quadrilateral mesh, which consists of 4 vertices {A, B, C, D}. Given any free‐form surface from Rhino (or initiated in Grasshopper), the first step is to decompose the surface into a number of sub- surfaces.

The second step is then to insert edge midpoints of each face and connect  these new points to create the central quads. The final step is to fill the gaps between these new quads. The final refinement output yields diamond‐like (diagrid) configuration.

Parametric Pavilion

 

The objective of this exercise is to generate a parametric pavilion in order to experiment with surface tessellations.

3D Diamond Box Morphing

 

Twisted Box component rather than surface boxes in this problem for box morphing has been used. The Twisted Box is a component that allows you to create a reference (or target) box from scratch by Specifying eight vertices of the box.  In this problem, you are asked to create the diamond boxes and use them for the surface tessellation with potential panel modules.

Responsive Component – Parametric Façade

 

A responsive surface that will respond to the input stimulus is created. For example, the sun position can be treated as the input parameter. Here, a parametric façade is designed which consists of number of surface components and they will respond to the change of the attracting resources respectively.

Surface Subdivision by Single / Multiple attractors

 

We can modify a two dimensional domain, defined by {{u0,u1},{v0,v1}}, to generate the customized grid pattern for surface subdivision. By utilizing the Graph component to vary the density of the grid pattern successfully.

Here, attractors are introduced as references to drive the surface subdivision pattern.

 

download .gh file

PARAMETRIC MODELING

 

Bentley Systems, Inc
GenerativeComponents (GC) | Parametric CAD Software
Fall - Winter 2014



Basic Example |
    Introduction of Parametric Design Concepts
    Hierarchical Levels of Zooming and Scales,
    Joints between Levels

Visualization: Luxulogy Rendering - MicroStation

 

Modeling Process|
    1. Modeling the basic module of the geometry
    2.Combination of 2 basic module by using transformation techniques and generation of the developed module
     3.Combination of the developed module and formation of the main geometry as a tower.
GC Tool: Generated Node Technique (GNT)
Transformation and Replication Concepts by using CoordinateSystem.ByUniversalTransform

Double Skin Tower Modeling | First Skin
Level of Zooming: Geometry Subdivision
Parametric Design Concepts: Place Holder
     1. by using the GNT providing one Module of Window and window frame (Place Holder)
    2. Point grid on one side of the building ( Replication, Jig) and Assigning the window to the point grid
    3. Extension to all sides of the building ( Replication)
Note: Final geometry assigned to all sides prevents large amount of calculation during the modeling and makes the file lighter.

Double Skin Tower Modeling | Second Skin
Level of Zooming: Geometry Subdivision
Parametric Design Concepts: Point Collection, Rationalization
Note1:
Using GNT( Generated Node Technique) in most of the parts
Modeling Process|
    1. Generation of Random points on one side of the cube
        (Delaunay diagram)
    2. Delaunay triangulation
    3.Extension to all sides of the building ( Replication)
Note 2: Final geometry assigned to all sides prevents large amount of calculation during the modeling and makes the file lighter.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

PARAMETRIC MODELING

 

These examples are done in

"Parametric Modeling" Course, And Grasshopper Environment

Special Thanks to Professor Ramesh Krishnamurti

Fall 2012 

 

 

 

  private void RunScript(List<Point3d> pointSet, List<Point3d> boundary, ref object polyLines, ref object curves)
  {

    //CHECK EDGE CONDITIONS
    if(pointSet.Count < 1 || boundary.Count < 1){
      Print("Input PointSet, or Boundary Points, is null");
      return;
    }

 

    //INITIALIZE LISTS TO STORE OUTPUT CURVES AND OUTPUT POLYLINES
    List<Curve> vCurves = new List<Curve>();
    List<Polyline> vPLines = new List<Polyline>();


    List<VoronoiCell> vCells = new List<VoronoiCell>();

    //ITERATE OVER EACH POINT IN THE INPUT POINTSET
    foreach(Point3d p in pointSet) {
  
    //instantiate a voronoi cell for each point
      VoronoiCell vCell = new VoronoiCell(p, boundary);

      foreach(Point3d pt in pointSet) {
     
   //we need to consider all points BUT the point under consideration
        if (pt != p) {
          vCell.sliceCell(pt);
        }
      }
      vPLines.Add(vCell.createPolyline());
      vCurves.Add(vCell.createCrv(3));
    }

    Print("\nNumber of polyLines: " + vPLines.Count);
    Print("Number of OnNurbsCurve: " + vCurves.Count);

 

    //SET THE OUTPUTS
    polyLines = vPLines;
    curves = vCurves;

  }

 

  public class VoronoiCell {
    //VARIABLES
    List<Point3d> vertices;
    Point3d center;


    //CONSTRUCTOR
    public VoronoiCell(Point3d p, List<Point3d> pts) {
      this.center = p;
      this.vertices = pts;
    }


    //METHODS

    // Slice Cell operation
    public void sliceCell(Point3d pt) {
      // list for storing temporary new boundary points
      List<Point3d> tempVertices = new List<Point3d>();
      // list for storing final new boundary points
      List<Point3d> newVertices = new List<Point3d>();
  
    // counter for number of intersection of bisector with current boundary
      int numIntersections = 0;
      // intersection of a boundary segment with bisector
      double intersection;


      //STEP 1: CREATE BISECTOR
      Line segment01 = createBisector(this.center, pt);

 

      //STEP 2: FIND INTERSECTING POINTS
      for (int i = 0; i < this.vertices.Count; i++) {
     
   // create a boundary segment by connecting two sequential points in vertices array
        Line segment02 = new Line(this.vertices[i], this.vertices[(i + 1) % vertices.Count]);
   
     // add first point of segment to new boundary
        tempVertices.Add(this.vertices[i]);
    
    // if a valid intersection found by current boundary segment
        // add the intersection point as a new point to boundary points list

        intersection = intersect2Lines(segment02, segment01);
        if (intersection != -1) {
      
    // a new intersection has been found
          numIntersections++;
          tempVertices.Add(segment02.PointAt(intersection));
        }


      }

 

 

      //STEP 3:UPDATE BOUNDARY VERTICES
      if (numIntersections == 2) {
      
  // two doubles could not be exactly equal
        // so there is an epsillon tolerance for finding distances

        double epsillon = 0.0001;

        // traverse trough all points in new vertices and find points
        // that belongs to this cell and remove others
        // note: points that belongs to this cell is closer or have equal distance to
        // cell center compare to point to slice from "pt"

        foreach(Point3d newVerticesPt in tempVertices) {
          if ((newVerticesPt.DistanceTo(this.center) - newVerticesPt.DistanceTo(pt)) < epsillon) {
            newVertices.Add(newVerticesPt);
          }
        }

        // update current boundary by replacing it with
        // new boundary

        this.vertices = newVertices;
      }
    }

    public Line createBisector(Point3d ptStart, Point3d ptEnd) {
      Point3d newPtEnd = new Point3d();

      Point3d newPtStart = (ptStart + ptEnd ) / 2;
      newPtEnd.X = newPtStart.X - (ptEnd.Y - ptStart.Y);
      newPtEnd.Y = newPtStart.Y + (ptEnd.X - ptStart.X);

      return new Line(newPtStart, newPtEnd);
    }

    /*
    *This method returns a double value representing the intersection
    *of two lines. If the value is equal to -1 the lines do not
    *intersect; otherwise the value represents the parameter to locate
    *the intersecting point.
    */

    public double intersect2Lines(Line line01, Line line02) {
      double D; // Denominator
      double E; // Enumerator

      //line01
      double line01startX = line01.From.X;
      double line01endX = line01.To.X;
      double line01startY = line01.From.Y;
      double line01endY = line01.To.Y;
     
//line02
      double line02startX = line02.From.X;
      double line02endX = line02.To.X;
      double line02startY = line02.From.Y;
      double line02endY = line02.To.Y;

      D = (line02endY - line02startY) * (line01endX - line01startX)
        - (line01endY - line01startY) * (line02endX - line02startX);

      if (D == 0) return -1.0;

      E = (line02endX - line02startX) * (line01startY - line02startY)
        - (line02endY - line02startY) * (line01startX - line02startX);

      //intersection function - parameter to locate the intersecting point with the line range
      double intersection = E / D;
      if (intersection < 0 || intersection > 1) return -1.0;

      return intersection;
    }

    /*
    *This method returns the points maintained by the VoronoiCell stored in an Array. Also the first point of the initial list is also added at the end. This method is called by the createCrv()method in order to get the points for generating a closed curve,
    */

    private Point3d[] get3DPoints() {
      List<Point3d> points = new List<Point3d>();
      points.AddRange(this.vertices);
    
  //repeat the first point to the end of the list
      points.Add(this.vertices[0]);
      return points.ToArray();
    }

    public Curve createCrv(int degree) {

      NurbsCurve nCurve = NurbsCurve.Create(true, degree, this.vertices);

      return nCurve;
    }

    public Polyline createPolyline() {

      // create a closed polyline by connecting cell boundary points
      Polyline boundaryPolyline = new Polyline();
      for(int i = 0; i <= this.vertices.Count; i++) {
        boundaryPolyline.Add(this.vertices[i % this.vertices.Count]);
      }
      return boundaryPolyline;
    }

  }

 

DELAUNAY TRIANGULATION

 

In mathematics and computational geometry, a Delaunay triangulation for a set P of points in a plane is a triangulation DT(P) such that no point in P is inside the circumcircle of any triangle in DT(P). Delaunay triangulations maximize the minimum angle of all the angles of the triangles in the triangulation; they tend to avoid skinny triangles.

For a set of points on the same line there is no Delaunay triangulation. For four or more points on the same circle (e.g., the vertices of a rectangle) the Delaunay triangulation is not unique: each of the two possible triangulations that split the quadrangle into two triangles satisfies the "Delaunay condition", i.e., the requirement that the circumcircles of all triangles have empty interiors.

By considering circumscribed spheres, the notion of Delaunay triangulation extends to three and higher dimensions. Generalizations are possible to metrics other than Euclidean. However in these cases a Delaunay triangulation is not guaranteed to exist or be unique.(Wikipedia)

 

download .gh File picture

 

 

  private void RunScript(List<Point3d> pointSet, List<Point3d> boundary, ref object polyLines, ref object curve)
  {
    circumCircle c;
    List<Point3d> triangles = new List<Point3d>();
    pointSet.AddRange(boundary);
    for (int i = 0; i < pointSet.Count - 2;i++){
      for (int j = i + 1; j < pointSet.Count - 1;j++){
        for (int k = j + 1; k < pointSet.Count;k++){
          c = new circumCircle(pointSet[i], pointSet[j], pointSet[k]);
          bool pointValid = true;
          for (int l = 0; l < pointSet.Count && pointValid;l++){
            if (l != i && l != j && l != k)
            {
              if (c.IsPointInside(pointSet[l]))
              {
                pointValid = false;
              }
            }
          }
          if (pointValid)
          {
            triangles.Add(pointSet[i]);
            triangles.Add(pointSet[j]);
            triangles.Add(pointSet[k]);
            triangles.Add(pointSet[i]);
          }
        }
      }
    }
    List<Polyline> poly = new List<Polyline>();
    List<Curve> vCurves = new List<Curve>();
    Polyline boundaryPolyline = new Polyline();
    List<Point3d> boundaryPoints = new List<Point3d>();
    for(int i = 0; i < triangles.Count / 4; i++) {
      boundaryPolyline = new Polyline();
      boundaryPoints = new List<Point3d>();
      for (int j = 0;j < 4;j++){
        boundaryPolyline.Add(triangles[i * 4 + j]);
        boundaryPoints.Add(triangles[i * 4 + j]);
      }
      vCurves.Add(NurbsCurve.Create(true, 3, boundaryPoints));
      poly.Add(boundaryPolyline);
    }
    polyLines = poly;
    curve = vCurves;
  }

 

BOID FLOCKING

 

Boids is an artificial life program which simulates the flocking behavior of birds. As with most artificial life simulations, Boid is an example of emergent behavior; the complexity of Boids arises from the interaction of individual agents adhering to a set of simple rules.

These rules are:

separation: steer to avoid crowding local flockmates

alignment: steer towards the average heading of local flockmates

cohesion: steer to move toward the average position (center of mass) of local flockmates.

(wikipedia)

 

download .gh File Picture

  private void RunScript(List<On3dPoint> P, List<On3dVector> D, double W, double L, double H, int S, ref object B_Loc, ref object B_Dir)
  {
    Print("Number of Flocking Boids:{0}\n\n", P.Count);
    if(P.Count != D.Count) return;

    // Add an initial set of boids into the system
    if(flock == null)
    {
      Print("Flock is null");
      flock = new Flock();
      for (int i = 0; i < P.Count; i++)
      {
        flock.addBoid(new Boid(P[i], D[i], 50.0, 5, W, L, H));
      }
    }

    flock.run();

    B_Loc = flock.getBoidsLocs();
    B_Dir = flock.getBoidsDirs();

  }

  Flock flock = null;// = new Flock();
  static public Random ran = new Random();

  public class Boid
  {
    public On3dPoint loc;
    public On3dVector vel;
    On3dVector acc;
    double r;
    double maxforce;    // Maximum steering force
    double maxspeed;    // Maximum speed
    double width;
    double length;
    double height;

    public Boid(On3dPoint lc, On3dVector v, double mf, double ms, double wd, double len, double ht)
    {
      loc = lc;
      acc = new On3dVector(0, 0, 0);
      vel = v;
      r = 2.0;
      maxforce = mf;
      maxspeed = ms;
      width = wd;
      length = len;
      height = ht;
    }

    public void run(List<Boid> boids) {
      flock(boids);
      update();
      borders();
    }

    // We accumulate a new acceleration each time based on three rules
    void flock(List<Boid> boids)
    {
      On3dVector sep = separate(boids);   // Separation
      On3dVector ali = align(boids);      // Alignment
      On3dVector coh = cohesion(boids);   // Cohesion

      // Arbitrarily weight these forces
      //sep.mult(1.5); ali.mult(1.0); coh.mult(1.0);

      sep *= (1.5);
      ali *= (1 );
      coh *= (1 );

      // Add the force vectors to acceleration
      acc += (sep);
      acc += (ali);
      acc += (coh);
    }

    // Method to update location
    void update() {
     
// Update velocity
      vel += (acc);

      // Limit speed
      if(vel.Length() > maxspeed)// Limit to maximum steering force
      {
        vel.Unitize();
        vel *= maxspeed;
      }

      loc += (vel);
 
     // Reset accelertion to 0 each cycle
      acc *= (0);
    }

    // Wraparound
    void borders() {
      if (loc.x < -r) loc.x = width + r;
      if (loc.y < -r) loc.y = length + r;
      if( loc.z < -r) loc.z = height + r;
      if (loc.x > width + r) loc.x = -r;
      if (loc.y > length + r) loc.y = -r;
      if( loc.z > height + r) loc.z = -r;
    }

    void render()
    {

    }

    // Separation
    // Method checks for nearby boids and steers away

    public On3dVector separate(List<Boid> boids)
    {
      double desiredseparation = 10.0;//20.0;
      On3dVector steer = new On3dVector(0, 0, 0);
      int count = 0;
     
// For every boid in the system, check if it's too close
      foreach( Boid b in boids)
      {
        double d = loc.DistanceTo(b.loc);
        if((d > 0) && (d < desiredseparation))
        {
      
    // Calculate vector pointing away from neighbor
          On3dVector diff = loc - b.loc;
          diff.Unitize();
          diff /= d; // Weight by distance

          steer += diff;
          count++;
        }
      }

      if (count > 0) {
        steer /= count;
      }

      // As long as the vector is greater than 0
      if (steer.Length() > 0) {
       
// Implement Reynolds: Steering = Desired - Velocity
        steer.Unitize();
        steer *= maxspeed;
        steer -= vel;

        if(steer.Length() > maxforce)// Limit to maximum steering force
        {
          steer.Unitize();
          steer *= maxforce;
        }
      }

      return steer;
    }

 

    // Alignment
    // For every nearby boid in the system, calculate the average velocity

    public On3dVector align(List<Boid> boids)
    {
      double neighbordist = 15;//25.0;
      On3dVector steer = new On3dVector(0, 0, 0);
      int count = 0;
      foreach( Boid b in boids)
      {
        double d = loc.DistanceTo(b.loc);
    
    // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
        if((d > 0) && (d < neighbordist))
        {
          steer += b.vel;
          count++;
        }
      }

      if (count > 0) { // Average -- divide by how many
        steer /= count;
      }

      // As long as the vector is greater than 0
      if(steer.Length() > 0)
      {
        steer.Unitize();
        steer *= maxspeed;
        steer -= vel;

        if(steer.Length() > maxforce)
        {
          steer.Unitize();
          steer *= maxforce;
        }
      }
      return steer;
    }

    // Cohesion
    // For the average location (i.e. center) of all nearby boids,
    // calculate steering vector towards that location

    public On3dVector cohesion(List<Boid> boids)
    {
      double neighbordist = 5;//25.0;
  
    // Start with empty vector to accumulate all locations
      On3dVector sum = new On3dVector(0, 0, 0);

      int count = 0;
      foreach(Boid b in boids)
      {
        double d = loc.DistanceTo(b.loc);
        if((d > 0) && (d < neighbordist))
        {
          sum += b.loc;
          count++;
        }
      }

      if(count > 0)
      {
        sum /= count;
        return steer(sum, false);
      }

      return sum;
    }
    public On3dVector steer(On3dVector target, bool slowdown) {
      On3dVector steer;  // The steering vector
      On3dVector desired = new On3dVector(target.x - loc.x, target.y - loc.y, target.z - loc.z);
      double d = desired.Length();

      // Distance from the target is the magnitude of the vector

      // If the distance is greater than 0, calc steering (otherwise return zero vector)
      if (d > 0)
      {
      
  // Normalize desired
        desired.Unitize();
       
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxspeed)
        if ((slowdown) && (d < 100.0)) desired *= (maxspeed * (d / 100.0)); // This damping is somewhat arbitrary
        else desired *= (maxspeed);

        // Steering = Desired minus Velocity
        steer = new On3dVector(desired.x - vel.x, desired.y - vel.y, desired.z - vel.z);

        if(steer.Length() > maxforce)// Limit to maximum steering force
        {
          steer.Unitize();
          steer *= maxforce;
        }
      }
      else
      {
        steer = new On3dVector(0, 0, 0);
      }
      return steer;
    }

  }

  public class Flock
  {
    List<Boid> boids;

    public Flock()
    {
      boids = new List<Boid>();
    }

    public void run()
    {
    
  // Passing the entire list of boids to each boid individually
      foreach(Boid b in boids)
        b.run(boids);
    }

    public void addBoid(Boid b)
    {
      boids.Add(b);
    }

    public List<On3dPoint> getBoidsLocs()
    {
      List<On3dPoint> locs = new List<On3dPoint>();

      foreach(Boid b in boids)
      {
        locs.Add(b.loc);
      }

      return locs;
    }

    public List<On3dVector> getBoidsDirs()
    {
      List<On3dVector> dirs = new List<On3dVector>();
      foreach(Boid b in boids)
        dirs.Add(b.vel);

      return dirs;
    }

  }

Rhino Script : C# Coding in Grasshopper

  private void RunScript(List<Point3d> pointList, int num, double H, ref object A, ref object B)
  {
   
//CREATE A SURFACE OUT OF THE THREE POINT
    Brep originalSurf = Brep.CreateFromCornerPoints(pointList[0], pointList[1], pointList[2], doc.ModelAbsoluteTolerance);

    //MAKE A LIST AND ADD THE ORIGINAL SURFACE TO IT
    List<Brep> surfaceList = new List<Brep>();
    surfaceList.Add(originalSurf);

    //GET THE MIDPOINTS OF THE EDGES OF THE ORIGINAL SURFACE
    findEdgeMidPoints(originalSurf);

    //RECURSIVELY SUBDIVIDE THE ORIGINAL SURFACE
    List<Brep> subSurfaces = divideTriangles(surfaceList, H, num);

    //REFERENCE OUTPUT VARIABLES
    A = originalSurf;
    B = subSurfaces;
  }

  /*
  * THIS METHOD RECURSIVELY SUBDIVIDES AN INPUT TRIANGULAR SURFACE
  * BY FINDING THE MIDPOINTS OF THE EDGES
  */

  public List<Brep> divideTriangles(List<Brep> subSrf, double height, int N) {
    //DEFINE A STOPPING CONDITION
    if (N == 0) {
      return subSrf;
    }

    //RECURSIVE PART
    N--;
  
  //make a list to add the new subsurfaces
    List<Brep> newSubSrf = new List<Brep>();

    //for every sub surface of the input list
    foreach (Brep srf in subSrf) {
     
//call the method that gets the midpoints of the edges of the surface
      //and fills triangles by connection the midpoints to the endpoints of
      //the edges

      List<Brep> tempSrfList = findEdgeMidPointsSubsurfaces(srf, height);
      newSubSrf.AddRange(tempSrfList);
    }
    return divideTriangles(newSubSrf, height / 2, N);
  }

 

    public List<Point3d> findEdgeMidPoints(Brep surface) {
    //1. GET THE EDGES FROM THE BREP
    Curve[] edges = surface.DuplicateEdgeCurves();
    List<Point3d> midPoints = new List<Point3d>();

    //2. ITERATE OVER THE EDGES AND FIND MIDPOINTS
    for(int i = 0; i < edges.Length; i++) {
  
    //2a. GET THE EDGE
      Curve thisEdge = edges[i];
  
    //2B. GET THE MIDPOINT
      Point3d midPoint = thisEdge.PointAt(thisEdge.GetLength() / 2);
    
  //2C STORE IT IN THE LIST
      midPoints.Add(midPoint);
    }
    return midPoints;
  }

  public List<Brep> findEdgeMidPointsSubsurfaces(Brep surface, double height) {

    //1. GET THE EDGES FROM THE BREP
    Curve[] edges = surface.DuplicateEdgeCurves();

    //2. MAKE A LIST TO ADD THE NEW SUBSURFACES
    List<Brep> subSurfaces = new  List<Brep>();

    //3. GET THE NORMAL OF THE ORIGINAL FACE
    Vector3d normal = surface.Faces[0].NormalAt(0, 0);

    //4. ITERATE OVER THE EDGES AND FIND MIDPOINTS
    for(int i = 0; i < edges.Length; i++) {
    
  //GET THE ith EDGE
      Curve thisEdge = edges[i];
  
    //GET THE MIDPOINT OF THAT EDGE
      Point3d thisMidPoint = thisEdge.PointAt(thisEdge.GetLength() / 2);
   
   //MOVE THE POINT ALONG THE NORMAL VECTOR
      thisMidPoint = thisMidPoint + normal * height;

      //GET THE (i+1)th EDGE
      Curve otherEdge = edges[(i + 1) % edges.Length];
     
//GET THE MIDPOINT OF THAT EDGE
      Point3d otherMidPoint = otherEdge.PointAt(otherEdge.GetLength() / 2);
     
//MOVE THE POINT ALONG THE NORMAL VECTOR
      otherMidPoint = otherMidPoint + normal * height;

      //FIND THE ENDPOINT OF THE APPROPRIATE EDGE
      Point3d endPoint = thisEdge.PointAtEnd;

      //MAKE A SURFACE OUT OF THE TWO MIDPOINTS AND THE ENDPOINT OF THE iTH EDGE
      Brep subSurf = Brep.CreateFromCornerPoints(thisMidPoint, endPoint, otherMidPoint, doc.ModelAbsoluteTolerance);
      subSurfaces.Add(subSurf);

    }
    return subSurfaces;
  }

SURFACE SUBDIVISION BY MULTIPLE ATTRACTORS

 

  private void RunScript(Surface S, List<Point3d> Pts, int uNum, int vNum, ref object subSurface)
  {

    //Lists of x values and y values of attractors in seperate lists
    List<double> Pts_x=new List<double>(),Pts_y = new List<double>();
    foreach (Point3d pt in Pts) {
      Pts_x.Add(pt.X);
      Pts_y.Add(pt.Y);
    }

    //Calculating the intervals between grids
    double vInterval = S.Domain(1).Length / vNum;
    double uInterval = S.Domain(0).Length / uNum;

    //defining u and v in the grid
    List<double> uList = new List<double>(),vList = new List<double>();
    for (int i = 0;i < uNum ;i++){
      uList.Add(S.Domain(0).Min + uInterval * i);
    }
    for (int i = 0;i < vNum;i++){
      vList.Add(S.Domain(1).Min + vInterval * i);
    }

    //subSurf is the output of the script
    List<Brep> subSurf = new List<Brep>();
    Brep surf;

    //finding domain values for both x and y
    List<double> iso_u = new List<double>(),iso_v = new List<double>();
    iso_u = isoCrvGen(Pts_x, uList);
    iso_v = isoCrvGen(Pts_y, vList);

    //Calculate the subsurface by having the domain values
    Point3d p1,p2,p3,p4;
    for (int i = 0;i < iso_u.Count - 1;i++){
      for (int j = 0;j < iso_v.Count - 1;j++){
        p1 = S.PointAt(iso_u[i] * uNum, iso_v[j] * vNum);
        p2 = S.PointAt(iso_u[i + 1] * uNum, iso_v[j] * vNum);
        p3 = S.PointAt(iso_u[i] * uNum, iso_v[j + 1] * vNum);
        p4 = S.PointAt(iso_u[i + 1] * uNum, iso_v[j + 1] * vNum);

 

Rhino Script : C# Coding in Grasshopper

        //creating each subsurface by the four points and adding it to the final subsurface list
        surf = Brep.CreateFromCornerPoints(p1, p3, p4, p2, doc.ModelAbsoluteTolerance);
        subSurf.Add(surf);
      }
    }

    //OUTPUT
    subSurface = subSurf;

  }

  //<Custom additional code>

  //Here is a method which we do most of the calculations to get the domains
  private List<double> isoCrvGen(List<double> Pts, List<double> list){
    List<double> mathAdd = new List<double>();
    try{
      foreach(double k in list){
        double tempdist = 0;
        for(int i = 0;i < Pts.Count;i++){

          // Here we may have divide by zero so we add try catch to catch this exception
          tempdist += 1 / Math.Abs((Pts[i] - k));
        }
        mathAdd.Add(tempdist);
      }
      for(int i = 0;i < mathAdd.Count - 1;i++){
        mathAdd[i + 1] = mathAdd[i] + mathAdd[i + 1];
      }
      mathAdd[0] = 0;

      double m = mathAdd[mathAdd.Count - 1] - mathAdd[0];
      for(int i = 0;i < mathAdd.Count;i++){
        mathAdd[i] = mathAdd[i] / m;
      }
    }catch{
      Console.Write("Divide by Zero");
    }
    return mathAdd;
  }

RECURSIVE TRIANGULAR SUBDIVISION

 

Rhino Script : C# Coding in Grasshopper

VORONOI TESSELATION

 

In mathematics, a Voronoi diagram is a way of dividing space into a number of regions. A set of points (called seeds, sites, or generators) is specified beforehand and for each seed there will be a corresponding region consisting of all points closer to that seed than to any other. The regions are called Voronoi cells.(wikipedia)

 

download .gh File picture