[ORD CONNECT C#] Looking for a managed API over the native Contraint3d

I am working on a C# application to be run inside OpenRoads Designer that creates a variety of 3D items (cells, cylinders, blocks, etc.) and would like to programatically create 3D constraints between these items and items that already exist in the 3D model.  The MicroStation SDK includes a good example for how to do this with 2D models using native C++.  Is there a managed API over Contstraint3d so that I can more readily code this using C#?  Also is it possible to query for existing constraints using an ECQuery?

Parents Reply Children
  • Hello Dorlig,

    Using your example from the PowerPoint I created a static helper class Constraint3dUtility.h.  See code, below.

    I was attempting to create a 3d concentric constraint between two cylinders by simply changing Constraint3dType::DCM3_PERPENDICULAR to Constraint3dType::DCM3_CONCENTRIC.  Of course I should have realized that wouldn't work.  I did get the constraint added to both of my cylinders but it wasn't usable.  I'm guessing the code below does not correctly select the required element surface (as we observe in the MicroStation command?)

    -- John

    #pragma once
    
    #include <windows.h>
    
    #include <Mstn\MdlApi\mssystem.fdf>
    #include <PSolid\PSolidCoreAPI.h>
    #include <Mstn\Constraint3dElement\Constraint3dElementAPI.h>
    
    USING_NAMESPACE_BENTLEY;
    USING_NAMESPACE_BENTLEY_MSTNPLATFORM;
    USING_NAMESPACE_BENTLEY_MSTNPLATFORM_ELEMENT;
    USING_NAMESPACE_BENTLEY_DGNPLATFORM;
    USING_NAMESPACE_CONSTRAINT3D
    
    class Constraint3dUtility
    {
    public:
    	static void ApplyPerpendicularConstraint(ElementId elmId1, ElementId elmId2)
    	{
    		ApplyConstraint(elmId1, elmId2, Constraint3dType::DCM3_PERPENDICULAR);
    	}
    
    	static void ApplyConcentricConstraint(ElementId elmId1, ElementId elmId2)
    	{
    		ApplyConstraint(elmId1, elmId2, Constraint3dType::DCM3_CONCENTRIC);
    	}
    
    	static void ApplyConstraint(ElementId elmId1, ElementId elmId2, Constraint3dType constraintType)
    	{
    		// Specify geometries to constrain
    		bvector<GeometryIdPtr> geomsToConstrain;
    
    		ElementHandle eh1(elmId1, mdlModelRef_getActive());
    		ISolidKernelEntityPtr solid1;
    		SolidUtil::Convert::ElementToBody(solid1, eh1, true, false, false);
    		bvector<ISubEntityPtr> subFacesOfSolid1;
    		SolidUtil::GetBodyFaces(&subFacesOfSolid1, *solid1);
    		geomsToConstrain.push_back(FaceGeometryId::Create(eh1, *subFacesOfSolid1[0]));
    
    		ElementHandle eh2(elmId2, mdlModelRef_getActive());
    		ISolidKernelEntityPtr solid2;
    		SolidUtil::Convert::ElementToBody(solid2, eh2, true, false, false);
    		bvector<ISubEntityPtr> subFacesOfSolid2;
    		SolidUtil::GetBodyFaces(&subFacesOfSolid2, *solid2);
    		geomsToConstrain.push_back(FaceGeometryId::Create(eh2, *subFacesOfSolid2[0]));
    
    		// Create  constraint
    		Constraint3dPtr newConstraint = Constraint3dBase::Create(constraintType);
    		newConstraint->SetGeometryIds(geomsToConstrain);
    
    		// Get existing constraint and add new constraint
    		bset<ElementRefP> elementRefs;
    		for (auto const& geometryId : geomsToConstrain)
    			elementRefs.insert(geometryId->GetElementRef());
    
    		bvector <Constraint3dPtr> existingConstraints;
    		bset<ElementRefP> hosts;
    		Constraint3dElement::GetExistingConstraints(hosts, existingConstraints, elementRefs, mdlModelRef_getActive(), false);
    
    		bvector <Constraint3dPtr>  allConstraints = existingConstraints;
    		allConstraints.push_back(newConstraint);
    
    		// Evaluate the constraint and get result transform map
    		Constraint3dSolver constraintSolver;
    		constraintSolver.Initialize(allConstraints, *mdlModelRef_getActive());
    
    		bmap <ElementRefP, Transform> transformMap;
    		constraintSolver.Evaluate(transformMap);
    
    		// Update the positions of the constrained element and save the constraints
    		for (auto const &curr : transformMap)
    		{
    			EditElementHandle eeh(curr.first, mdlModelRef_getActive());
    			if (SUCCESS == eeh.GetHandler().ApplyTransform(eeh, TransformInfo(curr.second)))
    				eeh.ReplaceInModel(curr.first);
    		}
    
    		SaveConstraintsToHost(hosts, mdlModelRef_getActive(), allConstraints);
    	}
    
    	static BentleyStatus SaveConstraintsToHost(T_StdElementRefSet& hosts, DgnModelRefP modelRef, T_Constraints& constraints)
    	{
    		EditElementHandle hostEh;
    
    		if (hosts.empty())
    			Constraint3dElement::Create(hostEh, *modelRef);
    		else
    		{
    			for (auto const& curr : hosts)
    			{
    				if (NULL == hostEh.GetElementRef())
    					hostEh.SetElementRef(curr, modelRef);
    				else
    				{
    					EditElementHandle eeh(curr, modelRef);
    					eeh.DeleteFromModel();
    				}
    			}
    		}
    
    		ElementRefP hostElementRef = hostEh.GetElementRef();
    		Constraint3dElement::SaveConstraints(hostEh, constraints);
    
    		if (NULL == hostElementRef)
    			hostEh.AddToModel();
    		else
    			hostEh.ReplaceInModel(hostElementRef);
    
    		return SUCCESS;
    	}
    };

  • the concentric constraint must be added to circular sub entities, such as circular edge and circular face. the problem here is that the example which only want to illustrate constraint topic hard-code the face to be constrained as the first face of a solid. for a cylinder, the first face may not be the circular face, it may be the top or bottom face.  solid utility or solid API is another topic I am not very familiar. @Robert Hook, would you explain how to select a circular face of a cylinder? or add another developer who is expert of solid API?

  • By a process of trial-and-error I was able to guess that the correct sub entity to use to create a 3rd concentric constraint between two cylinders was the third face, that is: subFacesOfSolid1[2] and subFacesOfSolid2[2].

    It looks like programatically creating the other 3d constraints will be a bit challenging for now.  My application is based on DgnElementSetTool which works with items of type Bentley.DgnPlatformNET.Elements.Element.  So to create the constraints I need to correctly get at the appropriate 3d sub entities.   I'm sure this will be much easier for folks once the SDK starts delivering 3d constraint examples.

  • By a process of trial-and-error

    I'm impressed by your diligence!  In my experience, trial-and-error has long been part and parcel of developing for MicroStation.

    Posting intelligent questions along with pragmatic use of the API helps (a) Bentley Systems to understand that people want to understand their technology and (b) developers to acknowledge that examples are required to supplement terse help documentation.

     
    Regards, Jon Summers
    LA Solutions