【问题标题】:Selecting a series of line segments in C#在 C# 中选择一系列线段
【发布时间】:2015-12-30 02:56:49
【问题描述】:

我正在尝试为我正在开发的自制计算机辅助加工软件应用程序创建一个非常简单的功能。基本上,我已经画出了一些刀具路径,它们可以告诉铣床到哪里去。所以,假设我有 3 组线段,每组都有 100 个单独的线段,每一个都包含在自己的列表中,如下所示:

List<PointF> points = new List<PointF>();
List<PointF> pointsOffsetHigh = new List<PointF>();  
List<PointF> pointsOffsetLow = new List<PointF>(); 

假设它们在屏幕上相互交叉,当我单击其中的任何线段时,我希望每个对象都被视为自己的对象。我将如何处理?我已经可以使用 StackOverflow 中的这个优秀示例来选择单独的线段:Graphic - DrawLine - draw line and move it

一旦我选择了一系列线段,我将查看它与另一系列线段相交的位置,然后擦除其中的一半。对于任何 CAD 程序来说,它都是非常基本的东西,但在屏幕上看起来如此简单的东西背后却有着如此多的复杂性。

如果有人可以提供帮助,我将不胜感激。代码,通用方法,我什么都会。

【问题讨论】:

  • 你搜索过“线相交算法”吗?
  • 仅供参考,有一些库可以让您省去自己完成所有几何数据结构和算法的麻烦。我在 NetTopologySuite(Java 库 JTS 的一个端口)上取得了成功,它专为 GIS(地理信息系统)而设计,但它可以完成您在 CAD 程序中想要的大部分功能。 (但没有圆/曲线,只有点、线和多边形......)

标签: c# intersection


【解决方案1】:

这将是一项严肃的开发工作,因此在重新发明一切之前,您绝对应该检查是否有任何开源或 3rd 方库可以满足您的需求。但是,如果您确实从头开始推出自己的解决方案,我建议您使用 LineSegment 类作为您的基础(原子对象),而不是 List&lt;PointF&gt;。这个提议的LineSegment 类中的基本成员字段将是pxpyqxqy,它们表示线段的两个端点的坐标。

您在上面发布的“可移动”线段图形解决方案自然适用于该对象。任何两个LineSegment 对象之间的交叉测试也可以使用此处概述的逻辑来完成:http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/

如果您想创建一系列连接的线段,您可以将这些单独的 LineSegment 对象放入 List&lt;LineSegment&gt; 中(或者将 public LineSegment Next; 成员引用添加到类中以将任意两个对象连接在一起链表时尚)。我意识到会有一些冗余,因为每个段的第二个点与下一个段的第一个点相同(如果这些段确实在空间上连接),但我可以根据经验说这个原子结构会更容易从长远来看,比简单点更有效,尤其是在拼接线、剪断小节、将它们传递给辅助函数等时。

LineSegment 类也可以自然地扩展以支持更多特定于线条的属性,如标签、线条颜色、线条宽度等,这些属性不能自然地分配给点列表。甚至 CAD 程序中的曲线通常也是直线的延伸(请参阅如何从线段生成 Bézier curves)。

希望这会有所帮助。

【讨论】:

  • 谢谢特别酱。我很欣赏这些建议,而线段网页正是我在流程的下一阶段需要做的。我仍然可能会走 OpenGL 路线,但这似乎是一项相当大的时间投资。
【解决方案2】:

