今天和挪威的同事討論點雲的vex函數pcunshaded(),結果兩個人都沒搞太明白倒是轉到了另一個話題,就是點雲采樣制作連線怎樣避免重復計算,這里做一下記錄。
如果采用嚴格的避免重復連接的方法,我們可以在每個點上增加兩個點屬性,一個是確定自己是否已經建立連線的判斷屬性,另一個是存儲連線對應的另一端點的點數,同時使用setattrib()函數在對應點上標記自己的點數。但由於setattrib()的局限性,這種方法只能從一個點上生出一根(int or float),三根(vector), 四根(vector4)以及九根(matrix3)這樣的線段數,所以局限性比較大。
還有一種方法不是非常嚴格的直接避免重復,而是在重復連線之后再刪除overlap的連線,這種方法雖然有計算冗余,但是卻非常直接而且連接數是可以任意變化的。
Houdini在connect adjacent pieces節點中已經實現了這個方法,我從中提取出來稍微做了修改。下圖是將connect adjacent pieces精簡后的樣子:
其中connect_nearby_points的代碼修改完之后為:
/// Creates a new line between the given point numbers.
void createline(int pt_a; int pt_b)
{
int prim = addprim(0, "polyline");
addvertex(0, prim, pt_a);
addvertex(0, prim, pt_b);
}
/// Returns true if the item is contained in the list.
int contains(string list[]; string item)
{
foreach(string str; list)
{
if (item == str)
return 1;
}
return 0;
}
int handle = pcopen(0, "P", @P, chf("../searchradius"), chi("../maxsearchpoints"));
int max_connections = chi("../maxconnections");
string other_name;
string known_pieces[];
int num_connections = 0;
string my_name = s@name;
while (pciterate(handle) && num_connections < max_connections)
{
pcimport(handle, "name", other_name);
// Don't create connections to multiple points on the same piece, or
// to other points on our piece.
if ((my_name != other_name) & (num_connections == 0 || !contains(known_pieces, other_name)))
{
vector other_P;
pcimport(handle, "P", other_P);
f@distance = length(other_P - @P);
int other_ptnum;
pcimport(handle, "point:number", other_ptnum);
createline(@ptnum, other_ptnum);
++num_connections;
if (num_connections < max_connections)
push(known_pieces, other_name);
}
}
pcclose(handle);
這段代碼的精妙之處在於可以將點通過name屬性進行分類,比如左手和右手的點進行連線,那么全在左手上的點之間是絕對不會相互進行連線的。其中 !contains(known_pieces, other_name) 確保了另一端點是不在同一個name下面的。另外 push(known_pieces, other_name) 決定了該點只會與另一個name下的所有點只會生成一根連線,好比左手上的一個點與右手在的一個點產生連線后就再也不會訪問右手的其他點了,當然這個在我們的這個需求里面是可有可無的,因為我在事前用點數給每個點取了單獨的名字,所以沒有一個點在同一個name下。
最后的技巧就在clean里面的fix overlaps上,這個確保重復再亮點之間建立的連線都會清理干凈至一條,其實我廢話了這么多重點都在這個開關上 :P
補充:
經過 機器貓的建議,我嘗試了使用他說的當前點之比較比自己點序號小的或者大的點。結果這個方法非常的靠譜,有點類似堆棧出棧的方法,確實是非常直接了當的避免了重復鏈接,這里貼上簡易的實驗代碼。非常短但是沒有了上面代碼中的通過name來將點分群的功能:
//get the max connection number
int maxNumber = ch("maxNumber"); /// Creates a new line between the given point numbers.
void createline(int pt_a; int pt_b) { int prim = addprim(0, "polyline"); addvertex(0, prim, pt_a); addvertex(0, prim, pt_b); } int handle = pcopen(geoself(), "P", @P, 99, maxNumber); while(pciterate(handle)){ int ptNumber; pcimport(handle , "point.number", ptNumber); if(ptNumber > @ptnum ){ i@ptn = ptNumber; createline(@ptnum, ptNumber); } }