如何使用 openGL 和贝塞尔曲线创建拼图块?
How to create jigsaw puzzle pieces using openGL and bezier curve?
我正在尝试创建一个拼图演示,我想知道在不使用遮罩的情况下创建拼图的替代方法。目前,我通过拍摄完整图像、将该图像分成四块(假设拼图是 2x2)然后存储并为每块应用蒙版来制作拼图块。看起来像下面
// create standard puzzle pieces
arryPieceEndPos = new int[mCols][mRows];
arryPieceImg = new Bitmap[mCols * mRows];
arryIsPieceLocked = new boolean[mCols * mRows];
int pos = 0;
for (int c = 0; c < mCols; c++) {
for (int r = 0; r < mRows; r++) {
arryPieceImg[pos] = Bitmap.createBitmap(mBitmap,
c * mPieceWidth, r * mPieceHeight,
mPieceWidth, mPieceHeight);
arryIsPieceLocked[pos] = false;
arryPieceEndPos[c][r] = pos;
pos++;
}
}
然后我使用辅助方法为每件作品应用蒙版
private Bitmap maskMethod(Bitmap bmpOriginal, Bitmap bmpMask) {
// adjust mask bitmap if size is not the size of the puzzle piece
if (bmpMask.getHeight() != mPieceHeight ||
bmpMask.getWidth() != mPieceWidth) {
Log.e("TEST", "Resize Error :: H (mask): " + bmpMask.getHeight() + " // W (mask): " +
bmpMask.getWidth());
Log.d("TEST", "Resize Error :: H (norm): " + mPieceHeight + " // W (norm): " +
mPieceWidth);
}
Canvas canvas = new Canvas();
Bitmap combine = Bitmap.createBitmap(bmpOriginal.getWidth(), bmpOriginal.getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(combine);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.drawBitmap(bmpOriginal, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(bmpMask, 0, 0, paint);
paint.setXfermode(null);
return combine;
}
我一直在阅读贝塞尔曲线和 openGL。这些我都不是很熟悉,而且它们非常复杂。我需要一些拼图方面的帮助,以便我可以举例说明如何完成。
Bezier 曲线可能很复杂,但我们可以 "cheat" 通过先使用 Catmul-Rom 曲线定义拼图头(它有很好的 属性 实际上通过它们的控制点)然后将它们简单地转换为贝塞尔曲线(因为它们都是普通的 Hermite 样条曲线)。
那么:让我们开始吧。示例图片:
到目前为止,我们所做的只是使用相当简单的规则将其拆分。在代码排序中(技术上:Processing):
int hx = width/4;
int hy = height/4;
for(int x = hx; x<width; x+=hx) {
line(x,0,x,height);
for(int y = hy; y<height; y+=hy) {
line(0,y,width,y);
}
}
除了有趣的图像之外,它不是很令人兴奋,所以让我们发明一些拼图连接。首先,我们标记这些切口的中心:
又不是很令人兴奋:
for(int x = hx/2; x<width; x+=hx) {
for(int y = hy/2; y<height; y+=hy) {
ellipse(x + hx/2,y,5,5);
ellipse(x,y + hy/2,5,5);
}
}
但是,我们可以让它变得令人兴奋。对于这些中心中的每一个,我们可以选择指向 left/right 或 up/down 的点(取决于边缘)并决定是否让棋子向左或向右延伸,然后发明一些点 "around the center" 给我们我们的 Catmull-Rom 曲线:
for(int x = hx/2; x<width; x+=hx) {
for(int y = hy/2; y<height; y+=hy) {
// horizontal
ellipse(x-5, y+hy/2, 2,2);
ellipse(x+5, y+hy/2, 2,2);
boolean up = random(1) < 0.5;
if(up) {
ellipse(x-random(5,10), y+hy/2 - random(10,20), 2,2);
ellipse(x+random(5,10), y+hy/2 - random(10,20), 2,2);
} else {
ellipse(x-random(5,10), y+hy/2 + random(10,20), 2,2);
ellipse(x+random(5,10), y+hy/2 + random(10,20), 2,2);
}
// vertical
ellipse(x+hx/2, y-5, 2,2);
ellipse(x+hx/2, y+5, 2,2);
boolean left = random(1) < 0.5;
if(left) {
ellipse(x+hx/2-random(10,20), y-random(5,10), 2,2);
ellipse(x+hx/2-random(10,20), y+random(5,10), 2,2);
} else {
ellipse(x+hx/2+random(10,20), y-random(5,10), 2,2);
ellipse(x+hx/2+random(10,20), y+random(5,10), 2,2);
}
}
}
我们在这里过度生成,所以我会留给您解决如何防止 "piece joiner" 坐标被计算为最右边和最下面的边缘(应该相当容易)。
现在:让我们把它变成一个坐标网格,因为它看起来很不错,我们应该使用 Catmull-Rom 得到非常好的拼接器:
美女
for (int x = hx/2; x<width; x+=hx) {
for (int y = hy/2; y<height; y+=hy) {
// horizontal
int xs = x-hx/2,
ym = y+hy/2,
xe = x+hx/2;
float x3, x4, y1, y2,
x1 = x-5,
x2 = x+5;
boolean up = random(1) < 0.5;
x3 = x - random(5, 10);
x4 = x + random(5, 10);
if (up) {
y1 = y+hy/2 - random(10, 20);
y2 = y+hy/2 - random(10, 20);
} else {
y1 = y+hy/2 + random(10, 20);
y2 = y+hy/2 + random(10, 20);
}
curve(xs, ym, x1, ym, x3, y1, x4, y2);
curve(x1, ym, x3, y1, x4, y2, x2, ym);
curve(x3, y1, x4, y2, x2, ym, xe, ym);
// vertical
int ys = y-hy/2,
xm = x+hx/2,
ye = y+hy/2;
y1 = y-5;
y2 = y+5;
float y3, y4;
boolean left = random(1) < 0.5;
y3 = y - random(5, 10);
y4 = y + random(5, 10);
if (left) {
x1 = x+hx/2 - random(10, 20);
x2 = x+hx/2 - random(10, 20);
} else {
x1 = x+hx/2 + random(10, 20);
x2 = x+hx/2 + random(10, 20);
}
curve(xm, ys, xm, y1, x1, y3, x2, y4);
curve(xm, y1, x1, y3, x2, y4, xm, y2);
curve(x1, y3, x2, y4, xm, y2, xm, ye);
}
}
现在应该相对明显地需要在何处执行切割以结束这些片段,但是如果您正在使用不能执行 Catmull-Rom 但只能执行贝塞尔曲线的系统,转换真的很简单。前面的代码一直在使用
curve(x1,x2,y1,y2,x3,y3,x4,y4);
但这是一条 Catmull-Rom 曲线。要获得等效曲线,we can use a Bezier segment of the form:
bezier(
x2, y2,
x2 - (x3-x1)/6, y2 - (y3-y1)/6,
x3 + (x4-x2)/6, y3 + (y4-y2)/6,
x3, y3
)
让我们开始困惑。
我正在尝试创建一个拼图演示,我想知道在不使用遮罩的情况下创建拼图的替代方法。目前,我通过拍摄完整图像、将该图像分成四块(假设拼图是 2x2)然后存储并为每块应用蒙版来制作拼图块。看起来像下面
// create standard puzzle pieces
arryPieceEndPos = new int[mCols][mRows];
arryPieceImg = new Bitmap[mCols * mRows];
arryIsPieceLocked = new boolean[mCols * mRows];
int pos = 0;
for (int c = 0; c < mCols; c++) {
for (int r = 0; r < mRows; r++) {
arryPieceImg[pos] = Bitmap.createBitmap(mBitmap,
c * mPieceWidth, r * mPieceHeight,
mPieceWidth, mPieceHeight);
arryIsPieceLocked[pos] = false;
arryPieceEndPos[c][r] = pos;
pos++;
}
}
然后我使用辅助方法为每件作品应用蒙版
private Bitmap maskMethod(Bitmap bmpOriginal, Bitmap bmpMask) {
// adjust mask bitmap if size is not the size of the puzzle piece
if (bmpMask.getHeight() != mPieceHeight ||
bmpMask.getWidth() != mPieceWidth) {
Log.e("TEST", "Resize Error :: H (mask): " + bmpMask.getHeight() + " // W (mask): " +
bmpMask.getWidth());
Log.d("TEST", "Resize Error :: H (norm): " + mPieceHeight + " // W (norm): " +
mPieceWidth);
}
Canvas canvas = new Canvas();
Bitmap combine = Bitmap.createBitmap(bmpOriginal.getWidth(), bmpOriginal.getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(combine);
Paint paint = new Paint();
paint.setFilterBitmap(false);
canvas.drawBitmap(bmpOriginal, 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(bmpMask, 0, 0, paint);
paint.setXfermode(null);
return combine;
}
我一直在阅读贝塞尔曲线和 openGL。这些我都不是很熟悉,而且它们非常复杂。我需要一些拼图方面的帮助,以便我可以举例说明如何完成。
Bezier 曲线可能很复杂,但我们可以 "cheat" 通过先使用 Catmul-Rom 曲线定义拼图头(它有很好的 属性 实际上通过它们的控制点)然后将它们简单地转换为贝塞尔曲线(因为它们都是普通的 Hermite 样条曲线)。
那么:让我们开始吧。示例图片:
到目前为止,我们所做的只是使用相当简单的规则将其拆分。在代码排序中(技术上:Processing):
int hx = width/4;
int hy = height/4;
for(int x = hx; x<width; x+=hx) {
line(x,0,x,height);
for(int y = hy; y<height; y+=hy) {
line(0,y,width,y);
}
}
除了有趣的图像之外,它不是很令人兴奋,所以让我们发明一些拼图连接。首先,我们标记这些切口的中心:
又不是很令人兴奋:
for(int x = hx/2; x<width; x+=hx) {
for(int y = hy/2; y<height; y+=hy) {
ellipse(x + hx/2,y,5,5);
ellipse(x,y + hy/2,5,5);
}
}
但是,我们可以让它变得令人兴奋。对于这些中心中的每一个,我们可以选择指向 left/right 或 up/down 的点(取决于边缘)并决定是否让棋子向左或向右延伸,然后发明一些点 "around the center" 给我们我们的 Catmull-Rom 曲线:
for(int x = hx/2; x<width; x+=hx) {
for(int y = hy/2; y<height; y+=hy) {
// horizontal
ellipse(x-5, y+hy/2, 2,2);
ellipse(x+5, y+hy/2, 2,2);
boolean up = random(1) < 0.5;
if(up) {
ellipse(x-random(5,10), y+hy/2 - random(10,20), 2,2);
ellipse(x+random(5,10), y+hy/2 - random(10,20), 2,2);
} else {
ellipse(x-random(5,10), y+hy/2 + random(10,20), 2,2);
ellipse(x+random(5,10), y+hy/2 + random(10,20), 2,2);
}
// vertical
ellipse(x+hx/2, y-5, 2,2);
ellipse(x+hx/2, y+5, 2,2);
boolean left = random(1) < 0.5;
if(left) {
ellipse(x+hx/2-random(10,20), y-random(5,10), 2,2);
ellipse(x+hx/2-random(10,20), y+random(5,10), 2,2);
} else {
ellipse(x+hx/2+random(10,20), y-random(5,10), 2,2);
ellipse(x+hx/2+random(10,20), y+random(5,10), 2,2);
}
}
}
我们在这里过度生成,所以我会留给您解决如何防止 "piece joiner" 坐标被计算为最右边和最下面的边缘(应该相当容易)。
现在:让我们把它变成一个坐标网格,因为它看起来很不错,我们应该使用 Catmull-Rom 得到非常好的拼接器:
美女
for (int x = hx/2; x<width; x+=hx) {
for (int y = hy/2; y<height; y+=hy) {
// horizontal
int xs = x-hx/2,
ym = y+hy/2,
xe = x+hx/2;
float x3, x4, y1, y2,
x1 = x-5,
x2 = x+5;
boolean up = random(1) < 0.5;
x3 = x - random(5, 10);
x4 = x + random(5, 10);
if (up) {
y1 = y+hy/2 - random(10, 20);
y2 = y+hy/2 - random(10, 20);
} else {
y1 = y+hy/2 + random(10, 20);
y2 = y+hy/2 + random(10, 20);
}
curve(xs, ym, x1, ym, x3, y1, x4, y2);
curve(x1, ym, x3, y1, x4, y2, x2, ym);
curve(x3, y1, x4, y2, x2, ym, xe, ym);
// vertical
int ys = y-hy/2,
xm = x+hx/2,
ye = y+hy/2;
y1 = y-5;
y2 = y+5;
float y3, y4;
boolean left = random(1) < 0.5;
y3 = y - random(5, 10);
y4 = y + random(5, 10);
if (left) {
x1 = x+hx/2 - random(10, 20);
x2 = x+hx/2 - random(10, 20);
} else {
x1 = x+hx/2 + random(10, 20);
x2 = x+hx/2 + random(10, 20);
}
curve(xm, ys, xm, y1, x1, y3, x2, y4);
curve(xm, y1, x1, y3, x2, y4, xm, y2);
curve(x1, y3, x2, y4, xm, y2, xm, ye);
}
}
现在应该相对明显地需要在何处执行切割以结束这些片段,但是如果您正在使用不能执行 Catmull-Rom 但只能执行贝塞尔曲线的系统,转换真的很简单。前面的代码一直在使用
curve(x1,x2,y1,y2,x3,y3,x4,y4);
但这是一条 Catmull-Rom 曲线。要获得等效曲线,we can use a Bezier segment of the form:
bezier(
x2, y2,
x2 - (x3-x1)/6, y2 - (y3-y1)/6,
x3 + (x4-x2)/6, y3 + (y4-y2)/6,
x3, y3
)
让我们开始困惑。