Thursday, 11 August 2011

Quality

We want quality models. A basic rule for quality processes is to determine the root causes of defects. Once you’ve found the root cause, you can change things to (try and) prevent the problem happening again (the oft-misunderstood preventative action)

So when we find a problem in the Revit model, I’d like to know how it happened. But Revit’s infuriatingly opaque. It won’t reveal its sources.

Revit knows who changed the elements. It knows when the changes were committed to the file. We’re not short of disk space (well we are, but that’s another story), so I can allow Revit to keep the full file history. But I just would like to be able to call up the version of the file from a date in the past, and then ask ‘what happened to this element between then and now?’

Then I could find out why the change was made, and change things to try and avoid the problem in the future.

Wednesday, 10 August 2011

Exploded Views

So in Revit 2020, I’ve modelled a complex construction with all its sub-components. How do I convey that to my construction teams?

I can let them pick my model apart, of course. But I’d really like to be able to generate live exploded views. Something like Revit 2012’s locked 3D views, except that I can move the components apart to show how it all fits together.

Memo to self: Is there anything like this in Inventor, for example?

Tuesday, 9 August 2011

Stone jointing

So I’m modelling a stone facade. I need to define the individual stones (my stone supplier needs dimensioned drawings). They’re not in a continuous pattern, but there’s some pattern-like quality.

I could define a complex .pat file and align it onto a basic wall. But that doesn’t show up in plan, so I can’t create setting-out plans for the different courses (my walls are curved, so they can’t be set out on elevation).

I’d really like a cross between a .pat file (to set the pattern), and curtain wall gridlines (real 3D cutting planes), to generate individual stones in a definable, patterned way.

Monday, 8 August 2011

Finding gaps in room boundaries

When a Room isn’t bounded where it should be, it can be tricky to find the gap. You have to spend time putting in Room Boundary lines to narrow down the search.

How would it be if you could just pour (virtual) liquid into the Room, at the room’s origin, and watch while it flowed out to fill the room. You’d instantly see where Revit was finding gaps in the boundary, because that would be where the ‘liquid’ flowed out.

Sunday, 7 August 2011

Room Floors

Joe Stott recently posted about using Ceilings to model floor finishes in Revit, because the Automatic Ceiling tool finds the ceiling boundaries, er, automatically.

But it would be better BIM if the floor finishes were modelled as Floors. And I’ve always though there should be some way of connecting a Room’s Floor Finish property with the modelled floor finish.

So, as a proof of concept, here is a VSTA macro for R2012: MakeFloorsForSelectedRooms.

For each Room you’ve selected, it makes a new floor the same shape as the Room. The floor type is taken from the Room’s Floor Finish property:

  • If there’s a Floor Type with a matching name, the new floor will be of that type.
  • If there isn’t a matching Floor Type, a new Type is created.
  • If the Floor Finish property is blank, the new Floor will be a new default type.

As a proof of concept, it’s still a bit rough around the edges. And there are lots of ways in which it could be extended. As usual, the code comes with no warranties: Experiment at your own risk.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Autodesk.Revit.DB;
using DBArch = Autodesk.Revit.DB.Architecture;

