Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Detect Cut between two elements

5 REPLIES 5
Reply
Message 1 of 6
maxime.touchard7RAK6
397 Views, 5 Replies

Detect Cut between two elements

I'm struggling to detect cut between two FamilyInstance elements.
The situation is the following we have a discomposed wall made of plates and studs. I want to check if all the studs are creating a nutch inside the plates. The nutch is created with the "Cut" function. There is a void at each ends of studs.
tempsnip.png
So far I've tried 3 methods :

 

BooleanOperationsUtils.ExecuteBooleanOperation(studSolid, plateSolid, BooleanOperationsType.Intersect);

 

Which is giving me a Solid with a volume even if there is visualy no intersection.

 

InstanceVoidCutUtils.InstanceVoidCutExists(topPlate, verticalStud);

 

Which is always return true in my case. Even if I move the stud far from the plate.

SolidSolidCutUtils.CutExistsBetweenElements(verticalStud, topPlate, out bool firstCutsSecond);

Which seems to work randomly. It may return false on specific pair (always the same) but return true on other.

 Please check the .rvt sample below (view : {3D}) it contains a plate and two vertical studs. The lefts stud gives me true in the solidsolidcututils method and the right one gives me false.

 

Thanks !

5 REPLIES 5
Message 2 of 6

Interesting results. Congratulations on trying several different approaches. Don't worry, there are only a couple of hundred or so other options for you to try out. One of them is sure to work.

 

Apparently, the void cut reports its existence regardless of whether it actually makes any difference to the model or not.

 

A good aspect is that you can successfully query both of those family instances for their solid geometry.

 

Now, if a non-zero intersection exists, then one or the other volume should change if the other instance is eliminated, shouldn't it?

 

So, you could try the TTT temporary transaction trick: in a temporary transaction that is later rolled back and never committed, retrieve the volume of the solid (all solids) of instance A before and after temporarily deleting instance B. if the two volumes differ, there was a non-zero cut, and you can determine its volume by the difference.

  

And so on and so forth. I hope you get the idea. This trick has been used successfully in hundreds of different situations, many of them discussed here in the forum; a few are mentioned in The Building Coder topic group on transactions:

  

https://thebuildingcoder.typepad.com/blog/about-the-author.html#5.53

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 3 of 6

Great Idea !
Unfortunatly It doesn't seem to work in my case.
If I do understand the trick I have to regenerage the document to make the volume to update.
And it pops a lot of warnings / errors when I do so. It could be ok if I had a few studs but I have hundreds...
Here is my code :

double beforePlateVolume = plate.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED).AsDouble();
double afterPlateVolume = beforePlateVolume;
using (Transaction tr = new Transaction(doc, "-delete vertical stud-"))
{
    tr.Start();
    doc.Delete(stud.Id);
    //doc.Regenerate();
    afterPlateVolume = plate.get_Parameter(BuiltInParameter.HOST_VOLUME_COMPUTED).AsDouble();
    DebugUtil.log("volumes : " + beforePlateVolume + " || " + afterPlateVolume); // same volume if no regenerate
    tr.RollBack();
}
Message 4 of 6

Yes, you need to regenerate to update the volume in the parameter.

 

However, I would suggest not using the parameter but querying the element for its geometry solid and checking that instead. It probably also requires a regen, though. 

 

Maybe you can implement a warning swallower to suppress the warnings:

 

https://thebuildingcoder.typepad.com/blog/2016/09/warning-swallower-and-roomedit3d-viewer-extension....

 

If you create hundreds of separate transactions, you will need hundreds of time T.

 

How about packaging all your intersection tests into one single transaction? Maybe that will perform in one single time T.

  

Remember, the transaction possibly costs a hundred times as much as the operation you perform. 

  

And the regeneration maybe costs thousands of times as much.

  

Maybe add some benchmarking to see which operation is really consuming the time?

  

Jeremy Tammik, Developer Advocacy and Support, The Building Coder, Autodesk Developer Network, ADN Open
Message 5 of 6

There are 2 ways to find an answer to your problem, not involving a transaction or regeneration.
The first works in conjunction with the InstanceVoidCutUtils.InstanceVoidCutExists(topPlate, verticalStud) method. Use the document warnings to find elements that are cut/joined but don't intersect. You'd have to determine which warning is valid for the studs/plates, as there are several warnings that give information regarding a disjoined state.Warning_NotIntersecting.PNG

 

 

 

bool disjointWarning = false;
foreach (FailureMessage message in  doc.GetWarnings())
{
	if(message.GetFailureDefinitionId().Guid  != BuiltInFailures.CutFailures.CannotCutGeometryWarn.Guid) continue;
	if(message.GetFailingElements().Contains(topplate.Id) && message.GetFailingElements().Contains(verticalStud.Id)) disjointWarning = true;
	break;
}
// other possible warnings
//			BuiltInFailures.CutFailures.CannotCutGeometry;
//			BuiltInFailures.JoinElementsFailures.JoiningDisjoint;

 

 

 

 

The second solution is to use the Element.GetGeneratingElementIds(face) method on all the faces of the plates, and find if the Id of the stud is contained in that list, meaning the stud is intersecting the plate.

Return ValueThe id(s) of the element(s) that generated (or may have generated) the given geometry object. Empty if no generating elements are found. If the set contains just one id, it is the id of the element that generated the geometry object.

 

 

 

     private List<ElementId> GetGeneratingElementIds(Element e)
     {
     	IEnumerable<ElementId> res = new List<ElementId>();
        Options opt = new Options();
	opt.ComputeReferences = false;
	opt.IncludeNonVisibleObjects = false;
	bool IsGetGeometryInstance = !((e is FamilyInstance) && !ExporterIFCUtils.UsesInstanceGeometry(e as FamilyInstance)) ;
	GeometryElement geoElem = e.get_Geometry(opt);
        // get references
        foreach (var geo in geoElem)
        {
            GeometryInstance geoIns = geo as GeometryInstance;
            if (geoIns != null)
            {
                GeometryElement geoElem2 =  IsGetGeometryInstance == true ? geoIns.GetInstanceGeometry() : geoIns.GetSymbolGeometry();
                foreach(GeometryObject geoObj2 in geoElem2)
                {
                	Solid s2 = geoObj2 as Solid;
                	if(s2==null) continue;
                	foreach(Face f in s2.Faces) res = res.Union(e.GetGeneratingElementIds(f));
                }
                continue;
         }
       	Solid s = geo as Solid;
       	if(s==null) continue;
       	foreach(Face f in s.Faces) res = res.Union(e.GetGeneratingElementIds(f));
       }
       return res.ToList();
    }
//"main code"
	bool cutting = GetGeneratingElementIds(topplate).Contains(verticalStud.Id);

 

 

 

 

Message 6 of 6

Hi guys,
Thanks for all your help.
For the record here is the solution i'm using :

InstanceVoidCutUtils.GetCuttingVoidInstances(bottomPlate).Any(i => i.IntegerValue == verticalStud.Id.IntegerValue)

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Forma Design Contest


Rail Community