模拟喷雾

时间:2010-05-25 16:54:28

标签: c# math image-editing

我如何模拟像窗户喷漆一样的喷雾?我认为它随机创造点,你的意见是什么?

6 个答案:

答案 0 :(得分:5)

是的,我会说它会在选择点的某个半径范围内对随机像素进行着色。在一个像素和另一个像素的着色之间也可能存在时间延迟,因为今天的机器足够快,能够在放开鼠标按钮之前为每个可能的像素着色(只要半径很小)。 / p>

另外,我认为Paint使用的算法可以选择要绘制的像素,即使它已经被绘制过,因为有时你最终会得到一个带有几个未上漆像素的画圆。

答案 1 :(得分:3)

喷漆的图案是半随机的。如果你拿出一罐Krylon并慢慢地在墙上喷一条线,你最终会得到一条宽阔的实线,它会在边缘渐变成背景。在一个地方喷洒十秒钟,然后在中心得到一个颜色完全饱和的大点,背景为径向渐变。

所以 - 你的模拟变量包括:

  • 持有“喷雾器”(鼠标按钮)的时间
  • “can”(鼠标)的动作
  • “can”的速度(快速移动会形成一条轻而不饱和的线。慢速移动会产生一条带有渐变的粗糙饱和线)
  • 涂抹图案:喷雾是否像喷枪一样聚焦,或像喷雾罐一样大?
  • “距离”:“画布”中的“喷雾器”有多远?

答案 2 :(得分:2)

您已收到许多答案,指示您正确的方向开始处理喷涂效果的用户体验。根据您对我的评论的回应,您还需要一种算法来生成半径内的随机点。

有很多方法可以做到这一点,最明显的可能是使用polar coordinates选择随机点,然后将极坐标转换为笛卡尔坐标(x,y)来渲染像素。以下是此方法的一个简单示例。为了简单起见,我为每个点绘制了一个简单的1x1椭圆。

private Random _rnd = new Random();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{      
  int radius = 15;

  using (Graphics g = this.CreateGraphics())
  {
    for (int i = 0; i < 100; ++i)
    {
      // Select random Polar coordinate
      // where theta is a random angle between 0..2*PI
      // and r is a random value between 0..radius
      double theta = _rnd.NextDouble() * (Math.PI * 2);
      double r = _rnd.NextDouble() * radius;

      // Transform the polar coordinate to cartesian (x,y)
      // and translate the center to the current mouse position
      double x = e.X + Math.Cos(theta) * r;
      double y = e.Y + Math.Sin(theta) * r;

      g.DrawEllipse(Pens.Black, new Rectangle((int)x - 1, (int)y - 1, 1, 1));
    }
  }
}

或者,您可以从适合喷雾圆的矩形中随机选择x,y坐标,并使用圆方程r ^ 2 = x ^ 2 + y ^ 2测试该点以确定它是否位于圆内,如果它是你随机选择另一个点并再次测试,直到你有一个位于圆圈内的点。以下是此方法的快速示例

private Random _rnd = new Random();
private void Form1_MouseDown(object sender, MouseEventArgs e)
{      
  int radius = 15;
  int radius2 = radius * 2;

  using (Graphics g = this.CreateGraphics())
  {
    double x;
    double y;

    for (int i = 0; i < 100; ++i)
    {          
      do 
      {
        // Randomy select x,y so that 
        // x falls between -radius..radius
        // y falls between -radius..radius
        x = (_rnd.NextDouble() * radius2) - radius;
        y = (_rnd.NextDouble() * radius2) - radius;

        // If x^2 + y^2 > r2 the point is outside the circle
        // and a new point needs to be selected
      } while ((x*x + y*y) > (radius * radius));

      // Translate the point so that the center is at the mouse
      // position
      x += e.X;
      y += e.Y;

      g.DrawEllipse(Pens.Black, new Rectangle((int)x - 1, (int)y - 1, 1, 1));
    }
  }
}

答案 3 :(得分:0)

您可以通过采样极坐标的某些数字(与所需的强度和扩展相关)来创建各种强度的喷雾模式。为此,通过以下方法确定每个样本的随机极坐标(ρ,θ):

ρ从N(0,1)采样:使用Normal (Gaussian) distribution表示距离喷雾模式的精确中心的距离。我不记得.NET库中是否有正常的variate生成器。如果没有,您可以create one from a U(0, 1) generator

从U(0,π)采样的θ:Uniform Continuous Distribution采样角度分量。在不损失性能或一般性的情况下,您可以在U( n π, m π)上采样 n &lt; m ,但U(0,π)可能会满足您的需求。

每个样本的笛卡尔坐标由(T x + S x ρcosθ,T y + S 给出y ρsinθ)其中(T x ,T y )是您想要创建的喷雾模式的中心; S x 和S y 分别是您想要在x和y方向上的扩展因子。

答案 4 :(得分:0)

尝试使用计时器

public partial class Form1 : Form
{
    int Radious = 5;
    Random _rnd = new Random();
    Timer T = new Timer();
    int InterVal = 1000;
    MouseEventArgs MEA = null;
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        T.Tick += (O, E) =>
        {
            StartSpray();
        };
        this.MouseDown += (O, E) =>
        {
            MEA = E;
            T.Interval = InterVal;
            T.Start();

         };
        this.MouseUp += (O, E) =>
        {
            T.Stop();
        };
    }
    private void StartSpray()
    {
        Point P = DrawPoint(Radious, MEA.X, MEA.Y);
        // Draw the point on any graphics area you can add the color or anything else
    }
    private Point DrawPoint(int Radious, int StatX, int StartY)
    {
        double theta = _rnd.NextDouble() * (Math.PI * 2);
        double r = _rnd.NextDouble() * Radious;
        Point P = new Point { X = StatX + Convert.ToInt32(Math.Cos(theta) * r), Y = StartY + Convert.ToInt32(Math.Sin(theta) * r) };
        return P;
    }      
}

请修改间隔和半径。

答案 5 :(得分:-1)

我认为很难在C#上找到样本。下面我介绍一种开始你的旅程的方法。我在这里使用纹理画笔。

private void Button1_Click(System.Object sender, System.EventArgs e)
{
    try
    {
        Bitmap image1 = (Bitmap)Image.FromFile(@"C:\temp\mybrush.bmp", true);

        TextureBrush t = new TextureBrush(image1);
        t.WrapMode = System.Drawing.Drawing2D.WrapMode.Tile;
        Graphics formGraphics = this.CreateGraphics();
        formGraphics.FillEllipse(t, new RectangleF(90.0F, 110.0F, 100, 100));
        formGraphics.Dispose();

    }
    catch (System.IO.FileNotFoundException)
    {
        MessageBox.Show("Image file not found!");
    }

}

正如杰西所说,我认为你应该找到一种扩散随机像素的算法。