算法 & 数据结构——多边形填充

在很多图形相关的编辑器里都会有选区功能,有的只能按某种固定形状选择,比如:矩形,圆形……,有的可以按任意形状选择,有的甚至可以分析图片自动选区(例如:Photoshop魔法棒工具),今天来说说任意形状选区怎么实现。

通过鼠标描点,再用线段连接,组合成一个几何选区,这看似没有难度,它实际上……的确没有难度。如果要填充选区内部,这就有一丢丢难度(至少很久前我被这个问题困住很久)。

计算机图形接口

在计算机里,图片普遍都是矩形的,当渲染一张图片到屏幕时,这张图片会被拆成两个三角形,这两个三角形将图片斜角对折切开,然后提交到渲染管道,而这个渲染管道则由显卡接管。

至于为什么会被拆成三角形,因为计算机3D渲染技术已经非常成熟,而3D渲染完全可以渲染2D图像,因此显卡只需要按3D渲染的架构设计就可以了,但是,3D图形比2D图形复杂的多,因为2D形状统统都可以在一个矩形的图片上画出来,而3D要显示一个形状需要模型,而怎么构建这个模型就成了问题。例如:立方体模型,六个面,那可以用六个正方形来构建。但是球形或更复杂的模型,纯用正方形去拼凑完全不可能,因此这可能需要各种各样的形状,从而导致非常凌乱。如果有一种图元可以拼凑出任意形状的模型,那一切都完美,三角形正是这样的图元,它虽然不能完美拼凑任意形状的模型,却可以在硬件允许的情况下无限近似任意形状的模型。当前流行的显卡通常只支持三角形硬件加速,听说俄罗斯还有人搞圆形硬件加速的?
上面大概已经说明三角形和渲染管道的关系了,下面进入正题。

闭合路径

要填充一个不规则的选区,要把它拆分成多个三角形,而怎么拆分成三角形就是问题所在。

任意形状选区

在拆分三角形之前,需要计算出选区内包含的所有闭合路径,因为一个选区可能会有多个闭合路径,就像上图显示,选区包含了两个闭合路径,要填充这两个闭合路径.

选区的数据结构是一组顶点列表[p0, p1, p2...pN]
从 p2 开始遍历:
pi -> p(i+1) 正好构成一条线段,将这条线段与前面的线段做相交检测,一旦相交,则说明此时遇到了一个闭合路径.
记录交点CrossP,
记录交点线段的起点CrossA,
记录交点线段的终点CrossB.
[CrossP, CrossB, ..., pi] 顶点列表则是该闭合路径.
随后将这个闭合路径的顶点列表从原始顶点列表中剔除,并将CrossP插入到CrossB的位置.
直到把原始顶点列表遍历完毕,则所有的闭合路径都被计算出来了.

三角形切割

已经得到了所有的闭合路径,接下来就可以切割三角形了.这非常容易,因为每一个闭合路径都是一个多边形,只需要求得这个多边形的中点,再从中点依次连接到各个顶点就可以了.

切割三角形

从图中可以清楚的看到闭合路径的中心,以及从中心连接到各个顶点的线段,因为图中的闭合路径只有四个顶点,因此只需要切割四个三角形就足够了.
计算多边形中心的公式:

(p[i] + p[i+1] + p[N]) / N

如果一切都是这么容易,那活着就舒坦了,几何学告诉我们,上述算法并不是总是凑效.

凹多边形切割

凹多边形闭合路径

从图中可以看出,闭合路径只有一个,但这个路径是凹多边形,按上述的方法求得中心很可能在多边形外面,连接出来的三角形并不正确.需要先把凹多边形切割成凸多边形,这非常容易.

只需要在多边形的凹点处,延伸出一条线段,连接到最近的边,这条线段作为交界,划分出两个多边形,再分别将这两个多边形重复此步骤,直到没有凹点,则这个多边形是凸多边形.

首先需要知道如何判断一个多边形是凹的还是凸的.
以左上角为原点的2D坐标系来说.

当一个顺时针排列的多边形,某一条边往左拐,这个多边形是凹多边形.
当一个逆时针排列的多边形,某一条边往右拐,这个多边形是凹多边形.
计算边的拐向很容易,只要判断两条边的向量积符号是正是负就行了.
//    向量拐向公式
//    具体拐向跟坐标系有关,在本例中,顺时针排列,向量积小于零是左拐.
Cross(p0p1, p1p2) < 0? "左拐?右拐?": "左拐?右拐?"

因为顶点顺序是随机的,因此可能是顺时针排列也可能是逆时针排列,所以要先计算出这个多边形的排列顺序,计算思路非常容易:
只需要遍历多边形顶点,对所有边计算拐向,如果左拐向多余右拐向,则是逆时针排列,否则是顺时针排列.

float Cutting::Polygon::CheckOrder(const math::Points & points)
{
    auto order0 = 0;    //  顺时针
    auto order1 = 0;    //  逆时针
    for (auto i = 0; i != points.size(); ++i)
    {
        auto & a = points.at(INDEX(i    , points.size()));
        auto & b = points.at(INDEX(i + 1, points.size()));
        auto & c = points.at(INDEX(i + 2, points.size()));
        if (CheckOrder(a, b, c) < 0)
        {
            ++order1;
        }
        else
        {
            ++order0;
        }
    }
    return order0 > order1 ? 1.0f : -1.0f;
}

整个算法到这就结束了,下图是凹多边形切割后的形状,很容易发现,多了几根线段,这些线段正是切分成凸多边形的线段,而闭合路径也由1个变成了3个,再用上述的方法计算三角形就容易多了.

切割凹多边形
填充选区

效果展示

png
png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 对于三月,我总是怀有一种别样的感觉。三月的天空不再如二月般晦涩,润湿的空气,夹杂着早春泥土的芳香,迎春花的气息,迎...
    记忆的抽象画阅读 283评论 0 0
  • ZaleJ阅读 232评论 0 0
  • 今天小编在逛贴吧时偶然看到一条帖子的标题十分醒目,名为“直播,上男朋友的贴吧号,然后直播封他所有的lol号!“ 作...
    f伐木累阅读 221评论 0 0
  • 妈妈 多么亲切的字眼 心中最温暖的港湾 给予了我们宝贵的生命 无微不至地呵护孩子成长 孩子是她的心头肉、眼中宝 老...
    浅陌心语阅读 228评论 0 0
  • 凡心所向,素履以往,生如逆旅,一苇以航。 时间很慢,慢的让我们开始疲倦,畅想着充满未知的明天,时间很快,快的令我们...
    田仁雲阅读 378评论 1 6