A. Points in Segments

本文介绍了一个算法问题:给定一系列区间,如何找出这些区间未覆盖的所有整数点。通过使用C++实现的示例代码,展示了如何初始化数组、标记区间内的点,并最终找出所有未被区间覆盖的点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A. Points in Segments

time limit per test

1 second

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given a set of nn segments on the axis OxOx, each segment has integer endpoints between 11 and mm inclusive. Segments may intersect, overlap or even coincide with each other. Each segment is characterized by two integers lili and riri (1≤li≤ri≤m1≤li≤ri≤m) — coordinates of the left and of the right endpoints.

Consider all integer points between 11 and mm inclusive. Your task is to print all such points that don't belong to any segment. The point xxbelongs to the segment [l;r][l;r] if and only if l≤x≤rl≤x≤r.

Input

The first line of the input contains two integers nn and mm (1≤n,m≤1001≤n,m≤100) — the number of segments and the upper bound for coordinates.

The next nn lines contain two integers each lili and riri (1≤li≤ri≤m1≤li≤ri≤m) — the endpoints of the ii-th segment. Segments may intersect, overlap or even coincide with each other. Note, it is possible that li=rili=ri, i.e. a segment can degenerate to a point.

Output

In the first line print one integer kk — the number of points that don't belong to any segment.

In the second line print exactly kk integers in any order — the points that don't belong to any segment. All points you print should be distinct.

If there are no such points at all, print a single integer 00 in the first line and either leave the second line empty or do not print it at all.

Examples

input

Copy

3 5
2 2
1 2
5 5

output

Copy

2
3 4 

input

Copy

1 7
1 7

output

Copy

0

Note

In the first example the point 11 belongs to the second segment, the point 22 belongs to the first and the second segments and the point 55belongs to the third segment. The points 33 and 44 do not belong to any segment.

In the second example all the points from 11 to 77 belong to the first segment.

代码:

#include <iostream>
#include <cstdio>

using namespace std;

int main()
{
    int a[105];
    fill(a,a+105,0);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        fill(a+x,a+y+1,1);
    }
    int ans=0;
    for(int i=1;i<=m;++i)
    {
        if(a[i]==0) ans++;
    }
    cout<<ans<<endl;
    for(int i=1;i<=m;++i)
    {
        if(a[i]==0) cout<<i<<" ";
    }
    return 0;
}

 

