Saturday, 8 October 2011

New AUGI Wish List system

A quick review of the new Wish List system, with pros, cons and comment.

AUGI President David Harrison’s October Letter from the President introduces AUGI’s new Wish List system. I went to have a look.

Pros: New ranking system; Wishes stay in the system until they’re added to the software.

Cons: Hard to get an overview; No way to see current activity; Older wishes’ discussions are locked; No feedback on ‘wish granted’ flagging; No way to flag/merge duplicate entries; No way to see wishes’ current ranking; No ‘Hall of Fame’; Ranking separated from discussion.

So, if you go to the Wish List home page, you’ll find three buttons:

  1. My Wishes, which will take you to a list of wishes that you have submitted.
  2. Add a New Wish, which allows you to search the existing wish list to make sure your idea is new, and then submit your wish to the list.
  3. Rank Wishes, which takes you to an individual wish and allows you to rate it according to how much you need it.

Now, it’s great that Autodesk are keen to get feedback from the users, and it’s great that AUGI facilitate that and are seeking to make the process better. I’m just not sure that the Wish List system is quite there in its current form.

First, it’s hard to get an overview: How many wishes are in the system? What are the latest wishes? Who has commented and rated recently? For this sort of crowdsourced activity to take off, you need some feedback: Just basically ‘What’s going on?’

Second, there are some niggles with the interface:

One of the virtues of the new system is that wishes stay in the system until they’ve been incorporated into the software. So the wish list includes all the wishes from way back. Unfortunately, for older wishes the Discuss this Wish button takes you to the original, archived AUGI forum post. Which is locked and can’t be commented on.

Since the wish list includes all the old wishes from way back, many of them have been incorporated into the software already. There is a ‘This wish has already been granted’ button, so you can flag the wish, but the flag doesn’t appear on the wish’s page. Maybe a flag will be added when the admins have reviewed your comment, but maybe it would be more ‘crowdsourcey’ to flag the wish immediately and add the comment to the wish’s discussion page. Then others could agree/disagree, argue the finer points, etc. Incidentally, David’s article says that there is a ‘Hall of Fame’ for wishes that have been granted, but I couldn’t see any reference to it on the site.

Similarly, since the list includes all the old wishes, and the old system was that you had to resubmit wishes every year, the list now has lots of duplicate wishes. There doesn’t appear to be a way of flagging these duplicates.

Oh, and since there’s a ranking system, it would be nice to see what a wish’s current rank is, who has ranked the wish, and so on. There doesn’t seem to be a way for that to happen.

Finally, it would be nice to have a closer linkage between ranking and discussion. Currently, they’re on separate pages: There’s a link from ranking page to discussion page, but not vice versa. But I’m not entirely sure what the new ranking system gains over the forum system’s standard ‘poll’ feature.

To sum up, I’m glad that AUGI have taken the trouble to improve the wish list system, and keeping wishes in the system ‘for ever’ is a definite improvement. I’m just not sure that the current site lives up to expectations.

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
}
}

Tuesday, 14 June 2011

X Y Z

It’s curious that Revit hides its coordinate system. When you look at the properties of a line in AutoCAD, the X,Y,Z coordinates of its endpoints are right there.

But in Revit, objects don’t know their own coordinates. So you can’t tag or schedule them. And you can’t use coordinates to drive object properties (other than via the API).