Hi Everybody.
I want to align two families by their connectors. Graphically it is shown in picture below. Families can be placed in any direction in 3D space. Any axis of any element is not to parallel to each other and project coordinates.
My code is at below. I explained code as much as I could. The code is working when element located parallel to project axes. But it doesn't in other situations.
Am I in the wrong path? Is there a better way to do it?
private: static void rotate(UIApplication^ uiapp, elmstoreplace^ elms)
{
// Select some elements in Revit before invoking this command
// Get the handle of current document.
UIDocument^ uidoc = uiapp->ActiveUIDocument;
Document^ doc = uidoc->Document;
// Elelements are stored in other header. We will read from there.
// We saved them there by selecting in GUI.
Element^ elmfrom = doc->GetElement(elmstoreplace::elmFrom); // Element will be aligned.
Element^ elmto = doc->GetElement(elmstoreplace::elmTo); // Element will be aligned to.
// 1. Cast Element to FamilyInstance
FamilyInstance^ faminstfrom = (FamilyInstance^)elmfrom;
FamilyInstance^ faminstto = (FamilyInstance^)elmto;
// 2. Get MEPModel Property
MEPModel^ mepmodelfrom = faminstfrom->MEPModel;
MEPModel^ mepmodelto = faminstto->MEPModel;
// 3. Get connector set of MEPModel
ConnectorSet^ connectorSetfrom = mepmodelfrom->ConnectorManager->Connectors;
ConnectorSet^ connectorSetto = mepmodelto->ConnectorManager->Connectors;
// Connector numbers to be aligned to each other are stored in other file.
// We saved them there by selecting in GUI.
Connector^ connFrom =nullptr; // Connector will be aligned.
Connector^ connTo = nullptr; // Connector to be aligned to.
// Get connector by iterating in connectorset.
for each (Connector ^ connector in connectorSetfrom)
{
if (connector->Id == elmstoreplace::connNoFrom)
{
connFrom = connector;
}
}
// Get connector by iterating in connectorset.
for each (Connector ^ connector in connectorSetto)
{
if (connector->Id == elmstoreplace::connNoTo)
{
connTo = connector;
}
}
// Get the element current location
LocationPoint^ elmfromLoc = (LocationPoint^)elmfrom->Location;
// Create vector based on element location and connector location.
XYZ^ diffvec1 = connFrom->Origin->Subtract(elmfromLoc->Point);
// Subtract element location which will be moved.
XYZ^ diffvec2 = connTo->Origin->Subtract(elmfromLoc->Point);
// Bring two connectors to same point
ElementTransformUtils::MoveElement(doc, elmfrom->Id, diffvec2->Add(diffvec1->Negate()));
// Get basisZ of connectors.
XYZ^ basisZTo = connTo->CoordinateSystem->BasisZ;
// Transform vector.
XYZ^ getOfbasisZTo = getOfVector(basisZTo);
// Get basisZ of connectors.
XYZ^ basisZFrom = connFrom->CoordinateSystem->BasisZ;
// Transform vector.
XYZ^ getOfbasisZFrom = getOfVector(basisZFrom);
// Calculate angle between connectors.
double angle = getOfbasisZTo->AngleTo(getOfbasisZFrom);
// Calculate crossproduct of two vectors.
XYZ^ crossprod = getOfbasisZTo->CrossProduct(getOfbasisZFrom);
// Calculate crossproduct of two vectors.
// Somehow this always give unit vectors
// BasisX(1,0,0), BasisY(0,1,0), BasisX(0,0,1)
// I think that is my problem.
XYZ^ getOfcrossprod = getOfVector(crossprod);
// Create rotation axis.
Line^ rotationaxis = Line::CreateUnbound(connTo->Origin, crossprod->BasisZ);
// Rotate element around axis.
ElementTransformUtils::RotateElement(doc, elmfrom->Id, rotationaxis, Math::PI - angle);
}
public: static XYZ^ getOfVector(XYZ^ xyz)
{
XYZ^ OfVector(xyz);
return xyz;
}
Solved! Go to Solution.
Solved by halukuzuner. Go to Solution.
Sorry, I do not understand your code, so I will simply try to answer your question:
How can I align the connectors?
Given two elements E and F containing connectors A and B with locations P and Q. Determine the difference between P and Q, the vector V = Q - P. If you translate the element E containing A by V, A will lie in exactly the same spot as B. If you don't want it in the exact same spot, but only vertically or horizontally aligned in some way, you need to adapt some of the coordinates of V accordingly. The translation can be accomplished using the MoveElement method:
https://www.revitapidocs.com/2023/aaddd413-01b0-2878-3f79-a281abb6d364.htm
Watch out that other attached elements are not moved as well.
Hi Jeremy,
O improved picture. As far as understood move element moves families/elements only. So that, I find U vector and subtract W from it and find V vector you mentioned. In the way you mentioned is works if two families placed in the same plane and orientation. In my case none of axes are parallel. My problem starts after moving element. My aim is finding crossproduct of two Z axes which is perpendicular to both axes. And rotate it by angle. I stuck in creating rotation axis along z axis of crossproduct. As shown in the picture, crossproduct BasisX, Y, Z always give unit vectors.
“When you are a Bear of Very Little Brain, and you Think of Things, you find sometimes that a Thing which seemed very Thingish inside you is quite different when it gets out into the open and has other people looking at it.”
Sorry, but this question is beyond my limited capacity for a quick answer.
It seems to me that you require some professional help, sir 🙂
Dear Jeremy,
Thanks for your modest answer. Possibly I couldn't express my problem. I finally made it worked. My problem was understanding "Basis" vectors. My final code is at below. It worked perfectly for me. I additionally draw rotation axis and rotation arc to visualize rotation.
Besides I was very surprised there is no similar code samples on internet. There is many codes but they are working on parallel planes. Revit itself already does the action which I was after. Bu API developers didn't need it.
private: static void RotateOnConnector(UIApplication^ uiapp, elmstoreplace^ elms)
{
try
{
// Select some elements in Revit before invoking this command
// Get the handle of current document.
UIDocument^ uidoc = uiapp->ActiveUIDocument;
Document^ doc = uidoc->Document;
// Elelements are stored in other header. They will be read from there.
// They are saved there by selecting in GUI.
Element^ elmfrom = doc->GetElement(elmstoreplace::elmFrom);
Element^ elmto = doc->GetElement(elmstoreplace::elmTo);
// 1. Cast Element to FamilyInstance
FamilyInstance^ faminstfrom = (FamilyInstance^)elmfrom;
FamilyInstance^ faminstto = (FamilyInstance^)elmto;
// 2. Get MEPModel Property
MEPModel^ mepmodelfrom = faminstfrom->MEPModel;
MEPModel^ mepmodelto = faminstto->MEPModel;
// 3. Get connector set of MEPModel
ConnectorSet^ connectorSetfrom = mepmodelfrom->ConnectorManager->Connectors;
ConnectorSet^ connectorSetto = mepmodelto->ConnectorManager->Connectors;
// Connector numbers to be aligned to each other are stored in other file.
// They saved there by selecting in GUI.
Connector^ connFrom =nullptr; // Connector will be aligned.
Connector^ connTo = nullptr; // Connector to be aligned to.
// Get connector by iterating in connectorset.
for each (Connector ^ connector in connectorSetfrom)
{
if (connector->Id == elmstoreplace::connNoFrom)
{
connFrom = connector;
String^ message = "Connector is owned by: " + connector->Owner->Name;
}
}
// Get connector by iterating in connectorset.
for each (Connector ^ connector in connectorSetto)
{
if (connector->Id == elmstoreplace::connNoTo)
{
connTo = connector;
}
}
// Get the element current location
LocationPoint^ elmfromLoc = (LocationPoint^)elmfrom->Location;
// Create vector based on element location and connector location.
XYZ^ diffvec1 = connFrom->Origin->Subtract(elmfromLoc->Point);
// Subtract element location which will be moved.
XYZ^ diffvec2 = connTo->Origin->Subtract(elmfromLoc->Point);
// Bring two connectors to same point
ElementTransformUtils::MoveElement(doc, elmfrom->Id, diffvec2->Add(diffvec1->Negate()));
// Get BasisZ of connectors.
XYZ^ basisZTo = connTo->CoordinateSystem->BasisZ;
XYZ^ basisZFrom = connFrom->CoordinateSystem->BasisZ;
// Calculate angle between connectors.
double angle = basisZTo->AngleTo(basisZFrom);
// Calculate crossproduct of two vectors.
XYZ^ crossprod = basisZTo->CrossProduct(basisZFrom);
// Create rotation axis.
Line^ rotationaxis = Line::CreateBound(connTo->Origin, gcnew XYZ(connTo->Origin->X+crossprod->X, connTo->Origin->Y+crossprod->Y, connTo->Origin->Z+crossprod->Z));
// Rotate element around axis.
ElementTransformUtils::RotateElement(doc, elmfrom->Id, rotationaxis, Math::PI-angle);
// get handle to application from document
Autodesk::Revit::ApplicationServices::Application^ application = doc->Application;
// Create a geometry line in Revit application
XYZ^ startPoint = connTo->Origin;
XYZ^ endPoint = gcnew XYZ(connTo->Origin->X + crossprod->X,
connTo->Origin->Y + crossprod->Y,
connTo->Origin->Z + crossprod->Z);
Line^ geomLine = Line::CreateBound(startPoint, endPoint);
// Create a geometry plane in Revit application
Plane^ linePlane = Plane::CreateByThreePoints(
connTo->Origin,
gcnew XYZ(connTo->Origin->X,
connTo->Origin->Y,
connTo->Origin->Z + connTo->CoordinateSystem->BasisZ->Z),
gcnew XYZ(connTo->Origin->X + crossprod->X,
connTo->Origin->Y + crossprod->Y,
connTo->Origin->Z + crossprod->Z));
Plane^ arcPlane = Plane::CreateByNormalAndOrigin(crossprod->Normalize(), connTo->Origin);
Arc^ geomArc = Arc::Create(arcPlane, 0.5, 0, angle);
// Create a sketch plane in current document
SketchPlane^ linesketch = SketchPlane::Create(doc, linePlane);
SketchPlane^ arcsketch = SketchPlane::Create(doc, arcPlane);
// Create a ModelLine element using the created geometry line and sketch plane
ModelLine^ line = (ModelLine^)doc->Create->NewModelCurve(geomLine, linesketch);
// Create a ModelArc element using the created geometry arc and sketch plane
ModelArc^ arc = (ModelArc^)doc->Create->NewModelCurve(geomArc, arcsketch);
}
catch (Exception^ ex)
{
TaskDialog::Show("!!! ", ex->Message->ToString());
}
}
Thank you very much for raising this and sharing your nice solution. So, now a sample is available online. I edited and shared it on the blog for posterity:
https://thebuildingcoder.typepad.com/blog/2022/09/aligning-connectors.html#2