Hi there,
First of all I haven't found an answer to my question in an existing post, please feel free to redirect me there if you know one.
I'm currently coding a plugin for Revit 2015 that insert a family of my choice inside an existing project.
I want to insert the family directly on a face of an existing 3D item. But depending on the situation I must insert it on different type of faces, such as planar faces or cylindrical. Problem is, I can't get my code to function on either, I don't know how to check for the type of face prior to my operations.
That's my code for cylindrical faces
Reference r = uidoc.Selection.PickObject(ObjectType.Face, "Please pick a point on a face for family instance insertion"); Element e = doc.GetElement(r.ElementId); GeometryObject obj = e.GetGeometryObjectFromReference(r); //PlanarFace face = obj as PlanarFace; CylindricalFace face = obj as CylindricalFace; XYZ p = r.GlobalPoint; XYZ v = face.Axis.CrossProduct(XYZ.BasisZ); if (v.IsZeroLength()) { v = face.Axis.CrossProduct(XYZ.BasisX); } doc.Create.NewFamilyInstance(r, p, v, symbol);
And that's my code for planar faces
Reference r = uidoc.Selection.PickObject(ObjectType.Face, "Please pick a point on a face for family instance insertion"); Element e = doc.GetElement(r.ElementId); GeometryObject obj = e.GetGeometryObjectFromReference(r); PlanarFace face = obj as PlanarFace; //CylindricalFace face = obj as CylindricalFace; XYZ p = r.GlobalPoint; XYZ v = face.Normal.CrossProduct(XYZ.BasisZ); if (v.IsZeroLength()) { v = face.Normal.CrossProduct(XYZ.BasisX); } doc.Create.NewFamilyInstance(r, p, v, symbol);
Almost nothing changes except for the declaration of v with the change : Axis/Normal
I've been using the code from Jeremy Tammik thebuildingcoder, I'm fairly new to programming and totally new to C#, and I've never used any API before.
I also have other questions :
Right now I'm typing the path and the name of the family item that I want to insert directly inside my code. Ideally, I'd like the user to pick the family when he starts the plugin, but I have no idea how to do that.
Last thing I'd like to know is, after my family is inserted, how do I run automatically an interference check to see if there is any collision. The plugin is aimed to people totally new to geomatics, that have never used any software like autocad, I want them to push the least buttons possible to achivieve their goal.
Thank you very much in advance for your time.
Jordi
Solved! Go to Solution.
Solved by jeremytammik. Go to Solution.
Dear Jordi,
Happy Valentine's Day!
🙂
That sounds like a pretty cool project.
Congratulations on getting so far with it already.
I answered your queries pretty extensively on The Building Coder:
http://thebuildingcoder.typepad.com/blog/2015/02/determining-the-face-tangent-at-a-picked-point.html
I hope this helps.
Good luck!
Cheers,
Jeremy
Good morning,
i writed this code to set the numbering automatically, but in each time I re-execute my code it begin the numbering from 0 instead of lastassignednumber.
so where is the error ?
Thanks in advance,
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.ExtensibleStorage;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Number
{
[Transaction(TransactionMode.Manual)]
public class Code : IExternalCommand
{
private const string SchemaGUID = "633FEF1C-4DF3-4C07-A812-D392C8015194";
private const string MinRangePropertyName = "MinRange";
private const string MaxRangePropertyName = "MaxRange";
private const string LastAssignedNumberPropertyName = "LastAssignedNumber";
private int currentNumber;
private int minRange;
private int maxRange;
private ICollection<ElementId> addedElementIds = new List<ElementId>();
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIApplication uiApp = commandData.Application;
UIDocument uiDoc = uiApp.ActiveUIDocument;
Document doc = uiDoc.Document;
addedElementIds = GetWallElement(doc);
if (!RetrieveRangeValues(doc))
{
// Range values not found, prompt the user to enter the range
using (Window window = new Window(doc))
{
window.ShowDialog();
minRange = window.minRange;
maxRange = window.maxRange;
currentNumber = minRange;
StoreRangeValues(doc);
}
}
else
{
currentNumber = RetrieveLastAssignedNumber(doc);
if (currentNumber < minRange || currentNumber > maxRange)
{
// Handle the case where the last assigned number is outside the specified range
// You can display an error message or take appropriate action here
}
TaskDialog.Show("Range", "Your entered range is between: \n" + minRange.ToString() + " - " + maxRange.ToString() + "\n" +
"Current Number: " + currentNumber);
}
// Subscribe to the Idling event
uiApp.Idling += HandleIdlingEvent;
return Result.Succeeded;
}
private void HandleIdlingEvent(object sender, IdlingEventArgs e)
{
UIApplication uiApp = sender as UIApplication;
Document doc = uiApp.ActiveUIDocument.Document;
try
{
// Check for newly added walls in the document
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfCategory(BuiltInCategory.OST_Walls);
ICollection<ElementId> currentElementIds = collector.ToElementIds();
// Find the added wall element ids by comparing the current element ids with the previously stored ids
IEnumerable<ElementId> newElementIds = currentElementIds.Except(addedElementIds);
if (newElementIds.Any())
{
using (Transaction trans = new Transaction(doc, "Assign Number to Walls"))
{
trans.Start();
foreach (ElementId elementId in newElementIds)
{
Element element = doc.GetElement(elementId);
if (element is Wall wall)
{
// Get the custom "Number" parameter
Parameter parameter = wall.LookupParameter("Number");
if (parameter != null && parameter.StorageType == StorageType.String)
{
parameter.Set(currentNumber.ToString());
currentNumber++;
}
}
}
trans.Commit();
// Update the added element ids for the next idling event
addedElementIds = currentElementIds;
// Store the last assigned number
StoreLastAssignedNumber(doc, currentNumber);
}
}
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
}
}
public ICollection<ElementId> GetWallElement(Document doc)
{
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfCategory(BuiltInCategory.OST_Walls);
return collector.ToElementIds().ToList();
}
private bool RetrieveRangeValues(Document doc)
{
try
{
Schema schema = Schema.Lookup(new Guid(SchemaGUID));
if (schema != null)
{
Entity entity = doc.ProjectInformation.GetEntity(schema);
if (entity.IsValid())
{
minRange = entity.Get<int>(schema.GetField(MinRangePropertyName));
maxRange = entity.Get<int>(schema.GetField(MaxRangePropertyName));
currentNumber = entity.Get<int>(schema.GetField(LastAssignedNumberPropertyName));
currentNumber = RetrieveLastAssignedNumber(doc); // Retrieve the last assigned number
return true;
}
}
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
}
return false;
}
private int RetrieveLastAssignedNumber(Document doc)
{
try
{
Schema schema = Schema.Lookup(new Guid(SchemaGUID));
if (schema != null)
{
Entity entity = doc.ProjectInformation.GetEntity(schema);
if (entity.IsValid())
{
return entity.Get<int>(schema.GetField(LastAssignedNumberPropertyName));
}
}
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
}
// Return a default value if the last assigned number cannot be retrieved
return 0;
}
private void StoreRangeValues(Document doc)
{
try
{
Schema schema = Schema.Lookup(new Guid(SchemaGUID));
if (schema == null)
{
SchemaBuilder schemaBuilder = new SchemaBuilder(new Guid(SchemaGUID));
schemaBuilder.SetSchemaName("NumberSchema");
schemaBuilder.SetReadAccessLevel(AccessLevel.Public);
schemaBuilder.SetWriteAccessLevel(AccessLevel.Public);
FieldBuilder minRangeField = schemaBuilder.AddSimpleField(MinRangePropertyName, typeof(int));
FieldBuilder maxRangeField = schemaBuilder.AddSimpleField(MaxRangePropertyName, typeof(int));
FieldBuilder lastAssignedNumberField = schemaBuilder.AddSimpleField(LastAssignedNumberPropertyName, typeof(int));
schema = schemaBuilder.Finish();
}
using (Transaction trans = new Transaction(doc, "Store Range Values"))
{
trans.Start();
Entity entity = new Entity(schema);
entity.Set<int>(schema.GetField(MinRangePropertyName), minRange);
entity.Set<int>(schema.GetField(MaxRangePropertyName), maxRange);
doc.ProjectInformation.SetEntity(entity);
trans.Commit();
}
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
}
}
private void StoreLastAssignedNumber(Document doc, int number)
{
try
{
Schema schema = Schema.Lookup(new Guid(SchemaGUID));
if (schema != null)
{
Entity entity = doc.ProjectInformation.GetEntity(schema);
if (entity.IsValid())
{
entity.Set<int>(schema.GetField(LastAssignedNumberPropertyName), number);
}
else
{
entity = new Entity(schema);
entity.Set<int>(schema.GetField(LastAssignedNumberPropertyName), number);
doc.ProjectInformation.SetEntity(entity);
}
}
}
catch (Exception ex)
{
TaskDialog.Show("Error", ex.Message);
}
}
}
}
TLDR; but:
As far as I remember, my SetoutPoints performs some pretty nifty, persistent and customisable automatic numbering:
https://github.com/jeremytammik/SetoutPoints
Can't find what you're looking for? Ask the community or share your knowledge.