RevitAPI:关于Curve.Distance(XYZ)方法

本文探讨了在使用线性几何计算时,遇到点到直线距离计算不准确的问题,并通过实例展示了如何从线段转换为无限直线来获取正确结果。

今天有客户问道Line.Distance(XYZ)方法有问题,返回的点到直线的距离不正确。

例如下面的图中,点到直线的距离应该是10,但是结果返回的却是14.142135623731



他的代码是这样:

var p1 = new XYZ(0, 0, 0);
var p2 = new XYZ(10, 0, 0);
var p3 = new XYZ(20, 10, 0);
Line line = Line.CreateBound(p1, p2);
StringBuilder sb = new StringBuilder();
sb.AppendLine("p3 to Line: " + line.Distance(p3));
TaskDialog.Show("distance", sb.ToString());

确实运行的结果是:

  p3 to Line: 14.142135623731


为什么不是10呢?

答案:点到直线的距离确实是10,但是这里的Line是Bound Line,也就是线段,而这里Distance()返回的距离是点到线段的距离,也就是14.14,如下图所示:



要得到正确的结果我们可以调用Line.MakeUnbound(),先把线变成Unbound,再求结果,代码如下:

    var p1 = new XYZ(0, 0, 0);
    var p2 = new XYZ(10, 0, 0);
    var p3 = new XYZ(20, 10, 0);
    Line line = Line.CreateBound(p1, p2);
    line.MakeUnbound();
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("p3 to Line: " + line.Distance(p3));
    TaskDialog.Show("distance", sb.ToString());

如此就能返回正确的结果了。


