Java 8 + Swing: How to Draw Flush Polygons
(很抱歉,这封长信…至少有照片?)
我已经写了一个算法,通过统计生成n个凸多边形来从一个图像创建一个马赛克,这些凸多边形覆盖了没有重叠的图像。这些多边形有3-8个边,每边有一个45度的倍数角。这些多边形内部存储为一个矩形,每个角都有位移。下面是一张图片,说明了这是如何工作的:
虽然用数学方法表示这些区域很好,但我想画出它们,这样我就能看到它们。使用简单的lambda和迭代多边形中每个像素的方法,我可以完美地渲染图像。举个例子,下面是克劳德·莫奈(ClaudeMonet)的女人,她用一把阳伞,使用99个多边形,允许所有方向的分裂。
呈现此图像的代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public void drawOnto(Graphics graphics) { graphics.setColor(getColor()); forEach( (i, j) -> { graphics.fillRect(x + i, y + j, 1, 1); } ); } private void forEach(PerPixel algorithm) { for (int j = 0; j < height; ++j) { int nj = height - 1 - j; int minX; if (j < ul) { minX = ul - j; } else if (nj < ll) { minX = ll - nj; } else { minX = 0; } int maxX = width; if (j < ur) { maxX -= ur - j; } else if (nj < lr) { maxX -= lr - nj; } for (int i = minX; i < maxX; ++i) { algorithm.perform(i, j); } } } |
然而,由于许多原因,这并不理想。首先,图形化表示多边形的概念现在是类本身的一部分;最好允许其他关注点是表示这些多边形的类。第二,这需要许多许多调用
所有这些都指向生成一个表示多边形顶点的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public Polygon getPolygon() { int[] xes = { x + ul, getRight() - ur, getRight(), getRight(), getRight() - lr, x + ll, x, x }; int[] yes = { y, y, y + ur, getBottom() - lr, getBottom(), getBottom(), getBottom() - ll, y + ul }; int[] keptXes = new int[8]; int[] keptYes = new int[8]; int length = 0; for (int i = 0; i < 8; ++i) { if ( length == 0 || keptXes[length - 1] != xes[i] || keptYes[length - 1] != yes[i] ) { keptXes[length] = xes[i]; keptYes[length] = yes[i]; length++; } } return new Polygon(keptXes, keptYes, length); } |
问题是,当我尝试使用这样一个
所以我有一些关于这种行为的相关问题:
为什么
我如何在渲染器中一致地围绕这个缺陷(就我的应用程序而言)进行编码,以便我可以像现在一样使用我的
MCE
如果上面的代码片段和图片不足以帮助解释这个问题,那么我添加了一个最小的、完整的、可验证的示例来演示我上面描述的行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | package com.sadakatsu.mce; import java.awt.Color; import java.awt.Graphics; import java.awt.Polygon; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class Main { @FunctionalInterface private static interface PerPixel { void perform(int x, int y); } private static class Region { private int height; private int ll; private int lr; private int width; private int ul; private int ur; private int x; private int y; public Region( int x, int y, int width, int height, int ul, int ur, int ll, int lr ) { if ( width < 0 || width <= ll + lr || width <= ul + ur || height < 0 || height <= ul + ll || height <= ur + lr || ul < 0 || ur < 0 || ll < 0 || lr < 0 ) { throw new IllegalArgumentException(); } this.height = height; this.ll = ll; this.lr = lr; this.width = width; this.ul = ul; this.ur = ur; this.x = x; this.y = y; } public Color getColor() { return Color.BLACK; } public int getBottom() { return y + height - 1; } public int getRight() { return x + width - 1; } public Polygon getPolygon() { int[] xes = { x + ul, getRight() - ur, getRight(), getRight(), getRight() - lr, x + ll, x, x }; int[] yes = { y, y, y + ur, getBottom() - lr, getBottom(), getBottom(), getBottom() - ll, y + ul }; int[] keptXes = new int[8]; int[] keptYes = new int[8]; int length = 0; for (int i = 0; i < 8; ++i) { if ( length == 0 || keptXes[length - 1] != xes[i] || keptYes[length - 1] != yes[i] ) { keptXes[length] = xes[i]; keptYes[length] = yes[i]; length++; } } return new Polygon(keptXes, keptYes, length); } public void drawOnto(Graphics graphics) { graphics.setColor(getColor()); forEach( (i, j) -> { graphics.fillRect(x + i, y + j, 1, 1); } ); } private void forEach(PerPixel algorithm) { for (int j = 0; j < height; ++j) { int nj = height - 1 - j; int minX; if (j < ul) { minX = ul - j; } else if (nj < ll) { minX = ll - nj; } else { minX = 0; } int maxX = width; if (j < ur) { maxX -= ur - j; } else if (nj < lr) { maxX -= lr - nj; } for (int i = minX; i < maxX; ++i) { algorithm.perform(i, j); } } } } public static void main(String[] args) throws IOException { int width = 10; int height = 8; Region region = new Region(0, 0, 10, 8, 2, 3, 4, 1); BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR ); Graphics graphics = image.getGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, width, height); region.drawOnto(graphics); ImageIO.write(image,"PNG", new File("expected.png")); image = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR ); graphics = image.getGraphics(); graphics.setColor(Color.WHITE); graphics.fillRect(0, 0, width, height); graphics.setColor(Color.BLACK); graphics.fillPolygon(region.getPolygon()); ImageIO.write(image,"PNG", new File("got.png")); } } |
我花了一整天的时间研究它,我似乎对此有了一个解决办法。线索是在
Definition of insideness: A point is considered to lie inside a Shape if and only if:
it lies completely inside theShape boundary or
it lies exactly on the Shape boundary and the space immediately adjacent to the point in the increasing X direction is entirely inside the boundary or
it lies exactly on a horizontal boundary segment and the space immediately adjacent to the point in the increasing Y direction is inside the boundary.
实际上,此文本有点误导性;第三种情况会覆盖第二种情况(即,即使
红色、绿色和蓝色像素是
最简单的方法是使用CAMICKR的方法:使用EDCOX1,4,EDCX1,5。至少在我的45度多棱凸壳的情况下,
我在试图理解上面的内部性定义时开发的实际解决方案是创建一个不同的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | package com.sadakatsu.mosaic.renderer; import java.awt.Polygon; import java.util.Arrays; import com.sadakatsu.mosaic.Region; public class RegionPolygon extends Polygon { public RegionPolygon(Region region) { int bottom = region.getBottom(); int ll = region.getLL(); int lr = region.getLR(); int right = region.getRight(); int ul = region.getUL(); int ur = region.getUR(); int x = region.getX(); int y = region.getY(); int[] xes = { x + ul, right - ur + 1, right + 1, right + 1, right - lr, x + ll + 1, x, x }; int[] yes = { y, y, y + ur, bottom - lr, bottom + 1, bottom + 1, bottom - ll, y + ul }; npoints = 0; xpoints = new int[xes.length]; ypoints = new int[xes.length]; for (int i = 0; i < xes.length; ++i) { if ( i == 0 || xpoints[npoints - 1] != xes[i] || ypoints[npoints - 1] != yes[i] ) { addPoint(xes[i], yes[i]); } } } } |