區域的填充可以根據區域的填充,採用不同的填充演算法,而其中有掃瞄線類演算法和種子填充演算法。這裡,先介紹掃瞄線類演算法之有序邊表的掃瞄線演算法。其他什麼種子填充、邊界標誌演算法、4連通區域的遞迴演算法、8連通區域的遞迴演算法、掃瞄線種子填充演算法比較簡單。
其實有序邊表其實領會了也好理解,關鍵是將思想轉化為**。
先介紹演算法思想:
1.根據給出的多邊形頂點座標,建立et表(同時計算ymax和ymin)
2.初始化aet表,使之為空
3.使用掃瞄線yi值作為迴圈變數,使之初始值為ymin
重複做以下操作:
(1)如果et表中yi桶非空,則合併到aet中
(2)對aet表中記錄按x值從小到大排序
(3)依次取出表中記錄,兩兩配對填充
(4)如果aet表中某記錄的ymax=yi,則刪除該記錄
(5)對仍在aet中的記錄,修改x值,x=x+1/m (m為斜率)
現在,關鍵要細節的處理與**實現。
1.頂點座標,由於無法確定頂點數,所以我們用乙個陣列儲存。建立乙個填充函式
void regionfill(hdc hdc,point1 *p,int len,int ymin,int ymax)
我們需要建立edgetable類,通過分析,這個結構很像我們資料結構的圖(鄰接表表示)
**如下:
/*
*author qyl
*date 2012/4/7
*purpose 邊表結構
*version 1.1
*/#include"llist.h"
#include"point1.h"
class edge
edge(float x0,float incr,float y0)
friend bool operator
class edgetable
~edgetable()
llist* getvalue(int pos)
return l;
} void setedge(int pos,edge e)
numberedge++;
vertex[pos]->insert(e);
} void deledge(int pos,edge e)
}} void deleteall(int pos)
void createtableedge(point1 * p,int len,int ymin,int ymax)
else
break;
case 2:
if((*(flag+len-1))!=1 && (*(flag+len-1))!=3)
else
if((*(flag+j+1))!=1 && (*(flag+j+1)!=1)!=3)
else
break;
case 3:
if((*(flag+j+1))!=1 && (*(flag+j+1))!=3)
else
break;
default: break;}}
else
else
break;
case 2:
if((*(flag+(j+1)%len))!=1 && (*(flag+(j+1)%len))!=3)
else
if((*(flag+j-1))!=1 && (*(flag+j-1))!=3)
else
break;
case 3:
if((*(flag+(j+1)%len))!=1 && (*(flag+(j+1)%len))!=3)
else
break;
default: break;}}
}}} */
/*for(int i=ymin;i<=ymax;i++)
//非極值點 ymax-1
else if(p[1].gety()>p[0].gety() && p[len-1].gety()insert(*e);
}//非極值點 ymax-1
else if(p[1].gety()p[0].gety())
}else
//非極值點 ymax-1
else if(p[(j+1)%len].gety()>p[j].gety() && p[(j-1)%len].gety()insert(*e);
}//非極值點 ymax-1
else if(p[(j+1)%len].gety()p[j].gety())}}
} */
/*for(int j=0;jp[0].gety() && p[len-1].gety()>p[0].gety())
//非極值點 ymax-1
else if(p[1].gety()>p[0].gety() && p[len-1].gety()insert(*e);
}//非極值點 ymax-1
else if(p[1].gety()p[0].gety())
}else
//非極值點 ymax-1
else if(p[(j+1)%len].gety()>p[j].gety() && p[(j-1)%len].gety()insert(*e);
}//非極值點 ymax-1
else if(p[(j+1)%len].gety()p[j].gety())
}} */
for(int i=ymin;i<=ymax;i++)
else
if(p[len-2].gety()>p[len-1].gety())
else
}//非極值點 ymax-1
else if(p[1].gety()>p[0].gety() && p[len-1].gety()p[1].gety())
else
}//非極值點 ymax-1
else if(p[1].gety()p[0].gety())
else}}
else
else
if(j==1)
else
}else
else}}
//非極值點 ymax-1
else if(p[(j+1)%len].gety()>p[j].gety() && p[(j-1)%len].gety()p[(j+1)%len].gety())
else
}//非極值點 ymax-1
else if(p[(j+1)%len].gety()p[j].gety())
else
}else
else}}
}}} }};
**有些凌亂, 但有些錯誤的我並沒有刪去,只是展示如何一步一步改進的,以及發現的錯誤,改了之後好像有些注釋也為修改.
這個表結構成了話,其實其他的就簡單了
這個函式就是掃瞄線演算法啦。
void regionfill(hdc hdc,point1 *p,int len,int ymin,int ymax)
l->removeall();
} //在aet表中按從小到大排序
edge curr;
edge temp;
edge min;
ptr=l=aet.getvalue(i);
/*aet.deleteall(i);
int count=0;
for(l->setstart();l->getvalue(curr);l->next())
count++;
aet.setedge(i,min);
} */
//依次取出aet表中掃瞄線y桶中記錄,兩兩配對填充
for(l->setstart();l->getvalue(curr);l->next())
//在aet表中的每個記錄 對x進行修改
ptr->removeall();
ptr=aet.getvalue(i);
for(ptr->setstart();ptr->getvalue(curr);ptr->next())
//填充完後 刪除aet當前掃瞄線及l,ptr
aet.deleteall(i);
l->removeall();
ptr->removeall();
}}
最後,談談細節問題。就是掃瞄線與頂點相交,必須正確的進行交點個數的計算,否則會出錯。這個我已經包含到edgetable類中了,上面給出了兩種思路,但其中一種由於時間問題為全部完成,已被注釋掉了,有興趣可以修改哈,**共享哈。由於頂點前後連線構成環,而我用的陣列,所以對一些特殊情況要注意,當然也可以用迴圈鍊錶。
對於求最大最小值,也可以建乙個函式求解,這裡為了簡便,採用手動輸入。還有指標一定要小心。可能上面我說要排序,但我沒有排序,那是我插入記錄就是按排序插入的。
因為乙個大括號而多了100多的錯誤,因為乙個變數折騰到凌晨一點……
最後的最後,我只想說,調bug是件考驗毅力耐力的活!!!
掃瞄線演算法
給出幾個矩形對角端點座標,求這些矩形整體覆蓋的面積。基本思想如下圖 先離散化。掃瞄線 是一根想象中的虛線,從左往右掃瞄,遇到 矩形 則成為 事件 遇到 起始邊 則update相應區間的 厚度 或者 覆蓋次數 covercnt 1。遇到 結束邊 則update相應區間的 厚度 covercnt 1。用...
寒江雪 區域填充演算法 掃瞄線
區域填充遞迴演算法已經領教過了。記憶體消耗大,時間效率低,經典的深搜思想。為了解決這個問題,於是有人提出了種子填充掃瞄線演算法。其實就是深搜不行,換寬搜 bfs 該演算法假設已知其中乙個畫素點,然後從這個畫素點出發,去尋找周圍可以著色的點。這個已知點,我們稱其為種子點。每一輪著色之後,記錄下著色的區...
X 掃瞄線演算法
多邊形有兩種重要的表示方法 頂點表示和點陣表示 頂點表示是用多邊形的頂點序列來表示多邊形。這種表示直觀 幾何意義強 佔記憶體少,易於進行幾何變換。但由於它沒有明確指出哪些象素在多邊形內,故不能直接用於面著色 點陣表示是用位於多邊形內的象素集合來刻畫多邊形。這種表示丟失了許多幾何資訊 如邊界 頂點等 ...