优化更新代码,添加界面功能并整合

This commit is contained in:
GG Z
2025-02-10 20:53:40 +08:00
parent 83b846f15f
commit 978e03a67f
1389 changed files with 95739 additions and 22200 deletions

View File

@@ -0,0 +1,24 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class BricksFinishesCmd : ExternalCommand
{
public override void Execute()
{
WinDialogHelper.ShowModeless<BricksFinishesView>(new BricksFinishesViewModel());
//var fl = SingletonViewHelpers<BricksFinishesView>.GetInstance(out var isNewCreate);
//if (isNewCreate)
//{
// fl.DataContext = new BricksFinishesViewModel();
// fl.ShowAhead();
//}
//fl.Activate();
}
}

View File

@@ -0,0 +1,68 @@
<ui:FluentWindowEx
x:Class="ShrlAlgo.RvKits.RvCivil.BricksFinishesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rvCivil="clr-namespace:ShrlAlgo.RvKits.RvCivil"
xmlns:ui="https://github.com/ShrlAlgo/WPFluent"
Title="铺贴"
Width="180"
Height="500"
MinHeight="500"
d:DataContext="{d:DesignInstance rvCivil:BricksFinishesViewModel}"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary Source="pack://application:,,,/ShrlAlgo.RvKits;component/WPFUI.xaml" />
</Window.Resources>
<ui:AutoGridEx ChildMargin="5" Columns="*">
<ui:TextBoxEx
x:Name="TbPavementLength"
InputMethod.IsInputMethodEnabled="False"
Prefix="砖长:"
Suffix="mm"
Text="{Binding PavementLength, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbPavementWidth"
InputMethod.IsInputMethodEnabled="False"
Prefix="砖宽:"
Suffix="mm"
Text="{Binding PavementWidth, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbPavementGap"
InputMethod.IsInputMethodEnabled="False"
Prefix="砖缝:"
Suffix="mm"
Text="{Binding PavementGap, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbPavementThickness"
InputMethod.IsInputMethodEnabled="False"
Prefix="砖厚:"
Suffix="mm"
Text="{Binding PavementThickness, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbPavementBaseOffset"
InputMethod.IsInputMethodEnabled="False"
Prefix="墙砖底标高:"
Suffix="mm"
Text="{Binding WallBaseOffset, StringFormat={}{0:F2}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbPavementName"
Prefix="铺砖名(类型名)"
Text="{Binding PanelName, UpdateSourceTrigger=PropertyChanged}" />
<GroupBox Grid.Row="1" Header="铺设方式">
<UniformGrid Rows="1">
<RadioButton Content="房间" IsChecked="{Binding ByRoom, UpdateSourceTrigger=PropertyChanged}" />
<RadioButton Content="平面" IsChecked="{Binding ByRoom, Converter={StaticResource InvertBooleanConverter}}" />
</UniformGrid>
</GroupBox>
<CheckBox Content="边界留缝" IsChecked="{Binding IsEdgeExistGap, UpdateSourceTrigger=PropertyChanged}" />
<Button
x:Name="BtnByFace"
HorizontalAlignment="Stretch"
Command="{Binding PlaceBricksCommand}"
Content="铺设"
IsEnabled="{Binding HasErrors, Mode=OneWay, Converter={StaticResource InvertBooleanConverter}, UpdateSourceTrigger=PropertyChanged}" />
</ui:AutoGridEx>
</ui:FluentWindowEx>

View File

