Hi All,
I need to convert large numbers of filled regions (with white solid pattern) to masking regions and maintain any linestyle settings. As of the 2024 API, I can do this. What I can't do is maintain any dimensions that were on the filled region, which is deleted and replaced with a masking region.
A dimension to a filled region shows two References in its Reference Array. The Reference to the filled region is to the whole element Id, not to a Curve or Detail Line that helps define the filled region.
How can I find which part of the filled region a dimension is referenced to - so I can recreate it with my new masking region?
___________________________________________________________________________________
I've hunted the Revit Lookup for dependant elements on all the lines in the filled region etc.
I think I've found the helpful previous answers such as:
https://thebuildingcoder.typepad.com/blog/2019/08/auto-dimension-filled-region-boundary.html
Solved! Go to Solution.
Solved by PeterKnight. Go to Solution.
How about aiming for the simplest possible solution, and avoiding all hard and tricky tasks?
In this case, it sounds to me as if it might be simpler if you just hide the existing filled regions instead of deleting them. Then, maybe you can retain the existing dimensioning.
KISS...
Thanks Jeremy,
Unless I'm mistaken, hiding an element in Revit also hides tags & dimensions that depend on it? Not sure I'm willing to take the second "s" 🙂
I'm also against hiding things long term, it's messy and over years in a project all those hidden elements become problematic. In this case 100s of sheets being exported to dwg and tiny mistakes can cost a day!
I have solved the issue another way:
>Get any dims for the filled region
>Create matching masking region in the same place
>Hide the filled region*
>if dims count >0, ElementTransformUtils.CopyElements - copy dims to same location
>Delete filled region.
*without hiding the region, the copied dims would host themselves to the older element, not the masking region - maybe because it's all in the same transaction?
Yes, I fully agree with your doubts, and they make perfect sense. The second 's' has various different interpretations, so one can take it or leave it 🙂
Congratulations on finding a very convincing and robust solution to this task. I am not completely clear on the details. For instance, which exact elements are included in your copy and paste operation? Would you mind sharing a code snippet or two showing exactly what you do? I cannot judge how likely it is that someone will run into the same problem, but it sounds like such a useful, powerful and clean approach that it is certainly worth sharing for posterity. Thank you!
Thank you Jeremy. My solution is provided below - working but not optimised or set to handle any errors yet. It relies on Revit API 2024 for CreateMaskingRegion(). Apologies for the newbie code - it's about the best I can do so far 🙂
A quick note on why I needed this which may help others. We have to export 100's of sheets to DWG, and have all elements coloured by layer. In Revit, we often have filled regions with a solid white hatch being used to hide all sorts of things. In AutoCAD, these appear with a solid hatch and frequently mask out linework of the same layer colour. Masking regions export without creating a solid hatch, and graphically match across Revit and DWG. The aim of the script is to pick up any dimensions and linestyle changes so in Revit it looks exactly the same when converted, and doesn't cause issues in the DWG exports.
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using Autodesk.Revit.UI;
using System.Collections.Generic;
using System.Linq;
using Document = Autodesk.Revit.DB.Document;
using System.Collections.ObjectModel;
using System;
using Autodesk.Revit.DB.Structure;
namespace YourNameSpace
{
[TransactionAttribute(TransactionMode.Manual)]
public class ConvertRegion : IExternalCommand
{
internal UIDocument UIDoc { get; set; }
internal Document Doc { get; set; }
internal IList<CurveLoop> OldCurves { get; set; }
internal FilledRegion OldRegion { get; set; }
internal IList<LineData> OldLineData { get; set; }
internal FilledRegion MRegion { get; set;}
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDoc = commandData.Application.ActiveUIDocument;
Doc = UIDoc.Document;
GetExistingCurves(); //Gets user to pick a filled region, then populates properties OldRegion and OldCurves
ICollection<ElementId> oldDims = CheckforDimensions(); //Gets any dimensions currently dependant on the region.
GetOldLineData(); //Populates OldLineData with LineData class - with X and Y start and end points of lines.
IList<LineData> projectDetailLines = GetDetailLineData(); //all the detail lines currently in the project as LineData objects with start and end coordinates
IList<LineData> matchedDetailLines = GetMatchingDetailLines(projectDetailLines); //matches any current detail lines geometrically to curves of filled region.
GetGraphicStyles(matchedDetailLines); //For matched detail lines, the graphic style is stored against the matched region curve.
//Transaction 1
CreateNewMaskingRegion(); //Run transaction to create new masking region
//The new masking regions detail lines need to be found, and the previously matched ones removed from the list.
//The graphic style stored in the old matched lines is copied across to the new matched lines.
IList<LineData> newMatchedDetailLines = GetNewDetailLines(matchedDetailLines);
//Transaction 2
UpdateGraphicsAndDims(newMatchedDetailLines, oldDims); //Apply and previous graphic styles from the filled region to the new masking region. Copy paste any dimension to new masking region.
return Result.Succeeded;
}
private void GetExistingCurves()
{
Selection selection = UIDoc.Selection;
Reference picked = selection.PickObject(ObjectType.Element);
if (picked != null)
{
OldRegion = Doc.GetElement(picked) as FilledRegion;
OldCurves = OldRegion.GetBoundaries();
}
}
private ICollection<ElementId> CheckforDimensions()
{
ICollection<ElementId> ids = new Collection<ElementId>();
if (OldRegion != null)
{
ElementClassFilter filter = new ElementClassFilter(typeof(Dimension));
IList<ElementId> elems = OldRegion.GetDependentElements(filter);
foreach (ElementId i in elems)
{
var test = Doc.GetElement(i);
if (test.Category.BuiltInCategory == BuiltInCategory.OST_Dimensions)
{
ids.Add(i);
}
}
}
return ids;
}
private void GetOldLineData()
{
OldLineData = new List<LineData>();
CurveLoopIterator itor = OldCurves.FirstOrDefault().GetCurveLoopIterator();
while (itor.MoveNext())
{
Curve curve = itor.Current;
OldLineData.Add(GetLinePoints(curve)); //populates LineData object with start and end point coords
}
}
private List<LineData> GetDetailLineData()
{
List<LineData> result = new List<LineData>();
var col = new FilteredElementCollector(Doc)
.WhereElementIsNotElementType()
.OfClass(typeof(CurveElement))
.ToElements();
foreach (Element e in col)
{
if (e is DetailLine)
{
DetailLine detailLine = (DetailLine)e;
LineData lineData = GetLinePoints(detailLine.GeometryCurve); //Call method to populate start end points of LineData object
lineData.ElId = detailLine.Id;
result.Add(lineData);
}
}
return result;
}
private LineData GetLinePoints(Curve curve)
{
LineData lineData = new LineData
(
curve.GetEndPoint(0).X,
curve.GetEndPoint(0).Y,
curve.GetEndPoint(1).X,
curve.GetEndPoint(1).Y
);
return lineData;
}
private List<LineData> GetMatchingDetailLines(IList<LineData> allDLines)
{
List<LineData> result = new List<LineData>();
foreach (LineData i in OldLineData)
{
result.AddRange(allDLines
.Where(x => x.StartX == i.StartX)
.Where(x => x.StartY == i.StartY)
.Where(x => x.EndX == i.EndX)
.Where(x => x.EndY == i.EndY)
.ToList());
}
return result;
}
private void GetGraphicStyles(IList<LineData> matchedDetailLines)
{
foreach (LineData l in matchedDetailLines)
{
DetailLine detailLine = Doc.GetElement(l.ElId) as DetailLine;
l.graphicStyle = detailLine.LineStyle as GraphicsStyle;
}
}
private void CreateNewMaskingRegion()
{
Transaction trans = new Transaction(Doc);
trans.Start("Create New Masking Region");
MRegion = FilledRegion.CreateMaskingRegion(Doc, UIDoc.ActiveView.Id, OldCurves);
trans.Commit();
}
private IList<LineData> GetNewDetailLines(IList<LineData> matchedDetailLines)
{
List<LineData> newDetailLines = GetDetailLineData(); //get all detail lines in project - including newly created ones for masking region
newDetailLines.RemoveAll(item => matchedDetailLines.Any(item2 => item.ElId == item2.ElId)); //removed previously matched lines which have the same geometry as the new ones
IList<LineData> newMatchedDetailLines = GetMatchingDetailLines(newDetailLines);
if (newMatchedDetailLines.Count == matchedDetailLines.Count)
{
for (int i = 0; i < newMatchedDetailLines.Count; i++)
{
newMatchedDetailLines[i].graphicStyle = matchedDetailLines[i].graphicStyle;
}
}
return newMatchedDetailLines;
}
private void UpdateGraphicsAndDims(IList<LineData> newMatchedDetailLines, ICollection<ElementId> oldDims)
{
Transaction trans = new Transaction(Doc);
trans.Start("Update MaskingRegion Graphics and Dims");
foreach (LineData i in newMatchedDetailLines) //Apply the graphic style to each detail line
{
DetailLine detailLine = Doc.GetElement(i.ElId) as DetailLine;
detailLine.LineStyle = i.graphicStyle;
}
ElementTransformUtils.MoveElement(Doc, MRegion.Id, XYZ.BasisZ); //apply a zero vector transform to wake up the graphics view.
ElementTransformUtils.MoveElement(Doc, MRegion.Id, XYZ.BasisZ.Negate());
UIDoc.ActiveView.HideElements(new Collection<ElementId>() { OldRegion.Id }); //Hide the filled region. If not, the copied dims may align to it.
if (oldDims.Count > 0) //apply any dimensions that were previously found
{
ElementTransformUtils.CopyElements(UIDoc.ActiveView, oldDims, UIDoc.ActiveView, null, null);
}
Doc.Delete(OldRegion.Id); //Once the dims are copied, the filled region can be deleted
trans.Commit();
}
public class LineData
{
public ElementId ElId { get; set; }
public GraphicsStyle graphicStyle { get; set; }
public double StartX { get; set; }
public double StartY { get; set; }
public double EndX { get; set; }
public double EndY { get; set; }
public LineData(double sX, double sY, double eX, double eY)
{
this.StartX = sX;
this.StartY = sY;
this.EndX = eX;
this.EndY = eY;
}
}
}
}
Can't find what you're looking for? Ask the community or share your knowledge.