Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Направление обхода точек многоугольника, перспектива помещения(вид сверху) (http://www.flasher.ru/forum/showthread.php?t=210529)

fresh.flash 26.03.2015 14:32

Направление обхода точек многоугольника, перспектива помещения(вид сверху)
 
Добрый день! Есть массив точек, который образует непересекающийся многоугольник. Точки даны по порядку(добавляет пользователь), т.е. первая и вторая точки образуют сторону многоугольника. Это пол помещения. Нужно нарисовать вид сверху с перспективами стен:
http://img.sc/img/7fa38eb4c5559263f332b4b232e24f74.jpg

Это пример для прямоугольника.
Рабочий код, который определяет точки со сдвигом в зависимости от высоты помещения:
Код AS3:

for (var j:uint = 0; j < length; j++) {
        //находим текущую, прошлую и следующую точку
        _currPoint = points[j];
        if (j < points.length - 1)
                _nextPoint = points[j + 1];
        else
                _nextPoint = points[0];
        if (j == 0)
                _prevPoint = points[points.length - 1];
        else
                _prevPoint = points[j - 1];
        //находим отрезки, биссектрису
        norm1 = _currPoint.subtract(_prevPoint);
        norm1.normalize(1);
        norm2 = _currPoint.subtract(_nextPoint);
        norm2.normalize(1);
        bisectrix = norm1.add(norm2);
        //направление обхода
        cw = (_currPoint.x - _prevPoint.x) * (_nextPoint.y - _currPoint.y) - (_currPoint.y - _prevPoint.y) * (_nextPoint.x - _currPoint.x) > 0;
        if (!cw)
                bisectrix.normalize(h);
        else
                bisectrix.normalize( -h);
        //умножаем вектор биссектрисы на высоту стен
        normRatio = h / Math.abs(bisectrix.x * norm1.y - bisectrix.y * norm1.x);
        bisectrix.x *= normRatio;
        bisectrix.y *= normRatio;
        _pointsOffset.push(_currPoint.add(bisectrix));
}

Работает исправно и для сложных многоугольников (хотя такие вряд ли встретятся):
http://img.sc/img/ebef9450dc7f95a80b543e1e7ac92858.jpg

НО! Проблема 1: точки должны добавляться против часовой стрелки, если по часовой добавляешь - стены рисуются внутрь помещения и получается каламбур:
http://img.sc/img/28d8f5c383dce46adf9ee427918bdb32.jpg

Проблема 2: если точки лежат на одной прямой - неправильно определяется биссектриса(на картинке ниже верхний отрезок многоугольника состоит из двух):
http://img.sc/img/9de1a7977b2663faaad1d960503e3fd7.jpg

Буду благодарен за любую помощь как решить эти проблемы.

Код родился из этой темы.

nubideus 26.03.2015 15:24

1. посчитать площадь полигона. если площадь положительное число - перевернуть полигон.

http://www.mathopenref.com/coordpolygonarea2.html
https://github.com/deltaluca/nape/bl...omPoly.cx#L261

2. можно изначально не добавлять проблемные вершины. от них только лишний шум идет.

silin 26.03.2015 16:23

1. можно соориентироваться по сумме всех внешних и внутренних углов
2. для соосных отрезков (не считается биссектриса в такой схеме)) просто брать перпендикуляр
Код AS3:

package
{
        import flash.display.Graphics;
        import flash.display.Sprite;
        import flash.geom.Point;
 
        public class Main extends Sprite
        {
 
                public function Main():void
                {
 
                        var a:Point = new Point(100, 200);
                        var b:Point = new Point(200, 100);
                        var c:Point = new Point(400, 200);
                        var d:Point = new Point(250, 310);
                        var e:Point = new Point(200, 300);
                        var f:Point = new Point(150, 290);
 
 
                        var points:Array = [a, b, c, d, e, f];
                        // другое направление обхода
                        //var points:Array = [f, e, d, c, b, a];
 
                        graphics.lineStyle(0);
                        drawPoly(graphics, points);
 
                        graphics.lineStyle(0, 0xFF0000);
                        var offsetPoints:Array = calcOffsetPoints(points, 20);
                        drawPoly(graphics, offsetPoints);
 
                }
 
                public static function calcOffsetPoints(points:Array, distance:Number = 0):Array
                {
 
                        var res:Array = [];
                        var len:int = points.length;
 
                        // нправление обхода
                        var angle1:Number = 0;
                        var angle2:Number = 0;
                        for (var j:int = 0; j < len; j++)
                        {
                                var dp1:Point = points[(j + 1) % len].subtract(points[(j + 0) % len]);
                                var dp2:Point = points[(j + 1) % len].subtract(points[(j + 2) % len]);
 
                                var a1:Number = Math.atan2(dp1.y, dp1.x);
                                var a2:Number = Math.atan2(dp2.y, dp2.x);
 
                                var da1:Number = a1 - a2;
                                var da2:Number = a2 - a1;
 
                                if (da1 < 0)
                                        da1 += Math.PI * 2;
                                if (da2 < 0)
                                        da2 += Math.PI * 2;
 
                                angle1 += da1;
                                angle2 += da2;
 
                        }
                        var winding:Boolean = angle2 - angle1 > 0;
 
                        // расчет точек
                        for (var i:int = 0; i < len; i++)
                        {
 
                                var p0:Point = points[i];
                                var p1:Point = points[(i + 1) % len];
                                var p2:Point = points[(i + 2) % len];
 
                                var norm1:Point = p0.subtract(p1);
                                var norm2:Point = p2.subtract(p1);
 
                                norm1.normalize(1);
                                norm2.normalize(1);
 
                                var bisectrix:Point = norm1.add(norm2);
 
                                // соосность отрезков
                                if (bisectrix.length == 0)
                                {
                                        bisectrix.y = norm1.x;
                                        bisectrix.x = norm1.y;
 
                                }
 
                                var normRatio:Number = distance / (bisectrix.x * norm1.y - bisectrix.y * norm1.x);
 
                                bisectrix.x *= normRatio;
                                bisectrix.y *= normRatio;
 
                                // учитываем направление обхода
                                var offsetPoint:Point = winding ? p1.subtract(bisectrix) : p1.add(bisectrix);
 
                                res.push(offsetPoint);
                        }
                        return (res);
 
                }
 
                public static function drawPoly(graphics:Graphics, points:Array):void
                {
 
                        graphics.moveTo(points[points.length - 1].x, points[points.length - 1].y);
                        for (var i:int = 0; i < points.length; i++)
                        {
                                graphics.lineTo(points[i].x, points[i].y);
                        }
                }
        }
 
}


fresh.flash 26.03.2015 16:23

nubideus, спасибо огромное)) Как просто оказалось. С первым пунктом проблема разрешилась.
Не добавлять вершины не вариант, т.к. заказчику нужна такая возможность(1 стену разбивать на две, чтобы раскладывать разные плитки)

silin, благодарю, у Вас, как обычно, код-эталон:) Всё работает и всё логически понятно. Будет возможность - закину деньгу, а то по Вашим примерам учил as3 и забыл поблагодарить))


Часовой пояс GMT +4, время: 02:47.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2024, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.