LinQ中合并、连接、相交、与非查询

本文介绍了LinQ中的四种主要查询操作:Union用于合并集合并自动过滤重复项;Concat用于连接集合但不过滤重复项;Intersect用于查找两个集合的交集;Except用于从一个集合中移除与另一个集合的共同项。

LinQUnion合并查询连接不同的集合,自动过滤相同项;延迟。即是将两个集合进行合并操作,过滤相同的项

 

var cities = (from p in mylinq.System_Places

          where p.PID == place

          select p).Union(

          from q in mylinq.System_Places

          where q.Parentid==place

          select q

          );

LinQ中的Concat连接查询连接不同的集合,不会自动过滤相同项;延迟。

(from p in System_Places

     where p.PID == 1010

     select p).Concat(

       from q in System_Places

         where q.Parentid==1010

         select q

         ).Concat(

           from n in System_Places

             where n.Parentid==1010

              select n
   )
 

 

LinQ中的Intersect相交查询:获取不同集合的相同项(交集),即两集合都出现的项。

(from c in Bst_System_Places

  select c.CnPlaceName).Intersect(

  from e in Bst_Company_Jobs

  select e.WorkPlace)

LinQ中的Except与非查询:排除相交项,即从某集合中排除与另一集合中相同的项,以前集合为主。。。

(from e in Bst_Company_Jobs

  select e.WorkPlace).Except(

  from c in Bst_System_Places

  select c.CnPlaceName)


转自:http://mwga.net.blog.163.com/blog/static/106456728201063044736712/

转载于:https://www.cnblogs.com/kinpauln/archive/2010/08/31/1813431.html

