Would you like to store data from any file in your RVT?
An easy way to create multiple drafting views with imported images for every page of a PDF?
The ability to embed any file (from Excel, PDF, Word or anything else) in your RVT?
Would you like to store data from any file in your RVT?
An easy way to create multiple drafting views with imported images for every page of a PDF?
The ability to embed any file (from Excel, PDF, Word or anything else) in your RVT?
Have you ever gotten lost while searching and searching in a complex family for a labeled dimension? You know there is a dimension somewhere labeled “Jamb Width” or “Head Height” or whatever but you don’t know in what view that dimension lives?
Here is a macro that will find a dimension based on the name of its labeled parameter, open the dimension’s view, select the dimension and zoom to it.
public void findLabelView()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
foreach (Dimension dim in new FilteredElementCollector(doc)
.OfClass(typeof(Dimension))
.Cast<Dimension>())
{
FamilyParameter fp = null;
try
{
fp = dim.FamilyLabel;
}
catch
{
continue;
}
if (fp != null && fp.Definition.Name == "Jamb Width")
{
uidoc.Selection.SetElementIds(new List<ElementId> { dim.Id});
View ownerview = doc.GetElement(dim.OwnerViewId) as View;
uidoc.ActiveView = ownerview;
UIView uiview = uidoc.GetOpenUIViews().FirstOrDefault(q => q.ViewId == dim.OwnerViewId);
BoundingBoxXYZ bbox = dim.get_BoundingBox(ownerview);
uiview.ZoomAndCenterRectangle(bbox.Min, bbox.Max);
break;
}
}
}
The new 2019.1 release has added API for attached detail groups! Thanks Autodesk!
For example:
On a technical note, GetAvailableAttachedDetailGroupTypeIds returns a C# “set”, a data structure that can store certain values, without any particular order, and no repeated values. A couple good resources to learn more about sets are:
public void GetAvailableAttachedDetailGroupTypeIds()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
Group modelGroup = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select Model Group")) as Group;
HashSet attachedDetailIds = modelGroup.GetAvailableAttachedDetailGroupTypeIds() as HashSet;
TaskDialog.Show("Attached detail groups", string.Join(Environment.NewLine, attachedDetailIds.Select(q => doc.GetElement(q).Name)));
}
public void HideDetailGroup()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
Group modelGroup = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select Model Group")) as Group;
Group detailGroup = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select detail group to hide")) as Group;
using (Transaction t = new Transaction(doc, "hide detail groups"))
{
t.Start();
modelGroup.HideAttachedDetailGroups(doc.ActiveView, detailGroup.GroupType.Id);
t.Commit();
}
}
public void ShowDetailGroup()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
Group modelGroup = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select Model Group")) as Group;
Element detailGroupType = new FilteredElementCollector(doc).OfClass(typeof(GroupType))
.OfCategory(BuiltInCategory.OST_IOSAttachedDetailGroups)
.FirstOrDefault(q => q.Name == "Door Dimensions A6");
using (Transaction t = new Transaction(doc, "show detail group"))
{
t.Start();
modelGroup.ShowAttachedDetailGroups(doc.ActiveView, detailGroupType.Id);
t.Commit();
}
}
It would be nice if we could right click on a specific linetype and “select all instances” in view instead of having to select everything and then filter them out.
public void selectAllLineStyle()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
DetailLine line = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select detail line")) as DetailLine;
// if you want only the lines in the current view, add "doc.ActiveView.Id" to the FilteredElementCollector
List<CurveElement> lines = new FilteredElementCollector(doc)
.OfClass(typeof(CurveElement))
.OfCategory(BuiltInCategory.OST_Lines)
.Where(q => q is DetailLine)
.Cast<CurveElement>()
.Where(q => q.LineStyle.Id == line.LineStyle.Id)
.ToList();
uidoc.Selection.SetElementIds(lines.Select(q => q.Id).ToList());
}
Isolate command should fade and lock all other objects instead of hiding them completely. What use can i find in seeing an object out of context? I can’t compare it visually to anything. If i want to modify it and can’t relate any modification to any context (because it’s hidden)
public void isolateFade()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
ElementId elementIdToIsolate = null;
try
{
elementIdToIsolate = uidoc.Selection.PickObject(ObjectType.Element, "Select element or ESC to reset the view").ElementId;
}
catch
{
}
OverrideGraphicSettings ogsFade = new OverrideGraphicSettings();
ogsFade.SetSurfaceTransparency(50);
ogsFade.SetSurfaceForegroundPatternVisible(false);
ogsFade.SetSurfaceBackgroundPatternVisible(false);
ogsFade.SetHalftone(true);
OverrideGraphicSettings ogsIsolate = new OverrideGraphicSettings();
ogsIsolate.SetSurfaceTransparency(0);
ogsIsolate.SetSurfaceForegroundPatternVisible(true);
ogsIsolate.SetSurfaceBackgroundPatternVisible(true);
ogsIsolate.SetHalftone(false);
using (Transaction t = new Transaction(doc, "Isolate with Fade"))
{
t.Start();
foreach (Element e in new FilteredElementCollector(doc, doc.ActiveView.Id).WhereElementIsNotElementType())
{
if (e.Id == elementIdToIsolate || elementIdToIsolate == null)
doc.ActiveView.SetElementOverrides(e.Id, ogsIsolate);
else
doc.ActiveView.SetElementOverrides(e.Id, ogsFade);
}
t.Commit();
}
}
Why doesn’t Revit check spelling in schedules? No good reason that I know of, so here’s an investigation into how it might be done with the API. There’s also some discussion of JSON deserialization and communicating with the Bing Spell Check web API.
public void scheduleSpellCheck()
{
Document doc = this.ActiveUIDocument.Document;
ViewSchedule viewSchedule = doc.ActiveView as ViewSchedule;
ViewSheet viewSheet = doc.ActiveView as ViewSheet;
if (viewSchedule == null && viewSheet == null)
{
TaskDialog.Show("Error", "Active a schedule or sheet before running this command");
return;
}
SectionType sectionType = SectionType.Body;
List<string> scheduleText = new List<string>();
List schedules = new List();
if (viewSheet != null)
{
schedules = new FilteredElementCollector(doc).OfClass(typeof(ScheduleSheetInstance)).Cast()
.Where(q => q.OwnerViewId == viewSheet.Id)
.Select(q => doc.GetElement(q.ScheduleId) as ViewSchedule)
.Where(q => !q.IsTitleblockRevisionSchedule).ToList();
}
else
{
schedules.Add(viewSchedule);
}
foreach (ViewSchedule vs in schedules)
{
scheduleText.Add(vs.Name);
for (int i = 0; i < getRowColumnCount(vs, sectionType, true); i++)
{
for (int j = 0; j < getRowColumnCount(vs, sectionType, false); j++)
{
try
{
scheduleText.Add(vs.GetCellText(sectionType, i, j));
}
catch (Exception ex)
{
}
}
}
SpellCheck(string.Join(" - ", scheduleText));
}
}
static async void SpellCheck(string text)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", key);
// The following headers are optional, but it is recommended they be treated as required.
// These headers help the service return more accurate results.
//client.DefaultRequestHeaders.Add("X-Search-Location", ClientLocation);
//client.DefaultRequestHeaders.Add("X-MSEdge-ClientID", ClientId);
//client.DefaultRequestHeaders.Add("X-MSEdge-ClientIP", ClientIp);
HttpResponseMessage response = new HttpResponseMessage();
string uri = host + path + params_;
List<KeyValuePair<string, string>> values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("text", text));
using (FormUrlEncodedContent content = new FormUrlEncodedContent(values))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
response = await client.PostAsync(uri, content);
}
string client_id;
IEnumerable<string> header_values = new List<string>();
if (response.Headers.TryGetValues("X-MSEdge-ClientID", out header_values))
{
client_id = header_values.First();
Console.WriteLine("Client ID: " + client_id);
}
string results = await response.Content.ReadAsStringAsync();
TaskDialog.Show("JSON", JsonPrettyPrint(results));
FlaggedTokenList flaggedTokenList = new JavaScriptSerializer().Deserialize(results);
List<string> output = new List<string>();
foreach (FlaggedToken flaggedToken in flaggedTokenList.flaggedTokens)
{
List<string> suggestions = flaggedToken.suggestions.Select(q => q.suggestion.ToString()).ToList();
if (suggestions.Count == 0)
continue;
output.Add(flaggedToken.token + " : " + string.Join(",", suggestions));
}
TaskDialog.Show("output", string.Join(Environment.NewLine, output));
}
static string host = "https://api.cognitive.microsoft.com";
static string path = "/bing/v7.0/spellcheck?";
// For a list of available markets, go to:
// https://docs.microsoft.com/rest/api/cognitiveservices/bing-autosuggest-api-v7-reference#market-codes
static string params_ = "mkt=en-US&mode=proof";
// NOTE: Replace this example key with a valid subscription key.
static string key = "d46d0d618a7343dabe8a4355e9d30187";
class FlaggedTokenList
{
public List flaggedTokens { get; set; }
}
class FlaggedToken
{
public string token { get; set; }
public List suggestions { get; set; }
}
class Suggestion
{
public string suggestion { get; set; }
public double score { get; set; }
}
private int getRowColumnCount(ViewSchedule view, SectionType sectionType, bool countRows)
{
int ctr = 1;
// loop over the columns of the schedule
while (true)
{
try // GetCellText will throw an ArgumentException is the cell does not exist
{
if (countRows)
view.GetCellText(sectionType, ctr, 1);
else
view.GetCellText(sectionType, 1, ctr);
ctr++;
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
return ctr;
}
}
}
static string JsonPrettyPrint(string json)
{
if (string.IsNullOrEmpty(json))
{
return string.Empty;
}
json = json.Replace(Environment.NewLine, "").Replace("\t", "");
StringBuilder sb = new StringBuilder();
bool quote = false;
bool ignore = false;
char last = ' ';
int offset = 0;
int indentLength = 3;
foreach (char ch in json)
{
switch (ch)
{
case '"':
if (!ignore) quote = !quote;
break;
case '\\':
if (quote && last != '\\') ignore = true;
break;
}
if (quote)
{
sb.Append(ch);
if (last == '\\' && ignore) ignore = false;
}
else
{
switch (ch)
{
case '{':
case '[':
sb.Append(ch);
sb.Append(Environment.NewLine);
sb.Append(new string(' ', ++offset * indentLength));
break;
case '}':
case ']':
sb.Append(Environment.NewLine);
sb.Append(new string(' ', --offset * indentLength));
sb.Append(ch);
break;
case ',':
sb.Append(ch);
sb.Append(Environment.NewLine);
sb.Append(new string(' ', offset * indentLength));
break;
case ':':
sb.Append(ch);
sb.Append(' ');
break;
default:
if (quote || ch != ' ') sb.Append(ch);
break;
}
}
last = ch;
}
return sb.ToString().Trim();
}
The problem
public void levelRemap()
{
Document doc = this.ActiveUIDocument.Document;
UIDocument uidoc = this.ActiveUIDocument;
Level level = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select level")) as Level;
Level levelBelow = new FilteredElementCollector(doc)
.OfClass(typeof(Level))
.Cast<Level>()
.OrderBy(q => q.Elevation)
.Where(q => q.Elevation <= level.Elevation)
.FirstOrDefault();
if (levelBelow == null)
{
TaskDialog.Show("Error", "No level below " + level.Elevation);
return;
}
List<string> paramsToAdjust = new List<string> { "Base Offset", "Sill Height", "Top Offset"};
List<Element> elements = new FilteredElementCollector(doc).WherePasses(new ElementLevelFilter(level.Id)).ToList();
using (Transaction t = new Transaction(doc, "Level Remap"))
{
t.Start();
foreach (Element e in elements)
{
foreach (Parameter p in e.Parameters)
{
if (p.StorageType != StorageType.ElementId || p.IsReadOnly)
continue;
if (p.AsElementId() != level.Id)
continue;
double elevationDiff = level.Elevation - levelBelow.Elevation;
p.Set(levelBelow.Id);
foreach (string paramName in paramsToAdjust)
{
Parameter pToAdjust = e.LookupParameter(paramName);
if (pToAdjust != null && !pToAdjust.IsReadOnly)
pToAdjust.Set(pToAdjust.AsDouble() + elevationDiff);
}
}
}
t.Commit();
}
}
The problem:
public void alignWallToCurvedGrid()
{
UIDocument uidoc = this.ActiveUIDocument;
Document doc = this.ActiveUIDocument.Document;
Wall wall = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select wall")) as Wall;
Grid grid = doc.GetElement(uidoc.Selection.PickObject(ObjectType.Element, "Select grid")) as Grid;
Curve gridCurve = grid.Curve;
Line wallLine = (((LocationCurve)wall.Location).Curve as Line);
XYZ wallDirection = wallLine.Direction;
XYZ wallPerpendicular = XYZ.BasisZ.CrossProduct(wallDirection);
XYZ intersection0 = intersect(wallLine.GetEndPoint(0), wallPerpendicular, gridCurve);
XYZ intersection1 = intersect(wallLine.GetEndPoint(1), wallPerpendicular, gridCurve);
XYZ intersectionMid = intersect(wallLine.Evaluate(0.5, true), wallPerpendicular, gridCurve);
Arc arc = Arc.Create(intersection0, intersection1, intersectionMid);
List wallChildren = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.Cast()
.Where(q => q.Host.Id == wall.Id).ToList();
using (Transaction t = new Transaction(doc, "Wall to Curve"))
{
t.Start();
Wall newWall = Wall.Create(doc, arc, wall.LevelId, false);
newWall.WallType = doc.GetElement(wall.GetTypeId()) as WallType;
foreach (FamilyInstance fi in wallChildren)
{
XYZ fiPoint = (fi.Location as LocationPoint).Point;
XYZ fiIntersection = intersect(fiPoint, wallPerpendicular, gridCurve);
if (fiIntersection != null)
{
FamilyInstance fiNew = doc.Create.NewFamilyInstance(fiIntersection,
fi.Symbol,
newWall,
doc.GetElement(fi.LevelId) as Level,
StructuralType.NonStructural);
if (fi.FacingFlipped)
fiNew.flipFacing();
if (fi.HandFlipped)
fiNew.flipHand();
}
}
doc.Delete(wall.Id);
t.Commit();
}
}
private XYZ intersect(XYZ point, XYZ direction, Curve curve)
{
Line unbound = Line.CreateUnbound(new XYZ(point.X, point.Y, curve.GetEndPoint(0).Z), direction);
IntersectionResultArray ira = null;
unbound.Intersect(curve, out ira);
if (ira == null)
{
TaskDialog td = new TaskDialog("Error");
td.MainInstruction = "no intersection";
td.MainContent = point.ToString() + Environment.NewLine + direction.ToString();
td.Show();
return null;
}
IntersectionResult ir = ira.Cast().First();
return ir.XYZPoint;
}
This sample shows how to “Remove warning/pop-up when modifying type parameter values within a schedule” using the DialogBoxShowing
event.
Please remove the warning/pop-up that appears when editing scheduled fields that are tied to type parameters. This is EXTREMELY annoying when entering data for our MEP schedules. I.E. Plumbing Fixtures, Air Devices, Lighting Fixtures, etc.
The popup reads:
“This change will be applied to all elements of type _______.”
In instances where we are filling out a schedule, we are forced to enter our data, press enter or click out of the cell, and then respond to this popup. EVERY TIME
public void registerWarningSuppresion()
{
Document doc = this.ActiveUIDocument.Document;
Application app = doc.Application;
UIApplication uiapp = new UIApplication(app);
uiapp.DialogBoxShowing += new EventHandler(dismissTaskDialog);
}
private void dismissTaskDialog(object sender, DialogBoxShowingEventArgs args)
{
TaskDialogShowingEventArgs e = args as TaskDialogShowingEventArgs;
if (e == null)
return;
if (e.Message.StartsWith("This change will be applied to all elements of type"))
{
e.OverrideResult((int)TaskDialogCommonButtons.Ok);
}
}