给出平均每家临街距离The plots, courtyards, and rooms include many variants. To be able to generate the variants, in the generation process the application of some rules could be altered by each other. The Hutong Neighbourhood Grammar is a parametric shape grammar. Focusing on the plan view, this grammar is defined in the Cartesian product of algebras: U12 × (V02 × V12 × V22). Shapes in algebras U12 include lines to represent the boundaries of Siheyuan plots and alleys. Labels are alphanumeric characters attached to a shape to represent additional information about the shape, which are employed to identify the available rules to be applied to the shape (Stiny, 1980). Shapes in algebras V02 include labelled points to represent the central point of a room plan and the midpoint of an edge of a room plan or a courtyard plan; shapes in algebras V12 include labelled line segments to represent the edge of Siheyuan plot, courtyard, and room; and shapes in algebras V22 include labelled plane segments to represent the plan of Siheyuan plot, courtyard, and room. In the rules, variables are introduced to control the generation of variants. For clarity, labels are used in the form of points, lines, or planes to mark the geometries in assistance in the derivation process, which are deleted once the generation is finished. The grammar describes the iteration of plots from Yuan to Qing dynasties and the generation of Siheyuan on the Qianlong Capital Map in a top-down fashion by dividing and iterating shapes representing the plots and courtyards and adding shapes representing rooms and gates. Corresponding to the historical planning of Hutong neighbourhoods, the generation process followed the steps: 1) generating the Hutong neighbourhood and dividing the neighbourhood into Siheyuan plots, 2) iterating Siheyuan plots, 3) dividing plots into courtyards and defining their types, 4) iterating courtyard, 5) defining room layout pattern and locating rooms and walls. Fig. 3 demonstrates the workflow of the grammar, the rules that could be applied in each step, and the shapes for this grammar. It is noted there are branches in some steps, which case variants of Siheyuan. In the grammar, the rectangles represent neighbourhoods, plots, sub-plots, courtyards, and rooms. The letters in the rectangles are employed to distinguish types of sub-plots and courtyards, and colours of rectangles are used to distinguish types of rooms. The black solid line segments represent the Siheyuan walls and the black dash line segments represent the courtyard walls. Fig. 3 Download: Download high-res image (905KB) Download: Download full-size image Fig. 3. The workflow and the shapes of the grammar. 3.1. Generation of Hutong neighbourhoods plan and Siheyuan plots As recorded in ancient literature such as Kao Gong Ji, an ideal Chinese city should be planned in a square shape to conform to the concept of “round sky and square earth (tian yuan di fang)”. When rebuilt by the Mongols in 1264, the scheme of Beijing was planned in a rectangular shape, which was close to the set forth by the Chinese philosophers. On this rectangular city plan, the urban neighbourhoods were also rectangular, enclosed by the south-north and east-west oriented streets, which caused an orthogonal grid system. This urban system passed through Yuan, Ming, and Qing dynasties and survived into the last century (Steinhardt, 1990). Zhao (1972) pointed out that, when Beijing was rebuilt in the Yuan dynasty, each neighbourhood's depth (length in south–north orientation) was planned to be 67.76 m and its width (length in east-west orientation) was 677.6 m. The Hutong alley between two adjacent neighbourhoods was 9.24 m wide. These key urban elements and dimensions are determining the initial shapes of the Hutong grammar. Each neighbourhood was averagely divided into ten plots aligned in an east–west orientation. We defined these neighbourhoods as “Type A Neighbourhood” (TAN). It is noted that some TANs were replanned in the Qing dynasty. Specifically, in the south-north orientation, two adjacent neighbourhoods were combined as a whole and were divided into three new neighbourhoods, with an alley between each of the two adjacent new neighbourhoods. We defined these new neighbourhoods as “Type B Neighborhoods” (TBN). The neighbourhoods planned in the Yuan dynasty (TAN) were divided into 67.67 m × 67.67 m Siheyuan plots. Although the development of neighbourhoods in the Qing dynasty changed the depth of both Siheyuan plots and neighbourhoods, the width remained. Two 67.76 × 677.6 m2 neighbourhood plans, represented by two rectangles are placed on a two-dimensional (XY) coordinate system, which is defined as the initial shape. On the XY coordinate system, the X-axis is defined as the east-west orientation and the Y-axis is defined as the north-south orientation, and the origin (0, 0) is on the southwest vertex of the south neighbourhood plan. Rule R1 describes the transformation of neighbourhoods from two TANs with one alley to three TBNs with two alleys. Variables associated with the rules are the neighbourhood depth (Nd) and the alley width (Aw), whose values are smaller than the original neighbourhood depth and alley width. Rules R2 describe the division of a TAN or a TBN neighbourhood into Siheyuan plots, represented by squares. Rules R1–R2 are shown in Fig. 4. Fig. 4 Download: Download high-res image (242KB) Download: Download full-size image Fig. 4. Rule R1 transforms TAN to TBN and Rule R2 divides a TAN or TBN into plots. 3.2. Siheyuan plots iteration It is noted the plots experienced iterations in various modes in Yuan, Ming, and Qing dynasties, which result in Siheyuan variants shown on the Qianlong Capital Map. 3.2.1. Division of plots into sub-plots A Siheyuan plot in the Yuan dynasty could be divided into two to four sub-plots in the east-west orientation. Each sub-plot could include several courtyards aligned in south-north orientation (Type A sub-plot, TASP) or empty space (Type B sub-plot, TBSP). We categorized six dividing patterns of a plot, by which the plot is divided into one or two sub-plot types. We found that the width of a TASP is normally around 12–35 m, while most TBSPs are close to or narrower than TASPs but wider than 5 m. Therefore, for simplification, we define the value of the parameter TASPw, the width of a TASP, which ranges from 12 to 35 m, and the value of the parameter TASPw, the width of a TBSP, from 5 to 12 m. The six division patterns of the plots are defined as Rules R3a to R3f respectively. In Fig. 5, Rule R3a divides a plot into two TASPs, and R3b divides a plot into three TASPs. Rule R3c and R3e describe three modes of dividing a plot into two TASPs and one TBSP. The difference between them is the location of the TBSP, which are in the east, west, and mid respectively. Rule 3f describes that a plot is divided into four sub-plots: two TASPs in the mid and a TBSP in the east and west respectively. In these rules, north-south orientated line segments are inserted into the plots to generate new rectangles on the XY plane to represent sub-plots. To locate these line segments, the variables, width of TASP (TASPw) and width of TBSP (TBSPw), are used for description. Fig. 5 Download: Download high-res image (479KB) Download: Download full-size image Fig. 5. Rule R3 divides a plot into sub-plots. Fig. 6 Download: Download high-res image (120KB) Download: Download full-size image Fig. 6. Rule R4 combines sub-plots to become a new sub-plot. 3.2.2. Sub-plot combination Some TASPs and TBSPs were combined to recreate new sub-plots. It is noted some sub-plots were additionally developed into new forms. As the historical material (E, 1739) recorded, to relieve the stress of the increase of the population of Beijing in the Qing dynasty, the government advocated using empty spaces to construct dwellings. It is noted that, since the width of TBSPs was usually too narrow to construct Siheyuan and many Siheyuans on TASPs were dilapidated or derelict due to the change of dynasty, some TASPs and TBSPs were combined to recreate new sub-plots. According to Liu's (2019) investigation of selected Siheyuan examples shown on the Qianlong Capital Map, we inferred the principles that one TASP could be combined with one adjacent TBSP to become a combined sub-plot, called Type AB sub-plot (TABSP). Then the TABSP could be used to construct a Siheyuan with a side courtyard. Since the TABSPs are wider than other types, it enables a Siheyuan to be built on the plot with two courtyards in the east-west orientation. Normally, on a TABSP, a set of courtyards are aligned in a south-north orientation, with a side courtyard next to these courtyards in parallel and connecting the south and north sub-plot boundaries. Rule R4a and R4b describe how a TASP and a TBSP combine to become a TABSP, as shown in Fig. 6. The difference between them is the locations of the TASP and the TBSP. On the right side of the rule, the line segment between the two sub-plots is removed and two rectangles are merged to become a new rectangle, representing the TABSP. The summation of the variables TASPw and TBSPw is the width of TABSP (TABSPw). 3.2.3. Sub-plot disaggregation and recombination The Siheyuan housings with a side courtyard were rare and usually belonged to upper-class owners in ancient times. In many cases, the TABSP disaggregates into many smaller sub-plots, called a Type ABd sub-plot (TABdSP), on which a one-courtyard Siheyuan is constructed. The disaggregation may also happen to some TASPs, whose disaggregated sub-plot is called a Type Ad sub-plot (TAdSP). Two TABdSPs or two TAdSPs adjacent in north-south orientation may be recombined to become a new sub-plot, called Type Ad2 sub-plot (TAd2SP) and Type ABd2 sub-plot (TABd2SP) respectively. In the disaggregation, the number of generated TAdSPs or TABdSPs in a north-south orientation, constrained by the depth of the neighbourhood, is 3 or 4 in most cases. For simplification, we assume it is 4 in TANs and 3 in TBNs. The number in the east-west orientation, constrained by the width of the sub-plot, is 2 or 3 mostly. We define it as 3 if the width is more than 20 m, otherwise 2. These Siheyuans built on disaggregated sub-plots are clustered, in which some Siheyuans are not on street. To create access to these Siheyuans, the west and east edges of disaggregated sub-plots that are adjacent in the east-west orientation, shrink to give space to generate a small south-north oriented alley. The alley might, or might not, cross the neighbourhood to connect the alleys on the south and north side of the neighbourhood. To simplify, we assume that an alley crosses the neighbourhood in this grammar. For 2 × 3 and 2 × 4 disaggregated sub-plots, an alley is generated by shrinking the sub-plot edges. 3 × 3 and 3 × 4 cases could be considered subplots in three rows aligned in parallel in the north-south orientation. For the pattern of three rows, it is unusual to generate an alley between every two adjacent rows. Alternatively, one small alley is generated between two rows in the way the same as the rules of 2 × 3 or 2 × 4 disaggregated sub-plots. For the third row of sub-plots that are not adjacent to the alley, if there are four sub-plots in the rest row, they will recombine to become two TAd2SPs/TABd2SPs, and if there are three sub-plots, two adjacent sub-plots will recombine to become a TAd2SPs/TABd2SP, both of which ensure each sub-plot to have access to the urban fabric. In the shrinkage, the movement distance of each sub-plot edge is slightly different, which makes the alley geometrically irregular. Except for the above disaggregation, there was another mode that a TASP separates into two sub-plots. Historically, a TASP was usually used to construct a three-courtyard Siheyuan or a four-courtyard Siheyuan, whose courtyards were aligned in a row in the north-south orientation. In this mode, the TASP separates by dividing the boundary of the second and the third courtyard, in which the original type of each courtyard remains. We call the separated sub-plot on the south side as the Type ASS sub-plot (TASSSP) and the one on the north as the Type ASN sub-plot (TASNSP). Rule R5 defines the disaggregation of a TASP and a TABSP. Considering the variants caused by neighbourhood depth and width, the rule includes variants Rule R5a-d. Line segments are introduced to indicate the edges of new sub-plots after the disaggregation of a TASP or a TABSP. In Rule R5a, two east-west orientated line segments and one north-south line segment are inserted to divide the TASP into 2 × 3 TAdSPs. In Rule R5b, there is one more east-west orientated line segment, enabling the generation of 2 × 4 TAdSPs. In Rule R5c, two east-west segments and two north-south segments are inserted to divide the plot into 3 × 3 TABdSPs. And in Rule R5d, one more east-west orientated segment than in R5c is introduced to divide the plot into 3 × 4 TABdSPs. The location of each line segment is important since they determine the size of each generated subplot. To describe them, including the inserted north-south orientated line segments and the line segments representing the east and west edges of the plot, the distances between each two of them are defined as w1, w2, and w3 from west to east. And the distances between each two east-west orientated line segments, including the ones representing the north and south edges of the plot and the inserted ones, are defined as d1, d2, d3, and d4 from south to north. These distances are variables to control the disaggregation. Rule R6 describes the generation of a small alley between two north-south orientated rows, which includes two variations. In Rule R6a, six TAdSPs or TABdSPs are clustered in two north-south orientated rows (2 × 3 mode) on the left of the rule. The shared edges of each two adjacent sub-plots in the west and east are labelled as thick line segments. On the right of the rule, for each segment, two copies of it are generated by offsetting itself in the east and west in the mirror using the segment as an axis, which are the boundaries between sub-plots and the small alley edge fragment. The distances describing the width of the small alley fragments (SAw1, SAw2, and SAw3) are variables in this rule. In Rule R6b, there are eight TAdSPs or TABdSPs clustered in two north-south orientated rows (2 × 4 mode). The generation of each small alley fragment is the same. The small alley fragments may move in the east-west orientation. Rule R7 is introduced to simulate the movement. In Rule R7, each pair of line segments moves in the east-west orientation using its mirror axis as an indicator. The displacement is a variable, (SAm1, SAm2, SAm3, and SAm4) in the east-west orientation, and the movement toward the east is positive (+x) and toward the west is negative (-x). The same as Rule R6, Rule R7 has two variations corresponding to the two clustered sub-plot modes. Rule R8 introduces the recombination of two TABdSPs or two TAdSPs, which normally happen to sub-plots that have no access to the urban fabric. In this rule, the shared edge of the two sub-plots adjacent in north-south orientation is removed and the two sub-plots are merged. Considering the disaggregation patterns and sub-plots locations, it includes three variations. Rule R8a-R8c. Rule R9 describes the division of a TASP into a TASNSP and a TASSSP. In the rule, an east-west line segment is inserted into the rectangle, whose variable is the depth of the TASSSP. Rules R5-R9 are shown in Fig. 7. Fig. 7 Download: Download high-res image (722KB) Download: Download full-size image Fig. 7. Rules R5-R9 define the disaggregation and recombination of sub-plots.
06-25
The MLS point cloud registration process for highway tunnels based on a coarse-to-fine two-stage segmentation approach is implemented as follows: (1) Adaptive Overlapping Segmentation: An adaptive overlapping segmentation method considering tunnel ancillary facilities distribution is employed, dividing the point cloud into segments with a segment length of [value] through adaptive overlapping partitioning. (2) Feature-Based Initial Registration: Feature points are extracted from segmented point clouds. A weighted ICP registration incorporating spatial consistency constraints through point-tangent plane matching is performed to obtain the transformation matrix. (3) Deviation Verification: The maximum alignment deviation along the driving direction is calculated using ancillary facilities in adaptively segmented point clouds. If the maximum deviation satisfies threshold [threshold], the process proceeds to step (5). Otherwise, it advances to step (4). (4) Refined Cylindrical Segmentation: A uniformly resampled cylindrical fitting method is applied for secondary overlapping segmentation with a refined segment length of [value]. For segments containing ancillary facilities, step (3) is repeated. For facility-free segments, the transformation matrix from adjacent facility-containing segments is adopted for registration before proceeding to step (5). (5) Redundant Point Removal: Using the maximum scanning line interval as neighborhood search radius [radius], each point in the subsequent segment serves as a query point to identify and remove all neighboring points within the [radius] range from the preceding segment. (6) Point Cloud Integration: The deduplicated point clouds from adjacent segments are merged to obtain the complete registered tunnel point cloud. 画个流程图
03-28
using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Colors; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; // 使用别名解决 Exception 冲突 using SysException = System.Exception; using AecException = Autodesk.AutoCAD.Runtime.Exception; namespace ScaffoldPlugin { public class ScaffoldCommands { #region 常量配置 // 杆件长度配置 private static readonly double[] BarLengths = { 300, 600, 900, 1200, 1500, 1800 }; // 块目录路径 private static readonly string BlockDirectory = Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "ScaffoldBlocks"); // 容差参数 private const double ToleranceValue = 0.1; private const double SpacingTolerance = 150.0; private const double MinAxisLength = 100.0; private const double MinDistanceForBar = 50.0; private const double MaxGapFactor = 1.2; private const double SkipTolerance = 1.0; // 块名称定义 private const string PoleBlockName = "ScaffoldPole立杆"; private const string BarPrefix = "ScaffoldPole横杆"; // 图层定义 private const string PoleLayer = "盘扣-立杆"; private const string BarLayer = "盘扣-横杆"; private const string AxisLayer = "盘扣轴网"; private const string DebugLayer = "盘扣-调试"; #endregion #region BP命令 - 布置脚手架杆件 [CommandMethod("BuildPlatform", "BP", CommandFlags.Modal)] public void BuildPlatform() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 用户确认对话框 if (!ConfirmWithUser(ed, "⚠️ 框选布置杆件数量建议不要超过40000个\n是否继续?")) return; // 检查块目录 if (!CheckBlockDirectory()) return; // 创建必要图层 CreateLayers(db); // 选择轴网线 var axisLines = GetAxisLines(ed); if (axisLines.Count == 0) return; // 计算交点 List<Point3d> intersections = CalculateIntersections(axisLines); ed.WriteMessage($"\n共找到 {intersections.Count} 个有效交点"); // 加载块定义 if (!LoadBlockDefinitions(db, ed)) return; // 缓存现有实体 BuildExistingEntityCache(db); // 布置立杆 int poleCount = PlacePoles(db, intersections, ed); ed.WriteMessage($"\n已布置 {poleCount} 根立杆"); // 布置横杆 int barCount = PlaceBars(db, intersections, axisLines, ed); ed.WriteMessage($"\n已布置 {barCount} 根横杆"); // 结果反馈 if (poleCount > 0 || barCount > 0) { Application.ShowAlertDialog($"成功布置 {poleCount}根立杆和{barCount}根横杆!"); } else { ShowErrorDialog("脚手架布置失败,未布置任何构件!"); } } catch (AecException ex) // 使用别名处理AutoCAD异常 { HandleException(ed, ex); } catch (SysException ex) // 使用别名处理.NET异常 { HandleException(ed, ex); } } #endregion #region BPS命令 - 材料统计 [CommandMethod("GenerateMaterialStats", "BPS", CommandFlags.Modal)] public void GenerateMaterialStats() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 提示选择统计方式 PromptKeywordOptions modeOptions = new PromptKeywordOptions("\n选择统计方式 [n(S)/n(A)]"); modeOptions.Keywords.Add("S", "点选块统计"); modeOptions.Keywords.Add("A", "框选所有块统计"); modeOptions.Keywords.Default = "A"; modeOptions.AllowNone = true; PromptResult modeRes = ed.GetKeywords(modeOptions); if (modeRes.Status != PromptStatus.OK) return; bool isSingleBlockMode = (modeRes.StringResult == "S"); ObjectIdCollection blockIds = new ObjectIdCollection(); string targetBlockName = null; if (isSingleBlockMode) { // 点选块统计模式 PromptEntityOptions singleOpts = new PromptEntityOptions("\n请点选要统计的块参照: "); singleOpts.SetRejectMessage("\n请选择有效的块参照."); singleOpts.AddAllowedClass(typeof(BlockReference), false); PromptEntityResult singleRes = ed.GetEntity(singleOpts); if (singleRes.Status != PromptStatus.OK) return; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockReference br = tr.GetObject(singleRes.ObjectId, OpenMode.ForRead) as BlockReference; if (br != null) { targetBlockName = br.Name; ed.WriteMessage($"\n已选择块: {targetBlockName}"); } tr.Commit(); } if (string.IsNullOrEmpty(targetBlockName)) { ed.WriteMessage("\n错误: 无法获取块名称"); return; } // 获取要统计的块选择集 PromptSelectionOptions selOpts = new PromptSelectionOptions(); selOpts.MessageForAdding = $"\n框选要统计的 {targetBlockName} 块: "; TypedValue[] filterValues = { new TypedValue(0, "INSERT"), new TypedValue(2, targetBlockName) }; PromptSelectionResult selRes = ed.GetSelection(selOpts, new SelectionFilter(filterValues)); if (selRes.Status != PromptStatus.OK) return; blockIds = new ObjectIdCollection(selRes.Value.GetObjectIds()); } else { // 框选所有块统计模式 PromptSelectionOptions selOpts = new PromptSelectionOptions(); selOpts.MessageForAdding = "\n框选要统计的所有块参照: "; TypedValue[] filterValues = { new TypedValue(0, "INSERT") }; PromptSelectionResult selRes = ed.GetSelection(selOpts, new SelectionFilter(filterValues)); if (selRes.Status != PromptStatus.OK) return; blockIds = new ObjectIdCollection(selRes.Value.GetObjectIds()); } // 统计块数量 Dictionary<string, int> stats = CountBlocks(db, blockIds); if (stats.Count == 0) { ed.WriteMessage("\n选定区域中没有可统计的杆件"); return; } // 构建表格数据 List<TableRowData> tableData = BuildTableData(stats, isSingleBlockMode); // 插入统计表格 InsertStatTable(db, ed, tableData, isSingleBlockMode ? targetBlockName : "多种块类型"); ed.WriteMessage("\n材料统计表已成功插入!"); } catch (AecException ex) { HandleException(ed, ex); } catch (SysException ex) { HandleException(ed, ex); } } #endregion #region BP命令辅助方法 private bool ConfirmWithUser(Editor ed, string message) { PromptKeywordOptions options = new PromptKeywordOptions(message); options.Keywords.Add("Y"); options.Keywords.Add("N"); options.AllowNone = false; return ed.GetKeywords(options).StringResult == "Y"; } private bool CheckBlockDirectory() { if (!Directory.Exists(BlockDirectory)) { Directory.CreateDirectory(BlockDirectory); ShowErrorDialog($"块目录已创建: {BlockDirectory}\n请添加块文件后重新运行命令"); return false; } return true; } private void CreateLayers(Database db) { CreateLayerIfNotExists(db, PoleLayer, 7); CreateLayerIfNotExists(db, BarLayer, 1); CreateLayerIfNotExists(db, AxisLayer, 3); CreateLayerIfNotExists(db, DebugLayer, 2); } private List<Line> GetAxisLines(Editor ed) { var filter = new SelectionFilter(new[] { new TypedValue(0, "LINE"), new TypedValue(8, AxisLayer) }); PromptSelectionResult selection = ed.GetSelection( new PromptSelectionOptions { MessageForAdding = "\n选择盘扣轴网线: " }, filter ); if (selection.Status != PromptStatus.OK) return new List<Line>(); List<Line> lines = new List<Line>(); using (Transaction tr = ed.Document.TransactionManager.StartTransaction()) { foreach (ObjectId id in selection.Value.GetObjectIds()) { Line line = tr.GetObject(id, OpenMode.ForRead) as Line; if (line != null && line.Length > MinAxisLength) { lines.Add(line.Clone() as Line); } } tr.Commit(); } ed.WriteMessage($"\n已选择 {lines.Count} 条有效轴网线"); return lines; } private List<Point3d> CalculateIntersections(List<Line> lines) { var results = new HashSet<Point3d>(new Point3dComparer(ToleranceValue)); for (int i = 0; i < lines.Count; i++) { for (int j = i + 1; j < lines.Count; j++) { Point3dCollection pts = new Point3dCollection(); lines[i].IntersectWith(lines[j], Intersect.OnBothOperands, pts, IntPtr.Zero, IntPtr.Zero); foreach (Point3d pt in pts) { if (IsPointOnLineSegment(pt, lines[i], ToleranceValue) && IsPointOnLineSegment(pt, lines[j], ToleranceValue)) { results.Add(pt); } } } } return results.ToList(); } private bool IsPointOnLineSegment(Point3d pt, Line line, double tolerance) { Vector3d lineVec = line.EndPoint - line.StartPoint; Vector3d startToPt = pt - line.StartPoint; double dotProduct = lineVec.DotProduct(startToPt); if (dotProduct < -tolerance || dotProduct > lineVec.LengthSqrd + tolerance) return false; double distSq = startToPt.LengthSqrd - (dotProduct * dotProduct) / lineVec.LengthSqrd; return distSq <= tolerance * tolerance; } private bool LoadBlockDefinitions(Database db, Editor ed) { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; // 加载立杆块 string polePath = Path.Combine(BlockDirectory, $"{PoleBlockName}.dwg"); if (!File.Exists(polePath)) { ShowErrorDialog($"立杆块文件未找到: {polePath}"); return false; } if (!bt.Has(PoleBlockName)) { LoadBlockFromFile(db, polePath, PoleBlockName); ed.WriteMessage($"\n已加载立杆块: {PoleBlockName}"); } // 加载横杆块 foreach (double len in BarLengths) { string barName = $"{BarPrefix}{len}mm"; string barPath = Path.Combine(BlockDirectory, $"{barName}.dwg"); if (!bt.Has(barName) && File.Exists(barPath)) { LoadBlockFromFile(db, barPath, barName); ed.WriteMessage($"\n已加载横杆块: {barName}"); } } tr.Commit(); return true; } } private void LoadBlockFromFile(Database db, string filePath, string blockName) { using (Database sourceDb = new Database(false, true)) { sourceDb.ReadDwgFile(filePath, FileOpenMode.OpenForReadAndAllShare, false, null); db.Insert(blockName, sourceDb, true); } } private void BuildExistingEntityCache(Database db) { _existingEntitiesCache = new HashSet<(string, Point3d, double)>(new EntityKeyComparer()); using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForRead) as BlockTableRecord; foreach (ObjectId id in btr) { BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference; if (br != null && (br.Name == PoleBlockName || br.Name.StartsWith(BarPrefix))) { _existingEntitiesCache.Add((br.Name, br.Position, br.Rotation)); } } } } private int PlacePoles(Database db, List<Point3d> points, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; if (!bt.Has(PoleBlockName)) { ShowErrorDialog($"错误:立杆块 {PoleBlockName} 未加载!"); return 0; } ObjectId poleBlockId = bt[PoleBlockName]; foreach (Point3d pt in points) { if (double.IsNaN(pt.X) || double.IsNaN(pt.Y)) continue; if (IsCached(PoleBlockName, pt, 0)) continue; BlockReference br = new BlockReference(pt, poleBlockId); br.Layer = PoleLayer; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } tr.Commit(); } return count; } private int PlaceBars(Database db, List<Point3d> points, List<Line> axisLines, Editor ed) { int count = 0; using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable; BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; foreach (Line axisLine in axisLines) { List<Point3d> linePoints = points.Where(p => IsPointOnLineSegment(p, axisLine, ToleranceValue)).ToList(); if (linePoints.Count < 2) continue; linePoints = SortPointsByLineParam(axisLine, linePoints); List<Segment> segments = GroupPointsIntoSegments(linePoints); foreach (Segment segment in segments) { for (int i = 0; i < segment.Points.Count - 1; i++) { Point3d start = segment.Points[i]; Point3d end = segment.Points[i + 1]; double distance = start.DistanceTo(end); if (distance < MinDistanceForBar) continue; double bestLength = FindBestBarLength(distance); if (bestLength < 0) continue; string blockName = $"{BarPrefix}{bestLength}mm"; if (!bt.Has(blockName)) continue; Point3d midPoint = new Point3d( (start.X + end.X) / 2, (start.Y + end.Y) / 2, (start.Z + end.Z) / 2); Vector3d direction = (end - start).GetNormal(); double angle = Math.Atan2(direction.Y, direction.X); if (IsCached(blockName, midPoint, angle)) continue; BlockReference br = new BlockReference(midPoint, bt[blockName]); br.Layer = BarLayer; br.Rotation = angle; btr.AppendEntity(br); tr.AddNewlyCreatedDBObject(br, true); count++; } } } tr.Commit(); } return count; } private double FindBestBarLength(double distance) { double bestLength = -1; double minDiff = double.MaxValue; foreach (double len in BarLengths) { double diff = Math.Abs(len - distance); if (diff < minDiff && diff <= SpacingTolerance) { minDiff = diff; bestLength = len; } } return bestLength; } private List<Point3d> SortPointsByLineParam(Line line, List<Point3d> points) { Vector3d lineVec = line.EndPoint - line.StartPoint; double lineLength = lineVec.Length; return points .Select(p => new { Point = p, Param = lineVec.DotProduct(p - line.StartPoint) / lineLength }) .OrderBy(x => x.Param) .Select(x => x.Point) .ToList(); } private List<Segment> GroupPointsIntoSegments(List<Point3d> points) { List<Segment> segments = new List<Segment>(); if (points.Count < 2) return segments; double maxGap = BarLengths.Max() * MaxGapFactor; Segment currentSegment = new Segment { Points = { points[0] } }; for (int i = 1; i < points.Count; i++) { double distance = points[i - 1].DistanceTo(points[i]); if (distance > maxGap) { if (currentSegment.Points.Count > 1) segments.Add(currentSegment); currentSegment = new Segment { Points = { points[i] } }; } else { currentSegment.Points.Add(points[i]); } } if (currentSegment.Points.Count > 1) segments.Add(currentSegment); return segments; } private void CreateLayerIfNotExists(Database db, string layerName, short colorIndex) { using (Transaction tr = db.TransactionManager.StartTransaction()) { LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable; if (lt.Has(layerName)) return; lt.UpgradeOpen(); LayerTableRecord ltr = new LayerTableRecord { Name = layerName, Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex) }; lt.Add(ltr); tr.AddNewlyCreatedDBObject(ltr, true); tr.Commit(); } } private bool IsCached(string blockName, Point3d pos, double angle) { return _existingEntitiesCache?.Contains((blockName, pos, angle)) ?? false; } #endregion #region BPS命令辅助方法 private Dictionary<string, int> CountBlocks(Database db, ObjectIdCollection ids) { var stats = new Dictionary<string, int>(); using (Transaction tr = db.TransactionManager.StartTransaction()) { foreach (ObjectId id in ids) { BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference; if (br == null) continue; string blockName = br.Name; stats[blockName] = stats.TryGetValue(blockName, out int count) ? count + 1 : 1; } tr.Commit(); } return stats; } private List<TableRowData> BuildTableData(Dictionary<string, int> stats, bool isSingleBlockMode) { var tableData = new List<TableRowData>(); if (isSingleBlockMode) { // 点选模式 - 只有一行数据 var item = stats.First(); tableData.Add(new TableRowData { MaterialName = GetMaterialName(item.Key), Length = GetLengthFromName(item.Key), Quantity = item.Value.ToString() }); } else { // 框选模式 - 按块名排序 foreach (var item in stats.OrderBy(kvp => kvp.Key)) { tableData.Add(new TableRowData { MaterialName = GetMaterialName(item.Key), Length = GetLengthFromName(item.Key), Quantity = item.Value.ToString() }); } } return tableData; } private string GetMaterialName(string blockName) { if (blockName.Contains("横杆")) return "横杆"; if (blockName.Contains("立杆")) return "立杆"; if (blockName.Contains("斜拉杆")) return "斜拉杆"; if (blockName.Contains("顶托")) return "顶托"; if (blockName.Contains("底托")) return "底托"; return "其他杆件"; } private string GetLengthFromName(string blockName) { Match match = Regex.Match(blockName, @"\d+"); return match.Success ? match.Value : "\\"; } private void InsertStatTable(Database db, Editor ed, List<TableRowData> data, string tableTitle) { using (Transaction tr = db.TransactionManager.StartTransaction()) { BlockTableRecord btr = tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite) as BlockTableRecord; // 获取表格插入点 PromptPointResult ppr = ed.GetPoint("\n请选择表格插入点: "); if (ppr.Status != PromptStatus.OK) return; Point3d insertPoint = ppr.Value; // 创建表格对象 Table table = new Table(); table.Position = insertPoint; table.SetSize(data.Count + 2, 4); // 标题行+表头行+数据行 table.SetRowHeight(3.0); table.SetColumnWidth(0, 6.0); table.SetColumnWidth(1, 18.0); table.SetColumnWidth(2, 18.0); table.SetColumnWidth(3, 18.0); table.Layer = "0"; // 合并标题行单元格 table.MergeCells(CellRange.Create(table, 0, 0, 0, 3)); // 设置标题行 table.Cells[0, 0].TextString = $"{tableTitle} 材料统计表"; table.Cells[0, 0].Alignment = CellAlignment.MiddleCenter; table.Cells[0, 0].TextHeight = 3.0; // 设置表头行 table.Cells[1, 0].TextString = "序号"; table.Cells[1, 1].TextString = "材料名称"; table.Cells[1, 2].TextString = "杆件长度(mm)"; table.Cells[1, 3].TextString = "合计数量/根"; // 设置表头样式(全部居中) for (int col = 0; col < 4; col++) { table.Cells[1, col].Alignment = CellAlignment.MiddleCenter; table.Cells[1, col].TextHeight = 2.5; } // 填充数据行 for (int i = 0; i < data.Count; i++) { int row = i + 2; table.Cells[row, 0].TextString = (i + 1).ToString(); table.Cells[row, 1].TextString = data[i].MaterialName ?? "\\"; table.Cells[row, 2].TextString = data[i].Length ?? "\\"; table.Cells[row, 3].TextString = data[i].Quantity ?? "\\"; // 设置数据行样式(全部居中) for (int col = 0; col < 4; col++) { table.Cells[row, col].Alignment = CellAlignment.MiddleCenter; table.Cells[row, col].TextHeight = 2.5; } } btr.AppendEntity(table); tr.AddNewlyCreatedDBObject(table, true); tr.Commit(); } } #endregion #region 通用辅助方法 private void ShowErrorDialog(string message) { Application.ShowAlertDialog(message); } private void HandleException(Editor ed, SysException ex) { string errorType = ".NET"; ed.WriteMessage($"\n{errorType}错误: {ex.Message}"); ShowErrorDialog($"发生{errorType}异常: {ex.Message}\n详细请查看命令行日志"); } private void HandleException(Editor ed, AecException ex) { string errorType = "AutoCAD"; ed.WriteMessage($"\n{errorType}错误: {ex.Message} (状态: {ex.ErrorStatus})"); ShowErrorDialog($"发生{errorType}异常: {ex.Message}\n详细请查看命令行日志"); } #endregion #region 数据结构 private class TableRowData { public string MaterialName { get; set; } public string Length { get; set; } public string Quantity { get; set; } } private class Segment { public List<Point3d> Points { get; set; } = new List<Point3d>(); } private class Point3dComparer : IEqualityComparer<Point3d> { private readonly double _tolerance; public Point3dComparer(double tolerance) => _tolerance = tolerance; public bool Equals(Point3d p1, Point3d p2) => p1.DistanceTo(p2) < _tolerance; public int GetHashCode(Point3d p) => p.X.GetHashCode() ^ p.Y.GetHashCode() ^ p.Z.GetHashCode(); } private class EntityKeyComparer : IEqualityComparer<(string, Point3d, double)> { public bool Equals((string, Point3d, double) x, (string, Point3d, double) y) => x.Item1 == y.Item1 && x.Item2.DistanceTo(y.Item2) < SkipTolerance && Math.Abs(x.Item3 - y.Item3) < 0.01; public int GetHashCode((string, Point3d, double) obj) => obj.Item1.GetHashCode() ^ obj.Item2.GetHashCode() ^ obj.Item3.GetHashCode(); } private HashSet<(string blockName, Point3d pos, double angle)> _existingEntitiesCache; #endregion } } 点选”点选块统计”和”框选所有块统计”不起作用 按原代码修改一套完整的代码
07-12
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值