namespace RoomFloors
{
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
[Autodesk.Revit.VSTA.AddInId("039bfb1c-5582-4afc-ab85-491abcf2484a")]
public partial class ThisApplication
{
#region Fields

public int MakeOpeningExceptionCount { get; set; }
public int MakeFloorExceptionCount { get; set; }

private FloorType defaultFloorType;

#endregion

#region Startup and Shutdown

private void Module_Startup(object sender, EventArgs e)
{

}

private void Module_Shutdown(object sender, EventArgs e)
{

}

#endregion

#region VSTA generated code
private void InternalStartup()
{
this.Startup += new System.EventHandler(Module_Startup);
this.Shutdown += new System.EventHandler(Module_Shutdown);
}
#endregion

public void MakeFloorsForSelectedRooms()
{
Transaction transaction = new Transaction(ActiveUIDocument.Document);
if (transaction.Start("Create new floors") == TransactionStatus.Started)
{

defaultFloorType = GetDefaultFloorType();
Autodesk.Revit.UI.Selection.SelElementSet collection = ActiveUIDocument.Selection.Elements;
foreach (Autodesk.Revit.DB.Element element in collection)
{
if (element is DBArch.Room)
{
MakeFloorForRoom(element as DBArch.Room);
}
}
transaction.Commit();
}

}

#region Floor Making

private void MakeFloorForRoom(DBArch.Room room)
{
SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
IList<IList<BoundarySegment>> bounds = room.GetBoundarySegments(options);
CurveArrArray boundsArray = new CurveArrArray();
Floor floor = null;
bool outerLoop = true;
foreach (IList<BoundarySegment> loop in bounds)
{
CurveArray profile = new CurveArray();
foreach (BoundarySegment segment in loop)
{
Curve curve = segment.Curve;
profile.Append(curve);
}
if (outerLoop)
{
FloorType newFloorType;
string newFloorTypeName = GetFloorTypeName(room);
if ("" == newFloorTypeName)
{ newFloorType = defaultFloorType; }
else { newFloorType = GetOrMakeFloorType(newFloorTypeName); }

floor = MakeAFloor(profile, room.Level, room.BaseOffset, newFloorType);
outerLoop = false;
}
else
{
if (null != floor) { MakeAnOpening(floor, profile); }
}
}
}

private string GetFloorTypeName(DBArch.Room room)
{
string newFloorTypeName = GetProperty(room, BuiltInParameter.ROOM_FINISH_FLOOR);
if (null == newFloorTypeName) { newFloorTypeName = ""; }
return newFloorTypeName;
}

private Floor MakeAFloor(CurveArray loop, Level level, double offset, FloorType newFloorType)
{
Floor newFloor = null;

try
{
newFloor = ActiveUIDocument.Document.Create.NewFloor(loop, newFloorType, level, false);

}
catch (Exception ex)
{
MakeFloorExceptionCount++;
Debug.WriteLine("Exception: " + ex.Message);
//throw;
}

if (null != newFloor)
{
BuiltInParameter paraIndex = BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM;
Parameter parameter = newFloor.get_Parameter(paraIndex);
parameter.Set(offset + GetFloorThickness(defaultFloorType));
}
return newFloor;
}

private void MakeAnOpening(Floor floor, CurveArray loop)
{
try
{
Opening newOpening = ActiveUIDocument.Document.Create.NewOpening(floor, loop, true);
}
catch (Exception ex)
{
MakeOpeningExceptionCount++;
Debug.WriteLine("Exception: " + ex.Message);
//throw;
}

}

#endregion

#region Floor Types


private FloorType GetDefaultFloorType()
{
string newFloorTypeName = GetUniqueFloorTypeName();
return MakeNewFloorType(newFloorTypeName);
}

private FloorType MakeNewFloorType(string newFloorTypeName)
{
FloorType newFloortype = (FloorType)GetFloorType().Duplicate(newFloorTypeName);
return newFloortype;
}

private string GetUniqueFloorTypeName()
{
int index = 0;
string proposedFloorTypeName;
do
{
index++;
proposedFloorTypeName = "DefaultFloorType" + index.ToString();
} while (!IsUniqueName(proposedFloorTypeName));
return proposedFloorTypeName;
}

private bool IsUniqueName(string proposedFloorTypeName)
{
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
bool isUniqueName = true;
foreach (FloorType floorType in floorTypes)
{
if (floorType.Name == proposedFloorTypeName) { isUniqueName = false; }
}
return isUniqueName;
}

private FloorType GetFloorType()
{
FloorType myFloorType = null;
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
foreach (FloorType floorType in floorTypes)
{
myFloorType = floorType;
}
if (null == myFloorType)
{ throw new Exception("No FloorTypes in Document"); }
return myFloorType;
}

private FloorType GetOrMakeFloorType(string floorTypeName)
{
FloorType myFloorType = null;
FloorTypeSet floorTypes = ActiveUIDocument.Document.FloorTypes;
foreach (FloorType floorType in floorTypes)
{
if (floorTypeName == floorType.Name)
{ myFloorType = floorType; }
}
if (null == myFloorType)
{ myFloorType = MakeNewFloorType(floorTypeName); }
return myFloorType;
}



private double GetFloorThickness(FloorType floorType)
{
//return 1; //in feet;
double floorThickness = 0;
CompoundStructure comStruct = floorType.GetCompoundStructure();
foreach (CompoundStructureLayer structLayer in comStruct.GetLayers())
{ floorThickness += structLayer.Width; }

return floorThickness;
}


#endregion

#region Utility

public String GetProperty(DBArch.Room room, BuiltInParameter paramEnum)
{
String propertyValue = null; //the value of parameter

//get the parameter via the parameterId
Parameter param = room.get_Parameter(paramEnum);
//get the parameter's storage type
StorageType storageType = param.StorageType;
switch (storageType)
{
case StorageType.Integer:
int iVal = param.AsInteger();
propertyValue = iVal.ToString();
break;
case StorageType.String:
String stringVal = param.AsString();
propertyValue = stringVal;
break;
case StorageType.Double:
Double dVal = param.AsDouble();
dVal = Math.Round(dVal, 2);
propertyValue = dVal.ToString();
break;
default:
break;
}
return propertyValue;
}

#endregion
}
}