using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using Autodesk.Revit.UI.Selection; using Nice3point.Revit.Toolkit.External; using System; using System.Linq; namespace Szmedi.RvKits.Civil; [Transaction(TransactionMode.Manual)] public class SlopedFloorCmd : ExternalCommand { public override void Execute() { #region SelectExecute using Transaction trans = new(Document, "坡度楼板"); try { Line line1 = null; Line line2 = null; HostObject obj1 = null; HostObject obj2 = null; UiDocument.Selection.PickObject( ObjectType.Edge, new FuncFilter( elem => elem is HostObject, (r, p) => { obj1 = Document.GetElement(r) as HostObject; Edge edge = obj1.GetGeometryObjectFromReference(r) as Edge; if (edge.AsCurve() is Line l && l.Direction.Z < 0.0001) { line1 = l; return true; } return false; } ), "请选择主体图元其中的一条水平边" ); UiDocument.Selection.PickObject( ObjectType.Edge, new FuncFilter( elem => elem is HostObject && elem.Id.IntegerValue != obj1.Id.IntegerValue, (r, p) => { obj2 = Document.GetElement(r) as HostObject; Edge edge1 = obj2.GetGeometryObjectFromReference(r) as Edge; if ( edge1.AsCurve() is Line l && l.Direction.Z < 0.0001 && l.Direction.CrossProduct(line1.Direction).IsAlmostEqualTo(XYZ.Zero) ) { line2 = l; return true; } return false; } ), "请选择另一个主体图元的水平边(与第一条平行)" ); Level level1 = Document.GetElement(obj1.LevelId) as Level; Level level2 = Document.GetElement(obj2.LevelId) as Level; Level baseLevel = level1.Elevation > level2.Elevation ? level2 : level1; //基准边,低的边 Line lineBase; Line lineAnother; if (line1.Origin.Z < line2.Origin.Z) { lineBase = line1; lineAnother = line2; } else { lineBase = line2; lineAnother = line1; } CurveArray curveArr = new(); Plane planeToProject = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, lineBase.Origin); SignedDistanceTo(planeToProject, lineBase.GetEndPoint(0), out XYZ p1); SignedDistanceTo(planeToProject, lineBase.GetEndPoint(1), out XYZ p2); SignedDistanceTo(planeToProject, lineAnother.GetEndPoint(0), out XYZ p3); SignedDistanceTo(planeToProject, lineAnother.GetEndPoint(1), out XYZ p4); Line l1; Line l2; Line l3; Line l4; if (lineBase.Direction.IsAlmostEqualTo(lineAnother.Direction)) { l1 = Line.CreateBound(p1, p2); l3 = Line.CreateBound(p4, p3); l4 = Line.CreateBound(p3, p1); l2 = Line.CreateBound(p2, p4); } else { l1 = Line.CreateBound(p1, p2); l3 = Line.CreateBound(p3, p4); l4 = Line.CreateBound(p4, p1); l2 = Line.CreateBound(p2, p3); } trans.Start(); curveArr.Append(l1); curveArr.Append(l2); curveArr.Append(l3); curveArr.Append(l4); //var isStructure = obj1.get_Parameter(BuiltInParameter.FLOOR_PARAM_IS_STRUCTURAL).AsInteger(); //选择线的中点连线 Line centerLine = Line.CreateBound(lineBase.Evaluate(0.5, true), lineAnother.Evaluate(0.5, true)); //中线和边线叉乘,求出楼板面的法向量 XYZ floorNormal = lineBase.Direction.CrossProduct(centerLine.Direction); if (floorNormal.Z < 0) { floorNormal = floorNormal.Negate(); } //坡度角 double radian = floorNormal.AngleTo(XYZ.BasisZ); //另一条楼板边垂直xy的平面 XYZ normal = lineAnother.Direction.CrossProduct(XYZ.BasisZ); Plane plane = Plane.CreateByNormalAndOrigin(normal, lineAnother.Evaluate(0.5, true)); //点与平面的距离 XYZ centerPoint = lineBase.Evaluate(0.5, true); double distance = SignedDistanceTo(plane, centerPoint, out XYZ perpPoint); Line slopedArrow = Line.CreateBound(centerPoint, new XYZ(perpPoint.X, perpPoint.Y, centerPoint.Z)); //if (lineBase.Origin.Z > lineAnother.Origin.Z) //{ // slopedArrow = slopedArrow.CreateReversed() as Line; //} //即板上表面法向量和Z轴正向之间 //double radian = slopedArrow.Direction.AngleOnPlaneTo(XYZ.BasisX, XYZ.BasisZ); //double slope = Math.Abs(line1.Evaluate(0.5, true).Z - line2.Evaluate(0.5, true).Z); //slopedArrow用以指定在XY平面上绘制的板轮廓,是沿着哪个方向进行坡度变化的。因此,这条线实际上是XY平面上,垂直于板上表面与XY平面交线的一条线。 Document.Create.NewSlab(curveArr, baseLevel, slopedArrow, Math.Tan(radian), true); trans.Commit(); } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { if (trans.GetStatus() == TransactionStatus.Started) { trans.Commit(); } } catch (Exception ex) { ErrorMessage = ex.Message; if (trans.GetStatus() == TransactionStatus.Started) { trans.RollBack(); } Result = Result.Failed; } #endregion SelectExecute } public bool IfSameHeight(List vs) { //判断楼板中各端点标高是否相等 List list = new(); foreach (double z in vs) { if (list.Contains(z) == false) { list.Add(z); } } return list.Count == 1; } /// /// 得到垂足 /// /// /// /// 经过该点作与p0,p1所在直线的垂线 /// public static XYZ PerpIntersection(XYZ pX, XYZ p0, XYZ p1) { //直线向量和p0点到px点的点积 XYZ v0 = pX - p0; XYZ v1 = p1 - p0; double dotProduct = v0.DotProduct(v1); //|v0||v1|cos α double length = v1.GetLength(); //直线的长度|v1| XYZ normalize = v1 / length; //直线单位向量 double m = dotProduct / length; //垂足的模 return (normalize * m) + p0; //var nu3 = nu / (nu2 * nu2); //XYZ result = p0 + v1 * nu3; //return result; //double num = (pX.X - p0.X) * (p1.X - p0.X) + (pX.Y - p0.Y) * (p1.Y - p0.Y) + (pX.Z - p0.Z) * (p1.Z - p0.Z); //double num2 = p0.DistanceTo(p1);//直线的长度 //num /= num2 * num2; //return new XYZ(p0.X + num * (p1.X - p0.X), p0.Y + num * (p1.Y - p0.Y), p0.Z + num * (p1.Z - p0.Z)); } private void EditSubElements(Floor floor) { SlabShapeEditor slabShapeEditor = floor.SlabShapeEditor; if (slabShapeEditor.IsEnabled) { SlabShapeVertexArray vertices = slabShapeEditor.SlabShapeVertices; List z_list = new(); foreach (SlabShapeVertex slabShapeVertex in vertices) { z_list.Add(slabShapeVertex.Position.Z); } //判断为水平板后重设形状 if (IfSameHeight(z_list)) { Level floor_level = floor.Document.GetElement(floor.LevelId) as Level; //获取楼板所在标高 double level_height = double.Parse(floor_level.LookupParameter("立面").AsValueString()); //获取所在标高的标高 double offset_height = (z_list.FirstOrDefault() * 304.8) - level_height; floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM).SetValueString(offset_height.ToString()); slabShapeEditor.ResetSlabShape(); } } } /// /// 点与平面的距离 /// /// /// /// /// private double SignedDistanceTo(Plane plane, XYZ p, out XYZ projectPoint) { XYZ v = p - plane.Origin; //垂向距离(可正可负,夹角钝角为负) double perp = v.DotProduct(plane.Normal); projectPoint = p - (perp * plane.Normal); return plane.Normal.DotProduct(v); } }