在C#中选择一系列线段
本文关键字:一系列 选择 | 更新日期: 2023-09-27 18:28:22
我正试图为我正在开发的一个自制计算机辅助加工软件应用程序创建一个非常简单的功能。基本上,我已经绘制了一些工具路径,可以告诉铣床在哪里行驶。所以,假设我有3组线段,每个线段有100个单独的线段,每个都包含在自己的列表中,如下所示:
List<PointF> points = new List<PointF>();
List<PointF> pointsOffsetHigh = new List<PointF>();
List<PointF> pointsOffsetLow = new List<PointF>();
假设它们在屏幕上纵横交错,当我点击其中的任何线段时,我希望每个线段都被视为自己的对象。我该如何处理?我已经可以使用StackOverflow中的这个优秀例子选择一个单独的线段:图形-绘制线-绘制线并将其移动到
一旦我选择了一系列线段,我就会看到它与另一系列线段的相交处,然后擦除其中的一半。这对任何CAD程序来说都是非常基本的,但在屏幕上看起来如此简单的东西背后却有如此复杂的东西。
如果有人能帮忙,我将不胜感激。代码,通用方法,我愿意接受任何东西。
这将是一项严肃的开发工作,因此在重新设计一切之前,您应该先检查是否有任何开源或第三方库可以满足您的需求。但是,如果您确实从头开始构建自己的解决方案,我建议使用LineSegment
类作为基本(原子对象),而不是List<PointF>
。该提出的LineSegment
类中的基本成员字段将是px
、py
、qx
和qy
,它们表示线段的两个端点的坐标。
你在上面发布的"可移动"线段图形解决方案可以很自然地处理这个对象。此外,任何两个LineSegment
对象之间的交叉测试都可以使用此处概述的逻辑来完成:http://www.geeksforgeeks.org/check-if-two-given-line-segments-intersect/
如果要创建一系列连接的线段,可以将这些单独的LineSegment
对象放置到List<LineSegment>
中(或者在类中添加一个public LineSegment Next;
成员引用,以链表方式将任意两个对象连接在一起)。我意识到会有一些冗余,因为每个段的第二个点将与下一个段的第一个点相同(如果这些段确实在空间上连接的话),但我可以根据经验说,从长远来看,这种原子结构将比简单的点更容易处理,尤其是当拼接线、剪切子段、将它们传递给辅助函数时,等
LineSegment
类还可以自然地扩展,以支持其他特定于线条的属性,如标签、线条颜色、线条宽度等,这些属性不能自然地仅分配给点列表。CAD程序中的曲线通常也是直线的延伸(请参见如何从线段生成Bézier曲线)。
希望这能有所帮助。
我发布了下面的工作代码。基本上,曲线是由线段组成的,线段由两条GraphLines组成。它会检查曲线是否选中了其中一条线段,如果为true,则会点亮整个曲线。有关该代码,请参见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)
{
}
}
}