功能更新

This commit is contained in:
GG Z
2026-02-23 11:21:51 +08:00
parent 3650f8550e
commit 6beb8b5be9
24 changed files with 694 additions and 1975 deletions

View File

@@ -8,11 +8,12 @@
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources RequestedTheme="Dark" />
<ui:ThemeResources RequestedTheme="Light" />
<ui:XamlControlsResources />
<!--<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/schemes/dark.xaml" />
<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/themeresources.xaml" />
<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/controlsresources.xaml" />-->
<!--<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/themeresources.xaml" />
<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/controlsresources.xaml" />
<ResourceDictionary Source="pack://application:,,,/iNKORE.UI.WPF.Modern;component/themes/schemes/dark.xaml" />-->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

View File

@@ -8,6 +8,7 @@
<UseWPF>true</UseWPF>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<ApplicationIcon>authentication.ico</ApplicationIcon>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<ItemGroup>
<Content Include="authentication.ico" />

View File

@@ -28,131 +28,198 @@
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox
Margin="5"
BorderThickness="0"
FontSize="16"
Foreground="DeepSkyBlue"
Header="公私密钥"
Style="{x:Null}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox
x:Name="PrivateKeyTextBox"
ui:ControlHelper.Header="RSA私钥"
ui:ControlHelper.PlaceholderText="请输入RSA私钥私钥需要保存在自己电脑避免外泄"
TextWrapping="WrapWithOverflow"
ToolTip="私钥需要保存在自己电脑,避免外泄" />
<TextBox
x:Name="PublicKeyTextBox"
Grid.Row="0"
Grid.Column="1"
ui:ControlHelper.Header="RSA公钥"
ui:ControlHelper.PlaceholderText="请输入RSA公钥公钥用在需要验证密钥的客户端代码、蓝图里"
TextWrapping="WrapWithOverflow"
ToolTip="公钥用在需要验证密钥的客户端代码、蓝图里" />
<Button
x:Name="ToggleButton"
Grid.Row="1"
Grid.Column="0"
Click="Button_ToggleTheme_Click"
Content="明暗切换" />
<Button
Grid.Row="1"
Grid.Column="0"
Margin="5"
HorizontalAlignment="Right"
Click="ImportPrivateKeyClick"
Content="导入文件私钥" />
<UniformGrid
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
Rows="1">
<Button Click="GenerateKeyClick" Content="计算公私密钥" />
<Button Click="SaveKeysClick" Content="保存公私密钥" />
</UniformGrid>
</Grid>
</GroupBox>
<Rectangle
Grid.Row="1"
Grid.Column="0"
Height="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Fill="Gray" />
<GroupBox
Grid.Row="1"
Grid.Column="0"
Margin="5"
BorderThickness="0"
FontSize="16"
Foreground="DeepSkyBlue"
Header="获取授权"
Style="{x:Null}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox
x:Name="MachineCodeTextBox"
ui:ControlHelper.Header="机器码:"
ui:ControlHelper.PlaceholderText="请输入硬件机器码由CPU、主板、C盘所在磁盘构成"
ToolTip="硬件机器码由CPU、主板、C盘所在磁盘构成" />
<TextBox
x:Name="LicenseTextBox"
Grid.Row="1"
Grid.Column="0"
ui:ControlHelper.Header="授权激活码:"
ui:ControlHelper.PlaceholderText="授权激活码"
IsReadOnly="True" />
<DatePicker
x:Name="ExpiryDatePicker"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Stretch"
ui:ControlHelper.Header="授权到期日期:" />
<UniformGrid
Grid.Row="1"
Grid.Column="1"
Rows="1">
<Button
<TabControl ui:TabControlHelper.IsAddTabButtonVisible="False">
<TabItem
ui:TabItemHelper.IsClosable="False"
Header="长期授权算号器"
IsSelected="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox
Margin="5"
BorderThickness="0"
FontSize="16"
Foreground="DeepSkyBlue"
Header="公私密钥"
Style="{x:Null}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox
x:Name="PrivateKeyTextBox"
ui:ControlHelper.Header="RSA私钥"
ui:ControlHelper.PlaceholderText="请输入RSA私钥私钥需要保存在自己电脑避免外泄"
TextWrapping="WrapWithOverflow"
ToolTip="私钥需要保存在自己电脑,避免外泄" />
<TextBox
x:Name="PublicKeyTextBox"
Grid.Row="0"
Grid.Column="1"
ui:ControlHelper.Header="RSA公钥"
ui:ControlHelper.PlaceholderText="请输入RSA公钥公钥用在需要验证密钥的客户端代码、蓝图里"
TextWrapping="WrapWithOverflow"
ToolTip="公钥用在需要验证密钥的客户端代码、蓝图里" />
<Button
Grid.Row="1"
Grid.Column="0"
Margin="5"
HorizontalAlignment="Right"
Click="ImportPrivateKeyClick">
<ui:IconAndText Content="导入文件私钥" Icon="{x:Static ui:SegoeFluentIcons.Import}" />
</Button>
<UniformGrid
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Right"
Rows="1">
<Button Click="GenerateKeyClick">
<ui:IconAndText Content="计算公私密钥" Icon="{x:Static ui:SegoeFluentIcons.Calculator}" />
</Button>
<Button Click="SaveKeysClick">
<ui:IconAndText Content="保存公私密钥" Icon="{x:Static ui:SegoeFluentIcons.Save}" />
</Button>
</UniformGrid>
</Grid>
</GroupBox>
<Rectangle
Grid.Row="1"
Grid.Column="0"
Height="1"
HorizontalAlignment="Stretch"
Click="LicenseClick"
Content="获取激活码" />
<Button
VerticalAlignment="Top"
Fill="Gray" />
<GroupBox
Grid.Row="1"
Grid.Column="0"
Margin="5"
BorderThickness="0"
FontSize="16"
Foreground="DeepSkyBlue"
Header="获取授权"
Style="{x:Null}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox
x:Name="MachineCodeTextBox"
ui:ControlHelper.Header="机器码:"
ui:ControlHelper.PlaceholderText="请输入硬件机器码由CPU、主板、C盘所在磁盘构成"
ToolTip="硬件机器码由CPU、主板、C盘所在磁盘构成" />
<TextBox
x:Name="LicenseTextBox"
Grid.Row="1"
Grid.Column="0"
ui:ControlHelper.Header="授权激活码:"
ui:ControlHelper.PlaceholderText="授权激活码"
IsReadOnly="True" />
<DatePicker
x:Name="ExpiryDatePicker"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Stretch"
ui:ControlHelper.Header="授权到期日期:" />
<UniformGrid
Grid.Row="1"
Grid.Column="1"
VerticalAlignment="Bottom"
Rows="1">
<Button HorizontalAlignment="Stretch" Click="LicenseClick">
<ui:IconAndText Content="获取激活码" Icon="{x:Static ui:SegoeFluentIcons.Calculator}" />
</Button>
<Button HorizontalAlignment="Stretch" Click="SaveLicenseClick">
<ui:IconAndText Content="保存激活码" Icon="{x:Static ui:SegoeFluentIcons.SaveLocal}" />
</Button>
</UniformGrid>
</Grid>
</GroupBox>
<Rectangle
Grid.Row="2"
Grid.Column="0"
Height="1"
HorizontalAlignment="Stretch"
Click="SaveLicenseClick"
Content="保存激活码" />
</UniformGrid>
</Grid>
</GroupBox>
<Rectangle
Grid.Row="2"
Grid.Column="0"
Height="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Fill="Gray" />
VerticalAlignment="Top"
Fill="Gray" />
</Grid>
</TabItem>
<TabItem ui:TabItemHelper.IsClosable="False" Header="应急动态码计算">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<UniformGrid Rows="1">
<ComboBox
x:Name="DynamicDurationComboBox"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
ui:ControlHelper.Header="授权时长:">
<ComboBoxItem
Content="1天"
IsSelected="True"
Tag="1D" />
<ComboBoxItem Content="7天" Tag="7D" />
<ComboBoxItem Content="30天" Tag="30D" />
</ComboBox>
<TextBox
x:Name="SaltTextBox"
ui:ControlHelper.Header="应急盐值Emergency Salt"
ui:ControlHelper.PlaceholderText="盐值需要与UE中设置的一致" />
</UniformGrid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
x:Name="DynamicLicenseTextBox"
ui:ControlHelper.Header="动态授权激活码:"
ui:ControlHelper.PlaceholderText="动态授权激活码"
IsReadOnly="True" />
<Button
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Click="DynamicLicenseClick">
<ui:IconAndText Content="计算动态激活码" Icon="{x:Static ui:SegoeFluentIcons.Calculator}" />
</Button>
</Grid>
</Grid>
</TabItem>
</TabControl>
<TextBox
x:Name="Message"
Grid.Row="2"
ui:ControlHelper.Header="消息:"
Foreground="LightCoral" />
<ui:ToggleSwitch
x:Name="ToggleButton"
MinWidth="80"
Margin="5,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
ui:OffContent="浅色"
ui:OnContent="深色"
Toggled="ToggleTheme_Click" />
</Grid>
</Window>

View File

@@ -3,6 +3,7 @@ using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using iNKORE.UI.WPF.Modern;
@@ -174,7 +175,7 @@ namespace KeyGen
Message.Text = ($"导入文件私钥完成");
}
}
private void Button_ToggleTheme_Click(object sender, RoutedEventArgs e)
private void ToggleTheme_Click(object sender, RoutedEventArgs e)
{
if (ThemeManager.Current.ApplicationTheme == ApplicationTheme.Dark)
{
@@ -185,5 +186,61 @@ namespace KeyGen
ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark;
}
}
/// <summary>
/// 生成通用的 6 位应急码
/// </summary>
/// <param name="durationTag">"1D", "7D", 或 "30D"</param>
public static string GenerateGlobalOTP(string durationTag, string salt)
{
string secret = salt + durationTag;
// 计算天数步长
long unixTime = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
long step = unixTime / 86400;
byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
// 字符表必须严格一致
string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 步长转 8 字节大端
byte[] stepBytes = BitConverter.GetBytes(step);
if (BitConverter.IsLittleEndian) Array.Reverse(stepBytes);
byte[] msg = new byte[8];
Array.Copy(stepBytes, 0, msg, 8 - stepBytes.Length, stepBytes.Length);
using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret)))
{
byte[] hash = hmac.ComputeHash(msg);
int offset = hash[hash.Length - 1] & 0xf;
uint binary = (uint)(((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff));
string code = "";
for (int i = 0; i < 6; i++)
{
code += charset[(int)(binary % (uint)charset.Length)];
binary /= (uint)charset.Length;
}
return code;
}
}
private void DynamicLicenseClick(object sender, RoutedEventArgs e)
{
if (DynamicDurationComboBox.SelectedItem is not ComboBoxItem selectedItem)
{
Message.Text = ("请选择授权时长");
return;
}
if (string.IsNullOrEmpty(SaltTextBox.Text))
{
Message.Text = ("请设置Salt值");
return;
}
string durationTag = selectedItem.Tag.ToString();
string salt = SaltTextBox.Text.Trim();
string globalOTP = GenerateGlobalOTP(durationTag, salt);
DynamicLicenseTextBox.Text = globalOTP;
Clipboard.SetText(globalOTP);
Message.Text = "动态应急码已复制!";
}
}
}

View File

@@ -68,6 +68,12 @@
Margin="5"
Click="CleanLinesButton_Click"
Content="清理连线" />
<Button
x:Name="ConvertToPointsButton"
Margin="5"
Click="ConvertToPointsButton_Click"
Content="多段线转点"
ToolTip="将列表中所有的多段线圆弧符号转换为几何中心点" />
<!--<Button
x:Name="CloseButton"
Margin="5"

View File

