Files
ShrlAlgoToolkit/ShrlAlgoToolkit.RevitAddins/RvCommon/ModelByCurveCreatorViewModel.cs
2026-02-21 16:31:24 +08:00

548 lines
26 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.Common.Assists;
namespace ShrlAlgoToolkit.RevitAddins.RvCommon
{
public partial class ModelByCurveCreatorViewModel : ObservableValidator
{
[ObservableProperty]
public partial CenterCurveType CenterCurveType { get; set; }
[ObservableProperty]
public partial List<FamilySymbol> FamilyTypes { get; set; }
private readonly ActionEventHandler handler;
[Required(ErrorMessage = "不可为空")]
[Common.Attributes.IsNumeric]
[ObservableProperty]
public partial double InstanceOffsetX { get; set; } = 0;
[ObservableProperty]
[Required(ErrorMessage = "不可为空")]
[Common.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<Material> Materials { get; set; }
[Required(ErrorMessage = "不可为空")]
[Common.Attributes.IsNumeric]
[ObservableProperty]
public partial double OffsetX { get; set; } = 0;
[ObservableProperty]
[Required(ErrorMessage = "不可为空")]
[Common.Attributes.IsNumeric]
public partial double OffsetY { get; set; } = 0;
[Required(ErrorMessage = "不可为空")]
[Common.Attributes.IsNumeric]
[Common.Attributes.Minimum(0.5)]
[ObservableProperty]
[NotifyDataErrorInfo]
public partial double Precision { get; set; } = 1.0;
//private List<ElementId> previewIds = [];
[ObservableProperty]
public partial List<FamilySymbol> 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 = "不可为空")]
[Common.Attributes.IsNumeric]
[Common.Attributes.Minimum(100)]
[NotifyDataErrorInfo]
[ObservableProperty]
public partial double Spacing { get; set; } = 100;
public ModelByCurveCreatorViewModel(Document doc)
{
handler = new();
ProfileFamilyTypes = [.. doc.OfClass<FamilySymbol>()
.OfCategory(BuiltInCategory.OST_ProfileFamilies)
.Cast<FamilySymbol>()
.OrderBy(n => n.FamilyName)];
FamilyTypes = [.. doc.OfClass<FamilySymbol>()
.OfType<FamilySymbol>()
.Where(
s =>
s.Category.Parent == null
&& s.Family.IsEditable
&& s.Family.IsUserCreated
&& s.Family.FamilyPlacementType == FamilyPlacementType.OneLevelBased
)
.OrderBy(n => n.FamilyName)];
Materials = doc.OfClass<Material>().OrderBy(n => n.Name).OfType<Material>().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<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 = 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<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;
}
}
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<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));
}
}
}
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<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,
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<CurveElement>()
.OfType<CurveElement>()
.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<CurveLoop>();
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<GeometryObject>() { 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,
}
}