@@ -0,0 +1,15 @@
using System.Windows;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// BricksFinishesView.xaml 的交互逻辑
/// </summary>
public partial class BricksFinishesView
{
public BricksFinishesView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,838 @@
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
using ShrlAlgo.Toolkit.Mvvm.Attributes;
using System.ComponentModel.DataAnnotations;
using System.Windows;
namespace ShrlAlgo.RvKits.RvCivil;
public partial class BricksFinishesViewModel : ObservableValidator
{
public BricksFinishesViewModel()
{
finishesHandler = new ActionEventHandler();
PavementLength = 600;
PavementWidth = 600;
PavementGap = 2;
WallBaseOffset = 0;
PavementThickness = 2;
PanelName = "铺砖";
}
private readonly ActionEventHandler finishesHandler;
[ObservableProperty]
private bool byRoom;
//边界限具有缝隙
[ObservableProperty]
private bool isEdgeExistGap;
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[Minimum(10.0)]
[ObservableProperty]
[NotifyDataErrorInfo]
private double pavementWidth;
//public double PavementWidth
//{
// get => pavementWidth;
// set => SetProperty(ref pavementWidth, value, true);
//}
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[ObservableProperty]
[NotifyDataErrorInfo]
private double wallBaseOffset;
// public double WallBaseOffset
//{
// get => wallBaseOffset;
// set => SetProperty(ref wallBaseOffset, value, true);
//}
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[Minimum(1.0)]
[ObservableProperty]
[NotifyDataErrorInfo]
private double pavementThickness;
// public double PavementThickness
//{
// get => pavementThickness;
// set => SetProperty(ref pavementThickness, value, true);
//}
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[Minimum(10.0)]
[ObservableProperty]
[NotifyDataErrorInfo]
private double pavementLength;
// public double PavementLength
//{
// get => pavementLength;
// set => SetProperty(ref pavementLength, value, true);
//}
[Required(ErrorMessage = "不可为空")]
[IsNumeric]
[Minimum(1.0)]
[ObservableProperty]
[NotifyDataErrorInfo]
private double pavementGap;
// public double PavementGap
//{
// get => pavementGap;
// set => SetProperty(ref pavementGap, value, true);
//}
[Required(ErrorMessage = "不可为空")]
[MinLength(1)]
[ObservableProperty]
[NotifyDataErrorInfo]
private string panelName;
// public string PanelName
//{
// get => panelName;
// set
// {
// SetProperty(ref panelName, value, true);
// PlaceBricksCommand.NotifyCanExecuteChanged();
// }
//}
private static CurtainSystemType SetCurtainSystemType(Document doc, double length, double width, double gap, double thickness, string panelName)
{
var curtainSystemTypeCollector = new FilteredElementCollector(doc)
.OfClass(typeof(CurtainSystemType))
.OfCategory(BuiltInCategory.OST_CurtaSystem)
.Cast<CurtainSystemType>()
.ToList();
var panelTypeCollector = new FilteredElementCollector(doc)
.OfClass(typeof(PanelType))
.OfCategory(BuiltInCategory.OST_CurtainWallPanels)
.Cast<PanelType>()
.ToList();
var curtainWallMullionsTypeCollector = new FilteredElementCollector(doc)
.OfClass(typeof(MullionType))
.OfCategory(BuiltInCategory.OST_CurtainWallMullions)
.Cast<MullionType>()
.ToList();
CurtainSystemType curtainSystemType = null;
PanelType panelType = null;
MullionType mullionType = null;
var curtainSystemTypeName = $"铺砖-{length}mm x {width}mm";
var panelTypeName = panelName;
var mullionTypeName = $"{gap}mm x {thickness}mm";
//创建嵌板类型
try
{
var defaultPanelType = panelTypeCollector.FirstOrDefault();
panelType = defaultPanelType.Duplicate(panelTypeName) as PanelType;
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
foreach (var t in panelTypeCollector)
{
if (t.Name == panelTypeName)
{
panelType = t;
break;
}
}
}
panelType?.get_Parameter(BuiltInParameter.CURTAIN_WALL_SYSPANEL_THICKNESS).Set(thickness / 304.8);
//嵌板偏移
panelType?.get_Parameter(BuiltInParameter.CURTAIN_WALL_SYSPANEL_OFFSET).Set(0);
//创建竖挺类型
try
{
var defaultMullionType = curtainWallMullionsTypeCollector.FirstOrDefault(
m => m.FamilyName.Contains("矩形") || m.FamilyName.Contains("Rectangular")
);
mullionType = defaultMullionType.Duplicate(mullionTypeName) as MullionType;
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
foreach (var t in curtainWallMullionsTypeCollector)
{
if (t.Name == mullionTypeName)
{
mullionType = t;
break;
}
}
}
//设置参数
mullionType?.get_Parameter(BuiltInParameter.RECT_MULLION_WIDTH1).Set(gap / 304.8 / 2);
mullionType?.get_Parameter(BuiltInParameter.RECT_MULLION_WIDTH2).Set(gap / 304.8 / 2);
mullionType?.get_Parameter(BuiltInParameter.RECT_MULLION_THICK).Set(thickness / 304.8);
//创建幕墙系统类型
try
{
var defaultCurtainSystemType = curtainSystemTypeCollector.FirstOrDefault();
curtainSystemType = defaultCurtainSystemType.Duplicate(curtainSystemTypeName) as CurtainSystemType;
}
catch (Autodesk.Revit.Exceptions.ArgumentException)
{
foreach (var t in curtainSystemTypeCollector)
{
if (t.Name == curtainSystemTypeName)
{
curtainSystemType = t;
break;
}
}
}
//mullionType.get_Parameter(BuiltInParameter.MULLION_OFFSET).Set(0);
//mullionType.get_Parameter(BuiltInParameter.MULLION_OFFSET).Set(thickness / 304.8 );
curtainSystemType?.get_Parameter(BuiltInParameter.SPACING_LAYOUT_1).Set(1);
curtainSystemType?.get_Parameter(BuiltInParameter.SPACING_LAYOUT_2).Set(1);
//panelType.get_Parameter(BuiltInParameter.CURTAIN_WALL_SYSPANEL_OFFSET).Set(-thickness / 304.8 / 2);
curtainSystemType?.get_Parameter(BuiltInParameter.SPACING_LENGTH_1).Set((width + gap) / 304.8);
curtainSystemType?.get_Parameter(BuiltInParameter.SPACING_LENGTH_2).Set((length + gap) / 304.8);
if (mullionType != null && panelType != null)
{
curtainSystemType?.get_Parameter(BuiltInParameter.AUTO_PANEL).Set(panelType.Id);
curtainSystemType?.get_Parameter(BuiltInParameter.AUTO_MULLION_INTERIOR_GRID1).Set(mullionType.Id);
curtainSystemType?.get_Parameter(BuiltInParameter.AUTO_MULLION_INTERIOR_GRID2).Set(mullionType.Id);
}
curtainSystemType?.get_Parameter(BuiltInParameter.CURTAINGRID_ADJUST_BORDER_1).Set(1);
curtainSystemType?.get_Parameter(BuiltInParameter.CURTAINGRID_ADJUST_BORDER_2).Set(1);
//curtainSystemType.get_Parameter(BuiltInParameter.AUTO_MULLION_BORDER1_GRID1).Set(mullionType.ViewId);
//curtainSystemType.get_Parameter(BuiltInParameter.AUTO_MULLION_BORDER1_GRID2).Set(mullionType.ViewId);
//curtainSystemType.get_Parameter(BuiltInParameter.AUTO_MULLION_BORDER2_GRID1).Set(mullionType.ViewId);
//curtainSystemType.get_Parameter(BuiltInParameter.AUTO_MULLION_BORDER2_GRID2).Set(mullionType.ViewId);
return curtainSystemType;
}
/// <summary>
/// 获取偏移量
/// </summary>
/// <param name="coord">UV平面坐标系</param>
/// <param name="start">拾取的起铺点</param>
/// <returns></returns>
private static double[,] GetOffset(List<XYZ> coord, XYZ start)
{
var origin = coord[0];
//var insterIntersectionResult = line.ProjectOf(origin);
//var project = insterIntersectionResult?.XYZPoint;
//var dis = insterIntersectionResult?.DistanceX;
//var uDirection = XYZ.Zero;
//var vDirection = XYZ.Zero;
//bool lineIsU = false;
double uoffest;
double voffest;
var p = start - origin;
//u轴进行的偏移实际是v轴沿着u轴的偏移值故应相反设置偏移值
//因为轴的模长度为1所以直接等于p在轴上的投影长度
uoffest = p.DotProduct(coord[1]);
//if (coord[1].CrossProduct(p).Z < 0)
//{
// uoffest = -uoffest;
//}
voffest = p.DotProduct(coord[2]);
//if (coord[2].CrossProduct(p).Z < 0)
//{
// voffest = -voffest;
//}
//if (coord[1].CrossProduct(line.Direction).IsAlmostEqualTo(XYZ.Zero))
//{
// uoffest = coord[1].DotProduct(p) * coord[1];
// voffest = coord[2].DotProduct(p) * coord[2];
// ////得到起铺点相对幕墙系统原点的偏移量
// //double offest = default;
// //if ((start - origin).Normalize().IsAlmostEqualTo(coord[1]))
// //{
// // offest = (start - origin).GetLength();
// //}
// //else
// //{
// // offest = -(start - origin).GetLength();
// //}
// //result.Add(coord[1], offest);//u
// //if ((project - origin).Normalize().IsAlmostEqualTo(coord[2]))
// //{
// // offest = (project - origin).GetLength();
// //}
// //else
// //{
// // offest = -(project - origin).GetLength();
// //}
// //result.Add(coord[2], offest);//v
//}
//else if (coord[2].CrossProduct(line.Direction).IsAlmostEqualTo(XYZ.Zero))
//{
// isInvert = true;
// uoffest = coord[2].DotProduct(p) * coord[2];
// voffest = coord[1].DotProduct(p) * coord[1];
// //double offest = default;
// //if ((project - origin).Normalize().IsAlmostEqualTo(coord[1]))
// //{
// // offest = (project - origin).GetLength();
// //}
// //else
// //{
// // offest = -(project - origin).GetLength();
// //}
// //result.Add(coord[1], offest);//v
// //if ((start - origin).DotProduct(coord[1]) > 0)
// //{
// // offest = (start - origin).GetLength();
// //}
// //else
// //{
// // offest = -(start - origin).GetLength();
// //}
// //result.Add(coord[1], offest);//v
//}
////if (project != null)
////{
//// var offset = project.DistanceTo(start);
//// //判断正方向还是反方向
//// if ((start - project).Normalize().IsAlmostEqualTo(-coord[1]))
//// {
//// offset = -offset;
//// }
//// //var direction = XYZ.BasisZ.CrossProduct(pl.FaceNormal);
//// //if (direction.IsAlmostEqualTo(line.Direction))
//// //{
//// // offset = xyz.DistanceTo(startPoint) * 304.8;
//// //}
//// //else
//// //{
//// // offset = xyz.DistanceTo(endPoint) * 304.8;
//// //}
//// //offset = x;
//// var n = Math.Floor(offset / interval);
//// offset -= Math.Floor(n) * interval;
//// return offset;
////}
//if (voffest == null || uoffest == null)
//{
// return null;
//}
return new[,]
{
{ uoffest, voffest }
};
}
private static FaceArray GetFaceArray(Document doc, List<CurveLoop> curveLoops, out DirectShape ds)
{
FaceArray array = new();
var solid = GeometryCreationUtilities.CreateExtrusionGeometry(curveLoops, -XYZ.BasisZ, 10.0);
ds = DirectShape.CreateElement(doc, new ElementId(BuiltInCategory.OST_Mass));
ds?.AppendShape(new List<GeometryObject> { solid });
foreach (Face face in solid.Faces)
{
var pf = face as PlanarFace;
if (pf.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
{
array.Append(face);
break;
}
}
return array;
}
/// <summary>
/// 获取最终幕墙系统坐标
/// </summary>
/// <param name="doc"></param>
/// <param name="array"></param>
/// <param name="line"></param>
/// <param name="rotation"></param>
/// <returns></returns>
private static List<XYZ> GetCoordinateSystem(Document doc, FaceArray array, Line line, out Rotation rotation)
{
var curtainSystemTypeCollector = new FilteredElementCollector(doc)
.OfClass(typeof(CurtainSystemType))
.OfCategory(BuiltInCategory.OST_CurtaSystem);
XYZ origin = null;
XYZ u = null;
XYZ v = null;
rotation = null;
using (Transaction trans = new(doc, "获取坐标系"))
{
trans.Start();
if (curtainSystemTypeCollector.FirstOrDefault() is CurtainSystemType defaultCurtainSystemType)
{
defaultCurtainSystemType.get_Parameter(BuiltInParameter.SPACING_LAYOUT_1).Set(2);
defaultCurtainSystemType.get_Parameter(BuiltInParameter.SPACING_LAYOUT_2).Set(2);
var curtainSystem = doc.Create.NewCurtainSystem(array, defaultCurtainSystemType);
//编号
curtainSystem.get_Parameter(BuiltInParameter.SPACING_NUM_DIVISIONS_1).Set(1);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_NUM_DIVISIONS_2).Set(1);
//对正
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_1).Set(2);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_2).Set(2);
//
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_1).Set(0);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_2).Set(0);
doc.Regenerate();
origin = GetOrigin(doc, curtainSystem, out u, out v);
var list = new List<XYZ> { origin, u, v };
//获取旋转角度
rotation = GetRotation(list, line);
//设置旋转角
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_1).Set(rotation.Radian);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_2).Set(rotation.Radian);
//得到转换后的角度
doc.Regenerate();
origin = GetOrigin(doc, curtainSystem, out u, out v);
}
trans.RollBack();
//trans.Commit();
}
return [origin, u, v];
}
/// <summary>
/// 得到旋转角度
/// </summary>
/// <param name="coordSystem"></param>
/// <param name="line"></param>
/// <returns></returns>
private static Rotation GetRotation(List<XYZ> coordSystem, Line line)
{
var radian1 = coordSystem[1].AngleTo(line.Direction);
Rotation rotation1 =
new()
{
Radian = radian1,
Direction = line.Direction,
Orientation = Orientation.U,
CoordSystemAxis = coordSystem[1]
};
var radian2 = coordSystem[1].AngleTo(-line.Direction);
Rotation rotation2 =
new()
{
Radian = radian2,
Direction = -line.Direction,
Orientation = Orientation.U,
CoordSystemAxis = coordSystem[1]
};
var radian3 = coordSystem[2].AngleTo(line.Direction);
Rotation rotation3 =
new()
{
Radian = radian3,
Direction = line.Direction,
Orientation = Orientation.V,
CoordSystemAxis = coordSystem[2]
};
var radian4 = coordSystem[2].AngleTo(-line.Direction);
Rotation rotation4 =
new()
{
Radian = radian4,
Direction = -line.Direction,
Orientation = Orientation.V,
CoordSystemAxis = coordSystem[2]
};
List<Rotation> rotations = [rotation1, rotation2, rotation3, rotation4];
var rotation = rotations.OrderBy(r => r.Radian).First();
if (rotation.CoordSystemAxis.CrossProduct(rotation.Direction).Z < 0)
{
rotation.Radian = -rotation.Radian;
}
return rotation;
//double radian;
////grid2的轴向单位向量
////在平面
//if (pl.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ))
//{
// var perpendicularVector = line.Direction.CrossProduct(XYZ.BasisZ);
// if (perpendicularVector.Y < 0)
// {
// perpendicularVector = -perpendicularVector;
// }
// radian = perpendicularVector.X > 0 ? -perpendicularVector.AngleTo(XYZ.BasisY) : perpendicularVector.AngleTo(XYZ.BasisY);
// //angle = radian * 180 / Math.PI;
// if (Math.Abs(Math.Round(radian, MidpointRounding.AwayFromZero) - Math.PI / 2) < 0.0001)
// {
// radian = 0;
// }
//}
//else//在垂直面
//{
// radian = 0;
//}
//return radian;
}
private static void CreateCurtainSystemByFace(
UIDocument uidoc,
double length,
double width,
double gap,
double thickness,
double wallBaseOffset,
string panelName,
bool isEdgeExistGap
)
{
var doc = uidoc.Document;
var faceReference = uidoc.Selection.PickObject(ObjectType.Face, "请选择铺贴的面");
var element = doc.GetElement(faceReference);
double baseHeight = default;
double height = default;
doc.InvokeGroup(
tg =>
{
if (element is Wall)
{
doc.Invoke(
ts =>
{
//顶部约束
var topConstraint = element.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
if (topConstraint == ElementId.InvalidElementId) //未连接
{
//无连接高度
height = element.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).AsDouble();
element.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(height - (wallBaseOffset / 304.8));
}
baseHeight = element.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).AsDouble();
element.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(baseHeight + (wallBaseOffset / 304.8));
},
"设置墙底部高度"
);
}
var face = element.GetGeometryObjectFromReference(faceReference) as Face;
var pl = face as PlanarFace;
if (pl == null)
{
return;
}
double radian = 0;
double actualU = 0;
double actualV = 0;
FaceArray array = new();
array.Append(face);
var referEdge = uidoc.Selection.PickObject(ObjectType.Edge, new EdgeFilter(element), "请选择对齐的边");
var xyz = uidoc.Selection.PickPoint(
ObjectSnapTypes.Intersections
| ObjectSnapTypes.Endpoints
| ObjectSnapTypes.Centers
| ObjectSnapTypes.Midpoints
| ObjectSnapTypes.Points
| ObjectSnapTypes.Nearest
| ObjectSnapTypes.Perpendicular
| ObjectSnapTypes.WorkPlaneGrid
| ObjectSnapTypes.Quadrants
| ObjectSnapTypes.Tangents,
"请选择起铺点"
);
var edgeObj = element.GetGeometryObjectFromReference(referEdge) as Edge;
//var ourterEdgeArray = face.EdgeLoops.get_Item(0);
if (edgeObj != null && edgeObj.AsCurve() is Line)
{
var line = edgeObj.AsCurve() as Line;
var coordinateSystem = GetCoordinateSystem(doc, array, line, out var rotation);
var startPoint = line!.GetEndPoint(0);
var endPoint = line.GetEndPoint(1);
var start = line.Project(xyz).XYZPoint;
var totalLength = start.DistanceTo(startPoint) + start.DistanceTo(endPoint);
//判断点是否在直线上
if (totalLength - line.Length > 0.0001)
{
MessageBox.Show("选择点不在对齐的边上", "错误");
return;
}
radian = rotation.Radian;
var uv = GetOffset(coordinateSystem, start);
if (uv != null)
{
//原始起铺点基于幕墙系统原点平面uv偏移量
var intervalU = (length + gap) / 304.8;
var intervalV = (width + gap) / 304.8;
var n = Math.Floor(uv[0, 0] / intervalU);
var m = Math.Floor(uv[0, 1] / intervalV);
if (isEdgeExistGap)
{
actualU = uv[0, 0] - (n * intervalU);
actualV = uv[0, 1] - (m * intervalV);
}
else
{
actualU = uv[0, 0] - (n * intervalU) - (gap / 2 / 304.8);
actualV = uv[0, 1] - (m * intervalV) - (gap / 2 / 304.8);
}
}
}
CurtainSystem curtainSystem = null;
//doc.Regenerate();
doc.Invoke(
_ =>
{
var curtainSystemType = SetCurtainSystemType(doc, length, width, gap, thickness, panelName);
curtainSystem = doc.Create.NewCurtainSystem(array, curtainSystemType);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_1).Set(radian);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_2).Set(radian);
doc.Regenerate();
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_1).Set(actualU);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_2).Set(actualV);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_1).Set(2);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_2).Set(2);
ElementTransformUtils.MoveElement(doc, curtainSystem.Id, pl.FaceNormal * thickness / 304.8 / 2);
},
"创建幕墙系统"
);
if (element is Wall)
{
doc.Invoke(
_ =>
{
element.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(baseHeight);
var topConstraint = element.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
if (topConstraint == ElementId.InvalidElementId) //未连接
{
//无连接高度
element.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(height);
}
},
"设置墙偏移"
);
}
if (curtainSystem != null)
{
doc.Invoke(_ =>
{
var iterator = curtainSystem.CurtainGrids.ForwardIterator();
iterator.MoveNext();
var grid = iterator.Current as CurtainGrid;
var panels = grid!.GetPanelIds().Select(id => doc.GetElement(id) as Panel);
var area = width / 304.8 * length / 304.8;
var errorPanels =
from panel in panels
where panel!.get_Parameter(BuiltInParameter.HOST_AREA_COMPUTED).AsDouble() < area / 4
select panel;
foreach (var panel in errorPanels)
{
var overrideGraphicSettings = doc.ActiveView.GetElementOverrides(panel.Id);
overrideGraphicSettings.SetProjectionLineColor(new Color(255, 0, 0));
//在当前视图下设置,其它视图保持原来的
doc.ActiveView.SetElementOverrides(panel.Id, overrideGraphicSettings);
doc.Regenerate();
}
});
}
},
"创建饰面"
);
//CreateCurtainSystem(length, width, gap, thickness, angle, offset, doc, curveLoops, pl.FaceNormal);
//var xyz = referEdge.GlobalPoint;
//if (angle > Math.PI / 2)
//{
// angle -= Math.PI;
// if (perpendicularVector.Y>0)
// {
// angle -= Math.PI / 2 ;
// }
// else
// {
// angle = Math.PI / 2 - angle;
// }
//}
}
private static void CreateCurtainSystemByRoom(UIDocument uidoc, double length, double width, double gap, double thickness, string panelName)
{
var doc = uidoc.Document;
var rooms = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms);
if (!rooms.Any())
{
MessageBox.Show("项目中当前没有房间", "提示", MessageBoxButton.OK, MessageBoxImage.Stop);
return;
}
var refer = uidoc.Selection.PickObject(ObjectType.Element, new GenericFilter<Room>(), "请选择铺贴的房间");
var room = doc.GetElement(refer) as Room;
List<CurveLoop> curveLoops = [];
var opts = new SpatialElementBoundaryOptions();
var segmentsList = room!.GetBoundarySegments(opts);
if (segmentsList != null)
{
foreach (var boundarySegments in segmentsList)
{
CurveLoop curveLoop = new();
foreach (var boundarySegment in boundarySegments)
{
curveLoop.Append(boundarySegment.GetCurve());
}
curveLoops.Add(curveLoop);
}
}
doc.Invoke(
_ =>
{
//double angle = 0;
var array = GetFaceArray(doc, curveLoops, out var ds);
var curtainSystemType = SetCurtainSystemType(doc, length, width, gap, thickness, panelName);
var curtainSystem = doc.Create.NewCurtainSystem(array, curtainSystemType);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_1).Set(0);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ANGLE_2).Set(0);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_1).Set(2);
curtainSystem.get_Parameter(BuiltInParameter.SPACING_JUSTIFICATION_2).Set(2);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_1).Set(0);
curtainSystem.get_Parameter(BuiltInParameter.CURTAINGRID_ORIGIN_2).Set(0);
if (ds != null)
{
doc.Delete(ds.Id);
}
ElementTransformUtils.MoveElement(doc, curtainSystem.Id, XYZ.BasisZ * thickness / 304.8 / 2);
},
"创建铺贴饰面"
);
//CreateCurtainSystem(length, width, gap, thickness, angle, offset, doc, curveLoops, XYZ.BasisZ);
}
[RelayCommand]
private void PlaceBricks()
{
finishesHandler.Raise(uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
try
{
if (ByRoom)
{
CreateCurtainSystemByRoom(uidoc, PavementLength, PavementWidth, PavementGap, PavementThickness, PanelName);
}
else
{
CreateCurtainSystemByFace(
uidoc,
PavementLength,
PavementWidth,
PavementGap,
PavementThickness,
WallBaseOffset,
PanelName,
IsEdgeExistGap
);
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
});
}
private static XYZ GetOrigin(Document doc, CurtainSystem curtainSystem, out XYZ u, out XYZ v)
{
XYZ origin = null;
var iterator = curtainSystem.CurtainGrids.ForwardIterator();
iterator.MoveNext();
var grid = iterator.Current as CurtainGrid;
var uLineId = grid.GetUGridLineIds().FirstOrDefault();
var vLineId = grid.GetVGridLineIds().FirstOrDefault();
var uLine = doc.GetElement(uLineId) as CurtainGridLine;
var vLine = doc.GetElement(vLineId) as CurtainGridLine;
var l1 = uLine.FullCurve as Line;
u = l1.Direction;
var l2 = vLine.FullCurve as Line;
v = l2.Direction;
var result = l1.Intersect(l2, out var resultArray);
if (result != SetComparisonResult.Disjoint)
{
if (!resultArray.IsEmpty)
{
origin = resultArray.get_Item(0).XYZPoint;
}
}
return origin;
}
}
public class EdgeFilter : ISelectionFilter
{
public EdgeFilter(Element preElement)
{
element = preElement;
}
private readonly Element element;
public bool AllowElement(Element elem)
{
return element.Id == elem.Id;
}
public bool AllowReference(Reference reference, XYZ position)
{
return true;
}
}

