C# 删除已绘制的形状
我正在使用System.Drawing命名空间用C#编写我自己的画作,到目前为止,基本上一切都进展顺利,除了一件事,橡皮擦工具 现在我知道橡皮擦的工作原理就是画一条与背景颜色相同的线,这样看起来就像是在擦什么东西 然而,我想做一个新的橡皮擦工具,也许改进。这个新的橡皮擦将能够删除一个单一的点击,如果点击是在它的界限内的元素。 我知道如果已经画了一个东西,它就在那里,什么也做不了,但是我正在考虑创建一个字符串数组,我将向数组中添加新元素。例如,当我添加一条直线和一个矩形时,数组的前两个元素是: 线路起点端点 矩形高度宽度x y 差不多吧。使用擦除工具时,只需比较坐标即可 有没有更简单的方法C# 删除已绘制的形状,c#,system.drawing,C#,System.drawing,我正在使用System.Drawing命名空间用C#编写我自己的画作,到目前为止,基本上一切都进展顺利,除了一件事,橡皮擦工具 现在我知道橡皮擦的工作原理就是画一条与背景颜色相同的线,这样看起来就像是在擦什么东西 然而,我想做一个新的橡皮擦工具,也许改进。这个新的橡皮擦将能够删除一个单一的点击,如果点击是在它的界限内的元素。 我知道如果已经画了一个东西,它就在那里,什么也做不了,但是我正在考虑创建一个字符串数组,我将向数组中添加新元素。例如,当我添加一条直线和一个矩形时,数组的前两个元素是: 线
非常感谢 是的,有。你计划做的基本上是在某种程度上。您保留画布上对象的列表(或其他数据结构),您可以以任何方式重新排序或更改该列表,例如通过删除或添加对象。之后,只需重新创建图形,即清除绘图区域,然后按顺序绘制列表中的每个对象。这是必要的,因为一旦你画了一些东西,你只有像素,如果你的线和矩形相交,你可能会有麻烦从矩形像素分离线像素 使用GDI+时,这是唯一的方法,因为您只能获得一个原始图形表面。但是,其他已经为您提供该渲染模型的东西,例如WPF 然而,字符串[]是解决这个问题的可怕方法。通常你会有一些接口,例如
public interface IShape {
public void Draw(Graphics g);
public bool IsHit(PointF p);
}
你的形状实现了什么。线条将保持其笔划颜色和开始/结束坐标作为状态,然后使用draw
方法绘制自身。此外,当你想用橡皮擦点击一个形状时,你可以用IsHit
方法来确定一个形状是否被点击。这样,每个形状都负责自己的命中测试。例如,一条线可以实现一点模糊,这样你就可以点击线旁边的一点,而不必精确地点击一个像素
无论如何,这是总的想法。您可以根据需要将其扩展到其他想法。请注意,通过使用这种方法,您的核心代码不必了解任何可能的形状(如果您必须维护不断增长的不同形状的开关语句,则比较坐标可能会有点麻烦)。当然,对于绘制这些形状,您仍然需要更多的代码,因为线条可能需要与矩形、椭圆或文本对象不同的交互
我创建了一个小样本,概述了上述方法。有趣的部分如下:
interface IShape
{
Pen Pen { get; set; }
Brush Fill { get; set; }
void Draw(Graphics g);
bool IsHit(PointF p);
}
class Line : IShape
{
public Brush Fill { get; set; }
public Pen Pen { get; set; }
public PointF Start { get; set; }
public PointF End { get; set; }
public void Draw(Graphics g)
{
g.DrawLine(Pen, Start, End);
}
public bool IsHit(PointF p)
{
// Find distance to the end points
var d1 = Math.Sqrt((Start.X - p.X) * (Start.X - p.X) + (Start.Y - p.Y) * (Start.Y - p.Y));
var d2 = Math.Sqrt((End.X - p.X) * (End.X - p.X) + (End.Y - p.Y) * (End.Y - p.Y));
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
var dx = End.X - Start.X;
var dy = End.Y - Start.Y;
var length = Math.Sqrt(dx * dx + dy * dy);
var distance = Math.Abs(dy * p.X - dx * p.Y + End.X * Start.Y - End.Y * Start.X) / Math.Sqrt(dy * dy + dx * dx);
// Make sure the click was really near the line because the distance above also works beyond the end points
return distance < 3 && (d1 < length + 3 && d2 < length + 3);
}
}
public partial class Form1 : Form
{
private ObservableCollection<IShape> shapes = new ObservableCollection<IShape>();
private static Random random = new Random();
public Form1()
{
InitializeComponent();
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
shapes.CollectionChanged += Shapes_CollectionChanged;
}
private void Shapes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Redraw();
}
public void Redraw()
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
foreach (var shape in shapes)
{
shape.Draw(g);
}
}
pictureBox1.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
shapes.Add(new Line
{
Pen = Pens.Red,
Start = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height)),
End = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height))
});
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Redraw();
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
private void button1_MouseClick(object sender, MouseEventArgs e)
{
var shape = FindShape(e.Location);
if (shape != null)
{
shape.Pen = Pens.Blue;
Redraw();
}
}
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
点击图片将尝试在点击点找到一个形状,如果它找到一个,它将把它涂成蓝色(连同一个重画)。查找该项的工作方式如下:
interface IShape
{
Pen Pen { get; set; }
Brush Fill { get; set; }
void Draw(Graphics g);
bool IsHit(PointF p);
}
class Line : IShape
{
public Brush Fill { get; set; }
public Pen Pen { get; set; }
public PointF Start { get; set; }
public PointF End { get; set; }
public void Draw(Graphics g)
{
g.DrawLine(Pen, Start, End);
}
public bool IsHit(PointF p)
{
// Find distance to the end points
var d1 = Math.Sqrt((Start.X - p.X) * (Start.X - p.X) + (Start.Y - p.Y) * (Start.Y - p.Y));
var d2 = Math.Sqrt((End.X - p.X) * (End.X - p.X) + (End.Y - p.Y) * (End.Y - p.Y));
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
var dx = End.X - Start.X;
var dy = End.Y - Start.Y;
var length = Math.Sqrt(dx * dx + dy * dy);
var distance = Math.Abs(dy * p.X - dx * p.Y + End.X * Start.Y - End.Y * Start.X) / Math.Sqrt(dy * dy + dx * dx);
// Make sure the click was really near the line because the distance above also works beyond the end points
return distance < 3 && (d1 < length + 3 && d2 < length + 3);
}
}
public partial class Form1 : Form
{
private ObservableCollection<IShape> shapes = new ObservableCollection<IShape>();
private static Random random = new Random();
public Form1()
{
InitializeComponent();
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
shapes.CollectionChanged += Shapes_CollectionChanged;
}
private void Shapes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Redraw();
}
public void Redraw()
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
foreach (var shape in shapes)
{
shape.Draw(g);
}
}
pictureBox1.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
shapes.Add(new Line
{
Pen = Pens.Red,
Start = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height)),
End = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height))
});
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Redraw();
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
private void button1_MouseClick(object sender, MouseEventArgs e)
{
var shape = FindShape(e.Location);
if (shape != null)
{
shape.Pen = Pens.Blue;
Redraw();
}
}
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
正如你所看到的,画东西的基本部分,也许再选择一次,是很容易的。当然,不同的绘图工具是另一回事。是的,有。你计划做的基本上是在某种程度上。您保留画布上对象的列表(或其他数据结构),您可以以任何方式重新排序或更改该列表,例如通过删除或添加对象。之后,只需重新创建图形,即清除绘图区域,然后按顺序绘制列表中的每个对象。这是必要的,因为一旦你画了一些东西,你只有像素,如果你的线和矩形相交,你可能会有麻烦从矩形像素分离线像素
使用GDI+时,这是唯一的方法,因为您只能获得一个原始图形表面。但是,其他已经为您提供该渲染模型的东西,例如WPF
然而,字符串[]
是解决这个问题的可怕方法。通常你会有一些接口,例如
public interface IShape {
public void Draw(Graphics g);
public bool IsHit(PointF p);
}
你的形状实现了什么。线条将保持其笔划颜色和开始/结束坐标作为状态,然后使用draw
方法绘制自身。此外,当你想用橡皮擦点击一个形状时,你可以用IsHit
方法来确定一个形状是否被点击。这样,每个形状都负责自己的命中测试。例如,一条线可以实现一点模糊,这样你就可以点击线旁边的一点,而不必精确地点击一个像素
无论如何,这是总的想法。您可以根据需要将其扩展到其他想法。请注意,通过使用这种方法,您的核心代码不必了解任何可能的形状(如果您必须维护不断增长的不同形状的开关语句,则比较坐标可能会有点麻烦)。当然,对于绘制这些形状,您仍然需要更多的代码,因为线条可能需要与矩形、椭圆或文本对象不同的交互
我创建了一个小样本,概述了上述方法。有趣的部分如下:
interface IShape
{
Pen Pen { get; set; }
Brush Fill { get; set; }
void Draw(Graphics g);
bool IsHit(PointF p);
}
class Line : IShape
{
public Brush Fill { get; set; }
public Pen Pen { get; set; }
public PointF Start { get; set; }
public PointF End { get; set; }
public void Draw(Graphics g)
{
g.DrawLine(Pen, Start, End);
}
public bool IsHit(PointF p)
{
// Find distance to the end points
var d1 = Math.Sqrt((Start.X - p.X) * (Start.X - p.X) + (Start.Y - p.Y) * (Start.Y - p.Y));
var d2 = Math.Sqrt((End.X - p.X) * (End.X - p.X) + (End.Y - p.Y) * (End.Y - p.Y));
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
var dx = End.X - Start.X;
var dy = End.Y - Start.Y;
var length = Math.Sqrt(dx * dx + dy * dy);
var distance = Math.Abs(dy * p.X - dx * p.Y + End.X * Start.Y - End.Y * Start.X) / Math.Sqrt(dy * dy + dx * dx);
// Make sure the click was really near the line because the distance above also works beyond the end points
return distance < 3 && (d1 < length + 3 && d2 < length + 3);
}
}
public partial class Form1 : Form
{
private ObservableCollection<IShape> shapes = new ObservableCollection<IShape>();
private static Random random = new Random();
public Form1()
{
InitializeComponent();
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
shapes.CollectionChanged += Shapes_CollectionChanged;
}
private void Shapes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Redraw();
}
public void Redraw()
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
foreach (var shape in shapes)
{
shape.Draw(g);
}
}
pictureBox1.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
shapes.Add(new Line
{
Pen = Pens.Red,
Start = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height)),
End = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height))
});
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Redraw();
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
private void button1_MouseClick(object sender, MouseEventArgs e)
{
var shape = FindShape(e.Location);
if (shape != null)
{
shape.Pen = Pens.Blue;
Redraw();
}
}
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
点击图片将尝试在点击点找到一个形状,如果它找到一个,它将把它涂成蓝色(连同一个重画)。查找该项的工作方式如下:
interface IShape
{
Pen Pen { get; set; }
Brush Fill { get; set; }
void Draw(Graphics g);
bool IsHit(PointF p);
}
class Line : IShape
{
public Brush Fill { get; set; }
public Pen Pen { get; set; }
public PointF Start { get; set; }
public PointF End { get; set; }
public void Draw(Graphics g)
{
g.DrawLine(Pen, Start, End);
}
public bool IsHit(PointF p)
{
// Find distance to the end points
var d1 = Math.Sqrt((Start.X - p.X) * (Start.X - p.X) + (Start.Y - p.Y) * (Start.Y - p.Y));
var d2 = Math.Sqrt((End.X - p.X) * (End.X - p.X) + (End.Y - p.Y) * (End.Y - p.Y));
// https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
var dx = End.X - Start.X;
var dy = End.Y - Start.Y;
var length = Math.Sqrt(dx * dx + dy * dy);
var distance = Math.Abs(dy * p.X - dx * p.Y + End.X * Start.Y - End.Y * Start.X) / Math.Sqrt(dy * dy + dx * dx);
// Make sure the click was really near the line because the distance above also works beyond the end points
return distance < 3 && (d1 < length + 3 && d2 < length + 3);
}
}
public partial class Form1 : Form
{
private ObservableCollection<IShape> shapes = new ObservableCollection<IShape>();
private static Random random = new Random();
public Form1()
{
InitializeComponent();
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
shapes.CollectionChanged += Shapes_CollectionChanged;
}
private void Shapes_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Redraw();
}
public void Redraw()
{
using (var g = Graphics.FromImage(pictureBox1.Image))
{
foreach (var shape in shapes)
{
shape.Draw(g);
}
}
pictureBox1.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
shapes.Add(new Line
{
Pen = Pens.Red,
Start = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height)),
End = new PointF(random.Next(pictureBox1.Width), random.Next(pictureBox1.Height))
});
}
private void pictureBox1_SizeChanged(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Redraw();
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
private void button1_MouseClick(object sender, MouseEventArgs e)
{
var shape = FindShape(e.Location);
if (shape != null)
{
shape.Pen = Pens.Blue;
Redraw();
}
}
}
private IShape FindShape(PointF p)
{
// Reverse search order because we draw from bottom to top, but we need to hit-test from top to bottom.
foreach (var shape in shapes.Reverse())
{
if (shape.IsHit(p))
return shape;
}
return null;
}
正如你所看到的,画东西的基本部分,也许再选择一次,是很容易的。当然,不同的绘图工具是另一回事。有更简单的方法吗?也许,这取决于代码、存储形状的方式等。在没有看到代码的情况下,我无法确定什么是容易的,什么是困难的,所以这个问题最多不清楚您在问什么,或者