using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Windows; using System.Windows.Data; using Autodesk.Revit.DB; using Autodesk.Revit.UI.Selection; using Nice3point.Revit.Toolkit.External.Handlers; using Szmedi.RvKits.Assists; using Szmedi.RvKits.Properties; namespace Szmedi.RvKits.Modeling { public partial class TrackCreatorViewModel : ObservableValidator { [ObservableProperty] private string searchProfileText; partial void OnSearchProfileTextChanged(string value) { if (ProfileFamilyTypes != null) { ProfileFamilyTypesView.Filter = obj => { if (obj is WrapperSymbol ws) { return ws.FullName.Contains(value); } return false; }; ProfileFamilyTypesView.Refresh(); } } [ObservableProperty] private string searchFamilyText; partial void OnSearchFamilyTextChanged(string value) { if (FamilyTypes != null) { FamilyTypesView.Filter = obj => { if (obj is WrapperSymbol ws) { return ws.FullName.Contains(value); } return false; }; FamilyTypesView.Refresh(); } } [ObservableProperty] private string searchMaterialText; partial void OnSearchMaterialTextChanged(string value) { if (Materials != null) { MaterialsView.Filter = obj => { if (obj is Material mat) { return mat.Name.Contains(value); } return false; }; MaterialsView.Refresh(); } } [ObservableProperty] private CenterCurveType centerCurveType; [ObservableProperty] public partial List FamilyTypes { get; set; } private readonly ActionEventHandler handler; [Required(ErrorMessage = "不可为空")] [IsNumeric] [ObservableProperty] private double instanceOffsetX = 717.5; [ObservableProperty] [Required(ErrorMessage = "不可为空")] [IsNumeric] private double instanceOffsetY = -216; [ObservableProperty] private bool isModelCurve = true; [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateTrackCommand))] private bool isRunning; [ObservableProperty] private bool isTwoSides = false; [ObservableProperty] private List materials; [Required(ErrorMessage = "不可为空")] [IsNumeric] [ObservableProperty] private double offsetX = 717.5; [ObservableProperty] [Required(ErrorMessage = "不可为空")] [IsNumeric] private double offsetY = 0; [Required(ErrorMessage = "不可为空")] [IsNumeric] [Minimum(0.5)] [ObservableProperty] [NotifyDataErrorInfo] private double precision = 1; [ObservableProperty] private System.Windows.Media.ImageSource previewImage = Resources.TrackPic.ToBitmapSource(); [ObservableProperty] public partial List ProfileFamilyTypes { get; set; } public ICollectionView ProfileFamilyTypesView { get; set; } public ICollectionView FamilyTypesView { get; set; } public ICollectionView MaterialsView { get; set; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateTrackCommand))] public partial WrapperSymbol SelectedFamilyType { get; set; } [ObservableProperty] public partial Material SelectedMaterial { get; set; } [ObservableProperty] [NotifyCanExecuteChangedFor(nameof(CreateTrackCommand))] public partial WrapperSymbol SelectedProfileFamilyType { get; set; } [Required(ErrorMessage = "不可为空")] [IsNumeric] [Minimum(100)] [NotifyDataErrorInfo] [ObservableProperty] private double spacing = 900; public TrackCreatorViewModel(Document doc) { handler = new(); ProfileFamilyTypes = doc.QueryElementsByTypeAndCategory(BuiltInCategory.OST_ProfileFamilies) .Cast() .OrderBy(n => n.FamilyName) .ToList().ConvertAll(s => new WrapperSymbol(s)); FamilyTypes = doc.QueryElementsByType() .OfType() .Where( s => s.Category.Parent == null && s.Family.IsEditable && s.Family.IsUserCreated && s.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased ) .OrderBy(n => n.FamilyName) .ToList().ConvertAll(s => new WrapperSymbol(s)); Materials = doc.QueryElementsByType().OrderBy(n => n.Name).OfType().ToList(); ProfileFamilyTypesView = CollectionViewSource.GetDefaultView(ProfileFamilyTypes); FamilyTypesView = CollectionViewSource.GetDefaultView(FamilyTypes); MaterialsView = CollectionViewSource.GetDefaultView(Materials); } [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 = CurveUtils.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; } //if (IsModelCurve) //{ // IList refers = UiDocument.Selection.PickObjects( // ObjectType.Element, // new GenericFilter(), // "请选择模型线并完成选择" // ); // curves = refers.Select(r => doc.GetElement(r)).OfType().Select(mc => mc.GeometryCurve).ToList(); // List> loops = new CurveUtils().GetCurvesOfLoops(curves); // if (loops.Count != 1) // { // MessageBox.Show("未选择路径或所选线条不止一条路径", "错误"); // IsRunning = false; // return; // } // curves = loops.FirstOrDefault(); //} //else //{ // Reference refer = UiDocument.Selection.PickObject( // ObjectType.Element, // new FuncFilter(e => e is ImportInstance importInstance && importInstance.Category.Name.EndsWith(".dwg")), // "请选择dwg实例" // ); // ImportInstance importInstance = doc.GetElement(refer) as ImportInstance; // curves = importInstance.GetCurves(); //} } catch (Autodesk.Revit.Exceptions.OperationCanceledException) { IsRunning = false; return; } try { if ((curves != null) && (curves.Count > 1)) { CurveUtils.SortCurvesContiguous(curves); 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)); } } //var li = curves[i].Tessellate(); //li.Remove(li.Last()); //points.AddRange(li); } points.Add(curves[curves.Count - 1].GetEndPoint(1)); var planePoints = points.Select(p => new XYZ(p.X, p.Y, 0)).ToList(); var spline3D = HermiteSpline.Create(points, false); doc.InvokeGroup( _ => { //SketchPlane sketch = SketchPlane.Create(doc, Plane.CreateByNormalAndOrigin(XYZ.BasisZ, spline3D.GetEndPoint(0))); //var mc = doc.Create.NewModelCurve(spline3D, sketch); try { //实例布置 if (SelectedFamilyType != null) { doc.Invoke( _ => { var horizonSpline = HermiteSpline.Create(planePoints, false); if (!SelectedFamilyType.FamilySymbol.IsActive) { SelectedFamilyType.FamilySymbol.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.FamilySymbol, 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.FamilySymbol, referVector, level, Autodesk.Revit.DB.Structure.StructuralType.Footing ); dataList.Add(data); } doc.Create.NewFamilyInstances2(dataList); IsRunning = false; }, "创建轨枕、扣件" ); } } catch (Exception ex) { LogAssists.WriteLog(ex.StackTrace); 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.FamilySymbol.Family); XYZ tangent; XYZ rightBasis; XYZ topBasis; tangent = transform.BasisX.Normalize(); rightBasis = tangent.CrossProduct(XYZ.BasisZ).Normalize(); topBasis = rightBasis.CrossProduct(tangent).Normalize(); if (!XYZ.IsWithinLengthLimits(point)) { MessageBox.Show("点不在Revit模型空间内,内部原点坐标与模型线相距太远", "错误"); return; } //Plane plane = Plane.CreateByOriginAndBasis(point, rightBasis, topBasis); //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 d = rightBasis.DotProduct(transform.BasisX); //if (!profileTransform.IsConformal) //{ // MessageBox.Show($"起始段必须是直线", "创建失败", MessageBoxButton.OK, MessageBoxImage.Error); // IsRunning = false; // return; //} //偏移变换 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.FamilySymbol.Name) { fm.CurrentType = type; break; } } }); //获取截面 var profiles = familyDocument .QueryElementsByType() .OfType() .Select(c => c.GeometryCurve) .ToList(); #if DEBUG //doc.Invoke(_ => //{ // SketchPlane sketch = SketchPlane.Create( // doc, // Plane.CreateByNormalAndOrigin(XYZ.BasisZ, spline3D.GetEndPoint(0)) // ); // var mc = doc.Create.NewModelCurve(spline3D, sketch); //}); #endif //doc.Invoke( // _ => // { // SketchPlane sketch = SketchPlane.Create(doc, Plane.CreateByNormalAndOrigin(XYZ.BasisZ, XYZ.Zero)); // foreach (var item in profiles) // { // var mc = doc.Create.NewModelCurve(item, sketch); // } // }); doc.Invoke( _ => { //if (!SelectedProfileFamilyType.IsActiveZoom) //{ // SelectedProfileFamilyType.Activate(); //} //if (loop.IsOpen()) //{ // return; //} //familyDocument.FamilyCreate.NewSweep //材质处理 Solid solidMirror = null; SolidOptions options = new( SelectedMaterial == null ? ElementId.InvalidElementId : SelectedMaterial.Id, ElementId.InvalidElementId ); //获取截面的曲线集合 var list = CurveUtils.GroupContinuousCurves(profiles); //生成线串 var loops = list.Select(cs => CurveLoop.Create(cs)).ToList(); if (IsTwoSides) { List loopsAnother = []; var offsetTransformAnother = Transform.CreateTranslation(new XYZ(-OffsetX, OffsetY, 0) / 304.8); foreach (var item in loops) { loopsAnother.Add(CurveLoop.CreateViaCopy(item)); } foreach (var item in loopsAnother) { item.Transform(offsetTransformAnother); item.Transform(profileTransform); } solidMirror = GeometryCreationUtilities.CreateFixedReferenceSweptGeometry( CurveLoop.Create([spline3D]), 0, 0, loopsAnother, XYZ.BasisZ, options ); } foreach (var item in loops) { item.Transform(offsetTransform); item.Transform(profileTransform); } var solid = GeometryCreationUtilities.CreateFixedReferenceSweptGeometry( CurveLoop.Create([spline3D]), 0, 0, loops, XYZ.BasisZ, options ); List geos = [solid]; if (IsTwoSides && solidMirror != null) { geos.Add(solidMirror); } doc.CreateDirectShapeInstance( "轨道", BuiltInCategory.OST_GenericModel, geos); //var shape = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_GenericModel)); //shape.SetShape(geos); }, "创建钢轨" ); IsRunning = false; familyDocument.Close(false); //spline3D.CreateOffset(); //SketchPlane sketch = SketchPlane.Create(doc, Plane.CreateByNormalAndOrigin(p4, p1)); //var mc = doc.Create.NewModelCurve(spline3D, sketch); } catch (Exception ex) { familyDocument?.Close(false); LogAssists.WriteLog(ex.StackTrace); 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 class WrapperSymbol { public FamilySymbol FamilySymbol { get; } public WrapperSymbol(FamilySymbol familySymbol) { FamilySymbol = familySymbol; } public string FullName => $"{FamilySymbol.FamilyName} : {FamilySymbol.Name}"; public override string ToString() { return $"{FamilySymbol.FamilyName} : {FamilySymbol.Name}"; } } public enum CenterCurveType { ModelCurve, MassCurve, DWGCurve, } }