今天和挪威的同事讨论点云的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);
}
}