切成矩形 [英] Slicing up a Rectangle

查看:89
本文介绍了切成矩形的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要从接收其他矩形作为参数的函数中获取AS3矩形对象.结果与Photoshop中的切片工具非常相似.很难解释,所以这是一张图片:

I need to get AS3 Rectangle objects from a function receiving other Rectangles as parameters. The result is very similar to the slice tool in Photoshop. It is quite hard to explain, so here is a picture:


(来源: free.fr )


(source: free.fr)

蓝色正方形是作为参数给出的矩形,绿色是结果.如图2所示,给定的矩形可以重叠,也可以不合框架.

The blue squares are the rectangles that are given as parameters and the green ones are the result. Given Rectangles can overlap, as seen on picture 2 or be out of frame.

我不是在寻找图形实现,而是在寻找一种将Rectangle对象作为结果的方法.

I don't look for a graphical realisation but for a way to get Rectangle objects as result.

您知道有没有这样做的库吗?

Do you know any lib to do that?

推荐答案

看起来像一个有趣的问题,所以我给了它一个破解.我的想法是通过以下方式强行将其强行使用:

Looked like a fun problem, so I gave it a crack. My idea was to just brute force it by:

  1. 确定生成的矩形 的角在哪里.
  2. 从此点列表中删除所有重复项.
  3. 检查理论上可以绘制的所有矩形,其中rect在点列表中具有所有4个角.
  4. 过滤掉所有无效的矩形(它与原始矩形之一相交,等等)
  5. 将所有有效矩形减少到所需的最小数量(如果一个有效矩形包含另一个有效矩形,则将删除子".
  1. Determine which points where the corners of the generated rectangles could be.
  2. Remove all duplicates from this list of points.
  3. Check all rectangles that could theoretically be drawn where the rect would have all 4 corners in the list of point.
  4. Filter out all invalid rectangles (it intersects with one of our original rectangles etc.)
  5. Reduce all valid rectangles to the minimum amount needed (if a valid rectangle contains another valid rectangle the "child" is removed.

它似乎可以工作(尽管我还没有进行广泛的测试).

It seems to work (although I haven't tested extensively).

这是演示.对不起调色板.我在飞.

Here's a demo. Sorry about the color palette. I was winging it.

这是源代码(可能会进行很多优化):

Here's the source code (could probably be optimized quite a bit):

package 
{
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.getTimer;

    public class Main extends Sprite {

        private var m_colors : Array = [0xffaaaa, 0x77ff77, 0xaaaaff, 0xffff44, 0xff44ff, 0xaaffff, 0x444444, 0xffaa55, 0xaaff55, 0x55aaff, 0x55ffaa];
        private var m_roomRect : Rectangle;
        private var m_sourceRects : Vector.<Rectangle> = new Vector.<Rectangle>();
        private var m_currentDragRect : Rectangle;
        private var m_dragMousePoint : Point = new Point();
        private var m_outputTextField : TextField;

        public function Main() : void {
            m_roomRect = new Rectangle(40, 40, 400, 400);

            m_sourceRects.push(new Rectangle(60, 60, 60, 80));
            m_sourceRects.push(new Rectangle(130, 220, 70, 80));
            m_sourceRects.push(new Rectangle(160, 260, 100, 80));

            this.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
            this.stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
            this.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);

            var tf : TextField = new TextField();
            tf.defaultTextFormat = new TextFormat("_sans", 12);
            tf.text = "Click and drag blue rectangles to move them";
            tf.autoSize = TextFieldAutoSize.LEFT;
            tf.x = (m_roomRect.left + m_roomRect.right) / 2 - tf.width / 2;
            tf.y = m_roomRect.top - tf.height;
            this.stage.addChild(tf);

            m_outputTextField = new TextField();
            m_outputTextField.defaultTextFormat = tf.defaultTextFormat;
            m_outputTextField.width = m_roomRect.width;
            m_outputTextField.x = m_roomRect.x;
            m_outputTextField.y = m_roomRect.bottom + 5;
            this.stage.addChild(m_outputTextField);

            redraw();
        }

        private function onMouseEvent(event : MouseEvent):void {
            switch(event.type) {
                case MouseEvent.MOUSE_DOWN:
                    checkMouseDownOnRect();
                    break;
                case MouseEvent.MOUSE_MOVE:
                    checkMouseDrag();
                    break;
                case MouseEvent.MOUSE_UP:
                    m_currentDragRect = null;
                    break;
            }
        }

        private function checkMouseDownOnRect():void {
            m_currentDragRect = null;
            m_dragMousePoint = new Point(this.stage.mouseX, this.stage.mouseY);

            for each(var sourceRect : Rectangle in m_sourceRects) {
                if (sourceRect.containsPoint(m_dragMousePoint)) {
                    m_currentDragRect = sourceRect;
                    break;
                }
            }
        }

        private function checkMouseDrag():void {
            if (m_currentDragRect != null) {
                m_currentDragRect.x += this.stage.mouseX - m_dragMousePoint.x;
                m_currentDragRect.y += this.stage.mouseY - m_dragMousePoint.y;
                m_dragMousePoint.x = this.stage.mouseX;
                m_dragMousePoint.y = this.stage.mouseY;
                redraw();
            }
        }

        private function redraw():void {
            // calculate data
            var time : int = getTimer();
            var data : CalculationData = calculate();
            var calcTime : int = getTimer() - time;

            // draw room bounds
            this.graphics.clear();
            this.graphics.lineStyle(3, 0x0);
            this.graphics.drawRect(m_roomRect.x, m_roomRect.y, m_roomRect.width, m_roomRect.height);

            // draw generated rectangles
            for (var i : int = 0; i < data.outputRects.length; i++) {
                var color : int = m_colors[i % m_colors.length];
                var rect : Rectangle = data.outputRects[i];
                this.graphics.lineStyle(2, color, 0.5);
                this.graphics.beginFill(color, 0.5);
                this.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
                this.graphics.endFill();
            }

            // draw horisontal lines (a line that crosses each red point) for debug purposes
            for each (var lineY : int in data.lines) {
                this.graphics.lineStyle(1, 0, 0.2);
                this.graphics.moveTo(m_roomRect.x, lineY);
                this.graphics.lineTo(m_roomRect.x + m_roomRect.width, lineY);
                this.graphics.endFill();
            }

            // the original rectangles
            for each (var sourceRect : Rectangle in m_sourceRects) {
                this.graphics.lineStyle(2, 0x0);
                this.graphics.beginFill(0x0000aa, 0.5);
                this.graphics.drawRect(sourceRect.x, sourceRect.y, sourceRect.width, sourceRect.height);
                this.graphics.endFill();
            }

            // draw all points that was used to generate the output rectangles for debug purposes
            for each (var p : Point in data.points) {
                this.graphics.lineStyle(0, 0, 0);
                this.graphics.beginFill(0xff0000, 1);
                this.graphics.drawCircle(p.x, p.y, 3);
                this.graphics.endFill();
            }

            m_outputTextField.text = "Rect count: " + data.outputRects.length + " (calculation time: " + calcTime + "ms)";
        }

        private function calculate(): CalculationData {
            // list of y coords for horisontal lines,
            // which are interesting when determining which rectangles to generate
            var lines : Vector.<int> = new Vector.<int>();

            // list of all points which are interesting
            // when determining where the corners of the generated rect could be
            var points : Vector.<Point> = new Vector.<Point>();

            // add the 4 corners of the room to interesting points
            points.push(new Point(m_roomRect.left, m_roomRect.top));
            points.push(new Point(m_roomRect.right, m_roomRect.top));
            points.push(new Point(m_roomRect.left, m_roomRect.bottom));
            points.push(new Point(m_roomRect.right, m_roomRect.bottom));

            for (var i:int = 0; i < m_sourceRects.length; i++) {
                var sourceRect : Rectangle = m_sourceRects[i];

                // source rect is completely outside of the room, we shoud ignore it
                if (!m_roomRect.containsRect(sourceRect) && !m_roomRect.intersects(sourceRect)) {
                    continue;
                }

                // push the y coord of the rect's top edge to the list of lines if it's not already been added
                if (lines.indexOf(sourceRect.y) == -1) {
                    lines.push(sourceRect.y);
                }

                // push the y coord of the rect's bottom edge to the list of lines if it's not already been added
                if (lines.indexOf(sourceRect.bottom) == -1) {
                    lines.push(sourceRect.bottom);
                }

                // add the 4 corners of the source rect to the list of interesting points
                addCornerPoints(points, sourceRect);

                // find the intersections between source rectangles and add those points
                for (var j:int = 0; j < m_sourceRects.length; j++) {
                    if (j != i) {
                        var intersect : Rectangle = m_sourceRects[i].intersection(m_sourceRects[j]);
                        if (intersect.width != 0 && intersect.height != 0) {
                            addCornerPoints(points, intersect);
                        }
                    }
                }
            }

            for (i = 0; i < lines.length; i++) {
                // add the points where the horisontal lines intersect with the room's left and right edges
                points.push(new Point(m_roomRect.x, lines[i]));
                points.push(new Point(m_roomRect.right, lines[i]));

                var lineRect : Rectangle = new Rectangle(m_roomRect.x, m_roomRect.y, 
                                                         m_roomRect.width, lines[i] - m_roomRect.y);

                // add all points where the horisontal lines intersect with the source rectangles
                for (a = 0; a < m_sourceRects.length;a++) {
                    intersect = m_sourceRects[a].intersection(lineRect);
                    if (intersect.width != 0 && intersect.height != 0) {
                        addCornerPoints(points, intersect);
                    }
                }
            }

            // clamp all points that are outside of the room to the room edges
            for (i = 0; i < points.length; i++) {
                points[i].x = Math.min(Math.max(m_roomRect.left, points[i].x), m_roomRect.right);
                points[i].y = Math.min(Math.max(m_roomRect.top, points[i].y), m_roomRect.bottom);
            }

            removeDuplicatePoints(points);

            var outputRects : Vector.<Rectangle> = new Vector.<Rectangle>();

            var pointsHash : Object = { };
            for (a = 0; a < points.length; a++) {
                pointsHash[points[a].x + "_" + points[a].y] = true;
            }

            for (var a:int = 0; a < points.length; a++) {
                for (var b:int = 0; b < points.length; b++) {
                    if (b != a && points[b].x > points[a].x && points[b].y == points[a].y) {
                        for (var c:int = 0; c < points.length; c++) {
                            // generate a rectangle that has its four corners in our points of interest
                            if (c != b && c != a && points[c].y > points[b].y && points[c].x == points[b].x) {
                                var r : Rectangle = new Rectangle(points[a].x, points[a].y, points[b].x - points[a].x, points[c].y - points[b].y);
                                // make sure the rect has the bottom left corner in one of our points
                                if (pointsHash[r.left+"_"+r.bottom]) {
                                    var containsOrIntersectsWithSource : Boolean = false;
                                    for (i = 0; i < m_sourceRects.length;i++) {
                                        if (r.containsRect(m_sourceRects[i]) || r.intersects(m_sourceRects[i])) {
                                            containsOrIntersectsWithSource = true;
                                            break;
                                        }
                                    }

                                    // we don't add any rectangles that either intersects with a source rect
                                    // or completely contains a source rect
                                    if (!containsOrIntersectsWithSource) {
                                        outputRects.push(r);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            trace("outputRects before cleanup:", outputRects.length);
            combineOutputRects(outputRects)
            trace("outputRects after cleanup", outputRects.length);

            var data : CalculationData = new CalculationData();
            data.outputRects = outputRects;
            data.lines = lines;
            data.points = points;

            return data;
        }

        private function addCornerPoints(points : Vector.<Point>, rect : Rectangle) : void {
            points.push(new Point(rect.left, rect.top));
            points.push(new Point(rect.right, rect.top));
            points.push(new Point(rect.left, rect.bottom));
            points.push(new Point(rect.right, rect.bottom));
        }

        // removes all rectangle that are already contained in another rectangle
        private function combineOutputRects(outputRects : Vector.<Rectangle>):Boolean {
            for (var a : int = 0; a < outputRects.length; a++) {
                for (var b : int = 0; b < outputRects.length; b++) {
                    if (b != a) {
                        if (outputRects[a].containsRect(outputRects[b])) {
                            trace("\tremoved rect " + outputRects[b] + ", it was contained in " + outputRects[a]);
                            outputRects.splice(b, 1);
                            b--;
                            a = 0;
                        }
                    }
                }
            }
            return false;
        }

        private function removeDuplicatePoints(points : Vector.<Point>) : void {
            var usedPoints : Object = {};
            for (var i : int = 0; i < points.length; i++) {
                if (usedPoints[points[i].toString()]) {
                    points.splice(i, 1);
                    i--;
                } else {
                    usedPoints[points[i].toString()] = true;
                }
            }
        }
    }
}

import flash.geom.Point;
import flash.geom.Rectangle;

class CalculationData {
    public var outputRects : Vector.<Rectangle> = new Vector.<Rectangle>;
    public var lines : Vector.<int> = new Vector.<int>;
    public var points : Vector.<Point> = new Vector.<Point>;
}

这篇关于切成矩形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