@@ -14,18 +14,29 @@ using Autodesk.AutoCAD.Windows;
using Application = Autodesk.AutoCAD.ApplicationServices.Application;
namespace Szmedi.CADkits
{
// 定义视图模型,对应列表中的一行
public class EntityReviewItem
{
public ObjectId EntityId { get; set; }
public string Handle { get; set; }
public string Position { get; set; }
// 支持属性通知可使用 INotifyPropertyChanged这里为了简化直接使用属性
public string Status { get; set; }
}
public partial class ReviewAndMatchWindow : UserControl
{
public ObservableCollection<BlockReviewItem> ReviewItems { get; set; }
public ObservableCollection<EntityReviewItem> ReviewItems { get; set; }
private readonly string _checkLineLayer;
private bool _isListeningToSelection = true;
private readonly PaletteSet _parentPalette;
private readonly Dictionary<ObjectId, TextWithPosition> _textDataMap;
// 构造函数参数改为 TerrainEntityInfo
public ReviewAndMatchWindow(
List<BlockRefWithPosition> allBlocks,
List<TerrainEntityInfo> allEntities,
Dictionary<ObjectId, ObjectId> autoPairs,
List<TextWithPosition> allTexts,
string checkLineLayer,
@@ -36,67 +47,51 @@ namespace Szmedi.CADkits
_parentPalette = parentPalette;
_textDataMap = allTexts.ToDictionary(t => t.ObjectId, t => t);
ReviewItems = new ObservableCollection<BlockReviewItem>();
ReviewItems = new ObservableCollection<EntityReviewItem>();
ReviewListView.ItemsSource = ReviewItems;
PopulateList(allBlocks, autoPairs);
PopulateList(allEntities, autoPairs);
}
private void PopulateList(List<BlockRefWithPosition> allBlocks, Dictionary<ObjectId, ObjectId> autoPairs)
private void PopulateList(List<TerrainEntityInfo> allEntities, Dictionary<ObjectId, ObjectId> autoPairs)
{
foreach (var blockData in allBlocks)
foreach (var entData in allEntities)
{
var item = new BlockReviewItem
var item = new EntityReviewItem
{
BlockId = blockData.ObjectId,
Handle = blockData.ObjectId.Handle.Value.ToString(),
Position = $"{blockData.Position.X:F2}, {blockData.Position.Y:F2}"
EntityId = entData.ObjectId,
Handle = entData.Handle,
Position = $"{entData.Position.X:F2}, {entData.Position.Y:F2}"
};
if (autoPairs.TryGetValue(blockData.ObjectId, out ObjectId textId))
if (autoPairs.TryGetValue(entData.ObjectId, out ObjectId textId))
{
// 自动匹配成功
item.Status = _textDataMap[textId].TextContent;
}
else
{
// 未匹配
item.Status = "--- 未匹配 ---";
}
ReviewItems.Add(item);
}
}
//private void LocateButton_Click(object sender, RoutedEventArgs e)
//{
// if (UnmatchedBlocksListView.SelectedItem is UnmatchedBlockItem selected)
// {
// ZoomToEntity(selected.BlockId);
// }
// else
// {
// MessageBox.Show("请先在列表中选择一个块。", "提示");
// }
//}
private void MatchButton_Click(object sender, RoutedEventArgs e)
{
if (!(ReviewListView.SelectedItem is BlockReviewItem selected))
if (!(ReviewListView.SelectedItem is EntityReviewItem selected))
{
MessageBox.Show("请先在列表中选择一个要匹配的。", "提示");
MessageBox.Show("请先在列表中选择一个要匹配的实体。", "提示");
return;
}
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
if (_parentPalette.Dock == DockSides.None)
{
// 通过控制父面板来隐藏
_parentPalette.Visible = false;
}
if (_parentPalette.Dock == DockSides.None) _parentPalette.Visible = false;
MatchButton.IsEnabled = false;
try
{
PromptEntityOptions peo = new PromptEntityOptions("\n请点选此对应的标高文字: ");
PromptEntityOptions peo = new PromptEntityOptions("\n请点选此实体对应的标高文字: ");
peo.SetRejectMessage("\n选择的不是文字对象。");
peo.AddAllowedClass(typeof(DBText), true);
peo.AddAllowedClass(typeof(MText), true);
@@ -105,7 +100,7 @@ namespace Szmedi.CADkits
if (per.Status == PromptStatus.OK)
{
UpdateBlockWithSelectedText(selected, per.ObjectId);
UpdateEntityWithSelectedText(selected, per.ObjectId);
}
}
catch (System.Exception ex)
@@ -114,17 +109,12 @@ namespace Szmedi.CADkits
}
finally
{
if (_parentPalette.Dock == DockSides.None)
{
// 操作结束后,重新显示父面板
_parentPalette.Visible = true;
}
if (_parentPalette.Dock == DockSides.None) _parentPalette.Visible = true;
MatchButton.IsEnabled = true;
}
}
private void UpdateBlockWithSelectedText(BlockReviewItem itemToUpdate, ObjectId textId)
private void UpdateEntityWithSelectedText(EntityReviewItem itemToUpdate, ObjectId textId)
{
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
@@ -132,13 +122,11 @@ namespace Szmedi.CADkits
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 1. 获取文字内容和中心点
Entity textEnt = (Entity)tr.GetObject(textId, OpenMode.ForRead);
string textContent = "";
if (textEnt is MText mtext) textContent = mtext.Contents;
else if (textEnt is DBText dbtext) textContent = dbtext.TextString;
// 验证文字内容
if (!double.TryParse(textContent, NumberStyles.Float, CultureInfo.InvariantCulture, out double elevationZ))
{
MessageBox.Show($"文字内容 '{textContent}' 不是有效的数字。", "匹配失败");
@@ -146,40 +134,62 @@ namespace Szmedi.CADkits
return;
}
// 2. 更新块
BlockReference blockRef = (BlockReference)tr.GetObject(itemToUpdate.BlockId, OpenMode.ForWrite);
blockRef.Position = new Point3d(blockRef.Position.X, blockRef.Position.Y, elevationZ);
// --- 更新逻辑:区分 BlockReference 和 DBPoint ---
Entity targetEnt = (Entity)tr.GetObject(itemToUpdate.EntityId, OpenMode.ForWrite);
Point3d currentPos;
// 3. 创建校对线
if (targetEnt is BlockReference blockRef)
{
blockRef.Position = new Point3d(blockRef.Position.X, blockRef.Position.Y, elevationZ);
currentPos = blockRef.Position;
}
else if (targetEnt is DBPoint dbPoint)
{
dbPoint.Position = new Point3d(dbPoint.Position.X, dbPoint.Position.Y, elevationZ);
currentPos = dbPoint.Position;
}
else if (targetEnt is Polyline polyline)
{
//// 修改多段线的整体标高
//polyline.Elevation = elevationZ;
//currentPos = polyline.GetPoint3dAt(0);
polyline.Elevation = elevationZ;
// 重新计算中心点用于校对线绘制
Extents3d ext = polyline.GeometricExtents;
currentPos = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
}
else
{
MessageBox.Show("未知的实体类型,无法更新。");
tr.Abort();
return;
}
// 创建连线
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
Extents3d textExtents = textEnt.GeometricExtents;
Point3d textCenter = textExtents.MinPoint + (textExtents.MaxPoint - textExtents.MinPoint) * 0.5;
Line checkLine = new Line(blockRef.Position, textCenter);
Line checkLine = new Line(currentPos, textCenter);
checkLine.Layer = _checkLineLayer;
checkLine.ColorIndex = 256; // ByLayer
checkLine.ColorIndex = 256;
btr.AppendEntity(checkLine);
tr.AddNewlyCreatedDBObject(checkLine, true);
// 更新UI
itemToUpdate.Status = $"手动: {elevationZ}";
// 强制刷新列表显示 (如果需要)
ReviewListView.Items.Refresh();
tr.Commit();
//Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog($"块 {itemToUpdate.Handle} 已手动更新为 {elevationZ}。");
}
}
}
//private void CloseButton_Click(object sender, RoutedEventArgs e)
//{
// _parentPalette.Visible = false;
//}
// 辅助方法:缩放到实体
private void ZoomToEntity(ObjectId entId)
{
if (!entId.IsValid)
{
return;
}
if (!entId.IsValid) return;
Document doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
Editor ed = doc.Editor;
@@ -191,11 +201,17 @@ namespace Szmedi.CADkits
Entity ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;
if (ent != null)
{
// 创建一个基于实体包围盒的视图
Extents3d extents = ent.GeometricExtents;
// 如果是点,包围盒可能很小,稍微扩大一点显示范围
if (ent is DBPoint)
{
extents = new Extents3d(extents.MinPoint - new Vector3d(1, 1, 0), extents.MaxPoint + new Vector3d(1, 1, 0));
}
Matrix3d ucs = ed.CurrentUserCoordinateSystem;
double viewSize = Math.Max(extents.MaxPoint.X - extents.MinPoint.X, extents.MaxPoint.Y - extents.MinPoint.Y) * 5.0;
// 扩大一点包围盒,使其不至于占满全屏
double viewSize = Math.Max(extents.MaxPoint.X - extents.MinPoint.X, extents.MaxPoint.Y - extents.MinPoint.Y) * 10.0;
if (viewSize < 1.0) viewSize = 20.0; // 最小视野
extents.TransformBy(ucs.Inverse());
ViewTableRecord view = new ViewTableRecord
{
@@ -214,16 +230,15 @@ namespace Szmedi.CADkits
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (ReviewListView.SelectedItem is BlockReviewItem selected)
if (ReviewListView.SelectedItem is EntityReviewItem selected)
{
ZoomToEntity(selected.BlockId);
ZoomToEntity(selected.EntityId);
}
}
private void SelectAndScrollToItem(ObjectId blockId)
private void SelectAndScrollToItem(ObjectId entId)
{
var itemToSelect = ReviewItems.FirstOrDefault(item => item.BlockId == blockId);
var itemToSelect = ReviewItems.FirstOrDefault(item => item.EntityId == entId);
if (itemToSelect != null)
{
ReviewListView.SelectedItem = itemToSelect;
@@ -231,8 +246,6 @@ namespace Szmedi.CADkits
}
}
private void CleanLinesButton_Click(object sender, RoutedEventArgs e)
{
MessageBoxResult result = MessageBox.Show(
@@ -243,7 +256,6 @@ namespace Szmedi.CADkits
if (result == MessageBoxResult.Yes)
{
// 调用主类中的公共静态方法来执行清理
TerrainTools.CleanAllCheckLines();
}
}
@@ -253,34 +265,25 @@ namespace Szmedi.CADkits
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
ObjectId targetId = ObjectId.Null;
_isListeningToSelection = false; // 暂时关闭自动监听,避免冲突
_isListeningToSelection = false;
LocateInListButton.IsEnabled = false;
try
{
// 1. 检查是否已经有选中的对象
PromptSelectionResult psr = ed.GetSelection();
if (psr.Status == PromptStatus.OK && psr.Value.Count > 0)
{
// 如果有,直接使用第一个
targetId = psr.Value.GetObjectIds()[0];
}
else
{
if (_parentPalette.Dock == DockSides.None)
{
// 2. 如果没有选中,则提示用户去选择
_parentPalette.Visible = false; // 隐藏面板方便用户选择
}
else
{
LocateInListButton.IsEnabled = false;
}
if (_parentPalette.Dock == DockSides.None) _parentPalette.Visible = false;
PromptEntityOptions peo = new PromptEntityOptions("\n请在图中选择一个地形点进行定位: ");
peo.SetRejectMessage("\n选择的不是块参照。");
// 允许选择块或点
PromptEntityOptions peo = new PromptEntityOptions("\n请在图中选择一个地形点(块或点)进行定位: ");
peo.SetRejectMessage("\n选择的不是块参照或点。");
peo.AddAllowedClass(typeof(BlockReference), true);
peo.AddAllowedClass(typeof(DBPoint), true);
PromptEntityResult per = ed.GetEntity(peo);
if (per.Status == PromptStatus.OK)
@@ -289,7 +292,6 @@ namespace Szmedi.CADkits
}
}
// 3. 如果成功获取到ID则在列表中定位
if (!targetId.IsNull)
{
SelectAndScrollToItem(targetId);
@@ -297,17 +299,84 @@ namespace Szmedi.CADkits
}
finally
{
// 4. 无论如何都恢复UI状态
_isListeningToSelection = true; // 重新开启监听
if (_parentPalette.Dock == DockSides.None)
{
// 操作结束后,重新显示父面板
_parentPalette.Visible = true;
}
_isListeningToSelection = true;
if (_parentPalette.Dock == DockSides.None) _parentPalette.Visible = true;
LocateInListButton.IsEnabled = true;
this.Focus();
}
}
private void ConvertToPointsButton_Click(object sender, RoutedEventArgs e)
{
Document doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
Database db = doc.Database;
Editor ed = doc.Editor;
int convertCount = 0;
using (DocumentLock docLock = doc.LockDocument())
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
// 遍历列表中所有的项
foreach (var item in ReviewItems)
{
// 获取实体对象,判断是否为多段线
if (item.EntityId.IsErased) continue;
Entity ent = tr.GetObject(item.EntityId, OpenMode.ForRead) as Entity;
if (ent is Polyline pl)
{
try
{
// 1. 计算几何中心
Extents3d ext = pl.GeometricExtents;
Point3d center = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
// 2. 创建新点 (继承图层和当前的高程)
DBPoint newPoint = new DBPoint(center);
newPoint.Layer = pl.Layer;
newPoint.Color = pl.Color;
btr.AppendEntity(newPoint);
tr.AddNewlyCreatedDBObject(newPoint, true);
// 3. 更新列表中的数据源 (核心步骤)
item.EntityId = newPoint.ObjectId;
item.Handle = newPoint.ObjectId.Handle.Value.ToString();
// 坐标显示也更新一下
item.Position = $"{center.X:F2}, {center.Y:F2}";
// 4. 删除原多段线
pl.UpgradeOpen();
pl.Erase();
convertCount++;
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n转换句柄为 {item.Handle} 的对象时失败: {ex.Message}");
}
}
}
tr.Commit();
}
}
if (convertCount > 0)
{
// 强制刷新 UI 列表显示
ReviewListView.Items.Refresh();
MessageBox.Show($"成功将 {convertCount} 个多段线符号转换为中心点。", "转换完成");
}
else
{
MessageBox.Show("列表中没有检测到可转换的多段线实体。", "提示");
}
}
}
}

View File

