给定两个具有相同 LocationPoint 的系列实例,如何让实例 #1 和 #2 具有相同的方向
Given two family instances with the same LocationPoint how can I get instance #1 and #2 to have the same orientation
在 Revit API 中,我试图用几乎相同版本的类别 "Mechanical Equipment" 替换 "Specialty Equipment" 类别的系列实例。到目前为止,我已经能够将新实例插入到与旧实例相同的位置,但我无法匹配相同的方向。 FamilyInstance
有一个方法 GetTransform
,我可以用它来获取旧 FamilyInstance
的 Transform
,但是没有方法 SetTransform
来设置转换新 FamilyInstance
。唯一可用于移动新 FamilyInstance
的工具是具有 MoveElement
和 RotateElement
等功能的 ElementTransformUtils
但我不确定使用旧的 [=12 将什么传递给它们=].
如何确保新 FamilyInstance
的方向与旧 FamilyInstance
的方向一致?
在查看维基百科 how to determine the axis and the angle of rotation. I also had to look up how to calculate the trace 以确定角度后,我能够为此编写代码。
private static Line GetRotationAxisFromTransform(Transform transform)
{
double x = transform.BasisY.Z - transform.BasisZ.Y;
double y = transform.BasisZ.X - transform.BasisX.Z;
double z = transform.BasisX.Y - transform.BasisY.X;
return Line.CreateUnbound(transform.Origin, new XYZ(x, y, z));
}
private static double GetRotationAngleFromTransform(Transform transform)
{
double x = transform.BasisX.X;
double y = transform.BasisY.Y;
double z = transform.BasisZ.Z;
double trace = x + y + z;
return Math.Acos((trace - 1) / 2.0);
}
然后我将这些传递给 ElementTransformUtils.RotateElement
方法,以便将 FamilyInstance
#2 旋转到与 FamilyInstance
#1 相同的位置。
我找到了一个更好的解决方案,可以解决 0 度或 180 度旋转问题。我的第一个解决方案在这种情况下不起作用。我在 euclideanspace.com.
找到了解决方案和代码的 java 版本
这是我的代码,从 Transform
获取矩阵,然后调用函数获取轴和角度。
double[][] matrix = new double[][]
{
new double[]{ oldTransform.BasisX.X, oldTransform.BasisY.X, oldTransform.BasisZ.X },
new double[]{ oldTransform.BasisX.Y, oldTransform.BasisY.Y, oldTransform.BasisZ.Y },
new double[]{ oldTransform.BasisX.Z, oldTransform.BasisY.Z, oldTransform.BasisZ.Z }
};
GetAxisAngleFromMatrix(matrix, out double angleOfRotation, out XYZ axisOfRotation);
Line rotationLine = Line.CreateUnbound(oldTransform.Origin, axisOfRotation);
这是数学函数
public void GetAxisAngleFromMatrix(double[][] m, out double angleOfRotation, out XYZ axisOfRotation)
{
double angle, x, y, z; // variables for result
double epsilon = 0.01; // margin to allow for rounding errors
double epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
// optional check that input is pure rotation, 'isRotationMatrix' is defined at:
// https://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/
if ((Math.Abs(m[0][1] - m[1][0]) < epsilon)
&& (Math.Abs(m[0][2] - m[2][0]) < epsilon)
&& (Math.Abs(m[1][2] - m[2][1]) < epsilon))
{
// singularity found
// first check for identity matrix which must have +1 for all terms
// in leading diagonaland zero in other terms
if ((Math.Abs(m[0][1] + m[1][0]) < epsilon2)
&& (Math.Abs(m[0][2] + m[2][0]) < epsilon2)
&& (Math.Abs(m[1][2] + m[2][1]) < epsilon2)
&& (Math.Abs(m[0][0] + m[1][1] + m[2][2] - 3) < epsilon2))
{
// this singularity is identity matrix so angle = 0
angleOfRotation = 0;
axisOfRotation = new XYZ(1, 0, 0);
return;
}
// otherwise this singularity is angle = 180
angle = Math.PI;
double xx = (m[0][0] + 1) / 2;
double yy = (m[1][1] + 1) / 2;
double zz = (m[2][2] + 1) / 2;
double xy = (m[0][1] + m[1][0]) / 4;
double xz = (m[0][2] + m[2][0]) / 4;
double yz = (m[1][2] + m[2][1]) / 4;
if ((xx > yy) && (xx > zz))
{ // m[0][0] is the largest diagonal term
if (xx < epsilon)
{
x = 0;
y = 0.7071;
z = 0.7071;
}
else
{
x = Math.Sqrt(xx);
y = xy / x;
z = xz / x;
}
}
else if (yy > zz)
{ // m[1][1] is the largest diagonal term
if (yy < epsilon)
{
x = 0.7071;
y = 0;
z = 0.7071;
}
else
{
y = Math.Sqrt(yy);
x = xy / y;
z = yz / y;
}
}
else
{ // m[2][2] is the largest diagonal term so base result on this
if (zz < epsilon)
{
x = 0.7071;
y = 0.7071;
z = 0;
}
else
{
z = Math.Sqrt(zz);
x = xz / z;
y = yz / z;
}
}
angleOfRotation = angle;
axisOfRotation = new XYZ(x, y, z); // return 180 deg rotation
return;
}
// as we have reached here there are no singularities so we can handle normally
double s = Math.Sqrt((m[2][1] - m[1][2]) * (m[2][1] - m[1][2])
+ (m[0][2] - m[2][0]) * (m[0][2] - m[2][0])
+ (m[1][0] - m[0][1]) * (m[1][0] - m[0][1])); // used to normalise
if (Math.Abs(s) < 0.001) s = 1;
// prevent divide by zero, should not happen if matrix is orthogonal and should be
// caught by singularity test above, but I've left it in just in case
angle = Math.Acos((m[0][0] + m[1][1] + m[2][2] - 1) / 2);
x = (m[2][1] - m[1][2]) / s;
y = (m[0][2] - m[2][0]) / s;
z = (m[1][0] - m[0][1]) / s;
angleOfRotation = angle;
axisOfRotation = new XYZ(x, y, z);
}
在 Revit API 中,我试图用几乎相同版本的类别 "Mechanical Equipment" 替换 "Specialty Equipment" 类别的系列实例。到目前为止,我已经能够将新实例插入到与旧实例相同的位置,但我无法匹配相同的方向。 FamilyInstance
有一个方法 GetTransform
,我可以用它来获取旧 FamilyInstance
的 Transform
,但是没有方法 SetTransform
来设置转换新 FamilyInstance
。唯一可用于移动新 FamilyInstance
的工具是具有 MoveElement
和 RotateElement
等功能的 ElementTransformUtils
但我不确定使用旧的 [=12 将什么传递给它们=].
如何确保新 FamilyInstance
的方向与旧 FamilyInstance
的方向一致?
在查看维基百科 how to determine the axis and the angle of rotation. I also had to look up how to calculate the trace 以确定角度后,我能够为此编写代码。
private static Line GetRotationAxisFromTransform(Transform transform)
{
double x = transform.BasisY.Z - transform.BasisZ.Y;
double y = transform.BasisZ.X - transform.BasisX.Z;
double z = transform.BasisX.Y - transform.BasisY.X;
return Line.CreateUnbound(transform.Origin, new XYZ(x, y, z));
}
private static double GetRotationAngleFromTransform(Transform transform)
{
double x = transform.BasisX.X;
double y = transform.BasisY.Y;
double z = transform.BasisZ.Z;
double trace = x + y + z;
return Math.Acos((trace - 1) / 2.0);
}
然后我将这些传递给 ElementTransformUtils.RotateElement
方法,以便将 FamilyInstance
#2 旋转到与 FamilyInstance
#1 相同的位置。
我找到了一个更好的解决方案,可以解决 0 度或 180 度旋转问题。我的第一个解决方案在这种情况下不起作用。我在 euclideanspace.com.
找到了解决方案和代码的 java 版本这是我的代码,从 Transform
获取矩阵,然后调用函数获取轴和角度。
double[][] matrix = new double[][]
{
new double[]{ oldTransform.BasisX.X, oldTransform.BasisY.X, oldTransform.BasisZ.X },
new double[]{ oldTransform.BasisX.Y, oldTransform.BasisY.Y, oldTransform.BasisZ.Y },
new double[]{ oldTransform.BasisX.Z, oldTransform.BasisY.Z, oldTransform.BasisZ.Z }
};
GetAxisAngleFromMatrix(matrix, out double angleOfRotation, out XYZ axisOfRotation);
Line rotationLine = Line.CreateUnbound(oldTransform.Origin, axisOfRotation);
这是数学函数
public void GetAxisAngleFromMatrix(double[][] m, out double angleOfRotation, out XYZ axisOfRotation)
{
double angle, x, y, z; // variables for result
double epsilon = 0.01; // margin to allow for rounding errors
double epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
// optional check that input is pure rotation, 'isRotationMatrix' is defined at:
// https://www.euclideanspace.com/maths/algebra/matrix/orthogonal/rotation/
if ((Math.Abs(m[0][1] - m[1][0]) < epsilon)
&& (Math.Abs(m[0][2] - m[2][0]) < epsilon)
&& (Math.Abs(m[1][2] - m[2][1]) < epsilon))
{
// singularity found
// first check for identity matrix which must have +1 for all terms
// in leading diagonaland zero in other terms
if ((Math.Abs(m[0][1] + m[1][0]) < epsilon2)
&& (Math.Abs(m[0][2] + m[2][0]) < epsilon2)
&& (Math.Abs(m[1][2] + m[2][1]) < epsilon2)
&& (Math.Abs(m[0][0] + m[1][1] + m[2][2] - 3) < epsilon2))
{
// this singularity is identity matrix so angle = 0
angleOfRotation = 0;
axisOfRotation = new XYZ(1, 0, 0);
return;
}
// otherwise this singularity is angle = 180
angle = Math.PI;
double xx = (m[0][0] + 1) / 2;
double yy = (m[1][1] + 1) / 2;
double zz = (m[2][2] + 1) / 2;
double xy = (m[0][1] + m[1][0]) / 4;
double xz = (m[0][2] + m[2][0]) / 4;
double yz = (m[1][2] + m[2][1]) / 4;
if ((xx > yy) && (xx > zz))
{ // m[0][0] is the largest diagonal term
if (xx < epsilon)
{
x = 0;
y = 0.7071;
z = 0.7071;
}
else
{
x = Math.Sqrt(xx);
y = xy / x;
z = xz / x;
}
}
else if (yy > zz)
{ // m[1][1] is the largest diagonal term
if (yy < epsilon)
{
x = 0.7071;
y = 0;
z = 0.7071;
}
else
{
y = Math.Sqrt(yy);
x = xy / y;
z = yz / y;
}
}
else
{ // m[2][2] is the largest diagonal term so base result on this
if (zz < epsilon)
{
x = 0.7071;
y = 0.7071;
z = 0;
}
else
{
z = Math.Sqrt(zz);
x = xz / z;
y = yz / z;
}
}
angleOfRotation = angle;
axisOfRotation = new XYZ(x, y, z); // return 180 deg rotation
return;
}
// as we have reached here there are no singularities so we can handle normally
double s = Math.Sqrt((m[2][1] - m[1][2]) * (m[2][1] - m[1][2])
+ (m[0][2] - m[2][0]) * (m[0][2] - m[2][0])
+ (m[1][0] - m[0][1]) * (m[1][0] - m[0][1])); // used to normalise
if (Math.Abs(s) < 0.001) s = 1;
// prevent divide by zero, should not happen if matrix is orthogonal and should be
// caught by singularity test above, but I've left it in just in case
angle = Math.Acos((m[0][0] + m[1][1] + m[2][2] - 1) / 2);
x = (m[2][1] - m[1][2]) / s;
y = (m[0][2] - m[2][0]) / s;
z = (m[1][0] - m[0][1]) / s;
angleOfRotation = angle;
axisOfRotation = new XYZ(x, y, z);
}