最近遇到一个问题,如何判断若干个小的矩形可不可以被放到一个大矩形中,小矩形之间不重叠。
举个例子,现在有一个
6∗6
的矩形,我们可不可以在其中放入两个
2∗2
,两个
2∗3
,一个
2∗1
和 一个
3∗3
的矩形?
从面积上讲,
2∗2∗2+2∗2∗3+2∗1+3∗3<36
,但是矩形之间不能有重叠,旋转和变形,那最终能不能放下就不得而知了。
最终得到一个方案就是使用Region。
Region有一个好处,可以在上边进行区域的操作,简单来讲就是可以挖各种各样的洞出来。直接上代码吧:
class Widget {
public Widget(int spanX, int spanY) {
mSpanX = spanX;
mSpanY = spanY;
}
public boolean mAlreadyPut;
public int mSpanX; //宽度
public int mSpanY; //高度
public Region mRegion = new Region();
}
//Region region表示大的区域
private boolean predict(Region region, ArrayList<Widget> widgets, int level) {
if (level == widgets.size()) {
return true;
}
Rect rect = new Rect();
for (int i = 0; i < widgets.size(); i++) {
Widget widget = widgets.get(i);
if (!widget.mAlreadyPut) {
widget.mAlreadyPut = true;
RegionIterator regionIterator = new RegionIterator(region);
while (regionIterator.next(rect)) {
if (rect.right - rect.left >= widget.mSpanX && rect.bottom - rect.top >= widget.mSpanY) {
widget.mRegion.set(rect.left, rect.top, rect.left + widget.mSpanX, rect.top + widget.mSpanY);
region.op(widget.mRegion, Region.Op.DIFFERENCE);
boolean ret = predict(region, widgets, level + 1);
if (ret) {
return true;
}
region.op(widget.mRegion, Region.Op.UNION);
}
}
widget.mAlreadyPut = false;
}
}
return false;
}
简单解释一下,这个算法是通过递归的方式来判断的,将一个矩形放下后,设置它的mAlreadyPut为true,然后将它的region从大的region中裁剪出去,用这个“带着洞”的region进行递归。
中间涉及到RegionIterator,它是用来遍历一个Region,返回其中包含的矩形,如果其中的某一个矩形可以容纳我们将要放下的矩形,则可以在其中放置。
RegionIterator
一个带着洞的Region,是如何返回它里面包含的矩形区域的,这个是一个问题,如名字所示,它是一个Iterator,那遍历出来的这些矩形,到底是什么样子的是一个未知。如下图,左上角有个小矩形被裁剪出来,剩余区域到底是如何返回矩形,这是个问题。
我看了一下代码,不过region的代码进入到了skia库中,太长了,没有继续分析,不过做实验测试一下开始可以的。于是写了个小程序:
public class RegionImageView extends ImageView {
public RegionImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
Region mRegion = new Region();
Rect mRect = new Rect();
Paint mPaint = new Paint();
Region mCut = new Region();
Region mCut1 = new Region();
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
getDrawingRect(mRect);
mRegion.set(mRect);
mCut.set(0,0,600,600);
mCut1.set(0,600,800,1000);
mRegion.op(mCut, Region.Op.DIFFERENCE);
mRegion.op(mCut1, Region.Op.DIFFERENCE);
RegionIterator regionIterator = new RegionIterator(mRegion);
while (regionIterator.next(mRect)) {
Log.d("RegionImageView", mRect.toString());
setRandomColorToPaint();
canvas.drawRect(mRect, mPaint);
}
}
private void setRandomColorToPaint() {
int color = Color.argb(255, (int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255));
Log.d("RegionImageView", "color:"+color);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.FILL);
}
}
可以看到运行结果,我们可以猜测,Iterator取区域的时候,是让从上到下,x轴的长度一致的一块矩形,没有重叠。