@@ -21,6 +21,7 @@ public class TerrainTools
{
private static PaletteSet ps;
public const string CheckLineLayer = "校对线";
[CommandMethod("MoveTerrainElevation")]
public void MoveTerrainElevation()
{
@@ -33,29 +34,97 @@ public class TerrainTools
{
// --- Phase 1: User Input ---
#region User Input Section
string targetBlockName; string blockLayerName; string textLayerName; double maxDistance = 5.0;
using (Transaction tr = db.TransactionManager.StartTransaction()) { PromptEntityOptions peoBlock = new PromptEntityOptions("\n请选择一个样例地形点块 (用于确定块名和图层): "); peoBlock.SetRejectMessage("\n选择的不是块参照。"); peoBlock.AddAllowedClass(typeof(BlockReference), true); PromptEntityResult perBlock = ed.GetEntity(peoBlock); if (perBlock.Status != PromptStatus.OK) { ed.WriteMessage("\n操作已取消。"); return; } BlockReference sampleBlockRef = (BlockReference)tr.GetObject(perBlock.ObjectId, OpenMode.ForRead); targetBlockName = sampleBlockRef.IsDynamicBlock ? ((BlockTableRecord)tr.GetObject(sampleBlockRef.DynamicBlockTableRecord, OpenMode.ForRead)).Name : sampleBlockRef.Name; blockLayerName = sampleBlockRef.Layer; ed.WriteMessage($"\n已选择样例块: '{targetBlockName}', 所在图层: '{blockLayerName}'"); tr.Commit(); }
using (Transaction tr = db.TransactionManager.StartTransaction()) { PromptEntityOptions peoText = new PromptEntityOptions("\n请选择一个样例标高文字 (用于确定图层): "); peoText.SetRejectMessage("\n选择的不是 MText 或 DBText。"); peoText.AddAllowedClass(typeof(MText), true); peoText.AddAllowedClass(typeof(DBText), true); PromptEntityResult perText = ed.GetEntity(peoText); if (perText.Status != PromptStatus.OK) { ed.WriteMessage("\n操作已取消。"); return; } Entity sampleTextEnt = (Entity)tr.GetObject(perText.ObjectId, OpenMode.ForRead); textLayerName = sampleTextEnt.Layer; ed.WriteMessage($"\n已选择样例文字所在图层: '{textLayerName}'"); tr.Commit(); }
PromptDoubleOptions pdo = new PromptDoubleOptions($"\n请输入查找标高文字的最大距离(图形单位) <{maxDistance}>: "); pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = true; pdo.DefaultValue = maxDistance; PromptDoubleResult pdr = ed.GetDouble(pdo); if (pdr.Status != PromptStatus.OK && pdr.Status != PromptStatus.None) { ed.WriteMessage("\n操作已取消。"); return; }
if (pdr.Status == PromptStatus.OK) maxDistance = pdr.Value; ed.WriteMessage($"\n最大匹配距离设置为: {maxDistance}");
string targetBlockName = null; // 仅当选中块时使用
string terrainLayerName;
// 定义处理模式: 0-块, 1-点, 2-多段线
int processingMode = 0;
string textLayerName;
double maxDistance = 5.0;
// 1. 选择样例地形点(块 或 点)
using (Transaction tr = db.TransactionManager.StartTransaction())
{
PromptEntityOptions peoEnt = new PromptEntityOptions("\n请选择一个样例地形点 (块/点/多段线(起终点重合)): ");
peoEnt.SetRejectMessage("\n选择的对象无效。");
peoEnt.AddAllowedClass(typeof(BlockReference), true);
peoEnt.AddAllowedClass(typeof(DBPoint), true);
peoEnt.AddAllowedClass(typeof(Polyline), true);
PromptEntityResult perEnt = ed.GetEntity(peoEnt);
if (perEnt.Status != PromptStatus.OK) { ed.WriteMessage("\n操作已取消。"); return; }
Entity sampleEnt = (Entity)tr.GetObject(perEnt.ObjectId, OpenMode.ForRead);
terrainLayerName = sampleEnt.Layer;
if (sampleEnt is BlockReference sampleBlock)
{
processingMode = 0;
targetBlockName = sampleBlock.IsDynamicBlock ? ((BlockTableRecord)tr.GetObject(sampleBlock.DynamicBlockTableRecord, OpenMode.ForRead)).Name : sampleBlock.Name;
ed.WriteMessage($"\n已选择模式: [处理块]。块名: '{targetBlockName}', 图层: '{terrainLayerName}'");
}
else if (sampleEnt is DBPoint)
{
processingMode = 1;
ed.WriteMessage($"\n已选择模式: [处理点实体]。所在图层: '{terrainLayerName}'");
}
else if (sampleEnt is Polyline)
{
processingMode = 2;
ed.WriteMessage("\n模式: [多段线]");
}
tr.Commit();
}
// 2. 选择样例文字
using (Transaction tr = db.TransactionManager.StartTransaction())
{
PromptEntityOptions peoText = new PromptEntityOptions("\n请选择一个样例标高文字 (用于确定图层): ");
peoText.SetRejectMessage("\n选择的不是 MText 或 DBText。");
peoText.AddAllowedClass(typeof(MText), true);
peoText.AddAllowedClass(typeof(DBText), true);
PromptEntityResult perText = ed.GetEntity(peoText);
if (perText.Status != PromptStatus.OK) { ed.WriteMessage("\n操作已取消。"); return; }
Entity sampleTextEnt = (Entity)tr.GetObject(perText.ObjectId, OpenMode.ForRead);
textLayerName = sampleTextEnt.Layer;
ed.WriteMessage($"\n已选择样例文字所在图层: '{textLayerName}'");
tr.Commit();
}
// 3. 输入距离
PromptDoubleOptions pdo = new PromptDoubleOptions($"\n请输入查找标高文字的最大距离(图形单位) <{maxDistance}>: ");
pdo.AllowNegative = false; pdo.AllowZero = false; pdo.AllowNone = true; pdo.DefaultValue = maxDistance;
PromptDoubleResult pdr = ed.GetDouble(pdo);
if (pdr.Status != PromptStatus.OK && pdr.Status != PromptStatus.None) { ed.WriteMessage("\n操作已取消。"); return; }
if (pdr.Status == PromptStatus.OK) maxDistance = pdr.Value;
#endregion
// --- Phase 2: Automatic Matching ---
List<BlockRefWithPosition> terrainBlocks;
List<TextWithPosition> elevationTexts;
Dictionary<ObjectId, ObjectId> validPairs;
List<TerrainEntityInfo> terrainEntities = new List<TerrainEntityInfo>();
int updatedCount = 0;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
terrainBlocks = CollectBlocks(tr, btr, targetBlockName, blockLayerName);
if (terrainBlocks.Count == 0) { ed.WriteMessage($"\n错误: 在图层 '{blockLayerName}' 上未找到名为 '{targetBlockName}' 的块。"); return; }
elevationTexts = CollectFilteredTexts(tr, btr, textLayerName);
// 根据模式采集
if (processingMode == 0)
terrainEntities = CollectBlocks(tr, btr, targetBlockName, terrainLayerName);
else if (processingMode == 1)
terrainEntities = CollectPoints(tr, btr, terrainLayerName);
else if (processingMode == 2)
terrainEntities = CollectPolylines(tr, btr, terrainLayerName);
var elevationTexts = CollectFilteredTexts(tr, btr, textLayerName);
if (elevationTexts.Count == 0) { ed.WriteMessage($"\n警告: 在图层 '{textLayerName}' 上未找到任何有效的标高文字。"); tr.Commit(); return; }
ed.WriteMessage($"\n正在使用贪婪匹配算法分析 {terrainBlocks.Count} 个...");
validPairs = GetScoredValidPairs(terrainBlocks, elevationTexts, maxDistance);
ed.WriteMessage($"\n正在使用贪婪匹配算法分析 {terrainEntities.Count} 个地形点...");
// 核心匹配算法不需要修改,因为它只依赖 ObjectId 和 Position这在 TerrainEntityInfo 中都有
var validPairs = GetScoredValidPairs(terrainEntities, elevationTexts, maxDistance);
ed.WriteMessage($"\n分析完成。找到 {validPairs.Count} 个最优匹配对。");
if (validPairs.Count > 0)
@@ -63,17 +132,46 @@ public class TerrainTools
CreateLayerIfNotExists(db, tr, CheckLineLayer, 8);
ProgressMeter pm = new ProgressMeter();
pm.SetLimit(validPairs.Count);
pm.Start("正在更新已自动匹配的...");
pm.Start("正在更新已自动匹配的实体...");
var textDataMap = elevationTexts.ToDictionary(t => t.ObjectId, t => t);
foreach (var pair in validPairs)
{
if (double.TryParse(textDataMap[pair.Value].TextContent, out double elevationZ))
{
BlockReference blockRef = (BlockReference)tr.GetObject(pair.Key, OpenMode.ForWrite);
blockRef.Position = new Point3d(blockRef.Position.X, blockRef.Position.Y, elevationZ);
Line line = new Line(blockRef.Position, textDataMap[pair.Value].Position);
line.Layer = CheckLineLayer;
line.ColorIndex = 256;
// 更新位置逻辑:需要判断是块还是点
Entity ent = (Entity)tr.GetObject(pair.Key, OpenMode.ForWrite);
Point3d currentPos;
if (ent is BlockReference blk)
{
blk.Position = new Point3d(blk.Position.X, blk.Position.Y, elevationZ);
currentPos = blk.Position;
}
else if (ent is DBPoint pt)
{
pt.Position = new Point3d(pt.Position.X, pt.Position.Y, elevationZ);
currentPos = pt.Position;
}
else if (ent is Polyline pl)
{
// 对于 2D 多段线,修改 Elevation 属性
pl.Elevation = elevationZ;
Extents3d ext = pl.GeometricExtents;
currentPos = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
//currentPos = pl.GetPoint3dAt(0); // 取第一个点作为连线起点
}
else
{
continue;
}
Line line = new Line(currentPos, textDataMap[pair.Value].Position)
{
Layer = CheckLineLayer,
ColorIndex = 256
};
btr.AppendEntity(line);
tr.AddNewlyCreatedDBObject(line, true);
updatedCount++;
@@ -82,124 +180,183 @@ public class TerrainTools
pm.Stop();
}
tr.Commit();
ed.WriteMessage($"\n\n--- 自动处理报告 ---\n成功自动更新 {updatedCount} 个实体。\n{terrainEntities.Count - updatedCount} 个实体需要审核。");
if (terrainEntities.Count > 0)
{
if (ps == null) { ps = new PaletteSet("审核并校对高程点", new Guid("C4E61D33-3162-4889-A263-6A1D2C52D26C")); }
if (ps.Count > 0) { ps.Remove(0); }
// UI 控件初始化
var reviewControl = new ReviewAndMatchWindow(terrainEntities, validPairs, elevationTexts, CheckLineLayer, ps);
ElementHost host = new ElementHost { Dock = DockStyle.Fill, Child = reviewControl };
ps.Add("ReviewAndMatch", host);
ps.Visible = true;
}
}
// --- Phase 3: Reporting and Launching Review Palette ---
ed.WriteMessage($"\n\n--- 自动处理报告 ---\n成功自动更新 {updatedCount} 个块。\n{terrainBlocks.Count - updatedCount} 个块需要审核。");
if (terrainBlocks.Count > 0)
{
if (ps == null) { ps = new PaletteSet("审核并校对高程点", new Guid("C4E61D33-3162-4889-A263-6A1D2C52D26C")); }
if (ps.Count > 0) { ps.Remove(0); }
var reviewControl = new ReviewAndMatchWindow(terrainBlocks, validPairs, elevationTexts, CheckLineLayer, ps);
ElementHost host = new ElementHost { Dock = DockStyle.Fill, Child = reviewControl };
ps.Add("ReviewAndMatch", host);
ps.Visible = true;
}
}
catch (System.Exception ex) { ed.WriteMessage($"\n发生严重错误: {ex.Message}\n{ex.StackTrace}"); }
}
private Dictionary<ObjectId, ObjectId> GetScoredValidPairs(List<BlockRefWithPosition> blocks, List<TextWithPosition> texts, double maxDist)
// 匹配算法逻辑保持不变,只需签名中的 List 类型改为 TerrainEntityInfo
private Dictionary<ObjectId, ObjectId> GetScoredValidPairs(List<TerrainEntityInfo> entities, List<TextWithPosition> texts, double maxDist)
{
// 1. 预先计算每个文字的竞争情况
var textCompetition = new Dictionary<ObjectId, double>();
// ... (原逻辑不变ent.Position 属性是一样的) ...
foreach (var text in texts)
{
var distancesToBlocks = blocks
var distancesToEnts = entities
.Select(b => new Point2d(b.Position.X, b.Position.Y).GetDistanceTo(new Point2d(text.Position.X, text.Position.Y)))
.OrderBy(d => d)
.ToList();
if (distancesToBlocks.Count >= 2)
if (distancesToEnts.Count >= 2)
{
double d1 = distancesToBlocks[0]; // 最近距离
double d2 = distancesToBlocks[1]; // 次近距离
// 竞争度比率d1/d2越小说明越没有争议。范围 [0, 1]
double d1 = distancesToEnts[0];
double d2 = distancesToEnts[1];
textCompetition[text.ObjectId] = (d2 > 0.001) ? (d1 / d2) : 1.0;
}
else
{
textCompetition[text.ObjectId] = 0.0; // 只有一个块,没有竞争
textCompetition[text.ObjectId] = 0.0;
}
}
// 2. 计算所有可能配对的综合得分
var allPossiblePairs = new List<Tuple<ObjectId, ObjectId, double>>(); // BlockId, TextId, Score
foreach (var block in blocks)
var allPossiblePairs = new List<Tuple<ObjectId, ObjectId, double>>();
foreach (var ent in entities)
{
foreach (var text in texts)
{
double dist = new Point2d(block.Position.X, block.Position.Y)
double dist = new Point2d(ent.Position.X, ent.Position.Y)
.GetDistanceTo(new Point2d(text.Position.X, text.Position.Y));
if (dist <= maxDist)
{
// 距离分:距离越小,分数越高。用 1/dist 标准化
double distanceScore = 1.0 / (dist + 0.001); // 加一个小数防止除以0
// 唯一性分:竞争度越小(比率越小),分数越高
double uniquenessScore = 1.0 - textCompetition[text.ObjectId]; // 范围 [0, 1]
// **综合得分 = 70% 距离分 + 30% 唯一性分**
double finalScore = 0.7 * distanceScore + 0.3 * uniquenessScore * distanceScore; // 让唯一性也受距离影响
allPossiblePairs.Add(new Tuple<ObjectId, ObjectId, double>(block.ObjectId, text.ObjectId, finalScore));
double distanceScore = 1.0 / (dist + 0.001);
double uniquenessScore = 1.0 - textCompetition[text.ObjectId];
double finalScore = 0.7 * distanceScore + 0.3 * uniquenessScore * distanceScore;
allPossiblePairs.Add(new Tuple<ObjectId, ObjectId, double>(ent.ObjectId, text.ObjectId, finalScore));
}
}
}
// 3. 按综合得分从高到低排序
var sortedPairs = allPossiblePairs.OrderByDescending(p => p.Item3).ToList();
// 4. 贪婪匹配最高分的
var validPairs = new Dictionary<ObjectId, ObjectId>();
var matchedBlocks = new HashSet<ObjectId>();
var matchedEnts = new HashSet<ObjectId>();
var matchedTexts = new HashSet<ObjectId>();
foreach (var pair in sortedPairs)
{
ObjectId blockId = pair.Item1;
ObjectId textId = pair.Item2;
if (!matchedBlocks.Contains(blockId) && !matchedTexts.Contains(textId))
if (!matchedEnts.Contains(pair.Item1) && !matchedTexts.Contains(pair.Item2))
{
validPairs.Add(blockId, textId);
matchedBlocks.Add(blockId);
matchedTexts.Add(textId);
validPairs.Add(pair.Item1, pair.Item2);
matchedEnts.Add(pair.Item1);
matchedTexts.Add(pair.Item2);
}
}
return validPairs;
}
/// <summary>
/// 采集多段线方法 ---
/// </summary>
/// <param name="tr"></param>
/// <param name="btr"></param>
/// <param name="layerName"></param>
/// <returns></returns>
private List<TerrainEntityInfo> CollectPolylines(Transaction tr, BlockTableRecord btr, string layerName)
{
var list = new List<TerrainEntityInfo>();
foreach (ObjectId id in btr)
{
if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(Polyline))))
{
var pl = (Polyline)tr.GetObject(id, OpenMode.ForRead);
if (pl.Layer.Equals(layerName, StringComparison.OrdinalIgnoreCase))
{
try
{
// 【核心修改】:对于圆弧/圆形多段线,取其几何中心作为参考点
// 这样即使多段线是由两个半圆组成的,中心点也会准确落在圆心
Extents3d ext = pl.GeometricExtents;
Point3d center = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5;
// 将计算出的“圆心”存入 TerrainEntityInfo
list.Add(new TerrainEntityInfo(id, center));
}
catch
{
// 如果某些特殊情况无法获取包围盒,则退而求其次取第一个点
list.Add(new TerrainEntityInfo(id, pl.GetPoint3dAt(0)));
}
}
}
}
return list;
}
/// 采集点
/// </summary>
/// <param name="tr"></param>
/// <param name="btr"></param>
/// <param name="layerName"></param>
/// <returns></returns>
private List<TerrainEntityInfo> CollectPoints(Transaction tr, BlockTableRecord btr, string layerName)
{
var list = new List<TerrainEntityInfo>();
foreach (ObjectId id in btr)
{
if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(DBPoint))))
{
var pt = (DBPoint)tr.GetObject(id, OpenMode.ForRead);
if (pt.Layer.Equals(layerName, StringComparison.OrdinalIgnoreCase))
{
list.Add(new TerrainEntityInfo(id, pt.Position));
}
}
}
return list;
}
// 修改:采集块实体 (返回类型变更为 TerrainEntityInfo)
private List<TerrainEntityInfo> CollectBlocks(Transaction tr, BlockTableRecord btr, string targetBlockName, string blockLayerName)
{
var list = new List<TerrainEntityInfo>();
foreach (ObjectId id in btr)
if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(BlockReference))))
{
var br = (BlockReference)tr.GetObject(id, OpenMode.ForRead);
string name = br.IsDynamicBlock ? ((BlockTableRecord)tr.GetObject(br.DynamicBlockTableRecord, OpenMode.ForRead)).Name : br.Name;
if (br.Layer.Equals(blockLayerName, StringComparison.OrdinalIgnoreCase) && name.Equals(targetBlockName, StringComparison.OrdinalIgnoreCase))
list.Add(new TerrainEntityInfo(id, br.Position));
}
return list;
}
private List<TextWithPosition> CollectFilteredTexts(Transaction tr, BlockTableRecord btr, string textLayerName) { var list = new List<TextWithPosition>(); Regex rgx = new Regex(@"[\u4e00-\u9fa5]"); foreach (ObjectId id in btr) { if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(DBText))) || id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(MText)))) { var ent = (Entity)tr.GetObject(id, OpenMode.ForRead); if (ent.Layer.Equals(textLayerName, StringComparison.OrdinalIgnoreCase)) { string txt = (ent is MText mt) ? mt.Contents : ((DBText)ent).TextString; if (!string.IsNullOrWhiteSpace(txt) && !rgx.IsMatch(txt) && txt.Contains(".") && double.TryParse(txt, out _)) { try { Extents3d ext = ent.GeometricExtents; Point3d center = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5; list.Add(new TextWithPosition(id, center, txt)); } catch { } } } } } return list; }
private void CreateLayerIfNotExists(Database db, Transaction tr, string layerName, short colorIndex) { LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead); if (!lt.Has(layerName)) { lt.UpgradeOpen(); var ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex) }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } }
// 清理功能保持不变
public static void CleanAllCheckLines()
{
// ... (与原代码一致,此处略) ...
// 请确保保留原 CleanAllCheckLines 实现
Document doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
Database db = doc.Database;
Editor ed = doc.Editor;
int cleanedCount = 0;
using (doc.LockDocument())
using (Transaction tr = db.TransactionManager.StartTransaction())
{
try
{
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
if (!lt.Has(CheckLineLayer))
{
ed.WriteMessage($"\n图层 '{CheckLineLayer}' 不存在,无需清理。");
return;
}
if (!lt.Has(CheckLineLayer)) return;
LayerTableRecord layer = (LayerTableRecord)tr.GetObject(lt[CheckLineLayer], OpenMode.ForRead);
if (layer.IsLocked)
{
layer.UpgradeOpen();
layer.IsLocked = false;
}
if (layer.IsLocked) { layer.UpgradeOpen(); layer.IsLocked = false; }
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForRead);
foreach (ObjectId id in btr)
{
if (id.ObjectClass.DxfName.Equals("LINE", StringComparison.OrdinalIgnoreCase))
@@ -207,72 +364,13 @@ public class TerrainTools
Line line = tr.GetObject(id, OpenMode.ForRead) as Line;
if (line != null && line.Layer.Equals(CheckLineLayer, StringComparison.OrdinalIgnoreCase))
{
line.UpgradeOpen();
line.Erase();
cleanedCount++;
line.UpgradeOpen(); line.Erase(); cleanedCount++;
}
}
}
tr.Commit();
ed.WriteMessage($"\n已清理 {cleanedCount} 条校对线。");
ed.Regen();
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n清理时发生错误: {ex.Message}");
tr.Abort();
tr.Commit(); ed.WriteMessage($"\n已清理 {cleanedCount} 条校对线。"); ed.Regen();
}
catch { tr.Abort(); }
}
}
/// <summary>
/// Implements a greedy algorithm to find the best one-to-one block/text pairings.
/// </summary>
private Dictionary<ObjectId, ObjectId> GetGreedyValidPairs(List<BlockRefWithPosition> blocks, List<TextWithPosition> texts, double maxDist)
{
// 1. Create a list of all possible pairs within the max distance
var allPossiblePairs = new List<Tuple<ObjectId, ObjectId, double>>();
foreach (var block in blocks)
{
foreach (var text in texts)
{
double dist = new Point2d(block.Position.X, block.Position.Y)
.GetDistanceTo(new Point2d(text.Position.X, text.Position.Y));
if (dist <= maxDist)
{
allPossiblePairs.Add(new Tuple<ObjectId, ObjectId, double>(block.ObjectId, text.ObjectId, dist));
}
}
}
// 2. Sort all pairs by distance, ascending
var sortedPairs = allPossiblePairs.OrderBy(p => p.Item3).ToList();
// 3. Iterate and match, ensuring one-to-one mapping
var validPairs = new Dictionary<ObjectId, ObjectId>();
var matchedBlocks = new HashSet<ObjectId>();
var matchedTexts = new HashSet<ObjectId>();
foreach (var pair in sortedPairs)
{
ObjectId blockId = pair.Item1;
ObjectId textId = pair.Item2;
// If neither the block nor the text has been matched yet, it's a valid pair
if (!matchedBlocks.Contains(blockId) && !matchedTexts.Contains(textId))
{
validPairs.Add(blockId, textId);
matchedBlocks.Add(blockId);
matchedTexts.Add(textId);
}
}
return validPairs;
}
#region Other Helper Methods (Unchanged)
private List<BlockRefWithPosition> CollectBlocks(Transaction tr, BlockTableRecord btr, string targetBlockName, string blockLayerName) { var list = new List<BlockRefWithPosition>(); foreach (ObjectId id in btr) if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(BlockReference)))) { var br = (BlockReference)tr.GetObject(id, OpenMode.ForRead); string name = br.IsDynamicBlock ? ((BlockTableRecord)tr.GetObject(br.DynamicBlockTableRecord, OpenMode.ForRead)).Name : br.Name; if (br.Layer.Equals(blockLayerName, StringComparison.OrdinalIgnoreCase) && name.Equals(targetBlockName, StringComparison.OrdinalIgnoreCase)) list.Add(new BlockRefWithPosition(id, br.Position)); } return list; }
private List<TextWithPosition> CollectFilteredTexts(Transaction tr, BlockTableRecord btr, string textLayerName) { var list = new List<TextWithPosition>(); Regex rgx = new Regex(@"[\u4e00-\u9fa5]"); foreach (ObjectId id in btr) { if (id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(DBText))) || id.ObjectClass.IsDerivedFrom(RXObject.GetClass(typeof(MText)))) { var ent = (Entity)tr.GetObject(id, OpenMode.ForRead); if (ent.Layer.Equals(textLayerName, StringComparison.OrdinalIgnoreCase)) { string txt = (ent is MText mt) ? mt.Contents : ((DBText)ent).TextString; if (!string.IsNullOrWhiteSpace(txt) && !rgx.IsMatch(txt) && txt.Contains(".") && double.TryParse(txt, out _)) { try { Extents3d ext = ent.GeometricExtents; Point3d center = ext.MinPoint + (ext.MaxPoint - ext.MinPoint) * 0.5; list.Add(new TextWithPosition(id, center, txt)); } catch { } } } } } return list; }
private void CreateLayerIfNotExists(Database db, Transaction tr, string layerName, short colorIndex) { LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead); if (!lt.Has(layerName)) { lt.UpgradeOpen(); var ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex) }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); } }
#endregion
}