import clr clr.AddReference('RevitAPI') from Autodesk.Revit.DB import XYZ, Line, Element, FamilyInstance, Structure, Plane, BuiltInParameter from Autodesk.Revit.DB.Structure import StructuralType clr.AddReference('RevitServices') from RevitServices.Persistence import DocumentManager from RevitServices.Transactions import TransactionManager # 导入Revit API中的MessageBox类 clr.AddReference('System.Windows.Forms') from System.Windows.Forms import MessageBox # 获取当前文档实例 doc = DocumentManager.Instance.CurrentDBDocument class BeamData: def __init__(self, levelName, typename, elename, location): self.levelName = levelName self.typename = typename self.elename = elename self.location = location def calculate_distance(point1, point2): """ 计算两点之距离 Args: point1: 第一个点的坐标,格式为 (x, y, z) point2: 第二个点的坐标,格式为 (x, y, z) Returns: distance: 两点之距离 """ distance = ((point2.X - point1.X ) ** 2 + (point2.Y- point1.Y) ** 2 + (point2.Z - point1.Z) ** 2)** 0.5 return distance beams = UnwrapElement(IN[0]) dit = IN[1] beaminfos = [] beam_names = [] for beam in beams: curve = beam.Location.Curve # 获取梁的中点 baempoint = curve.Evaluate(0.5,True) dis= 999999999999 newkey = None newvalue = None for key, value in dit.items(): for point in value: newpointcalu =XYZ(point.X / 304.8,point.Y / 304.8,0) nowdis = calculate_distance(newpointcalu,baempoint) if nowdis < dis: # 刷新最近距离值 dis = nowdis newkey = key newvalue = value # 验证刷新过数据 if newkey != None: #MessageBox.Show(str(newkey)+"-"+str(dis*304.8)) level_id = beam.get_Parameter(BuiltInParameter.INSTANCE_REFERENCE_LEVEL_PARAM).AsElementId() level = doc.GetElement(level_id) level_name = level.Name #level_name = "F1" family_name = beam.Symbol.Family.Name symbol_name = beam.Name #if symbol_name not in beam_names: beam_names.append(symbol_name) beaminfos.append(BeamData(level_name, family_name, symbol_name, newkey)) data_list = [] for beaminfo in beaminfos: data_list.append([beaminfo.levelName, beaminfo.typename, beaminfo.elename, "'" + str(beaminfo.location)]) # 创建表头 header = ["楼层", "构件类型", "构件名称", "定位"] OUT = [header] + data_list怎么样获取板的
最新发布
09-30
using System; using System.Collections.Generic; using System.IO; using System.Linq; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using Autodesk.Revit.Attributes; using Document = Autodesk.Revit.DB.Document; namespace SmartDimensionCommandNS { [Transaction(TransactionMode.Manual)] public class SmartDimensionCommand : IExternalCommand { private Logger _logger = new Logger(); public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiApp = commandData.Application; UIDocument uiDoc = uiApp.ActiveUIDocument; Document doc = uiDoc.Document; View activeView = uiDoc.ActiveView; var config = new PluginConfig { MinLineLength = 0.1, AngleTolerance = 0.001745, BaseDimensionOffset = 0.3, LogFilePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "RevitDimensioningLog.txt"), MaxOffsetMultiplier = 3.0, OffsetIncrement = 0.1, BoundingBoxPadding = 0.2, DimensionGroupSpacing = 0.3, UseAdaptiveOffset = true, UseGrouping = true, GroupByDirection = true, GroupByType = true, UseParallelProcessing = true, CacheGeometryResults = true, SectionViewDepthExtension = 5.0, DimensionAlignment = DimensionAlignment.Above, MinimumDimensionGap = 0.1, MaxThreadCount = Environment.ProcessorCount }; try { if (!(activeView is ViewPlan || activeView is ViewSection || activeView.ViewType == ViewType.Elevation)) { throw new System.InvalidOperationException("不支持的视图类型,无法创建。"); } } catch (System.InvalidOperationException ex) { _logger.Log($"错误: {ex.Message}", LogLevel.Error); message = ex.Message; return Result.Failed; } _logger.Initialize(config.LogFilePath); _logger.Log("开始执行智能标注命令"); _logger.Log($"当前视图: {activeView.Name} ({activeView.Id.IntegerValue})", LogLevel.Debug); try { if (!(activeView is ViewPlan || activeView is ViewSection || activeView.ViewType == ViewType.Elevation)) { _logger.Log("错误:仅支持平面、剖面或立面视图", LogLevel.Error); return Result.Failed; } var selectedElementIds = uiDoc.Selection.GetElementIds(); if (selectedElementIds.Count == 0) { _logger.Log("提示:请先选择需要标注的构件", LogLevel.Info); return Result.Cancelled; } _logger.Log($"选中了 {selectedElementIds.Count}个素", LogLevel.Info); using (Transaction trans = new Transaction(doc, "智能标注")) { trans.Start(); _logger.Log("事务启动:智能标注"); try { var selectedElements = selectedElementIds .Select(id => doc.GetElement(id)) .Where(e => e != null && e.IsValidObject) .ToList(); var geometryExtractor = new GeometryExtractor(doc, config, _logger); var curves = geometryExtractor.ExtractCurves(selectedElements.Select(e => e.Id), activeView); _logger.Log($"成功提取 {curves.Count}条曲线"); var dimensionCreator = new DimensionCreator(doc, activeView, config, _logger, selectedElements); int createdCount = dimensionCreator.CreateDimensions(curves); _logger.Log($"成功创建 {createdCount}个标注"); trans.Commit(); _logger.Log("事务提交成功:智能标注", LogLevel.Info); return Result.Succeeded; } catch (Exception ex) { _logger.Log($"事务执行失败: {ex.Message}", LogLevel.Error); trans.RollBack(); message = ex.Message; return Result.Failed; } } } catch (Exception ex) { _logger.Log($"命令执行失败: {ex.Message}", LogLevel.Error); message = ex.Message; return Result.Failed; } finally { _logger.Flush(); } } } public class PluginConfig { public double MinLineLength { get; set; } public double AngleTolerance { get; set; } public double BaseDimensionOffset { get; set; } public string LogFilePath { get; set; } public double MaxOffsetMultiplier { get; set; } public double OffsetIncrement { get; set; } public double BoundingBoxPadding { get; set; } public double DimensionGroupSpacing { get; set; } public bool UseAdaptiveOffset { get; set; } public bool UseGrouping { get; set; } public bool GroupByDirection { get; set; } public bool GroupByType { get; set; } public bool UseParallelProcessing { get; set; } public bool CacheGeometryResults { get; set; } public double SectionViewDepthExtension { get; set; } public DimensionAlignment DimensionAlignment { get; set; } public double MinimumDimensionGap { get; set; } public int MaxThreadCount { get; set; } } public enum LogLevel { Debug, Info, Warning, Error, Critical } public class Logger { private StreamWriter _logWriter; private readonly Queue<string> _logQueue = new Queue<string>(); private bool _isInitialized = false; public void Initialize(string filePath) { try { _logWriter = new StreamWriter(filePath, true) { AutoFlush = false }; _isInitialized = true; Log("日志系统初始化成功", LogLevel.Info); } catch (Exception ex) { _isInitialized = false; Console.WriteLine($"日志初始化失败: {ex.Message}"); } } public void Log(string message, LogLevel level = LogLevel.Info) { if (!_isInitialized) return; string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{level}] - {message}"; lock (_logQueue) { _logQueue.Enqueue(logEntry); } } public void LogException(string context, Exception ex) { string fullMessage = $"{context}: {ex.Message}\n{ex.StackTrace}"; Log(fullMessage, LogLevel.Error); if (ex.InnerException != null) { Log($"内部异常: {ex.InnerException.Message}", LogLevel.Error); } } public void Flush() { if (!_isInitialized || _logWriter == null) return; try { lock (_logQueue) { while (_logQueue.Count > 0) { _logWriter.WriteLine(_logQueue.Dequeue()); } _logWriter.Flush(); } } catch (Exception ex) { Console.WriteLine($"日志写入失败: {ex.Message}"); } } } // ======================================== // ReferenceFactory(修正版,仅对齐标注和弧度标注,移除临时素创建,直接查找真实Reference) // ======================================== public static class ReferenceFactory { // 获取素的边(Edge)Reference,适用于对齐标注和弧度标注 public static Reference GetEdgeReference(Element element, Curve targetCurve) { Options opt = new Options { ComputeReferences = true }; GeometryElement geomElem = element.get_Geometry(opt); foreach (GeometryObject geomObj in geomElem) { if (geomObj is Solid solid) { foreach (Edge edge in solid.Edges) { Curve edgeCurve = edge.AsCurve(); if (IsCurveMatch(edgeCurve, targetCurve)) { return edge.Reference; } } } } return null; } // 判断两条曲线是否近似相等(支持Line和Arc) private static bool IsCurveMatch(Curve a, Curve b, double tol = 1e-6) { if (a is Line la && b is Line lb) { return (la.GetEndPoint(0).IsAlmostEqualTo(lb.GetEndPoint(0), tol) && la.GetEndPoint(1).IsAlmostEqualTo(lb.GetEndPoint(1), tol)) || (la.GetEndPoint(0).IsAlmostEqualTo(lb.GetEndPoint(1), tol) && la.GetEndPoint(1).IsAlmostEqualTo(lb.GetEndPoint(0), tol)); } if (a is Arc aa && b is Arc ab) { return aa.Center.IsAlmostEqualTo(ab.Center, tol) && Math.Abs(aa.Radius - ab.Radius) < tol; } return false; } private static bool IsAlmostEqualTo(this XYZ a, XYZ b, double tol = 1e-6) { return a.DistanceTo(b) < tol; } } // ======================================== // DimensionCreator(仅对齐标注和弧度标注,自动遍历素) // ======================================== public class DimensionCreator { private readonly Document _doc; private readonly View _view; private readonly PluginConfig _config; private readonly Logger _logger; private readonly List<Element> _elements; public DimensionCreator(Document doc, View view, PluginConfig config, Logger logger, List<Element> elements) { _doc = doc; _view = view; _config = config; _logger = logger; _elements = elements; } public int CreateDimensions(List<Curve> curves) { int count = 0; foreach (var curve in curves) { foreach (var element in _elements) { if (curve is Line) { if (CreateAlignmentDimension(element, curve)) { count++; break; } } else if (curve is Arc arc) { if (CreateArcDimension(element, arc)) { count++; break; } } } } return count; } private bool CreateAlignmentDimension(Element element, Curve curve) { try { Reference edgeRef = ReferenceFactory.GetEdgeReference(element, curve); if (edgeRef == null) return false; XYZ p1 = curve.GetEndPoint(0); XYZ p2 = curve.GetEndPoint(1); Line dimLine = Line.CreateBound(p1, p2); ReferenceArray refArray = new ReferenceArray(); refArray.Append(edgeRef); Dimension dimension = _doc.Create.NewDimension(_view, dimLine, refArray); return true; } catch (Exception ex) { _logger.LogException($"创建对齐标注失败: {ex.Message}", ex); return false; } } private bool CreateArcDimension(Element element, Arc arc) { try { Reference edgeRef = ReferenceFactory.GetEdgeReference(element, arc); if (edgeRef == null) return false; XYZ arcMid = arc.Evaluate(0.5, true); _doc.Create.NewSpotElevation(_view, edgeRef, arcMid, arcMid, arcMid, arcMid, false); return true; } catch (Exception ex) { _logger.LogException($"创建弧度标注失败: {ex.Message}", ex); return false; } } } // ======================================== // 标注对齐方式枚举 // ======================================== public enum DimensionAlignment { Above, Below, Left, Right } } namespace SmartDimensionCommandNS { public class GeometryExtractor { private readonly Document _doc; private readonly PluginConfig _config; private readonly Logger _logger; public GeometryExtractor(Document doc, PluginConfig config, Logger logger) { _doc = doc; _config = config; _logger = logger; } public List<Curve> ExtractCurves(IEnumerable<ElementId> elementIds, View view) { var curves = new List<Curve>(); foreach (var id in elementIds) { var element = _doc.GetElement(id); if (element == null || !element.IsValidObject) continue; var options = new Options { ComputeReferences = true }; var geometryElement = element.get_Geometry(options); foreach (var geomObj in geometryElement) { if (geomObj is Solid solid) { foreach (Edge edge in solid.Edges) { curves.Add(edge.AsCurve()); } } } } return curves; } } }这段代码有没有实际用处,对于使用revit的人来说能做什么
06-13
严重性 代码 说明 项目 文件 行 禁止显示状态 错误(活动) CS0234 命名空“Autodesk.Revit.UI.Events”中不存在类型或命名空名“ViewChangedEventArgs”(是否缺少程序集引用?) 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 70 错误(活动) CS1503 参数 5: 无法从“string”转换为“Autodesk.Revit.DB.TextNoteOptions” 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 62 错误(活动) CS1503 参数 4: 无法从“Autodesk.Revit.DB.TextNoteOptions”转换为“string” 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 62 错误(活动) CS1061 “View”未包含“Transform”的定义,并且找不到可接受第一个“View”类型参数的可访问扩展方法“Transform”(是否缺少 using 指令或程序集引用?) 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 112 错误(活动) CS1061 “UIApplication”未包含“ViewChanged”的定义,并且找不到可接受第一个“UIApplication”类型参数的可访问扩展方法“ViewChanged”(是否缺少 using 指令或程序集引用?) 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 38 错误(活动) CS1061 “Location”未包含“Origin”的定义,并且找不到可接受第一个“Location”类型参数的可访问扩展方法“Origin”(是否缺少 using 指令或程序集引用?) 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 90 错误(活动) CS0122 “FloatingAxisCommand._axisLabels”不可访问,因为它具有一定的保护级别 轴网定位 F:\revitDD\轴网定位\VirtualAxisCommand.cs 134 using Autodesk.Revit.ApplicationServices; using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using System.Collections.Generic; using System.Linq; namespace FloatingAxisLabel { [Transaction(TransactionMode.Manual)] public class FloatingAxisCommand : IExternalCommand { // 存储轴线与浮动标签的映射关系 private static Dictionary<ElementId, TextNote> _axisLabels = new Dictionary<ElementId, TextNote>(); public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiApp = commandData.Application; UIDocument uiDoc = uiApp.ActiveUIDocument; Document doc = uiDoc.Document; View activeView = doc.ActiveView; // 步骤1:获取所有轴线 FilteredElementCollector collector = new FilteredElementCollector(doc, activeView.Id); IList<Grid> grids = collector.OfClass(typeof(Grid)).Cast<Grid>().ToList(); using (Transaction trans = new Transaction(doc, "创建浮动轴号")) { trans.Start(); // 步骤2:创建浮动标签 CreateFloatingLabels(doc, activeView, grids); trans.Commit(); } // 步骤3:注册视图变化事件 uiApp.ViewChanged += OnViewChanged; return Result.Succeeded; } // 创建浮动标签 private void CreateFloatingLabels(Document doc, View view, IList<Grid> grids) { // 获取默认文本类型 ElementId textTypeId = GetDefaultTextTypeId(doc); foreach (Grid grid in grids) { // 计算标签位置(轴线中点) XYZ labelPosition = CalculateMidPoint(grid.Curve); // 创建TextNote TextNoteOptions options = new TextNoteOptions { HorizontalAlignment = HorizontalTextAlignment.Center, VerticalAlignment = VerticalTextAlignment.Middle, TypeId = textTypeId }; TextNote floatingLabel = TextNote.Create(doc, view.Id, labelPosition, options, grid.Name); // 存储映射关系 _axisLabels[grid.Id] = floatingLabel; } } // 视图变化事件处理 private void OnViewChanged(object sender, Autodesk.Revit.UI.Events.ViewChangedEventArgs e) { UIApplication uiApp = sender as UIApplication; if (uiApp == null) return; UIDocument uiDoc = uiApp.ActiveUIDocument; Document doc = uiDoc.Document; View activeView = doc.ActiveView; // 步骤4:更新所有浮动标签位置 using (Transaction trans = new Transaction(doc, "更新浮动轴号")) { trans.Start(); foreach (var pair in _axisLabels.ToList()) { Element axis = doc.GetElement(pair.Key); if (axis is Grid grid && grid.IsValidObject) { XYZ newPosition = CalculateScreenPosition(grid, activeView); pair.Value.Location.Move(newPosition - pair.Value.Location.Origin); } else // 清理无效引用 { _axisLabels.Remove(pair.Key); } } trans.Commit(); } } // 计算轴线中点(模型坐标) private XYZ CalculateMidPoint(Curve curve) { return curve.Evaluate(0.5, true); } // 计算屏幕位置(视图坐标) private XYZ CalculateScreenPosition(Grid grid, View view) { XYZ modelPoint = CalculateMidPoint(grid.Curve); return view.Transform.OfPoint(modelPoint); } // 获取默认文本类型ID private ElementId GetDefaultTextTypeId(Document doc) { return new FilteredElementCollector(doc) .OfClass(typeof(TextNoteType)) .FirstElementId(); } } // 应用关闭时清理资源 public class ApplicationEvents : IExternalApplication { public Result OnStartup(UIControlledApplication application) { return Result.Succeeded; } public Result OnShutdown(UIControlledApplication application) { FloatingAxisCommand._axisLabels.Clear(); return Result.Succeeded; } } } 代码存在问题,请帮我解决并给我一份完整的代码
07-05
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值