<think>我们需要开发一个AutoCAD插件,实现将多条首尾相邻的线段延长相交合并成闭合多段线的功能。 步骤: 1. 用户选择多条线段(至少3条)。 2. 设置容差值(用于判断线段端点是否相邻)。 3. 延长这些线段直到两两相交(注意:这里我们处理的是首尾相邻的线段,可能形成一条折线,我们需要延长每条线段使其与相邻的线段相交,然后修剪,最终形成闭合?注意题目要求是“闭合的多段线”)。 4. 修剪多余的线段部分。 5. 合并成闭合多段线。 注意:题目要求是“首尾相邻”的线段,所以这些线段应该构成一个连续的折线(可能不闭合?但最后要求闭合)。实际上,题目要求最终是闭合的多段线,所以我们需要确保延长后形成闭合图形。 然而,题目描述是“批量处理至少三条首尾相临的线段”,并且“合并成一个闭合的多段线”。所以,我们假设这些线段构成一个环(或者通过延长相交后可以形成一个环)。 思路: 1. 获取用户选择的线段(使用选择集,并过滤为直线)。 2. 获取容差值(通过一个简单的对话框或命令行输入)。 3. 将线段按照首尾相连的顺序排序(使用容差值判断相邻)。 4. 延长线段:对于排序后的线段序列(假设为L0, L1, ... , Ln-1),我们需要延长每一条线段,使其与它的前一条和后一条线段相交(注意:首尾相连,所以是一个环?但题目没有明确是环,但要求闭合多段线,所以我们需要形成闭合。因此,我们假设这些线段构成一个封闭的折线,只是每个角点处没有相交,而是断开)。 具体步骤: a) 排序:将线段连接成链(或环)。我们需要找到起点和终点,然后按顺序连接。由于是首尾相邻,我们可以从任意一条线段开始,然后找到与它端点相邻的线段,依次连接。注意容差:两个端点距离小于容差即认为相邻。 b) 延长线段:对于链中的每条线段,计算它与前一条线段和后一条线段的交点(延长后)。注意,我们可能需要延长或缩短线段以达到交点(但题目要求是延长,所以假设是延长)。实际上,在折线中,我们只需要在转折处让相邻的两条线段相交即可。 但是,注意:我们有多条线段,需要每个转折处都相交。因此,我们可以这样处理: - 对于相邻的两条线段(比如A和B),计算它们的交点(延长A的终点端和B的起点端,直到相交)。注意,这里的相邻两条线段都是直线,我们可以直接计算两条直线的交点(即使需要延长)。 - 然后,将A的终点移动到交点,B的起点也移动到交点(这样两条线段就交于一点)。 然而,对于整个链,我们需要依次处理每一对相邻线段(包括最后一对:最后一条线段和第一条线段,因为要闭合?)。但题目要求是闭合多段线,所以我们的链应该是首尾也相连的(即形成一个环)。因此,我们需要确保第一条线段和最后一条线段也是相邻的(通过容差判断首尾端点是否接近)。 c) 修剪:在得到交点后,每条线段只需要保留从它与前一条线段的交点到与后一条线段的交点之间的部分。注意,对于环,每个线段有两个交点(一个与前一交,一个与后一交)。 d) 然后,我们使用这些交点按顺序创建一个闭合的多段线。 然而,有一个问题:原始线段可能不止三条,而且延长后,我们只需要保留交点之间的部分,那么原始线段中间的部分就被修剪掉了。所以,我们实际上不需要真的去延长和修剪实体(因为最终我们要创建新的多段线,并删除原始线段?或者保留?)。根据题目,我们最终要插入一个闭合的多段线,所以我们可以直接根据交点创建多段线,然后删除原始线段(或者不删除?题目没有说,但通常插件会清理原始线段,或者用户可以选择保留)。但题目要求“修剪掉多余的线段部分”,所以我们需要删除原始线段,并用新的多段线代替。 但是,注意:题目要求“延长完成后,修剪掉多余的线段部分,只保留相交的部分”,所以我们需要在图形中修改这些线段,只保留交点之间的部分,然后删除其他部分?但是这样每条线段都被截断了,然后我们还要合并成多段线。所以另一种做法是: - 计算所有交点(每个顶点处,由相邻的两条线段相交得到)。 - 然后,我们得到一系列按顺序排列的交点(顶点)。 - 直接用这些顶点创建一个闭合的多段线。 步骤总结: 1. 选择线段,并检查数量(至少3条)。 2. 按顺序连接这些线段形成一个环(如果首尾端点不接近,则可能需要用户重新选择,或者报错)。 3. 计算相邻线段的交点(包括最后一条和第一条的交点),这些交点就是多段线的顶点。 4. 创建一个闭合的多段线,顶点就是这些交点(按顺序)。 5. 删除原始线段,并添加多段线到模型空间。 注意:计算交点时,相邻的两条线段(非平行)一定有一个交点。如果平行,则无法相交,需要处理错误。 实现细节: - 排序:将线段集合排序成一个环。 a) 任意选择一条线段作为起始,起点为A0,终点为B0。 b) 在剩下的线段中,找一个端点与B0距离小于容差的线段(有两种情况:可能是该线段的起点或终点接近B0)。如果找到,则连接,并记录该线段的另一个端点作为新的终点。如果找不到,则报错。 c) 继续连接,直到所有线段都被连接。最后,检查最后一个线段的终点是否与起始线段的起点接近(容差内),形成环。 但是,注意:线段的方向可能不一致,所以我们需要考虑线段的两个端点。在连接时,我们可能需要翻转线段(即交换起点终点)来使它们首尾相连。 步骤: - 创建一个列表存放排序后的线段,以及一个点列表(用于多段线的顶点)。 - 从第一条线段开始,记录起点和终点(点A0,点B0)。 - 然后寻找下一条线段:遍历剩余线段,检查每条线段的两个端点(P1和P2)是否与当前终点B0接近(距离<容差)。如果找到,假设P1接近B0,则下一条线段的起点为P1,终点为P2;如果P2接近B0,则下一条线段的起点为P2,终点为P1(相当于翻转线段)。 - 将找到的线段从剩余集合移除,并加入排序列表,同时更新当前终点为新的终点。 - 重复直到没有线段剩余。 - 最后,检查最后一条线段的终点是否与第一条线段的起点接近(容差内),如果不接近,则说明不是闭合的,但题目要求最终是闭合多段线,所以我们需要闭合?实际上,我们要求这些线段首尾相邻形成闭合。所以,如果最后一条线段的终点与第一条线段的起点距离大于容差,则报错。 然后,我们得到排序后的线段列表:Line0, Line1, ... , LineN-1,每条线段的起点和终点已经调整,使得前一条的终点与后一条的起点相邻(容差内),并且最后一条的终点与第一条的起点相邻(容差内)。 接下来,计算交点: - 对于相邻的两条线段(包括最后一条和第一条),计算它们的交点。即:Line0和Line1的交点,Line1和Line2的交点,...,LineN-1和Line0的交点。 - 注意:两条线段的交点可能不在它们的原始长度内,所以我们需要计算两条直线的交点(无限延长线)。 然后,我们得到N个交点(每个交点对应一个顶点)。注意,每个交点是由两条相邻线段相交得到的,所以这些交点就是多段线的顶点。 最后,用这N个交点创建一个闭合的多段线(AcDbPolyline)。 代码结构: - 命令:比如叫做"BatchExtendLines" - 步骤: a. 获取当前文档和数据库 b. 使用选择集让用户选择多个线段(过滤直线) c. 如果选择数量小于3,提示错误 d. 获取容差值(使用PromptDoubleOptions) e. 排序线段(形成环) f. 计算相邻线段的交点(注意:相邻线段包括最后一条和第一条) g. 创建多段线(顶点为交点,闭合) h. 删除原始线段(或者根据用户需求,这里我们直接删除原始线段,因为已经合并成多段线了) i. 将多段线添加到模型空间 注意:在计算交点时,如果两条线段平行(无交点),则跳过并报错(或者使用延长线?但平行线无交点,所以需要处理)。 代码实现: 由于代码较长,我们分步骤编写。 注意:使用AutoCAD .NET API,需要引用: - acdbmgd.dll - acmgd.dll 我们使用Transaction处理数据库操作。 步骤详细: 1. 获取线段选择集 2. 获取容差值 3. 将选择到的线段(ObjectId)转换成AcDbLine,并收集起点和终点(同时记录ObjectId,以便后面删除) 4. 排序线段(形成环) 5. 计算交点:遍历排序后的线段列表,对于每一对相邻线段(包括最后一条和第一条)计算交点 6. 创建多段线(AcDbPolyline),设置闭合 7. 删除原始线段,添加多段线 计算交点的方法: - 使用LineSegment3d类(在Geometry命名空间)可以方便计算交点。 - 将两条线段转换为LineSegment3d,然后使用它们的无限长线求交点(IntersectWith)。 注意:AutoCAD .NET API中,LineSegment3d的IntersectWith方法可以设置延长线。 具体:使用IntersectWith,并将第二个参数设置为`Intersect.ExtendBoth`(表示两条线都无限延长)。 但是,如果平行,则返回空数组。 代码: 由于代码较长,我们逐步实现。 注意:排序过程中,我们需要记录每个线段的起点和终点(在排序后调整好的方向)。同时,在排序时,我们构建一个有序列表,列表中的每个元素是一个调整好方向的线段(即起点是前一个线段的终点,终点是下一个线段的起点)。 我们定义一个类来辅助排序(或者使用元组),但为了清晰,我们可以定义一个结构体: SortedLine { public ObjectId Id; // 原始ObjectId public Point3d StartPoint; // 调整后的起点 public Point3d EndPoint; // 调整后的终点 } 然后,排序算法: List<SortedLine> sortedLines = new List<SortedLine>(); List<Line> lines = ...; // 从选择集得到的原始线段(未排序) // 取第一条 SortedLine first = new SortedLine(); first.Id = lines[0].Id; first.StartPoint = lines[0].StartPoint; first.EndPoint = lines[0].EndPoint; sortedLines.Add(first); // 剩余线段 List<Line> remaining = lines.Skip(1).ToList(); // 当前终点 Point3d currentEnd = first.EndPoint; while (remaining.Count > 0) { bool found = false; for (int i = 0; i < remaining.Count; i++) { Line line = remaining[i]; // 检查当前终点与line的起点或终点是否接近 double dist1 = currentEnd.DistanceTo(line.StartPoint); double dist2 = currentEnd.DistanceTo(line.EndPoint); if (dist1 < tolerance || dist2 < tolerance) { SortedLine sortedLine = new SortedLine(); sortedLine.Id = line.Id; if (dist1 < tolerance) { // 当前线段的起点与currentEnd接近,所以起点作为连接点,终点作为新的终点 sortedLine.StartPoint = line.StartPoint; sortedLine.EndPoint = line.EndPoint; } else { // 当前线段的终点与currentEnd接近,所以需要翻转:将终点作为起点,起点作为终点 sortedLine.StartPoint = line.EndPoint; sortedLine.EndPoint = line.StartPoint; } sortedLines.Add(sortedLine); currentEnd = sortedLine.EndPoint; remaining.RemoveAt(i); found = true; break; } } if (!found) { // 无法找到下一条线段,报错 throw new Exception("无法连接线段,可能缺少连接或容差太小。"); } } // 最后,检查最后一条线段的终点与第一条线段的起点是否接近(形成环) if (currentEnd.DistanceTo(sortedLines[0].StartPoint) > tolerance) { throw new Exception("线段未形成闭合环。"); } 然后,我们得到sortedLines,其中线段按顺序连接。 接下来,计算交点: List<Point3d> vertices = new List<Point3d>(); for (int i = 0; i < sortedLines.Count; i++) { // 当前线段和下一线段(如果是最后一条,则下一线段是第一条) SortedLine currentLine = sortedLines[i]; SortedLine nextLine = sortedLines[(i+1) % sortedLines.Count]; // 将两条线段转换为LineSegment3d LineSegment3d seg1 = new LineSegment3d(currentLine.StartPoint, currentLine.EndPoint); LineSegment3d seg2 = new LineSegment3d(nextLine.StartPoint, nextLine.EndPoint); // 求两条无限延长线的交点 Point3dCollection points = new Point3dCollection(); seg1.IntersectWith(seg2, Intersect.ExtendBoth, points, IntPtr.Zero, IntPtr.Zero); if (points.Count == 0) { // 平行,没有交点 throw new Exception("线段平行,无法相交。"); } // 取第一个交点(应该只有一个) vertices.Add(points[0]); } 注意:这样得到的vertices列表就是多段线的顶点(顺序与sortedLines一致)。 然后,创建多段线: using (Polyline polyline = new Polyline()) { for (int i = 0; i < vertices.Count; i++) { polyline.AddVertexAt(i, new Point2d(vertices[i].X, vertices[i].Y), 0, 0, 0); } polyline.Closed = true; // 闭合 // 添加到模型空间 // 注意:在事务中 } 最后,删除原始线段(sortedLines中的所有Id对应的线段)。 完整代码: 注意:我们使用事务处理。 由于代码较长,我们将其写在一个命令方法中。 需要引用的命名空间: using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; 代码实现如下:</think>以下是一个完整的C#实现,用于创建AutoCAD插件,实现批量处理首尾相邻线段并生成闭合多段线的功能: ```csharp using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.EditorInput; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.Linq; namespace LineExtensionPlugin { public class Commands { [CommandMethod("EXTEND_LINES")] public void ExtendLinesCommand() { Document doc = Application.DocumentManager.MdiActiveDocument; Database db = doc.Database; Editor ed = doc.Editor; try { // 步骤1:选择线段 List<Line> selectedLines = SelectLines(ed, 3); if (selectedLines == null) return; // 步骤2:获取容差值 double tolerance = GetTolerance(ed); if (tolerance < 0) return; // 步骤3:排序和连接线段 List<Line> sortedLines = SortLines(selectedLines, tolerance); if (sortedLines == null) { ed.WriteMessage("\n线段无法形成连续路径。"); return; } using (Transaction tr = db.TransactionManager.StartTransaction()) { // 步骤4:计算交点并延长线段 List<Point3d> vertices = CalculateIntersections(sortedLines); // 步骤5:创建闭合多段线 Polyline polyline = CreatePolyline(vertices); BlockTableRecord btr = (BlockTableRecord)tr.GetObject( SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite); btr.AppendEntity(polyline); tr.AddNewlyCreatedDBObject(polyline, true); // 删除原始线段 foreach (Line line in sortedLines) { line.UpgradeOpen(); line.Erase(); } tr.Commit(); ed.WriteMessage("\n成功创建闭合多段线!"); } } catch (Exception ex) { ed.WriteMessage($"\n错误: {ex.Message}"); } } private List<Line> SelectLines(Editor ed, int minCount) { PromptSelectionOptions opts = new PromptSelectionOptions { MessageForAdding = "\n选择首尾相邻的线段: ", AllowDuplicates = false }; TypedValue[] filter = { new TypedValue(0, "LINE") }; SelectionFilter selFilter = new SelectionFilter(filter); PromptSelectionResult res = ed.GetSelection(opts, selFilter); if (res.Status != PromptStatus.OK) return null; if (res.Value.Count < minCount) { ed.WriteMessage($"\n至少需要选择{minCount}条线段。"); return null; } return res.Value.GetObjectIds() .Select(id => id.GetObject(OpenMode.ForRead) as Line) .Where(l => l != null) .ToList(); } private double GetTolerance(Editor ed) { PromptDoubleOptions opts = new PromptDoubleOptions("\n输入连接容差值: ") { AllowNegative = false, DefaultValue = 0.1, UseDefaultValue = true }; PromptDoubleResult res = ed.GetDouble(opts); return res.Status == PromptStatus.OK ? res.Value : -1; } private List<Line> SortLines(List<Line> lines, double tolerance) { List<Line> sorted = new List<Line>(); Line current = lines[0]; lines.RemoveAt(0); sorted.Add(current); while (lines.Count > 0) { bool found = false; for (int i = 0; i < lines.Count; i++) { Line testLine = lines[i]; if (current.EndPoint.DistanceTo(testLine.StartPoint) <= tolerance) { sorted.Add(testLine); current = testLine; lines.RemoveAt(i); found = true; break; } else if (current.EndPoint.DistanceTo(testLine.EndPoint) <= tolerance) { testLine.UpgradeOpen(); testLine.StartPoint = testLine.EndPoint; testLine.EndPoint = testLine.StartPoint; sorted.Add(testLine); current = testLine; lines.RemoveAt(i); found = true; break; } } if (!found) return null; } // 验证闭合 if (sorted[0].StartPoint.DistanceTo(sorted.Last().EndPoint) > tolerance) return null; return sorted; } private List<Point3d> CalculateIntersections(List<Line> lines) { List<Point3d> vertices = new List<Point3d>(); for (int i = 0; i < lines.Count; i++) { Line current = lines[i]; Line next = lines[(i + 1) % lines.Count]; Line3d l1 = new Line3d(current.StartPoint, current.EndPoint); Line3d l2 = new Line3d(next.StartPoint, next.EndPoint); Point3d? intersection = l1.IntersectWith(l2); if (!intersection.HasValue) throw new InvalidOperationException("线段平行无法相交"); vertices.Add(intersection.Value); } return vertices; } private Polyline CreatePolyline(List<Point3d> vertices) { Polyline polyline = new Polyline(); for (int i = 0; i < vertices.Count; i++) { polyline.AddVertexAt(i, new Point2d(vertices[i].X, vertices[i].Y), 0, 0, 0); } polyline.Closed = true; return polyline; } } } ``` ### 功能说明: 1. **选择线段**:用户需选择至少3条首尾相邻的直线(支持框选)。 2. **容差设置**:通过命令行交互设置端点连接的最大允许距离。 3. **智能排序**:自动将线段按连接顺序重新排列(支持反向线段)。 4. **延长相交**:计算相邻线段延长后的精确交点。 5. **创建多段线**:根据交点生成闭合多段线并删除原始线段。 ### 使用说明: 1. 在AutoCAD中加载插件后,输入命令`EXTEND_LINES` 2. 按提示选择至少3条首尾相邻的线段 3. 输入连接容差值(默认0.1) 4. 自动生成闭合多段线 ### 注意事项: - 需要引用AutoCAD .NET API组件(acdbmgd.dll, acmgd.dll) - 线段必须能形成闭合路径 - 相邻线段延长后必须相交(非平行状态) - 原始线段将被永久删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值