View File

@@ -20,3 +20,18 @@ public readonly struct TextWithPosition
TextContent = content;
}
}
// 用于通用存储 地形点(块或点实体)的信息
public readonly struct TerrainEntityInfo
{
public ObjectId ObjectId { get; }
public Point3d Position { get; }
public string Handle { get; }
public TerrainEntityInfo(ObjectId id, Point3d pos)
{
ObjectId = id;
Position = pos;
Handle = id.Handle.Value.ToString();
}
}

8
Szmedi.RvKits.crproj Normal file
View File

@@ -0,0 +1,8 @@
<project outputDir="I:\04 程序开发\Revit Dev\Szmedi.RvKit\bin\Release" baseDir="I:\04 程序开发\Revit Dev\Szmedi.RvKit\bin\Release" xmlns="http://confuser.codeplex.com">
<rule pattern="true" />
<rule pattern="member-type('type')" inherit="false">
<protection id="rename" action="remove" />
</rule>
<module path="bin\Release\2018\Szmedi.RvKits.dll" />
<module path="bin\Release\2020\Szmedi.RvKits.dll" />
</project>

View File

@@ -55,7 +55,7 @@
<Setter Property="Background" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorMessage, Converter={StaticResource NullOrEmptyToBooleanConverter}}" Value="True">
<Setter Property="Background" Value="Red" />
<Setter Property="Background" Value="IndianRed" />
</DataTrigger>
</Style.Triggers>
</Style>

View File

