Windowsペイントのようなスプレーをシミュレートするにはどうすればよいですか?ランダムにポイントが出ると思いますが、どう思いますか?
6 に答える
はい、選択ポイントの特定の半径内のランダムなピクセルに色を付けると思います。また、現在のマシンは (半径が小さい限り) マウス ボタンを放す前に可能なすべてのピクセルに色を付けることができるほど高速であるため、1 つのピクセルの色付けと別のピクセルの色付けの間にはおそらく時間の遅延があります。
また、ペイントが使用するアルゴリズムは、ペイント済みのピクセルであってもペイントするピクセルを選択できると思います。
スプレーペイントのパターンは半ランダムになります。Krylonの缶を取り出して壁にゆっくりと線をスプレーすると、幅の広い実線が背景にフェードアウトし、エッジの周りにグラデーションが表示されます。1つのスポットに10秒間スプレーすると、中央に大きなドットが表示され、背景に放射状のグラデーションが付いた、色が完全に飽和します。
したがって、シミュレーションの変数は次のとおりです。
- 「噴霧器」(マウスボタン)を持っている時間
- 「缶」(マウス)の動き
- 「缶」の速度(速い動きは軽くて不飽和の線を作ります。遅い動きはグラデーションのある太くて飽和した線を作ります)
- 広がりパターン:スプレーはエアブラシのように焦点を合わせていますか、それともスプレー缶のように大きいですか?
- 「距離」:「噴霧器」は「帆布」からどれくらい離れていますか?
スプレー効果のユーザー エクスペリエンスの処理を開始するための正しい方向性を示す多くの回答を受け取りました。私のコメントへの返信に基づいて、半径内のランダムなポイントを生成するためのアルゴリズムも必要です。
これを行うにはいくつかの方法がありますが、おそらく最も明白な方法は、 極座標を使用してランダムな点を選択し、極座標をデカルト (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));
}
}
}
いくつかの極座標 (目的の強度と広がりに関連する) をサンプリングすることにより、さまざまな強度のスプレー パターンを作成できます。これを行うには、各サンプルのランダムな極座標 (ρ, θ) を次のように決定します。
N(0, 1) からサンプリングされた ρ:スプレー パターンの正確な中心からの距離に正規 (ガウス) 分布を使用します。.NET ライブラリに通常の変量ジェネレーターがあるかどうかは覚えていません。ない場合は、 U(0, 1) generator から作成できます。
U(0, π) からサンプリングされた θ:一様連続分布から角度成分をサンプリングします。パフォーマンスや一般性を失うことなく、代わりに U( n π, m π) でn < mをサンプリングすることもできますが、必要なものにはおそらく U(0, π) で十分です。
各サンプルのデカルト座標は (T x + S x ρ cos θ, T y + S y ρ sin θ) で与えられます。ここで、(T x , T y ) は作成するスプレー パターンの中心です。S xと S yは、それぞれ x 方向と y 方向に必要な拡散係数です。
タイマーを使ってみる
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;
}
}
間隔と半径を変更してください。
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!");
}
}
Jesse が言ったように、ランダムなピクセルを拡散するアルゴリズムを見つける必要があると思います。