我在下面发布了工作代码。基本上一条曲线是由两条 GraphLines 组成的线段组成的。它检查一条曲线是否选择了一条线段,如果为真,则点亮整条曲线。有关该代码,请参阅 LineMover_Paint。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace LightUpWholeCurve
{

public partial class Form1 : Form
{
    public List<GraphLine> SinArr = new List<GraphLine>();
    public List<GraphLine> Bowditch = new List<GraphLine>();
    public List<GraphLine> CircArr = new List<GraphLine>();


    public List<List<GraphLine>> MasterArr = new List<List<GraphLine>>();


    public List<GraphLine> MasterArrayOfGraphLines = new List<GraphLine>();




    GraphLine SelectedLine = null;
    MoveInfo Moving = null;



    public Form1()
    {
        this.DoubleBuffered = true;

        this.Paint += new PaintEventHandler(LineMover_Paint);
        this.MouseMove += new MouseEventHandler(LineMover_MouseMove);
        this.MouseDown += new MouseEventHandler(LineMover_MouseDown);
        this.MouseUp += new MouseEventHandler(LineMover_MouseUp);

        MakeSinArray();
        MakeBowditchArray();
        MakeCircleArray();


        //Create a lists of lists of each curve
        this.MasterArr.Add(SinArr);
        this.MasterArr.Add(Bowditch);
        this.MasterArr.Add(CircArr);

        foreach (var fullcurve in MasterArr)
        {
            foreach (var GL in fullcurve)
            {
                MasterArrayOfGraphLines.Add(GL);
            }
        }




        //You must use initialize component or you'll get a small window
        //Also, keep in mind that if you cut and paste a whole file you
        //must change the namespace, or it won't recognize "InitializeComponent
        InitializeComponent();
    }

    void MakeBowditchArray()
    {
        int numberOfSeg = 100;
        double TwoPI = (float)(2 * Math.PI) / numberOfSeg;
        for (int t = 0; t <= numberOfSeg; t++)
            this.Bowditch.Add(new GraphLine(
                500 + 25 * (float)Math.Sin(3 * TwoPI * t),
                300 + 25 * (float)Math.Cos(5 * TwoPI * t),
                500 + 25 * (float)Math.Sin(3 * TwoPI * (t + 1)),
                300 + 25 * (float)Math.Cos(5 * TwoPI * (t + 1))));

    }

    void MakeSinArray()
    {
        int numberOfSeg = 100;
        double TwoPI = (float)(2 * Math.PI) / numberOfSeg;
        for (int t = 0; t <= numberOfSeg; t++)
            this.SinArr.Add(new GraphLine(
                200 + 25 * (float)t / 20,
                200 + 25 * (float)Math.Sin(3 * TwoPI * t),
                200 + 25 * (float)(t + 1) / 20,
                200 + 25 * (float)Math.Sin(3 * TwoPI * (t + 1))));
    }

    void MakeCircleArray()
    {
        int numberOfSeg = 50;
        double TwoPI = (float)(2 * Math.PI) / numberOfSeg;
        for (int t = 0; t <= numberOfSeg; t++)
            this.CircArr.Add(new GraphLine(
                300 + 25 * (float)Math.Sin(TwoPI * t),
                300 + 25 * (float)Math.Cos(TwoPI * t),
                300 + 25 * (float)Math.Sin(TwoPI * (t + 1)),
                300 + 25 * (float)Math.Cos(TwoPI * (t + 1))));
    }



    private void LineMover_MouseUp(object sender, MouseEventArgs e)
    {
        if (Moving != null)
        {
            this.Capture = false;  //Capture is part of Control.Capture
            Moving = null;
        }
        RefreshLineSelection(e.Location);
    }

    private void LineMover_MouseDown(object sender, MouseEventArgs e)
    {
        RefreshLineSelection(e.Location);
        if (this.SelectedLine != null && Moving == null)
        {
            this.Capture = true; //gets or sets a bool whether control has captured the mouse.
            Moving = new MoveInfo
            {
                Line = this.SelectedLine,
                StartLinePoint = SelectedLine.StartPoint,
                EndLinePoint = SelectedLine.EndPoint,
                StartMoveMousePoint = e.Location
            };
        }
        RefreshLineSelection(e.Location);

    }

    private void LineMover_MouseMove(object sender, MouseEventArgs e)
    {
        if (Moving != null)
        {

            Moving.Line.StartPoint = new PointF(Moving.StartLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.StartLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
            Moving.Line.EndPoint = new PointF(Moving.EndLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.EndLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
        }
        RefreshLineSelection(e.Location);
    }




    private void LineMover_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;


        //Look at every GraphLine in SinArray and if it is the segment selected, 
        //then turn it to the color Red
        Color color1 = Color.Blue;
        Pen pen1 = new Pen(color1, 2);

        foreach (var line in SinArr)
        {
            if (line == SelectedLine)
            {
                color1 = Color.Red;
                pen1 = new Pen(color1, 2);
                break;
            }
        }

        foreach (var line in SinArr)
        {
            e.Graphics.DrawLine(pen1, line.StartPoint, line.EndPoint);
        }

        //Go through entire array in Bowditch and check to see if any line was selected.
        //If it was, then set color to Red

        color1 = Color.Blue;
        pen1 = new Pen(color1, 2);

        foreach (var line in Bowditch)
        {
            if (line == SelectedLine)
            {
                color1 = Color.Red;
                pen1 = new Pen(color1, 2);
                break;
            }   
        }

        foreach (var line in Bowditch)
        {
            e.Graphics.DrawLine(pen1, line.StartPoint, line.EndPoint);
        }






        color1 = Color.Blue;
        pen1 = new Pen(color1, 2);

        foreach (var line in CircArr)
        {
            if (line == SelectedLine)
            {
                color1 = Color.Red;
                pen1 = new Pen(color1, 2);
                break;
            }
        }

        foreach (var line in CircArr)
        {
            e.Graphics.DrawLine(pen1, line.StartPoint, line.EndPoint);
        }


    }


    private void RefreshLineSelection(Point point)
    {



        var selectedLine = FindLineByPoint(MasterArrayOfGraphLines, point);
        if (selectedLine != this.SelectedLine)
        {
            this.SelectedLine = selectedLine;
            this.Invalidate();
        }
        if (Moving != null)
            this.Invalidate();



        this.Cursor =
            Moving != null ? Cursors.Hand :
            SelectedLine != null ? Cursors.SizeAll :
              Cursors.Default;

    }


    static GraphLine FindLineByPoint(List<GraphLine> lines, Point p)
    {
        var size = 40;
        var buffer = new Bitmap(size * 2, size * 2);

        foreach (var line in lines)
        {
            //draw each line on small region around current point p and check pixel in point p 
            //does it really draw all the lines from this.Lines = new List<GraphLine>() ? [I wrote that]

            using (var g = Graphics.FromImage(buffer))
            {
                g.Clear(Color.Black);  //Makes entire buffer black
                g.DrawLine(new Pen(Color.Green, 3),  //makes a line through it green
                    line.StartPoint.X - p.X + size,
                    line.StartPoint.Y - p.Y + size,
                    line.EndPoint.X - p.X + size,
                    line.EndPoint.Y - p.Y + size);
            }

            if (buffer.GetPixel(size, size).ToArgb() != Color.Black.ToArgb())
                return line;
        }
        return null;
    }


    public class MoveInfo
    {
        public GraphLine Line;
        public PointF StartLinePoint;
        public PointF EndLinePoint;
        public Point StartMoveMousePoint;
    }
    public class GraphLine
    {
        public GraphLine(float x1, float y1, float x2, float y2)
        {
            this.StartPoint = new PointF(x1, y1);
            this.EndPoint = new PointF(x2, y2);
        }
        public PointF StartPoint;
        public PointF EndPoint;
    }

    private void Form1_Load(object sender, EventArgs e)
    {

    }

}
}

【讨论】:

    猜你喜欢
    • 2019-01-15
    • 2022-10-04
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多