@@ -63,7 +63,8 @@ namespace Szmedi.RvKits.MEPTools
AppDomain.CurrentDomain.AssemblyResolve += ResolveHelper.ResolveAssembly;
try
{
Items = package.ToList<FacilityItem>(1, configuration => configuration.SkipCastingErrors());
var resultList = package.ToList<FacilityItem>(1, configuration => configuration.SkipCastingErrors());
Items = resultList.Where(f => !string.IsNullOrEmpty(f.Name)).ToList();
}
catch (EPPlus.Core.Extensions.Exceptions.ExcelValidationException ex)
{
@@ -207,7 +208,10 @@ namespace Szmedi.RvKits.MEPTools
foreach (var item in Items)
{
double minRatio = 30;
if(string.IsNullOrEmpty(item.Name))
{
continue;
}
foreach (var family in Families)
{
var famName = family.Name;

View File

@@ -16,7 +16,7 @@
<AssemblyName>Szmedi.RvKits</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<Company>Szmedi</Company>
<AssemblyVersion>2.2.3.83</AssemblyVersion>
<AssemblyVersion>2.2.3.84</AssemblyVersion>
<ProductVersion>$(AssemblyVersion)</ProductVersion>
<FileVersion>$(AssemblyVersion)</FileVersion>
</PropertyGroup>

View File

@@ -4,7 +4,7 @@
; 定义应用程序的名称
#define MyAppName "SzmediTools"
; 定义应用程序的版本号
#define MyAppVersion "2.2.3.83"
#define MyAppVersion "2.2.3.84"
; 定义应用程序的发布者
#define MyAppPublisher "SZMEDI"
; 定义应用程序的网址

View File

@@ -8,7 +8,6 @@
xmlns:editor="clr-namespace:RoslynPad.Editor;assembly=RoslynPad.Editor.Windows"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<ResourceDictionary.MergedDictionaries>
<!--<ResourceDictionary Source="pack://application:,,,/Szmedi.RvKits;component/Controls/MaterialWindow.xaml" />-->
<!--<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/Internal/MaterialDesignTheme.BaseThemeColors.xaml" />-->
<!--<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Dark.xaml" />-->

View File

@@ -1,172 +0,0 @@
using Autodesk.Revit.DB;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RevitAddins
{
internal class ElementModel
{
private readonly Element element;
private readonly EngineeringType engineeringType;
public string Category => element.GetParameters("深圳构件标识").FirstOrDefault()?.AsString() ?? "无";
public int Count => Parameters.Count;
public Dictionary<string, List<Parameter>> Groups
{
get
{
var groups = new Dictionary<string, List<Parameter>>();
foreach (Parameter parameter in Parameters)
{
string groupName = string.Empty;
var parameterName = parameter.Definition.Name;
switch (engineeringType)
{
case EngineeringType.Architecture:
//对Revit中的所有模型元素按照“深圳构件标识”属性的值进行分组分组后每个组中的元素都具有相同的“深圳构件标识”属性值。每个组都创建一张Sheet表格SheetName为“深圳构件标识”属性的值
//Revit第一行是深圳构件标识的属性值
//第二行是根据参数的名称,比如属性包含名称、编号、编码,则为身份信息,包含基点坐标则为定位信息,其余为技术信息
//第三行是Revit中的元素的“深圳构件标识”属性值对应的参数的名称
{
if (parameterName is "名称" or "编号" or "编码")
{
groupName = "身份信息";
}
else if (parameterName.Contains("基点坐标") || parameterName is "所在楼层" or "建筑单体名称")
{
groupName = "定位信息";
}
else if (parameterName.Contains("生产") || parameterName.Contains("产品") || parameterName.Contains("出厂"))
{
groupName = "生产信息";
}
else
{
groupName = "技术信息";
}
break;
}
case EngineeringType.Municipal:
{
//第二行是根据参数的名称,比如属性包含模型元素名称、编号、分类编码,则为身份信息,包含桩号范围、桥面高度、相对位置、梁底高程则为定位信息,包含长度、宽度、高度则为构造尺寸,其余为设计信息
if (parameterName is "模型元素名称" or "编号" or "分类编码")
{
groupName = "身份信息";
}
else if (parameterName is "桩号范围" or "桥面高度" or "相对位置" or "梁底高程")
{
groupName = "定位信息";
}
else if (parameterName is "长度" or "宽度" or "高度")
{
groupName = "构造尺寸";
}
else
{
groupName = "设计信息";
}
break;
}
default:
break;
}
if (!groups.ContainsKey(groupName))
{
groups[groupName] = new List<Parameter>();
}
groups[groupName].Add(parameter);
}
//对分组后的参数进行排序,排序按照名称、编号、分类编码、模型元素名称
//var sortedParameters = new Dictionary<string, List<Parameter>>();
//var paramOrder = new List<string>
//{
// "名称",
// "编号",
// "分类编码",
// "模型元素名称",
// "建筑单体名称",
// "所在楼层",
// "基点坐标X",
// "基点坐标Y",
// "基点坐标Z",
// "主要材料导热系数",
// "主要材料密度",
// "K 值",
// "耐火极限",
// "墙厚",
//};
foreach (var group in groups)
{
group.Value.Sort((x, y) => string.Compare(x.Definition.Name, y.Definition.Name, StringComparison.Ordinal));
}
//对分组进行排序,顺序是身份信息、定位信息、构造尺寸、设计信息、技术信息、生产信息
var sortedGroups = new Dictionary<string, List<Parameter>>();
var groupOrder = new List<string>
{
"身份信息",
"定位信息",
"构造尺寸",
"设计信息",
"技术信息",
"生产信息"
};
foreach (var groupName in groupOrder)
{
if (groups.ContainsKey(groupName))
{
sortedGroups[groupName] = groups[groupName];
}
}
//将排序后的分组添加到字典中
foreach (var group in groups)
{
if (!sortedGroups.ContainsKey(group.Key))
{
sortedGroups[group.Key] = group.Value;
}
}
return groups;
}
}
private List<Parameter> Parameters
{
get
{
var parameters = new List<Parameter>();
foreach (Parameter parameter in element.Parameters)
{
if (parameter.HasValue && parameter.Definition.ParameterGroup == BuiltInParameterGroup.PG_ADSK_MODEL_PROPERTIES && !string.IsNullOrEmpty(parameter.AsString()))
{
parameters.Add(parameter);
}
}
return parameters;
}
}
public ElementModel(Element element, EngineeringType engineeringType)
{
this.element = element;
this.engineeringType = engineeringType;
}
}
enum EngineeringType
{
Architecture,
Municipal
}
}

View File

@@ -1,64 +0,0 @@
using System;
using Autodesk.Windows;
using ricaun.Revit.ExtensibleShortcuts.Services;
namespace ricaun.Revit.ExtensibleShortcuts
{
/// <summary>
/// 为菜单按钮设置快捷键的扩展类
/// </summary>
/// <para>
/// private void SetShortcuts(Autodesk.Revit.UI.RibbonItem ribbonItem, params string[] shortcuts)
/// {
/// App.app.ControlledApplication.ApplicationInitialized += (object s, ApplicationInitializedEventArgs e)=>
/// {
/// KeyboardShortcutExtension.SetShortcutUnique(Autodesk.Windows.RibbonItem(需要转换), shortcuts);
/// };
///}
/// </para>
public static class KeyboardShortcutExtension
{
/// <summary>
/// 设置快捷键
/// </summary>
/// <param name="ribbonItem"></param>
/// <param name="shortcuts"></param>
/// <returns></returns>
public static bool SetShortcut(this RibbonItem ribbonItem, params string[] shortcuts)
{
KeyboardShortcutService keyboardShortcutService = new KeyboardShortcutService();
keyboardShortcutService.RemoveRibbonItemShortcut(ribbonItem.Id);
return keyboardShortcutService.SetRibbonItemShortcut(ribbonItem.Id, string.Join("#", shortcuts));
}
/// <summary>
/// 设置唯一快捷键
/// </summary>
/// <param name="ribbonItem"></param>
/// <param name="shortcuts"></param>
/// <returns></returns>
public static bool SetShortcutUnique(this RibbonItem ribbonItem, params string[] shortcuts)
{
KeyboardShortcutService keyboardShortcutService = new KeyboardShortcutService();
string text = shortcuts[0];
return !keyboardShortcutService.HasRibbonItemShortcut(ribbonItem.Id) && !keyboardShortcutService.HasCommandShortcut(text) && keyboardShortcutService.SetRibbonItemShortcut(ribbonItem.Id, string.Join("#", shortcuts));
}
/// <summary>
/// 是否具有快捷键
/// </summary>
/// <param name="ribbonItem"></param>
/// <returns></returns>
public static bool HasShortcut(this RibbonItem ribbonItem)
{
return new KeyboardShortcutService().HasRibbonItemShortcut(ribbonItem.Id);
}
/// <summary>
/// 移除快捷键
/// </summary>
/// <param name="ribbonItem"></param>
/// <returns></returns>
public static bool RemoveShortcut(this RibbonItem ribbonItem)
{
return new KeyboardShortcutService().RemoveRibbonItemShortcut(ribbonItem.Id);
}
}
}

View File

@@ -1,208 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Autodesk.Revit.UI;
using Szmedi.RvKits.Mvvm.Attributes;
using UIFramework;
using UIFrameworkServices;
namespace ricaun.Revit.ExtensibleShortcuts.Services
{
/// <summary>
/// 通过命令id设置快捷键
/// </summary>
public class KeyboardShortcutService
{
/// <summary>
/// 设置菜单快捷键
/// </summary>
/// <param name="id"></param>
/// <param name="shortcutsRep"></param>
/// <returns></returns>
public bool SetRibbonItemShortcut(string id, string shortcutsRep)
{
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
if (this.Commands.TryGetValue(id, out UIFrameworkServices.ShortcutItem shortcutItem))
{
shortcutItem.ShortcutsRep = shortcutsRep;
shortcutItem.Shortcuts.Clear();
//多个快捷键中间以#分割
foreach (string text in shortcutsRep.Split('#'))
{
shortcutItem.Shortcuts.Add(text);
}
dictionary.Add(id, shortcutItem);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
this.SaveShortcuts(list);
this.ArmCommands();
return true;
}
return false;
}
/// <summary>
/// 通过外部命令类设置快捷键
/// </summary>
/// <param name="id"></param>
/// <param name="shortcutsRep"></param>
/// <returns></returns>
public bool SetRibbonItemShortcut<T>() where T : IExternalCommand
{
var attribute = typeof(T).GetCustomAttribute<ShortcutRepAttribute>();
if (attribute == null)
{
return false;
}
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
foreach (var item in Commands)
{
if (item.Key.EndsWith(nameof(T)))
{
ShortcutItem shortcutItem = item.Value;
shortcutItem.ShortcutsRep = attribute.Shortcuts;
shortcutItem.Shortcuts.Clear();
//多个快捷键中间以#分割
foreach (string text in attribute.Shortcuts.Split('#'))
{
shortcutItem.Shortcuts.Add(text);
}
dictionary.Add(item.Key, shortcutItem);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
this.SaveShortcuts(list);
this.ArmCommands();
return true;
}
}
return false;
}
/// <summary>
/// 移除菜单快捷键
/// </summary>
/// <param name="id">命令id</param>
/// <returns></returns>
public bool RemoveRibbonItemShortcut(string id)
{
Dictionary<string, UIFrameworkServices.ShortcutItem> dictionary = this.LoadShortcuts();
UIFrameworkServices.ShortcutItem shortcutItem;
if (dictionary.TryGetValue(id, out shortcutItem))
{
dictionary.Remove(id);
List<UIFrameworkServices.ShortcutItem> list = dictionary.Select((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value).ToList<UIFrameworkServices.ShortcutItem>();
this.SaveShortcuts(list);
this.ArmCommands();
return true;
}
return false;
}
/// <summary>
/// 是否具有菜单快捷键
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public bool HasRibbonItemShortcut(string id)
{
return this.LoadShortcuts().TryGetValue(id, out var _);
}
/// <summary>
/// 获取快捷键的映射(最多五个按键)
/// </summary>
/// <param name="id">CommandId</param>
/// <returns>快捷键</returns>
public string GetRibbonItemShortcutsRep(string id)
{
if (this.LoadShortcuts().TryGetValue(id, out var shortcutItem))
{
return shortcutItem.ShortcutsRep;
}
return "";
}
/// <summary>
/// 获取全部命令快捷键CommandId-ObservableCollect<string>()
/// </summary>
/// <returns></returns>
public Dictionary<string, IEnumerable<string>> GetCommandShortcuts()
{
return (from e in this.Commands
select e.Value into e
group e by e.CommandId).ToDictionary((IGrouping<string, UIFrameworkServices.ShortcutItem> i) => i.Key, (IGrouping<string, UIFrameworkServices.ShortcutItem> j) => j.SelectMany((UIFrameworkServices.ShortcutItem i) => i.Shortcuts));
}
/// <summary>
/// 获取自定义快捷键被关联到的命令CommandId
/// </summary>
/// <param name="shortcut">自定义的快捷键</param>
/// <returns>CommandId</returns>
public string GetCommandShortcut(string shortcut)
{
return this.Commands.FirstOrDefault((KeyValuePair<string, UIFrameworkServices.ShortcutItem> e) => e.Value.Shortcuts.Contains(shortcut)).Key;
}
/// <summary>
/// 自定义的快捷键是否已经被关联到某个命令上
/// </summary>
/// <param name="shortcut">自定义的快捷键</param>
/// <returns></returns>
public bool HasCommandShortcut(string shortcut)
{
return this.GetCommandShortcuts().Any((KeyValuePair<string, IEnumerable<string>> e) => e.Value.Contains(shortcut));
}
/// <summary>
/// 加载命令
/// </summary>
public void LoadCommands()
{
ShortcutsHelper.LoadCommands();
}
/// <summary>
/// 应用快捷键更改
/// </summary>
public void ApplyShortcutChanges()
{
UIFrameworkServices.KeyboardShortcutService.applyShortcutChanges(this.LoadShortcuts());
}
/// <summary>
/// 应用更改
/// </summary>
private void ArmCommands()
{
ShortcutsHelper.ArmCommands();
}
/// <summary>
/// 获取已经加载的ShortcutItems
/// </summary>
/// <returns></returns>
private Dictionary<string, UIFrameworkServices.ShortcutItem> LoadShortcuts()
{
return ShortcutsHelper.LoadShortcuts();
}
/// <summary>
/// 保存ShortcutItems
/// </summary>
/// <param name="shortcutList"></param>
private void SaveShortcuts(ICollection<UIFrameworkServices.ShortcutItem> shortcutList)
{
ShortcutsHelper.SaveShortcuts(shortcutList);
}
/// <summary>
/// 所有快捷命令条目字典
/// </summary>
private Dictionary<string, UIFrameworkServices.ShortcutItem> Commands
{
get
{
return ShortcutsHelper.Commands;
}
}
/// <summary>
/// 获取所有ShortcutItems
/// </summary>
/// <returns>ShortcutItems</returns>
public IEnumerable<UIFrameworkServices.ShortcutItem> GetShortcutItems()
{
return from e in this.LoadShortcuts()
select new UIFrameworkServices.ShortcutItem(e.Value);
}
}
}

View File

@@ -1,66 +0,0 @@
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using Autodesk.Revit.DB.Plumbing;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
namespace Szmedi.Test
{
/// <summary>
/// Revit执行命令
/// </summary>
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
public class MappingSystemCmd : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//程序UI界面
UIApplication uiapp = commandData.Application;
//获取元素(选择) 显示元素 视图(活动视图)管理(对象)
UIDocument uidoc = uiapp.ActiveUIDocument;
//程序
Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
//获取位置和场地 视图(多个视图)管理 获取元素Revit 项目里的全部元素)
Document doc = uidoc.Document;
//获取所有打开文档
DocumentSet docset = uiapp.Application.Documents;
//当前视图
View view = doc.ActiveView;
var systems=new FilteredElementCollector(doc)
.OfClass(typeof(MEPSystem))
.WhereElementIsNotElementType().Cast<MEPSystem>().ToList();
using (Transaction tx = new Transaction(doc, "Mapping System Names"))
{
tx.Start();
foreach (var sys in systems)
{
var typeId = sys.GetTypeId();
var type = doc.GetElement(typeId) as MEPSystemType;
if (sys is MechanicalSystem mechanicalSystem)
{
var elementSet = mechanicalSystem.DuctNetwork;
foreach (Element elem in elementSet)
{
elem.LookupParameter("三级系统名称")?.Set(type.Name);
}
}
if (sys is PipingSystem pipingSystem)
{
var elementSet = pipingSystem.PipingNetwork;
foreach (Element elem in elementSet)
{
elem.LookupParameter("三级系统名称")?.Set(type.Name);
}
}
}
tx.Commit();
}
return Result.Succeeded;
}
}
}

View File

@@ -29,34 +29,6 @@ namespace Szmedi.Test
{
public override void Execute()
{
//UiApplication.Application.DocumentOpening+= (s, e) =>
//{
// MessageBox.Show("DocumentOpening");
//};
//UiApplication.Application.FamilyLoadingIntoDocument += (s, e) =>
//{
// MessageBox.Show("FamilyLoadingIntoDocument");
//};
//UiApplication.Application.FamilyLoadedIntoDocument += (s, e) =>
//{
// MessageBox.Show("FamilyLoadedIntoDocument");
//};
//var ptr = ComponentManager.ApplicationWindow;
var win = GetWindowFromHwnd(Process.GetCurrentProcess().MainWindowHandle);
win?.Drop += (s, e) =>
{
MessageBox.Show("Drop");
};
//UIApplication.DoDragDrop(, new DropHandler());
return;
#if false
var refer = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element);
var wall = Document.GetElement(refer);
//var point = UiDocument.Selection.PickPoint(Autodesk.Revit.UI.Selection.ObjectSnapTypes.Nearest);
#endif
Array builtInCategories = Enum.GetValues(typeof(BuiltInCategory));
StringBuilder sb = new StringBuilder();
var encoding = Encoding.GetEncoding(936);
@@ -91,318 +63,6 @@ namespace Szmedi.Test
//LogAssists.WriteTxtFile("内建类别", sb.ToString());
}
/// <summary>
/// 通过窗口句柄安全地获取WPF窗口对象
/// </summary>
/// <param name="hwnd">窗口句柄</param>
/// <returns>对应的WPF窗口对象如果找不到则返回null</returns>
public static Window GetWindowFromHwnd(IntPtr hwnd)
{
try
{
// 从句柄获取HwndSource
var hwndSource = HwndSource.FromHwnd(hwnd);
if (hwndSource == null)
{
return null;
}
// 检查RootVisual是否为Window类型
if (hwndSource.RootVisual is DependencyObject rootVisual)
{
// 通过可视化树向上查找Window
DependencyObject current = rootVisual;
while (current != null)
{
if (current is Window window)
{
return window;
}
current = VisualTreeHelper.GetParent(current);
}
}
}
catch (Exception ex)
{
// 可以在此记录异常或进行其他处理
Console.WriteLine($"获取窗口失败: {ex.Message}");
}
return null;
}
private static void MoveElement(Element item, XYZ vector)
{
if (item.Pinned || !item.IsValidObject || item == null)
{
return;
}
var doc = item.Document;
var levels = doc.OfType<Level>().OrderBy(l => l.Elevation).ToList();
if (item is FamilyInstance instance)
{
var baseElev = instance.get_BoundingBox(null).Min.Z + vector.Z;
var topElev = instance.get_BoundingBox(null).Max.Z + vector.Z;
var baseLevel = GetClosetLevel(levels, baseElev);
var topLevel = GetClosetLevel(levels, topElev);
switch (instance.Symbol.Family.FamilyPlacementType)
{
//柱
case FamilyPlacementType.TwoLevelsBased:
{
instance.Location.Move(vector);
instance.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_PARAM).Set(baseLevel.Id);
instance.get_Parameter(BuiltInParameter.FAMILY_BASE_LEVEL_OFFSET_PARAM).Set(baseElev - baseLevel.Elevation);
instance.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_PARAM).Set(topLevel.Id);
instance.get_Parameter(BuiltInParameter.FAMILY_TOP_LEVEL_OFFSET_PARAM).Set(topElev - topLevel.Elevation);
}
break;
case FamilyPlacementType.ViewBased:
break;
case FamilyPlacementType.WorkPlaneBased:
{
//instance.Location.Move(vector);
var param = instance.get_Parameter(BuiltInParameter.INSTANCE_ELEVATION_PARAM);
if (param != null && !param.IsReadOnly)
{
ElementTransformUtils.MoveElement(doc, instance.Id, XYZ.BasisZ * vector.Z);
//var value = param.AsDouble() + vector.Z;
//param.Set(value);
}
doc.Regenerate();
if (instance.Host == null)
{
ElementTransformUtils.MoveElement(doc, instance.Id, vector.Flatten());
}
}
break;
case FamilyPlacementType.OneLevelBased:
{
ElementTransformUtils.MoveElement(doc, instance.Id, vector);
}
break;
case FamilyPlacementType.OneLevelBasedHosted:
{
var ins = doc.Create
.NewFamilyInstance(
instance.GetLocXYZ(),
instance.Symbol,
instance.Host,
baseLevel,
StructuralType.NonStructural);
ins.get_Parameter(BuiltInParameter.INSTANCE_SILL_HEIGHT_PARAM)
.Set(baseElev - baseLevel.Elevation);
doc.Delete(instance.Id);
}
break;
case FamilyPlacementType.CurveBased:
{
instance.Location.Move(vector);
//var loc = instance.GetLocCurve().CreateTransformed(Transform.CreateTranslation(vector)) as Line;
//doc.Create.NewFamilyInstance(instance.HostFace, loc, instance.Symbol);
//doc.Delete(instance.Id);
}
break;
case FamilyPlacementType.CurveBasedDetail:
break;
//梁
case FamilyPlacementType.CurveDrivenStructural:
{
//instance.Location.Move(vector);
var loc = instance.GetLocCurve().CreateTransformed(Autodesk.Revit.DB.Transform.CreateTranslation(vector));
//var newLoc =
doc.Create.NewFamilyInstance(loc, instance.Symbol, baseLevel, StructuralType.Beam);
doc.Delete(instance.Id);
//ElementTransformUtils.MoveElement(doc, instance.Id, vector.Flatten());
}
break;
case FamilyPlacementType.Adaptive:
break;
case FamilyPlacementType.Invalid:
break;
default:
break;
}
}
else if (item is Wall wall)
{
var flat = vector.Flatten();
ElementTransformUtils.MoveElement(doc, wall.Id, flat);
doc.Regenerate();
SetWallHeight(vector, levels, wall);
var elemIds = wall.FindInserts(true, true, true, true);
foreach (var id in elemIds)
{
var elem = doc.GetElement(id);
if (elem is Wall nestWall)
{
SetWallHeight(vector, levels, nestWall);
}
else
{
MoveElement(elem, vector);
}
}
//墙饰条
var sweeps = doc.OfType<WallSweep>().Where(s => s.GetHostIds().Contains(wall.Id));
foreach (var sweep in sweeps)
{
//ElementTransformUtils.MoveElement(doc, sweep.Id, flat);
var boundingBox = sweep.get_BoundingBox(null);
var offset = boundingBox.Min.Z + vector.Z;
var baseLevel = levels.First(l => Math.Abs(l.Elevation - offset) == levels.Min(l => Math.Abs(l.Elevation - offset)));
sweep.get_Parameter(BuiltInParameter.WALL_SWEEP_LEVEL_PARAM).Set(baseLevel.Id);
sweep.get_Parameter(BuiltInParameter.WALL_SWEEP_OFFSET_PARAM).Set(offset - baseLevel.Elevation);
}
//依附在墙上的实例
var instances = doc.OfType<FamilyInstance>().Where(s =>
{
var stackedWallIds = wall.GetStackedWallMemberIds();
if (stackedWallIds.Count > 0)
{
return stackedWallIds.Any(id => s.Host?.Id == id);
}
return s.Host?.Id == wall.Id;
}).ToList();
foreach (var ins in instances)
{
//ins.Location.Move(vector);
ElementTransformUtils.MoveElement(doc, ins.Id, vector.Z * XYZ.BasisZ);
}
//ElementTransformUtils.CopyElements(v1, [wall.Id], v2, Transform.Identity, new CopyPasteOptions());
//var b = wall.Location.Move(vector);
//var loc = wall.GetLocCurve();
//var c = loc.CreateTransformed(Transform.CreateTranslation(vector));
//(wall.Location as LocationCurve).Curve = c; //可以移动,但是偏移量属性没更新
}
else if (item is Room room)
{
}
else if (item is Stairs stairs)
{
var flat = vector.Flatten();
ElementTransformUtils.MoveElement(doc, item.Id, flat);
}
else if (item is ModelCurve curve)//房间分割
{
}
else if (item is Railing railing)
{
if (railing.HasHost)
{
}
else//连带主体移动
{
ElementTransformUtils.MoveElement(doc, item.Id, vector);
}
}
//坡道
else if (item.Category.ToBuiltInCategory() == BuiltInCategory.OST_Ramps)
{
var flat = vector.Flatten();
ElementTransformUtils.MoveElement(doc, item.Id, flat);
}
else if (item is TopographySurface surface)
{
}
else if (item is SlabEdge or Fascia or Gutter)
{
return;
}
//else if (item is CeilingAndFloor ceilingAndFloor)
//{
// ElementTransformUtils.MoveElement(doc, ceilingAndFloor.Id, vector);
//}
else if (item is Opening opening)
{
if (opening.Host is Wall)
{
ElementTransformUtils.MoveElement(doc, opening.Id, vector.Z * XYZ.BasisZ);
}
}
else
{
ElementTransformUtils.MoveElement(doc, item.Id, vector);
}
}
/// <summary>
/// 设置墙高度
/// </summary>
/// <param name="vector"></param>
/// <param name="levels"></param>
/// <param name="wall"></param>
private static void SetWallHeight(XYZ vector, List<Level> levels, Wall wall)
{
var boundingBox = wall.get_BoundingBox(null);
//底部实际高度
var bottomElev = boundingBox.Min.Z + vector.Z;
var topElev = boundingBox.Max.Z + vector.Z;
//查找距离底部偏移最近的Level
var baseLevel = GetClosetLevel(levels, bottomElev);
wall.get_Parameter(BuiltInParameter.WALL_BASE_CONSTRAINT).Set(baseLevel.Id);
wall.get_Parameter(BuiltInParameter.WALL_BASE_OFFSET).Set(bottomElev - baseLevel.Elevation);
//是否有顶部约束
var topContraint = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).AsElementId();
if (topContraint != ElementId.InvalidElementId)
{
var topLevel = GetClosetLevel(levels, topElev);
wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE).Set(topLevel.Id);
wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(topElev - topLevel.Elevation);
}
}
/// <summary>
/// 获取距离最近的标高
/// </summary>
/// <param name="levels"></param>
/// <param name="elev">高度</param>
/// <returns></returns>
private static Level GetClosetLevel(List<Level> levels, double elev)
{
return levels.First(l => Math.Abs(l.Elevation - elev) == levels.Min(l => Math.Abs(l.Elevation - elev)));
}
private void Application_DocumentChanged(object sender, Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
UiApplication.Application.DocumentChanged -= Application_DocumentChanged;
var sb = new StringBuilder();
var doc = e.GetDocument();
sb.AppendLine("删除的元素ID");
e.GetDeletedElementIds().ToList().ForEach(id => sb.AppendLine(id.IntegerValue.ToString()));
//Debug.WriteLine(e.GetDeletedElementIds().Count);
sb.AppendLine("修改的元素");
e.GetModifiedElementIds().Select(Document.GetElement).Where(e => e != null && e.Category != null && e.Category.CategoryType == CategoryType.Model && e.get_BoundingBox(null) != null).ToList().ForEach(e =>
{
sb.AppendLine(e?.Category.Name + "---------" + e?.Name);
});
File.WriteAllText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "修改的元素.txt"), sb.ToString());
}
}
public class DropHandler : IControllableDropHandler
{
public bool CanExecute(UIDocument document, object data, ElementId dropViewId)
{
throw new NotImplementedException();
}
public void Execute(UIDocument document, object data)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Szmedi.RvKits.Mvvm.Attributes
{
/// <summary>
/// 多个快捷键使用#分割
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
class ShortcutRepAttribute : Attribute
{
public ShortcutRepAttribute(string shortcuts)
{
Shortcuts = shortcuts;
}
public string Shortcuts { get; }
}
}

View File

@@ -12,255 +12,14 @@ using EPPlus.Core.Extensions;
using Nice3point.Revit.Toolkit.External;
using Nice3point.Revit.Toolkit.Utils;
using OfficeOpenXml;
using RevitAddins;
using Szmedi.RvKits.Mvvm.Attributes;
namespace Szmedi.Test
{
[Transaction(TransactionMode.Manual)]
[Regeneration(RegenerationOption.Manual)]
[ShortcutRep("TEST")]
public class TestCmd : ExternalCommand
{
//一个判断两条模型线是否平行的方法
/// <summary>
/// 扩展包围盒的大小
/// </summary>
/// <param name="boundingBox"></param>
/// <param name="distance"></param>
/// <returns></returns>
public static BoundingBoxXYZ Extend(BoundingBoxXYZ boundingBox, double distance = 2.0)
{
var transform = boundingBox.Transform;
var max = boundingBox.Max + (distance * (transform.BasisX + transform.BasisY + transform.BasisZ));
var min = boundingBox.Min - (distance * (transform.BasisX + transform.BasisY + transform.BasisZ));
return new BoundingBoxXYZ { Max = max, Min = min, Transform = transform };
}
// 合法化 Sheet 名称
private string MakeValidSheetName(string name)
{
var invalid = new[] { '\\', '/', '*', '[', ']', ':', '?' };
foreach (var ch in invalid)
{
name = name.Replace(ch, '_');
}
return name.Length > 31 ? name.Substring(0, 31) : name;
}
private void NewMethod(IEnumerable<IGrouping<string, ElementModel>> groupedModels)
{
using (var pkg = new ExcelPackage())
{
//深圳构件标识
foreach (var g in groupedModels)
{
var sheetName = MakeValidSheetName(g.Key);
var ws = pkg.Workbook.Worksheets.Add(sheetName);
//第一行
ws.Cells[1, 1].Value = g.Key;
var columns = g.Sum(m => m.Count);
//合并单元格
ws.Cells[1, 1, 1, columns].Merge = true;
ws.Cells[1, 1].Style.HorizontalAlignment = OfficeOpenXml.Style.ExcelHorizontalAlignment.Center;
ws.Cells[1, 1].Style.VerticalAlignment = OfficeOpenXml.Style.ExcelVerticalAlignment.Center;
ws.Cells[1, 1].Style.Font.Size = 20;
ws.Cells[1, 1].Style.Fill.BackgroundColor.SetColor(System.Drawing.Color.FromArgb(255, 255, 0));
ws.Cells[1, 1].Style.Border.BorderAround(OfficeOpenXml.Style.ExcelBorderStyle.Thin);
var currentColumns = 1;
var models = g.ToList();
foreach (var m in models)
{
var subColumns = m.Count;
//第二行
ws.Cells[2, currentColumns, 2, subColumns].Merge = true;
//合并单元格
ws.Cells[2, currentColumns].Value = models.FirstOrDefault().Groups.Keys;
currentColumns += 1 + subColumns;
}
}
}
}
private static bool IsParallel(Line line1, Line line2)
{
return line1.Direction.IsAlmostEqualTo(line2.Direction) ||
line1.Direction.IsAlmostEqualTo(-line2.Direction);
}
//private bool IsConnectToSystem(FamilyInstance ins)
//{
// bool hasConnectToSystem = false;
// if (ins.Category.CategoryType != CategoryType.FacilityNumber)
// {
// return true;
// }
// if (ins.get_BoundingBox(null) != null &&
// ins.MEPModel?.ConnectorManager != null)
// {
// var cons = ins.GetConnectors(false);
// foreach (Connector con in cons)
// {
// if (con.Domain != Domain.DomainElectrical && con.Domain != Domain.DomainUndefined)
// {
// try
// {
// if (con.IsConnected)
// {
// hasConnectToSystem = true;
// }
// }
// catch (Exception)
// {
// }
// }
// }
// }
// return hasConnectToSystem;
//}
public List<Family> GetUsedFamilies(Document doc)
{
// 获取所有实例元素这里不需要由ToElements()转化全部对象,节省内存,仅遍历)
var instances = doc.OfClass<FamilyInstance>();
// 3. 收集所有被使用的“类型ID” (使用HashSet自动去重)
HashSet<ElementId> usedTypeIds = new HashSet<ElementId>();
foreach (Element elem in instances)
{
ElementId typeId = elem.GetTypeId();
if (typeId != ElementId.InvalidElementId)
{
usedTypeIds.Add(typeId);
}
}
// 4. 从类型反向获取族 (使用HashSet再次去重族ID防止多个类型属于同一个族)
HashSet<ElementId> usedFamilyIds = new HashSet<ElementId>();
List<Family> resultFamilies = new List<Family>();
foreach (ElementId typeId in usedTypeIds)
{
// 获取类型元素 (FamilySymbol)
Element typeElem = doc.GetElement(typeId);
FamilySymbol symbol = typeElem as FamilySymbol;
if (symbol != null)
{
Family family = symbol.Family;
// 确保族存在且未被添加过
if (family != null && !usedFamilyIds.Contains(family.Id))
{
usedFamilyIds.Add(family.Id);
resultFamilies.Add(family);
}
}
}
return resultFamilies;
}
public static void ToExcel(string excelPath, List<TestExcel> infos)
{
FileInfo fi = new(excelPath);
ExcelPackage package = infos.ToWorksheet("构件数量变动对比表")
.WithConfiguration(cfg => cfg.WithColumnConfiguration(c => c.AutoFit()))
.ToExcelPackage();
package.SaveAs(fi);
}
public override void Execute()
{
//var parameter = Document.FamilyManager.GetParameters().FirstOrDefault(p => p.Definition.Name == "额定电流");
//Document.Invoke(
// ts =>
// {
// Document.FamilyManager.ReplaceParameter(parameter, "测试", BuiltInParameterGroup.PG_TEXT, true);
// });
var docs = Application.Documents;
if (docs.Size != 2)
{
return;
}
var doc1 = docs.OfType<Document>().First();
var doc2 = docs.OfType<Document>().Last();
var elements1 = doc1.OfCollector().WhereElementIsNotElementType().Where(e => e.Category?.CategoryType == CategoryType.Model && e.get_BoundingBox(null) != null);
var elements2 = doc2.OfCollector().WhereElementIsNotElementType().Where(e => e.Category?.CategoryType == CategoryType.Model && e.get_BoundingBox(null) != null);
StringBuilder stringBuilder = new StringBuilder();
foreach (var e1 in elements1)
{
foreach (var e2 in elements2)
{
if (e1.Id.IntegerValue == e2.Id.IntegerValue)
{
stringBuilder.Append($"重复图元:{e1.Id};");
}
}
}
MessageBox.Show(stringBuilder.ToString());
return;
var doc = Document;
if (doc.IsFamilyDocument)
{
return;
}
var familyCollector = new FilteredElementCollector(doc)
.OfClass(typeof(Family))
.Cast<Family>()
.ToList();
MessageBox.Show(familyCollector.Count().ToString());
var fams = GetUsedFamilies(doc)
.Cast<Family>()
.Where(
fam => fam.FamilyCategory.AllowsBoundParameters &&
fam.IsEditable &&
fam.FamilyCategory.CategoryType == CategoryType.Model &&
!Enum.GetName(typeof(BuiltInCategory), fam.FamilyCategoryId.IntegerValue)!.Contains(
"Fitting") &&
fam.FamilyCategoryId.IntegerValue != -2008013).ToList();//风道末端
MessageBox.Show(fams.Count().ToString());
return;
var elements = new FilteredElementCollector(Document).WhereElementIsNotElementType()
.ToElements()
.ToList();
StringBuilder sb = new StringBuilder();
var elementIds = new List<ElementId>();
foreach (var item in elements)
{
var level = Document.GetElement(item.GetLevelId())?.Name;
var param = item.get_Parameter(BuiltInParameter.DOOR_NUMBER)?.AsString();
if (param != null && level != null && param.Contains("B2") && level.Contains("站厅"))
{
sb.AppendLine($"{item.Id}");
elementIds.Add(item.Id);
}
if (param != null && level != null && param.Contains("B1") && level.Contains("站台"))
{
sb.AppendLine($"{item.Id}");
elementIds.Add(item.Id);
}
}
if (sb.Length > 0)
{
UiDocument.Selection.SetElementIds(elementIds);
}
return;
var walls = new FilteredElementCollector(Document).WhereElementIsNotElementType()
.OfClass(typeof(Wall))
.ToElements()
@@ -310,372 +69,6 @@ namespace Szmedi.Test
}
}
}
//var elements = UiDocument.Selection
// .GetElementIds()
// .Select(Document.GetElement)
// .Cast<FamilyInstance>()
// .Where(e => e.get_Parameter(BuiltInParameter.INSTANCE_ELEVATION_PARAM).AsDouble() < 10e-5)
// .Select(e => e.Id).ToList();
//UiDocument.Selection.SetElementIds(elements);
return;
//Document.Invoke(ts =>
//{
// elements.ForEach(e=>e.Host)
//})
//Material.Create(Document, "测试材质");
//var reference = UiDocument.Selection.PickObject(ObjectType.Element);
//var ins = Document.GetElement(reference) as FamilyInstance;
//var bo = IsConnectToSystem(ins);
//MessageBox.Show(bo.ToString());
//return;
#if false
var manager = Document.FamilyManager;
var names = manager.Types.OfType<FamilyType>().Select(t => t.Name);
bool b = names.Contains(" ");
MessageBox.Show(manager.Types.Size.ToString());
return;
var e = UiDocument.Selection.PickElementsByRectangle().Cast<MEPCurve>().FirstOrDefault();
//var b1 = e.Diameter == null;
//var b2 = e.Height == null;
//MessageBox.Show(.ToString());
return;
var refer1 = UiDocument.Selection.PickObject(ObjectType.Element, new GenericFilter<ModelCurve>(), "选择模型线");
var elem = Document.GetElement(refer1) as ModelCurve;
var col = new FilteredElementCollector(Document, Document.ActiveView.Id).WhereElementIsNotElementType()
.Where(e => e is ModelCurve mc && mc.LineStyle.Id == elem.LineStyle.Id).Select(e => e.Id).ToList();
UiDocument.Selection.SetElementIds(col);
//var refer2 = UiDocument.Selection.PickObject(ObjectType.Element, new GenericFilter<ModelCurve>(), "选择模型线");
//var elem2 = Document.GetElement(refer2) as ModelCurve;
//if (IsParallel(elem.GeometryCurve as Line, elem2.GeometryCurve as Line))
//{
// MessageBox.Show("平行");
//}
//var groups = Document.OfParentModelCollector().WhereElementIsNotElementType()
// .GroupBy(e => e.GetParameters("深圳构件标识").FirstOrDefault().AsString());
//StringBuilder sb = new StringBuilder();
//foreach (var element in groups)
//{
// sb.AppendLine(element.Key);
//}
//MessageBox.Show(sb.ToString());
//var reference = UiDocument.Selection.PickObject(ObjectType.PointOnElement, new GenericFilter<ImportInstance>(), "请选择CAD文字");
//var instance = doc.GetElement(reference) as ImportInstance;
//var ids = UiDocument.Selection.GetElementIds();
//Document.Modify(
// settings => settings.Transaction).Commit((doc, _) =>
// {
//var familyName = "族名称";
//using (Transaction ts = new Transaction(doc, "更新族类型名称"))
//{
// ts.Start();
// //获取某个族
// var element = new FilteredElementCollector(doc).OfClass(typeof(Family)).Where(e => e.Name == familyName).Cast<Family>().FirstOrDefault();
// if (element == null)
// {
// TaskDialog.Show("提示", "没有找到指定的族");
// ts.RollBack();
// return;
// }
// var ids = element.GetFamilySymbolIds();
// foreach (var id in ids)
// {
// //获取族类型
// var symbol = doc.GetElement(id) as FamilySymbol;
// //宽度
// var w = Convert.ToDouble(symbol.GetParameters("宽度").FirstOrDefault().AsValueString());
// //高度
// var h = Convert.ToDouble(symbol.GetParameters("高度").FirstOrDefault().AsValueString());
// if (ids != null)
// {
// //前缀
// var prefix = string.Empty;
// //后缀
// var suffix = string.Empty;
// string width = string.Empty;
// string height = string.Empty;
// if (w / 1000 < 1)
// {
// width = $"0{w}mm";
// }
// if (h / 1000 < 1)
// {
// height = $"0{h}mm";
// }
// //如果width和height最后一个数字是5则把5改成a
// if (width.Last() == '5')
// {
// width = width.Remove(width.Length - 1) + "a";
// }
// if (height.Last() == '5')
// {
// height = height.Remove(height.Length - 1) + "a";
// }
// var newName = $"{prefix}{width} x {height}{suffix}";
// symbol.Name = newName;
// }
// }
// ts.Commit();
//}
// });
//var ids = Document.ActiveView
// .OfCollector()
// .Where(e => e.get_Parameter(BuiltInParameter.DOOR_NUMBER)?.AsString() == "J4921321-11332-(005)DK0001").Select(e => e.Id).ToList();
//MessageBox.Show(ids.Count.ToString());
//UiDocument.Selection.SetElementIds(ids);
//var elemIds = GetElementIds();
//var ids = UiDocument.Selection.GetElementIds();
//var start = new XYZ(-61995.832703373, 4243.667605320, 5999.999999997);
//var end = new XYZ(0.000000000, 0.000000000, 5999.999999997);
//var translate = (end - start) / 304.8;
//using (Transaction ts = new Transaction(doc, "移动"))
//{
// ts.Start();
// ElementTransformUtils.MoveElements(doc, ids, translate);
// ts.Commit();
//}
//var id = UiDocument.Selection.GetElementIds().FirstOrDefault();
//var ids = doc.GetElement(id) as FamilyInstance;
//var n = ids.GetSubComponentIds().Count;
//MessageBox.Show(n.ToString());
//var ids = UiDocument.Selection.GetElementIds();
//var translate = XYZ.BasisZ * 20;
//using (Transaction ts = new Transaction(doc, "移动"))
//{
// ts.Start();
// ElementTransformUtils.CopyElements(doc, ids, translate);
// ElementTransformUtils.MoveElements(doc, ids, translate);
// ts.Commit();
//}
#endif
#if false
var symbols = new FilteredElementCollector(Document).OfClass(typeof(Wall)).ToElementIds();
foreach (var item in symbols)
{
var elem = Document.GetElement(item);
var cat = elem.Category.Name;
var cate = Category.GetCategory(Document, elem.Category.Id);
var cate1 = Category.GetCategory(Document, (BuiltInCategory)elem.Category.Id.IntegerValue);
}
var dict = new FilteredElementCollector(Document).OfClass(typeof(ElementType))
.OfType<ElementType>()
.Where(
t => t.CanBeRenamed && t.Category is { CategoryType: CategoryType.Model or CategoryType.Annotation })
.GroupBy(t => t.Category.Id)
.Where(g => !string.IsNullOrEmpty(g.FirstOrDefault().Category?.Name))
.ToDictionary(g => g.FirstOrDefault().Category.Name, g => g.ToList());
//UiDocument.Selection.SetElementIds(symbols);
StringBuilder sb = new StringBuilder();
foreach (var kv in dict)
{
foreach (var item in kv.Value)
{
if (item is WallType t)
{
sb.AppendLine($"{kv.Key}-{t.Kind}-{item.Name}");
}
}
}
MessageBox.Show(sb.ToString());
#endif
//foreach (BuiltInCategory id in Enum.GetValues(typeof(BuiltInCategory)))
//{
// var names = Enum.GetName(typeof(BuiltInCategory), id);
//}
//using (Transaction ts = new Transaction(doc, "修改类型名称"))
//{
// ts.Start();
// try
// {
// foreach (var id in symbols)
// {
// if (id.Name == "标准")
// {
// if (id is FamilySymbol ids)
// {
// id.Name = ids.FamilyName;
// }
// }
// }
// }
// catch (Exception)
// {
// throw;
// }
// ts.Commit();
//}
//UiDocument.Selection.SetElementIds([new ElementId(2222596)]);
//var dims = doc.QueryElementsByType<Dimension>().WhereElementIsNotElementType().ToElementIds();
//UiDocument.Selection.SetElementIds(dims);
//var refer = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element);
//var ids = doc.GetElement(refer);
//foreach (Parameter param in ids.Parameters)
//{
//}
#region MyRegion
////var service = new ricaun.Revit.ExtensibleShortcuts.Services.KeyboardShortcutService();
////service.SetRibbonItemShortcut("CustomCtrl_%CustomCtrl_%SZMEDI工具%机电工具%MoveConnectCmd", "MC");
//var dict = ShortcutsHelper.Commands;
//StringBuilder sb = new StringBuilder();
//sb.AppendLine("Name,CommandName,CommandId,CommandPaths,CommandScope,Shortcuts,ShortcutType,ShortcutsRep,");
//foreach (var id in dict)
//{
// string text = id.Value.CommandName;
// string commandName = string.Join("", text.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries));
// sb.AppendLine(
// $"{id.Key},{commandName},{id.Value.CommandId},{id.Value.CommandPaths},{id.Value.CommandScope},{id.Value.Shortcuts},{id.Value.ShortcutType},{id.Value.ShortcutsRep},");
// //if (ShortcutsHelper.Commands.TryGetValue("CustomCtrl_%CustomCtrl_%SZMEDI工具%机电工具%MoveConnectCmd", out var shortcutItem))
// //{
// // //为命令指定快捷键QQQQ
// // shortcutItem.Shortcuts.Add("MC");
// // break;
// // //为命令删除快捷键QQQQ
// // //shortcutItem.Shortcuts.Remove("QQQQ");
// // //为命令清空快捷键
// // //shortcutItem.Shortcuts.Clear();
// //}
//}
//LogUtil.WriteTxtFile("快捷键", sb.ToString());
////KeyboardShortcutService.applyShortcutChanges(dict);
#endregion
//var window = new MainWindow() { DataContext = new MainWindowViewModel(UiDocument) };
//window.Show();
//var refer = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element);
//var ids = doc.GetElement(refer);
//var boundingbox = ids.get_BoundingBox(null);
//using var ts = new Transaction(doc, "Test");
//ts.Start();
//if (doc.ActiveView is View3D view3D)
//{
// view3D.SetSectionBox(Extend(boundingbox));
//}
//ts.Commit();
//var ids = UiDocument.Selection.GetElementIds();
//using (var ts = new Transaction(doc, "Test"))
//{
// ts.Start();
// foreach (var id in ids)
// {
// var ids = doc.GetElement(id) as FamilyInstance;
// //ElementTransformUtils.RotateElement(doc, id, Line.CreateUnbound((ids.Location as LocationPoint).Point, XYZ.BasisZ), /*65.43 / 180 * */Math.PI);
// ElementTransformUtils.MoveElement(doc, id, ids.HandOrientation * 1200 / 304.8);
// doc.Regenerate();
// //ElementTransformUtils.RotateElement(doc, id, Line.CreateUnbound((ids.Location as LocationPoint).Point, XYZ.BasisZ), /*65.43 / 180 * */Math.PI);
// }
// ts.Commit();
//}
#region Updater
//ChangeTypeUpdater _updater = new(UiApplication, new Guid("{A3441B77-7558-41EF-A3BF-4690C99F67C0}"));
//UpdaterRegistry.RegisterUpdater(_updater, true);
//UpdaterRegistry.AddTrigger(_updater.GetUpdaterId(), new ElementClassFilter(typeof(FamilyInstance)), Element.GetChangeTypeAny());
#endregion
//var ids = UiDocument.Selection.GetElementIds().Select(id=>doc.GetElement(id)).OfType<ModelCurve>().Select(mc=>mc.GeometryCurve).ToList();
//SetComparisonResult comparisonR = ids[0].Intersect(ids[1], out var intersectionResult);
//System.Windows.MessageBox.Show($"{comparisonR}");
//try
//{
// var cl = CurveLoop.Create(ids);
//}
//catch (Exception)
//{
// throw;
//}
//Application.DocumentChanged -= Application_DocumentChanged;
//Application.DocumentChanged += Application_DocumentChanged;
#region MyRegion
var refer = UiDocument.Selection.PickObject(ObjectType.Element);
var ids = (Document.GetElement(refer) as FamilyInstance).Symbol;
Document.Modify(set => set.Transaction)
.Commit((d, _) =>
{
if (!ids.IsActive)
{
ids.Activate();
}
var level = new FilteredElementCollector(d).OfClass(typeof(Level))
.OfType<Level>()
.FirstOrDefault(l => l.Elevation > 0);
d.Create
.NewFamilyInstance(
new XYZ(0, 0, 0),
ids,
level,
Autodesk.Revit.DB.Structure.StructuralType.NonStructural);
});
#endregion
//XYZ p1 = new XYZ();
//XYZ p2 = XYZ.BasisX;
//XYZ p3 = XYZ.BasisY;
//XYZ p4 = XYZ.BasisZ;
//var list = new List<XYZ>() { p1, p2, p3 };
//HermiteSpline curve = HermiteSpline.Create(list, false);
//using (Transaction ts = new Transaction(doc, "test"))
//{
// ts.Start();
// SketchPlane sketch = SketchPlane.Create(doc, Plane.CreateByNormalAndOrigin(p4, p1));
// var mc = doc.Create.NewModelCurve(curve, sketch);
// ts.Commit();
//}
//Autodesk.Revit.DB.Reference e = UiDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element);
}
private static void MoveRoomSeparator(Document doc, ICollection<ElementId> ids, double dis)
{
var distance = dis / 304.8;
foreach (var id in ids)
{
var element = doc.GetElement(id);
if (element is ModelCurve modelCurve)
{
var start = modelCurve.GeometryCurve.GetEndPoint(0);
var end = modelCurve.GeometryCurve.GetEndPoint(1);
var sketch = SketchPlane.Create(doc,
Plane.CreateByNormalAndOrigin(XYZ.BasisZ, start + (XYZ.BasisZ * distance)));
var line = Line.CreateBound(start + (XYZ.BasisZ * distance), end + (XYZ.BasisZ * distance));
var array = new CurveArray();
array.Append(line);
doc.Create.NewRoomBoundaryLines(sketch, array, doc.ActiveView);
}
}
doc.Delete(ids);
}
@@ -732,98 +125,5 @@ namespace Szmedi.Test
}
}
}
public class SelectionFilter : ISelectionFilter
{
public bool AllowElement(Element elem)
{
return elem is Wall;
}
public bool AllowReference(Reference reference, XYZ position)
{
return true;
}
}
public class ChangeTypeUpdater : IUpdater
{
private UpdaterId _uid;
public ChangeTypeUpdater(UIApplication uiapp, Guid guid)
{
_uid = new UpdaterId(uiapp.ActiveAddInId,
guid);
}
public void Execute(UpdaterData data)
{
Func<ICollection<ElementId>, string> toString = ids =>
ids.Aggregate(string.Empty, (ss, id) => ss + "," + id).TrimStart(',');
StringBuilder sb = new();
sb.AppendLine("added:" + toString(data.GetAddedElementIds()));
sb.AppendLine("modified:" + toString(data.GetModifiedElementIds()));
sb.AppendLine("deleted:" + toString(data.GetDeletedElementIds()));
TaskDialog.Show("Changes", sb.ToString());
}
public string GetAdditionalInformation()
{
return "N/A";
}
public ChangePriority GetChangePriority()
{
return ChangePriority.FreeStandingComponents;
}
public UpdaterId GetUpdaterId()
{
return _uid;
}
public string GetUpdaterName()
{
return "ChangeTypeUpdater";
}
}
private void Application_DocumentChanged(object sender, Autodesk.Revit.DB.Events.DocumentChangedEventArgs e)
{
var a = sender as Autodesk.Revit.ApplicationServices.Application;
var doc = e.GetDocument();
ElementClassFilter filter = new(typeof(FamilyInstance));
var modifyIds = e.GetModifiedElementIds(filter);
var addIds = e.GetAddedElementIds(filter);
var deleteIds = e.GetDeletedElementIds();
StringBuilder sb = new();
if (modifyIds.Count > 0)
{
sb.AppendLine("修改");
foreach (var id in modifyIds)
{
sb.AppendLine($"{id}-{doc.GetElement(id).Name}");
}
}
if (addIds.Count > 0)
{
sb.AppendLine("添加");
foreach (var id in addIds)
{
sb.AppendLine($"{id}-{doc.GetElement(id).Name}");
}
}
if (deleteIds.Count > 0)
{
sb.AppendLine("删除");
foreach (var id in deleteIds)
{
sb.AppendLine($"{id}-{doc.GetElement(id).Name}");
}
}
MessageBox.Show(sb.ToString(), "文档修改");
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EPPlus.Core.Extensions.Attributes;
namespace Szmedi.Test
{
public class TestExcel
{
[ExcelTableColumn("变更前")]
public Test Old { get; set; }
[ExcelTableColumn("变更后")]
public Test New { get; set; }
}
public class Test
{
public Test(string iD, string name, string description)
{
ID = iD;
Name = name;
Description = description;
}
[ExcelTableColumn("ID")]
public string ID { get; set; }
[ExcelTableColumn("名称")]
public string Name { get; set; }
[ExcelTableColumn("描述")]
public string Description { get; set; }
}
}