ezdxf 实体选择工具(ezdxf.select)完全指南:基于包围盒的窗口、圆形、多边形与栅栏选择

📅 发布时间:2026/10/15 16:09:12
ezdxf 实体选择工具(ezdxf.select)完全指南:基于包围盒的窗口、圆形、多边形与栅栏选择
图形学【免费下载链接】ezdxfPython interface to DXF项目地址https://gitcode.com/gh_mirrors/ez/ezdxf点击查看免费下载本文基于 docs/source/tasks/select.rst 与 实体选择教程并结合 src/ezdxf/select.py 源码与 tests/test_05_tools/test_543_select.py 测试用例整理而成。一、概述ezdxf.select模块是什么ezdxf.select是 ezdxf 提供的实体选择工具模块它允许用户基于各种二维形状——矩形窗口Window、圆形Circle、多边形Polygon、栅栏折线Fence以及单个点——从任意 DXF 实体集合中筛选实体。该模块自 ezdxf 1.4 起还提供了基于 R-Tree 的平面空间搜索索引PlanarSearchIndex用于在大规模绘图中快速预筛候选实体。与 CAD 应用中的选中/框选/交叉选择不同本模块的选择判定不是基于实体的真实几何轮廓而是基于实体的二维包围盒bounding box。因此bbox_inside与 CAD 的内部选择inside selection类似——要求实体的包围盒完全落在选择形状内部bbox_outside与 CAD 的外部选择outside selection类似——要求实体的包围盒完全位于选择形状之外bbox_overlap与 CAD 的交叉选择crossing selection类似但有一个关键区别只要实体的包围盒与选择形状发生重叠即使实体的实际几何完全在选择形状外部例如边框多段线横跨窗口边界也会被选中。这一点与 CAD 交叉选择的行为不同使用时应留意。所有选择函数都接受任意可迭代的 DXF 实体集合作为输入模型空间、图纸空间布局、块布局、EntityQuery查询结果、任何实现了__iter__的实体集合均可并返回一个 ezdxf.query.EntityQuery 容器。该容器自身还提供基于实体类型和 DXF 属性的进一步筛选能力详见下文链式筛选一节。适用场景从模型空间中按窗口快速提取一批实体用于批量处理如批量改图层、批量导出按圆形区域选取某点附近的实体按多边形如地块、区域边界选取落在其中的图元用栅栏折线选取与其路径相交的实体在大图纸中先用PlanarSearchIndex缩小搜索范围再做精确筛选。二、为什么基于包围盒Bounding Box包围盒方案的核心动机是性能优先于绝对精度。计算两个轴对齐矩形AABB之间是否相交/包含仅需几次坐标比较而精确的几何求交曲线、样条、文本轮廓代价高昂。因此select模块先用包围盒做粗筛把复杂几何判定留给后续更精细的处理。需要特别注意的是文本类实体与包含曲线的实体的包围盒并不精确。ezdxf.bbox模块对样条、贝塞尔曲线等采用离散化/控制点近似对文本实体使用字体度量近似因此基于包围盒的选择结果可能与肉眼看到的几何轮廓存在偏差。关于精度限制的详细说明请阅读 src/ezdxf/bbox.py 的文档。这一点在设计上是有意为之。测试文件 tests/test_05_tools/test_543_select.py 的类注释明确写道The selection functions are testing only the bounding boxes of entities. This is a design choice: performance and simplicity over accuracy选择函数只测试实体的包围盒——这是性能与简单性优先于精度的设计选择。例如TestBoundingBoxVsWindow.test_overlaps_all中窗口(0,0)-(2,2)只与 CIRCLE 和 LWPOLYLINE 的包围盒重叠并未触及几何本身但bbox_overlap仍会选中全部 4 个实体。实体来源选择函数的entities参数可以是模型空间doc.modelspace()任意图纸空间布局块布局block layoutEntityQuery查询结果容器任何可迭代的 DXF 实体集合。源码 src/ezdxf/select.py#L219-L242 中的select_by_bbox()展示了统一处理逻辑对每个实体调用bbox.extents((entity,), fastTrue, cachecache)计算包围盒若包围盒无数据has_data为假例如空 HATCH则跳过随后将二维包围盒交给判定函数测试。三、选择形状Selection Shapes三种选择形状全部继承自抽象基类SelectionShapesrc/ezdxf/select.py#L30-L43该基类约定了三个抽象方法is_inside_bbox(entity_bbox)实体包围盒是否在选择形状内部is_outside_bbox(entity_bbox)实体包围盒是否在选择形状外部is_overlapping_bbox(entity_bbox)实体包围盒是否与选择形状重叠。所有选择形状都将实体投影到xy 平面上进行判定忽略 z 轴。3.1 Window矩形窗口Window(p1, p2)以两个对角点定义一个轴对齐矩形窗口from ezdxf import select window select.Window((0, 0), (10, 10))内部实现src/ezdxf/select.py#L46-L68构造函数用两个角点构造BoundingBox2dis_inside_bbox调用BoundingBox2d.contains()判断实体包围盒是否完全被窗口包含is_outside_bboxnot self._bbox.has_overlap(entity_bbox)即两者完全不相交is_overlapping_bboxhas_overlap()判断两者是否相交包括边界相切。3.2 Circle圆形Circle(center, radius)以圆心和半径定义一个圆形选择区域circle select.Circle((150, 105), radius60)内部实现src/ezdxf/select.py#L71-L103构造时同时维护圆心Vec2与圆的外接包围盒_bbox用于快速排除is_inside_bbox要求实体包围盒的全部 4 个角点到圆心的距离 ≤ 半径is_overlapping_bbox先做包围盒相交粗筛再检查任一实体包围盒角点是否在圆内若 4 个角点都在圆外则检查包围盒中心是否在圆内处理圆完全吞入包围盒但角点在外的情况。对圆形而言判定是基于真实圆形区域进行的the selection tests are carried out on the real circular area但被测试对象仍是实体的包围盒——例如一条包围盒与圆相交但线段本身不与圆相交的 LINE仍可能被bbox_overlap选中测试用例TestBoundingBoxVsCircle.test_is_overlapping_line验证了这一行为。3.3 Polygon多边形Polygon(vertices)以一组顶点定义一个任意闭合多边形选择区域polygon select.Polygon([(110, 168), (110, 107), (316, 107), (316, 243), (236, 243)])内部实现src/ezdxf/select.py#L106-L165顶点会被转换为Vec2列表至少需要 3 个顶点否则抛出ValueError(3 or more vertices required)若首尾顶点重合闭合多边形写法会自动弹出最后一个重复顶点v.pop()将其视为开式顶点列表is_inside_bbox先做包围盒相交粗筛再用is_point_in_polygon_2d逐一检查实体包围盒 4 个角点任何角点在多边形外即判定不在内部此外对凹多边形增加了补充测试——检查是否有多边形顶点严格落在实体包围盒内部避免凹多边形漏判is_overlapping_bbox检查任一实体包围盒角点在多边形内或边界上或包围盒边与多边形边相交通过CohenSutherlandLineClipping2d线段裁剪实现见 src/ezdxf/math/clipping.py#L853。限制复杂凹多边形可能无法 100% 正确工作文档与源码均明确标注此警告。测试用例test_circle_is_not_inside_concave_polygon验证了凹多边形顶点[(-10,-10), (10,-10), (10,10), (0,0), (-10,10)]场景下CIRCLE 的包围盒虽然整体在多边形内且各边角点均在多边形内但多边形某些边穿过了实体包围盒因此bbox_inside与bbox_outside均不成立——这正是不保证 100% 正确的典型例子。四、六大选择函数详解所有函数均返回EntityQuery容器并接受可选的cache: bbox.Cache | None参数用于复用包围盒计算结果详见下文性能优化。4.1bbox_inside(shape, entities, *, cacheNone)选择包围盒完全位于选择形状内部的实体。对应shape.is_inside_bbox。import ezdxf from ezdxf import select doc ezdxf.readfile(your.dxf) msp doc.modelspace() # 定义选择窗口两个对角顶点 window select.Window((0, 0), (10, 10)) # 选择模型空间中窗口内的实体 selected_entities select.bbox_inside(window, msp) # 遍历选中实体 for entity in selected_entities: print(entity)基于 实体选择教程 的基准图base.dxf用窗口(150,105)-(280,240)执行bbox_inside的示例输出CIRCLE(#9D) LWPOLYLINE(#9E)即只有完整落入窗口内的 CIRCLE 与 LWPOLYLINE 被选中。4.2bbox_outside(shape, entities, *, cacheNone)选择包围盒完全位于选择形状之外的实体。对应shape.is_outside_bbox。window select.Window((185, 105), (245, 240)) for entity in select.bbox_outside(window, msp): print(str(entity))示例输出窗口收窄后TEXT(#9F) SPLINE(#A0) LINE(#A1)注意bbox_outside的判定是完全不重叠而非几何上在外。若实体的包围盒与窗口相交即使几何在窗口外该实体不会被bbox_outside选中。4.3bbox_overlap(shape, entities, *, cacheNone)选择包围盒与选择形状重叠的实体。对应shape.is_overlapping_bbox。这是最接近 CAD交叉选择的函数但如本文开头所述只要包围盒重叠即入选与真实几何是否相交无关。window select.Window((150, 105), (280, 240)) for entity in select.bbox_overlap(window, msp): print(str(entity))示例输出比bbox_inside多出 TEXT、SPLINE、LINE 和另一条 LWPOLYLINECIRCLE(#9D) LWPOLYLINE(#9E) TEXT(#9F) SPLINE(#A0) LINE(#A1) LWPOLYLINE(#A2)4.4bbox_chained(start, entities, *, cacheNone)从指定的起始实体出发选择所有与它直接或间接通过重叠包围盒相连的实体链式蔓延选择。例如选取一条 LINE 后所有与它沾边的实体都会被连带选中line msp.query(LINE).first for entity in select.bbox_chained(line, msp): print(str(entity))示例输出LINE(#A1) CIRCLE(#9D) LWPOLYLINE(#9E) SPLINE(#A0)源码实现src/ezdxf/select.py#L308-L344采用迭代蔓延算法维护已选集合反复扫描候选列表一旦发现某个候选实体的包围盒与任一已选实体的包围盒重叠即加入并触发新一轮扫描直到不再有新增为止。警告当前实现的时间复杂度为O(n²)对超大实体集合请谨慎使用。测试用例test_all_entities_chained_by_bounding_boxes用 10 条首尾重叠的 LINE 验证了链式传播选中全部 10 条。4.5bbox_crosses_fence(vertices, entities, *, cacheNone)选择包围盒与一条开放折线栅栏相交的实体。栅栏与多边形选择不同——它是开放的折线路径不构成闭合区域for entity in select.bbox_crosses_fence([(83, 101), (186, 193), (300, 107)], msp): print(str(entity))示例输出CIRCLE(#9D) LWPOLYLINE(#9E) SPLINE(#A0) LINE(#A1)实现细节src/ezdxf/select.py#L245-L285至少需要 2 个顶点否则抛出ValueError(2 or more vertices required)判定逻辑先检查实体包围盒与栅栏整体包围盒是否重叠再检查任一栅栏顶点是否落在实体包围盒内若都不满足则用CohenSutherlandLineClipping2d判断栅栏线段是否与实体包围盒相交单个点POINT按定义无法被栅栏选中——因为 POINT 的包围盒退化为一个点而代码对extmin 与 extmax 重合即点包围盒的情况直接返回Falseif extmin.isclose(extmax): return False。测试用例还验证了闭合的栅栏不等效于多边形选择——test_select_nothing_inside_a_closed_fence中闭合栅栏只经过实体包围盒外部未选中任何实体。另外test_select_by_touching_bbox_corner表明栅栏只需触碰实体包围盒的一个角点即可选中该实体即使并未穿过实体几何本身文档注释明确说明 The polyline does not cross the entity geometry itself!。4.6point_in_bbox(location, entities, *, cacheNone)选择指定点位于其包围盒内部的实体for entity in select.point_in_bbox((264, 140), msp): print(str(entity))示例输出LWPOLYLINE(#9E) SPLINE(#A0)实现非常直接src/ezdxf/select.py#L288-L305将点转换为Vec2后对每个实体调用entity_bbox.inside(point)。注意判定对象是包围盒而非真实几何——点落在某实体包围盒内不代表落在该实体几何上。五、链式筛选返回值 EntityQuery 的进一步使用选择函数返回的 EntityQuery 不仅是一个可迭代容器支持len()、索引、切片还支持按实体类型与 DXF 属性二次筛选例如selection.query(LINE[layerwalls])批量读写 DXF 属性selection.layer construction会将所有选中实体的图层统一改为construction__setitem__支持按属性名批量设置见 query.py#L156-L161丢弃/删除属性del selection[layer]清除已销毁实体selection.purge()。典型组合用法先用bbox_overlap按空间位置粗选再用EntityQuery的查询字符串精筛类型和属性实现空间 属性两级筛选selected select.bbox_overlap(window, msp) lines_on_layer selected.query(LINE[layerwalls])六、性能优化复用包围盒缓存bbox.Cache所有选择函数都接受cache关键字参数。当同一批实体需要执行多次选择不同窗口/形状时复用缓存可避免反复计算包围盒from ezdxf import bbox, select cache bbox.Cache() for corner1, corner2 in windows: window select.Window(corner1, corner2) selected select.bbox_inside(window, msp, cachecache) # 复用缓存缓存机制定义在 src/ezdxf/bbox.py#L16-L89以实体的 DXF handle 为键存储包围盒并统计hits与misses命中次数HATCH 实体因包含多个不可区分的边界路径图元而不参与缓存返回None。若实体被修改可用cache.invalidate(entities)使缓存失效。select_by_bbox内部使用fastTrue计算包围盒——对贝塞尔曲线等基于控制点近似可能得到略大的包围盒见 bbox.py#L92-L124 的multi_recursive文档。七、平面空间搜索索引PlanarSearchIndex1.4 新增对于超大规模图纸逐个计算包围盒并测试的选择方式可能成为瓶颈。PlanarSearchIndexsrc/ezdxf/select.py#L347-L450为 DXF 实体建立基于 R-Tree 的二维空间搜索索引底层为 src/ezdxf/math/rtree.py#L139 的RTree可用于预先缩小候选实体范围。7.1 索引的工作原理索引基于实体的包围盒索引的是包围盒的角点POINT 与 LINE 除外POINT 索引其坐标点本身LINE 索引其起点与终点索引在构造时一次性建立构建后不可扩展只能找到至少有一个索引点位于搜索区域内的实体。实体包围盒与搜索区域重叠、但没有任何角点落入搜索区域时将不会被找到例如圆心在搜索区域内、但 4 个包围盒角点都在区域外的圆检测行为可通过重写detection_points方法定制。7.2 基本用法import ezdxf from ezdxf import select doc ezdxf.readfile(large.dxf) msp doc.modelspace() # 建立索引可选传 cache 复用包围盒缓存 index select.PlanarSearchIndex(msp) # 查找检测点落在圆形区域内的实体 hits index.detection_point_in_circle(center(100, 100), radius50) # 查找检测点落在矩形区域内的实体含边界 hits index.detection_point_in_rect((0, 0), (200, 200))7.3 三个核心方法方法功能detection_points(entity)返回给定实体的检测点序列xy 平面投影。默认实现返回包围盒 4 个角点POINT 返回其坐标LINE 返回起点与终点。可重写此方法返回更精细的检测点例如 LWPOLYLINE/POLYLINE 的顶点、块引用的等距栅格点detection_point_in_circle(center, radius)返回检测点位于以center为圆心、radius为半径的圆内的所有实体detection_point_in_rect(p1, p2)返回检测点位于两个角点定义的矩形内含边界的所有实体7.4 与 bbox.Cache 的配合索引构造时若未提供cache会自行创建一个新的bbox.Cache可通过公开属性index.cache访问。文档建议由于该索引常与本模块其他选择工具搭配使用应维护同一个包围盒缓存避免频繁重算包围盒的开销。典型流程cache bbox.Cache() index select.PlanarSearchIndex(msp, cachecache) # 索引构建使用缓存 candidates index.detection_point_in_rect(p1, p2) # 粗筛 selected select.bbox_overlap(window, candidates, cachecache) # 精筛复用同一缓存测试用例TestPlanarSearchIndex验证了半径为 1 的圆形搜索只能命中 POINTCIRCLE 因无角点落入而未命中半径为 8 时全部 4 个实体命中。八、源码实现要点速览关注点位置说明选择函数入口src/ezdxf/select.py#L168-L216bbox_inside/bbox_outside/bbox_overlap均为select_by_bbox的薄封装统一选择引擎src/ezdxf/select.py#L219-L242逐实体算包围盒、跳过无数据实体、测试判定、收集结果形状抽象基类src/ezdxf/select.py#L30-L43SelectionShape三个抽象判定方法多边形点内判定src/ezdxf/math 的is_point_in_polygon_2d返回正值内部、负值外部、0边界线段-矩形相交src/ezdxf/math/clipping.py#L853CohenSutherlandLineClipping2d用于栅栏与多边形边相交判定空间索引src/ezdxf/math/rtree.py#L139RTree提供points_in_sphere/points_in_bbox查询包围盒计算src/ezdxf/bbox.py#L16-L124Cache类与extents/multi_recursive计算函数测试验证tests/test_05_tools/test_543_select.py覆盖三种形状 × 三种判定、栅栏、点选、链式选择、空间索引的全部边界情形九、注意事项与边界情况总结精度边界所有判定基于包围盒而非真实几何。文本与曲线类实体的包围盒不精确详见 src/ezdxf/bbox.py 文档。凹多边形Polygon对复杂凹多边形的is_inside_bbox/is_outside_bbox判定可能不 100% 正确源码注释明确说明 This may not cover all concave polygons。栅栏与点单个 POINT 实体按定义无法被栅栏选中闭合栅栏不等效于多边形选择。链式选择复杂度bbox_chained为 O(n²)大集合慎用。索引局限性PlanarSearchIndex只能发现至少一个检测点落入搜索区域的实体构建后不可扩展max_node_size参数默认 5源码注释提示仅在明确知道后果时才修改。投影规则所有形状均将实体投影到 xy 平面判定三维实体的 z 坐标不影响选择结果。空包围盒跳过无法计算包围盒的实体如无数据的 HATCH会被静默跳过不会导致选择失败。掌握上述函数与形状后你便可以在 ezdxf 中实现与 CAD 软件交互式选择工具相近的窗口选择、圆形选择、多边形选择、栅栏选择与点选能力并可借助EntityQuery链式属性筛选与PlanarSearchIndex空间索引将空间粗筛与属性精筛高效组合。赞分享图形学【免费下载链接】ezdxfPython interface to DXF项目地址https://gitcode.com/gh_mirrors/ez/ezdxf点击查看免费下载相关推荐5步终极指南如何用OpenCore Legacy Patcher让旧Mac焕发新生5步终极指南如何用OpenCore Legacy Patcher让旧Mac焕发新生 想让老旧Mac设备突破官方限制升级到最新macOS系统吗OpenCor操作系统固件驱动开发PX4 Geofence地理围栏完全指南从基础圆柱围栏到多边形规划与源码级原理解析PX4 Geofence地理围栏完全指南从基础圆柱围栏到多边形规划与源码级原理解析 地理围栏Geofence是 PX4 提供的虚拟飞行边界机制用于防嵌入式物联网机器人自动驾驶智能硬件SlickGrid行选择模型单选、多选与范围选择的完整实现SlickGrid行选择模型单选、多选与范围选择的完整实现 SlickGrid作为一款 闪电般快速的JavaScript网格组件 其强大的行选择模型功能让数前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考