View File

@@ -0,0 +1,26 @@
using System.Windows;
using Autodesk.Revit.Attributes;
using CommunityToolkit.Mvvm.DependencyInjection;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class CivilConnectionCmd : ExternalCommand
{
public override void Execute()
{
try
{
WinDialogHelper.ShowModeless<ResolveCivilConnectView>(new ResolveCivilConnectViewModel(UiDocument));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
ex.StackTrace.ToLog();
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Windows;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class CreateOpeningsCmd : ExternalCommand
{
public override void Execute()
{
//CreateOpeningsViewModel viewModel = new();
//CreateOpeningsView view = new(viewModel);
//view.ShowDialog();
SingletonChildWindowManager.ShowOrActivate<CreateOpeningsView, CreateOpeningsViewModel>();
}
}

View File

@@ -0,0 +1,47 @@
<ui:FluentWindowEx
x:Class="ShrlAlgo.RvKits.RvCivil.CreateOpeningsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rvCivil="clr-namespace:ShrlAlgo.RvKits.RvCivil"
xmlns:ui="https://github.com/ShrlAlgo/WPFluent"
Title="开洞设置"
Width="200"
Height="220"
d:DataContext="{d:DesignInstance Type={x:Type rvCivil:CreateOpeningsViewModel}}"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary Source="pack://application:,,,/ShrlAlgo.RvKits;component/WPFUI.xaml" />
</Window.Resources>
<ui:AutoGridEx
ChildHorizontalAlignment="Stretch"
ChildMargin="5"
Columns="*"
RowHeight="*"
Rows="Auto,Auto,Auto">
<ui:TextBoxEx
Prefix="向外延伸距离:"
Suffix="mm"
Text="{Binding Distance, StringFormat={}{0:N2}}" />
<CheckBox
x:Name="CbAddCasing"
Content="添加套管"
IsChecked="{Binding AddCasing}" />
<ComboBox
DisplayMemberPath="Name"
IsEnabled="{Binding IsChecked, ElementName=CbAddCasing}"
ItemsSource="{Binding CasingFiles}"
SelectedItem="{Binding SelectedCasing}" />
<!-- Command="{Binding DataContext.CloseWinCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}" -->
<Button
HorizontalAlignment="Stretch"
ui:AutoGridEx.RowHeightOverride="Auto"
Command="{Binding CreateOpeningsCommand}"
Content="开洞"
ToolTip="选择添加套管则使用套管开洞" />
</ui:AutoGridEx>
</ui:FluentWindowEx>

View File

@@ -0,0 +1,15 @@
using System.Windows;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// CreateOpeningsView.xaml 的交互逻辑
/// </summary>
public partial class CreateOpeningsView
{
public CreateOpeningsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,277 @@
using System.IO;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace ShrlAlgo.RvKits.RvCivil
{
public partial class CreateOpeningsViewModel : ObservableObject
{
[ObservableProperty] private bool addCasing;
[ObservableProperty] private double distance;
private readonly string openingsFamilyFolder = $"{Variables.FamilyFolder}Opening";
private readonly string casingsFamilyFolder = $"{Variables.FamilyFolder}Casing";
private readonly ActionEventHandler handler = new();
[ObservableProperty] private FileInfo selectedCasing;
[ObservableProperty] private List<FileInfo> casingFiles = [];
public CreateOpeningsViewModel()
{
var files = Directory.GetFiles(casingsFamilyFolder);
foreach (var file in files)
{
CasingFiles.Add(new FileInfo(file));
}
}
[RelayCommand]
private void CreateOpenings()
{
try
{
handler.Raise(
uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
var reference = uidoc.Selection.PickObject(
ObjectType.Element,
new FuncFilter(e => e is Floor or ExtrusionRoof or FootPrintRoof or Ceiling or Wall or FamilyInstance),
"请选择需要开洞的图元"
);
var elementToOpen = doc.GetElement(reference);
var intersectsElement = new ElementIntersectsElementFilter(elementToOpen);
var mepCurves = doc.OfCollector().WherePasses(intersectsElement).Where(e => e is MEPCurve).Cast<MEPCurve>().ToList();
if (doc.ActiveView.ViewType != ViewType.ThreeD)
{
MessageBox.Show("请选择三维视图", "提示");
}
doc.InvokeGroup(
_ =>
{
foreach (var mepCurve in mepCurves)
{
doc.Invoke(
_ =>
{
//var level = new FilteredElementCollector(doc, doc.ActiveView.Id).OfCollector(BuiltInCategory.OST_Levels).OfClass(typeof(Level)).FirstElement();
doc.ActiveView.SketchPlane = SketchPlane.Create(
doc,
elementToOpen.LevelId);
},
"设置工作平面");
var curve = mepCurve.GetCurve();
var conn = mepCurve.GetConnectors().OfType<Connector>().FirstOrDefault();
double diameter = default;
double width = default;
double height = default;
//double angle = default;
Family openingFamily = null;
var faceNormal = XYZ.Zero;
faceNormal = conn.CoordinateSystem.BasisY;
doc.Invoke(
ts =>
{
switch (conn.Shape)
{
case ConnectorProfileType.Oval:
break;
case ConnectorProfileType.Rectangular:
openingFamily = AddCasing
? doc.GetOrLoadedFamily(
$"{casingsFamilyFolder}\\矩形套管.rfa")
: doc.GetOrLoadedFamily(
$"{openingsFamilyFolder}\\矩形洞口.rfa");
width = conn.Width + (double)(Distance / 304.8);
height = conn.Height + (double)(Distance / 304.8);
break;
case ConnectorProfileType.Round:
openingFamily = doc.GetOrLoadedFamily(
AddCasing
? $"{casingsFamilyFolder}\\圆形套管.rfa"
: $"{openingsFamilyFolder}\\圆形洞口.rfa");
diameter = mepCurve.Diameter + (double)(Distance / 304.8);
break;
case ConnectorProfileType.Invalid:
break;
default:
break;
}
//doc.ActiveView.SketchPlane = originSketch;
if (openingFamily == null)
{
return Result.Failed;
}
var intersects = FindIntersects(elementToOpen, curve);
if (intersects.Count != 2)
{
return Result.Failed;
}
var line = Line.CreateBound(intersects[0], intersects[1])
.ExtendLine(0.5, true);
doc.ActiveView.ShowActiveWorkPlane();
if (faceNormal == XYZ.Zero)
{
return Result.Failed;
}
var plane = Plane.CreateByNormalAndOrigin(
faceNormal,
line.Evaluate(0.5, true));
doc.ActiveView.SketchPlane = SketchPlane.Create(doc, plane);
var workPlane = new FilteredElementCollector(
doc,
doc.ActiveView.Id)
.OfCategory(BuiltInCategory.OST_IOSSketchGrid)
.FirstElement();
doc.Regenerate();
var op = new Options
{
IncludeNonVisibleObjects = true,
View = doc.ActiveView,
//DetailLevel = ViewDetailLevel.Fine,
ComputeReferences = true
};
var planeFace = workPlane
.get_Geometry(op)
.Where(obj => obj is Solid)
.Cast<Solid>()
.Select(s => s.Faces.get_Item(0))
.FirstOrDefault();
var options = ts.GetFailureHandlingOptions();
options.SetFailuresPreprocessor(new FailuresPreProcessor());
ts.SetFailureHandlingOptions(options);
if (planeFace == null)
{
return Result.Failed;
}
var symbolIds = openingFamily.GetFamilySymbolIds().FirstOrDefault();
var symbol = doc.GetElement(symbolIds) as FamilySymbol;
if (symbol != null && !symbol.IsActive)
{
symbol.Activate();
}
var newFamilyInstance = doc.Create
.NewFamilyInstance(planeFace, line, symbol);
if (diameter != 0.0)
{
newFamilyInstance.GetParameters("洞口直径").FirstOrDefault()?.Set(
diameter);
}
else if (width != 0.0 && height != 0.0)
{
newFamilyInstance.GetParameters("洞口宽度").FirstOrDefault()?.Set(width);
newFamilyInstance.GetParameters("洞口高度").FirstOrDefault()?.Set(
height);
}
//ElementTransformUtils.RotateElement(doc, newFamilyInstance.ViewId, line, angle);
doc.Regenerate();
InstanceVoidCutUtils.AddInstanceVoidCut(
doc,
elementToOpen,
newFamilyInstance);
doc.Delete(doc.ActiveView.SketchPlane.Id);
return Result.Succeeded;
},
"新建洞口");
}
},
"开洞");
});
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
catch (Exception e)
{
e.Message.ToLog();
throw;
}
}
/// <summary>
/// 找到宿主的面
/// </summary>
/// <param name="wall"></param>
/// <returns></returns>
private static Face FindCeilingAndFloorFace(Wall wall)
{
Options opt = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
var geometryElement = wall.get_Geometry(opt);
Face normalFace = null;
foreach (var geometryObject in geometryElement)
{
var solid = geometryObject as Solid;
if (solid != null && solid.Faces.Size > 0)
{
foreach (Face face in solid.Faces)
{
var planarFace = face as PlanarFace;
if (planarFace != null)
{
if (
planarFace.FaceNormal.AngleTo(new XYZ(1, 1, 0)) == 0
|| Math.Abs(planarFace.FaceNormal.AngleTo(new XYZ(1, 1, 0)) - Math.PI) < 0.0001
)
{
normalFace = face;
}
}
}
}
}
return normalFace;
}
/// <summary>
/// 查找所有管线定位线与元素面的碰撞点
/// </summary>
/// <param name="elementToOpen"></param>
/// <param name="curve"></param>
/// <returns></returns>
private static List<XYZ> FindIntersects(Element elementToOpen, Curve curve)
{
var faces = elementToOpen.GetAllGeometryObjects<Face>();
var intersects = new List<XYZ>();
foreach (var face in faces)
{
//face = FindCeilingAndFloorFace(openingElement as Wall);
var intersect = face.Intersect(curve, out var result);
if (intersect == SetComparisonResult.Overlap)
{
//CurveArray curveArray = new CurveArray();
var x = result.get_Item(0);
var intersection = x.XYZPoint;
intersects.Add(intersection);
//doc.Create.NewOpening(openingElement, curveArray, true);
}
}
return intersects;
}
}
}

View File

@@ -0,0 +1,18 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class FloorFinishesCmd : ExternalCommand
{
public override void Execute()
{
var floorTypes = Document.OfClass<FloorType>().Cast<FloorType>().ToList();
WinDialogHelper.ShowModeless<FloorFinishesView>(new FloorFinishesViewModel(floorTypes));
}
}

View File

@@ -0,0 +1,78 @@
<ui:FluentWindowEx
x:Class="ShrlAlgo.RvKits.RvCivil.FloorFinishesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rvCivil="clr-namespace:ShrlAlgo.RvKits.RvCivil"
xmlns:ui="https://github.com/ShrlAlgo/WPFluent"
Title="房间饰面"
Width="200"
Height="500"
MinWidth="275"
MinHeight="500"
d:DataContext="{d:DesignInstance Type=rvCivil:FloorFinishesViewModel}"
ui:Design.Background="{DynamicResource ApplicationBackgroundBrush}"
ui:Design.Foreground="{DynamicResource TextFillColorPrimaryBrush}"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ShrlAlgo.RvKits;component/WPFUI.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<ui:AutoGridEx
ChildMargin="5"
Columns="*"
Rows="Auto,*,Auto,Auto">
<GroupBox Header="范围">
<UniformGrid Rows="1">
<RadioButton Content="所有房间" IsChecked="{Binding RbAllRooms}" />
<RadioButton Content="选中房间" IsChecked="{Binding RbAllRooms, Converter={StaticResource InvertBooleanConverter}}" />
</UniformGrid>
</GroupBox>
<GroupBox
Grid.Row="1"
VerticalAlignment="Stretch"
Header="楼板类型">
<ListBox
x:Name="LbFloorType"
DisplayMemberPath="Name"
ItemsSource="{Binding FloorTypes}"
SelectedValue="{Binding SelectedFloorType, UpdateSourceTrigger=PropertyChanged}" />
</GroupBox>
<ui:TextBox
Grid.Row="2"
InputMethod.IsInputMethodEnabled="False"
PlaceholderText="楼板偏移"
Text="{Binding FloorOffset, StringFormat=\{0:N2\}, UpdateSourceTrigger=PropertyChanged}" />
<!--<TextBox
Grid.Row="2"
InputMethod.IsInputMethodEnabled="False"
md:HintAssist.Hint="楼板偏移"
md:TextFieldAssist.SuffixText="mm">
<TextBox.Text>
<Binding
Path="FloorOffset"
StringFormat="{}{0:N2}"
UpdateSourceTrigger="PropertyChanged">
-->
<!-- 可以修改值转换异常的提示“未能转换值”,前端验证 -->
<!--
<Binding.ValidationRules>
<domain:RangeValidationRule Max="100" Min="0" xmlns:domain="clr-namespace:Sai.Toolkit.Mvvm.ValidationRules" ValidatesOnTargetUpdated = "True" />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>-->
<ui:Button
Grid.Row="3"
HorizontalAlignment="Stretch"
Command="{Binding PlaceFloorCommand}"
Content="布置"
Icon="{ui:FontIcon Glyph=&#xeab9;,
FontFamily={StaticResource BoxIcons}}" />
</ui:AutoGridEx>
</ui:FluentWindowEx>

View File

@@ -0,0 +1,15 @@
using System.Windows;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// FloorFinishesView.xaml 的交互逻辑
/// </summary>
public partial class FloorFinishesView
{
public FloorFinishesView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,135 @@
using System.ComponentModel.DataAnnotations;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace ShrlAlgo.RvKits.RvCivil;
public partial class FloorFinishesViewModel : ObservableValidator
{
public FloorFinishesViewModel(IList<FloorType> floorTypes)
{
FloorTypes = floorTypes;
finishesHandler = new ActionEventHandler();
}
private readonly ActionEventHandler finishesHandler;
//private double floorOffset;
[Required]
[Range(-6000, 6000, ErrorMessage = "输入偏移量值范围有误")]
[NotifyCanExecuteChangedFor(nameof(PlaceFloorCommand))]
[NotifyDataErrorInfo]
[ObservableProperty]
private double floorOffset;
//[CustomValidation(typeof(FloorFinishesViewModel), nameof(ValidateFloorOffset))]
//public double FloorOffset
//{
// get => floorOffset;
// set
// {
// SetProperty(ref floorOffset, value, true);
// PlaceFloorCommand.NotifyCanExecuteChanged();
// }
//}
//自定义验证方法
//public static ValidationResult ValidateFloorOffset(string floorOffset, ValidationContext context)
//{
// FloorFinishesViewModel instance = (FloorFinishesViewModel)context.ObjectInstance;
// bool isValid = double.TryParse(floorOffset, out var _);
// if (isValid)
// {
// return ValidationResult.Success;
// }
// return new("输入内容不是数值。");
//}
[ObservableProperty] private bool rbAllRooms;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(PlaceFloorCommand))]
private FloorType selectedFloorType;
private bool CanPlace => SelectedFloorType != null && !HasErrors;
public IList<FloorType> FloorTypes { get; }
private IEnumerable<Room> selectedRooms;
[RelayCommand(CanExecute = nameof(CanPlace))]
private void PlaceFloor()
{
finishesHandler.Raise(app =>
{
var uidoc = app.ActiveUIDocument;
GetRoomsToPlace(uidoc);
CreateFloors(uidoc);
});
}
public void CreateFloors(UIDocument uidoc)
{
var document = uidoc.Document;
document.Invoke(_ =>
{
foreach (var room in selectedRooms)
{
if (room != null && room.UnboundedHeight != 0.0)
{
//UnitFormatUtils.TryParse(document.GetUnits(), UnitType.UT_Length, FloorOffset, out double value);
var option = new SpatialElementBoundaryOptions
{
SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish
};
var blists = room.GetBoundarySegments(option);
CurveArray curveArray = new();
var loops = new List<CurveLoop>();
foreach (var bs in blists[0])
{
curveArray.Append(bs.GetCurve());
}
foreach (var bsList in blists)
{
var curveList = new List<Curve>();
foreach (var bs in bsList)
{
curveList.Add(bs.GetCurve());
}
loops.Add(CurveLoop.Create(curveList));
}
var level = document.GetElement(room.LevelId) as Level;
if (curveArray.Size != 0)
{
#if REVIT2018 || REVIT2020
document.Create.NewFloor(curveArray, SelectedFloorType, level, false)
#elif REVIT2025
Floor.Create(document, loops, SelectedFloorType.Id,level.Id,false,null,0.0)
#endif
.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM).Set(FloorOffset / 304.8);
}
}
}
}, "房间饰面");
}
private void GetRoomsToPlace(UIDocument uidoc)
{
var doc = uidoc.Document;
selectedRooms = RbAllRooms
? (from elem in new FilteredElementCollector(doc, doc.ActiveView.Id).OfClass(typeof(SpatialElement))
let room = elem as Room
select room)
: uidoc.SelectObjectsByRectangle<Room>("请框选以选择房间");
}
}

View File

@@ -0,0 +1,164 @@
using System.IO;
using System.Text;
using System.Windows;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
/// <summary>
/// 按标高拆分成多个模型
/// </summary>
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class ModelSplitterCmd : ExternalCommand
{
public override void Execute()
{
var result = MessageBox.Show("执行前请手动备份文件避免出现意外情况!", "模型拆分", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
return;
}
#region FilterExecute
var levels = Document
.OfClass<Level>()
.Cast<Level>()
.OrderBy(l => l.Elevation)
.ToList();
Dictionary<Level, Outline> levelOutlines = [];
//获取标高范围
for (var i = 0; i < levels.Count; i++)
{
var baseLevel = levels[i];
XYZ min = new(double.MinValue, double.MinValue, baseLevel.Elevation);
XYZ max;
Outline outline;
if (i < levels.Count - 1)
{
var topLevel = levels[i + 1];
max = new XYZ(double.MaxValue, double.MaxValue, topLevel.Elevation);
outline = new Outline(min, max);
}
else
{
max = new XYZ(double.MaxValue, double.MaxValue, double.MaxValue);
outline = new Outline(min, max);
}
levelOutlines.Add(baseLevel, outline);
}
StringBuilder sb = new();
//对比标高,得到实际分层位置
foreach (var keyPair in levelOutlines)
{
//得到在标高范围内的元素
BoundingBoxIsInsideFilter insideFilter = new(keyPair.Value);
LogicalOrFilter logicalOr = new(new ElementClassFilter(typeof(HostObject)), new ElementClassFilter(typeof(FamilyInstance)));
var insideCollector = Document.OfModelCollector()?.WherePasses(insideFilter).WherePasses(logicalOr).ToList();
if (insideCollector == null)
{
return;
}
var level = keyPair.Key;
var elemIds = insideCollector.Where(elem => elem.get_BoundingBox(null) != null)
.Select(elem => elem.Id)
.ToList();
foreach (var elementId in elemIds)
{
sb.AppendLine(elementId.ToString());
}
if (elemIds.Count == 0)
{
continue;
}
var folder = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + $"\\{Document.Title}";
var fileName = $"{level.Name}.rvt";
var filePath = $"{folder}\\{fileName}";
var targetDocument = Application.NewProjectDocument(UnitSystem.Metric);
using (Transaction trans = new(targetDocument, "创建标高"))
{
trans.Start();
try
{
var targetLevel = Level.Create(targetDocument, level.Elevation);
ElementTransformUtils.CopyElements(Document, elemIds, targetDocument, null, new CopyPasteOptions());
//targetLevel.Name = level.Name;
trans.Commit();
}
catch (Exception ex)
{
ErrorMessage = ex.Message + sb;
Result = Result.Failed;
if (trans.GetStatus() == TransactionStatus.Started)
{
trans.RollBack();
}
targetDocument.Close(false);
return;
}
}
try
{
if (!Directory.Exists(folder))
{
Directory.CreateDirectory(folder);
}
targetDocument.SaveAs($"{folder}\\{fileName}");
targetDocument.Close(false);
}
catch (Exception e)
{
ErrorMessage = e.Message;
Result = Result.Failed;
return;
}
using (Transaction trans = new(Document, "按标高分割文件"))
{
trans.Start();
try
{
Document.Delete(elemIds);
var mp = ModelPathUtils.ConvertUserVisiblePathToModelPath(filePath);
RevitLinkOptions options = new(true);
var linkType = RevitLinkType.Create(Document, mp, options);
var instance = RevitLinkInstance.Create(Document, linkType.ElementId, ImportPlacement.Origin);
trans.Commit();
}
catch (Exception ex)
{
ErrorMessage = ex.Message + sb;
Result = Result.Failed;
if (trans.GetStatus() == TransactionStatus.Started)
{
trans.RollBack();
}
return;
}
}
}
#endregion FilterExecute
}
}

View File

@@ -0,0 +1,11 @@
using System.ComponentModel;
namespace ShrlAlgo.RvKits.RvCivil
{
public enum PlacementType
{
[Description("墙面")] ByFace,
[Description("墙体")] ByWall,
[Description("房间")] ByRoom
}
}

View File

@@ -0,0 +1,91 @@
<ui:FluentWindowEx
x:Class="ShrlAlgo.RvKits.RvCivil.ResolveCivilConnectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rvCivil="clr-namespace:ShrlAlgo.RvKits.RvCivil"
xmlns:ui="https://github.com/ShrlAlgo/WPFluent"
Title="土建构件连接"
Width="250"
Height="600"
MinHeight="600"
d:DataContext="{d:DesignInstance Type=rvCivil:ResolveCivilConnectViewModel}"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ShrlAlgo.RvKits;component/WPFUI.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<ui:AutoGridEx
ChildMargin="5"
Columns="*"
Rows="*,Auto,Auto">
<GroupBox
Grid.Row="0"
Grid.Column="0"
Header="扣减选项">
<StackPanel>
<CheckBox
Content="结构柱剪切建筑墙"
IsChecked="{Binding SColumnCutAWall}"
ToolTip="保留原结构柱不变,建筑墙被剪切" />
<CheckBox
Content="结构柱剪切结构墙"
IsChecked="{Binding SColumnCutSWall}"
ToolTip="保留原结构柱不变,结构墙被剪切" />
<CheckBox
Content="结构柱剪切梁"
IsChecked="{Binding SColumnCutFraming}"
ToolTip="保留原结构柱不变,梁被剪切" />
<CheckBox
Content="结构柱剪切板"
IsChecked="{Binding SColumnCutFloor}"
ToolTip="保留原结构柱不变" />
<CheckBox
Content="梁剪切建筑墙"
IsChecked="{Binding FramingCutAWall}"
ToolTip="保留原结构梁,建筑墙被剪切" />
<CheckBox
Content="梁剪切结构墙"
IsChecked="{Binding FramingCutSWall}"
ToolTip="保留原结构梁,结构墙被剪切" />
<CheckBox
Content="梁剪切板"
IsChecked="{Binding FramingCutFloor}"
ToolTip="保留原梁,板被剪切" />
<CheckBox
Content="板剪切建筑墙"
IsChecked="{Binding FloorCutAWall}"
ToolTip="保留原板,建筑墙被剪切" />
<CheckBox
Content="主梁剪切次梁"
IsChecked="{Binding MFramingCutSFraming}"
ToolTip="保留主梁,次梁被剪切" />
<CheckBox
Content="板剪切结构墙"
IsChecked="{Binding FloorCutSWall}"
ToolTip="保留原板,结构墙被剪切" />
<CheckBox
Content="结构墙剪切建筑墙"
IsChecked="{Binding SWallCutAWall}"
ToolTip="保留原板,建筑墙被剪切" />
</StackPanel>
</GroupBox>
<GroupBox Grid.Row="1" Header="修改范围">
<UniformGrid Rows="1">
<RadioButton Content="框选范围" IsChecked="True" />
<RadioButton Content="整个模型" IsChecked="{Binding IsWholeModel}" />
</UniformGrid>
</GroupBox>
<Button
Grid.Row="2"
HorizontalAlignment="Stretch"
Command="{Binding ModifyModelCommand}"
Content="修改"
ToolTip="相交的图元才会被修改" />
</ui:AutoGridEx>
</ui:FluentWindowEx>

View File

@@ -0,0 +1,17 @@
using System.Windows;
using WPFluent.Controls;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// ResolveCivilConnectView.xaml 的交互逻辑
/// </summary>
public partial class ResolveCivilConnectView : FluentWindowEx
{
public ResolveCivilConnectView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,481 @@
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
using ShrlAlgo.RvKits.Windows;
namespace ShrlAlgo.RvKits.RvCivil;
public partial class ResolveCivilConnectViewModel : ObservableObject
{
private readonly ActionEventHandler modifyHandler = new();
public ResolveCivilConnectViewModel(UIDocument uidoc)
{
doc = uidoc.Document;
this.uidoc = uidoc;
}
private readonly Document doc;
private readonly UIDocument uidoc;
/// <summary>
/// 楼板-建筑墙
/// </summary>
[ObservableProperty]
private bool? floorCutAWall = false;
/// <summary>
/// 板-结构墙
/// </summary>
[ObservableProperty]
private bool? floorCutSWall = false;
/// <summary>
/// 梁-建筑墙
/// </summary>
[ObservableProperty]
private bool? framingCutAWall = false;
/// <summary>
/// 梁-板
/// </summary>
[ObservableProperty]
private bool? framingCutFloor = false;
/// <summary>
/// 梁-结构墙
/// </summary>
[ObservableProperty]
private bool? framingCutSWall = false;
/// <summary>
/// 主梁-次梁
/// </summary>
[ObservableProperty]
private bool? mFramingCutSFraming = false;
/// <summary>
/// 结构柱子-建筑墙
/// </summary>
[ObservableProperty]
private bool? sColumnCutAWall = false;
/// <summary>
/// 结构柱-板
/// </summary>
[ObservableProperty]
private bool? sColumnCutFloor = false;
/// <summary>
/// 结构柱-梁
/// </summary>
[ObservableProperty]
private bool? sColumnCutFraming = false;
/// <summary>
/// 结构柱-结构墙
/// </summary>
[ObservableProperty]
private bool? sColumnCutSWall = false;
/// <summary>
/// 结构墙-建筑墙
/// </summary>
[ObservableProperty]
private bool? sWallCutAWall = false;
[ObservableProperty]
private bool? isWholeModel = false;
public static Options SetOptions()
{
Options option = new() { ComputeReferences = true, DetailLevel = ViewDetailLevel.Fine };
return option;
}
/// <summary>
///
/// </summary>
/// <param name="elementsToSkip"></param>
/// <param name="view3D"></param>
/// <param name="tsName"></param>
/// <param name="collectorReserve">保留的集合</param>
/// <param name="collectorToCut">剪切的集合</param>
private void ConnectCivilComponents(
List<MessageModel> elementsToSkip,
View3D view3D,
string tsName,
FilteredElementCollector collectorReserve,
FilteredElementCollector collectorToCut
)
{
if (collectorReserve == null || collectorToCut == null)
{
return;
}
var ids = collectorToCut.WhereElementIsNotElementType().ToElementIds();
doc.Invoke(
ts =>
{
var options = ts.GetFailureHandlingOptions();
FailuresPreProcessor failuresProcessor = new();
options.SetFailuresPreprocessor(failuresProcessor);
ts.SetFailureHandlingOptions(options);
var reserveElements = collectorReserve.WhereElementIsNotElementType().ToList();
foreach (var element in reserveElements)
{
try
{
//var filter = new ElementIntersectsElementFilter(wall, false);
//var instances = collector.WherePasses(filter);
//instance.get_BoundingBox(view3D)
//BoundingBoxIntersectsFilter filter=new BoundingBoxIntersectsFilter(new Outline())
var box = element.get_BoundingBox(view3D);
Outline outline = new(box.Min, box.Max);
BoundingBoxIntersectsFilter intersectsFilter = new(outline);
BoundingBoxIsInsideFilter boxIsInsideFilter = new(outline);
var filter = new LogicalOrFilter(intersectsFilter, boxIsInsideFilter);
//ElementIntersectsElementFilter filter = new ElementIntersectsElementFilter(structuralColumn, false);
//执行一次后会修改集合,需修改
var newCollector = new FilteredElementCollector(doc, ids);
var intersectElements = newCollector.WherePasses(filter).ToList();
foreach (var intersectElem in intersectElements)
{
if (!JoinGeometryUtils.AreElementsJoined(doc, intersectElem, element))
{
JoinGeometryUtils.JoinGeometry(doc, element, intersectElem);
}
if (!JoinGeometryUtils.IsCuttingElementInJoin(doc, element, intersectElem))
{
JoinGeometryUtils.SwitchJoinOrder(doc, element, intersectElem);
}
}
}
catch (Exception ex)
{
//避免重复添加
if (!elementsToSkip.Exists(ele => ele.Element.Id == element.Id))
{
elementsToSkip.Add(new MessageModel(element, ex.Message));
}
}
}
},
tsName
);
}
private static List<Face> GetElementGeoList(Element elem)
{
List<Face> getElementGeoList = new();
var geometry = elem.get_Geometry(SetOptions());
foreach (var geomObj in geometry)
{
if (geomObj is GeometryInstance geomInstance)
{
var usesSymbolGeometry = elem is FamilyInstance instance && !instance.HasModifiedGeometry();
var instanceGeometry = usesSymbolGeometry ? geomInstance.GetSymbolGeometry() : geomInstance.GetInstanceGeometry();
foreach (var instObj in instanceGeometry)
{
getElementGeoList.AddRange(GetFaces(instObj));
}
}
getElementGeoList.AddRange(GetFaces(geomObj));
}
return getElementGeoList;
}
private static List<Face> GetFaces(GeometryObject geoObject)
{
List<Face> geometryObjects = new();
if (geoObject is Solid instSolid)
{
if (instSolid.Faces.Size > 0)
{
geometryObjects.AddRange(instSolid.Faces.Cast<Face>());
}
//else if (typeof(Face) == typeof(Edge) && instSolid.Edges.Size > 0)
//{
// geometryObjects.AddRange(instSolid.Edges.Cast<Face>());
//}
}
return geometryObjects;
}
[RelayCommand]
private void ModifyModel()
{
//if (obj is Windows view)
//{
// view.DialogResult = true;
//}
var result = MessageBox.Show("连接处理", "如果模型构件数量过多,存在崩溃风险。\n\r是否现在保存并继续?", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.Yes)
{
doc.Save(new SaveOptions());
}
if (result == MessageBoxResult.No)
{
return;
}
modifyHandler.Raise(_ =>
{
List<MessageModel> elementsToSkip = new();
//墙和柱空间位置,连接时,墙在柱间要打断处理,不能“一条线延伸”建筑结构均如此。墙的顶部到梁底或者板底,等等其他具有构件之间空间连接视具体情况而定。要保证既不影响模型完整准确性,又不会对后期量的统计造成误差。建筑和结构要协调连接处的处理方式,做到统一。
var view3D = doc.OfClass<View3D>().Cast<View3D>().FirstOrDefault(v => !v.IsTemplate);
//构造收集器
var floors = new FilteredElementCollector(doc).OfClass(typeof(Floor));
var columnCollector = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_Columns);
var structColumns = new FilteredElementCollector(doc)
.OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_StructuralColumns);
var beamsCollector = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_StructuralFraming);
var elemIds = new FilteredElementCollector(doc)
.OfClass(typeof(Wall))
.Where(wall => wall.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger() == 0)
.Select(elem => elem.Id)
.ToList();
var archiWalls = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = new FilteredElementCollector(doc)
.OfClass(typeof(Wall))
.Where(wall => wall.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger() == 1)
.Select(elem => elem.Id)
.ToList();
var structWalls = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
//ElementCategoryFilter structuralColumns = new ElementCategoryFilter(BuiltInCategory.OST_StructuralColumns);
//ElementCategoryFilter columns = new ElementCategoryFilter(BuiltInCategory.OST_Columns);
//ElementCategoryFilter beams = new ElementCategoryFilter(BuiltInCategory.OST_StructuralFraming);
//List<ElementFilter> filters = new List<ElementFilter>
//{
// structuralColumns,
// columns,
// beams
//};
//LogicalOrFilter orFilter = new LogicalOrFilter(filters);
try
{
if (IsWholeModel != true)
{
var elements = uidoc.Selection.PickElementsByRectangle(new FuncFilter(e => e is Wall or Floor or FamilyInstance));
elemIds = elements.Where(elem => elem is Floor).Select(f => f.Id).ToList();
floors = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = elements
.Where(elem => elem.Category.Id == Category.GetCategory(doc, BuiltInCategory.OST_StructuralColumns).Id)
.Select(elem => elem.Id)
.ToList();
structColumns = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = elements
.Where(elem => elem.Category.Id == Category.GetCategory(doc, BuiltInCategory.OST_Columns).Id)
.Select(elem => elem.Id)
.ToList();
columnCollector = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = elements
.Where(elem => elem.Category.Id == Category.GetCategory(doc, BuiltInCategory.OST_StructuralFraming).Id)
.Select(elem => elem.Id)
.ToList();
beamsCollector = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = elements
.Where(
elem =>
elem.Category.Id == Category.GetCategory(doc, BuiltInCategory.OST_Walls).Id
&& elem.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger() == 0
)
.Select(elem => elem.Id)
.ToList();
archiWalls = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
elemIds = elements
.Where(
elem =>
elem.Category.Id == Category.GetCategory(doc, BuiltInCategory.OST_Walls).Id
&& elem.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger() == 1
)
.Select(elem => elem.Id)
.ToList();
structWalls = elemIds.Any() ? new FilteredElementCollector(doc, elemIds) : null;
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
using (TransactionGroup tg = new(doc, "模型连接处理"))
{
tg.Start();
if (SColumnCutSWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "结构柱剪切结构墙", structColumns, structWalls);
}
if (FloorCutAWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "板剪切建筑墙", floors, archiWalls);
}
if (FramingCutFloor == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "梁剪切板", beamsCollector, floors);
}
if (FramingCutSWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "梁剪切结构墙", beamsCollector, structWalls);
}
if (SWallCutAWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "结构墙剪切建筑墙", structWalls, archiWalls);
}
if (SColumnCutFloor == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "结构柱剪切板", structColumns, floors);
}
if (SColumnCutFraming == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "结构柱剪切梁", structColumns, beamsCollector);
}
if (SColumnCutAWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "结构柱剪切建筑墙", structColumns, archiWalls);
}
if (FramingCutAWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "梁剪切建筑墙", beamsCollector, archiWalls);
}
if (FloorCutSWall == true)
{
ConnectCivilComponents(elementsToSkip, view3D, "板剪切结构墙", floors, structWalls);
}
if (MFramingCutSFraming == true)
{
//ConnectCivilComponents(elementsToSkip, view3D, "主梁连接次梁", beamsCollector, beamsCollector);
using Transaction trans = new(doc, "主梁连接次梁");
trans.Start();
var options = trans.GetFailureHandlingOptions();
FailuresPreProcessor failuresProcessor = new();
options.SetFailuresPreprocessor(failuresProcessor);
trans.SetFailureHandlingOptions(options);
var beams = beamsCollector.WhereElementIsNotElementType().ToList();
var ids = beamsCollector.WhereElementIsNotElementType().ToElementIds();
foreach (var beam in beams)
{
try
{
//ElementIntersectsElementFilter filter = new ElementIntersectsElementFilter(structuralColumn, false);
var box = beam.get_BoundingBox(view3D);
Outline outline = new(box.Min, box.Max);
BoundingBoxIntersectsFilter intersectsFilter = new(outline);
BoundingBoxIsInsideFilter boxIsInsideFilter = new(outline);
var filter = new LogicalOrFilter(intersectsFilter, boxIsInsideFilter);
//执行一次后会修改集合,需修改
var newCollector = new FilteredElementCollector(doc, ids);
var intersectBeams = newCollector.WherePasses(filter);
var firstBeam = beam as FamilyInstance;
foreach (var intersectBeam in intersectBeams)
{
if (intersectBeam.Id == firstBeam.Id)
{
continue;
}
var secondBeam = intersectBeam as FamilyInstance;
var face1 = GetElementGeoList(firstBeam)
.Find(face => face.ComputeNormal(new UV()).IsAlmostEqualTo(firstBeam.HandOrientation));
var face2 = GetElementGeoList(intersectBeam)
.Find(face => face.ComputeNormal(new UV()).IsAlmostEqualTo(secondBeam.HandOrientation));
FamilyInstance mainBeam;
FamilyInstance subBeam;
if (face1 == null || face2 == null)
{
continue;
}
if (face1.Area > face2.Area)
{
mainBeam = firstBeam;
subBeam = secondBeam;
}
else
{
mainBeam = secondBeam;
subBeam = firstBeam;
}
if (!JoinGeometryUtils.AreElementsJoined(doc, mainBeam, subBeam))
{
JoinGeometryUtils.JoinGeometry(doc, mainBeam, subBeam);
}
if (!JoinGeometryUtils.IsCuttingElementInJoin(doc, mainBeam, subBeam))
{
JoinGeometryUtils.SwitchJoinOrder(doc, mainBeam, subBeam);
}
//if (JoinGeometryUtils.AreElementsJoined(doc, mainBeam, subBeam))
//{
// //楼板被剪切了,第一个元素保持不变
// if (!JoinGeometryUtils.IsCuttingElementInJoin(doc, mainBeam, subBeam))
// {
// JoinGeometryUtils.SwitchJoinOrder(doc, mainBeam, subBeam);
// }
//}
//else
//{
// // 第一个元素保持不变
// JoinGeometryUtils.SwitchJoinOrder(doc, beam, intersectBeam);
//}
}
}
catch (Exception ex)
{
elementsToSkip.Add(new MessageModel(beam, ex.Message));
}
}
trans.Commit();
}
tg.Assimilate();
}
if (elementsToSkip.Any())
{
WinDialogHelper.ShowModeless<MessageWin>(new MessageViewModel(uidoc, elementsToSkip, "未解决构件"));
}
else
{
MessageBox.Show("处理完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
});
}
}

