Android如何判斷若干個小矩形可不可以被放到一個大矩形中

最近遇到一個問題,如何判斷若干個小的矩形可不可以被放到一個大矩形中,小矩形之間不重疊。

舉個例子,現在有一個66 的矩形,我們可不可以在其中放入兩個22 ,兩個23 ,一個21 和 一個33 的矩形?
從面積上講, 222+223+21+33<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軸的長度一致的一塊矩形,沒有重疊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章