Files
SzmediTools/Szmedi.RvKits/Modeling/TrackCreatorViewModel.cs

670 lines
30 KiB
C#
Raw Normal View History

2025-12-23 21:37:02 +08:00
using System.ComponentModel;
2025-09-16 16:06:41 +08:00
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 =>
{
2025-09-19 09:18:09 +08:00
if (obj is WrapperSymbol ws)
2025-09-16 16:06:41 +08:00
{
2025-09-19 09:18:09 +08:00
return ws.FullName.Contains(value);
2025-09-16 16:06:41 +08:00
}
return false;
};
ProfileFamilyTypesView.Refresh();
}
}
[ObservableProperty]
private string searchFamilyText;
partial void OnSearchFamilyTextChanged(string value)
{
if (FamilyTypes != null)
{
FamilyTypesView.Filter = obj =>
{
2025-09-19 09:18:09 +08:00
if (obj is WrapperSymbol ws)
2025-09-16 16:06:41 +08:00
{
2025-09-19 09:18:09 +08:00
return ws.FullName.Contains(value);
2025-09-16 16:06:41 +08:00
}
return false;
};
FamilyTypesView.Refresh();
}
}
2025-09-19 09:18:09 +08:00
[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();
}
}
2025-09-16 16:06:41 +08:00
[ObservableProperty]
private CenterCurveType centerCurveType;
[ObservableProperty]
2025-09-19 09:18:09 +08:00
public partial List<WrapperSymbol> FamilyTypes { get; set; }
2025-09-16 16:06:41 +08:00
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<Material> 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]
2025-09-19 09:18:09 +08:00
public partial List<WrapperSymbol> ProfileFamilyTypes { get; set; }
2025-09-16 16:06:41 +08:00
public ICollectionView ProfileFamilyTypesView { get; set; }
public ICollectionView FamilyTypesView { get; set; }
2025-09-19 09:18:09 +08:00
public ICollectionView MaterialsView { get; set; }
2025-09-16 16:06:41 +08:00
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(CreateTrackCommand))]
2025-09-19 09:18:09 +08:00
public partial WrapperSymbol SelectedFamilyType { get; set; }
2025-09-16 16:06:41 +08:00
[ObservableProperty]
2025-09-19 09:18:09 +08:00
public partial Material SelectedMaterial { get; set; }
2025-09-16 16:06:41 +08:00
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(CreateTrackCommand))]
2025-09-19 09:18:09 +08:00
public partial WrapperSymbol SelectedProfileFamilyType { get; set; }
2025-09-16 16:06:41 +08:00
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[Minimum(100)]
[NotifyDataErrorInfo]
[ObservableProperty]
private double spacing = 900;
public TrackCreatorViewModel(Document doc)
{
handler = new();
2025-09-19 09:18:09 +08:00
ProfileFamilyTypes = doc.QueryElementsByTypeAndCategory<FamilySymbol>(BuiltInCategory.OST_ProfileFamilies)
2025-09-16 16:06:41 +08:00
.Cast<FamilySymbol>()
.OrderBy(n => n.FamilyName)
2025-09-19 09:18:09 +08:00
.ToList().ConvertAll(s => new WrapperSymbol(s));
FamilyTypes = doc.QueryElementsByType<FamilySymbol>()
2025-09-16 16:06:41 +08:00
.OfType<FamilySymbol>()
.Where(
s =>
s.Category.Parent == null
&& s.Family.IsEditable
&& s.Family.IsUserCreated
&& s.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased
)
.OrderBy(n => n.FamilyName)
2025-09-19 09:18:09 +08:00
.ToList().ConvertAll(s => new WrapperSymbol(s));
Materials = doc.QueryElementsByType<Material>().OrderBy(n => n.Name).OfType<Material>().ToList();
2025-09-16 16:06:41 +08:00
ProfileFamilyTypesView = CollectionViewSource.GetDefaultView(ProfileFamilyTypes);
FamilyTypesView = CollectionViewSource.GetDefaultView(FamilyTypes);
2025-09-19 09:18:09 +08:00
MaterialsView = CollectionViewSource.GetDefaultView(Materials);
2025-09-16 16:06:41 +08:00
}
[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<Curve> curves = null;
try
{
IsRunning = true;
switch (CenterCurveType)
{
case CenterCurveType.ModelCurve:
{
var refers = UiDocument.Selection.PickObjects(
ObjectType.Element,
new GenericFilter<ModelCurve>(),
"请选择模型线并完成选择"
);
curves = refers.Select(r => doc.GetElement(r)).OfType<ModelCurve>().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<Curve>().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<Reference> refers = UiDocument.Selection.PickObjects(
// ObjectType.Element,
// new GenericFilter<ModelCurve>(),
// "请选择模型线并完成选择"
// );
// curves = refers.Select(r => doc.GetElement(r)).OfType<ModelCurve>().Select(mc => mc.GeometryCurve).ToList();
// List<List<Curve>> 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<XYZ> 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);
2025-09-19 09:18:09 +08:00
if (!SelectedFamilyType.FamilySymbol.IsActive)
2025-09-16 16:06:41 +08:00
{
2025-09-19 09:18:09 +08:00
SelectedFamilyType.FamilySymbol.Activate();
2025-09-16 16:06:41 +08:00
}
//找到零标高
var level = new FilteredElementCollector(doc)
.OfClass(typeof(Level))
.Cast<Level>()
.FirstOrDefault(l => Math.Abs(l.Elevation) < 0.00001);
List<Autodesk.Revit.Creation.FamilyInstanceCreationData> 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,
2025-09-19 09:18:09 +08:00
SelectedFamilyType.FamilySymbol,
2025-09-16 16:06:41 +08:00
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,
2025-09-19 09:18:09 +08:00
SelectedFamilyType.FamilySymbol,
2025-09-16 16:06:41 +08:00
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);
2025-09-19 09:18:09 +08:00
familyDocument = doc.EditFamily(SelectedProfileFamilyType.FamilySymbol.Family);
2025-09-16 16:06:41 +08:00
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)
{
2025-09-19 09:18:09 +08:00
if (type.Name == SelectedProfileFamilyType.FamilySymbol.Name)
2025-09-16 16:06:41 +08:00
{
fm.CurrentType = type;
break;
}
}
});
//获取截面
var profiles = familyDocument
.QueryElementsByType<CurveElement>()
.OfType<CurveElement>()
.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<CurveLoop> 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<GeometryObject> 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); }
//}
}
2025-09-19 09:18:09 +08:00
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}";
}
}
2025-09-16 16:06:41 +08:00
public enum CenterCurveType
{
ModelCurve,
MassCurve,
DWGCurve,
}
}