View File

@@ -0,0 +1,33 @@
using Autodesk.Revit.DB;
namespace ShrlAlgo.RvKits.RvCivil
{
internal class Rotation
{
/// <summary>
/// 旋转角度
/// </summary>
public double Radian { get; set; }
/// <summary>
/// 对齐的轴
/// </summary>
public Orientation Orientation { get; set; }
/// <summary>
/// 对齐边与轴角度最小的对齐边的方向
/// </summary>
public XYZ Direction { get; set; }
/// <summary>
/// 对齐UV轴的方向
/// </summary>
public XYZ CoordSystemAxis { get; set; }
}
public enum Orientation
{
U,
V
}
}

View File

@@ -0,0 +1,205 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI.Selection;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil;
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class SlopedFloorCmd : ExternalCommand
{
public override void Execute()
{
Document.Invoke(
_ =>
{
Line line1 = null;
Line line2 = null;
HostObject obj1 = null;
HostObject obj2 = null;
var refer = UiDocument.Selection.PickObject(
ObjectType.Edge,
new FuncFilter(
elem => elem is HostObject,
(r, p) =>
{
obj1 = Document.GetElement(r) as HostObject;
var edge = obj1?.GetGeometryObjectFromReference(r) as Edge;
if (edge?.AsCurve() is Line l && l.Direction.Z < 0.0001)
{
line1 = l;
return true;
}
return false;
}
),
"请选择主体元素其中的一条水平边"
);
var elem = Document.GetElement(refer);
UiDocument.Selection.PickObject(
ObjectType.Edge,
new FuncFilter(
elem => elem is HostObject && elem.Id != obj1.Id,
(r, _) =>
{
obj2 = Document.GetElement(r) as HostObject;
var 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)
&& l.Distance(line1.Origin) > Application.ShortCurveTolerance / 304.8
)
{
line2 = l;
return true;
}
return false;
}
),
"请选择与另一条平行的水平边"
);
var level1 = Document.GetElement(obj1.LevelId) as Level;
var level2 = Document.GetElement(obj2.LevelId) as Level;
var baseLevel = level1?.Elevation > level2?.Elevation ? level2 : level1;
//基准边,低的边
var lineBase = line1.Origin.Z < line2.Origin.Z ? line1 : line2;
var lineAnother = line1.Origin.Z < line2.Origin.Z ? line2 : line1;
//if (line1.Origin.Z < line2.Origin.Z)
//{
// lineBase = line1;
// lineAnother = line2;
//}
//else
//{
// lineBase = line2;
// lineAnother = line1;
//}
var curveArr = new CurveArray();
var planeToProject = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, lineBase.Origin);
var p1 = planeToProject.ProjectOf(lineBase.GetEndPoint(0));
var p2 = planeToProject.ProjectOf(lineBase.GetEndPoint(1));
var p3 = planeToProject.ProjectOf(lineAnother.GetEndPoint(0));
var p4 = planeToProject.ProjectOf(lineAnother.GetEndPoint(1));
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);
}
curveArr.Append(l1);
curveArr.Append(l2);
curveArr.Append(l3);
curveArr.Append(l4);
//var isStructure = obj1.get_Parameter(BuiltInParameter.FLOOR_PARAM_IS_STRUCTURAL).AsInteger();
//选择线的中点连线
var centerLine = Line.CreateBound(lineBase.Evaluate(0.5, true), lineAnother.Evaluate(0.5, true));
//中线和边线叉乘,求出楼板面的法向量
var floorNormal = lineBase.Direction.CrossProduct(centerLine.Direction);
if (floorNormal.Z < 0)
{
floorNormal = floorNormal.Negate();
}
//坡度角
var radian = floorNormal.AngleTo(XYZ.BasisZ);
//另一条楼板边垂直xy的平面
var normal = lineAnother.Direction.CrossProduct(XYZ.BasisZ);
var plane = Plane.CreateByNormalAndOrigin(normal, lineAnother.Evaluate(0.5, true));
//点与平面的距离
var centerPoint = lineBase.Evaluate(0.5, true);
var perpPoint = plane.ProjectOf(centerPoint);
var 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平面交线的一条线。
#if REVIT2018 || REVIT2020
Document.Create.NewSlab(curveArr, baseLevel, slopedArrow, Math.Tan(radian), true);
#elif REVIT2025
var floorType = Floor.GetDefaultFloorType(Document, false);
if (elem is Floor floor)
{
floorType = floor.GetTypeId();
}
var loops = new List<CurveLoop>() { CurveLoop.Create(new List<Curve>() { l1, l2, l3, l4 }) };
Floor.Create(Document, loops, floorType, baseLevel.Id, true, slopedArrow, Math.Tan(radian));
#endif
},
"坡度楼板"
);
}
private static bool IfSameHeight(List<double> vs)
{
//判断楼板中各端点标高是否相等
List<double> list = new();
foreach (var z in vs)
{
if (list.Contains(z) == false)
{
list.Add(z);
}
}
return list.Count == 1;
}
//编辑子图元
private void EditSubElements(Floor floor)
{
#if REVIT2018 || REVIT2020
var slabShapeEditor = floor.SlabShapeEditor;
#elif REVIT2025
var slabShapeEditor = floor.GetSlabShapeEditor();
#endif
if (slabShapeEditor.IsEnabled)
{
var vertices = slabShapeEditor.SlabShapeVertices;
var zList = new List<double>();
foreach (SlabShapeVertex slabShapeVertex in vertices)
{
zList.Add(slabShapeVertex.Position.Z);
}
//判断为水平板后重设形状
if (IfSameHeight(zList))
{
var floorLevel = floor.Document.GetElement(floor.LevelId) as Level; //获取楼板所在标高
var levelHeight = floorLevel.LookupParameter("立面").AsDouble(); //获取所在标高的标高
var offsetHeight = zList.FirstOrDefault() - levelHeight;
floor.get_Parameter(BuiltInParameter.FLOOR_HEIGHTABOVELEVEL_PARAM).Set(offsetHeight);
slabShapeEditor.ResetSlabShape();
}
}
}
}

