在C#中选择一系列线段

3

我正在尝试为一个家庭制造的计算机辅助加工软件应用程序创建一个非常简单的功能。基本上,我已经绘制出一些工具路径,这将告诉铣床机器要去哪里。所以,想象一下我有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程序中非常基本的功能,但在屏幕上看起来很简单,背后却有很多复杂性。
如果有人能提供帮助,我将不胜感激。 无论是代码还是一般方法,我都会接受。

你有搜索过“线段交点算法”吗? - aybe
顺便提一下,有一些库可以帮你省去自己编写几何数据结构和算法的麻烦。我曾经成功地使用了NetTopologySuite(Java库JTS的一个移植版),它专为GIS(地理信息系统)设计,但也能够完成你在CAD程序中所需的大部分功能。(不过没有圆/曲线,只有点、线和多边形...) - adv12
2个回答

1
这将是一项严肃的开发工作,因此在重新发明一切之前,您应该确保检查是否有任何开源或第三方库可以满足您的需求。但是,如果您决定从头开始自己解决问题,我建议使用LineSegment类作为您的基本(原子对象),而不是List<PointF>。这个建议中LineSegment类的必要成员字段是pxpyqxqy,它们表示线段的两个端点的坐标。
您发布的“可移动”线段图形解决方案与此对象自然配合。此外,任何两个LineSegment对象之间的交点测试都可以使用此处概述的逻辑来完成:http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/ 如果你想创建一系列相连的线段,你可以将这些单独的LineSegment对象放入一个List<LineSegment>中(或者在类中添加一个public LineSegment Next;成员引用来以链表方式连接任意两个对象)。我知道会有一些冗余,因为每个线段的第二个点将与下一个线段的第一个点相同(如果这些线段确实是空间上相连的),但是我可以从经验上说,这种原子结构在长期内比简单的点更容易处理,尤其是当拼接线条、剪切子段、将它们传递给辅助函数等时。

LineSegment类还可以自然地扩展以支持进一步的特定于线条的属性,如标签、线条颜色、线条宽度等,这些属性不能自然地分配给一个点列表。即使在CAD程序中,曲线也通常是直线的扩展(请参见Bézier curves如何由线段生成)。

希望这能帮到你。


谢谢你的建议。我很感激。那个线段网页正是我下一步需要做的。虽然我仍可能会选择OpenGL,但这似乎需要投入大量时间。 - SojourningStudent

0
我在下面发布了可用的代码。基本上,曲线由线段组成,每个线段由两个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)
    {

    }

}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接