Annotate Section and Tabulate Metrics

In this tutorial we will annotate a model by adding section levels and we will produce and emit building metrics to Excel. This is part of the joint exercise of performing a massing study for a building and producing a mold for casting. Interim steps have been presented previously in separate posts.

tabs

Technique

  1. Apply the same logic of slicing solid models to produce contours but this time use the information for creating spread sheets and annotating a building section a-la Building Information Modeling.
  2. Use the AreaMassProperties object to compute all sorts of interesting geometric properties such as mass, area, moments of inertia, centroid etc.
  3. Format and compound text to form a spread sheet table which may be emitted into a panel or copied directly into the clipboard such that it can be pasted into Excel.

Procedure

private void RunScript(Brep Mass, Brep Core, double FloorHeight, 
  ref object Geometry, ref object AnnoPnts, 
  ref object AnnoTxts, ref object Excel)
  {

    //-- Lesson: Break Complex Processes into
    //-- logical tasks. Each time store info into
    //-- lists. Keep minimal loop nesting.
    //--

    var geometry = new List<object>();



    //-- To Calculate the number of Slices Needed
    //--
    BoundingBox bounds = Mass.GetBoundingBox(true);

    var zmin = bounds.Min.Z;
    var zmax = bounds.Max.Z;

    var count = (int) ((zmax - zmin) / FloorHeight);
    if( count == 0)
    {
      Print("Nothing to Cut, Bye!");
      return;
    }





    //-- To Produce the Planes for Areas
    //--
    var planes = new List<Plane>();
    for( var index = 0; index < count; index++ )
    {
      int level = index;

      double height = level * FloorHeight + 1.5; //<-- +1.5m above FFL

      Point3d origin = new Point3d(0, 0, height);

      Plane plane = new Plane(origin, Vector3d.ZAxis);

      planes.Add(plane);

      ///geometry.Add(plane);
    }






    //-- To Produce the Mass Outlines
    //--
    var mass_curves = new List<Curve>();

    for( var index = 0; index < count; index++ )
    {
      Curve[] curves;
      Point3d[] points;

      Rhino.Geometry.Intersect.Intersection.BrepPlane(
        Mass, planes[index], 0.0001, out curves, out points);

      //-- Complete Failure
      //--
      if( curves.Length == 0)
      {
        Print("Failed at Level " + index.ToString());
        return;
      }

      //-- Numerical Issue or Topology Issue
      //--
      if( curves.Length != 1)
      {
        Print("Something is fishy at Level " + index.ToString());
      }

      mass_curves.Add(curves[0]);

      geometry.Add(curves[0]);
    }








    //-- To Produce the Core Outlines (Same as above)
    //-- Generally when you feel the urge to copy
    //-- paste chunks of code it should raise an
    //-- alarm that you need to wrap the logic into
    //-- a function...
    //--
    var core_curves = new List<Curve>();

    for( var index = 0; index < count; index++ )
    {
      Curve[] curves;
      Point3d[] points;

      Rhino.Geometry.Intersect.Intersection.BrepPlane(
        Core, planes[index], 0.0001, out curves, out points);  //<-- CHANGED to CORE

      //-- Complete Failure
      //--
      if( curves.Length == 0)
      {
        Print("Failed at Level " + index.ToString());
        return;
      }

      //-- Numerical Issue or Topology Issue
      //--
      if( curves.Length != 1)
      {
        Print("Something is fishy at Level " + index.ToString());
      }

      core_curves.Add(curves[0]);  //<-- CHANGED to CORE_CURVES
      geometry.Add(curves[0]);
    }











    //-- To Produce an Excel Table
    //-- Note: \t means tab and \r\n means new line (enter)
    //--
    var table = "Level\tFFL @ AGL(m)\tGEA(sqm)\tGIA(sqm)\tEfficiency(%)\r\n";

    for( var index = 0; index < count; index++ )
    {
      //-- To Invert a signal (if too confusing, store each line in a
      //-- list and invert the list.Reverse());
      //-- All we are doing here is going from index: [0..count-1]
      //-- to backwards: [count-1..0] so that the tabulated data
      //-- are presented from top to bottom of the building
      //--

      //-- Now I will use backwards instead of index
      //--
      var backwards = count - index - 1;

      var mass = mass_curves[backwards];
      var core = core_curves[backwards];

      //-- Look into documentation for: AreaMassProperties
      //--
      var mass_props = AreaMassProperties.Compute(mass);
      var core_props = AreaMassProperties.Compute(core);

      var mass_area = mass_props.Area;
      var core_area = core_props.Area;

      //-- This is WRONG because efficiency = NIA / GIA
      //-- as an exercise you may compute first a ball park
      //-- GIA by offsetting the GEA inbound by say 300mm
      //-- and then remove the core from it to get a ball park NIA
      //--
      var efficiency = ( mass_area - core_area) / mass_area;

      table += string.Format(
        "{0}\t{1:0.000}\t{2:0.000}\t{3:0.000}\t{4:0.000}\r\n",
        backwards,
        planes[backwards].Origin.Z - 1.5, //-- Because cutting plane was +1.5 above FFL
        mass_area,
        mass_area - core_area,
        efficiency);
    }

    //-- To Copy into Clipboard
    //--
    Clipboard.SetText(table);

    //-- To Export into Grasshopper
    //--
    Excel = table;









    //-- To Annotate a Section
    //--
    var annotxts = new List<string>();
    var annopnts = new List<Point3d>();


    for( var index = 0; index < count; index++ )
    {
      //-- To Move a Points 1.5m downwards
      //--
      var pnt = planes[index].Origin - Vector3d.ZAxis * 1.5;
      var txt = string.Format("Level {0}\r\nFFL +{1:0.000} AGL", index, pnt.Z);

      //-- And a nice section line
      //--
      var line = new Line(pnt, Vector3d.XAxis * 100);
      geometry.Add(line);


      annopnts.Add(pnt);
      annotxts.Add(txt);
    }

    AnnoPnts = annopnts;
    AnnoTxts = annotxts;




    Geometry = geometry;

  }

Remarks

  1. The technique used here is a semi-organized approach to writing multi-phase procedures by storing interim results into collections. For a more complete approach to process structuring see the previous layout exercise which uses simple object-oriented approach.
  2. To decide whether to go for (a) Full spaghetti procedural representation, eg. highly nested loop constructs and computations of the fly, (b) Semi-structured representations, such as here or (c) Fully structured object-oriented representations… use the Litmus test(s): Does it fit in one page? Then use strategy (a); if not then, do you prefer to never write some thing again and again? Then use strategy (c); otherwise stick with (b).
  3. Generally speaking full structure comes with administrative overhead that does not justify the loss of intuition and mental complexity, unless there is a long term plan of reuse, other people that may be collaborating with you etc.
  4. For general design audiences where code quality and standards is not the primary consideration, the approach shown here is highly advisable.