View File

@@ -0,0 +1,303 @@
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.IFC;
using Nice3point.Revit.Toolkit.External;
using ShrlAlgo.RvKits.Windows;
namespace ShrlAlgo.RvKits.RvCivil;
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class SplitComsByLevelCmd : ExternalCommand
{
public override void Execute()
{
var instances = Document.OfCollector().OfClass(typeof(FamilyInstance)).Cast<FamilyInstance>().ToList();
var errors = new List<MessageModel>();
using (TransactionGroup tg = new(Document, "按标高拆分墙、柱"))
{
tg.Start();
//墙打断
using (Transaction trans = new(Document, "楼层分割打断构件"))
{
trans.Start();
var walls = Document.OfCollector().OfClass(typeof(Wall)).Cast<Wall>().ToList();
var structuralColumns = Document.OfCollector()
.OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_StructuralColumns)
.Cast<FamilyInstance>()
.ToList();
var columns = Document.OfCollector()
.OfClass(typeof(FamilyInstance))
.OfCategory(BuiltInCategory.OST_Columns)
.Cast<FamilyInstance>()
.ToList();
var elemToSplitIds = new List<ElementId>();
foreach (var wall in walls)
{
//得到墙的包围框
var canModify = true;
foreach (var instance in instances)
{
if (instance.Host != null && instance.Host.Id == wall.Id)
{
errors.Add(new MessageModel(wall, "有族基于当前墙无法拆分"));
canModify = false;
break;
}
}
if (ExporterIFCUtils.HasElevationProfile(wall))
{
canModify = false;
errors.Add(new MessageModel(wall, "轮廓被修改无法拆分"));
}
var needToDelete = false;
if (canModify)
{
needToDelete = SplitWallByLevel(Document, wall, ActiveView);
}
if (needToDelete)
{
elemToSplitIds.Add(wall.Id);
}
}
foreach (var column in structuralColumns)
{
var canModify = true;
foreach (var instance in instances)
{
if (instance.Host != null && instance.Host.Id == column.Id)
{
errors.Add(new MessageModel(column, "有族基于当前结构柱,无法拆分"));
canModify = false;
break;
}
}
var needToDelete = false;
if (canModify)
{
needToDelete = SplitColumnByLevel(Document, column, ActiveView);
}
if (needToDelete)
{
elemToSplitIds.Add(column.Id);
}
}
foreach (var column in columns)
{
var canModify = true;
foreach (var instance in instances)
{
if (instance.Host != null && instance.Host.Id == column.Id)
{
errors.Add(new MessageModel(column, "有族基于当前柱,无法拆分"));
canModify = false;
break;
}
}
var needToDelete = false;
if (canModify)
{
needToDelete = SplitColumnByLevel(Document, column, ActiveView);
}
if (needToDelete)
{
elemToSplitIds.Add(column.Id);
}
}
Document.Delete(elemToSplitIds);
trans.Commit();
}
tg.Assimilate();
}
if (errors.Any())
{
WinDialogHelper.ShowModeless<MessageWin>(new MessageViewModel(UiDocument, errors, "未解决错误"));
}
else
{
MessageBox.Show("处理完成", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
}
private static bool SplitColumnByLevel(Document document, FamilyInstance column, View view)
{
var wallBox = column.get_BoundingBox(view);
var minZ = wallBox.Min.Z;
var maxZ = wallBox.Max.Z;
var allLevels = document
.OfClass<Level>()
.OfCategory(BuiltInCategory.OST_Levels)
.Cast<Level>()
.OrderBy(o => o.Elevation)
.ToList();
var toSplit = document
.OfClass<Level>()
.OfCategory(BuiltInCategory.OST_Levels)
.Cast<Level>()
.Where(l => l.Elevation - minZ > 0.0001 && maxZ - l.Elevation > 0.0001)
.OrderBy(o => o.Elevation)
.ToList();
if (toSplit.Count == 0)
{
return false;
}
var n = allLevels.FindIndex(l => l.Id == toSplit[0].Id);
var minLevel = allLevels[0];
if (n > 0)
{
minLevel = allLevels[n - 1]; //存在更低的标高时,取此标高作为底标高约束
}
var translation = XYZ.BasisZ;
var bottomEleIds = ElementTransformUtils.CopyElement(document, column.Id, translation);
var minWall = document.GetElement(bottomEleIds.FirstOrDefault()) as FamilyInstance;
//底部约束
minWall.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).Set(minLevel.Id);
minWall.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).Set(minZ - minLevel.Elevation);
minWall.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).Set(toSplit[0].Id);
minWall.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).Set(0);
var maxLevel = toSplit.Last();
var m = allLevels.FindIndex(l => l.Id == toSplit.Last().Id);
if (m < allLevels.Count - 1)
{
maxLevel = allLevels[m + 1]; //存在更高的标高
}
var topEleIds = ElementTransformUtils.CopyElement(document, column.Id, translation);
var maxWall = document.GetElement(topEleIds.FirstOrDefault()) as FamilyInstance;
maxWall.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).Set(toSplit.Last().Id);
maxWall.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).Set(0);
maxWall.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).Set(maxLevel.Id);
maxWall.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).Set(maxZ - maxLevel.Elevation);
for (var i = 0; i < toSplit.Count - 1; i++)
{
var baseLevel = toSplit[i];
var topLevel = toSplit[i + 1];
var eleIds = ElementTransformUtils.CopyElement(document, column.Id, translation);
var wallCopy = document.GetElement(eleIds.FirstOrDefault()) as FamilyInstance;
wallCopy.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).Set(baseLevel.Id);
wallCopy.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).Set(0);
wallCopy.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).Set(topLevel.Id);
wallCopy.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).Set(0);
//if (i == levelsToSplit.Count - 2 && maxZ < maxLevel.Bottom)
//{
// wallCopy.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(maxZ - maxLevel.Bottom);
//}
//Wall.Create(document, curve, wall.WallType.ViewId, levels[i].ViewId, 100, 100, false, false);
}
return true;
}
/// <summary>
/// 切分墙体
/// </summary>
/// <param name="doc"></param>
/// <param name="wall"></param>
/// <param name="view">三维视图</param>
/// <returns></returns>
private static bool SplitWallByLevel(Document doc, Wall wall, View view)
{
var wallBox = wall.get_BoundingBox(view);
var minZ = wallBox.Min.Z;
var maxZ = wallBox.Max.Z;
var allLevels = new FilteredElementCollector(doc)
.OfClass(typeof(Level))
.OfCategory(BuiltInCategory.OST_Levels)
.Cast<Level>()
.OrderBy(o => o.Elevation)
.ToList();
var toSplit = new FilteredElementCollector(doc)
.OfClass(typeof(Level))
.OfCategory(BuiltInCategory.OST_Levels)
.Cast<Level>()
.Where(l => l.Elevation - minZ > 0.0001 && maxZ - l.Elevation > 0.0001)
.OrderBy(o => o.Elevation)
.ToList();
if (toSplit.Count == 0)
{
return false;
}
var n = allLevels.FindIndex(l => l.Id == toSplit[0].Id);
var minLevel = allLevels[0];
if (n > 0)
{
minLevel = allLevels[n - 1]; //存在更低的标高时,取此标高作为底标高约束
}
var translation = XYZ.BasisZ;
var bottomEleIds = ElementTransformUtils.CopyElement(doc, wall.Id, translation);
var minWall = doc.GetElement(bottomEleIds.FirstOrDefault()) as Wall;
//底部约束
minWall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).Set(minLevel.Id);
minWall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(minZ - minLevel.Elevation);
minWall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).Set(toSplit[0].Id);
minWall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(0);
//Wall.Create(doc, (wall.Location as LocationCurve).Curve, wall.WallType.Id, minLevel.Id, minLevel.Bottom - minZ, minZ - minLevel.Bottom, wall.Flipped, Convert.ToBoolean(wall.get_Parameter(BuiltInParameter.WALL_STRUCTURAL_SIGNIFICANT).AsInteger()));
var maxLevel = toSplit.Last();
var m = allLevels.FindIndex(l => l.Id == toSplit.Last().Id);
if (m < allLevels.Count - 1)
{
maxLevel = allLevels[m + 1]; //存在更高的标高
}
var topEleIds = ElementTransformUtils.CopyElement(doc, wall.Id, translation);
doc.Regenerate();
var maxWall = doc.GetElement(topEleIds.FirstOrDefault()) as Wall;
maxWall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).Set(toSplit.Last().Id);
maxWall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(0);
maxWall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).Set(maxLevel.Id);
maxWall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(maxZ - maxLevel.Elevation);
for (var i = 0; i < toSplit.Count - 1; i++)
{
var baseLevel = toSplit[i];
var topLevel = toSplit[i + 1];
var eleIds = ElementTransformUtils.CopyElement(doc, wall.Id, translation);
doc.Regenerate();
var wallCopy = doc.GetElement(eleIds.FirstOrDefault()) as Wall;
wallCopy.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).Set(baseLevel.Id);
wallCopy.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(0);
wallCopy.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).Set(topLevel.Id);
wallCopy.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(0);
//if (i == levelsToSplit.Count - 2 && maxZ < maxLevel.Bottom)
//{
// wallCopy.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(maxZ - maxLevel.Bottom);
//}
//Wall.Create(document, curve, wall.WallType.ViewId, levels[i].ViewId, 100, 100, false, false);
}
return true;
}
}

