Revit Transform和镜像(翻转)问题
说在前面
例行胡言乱语,点击阅读正文
杂鱼表演,众鱼退散!
24/03/21记
突然烦躁,心不在焉,不想干了·。
一个项目要汇报,昨天检查、调试、改改Bug弄到了凌晨3点。然后早上起来突然想起啥不对劲的,打电话一问,会议提前了变为早上9点…
回来时午休将过,到下午忽然忧心仲仲,仿佛无数的事接踵而至…
买了火车票,定好旅馆,我要去到处走走
24/03/22写
票退了,旅馆也退了。
昨天回去大睡了一觉,我的猫见我没有理会她,安安静静趴在我腿上,想起周末还得带去医院拆线。
票是去武汉的,想看看樱花,但再想想,一天半不够做什么,一些地方还没预约上,不如就骑着自行车去转转。
P.S.写日记?正经人能写日记?
问题由来
之前有个需要生成与元素等外形的实心的实体的功能,这就需要用到Transform。实现了一版后,发现有个别模型与生成的模型不重叠,而是存在沿着某个轴旋转了180°的情况。后面在检查时,发现该模型是由镜像操作生成的。所以这里边写一写Revit Transform相关的知识。
先记下:Revit Transfrom 不包含镜像信息
Revit Transform
首先,我们得知道,只有族实例(FamilyInstance
)才有GetTransform()
方法,可以获取元素Transfrom,而系统族由于其有着特殊的实现,是不存在Transform的。
那么,Transfrom在Revit中是什么呢?
Transfrom是什么
数学里,有个叫矩阵
的东西,在许多三维引擎中,通常是使用4x4矩阵来表示模型的缩放、旋转、平移等操作。
而Revit中,Transfrom就是对矩阵的一种封装
。
// Transform 主要|常用属性
Transform.BaseX; // X轴方向,单位向量,对应矩阵第一行
Transform.BaseY; // Y轴方向,单位向量,对应矩阵第二行
Transform.BaseZ; // Z轴方向,单位向量,对于矩阵第三行,Z = X.Corss(Y).Normalize()
Transform.Origin; // 主坐标系下的位置
Transform.Inverse; // 矩阵的逆
Transfrom.Scale {Get;} // 只读,作用不明
为了更好的理解,我们可以从两个角度来看。
从模型角度:Transform就是动一动物体,可能是让它转一转,又或者是挪个位置,即模型的缩放、旋转、平移。
从坐标系角度:就是将物体从原坐标系中,变换到Transfrom所表示的坐标系下。
开发过程中,一般按第一种理解就行了,少数情况下用第二种更好理解。就Revit对矩阵的封装上,似乎倾向于第二种解释,比如有一个结构很像Transform的叫Frame
的东西。
Transfrom怎么用
现在我们知道了改变矩阵可以对物体的位置、形态做出调整,而Revit中也是如此?
当然不了,FamilyInstance并没有Transfrom属性,而是需要通过一个叫GetTransform()
的方法才能得到。
虽然不能通过改Transfrom对元素进行调整,但Revit开放了另一套接口供我们使用:ElementTransformUtils
。
// ElementTransformUtils Members
public static void MoveElement(Document document,ElementId elementToMove,XYZ translation) // 平移
public static void RotateElement(Document document,ElementId elementToRotate,Line axis,double angle) // 旋转
public static void MirrorElement(Document document,ElementId elementToMirror,Plane plane) // 镜像
// 还有复制,这些方法都有重载,详情请查看文API档
嘿,我们发现这里没有缩放方法。
补充:用Transfrom变换的的还有SolidUtils
,CurveLoop
,BoundingBoxXYZ
等等。
元素的镜像和翻转
在Revit中,我们可以通过镜像操作,调整一些物体(如门窗)的方位或快速生成对称的模型。
那么这方面有什么问题呢?要说也没什么问题,但这却是与我要开发的一个功能有关,那儿问题可大了去了。
镜像(Mirror)
镜像,关于镜面对称,从矩阵的角度看,也就是负缩放
or反射/镜像矩阵
。
可是Revit Transform么得缩放,前面也说了,Revit这边封装的偏向于坐标系的概念,三个轴两两垂直,且必须是单位向量
。这可咋办?
Transform轴的单位向量要求,限制了Revit元素无法进行尺寸上的变化(缩放),但原尺寸上的负缩放(镜像)还是可以的。
我们知道用Revit修改栏有控件可以创建镜像,但这样操作后,我们怎么知道那就是镜像来的呢?
这里就得提到FamilyInstance
的两个翻转
属性了。
翻转
手部翻转(HandFlipped
)和面向翻转(FacingFlipped
)表示一个族实例是否进行了翻转操作,当两者之一
为True
时,就表示进行了镜像,此时Mirrored
属性将为True。
从“南”朝前的视角看。
HandFlipped
:在X轴方向
上的镜像。伸出手,我们的手掌是左右翻转的,即Revit的X轴方向。
FacingFlipped
:在Y轴方向
上的镜像。同理,面朝前看,对应Revit中的Y轴方向。
当我们使用“镜像”按钮进行操作时,会发现无论是横向画轴还是纵向画轴,最后查看属性都是FacingFlipped
为Ture,这也是一些文章中会说无法判断翻转方向的原因。为什么会这样?真的无法判断吗?
我们先来看看相关定义,Wiki的矩阵变换部分清晰地指出,关于X、Y轴的反射/镜像是有不同的变换矩阵的:
通过使用API flipFacing()
、flipHand()
的方式,我们分别设置了模型Hand、Face、Hand+Face的对照组,再从一些参考文章中123,我们可以得到结论:
对于镜像操作,Revit采用镜像+旋转的组合方式进行表达。
Hand翻转
:采用Transfrom旋转180°的方式表示。
Facing翻转
:采用镜像/反射的方式表示,Transform不变化。
Transform和Mirror的计算关系
FamilyInstance上,还有俩属性HandOrientation
和FacingOrientation
,其Transfome和翻转方向的关系,由以下代码计算:
// familyInstance 族实例
Transform tf = Transform.Identity;
XYZ ho = familyInstance.HandOrientation;
XYZ fo = familyInstance.FacingOrientation;
if ((familyInstance.HandFlipped && !familyInstance.FacingFlipped) ||
(!familyInstance.HandFlipped && familyInstance.FacingFlipped))
{
fo = fo.Negate();
}
tf.BasisX = ho.Normalize();
tf.BasisY = fo.Normalize();
tf.BasisZ = ho.CrossProduct(fo).Normalize();
tf.Origin = transform.Origin;
// tf == familyInstance.GetTransform()
总结
总算是写完了,这篇文章从开始写到这里用了快6个小时了,没想到要用这么久。
一些问题本来是搁置着的,在写这篇文章的时候突然想到了,然后查查资料,进行验证,问题就这么搞定了。
下一篇,差不多得写《Revit CurveLoop、Plane和多边形面积比(二)》了吧,emmm…
不管了,睡觉
完、