using System.ComponentModel.DataAnnotations; using System.Windows; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Nice3point.Revit.Toolkit.External.Handlers; using ShrlAlgoToolkit.RevitAddins.Assists; namespace ShrlAlgoToolkit.RevitAddins.RvCommon { public partial class ModelByCurveCreatorViewModel : ObservableValidator { [ObservableProperty] public partial CenterCurveType CenterCurveType { get; set; } [ObservableProperty] public partial List FamilyTypes { get; set; } private readonly ActionEventHandler handler; [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] [ObservableProperty] public partial double InstanceOffsetX { get; set; } = 0; [ObservableProperty] [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] public partial double InstanceOffsetY { get; set; } = 0; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(RevitAddins.RvCommon.ModelByCurveCreatorViewModel.CreateTrackCommand))] public partial bool IsRunning { get; set; } [ObservableProperty] public partial bool IsTwoSides { get; set; } = false; [ObservableProperty] public partial List Materials { get; set; } [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] [ObservableProperty] public partial double OffsetX { get; set; } = 0; [ObservableProperty] [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] public partial double OffsetY { get; set; } = 0; [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] [Attributes.Minimum(0.5)] [ObservableProperty] [NotifyDataErrorInfo] public partial double Precision { get; set; } = 1.0; //private List previewIds = []; [ObservableProperty] public partial List ProfileFamilyTypes { get; set; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(RevitAddins.RvCommon.ModelByCurveCreatorViewModel.CreateTrackCommand))] public partial FamilySymbol SelectedFamilyType { get; set; } [ObservableProperty] public partial Material SelectedMaterial { get; set; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(RevitAddins.RvCommon.ModelByCurveCreatorViewModel.CreateTrackCommand))] public partial FamilySymbol SelectedProfileFamilyType { get; set; } [Required(ErrorMessage = "不可为空")] [Attributes.IsNumeric] [Attributes.Minimum(100)] [NotifyDataErrorInfo] [ObservableProperty] public partial double Spacing { get; set; } = 100; public ModelByCurveCreatorViewModel(Document doc) { handler = new(); ProfileFamilyTypes = [.. doc.OfClass() .OfCategory(BuiltInCategory.OST_ProfileFamilies) .Cast() .OrderBy(n => n.FamilyName)]; FamilyTypes = [.. doc.OfClass() .OfType() .Where( s => s.Category.Parent == null && s.Family.IsEditable && s.Family.IsUserCreated && s.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased ) .OrderBy(n => n.FamilyName)]; Materials = doc.OfClass().OrderBy(n => n.Name).OfType().ToList(); } [RelayCommand] private void ClearFmailyTypeSelection() { if (SelectedFamilyType != null) SelectedFamilyType = null; } [RelayCommand] private void ClearMaterialSelection() { if (SelectedMaterial != null) SelectedMaterial = null; } [RelayCommand] private void ClearProfilesSelection() { if (SelectedProfileFamilyType != null) SelectedProfileFamilyType = null; } [RelayCommand(CanExecute = nameof(CanCreate))] private void CreateTrack() { handler.Raise(uiapp => { var UiDocument = uiapp.ActiveUIDocument; var doc = UiDocument.Document; List curves = null; try { IsRunning = true; switch (CenterCurveType) { case CenterCurveType.ModelCurve: { var refers = UiDocument.Selection.PickObjects( ObjectType.Element, new GenericFilter(), "请选择模型线并完成选择" ); curves = refers.Select(r => doc.GetElement(r)).OfType().Select(mc => mc.GeometryCurve).ToList(); var loops = SpatialAssist.GroupContinuousCurves(curves); if (loops.Count != 1) { MessageBox.Show("未选择路径或所选线条不止一条路径", "错误"); IsRunning = false; return; } curves = loops.FirstOrDefault(); break; } case CenterCurveType.MassCurve: { var refer = UiDocument.Selection.PickObject( ObjectType.Element, new FuncFilter(e => e is FamilyInstance importInstance && importInstance.Category.ToBuiltInCategory() == BuiltInCategory.OST_Mass), "请选择仅包含一条连续曲线的体量族" ); var instance = doc.GetElement(refer) as FamilyInstance; var geo = instance.GetGeometryObjects().FirstOrDefault(); if (geo is GeometryInstance geometryInstance) { curves = geometryInstance.GetInstanceGeometry().Where(g => g is Curve).Cast().ToList(); } break; } case CenterCurveType.DWGCurve: { var refer = UiDocument.Selection.PickObject( ObjectType.Element, new FuncFilter(e => e is ImportInstance importInstance && importInstance.Category.Name.EndsWith(".dwg")), "请选择dwg实例" ); var importInstance = doc.GetElement(refer) as ImportInstance; //curves = importInstance.GetCurves(); var geo = importInstance.GetGeometryObjects().FirstOrDefault(); if (geo is GeometryInstance geometryInstance) { var polyLine = geometryInstance.GetInstanceGeometry().FirstOrDefault() as PolyLine; curves = [polyLine.ConverToHermiteSpline()]; } break; } default: break; } } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { IsRunning = false; return; } try { if ((curves != null) && (curves.Count > 1)) { var loop = CurveLoop.Create(curves); if (!loop.IsOpen()) { MessageBox.Show("选择的模型线是闭合的,无法使用", "提示"); IsRunning = false; return; } var cs = loop.GetEnumerator(); curves = []; while (cs.MoveNext()) { curves.Add(cs.Current); } } } catch (Autodesk.Revit.Exceptions.ArgumentException) { MessageBox.Show("所选模型线不连续或包含螺旋线", "错误"); IsRunning = false; return; } List points = []; var imperial = Precision * 1000 / 304.8; for (var i = 0; i < curves.Count; i++) { var curve = curves[i]; var divide = (int)Math.Floor(curve.Length / imperial); //长度小于最小细分间距 //始终都会添加起始点 if (divide == 0) { points.Add(curve.Evaluate(0, true)); } else { for (double j = 0; j < divide; j++) { var x = j / divide; points.Add(curve.Evaluate(x, true)); } } } points.Add(curves.Last().GetEndPoint(1)); var planePoints = points.Select(p => p.Flatten()).ToList(); var spline3D = HermiteSpline.Create(points, false); doc.InvokeGroup( _ => { try { if (SelectedFamilyType != null) { doc.Invoke( _ => { var horizonSpline = HermiteSpline.Create(planePoints, false); if (!SelectedFamilyType.IsActive) { SelectedFamilyType.Activate(); } var level = new FilteredElementCollector(doc) .OfClass(typeof(Level)) .Cast() .FirstOrDefault(l => Math.Abs(l.Elevation) < 0.00001); List dataList = []; var spacing = Spacing / 304.8; var x = InstanceOffsetX / 304.8; var y = InstanceOffsetY / 304.8; var n = Math.Floor(horizonSpline.Length / spacing); for (var i = 0; i < n; i++) { //XYZ pointSplie = spline3D.Evaluate(spacing * i, false); var pointxoy = horizonSpline.Evaluate(spacing * i, false); var unboundLine = Line.CreateUnbound(pointxoy, XYZ.BasisZ); var point = unboundLine.IntersectionPoint(spline3D); var transform = spline3D.ComputeDerivatives(spacing * i, false); //族的参考方向 var referVector = transform.BasisX.CrossProduct(XYZ.BasisZ); if (IsTwoSides) { var locLeft = point.Add(-referVector.Normalize() * x) + y * XYZ.BasisZ; Autodesk.Revit.Creation.FamilyInstanceCreationData data1 = new( locLeft, SelectedFamilyType, referVector, level, Autodesk.Revit.DB.Structure.StructuralType.Footing ); dataList.Add(data1); } var loc = point.Add(referVector.Normalize() * x) + y * XYZ.BasisZ; Autodesk.Revit.Creation.FamilyInstanceCreationData data = new( loc, SelectedFamilyType, referVector, level, Autodesk.Revit.DB.Structure.StructuralType.Footing ); dataList.Add(data); } doc.Create.NewFamilyInstances2(dataList); }, "创建轨枕" ); } } catch (Exception ex) { ex.StackTrace.ToLog(); //曲线之间的过度可能不平滑,平面点在三维点交点point==null MessageBox.Show(ex.Message, "实例布置失败", MessageBoxButton.OK, MessageBoxImage.Error); IsRunning = false; } Document familyDocument = null; try { if (SelectedProfileFamilyType == null) { IsRunning = false; return; } var point = spline3D.GetEndPoint(0); //非刚性变换,会改变原几何,此变换在曲线计算导数时,为非刚性变换,各个basis的模不等于1 var transform = spline3D.ComputeDerivatives(0, false); familyDocument = doc.EditFamily(SelectedProfileFamilyType.Family); var tangent = transform.BasisX.Normalize(); var rightBasis = tangent.CrossProduct(XYZ.BasisZ).Normalize(); var topBasis = rightBasis.CrossProduct(tangent).Normalize(); if (!XYZ.IsWithinLengthLimits(point)) { MessageBox.Show("点不在Revit模型空间内,内部原点坐标与模型线相距太远", "错误"); return; } //var d0 = transform.BasisX.CrossProduct(XYZ.BasisZ).DotProduct(transform.BasisX); //var translate = Transform.CreateTranslation(transform.Origin); //var rotate = Transform.CreateRotationAtPoint( // -rightBasis, // XYZ.BasisZ.AngleTo(transform.BasisX), // XYZ.Zero); //double det = transform.Determinant; //Transform profileTransform = rotate.Multiply(translate); //构造放样截面的变换 var profileTransform = Transform.Identity; profileTransform.Origin = point; profileTransform.BasisX = rightBasis; profileTransform.BasisY = topBasis; profileTransform.BasisZ = tangent; //偏移变换 var offsetTransform = Transform.CreateTranslation(new XYZ(OffsetX, OffsetY, 0) / 304.8); //修改类型以适应选择的族类型,保证几何与族类型一致 familyDocument.Invoke(_ => { var fm = familyDocument.FamilyManager; foreach (FamilyType type in fm.Types) { if (type.Name == SelectedProfileFamilyType.Name) { fm.CurrentType = type; break; } } }); //获取截面 var profiles = familyDocument .OfClass() .OfType() .Select(c => c.GeometryCurve) .ToList(); #if DEBUG //famdoc.Invoke(_ => //{ //var plane = Plane.CreateByOriginAndBasis(point, rightBasis, topBasis); // SketchPlane sketch = SketchPlane.Create( // famdoc, // plane) // ); // var mc = famdoc.Create.NewModelCurve(spline3D, sketch); //}); #endif //famdoc.Invoke( // _ => // { // SketchPlane sketch = SketchPlane.Create(famdoc, Plane.CreateByNormalAndOrigin(XYZ.BasisZ, XYZ.Zero)); // foreach (var item in profiles) // { // var mc = famdoc.Create.NewModelCurve(item, sketch); // } // }); doc.Invoke( _ => { //if (!SelectedProfileFamilyType.IsActive) //{ // SelectedProfileFamilyType.Activate(); //} //if (loop.IsOpen()) //{ // return; //} //familyDocument.FamilyCreate.NewSweep Solid solidAnother = null; var options = new SolidOptions( SelectedMaterial == null ? ElementId.InvalidElementId : SelectedMaterial.Id, ElementId.InvalidElementId ); //获取截面的曲线集合 var list = SpatialAssist.GroupContinuousCurves(profiles); //生成截面线串 var loops = list.Select(cs => CurveLoop.Create(cs)).ToList(); //两侧放在前面,先复制 if (IsTwoSides) { var loopsAnother = new List(); var offsetTransformAnother = Transform.CreateTranslation(new XYZ(-OffsetX, OffsetY, 0) / 304.8); //var offsetTransformAnother = Transform.CreateReflection(Plane.CreateByNormalAndOrigin(XYZ.BasisX, XYZ.Zero)); foreach (var item in loops) { loopsAnother.Add(CurveLoop.CreateViaCopy(item)); } foreach (var item in loopsAnother) { item.Transform(offsetTransformAnother); item.Transform(profileTransform); } solidAnother = GeometryCreationUtilities.CreateFixedReferenceSweptGeometry( CurveLoop.Create([spline3D]), 0, 0, loopsAnother, XYZ.BasisZ, options ); } foreach (var item in loops) { item.Transform(offsetTransform); item.Transform(profileTransform); } //foreach (var item in loops) //{ // foreach (var curve in item) // { // previewIds.Add(DebugExtensions.CreateTransientElements(doc, curve)); // } //} //var solid = GeometryCreationUtilities.CreateSweptGeometry( // CurveLoop.Create([spline3D]), // 0, // 0, // loops, // options); var solid = GeometryCreationUtilities.CreateFixedReferenceSweptGeometry( CurveLoop.Create([spline3D]), 0, 0, loops, XYZ.BasisZ, options ); var geos = new List() { solid }; if (IsTwoSides && solidAnother != null) { geos.Add(solidAnother); } var shape = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel)); shape.SetShape(geos); }, "曲线布置" ); IsRunning = false; familyDocument.Close(false); //spline3D.CreateOffset(); //SketchPlane sketch = SketchPlane.Create(Document, Plane.CreateByNormalAndOrigin(p4, p1)); //var mc = Document.Create.NewModelCurve(spline3D, sketch); } catch (Exception ex) { familyDocument?.Close(false); ex.StackTrace.ToLog(); MessageBox.Show(ex.Message, "轮廓放样失败", MessageBoxButton.OK, MessageBoxImage.Error); IsRunning = false; } }, "基于曲线创建模型" ); }); } partial void OnCenterCurveTypeChanged(CenterCurveType value) { switch (value) { case CenterCurveType.ModelCurve: Precision = 1; break; case CenterCurveType.MassCurve: Precision = 1; break; case CenterCurveType.DWGCurve: Precision = 2; break; default: break; } } private bool CanCreate => !IsRunning && !HasErrors && (SelectedProfileFamilyType != null || SelectedFamilyType != null); //public double Precision //{ // get { return precision; } // set { SetProperty(ref precision, value, true); } //} //public double Spacing //{ // get { return spacing; } // set { SetProperty(ref spacing, value, true); } //} } public enum CenterCurveType { ModelCurve, MassCurve, DWGCurve, } }