View File

@@ -0,0 +1,20 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// Revit执行命令
/// </summary>
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class SplitFloorCmd : ExternalCommand
{
public override void Execute()
{
}
}
}

View File

@@ -0,0 +1,20 @@
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Nice3point.Revit.Toolkit.External;
namespace ShrlAlgo.RvKits.RvCivil
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class WallFinishesCmd : ExternalCommand
{
public override void Execute()
{
var wallTypes = Document.OfCollector().OfClass(typeof(WallType)).Cast<WallType>().ToList();
WinDialogHelper.ShowModeless<WallFinishesView>(new WallFinishesViewModel(wallTypes));
}
}
}

View File

@@ -0,0 +1,69 @@
<ui:FluentWindowEx
x:Class="ShrlAlgo.RvKits.RvCivil.WallFinishesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:rvCivil="clr-namespace:ShrlAlgo.RvKits.RvCivil"
xmlns:ui="https://github.com/ShrlAlgo/WPFluent"
Title="墙饰面"
Width="250"
Height="500"
MinHeight="300"
d:DataContext="{d:DesignInstance rvCivil:WallFinishesViewModel}"
mc:Ignorable="d">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ShrlAlgo.RvKits;component/WPFUI.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<ui:AutoGridEx
ChildMargin="5"
Columns="*"
Rows="*,Auto,Auto,Auto,Auto">
<GroupBox
Height="300"
VerticalAlignment="Stretch"
Header="饰面类型">
<ListBox
x:Name="CbbWallTypes"
DisplayMemberPath="Name"
ItemsSource="{Binding WallTypes}"
SelectedValue="{Binding SelectedWallType, UpdateSourceTrigger=PropertyChanged}" />
</GroupBox>
<ui:TextBoxEx
x:Name="TbWallHeight"
Grid.Row="1"
InputMethod.IsInputMethodEnabled="False"
Prefix="饰面总高度:"
Suffix="mm"
Text="{Binding WallHeight, StringFormat=\{0:F2\}, UpdateSourceTrigger=PropertyChanged}" />
<ui:TextBoxEx
x:Name="TbWallBase"
Grid.Row="2"
InputMethod.IsInputMethodEnabled="False"
Prefix="饰面底部离地高度:"
Suffix="mm"
Text="{Binding WallBaseOffset, StringFormat=\{0:F2\}, UpdateSourceTrigger=PropertyChanged}" />
<ComboBox
x:Name="CbbPlaceType"
Grid.Row="3"
IsSynchronizedWithCurrentItem="True"
SelectedIndex="0"
Text="布置方式">
<ComboBoxItem Content="墙面" />
<ComboBoxItem Content="房间" />
<ComboBoxItem Content="整墙" />
</ComboBox>
<Button
x:Name="BtnPlace"
Grid.Row="4"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Command="{Binding PlaceWallCommand}"
Content="布置" />
</ui:AutoGridEx>
</ui:FluentWindowEx>

View File

@@ -0,0 +1,15 @@
using System.Windows;
namespace ShrlAlgo.RvKits.RvCivil
{
/// <summary>
/// WallFinishesView.xaml 的交互逻辑
/// </summary>
public partial class WallFinishesView
{
public WallFinishesView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,315 @@
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Nice3point.Revit.Toolkit.External.Handlers;
namespace ShrlAlgo.RvKits.RvCivil;
public partial class WallFinishesViewModel(List<WallType> wallTypes) : ObservableObject
{
private readonly ActionEventHandler wallFinishHandler = new ();
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(PlaceWallCommand))]
private double wallBaseOffset;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(PlaceWallCommand))]
private double wallHeight;
[ObservableProperty]
private List<WallType> wallTypes = wallTypes;
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(PlaceWallCommand))]
private WallType selectedWallType;
public PlacementType PlaceType { get; set; }
private bool CanPlaceWallFinish()
{
return SelectedWallType != null && WallHeight > 0 && WallHeight > WallBaseOffset;
}
[RelayCommand(CanExecute = nameof(CanPlaceWallFinish))]
private void PlaceWall()
{
wallFinishHandler.Raise(uiapp =>
{
var uidoc = uiapp.ActiveUIDocument;
var doc = uidoc.Document;
var wallWidth = SelectedWallType.Width;
try
{
while (true)
{
switch (PlaceType)
{
case PlacementType.ByFace:
/* 项目“ShrlAlgo.RvKits (net48)”的未合并的更改
在此之前:
PlaceWallFinishesByFace(uiapp, SelectedWallType, wallWidth, WallHeight, WallBaseOffset);
break;
在此之后:
PlaceWallFinishesByFace(uiapp, SelectedWallType, wallWidth, WallHeight, WallBaseOffset);
break;
*/
WallFinishesViewModel.PlaceWallFinishesByFace(uidoc, SelectedWallType, wallWidth, WallHeight, WallBaseOffset);
break;
case PlacementType.ByWall:
PlaceWallFinishesByWall(uidoc, SelectedWallType, wallWidth, WallHeight, WallBaseOffset);
break;
case PlacementType.ByRoom:
var rooms = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms);
if (!rooms.Any())
{
MessageBox.Show("项目中当前没有房间", "温馨提示");
//message = "项目中当前没有房间";
}
PlaceWallFinishesByRoom(uidoc, SelectedWallType, wallWidth, WallHeight, WallBaseOffset);
break;
default:
break;
}
}
}
catch (Autodesk.Revit.Exceptions.OperationCanceledException) { }
});
}
private static void PlaceWallFinishesByFace(UIDocument uidoc, WallType wallType, double wallWidth, double height, double baseOffset)
{
var doc = uidoc.Document;
var referFace = uidoc.Selection.PickObject(ObjectType.Face, new GenericFilter<Wall>(), "请选择布置的墙面");
var wall = doc.GetElement(referFace) as Wall;
var face = wall.GetGeometryObjectFromReference(referFace) as Face;
var bottomFace = wall.GetAllGeometryObjects<Face>().FirstOrDefault(f => f.ComputeNormal(new UV()).IsAlmostEqualTo(-XYZ.BasisZ));
if (bottomFace == null)
{
return;
}
var baseLevel = doc.GetElement(wall.LevelId) as Level;
face.Intersect(bottomFace, out var intersectCurve);
doc.Invoke(
ts =>
{
var curve = intersectCurve.CreateOffset(wallWidth / 2, XYZ.BasisZ).CreateReversed();
var wallCreated = Wall.Create(doc, curve, wallType.Id, baseLevel.Id, height / 304.8, baseOffset / 304.8, false, false);
doc.Regenerate();
try
{
if (wallCreated.WallType.Kind == WallKind.Stacked)
{
foreach (var wallid in wallCreated.GetStackedWallMemberIds())
{
var stackedWall = doc.GetElement(wallid) as Wall;
JoinGeometryUtils.JoinGeometry(doc, wall, stackedWall);
}
}
else
{
JoinGeometryUtils.JoinGeometry(doc, wall, wallCreated);
}
//JoinGeometryUtils.JoinGeometry(doc, wall, wallCreated);
}
catch (Exception ex)
{
LogHelper.ToLog(ex.Message);
}
},
"创建墙面饰面"
);
}
private static void PlaceWallFinishesByRoom(UIDocument uidoc, WallType wallType, double wallWidth, double wallHeight, double wallBaseOffset)
{
var doc = uidoc.Document;
var refer = uidoc.Selection.PickObject(ObjectType.Element, new GenericFilter<Room>(), "请选择布置的房间");
var room = uidoc.Document.GetElement(refer) as Room;
var baseLevel = doc.GetElement(room.LevelId) as Level;
List<CurveLoop> curveLoops =[];
List<ElementId> walltojoin = [];
List<List<ElementId>> wallToJoinList = [];
var opts = new SpatialElementBoundaryOptions();
//{
// SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish
//};
//BuiltInCategory builtInCategory = (BuiltInCategory)walltype.Category.ViewId.IntegerValue;
//if (walltype.Kind == WallKind.Stacked)
//{
// var li = walltype.GetSubelements();
// walltype.get
//}
//if (walltype.Kind == WallKind.Basic)
//{
// var compounds = walltype.GetCompoundStructure();
// for (int i = 0; i < compounds.LayerCount; i++)
// {
// offest += compounds.GetWidth();
// }
//}
var segementsList = room.GetBoundarySegments(opts);
if (segementsList != null)
{
foreach (var boundarySegments in segementsList)
{
CurveLoop curveLoop = new();
foreach (var boundarySegment in boundarySegments)
{
curveLoop.Append(boundarySegment.GetCurve());
walltojoin.Add(boundarySegment.ElementId);
}
wallToJoinList.Add(walltojoin);
curveLoops.Add(curveLoop);
}
}
doc.Invoke(
ts =>
{
var options = ts.GetFailureHandlingOptions();
FailuresPreProcessor failuresProcessor = new();
options.SetFailuresPreprocessor(failuresProcessor);
ts.SetFailureHandlingOptions(options);
//Plane plane = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, new XYZ());
//var mc = doc.Create.NewModelCurveArray(array, SketchPlane.Create(doc, plane));
for (var i = 0; i < curveLoops.Count; i++)
{
//切线方向叉乘参数中的向量,内侧叉乘-z
var curveLoopOffset = CurveLoop.CreateViaOffset(curveLoops[i], wallWidth / 2, -XYZ.BasisZ);
for (var j = 0; j < curveLoopOffset.Count(); j++)
{
var curve = curveLoopOffset.ElementAt(j);
////c的方向叉乘参数中的向量内侧叉乘-z
//var lc = c.CreateOffset(100 / 304.8, -XYZ.BasisZ);
//var x = curve.ComputeDerivatives(0.5, true).BasisX.CrossProduct(-XYZ.BasisZ).Normalize() * 100 / 304.8;
//var mc = doc.Create.NewModelCurve(lc, SketchPlane.Create(doc, plane));
//var mc = doc.Create.NewModelCurve(c, SketchPlane.Create(doc, plane));
var wallCreated = Wall.Create(
doc,
curve,
wallType.Id,
baseLevel.Id,
wallHeight / 304.8,
wallBaseOffset / 304.8,
false,
false
);
wallCreated.get_Parameter(BuiltInParameter.WALL_ATTR_ROOM_BOUNDING).Set(1);
doc.Regenerate();
//连接墙体让门窗可以剪切出来
#if REVIT2018 || REVIT2020
if (wallToJoinList[i][j].IntegerValue > 0)
#elif REVIT2025
if (wallToJoinList[i][j].Value > 0)
#endif
{
var elemToJoin = doc.GetElement(wallToJoinList[i][j]);
try
{
if (wallCreated.WallType.Kind == WallKind.Stacked)
{
foreach (var wallid in wallCreated.GetStackedWallMemberIds())
{
var stackedWall = doc.GetElement(wallid) as Wall;
JoinGeometryUtils.JoinGeometry(doc, elemToJoin, stackedWall);
}
}
else
{
JoinGeometryUtils.JoinGeometry(doc, elemToJoin, wallCreated);
}
}
catch (Exception ex)
{
LogHelper.ToLog(ex.Message);
}
}
//WallUtils.AllowWallJoinAtEnd(w, 0);
//offsetCurves.Add(lc);
}
}
},
"创建房间墙饰面"
);
}
private static void PlaceWallFinishesByWall(UIDocument uidoc, WallType wallType, double wallWidth, double wallHeight, double wallBaseOffset)
{
var doc = uidoc.Document;
var refer = uidoc.Selection.PickObject(ObjectType.Element, new GenericFilter<Wall>(), "请选择布置的墙体");
var wall = uidoc.Document.GetElement(refer) as Wall;
var baselevel = doc.GetElement(wall.LevelId) as Level;
var bottomFace = wall.GetAllGeometryObjects<Face>().FirstOrDefault(f => f.ComputeNormal(new UV()).IsAlmostEqualTo(-XYZ.BasisZ));
var sideFaces = wall.GetAllGeometryObjects<Face>().Where(f => f.ComputeNormal(new UV()).DotProduct(XYZ.BasisZ) < 1.0e-09);
//CurveLoop loop = new CurveLoop();
List<Curve> curs = [];
foreach (var sideFace in sideFaces)
{
bottomFace.Intersect(sideFace, out var intersectCurve);
curs.Add(intersectCurve);
//ModelCurve ml = doc.Create.NewModelCurve(cur, doc.ActiveView.SketchPlane);
//loop.Append(cur);
}
//var loop = CurveLoop.Create(curs);
//var offestcurveloop = CurveLoop.CreateViaOffset(loop, wallwidth / 2, XYZ.BasisZ);
doc.Invoke(
ts =>
{
for (var i = 0; i < curs.Count; i++)
{
var curve = curs.ElementAt(i).CreateOffset(wallWidth / 2, -XYZ.BasisZ);
var wallCreated = Wall.Create(doc, curve, wallType.Id, baselevel.Id, wallHeight / 304.8, wallBaseOffset / 304.8, false, false);
doc.Regenerate();
try
{
if (wallCreated.WallType.Kind == WallKind.Stacked)
{
foreach (var wallid in wallCreated.GetStackedWallMemberIds())
{
var stackedwall = doc.GetElement(wallid) as Wall;
JoinGeometryUtils.JoinGeometry(doc, wall, stackedwall);
}
}
else
{
JoinGeometryUtils.JoinGeometry(doc, wall, wallCreated);
}
//JoinGeometryUtils.JoinGeometry(doc, wall, wallCreated);
}
catch (Exception ex)
{
LogHelper.ToLog(ex.Message);
}
//WallUtils.AllowWallJoinAtEnd(w, 0);
//offestcurves.Add(lc);
}
},
"创建墙体饰面"
);
}
}