Each LINES draw call is now responsible for its own line width. No need to set it back to its 1.0 default after every draw. This eliminates half our calls to glLineWidth , similar to last week’s work on glPointSize.
3436 lines
77 KiB
C
3436 lines
77 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Contributor(s): Martin Poirier
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/editors/armature/reeb.c
|
|
* \ingroup edarmature
|
|
*/
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_edgehash.h"
|
|
#include "BLI_ghash.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "reeb.h"
|
|
|
|
#if 0 /* UNUSED 2.5 */
|
|
static ReebGraph *GLOBAL_RG = NULL;
|
|
static ReebGraph *FILTERED_RG = NULL;
|
|
#endif
|
|
|
|
/*
|
|
* Skeleton generation algorithm based on:
|
|
* "Harmonic Skeleton for Realistic Character Animation"
|
|
* Gregoire Aujay, Franck Hetroy, Francis Lazarus and Christine Depraz
|
|
* SIGGRAPH 2007
|
|
*
|
|
* Reeb graph generation algorithm based on:
|
|
* "Robust On-line Computation of Reeb Graphs: Simplicity and Speed"
|
|
* Valerio Pascucci, Giorgio Scorzelli, Peer-Timo Bremer and Ajith Mascarenhas
|
|
* SIGGRAPH 2007
|
|
*
|
|
* */
|
|
|
|
#if 0
|
|
#define DEBUG_REEB
|
|
#define DEBUG_REEB_NODE
|
|
#endif
|
|
|
|
/* place-holders! */
|
|
typedef struct EditEdge {
|
|
void *fake;
|
|
} EditEdge;
|
|
|
|
typedef struct EditFace {
|
|
void *fake;
|
|
} EditFace;
|
|
/* end place-holders! */
|
|
|
|
typedef struct VertexData {
|
|
float w; /* weight */
|
|
int i; /* index */
|
|
ReebNode *n;
|
|
} VertexData;
|
|
|
|
typedef struct EdgeIndex {
|
|
EditEdge **edges;
|
|
int *offset;
|
|
} EdgeIndex;
|
|
|
|
typedef enum {
|
|
MERGE_LOWER,
|
|
MERGE_HIGHER,
|
|
MERGE_APPEND
|
|
} MergeDirection;
|
|
|
|
int mergeArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1);
|
|
void mergeArcEdges(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc, MergeDirection direction);
|
|
int mergeConnectedArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1);
|
|
EditEdge *NextEdgeForVert(EdgeIndex *indexed_edges, int index);
|
|
void mergeArcFaces(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc);
|
|
void addFacetoArc(ReebArc *arc, EditFace *efa);
|
|
|
|
void REEB_RadialSymmetry(BNode *root_node, RadialArc *ring, int count);
|
|
void REEB_AxialSymmetry(BNode *root_node, BNode *node1, BNode *node2, struct BArc *barc1, BArc *barc2);
|
|
|
|
void flipArcBuckets(ReebArc *arc);
|
|
|
|
|
|
/***************************************** UTILS **********************************************/
|
|
|
|
#if 0 /* UNUSED */
|
|
static VertexData *allocVertexData(EditMesh *em)
|
|
{
|
|
VertexData *data;
|
|
EditVert *eve;
|
|
int totvert, index;
|
|
|
|
totvert = BLI_listbase_count(&em->verts);
|
|
|
|
data = MEM_callocN(sizeof(VertexData) * totvert, "VertexData");
|
|
|
|
for (index = 0, eve = em->verts.first; eve; index++, eve = eve->next)
|
|
{
|
|
data[index].i = index;
|
|
data[index].w = 0;
|
|
eve->tmp.p = data + index;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static int indexData(EditVert *eve)
|
|
{
|
|
return ((VertexData *)eve->tmp.p)->i;
|
|
}
|
|
|
|
static float weightData(EditVert *eve)
|
|
{
|
|
return ((VertexData *)eve->tmp.p)->w;
|
|
}
|
|
|
|
static void weightSetData(EditVert *eve, float w)
|
|
{
|
|
((VertexData *)eve->tmp.p)->w = w;
|
|
}
|
|
|
|
static ReebNode *nodeData(EditVert *eve)
|
|
{
|
|
return ((VertexData *)eve->tmp.p)->n;
|
|
}
|
|
|
|
static void nodeSetData(EditVert *eve, ReebNode *n)
|
|
{
|
|
((VertexData *)eve->tmp.p)->n = n;
|
|
}
|
|
|
|
#endif
|
|
|
|
void REEB_freeArc(BArc *barc)
|
|
{
|
|
ReebArc *arc = (ReebArc *)barc;
|
|
BLI_freelistN(&arc->edges);
|
|
|
|
if (arc->buckets)
|
|
MEM_freeN(arc->buckets);
|
|
|
|
if (arc->faces)
|
|
BLI_ghash_free(arc->faces, NULL, NULL);
|
|
|
|
MEM_freeN(arc);
|
|
}
|
|
|
|
void REEB_freeGraph(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc;
|
|
ReebNode *node;
|
|
|
|
// free nodes
|
|
for (node = rg->nodes.first; node; node = node->next) {
|
|
BLI_freeNode((BGraph *)rg, (BNode *)node);
|
|
}
|
|
BLI_freelistN(&rg->nodes);
|
|
|
|
// free arcs
|
|
arc = rg->arcs.first;
|
|
while (arc) {
|
|
ReebArc *next = arc->next;
|
|
REEB_freeArc((BArc *)arc);
|
|
arc = next;
|
|
}
|
|
|
|
// free edge map
|
|
BLI_edgehash_free(rg->emap, NULL);
|
|
|
|
/* free linked graph */
|
|
if (rg->link_up) {
|
|
REEB_freeGraph(rg->link_up);
|
|
}
|
|
|
|
MEM_freeN(rg);
|
|
}
|
|
|
|
ReebGraph *newReebGraph(void)
|
|
{
|
|
ReebGraph *rg;
|
|
rg = MEM_callocN(sizeof(ReebGraph), "reeb graph");
|
|
|
|
rg->totnodes = 0;
|
|
rg->emap = BLI_edgehash_new(__func__);
|
|
|
|
|
|
rg->free_arc = REEB_freeArc;
|
|
rg->free_node = NULL;
|
|
rg->radial_symmetry = REEB_RadialSymmetry;
|
|
rg->axial_symmetry = REEB_AxialSymmetry;
|
|
|
|
return rg;
|
|
}
|
|
|
|
void BIF_flagMultiArcs(ReebGraph *rg, int flag)
|
|
{
|
|
for (; rg; rg = rg->link_up) {
|
|
BLI_flagArcs((BGraph *)rg, flag);
|
|
}
|
|
}
|
|
|
|
#if 0 /* UNUSED */
|
|
static ReebNode *addNode(ReebGraph *rg, EditVert *eve)
|
|
{
|
|
float weight;
|
|
ReebNode *node = NULL;
|
|
|
|
weight = weightData(eve);
|
|
|
|
node = MEM_callocN(sizeof(ReebNode), "reeb node");
|
|
|
|
node->flag = 0; // clear flag on init
|
|
node->symmetry_level = 0;
|
|
node->arcs = NULL;
|
|
node->degree = 0;
|
|
node->weight = weight;
|
|
node->index = rg->totnodes;
|
|
copy_v3_v3(node->p, eve->co);
|
|
|
|
BLI_addtail(&rg->nodes, node);
|
|
rg->totnodes++;
|
|
|
|
nodeSetData(eve, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
static ReebNode *copyNode(ReebGraph *rg, ReebNode *node)
|
|
{
|
|
ReebNode *cp_node = NULL;
|
|
|
|
cp_node = MEM_callocN(sizeof(ReebNode), "reeb node copy");
|
|
|
|
memcpy(cp_node, node, sizeof(ReebNode));
|
|
|
|
cp_node->prev = NULL;
|
|
cp_node->next = NULL;
|
|
cp_node->arcs = NULL;
|
|
|
|
cp_node->link_up = NULL;
|
|
cp_node->link_down = NULL;
|
|
|
|
BLI_addtail(&rg->nodes, cp_node);
|
|
rg->totnodes++;
|
|
|
|
return cp_node;
|
|
}
|
|
|
|
static void relinkNodes(ReebGraph *low_rg, ReebGraph *high_rg)
|
|
{
|
|
ReebNode *low_node, *high_node;
|
|
|
|
if (low_rg == NULL || high_rg == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (low_node = low_rg->nodes.first; low_node; low_node = low_node->next)
|
|
{
|
|
for (high_node = high_rg->nodes.first; high_node; high_node = high_node->next)
|
|
{
|
|
if (low_node->index == high_node->index)
|
|
{
|
|
high_node->link_down = low_node;
|
|
low_node->link_up = high_node;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ReebNode *BIF_otherNodeFromIndex(ReebArc *arc, ReebNode *node)
|
|
{
|
|
return (arc->head->index == node->index) ? arc->tail : arc->head;
|
|
}
|
|
|
|
ReebNode *BIF_NodeFromIndex(ReebArc *arc, ReebNode *node)
|
|
{
|
|
return (arc->head->index == node->index) ? arc->head : arc->tail;
|
|
}
|
|
|
|
ReebNode *BIF_lowestLevelNode(ReebNode *node)
|
|
{
|
|
while (node->link_down) {
|
|
node = node->link_down;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
#if 0 /* UNUSED */
|
|
static ReebArc *copyArc(ReebGraph *rg, ReebArc *arc)
|
|
{
|
|
ReebArc *cp_arc;
|
|
ReebNode *node;
|
|
|
|
cp_arc = MEM_callocN(sizeof(ReebArc), "reeb arc copy");
|
|
|
|
memcpy(cp_arc, arc, sizeof(ReebArc));
|
|
|
|
cp_arc->link_up = arc;
|
|
|
|
cp_arc->head = NULL;
|
|
cp_arc->tail = NULL;
|
|
|
|
cp_arc->prev = NULL;
|
|
cp_arc->next = NULL;
|
|
|
|
cp_arc->edges.first = NULL;
|
|
cp_arc->edges.last = NULL;
|
|
|
|
/* copy buckets */
|
|
cp_arc->buckets = MEM_callocN(sizeof(EmbedBucket) * cp_arc->bcount, "embed bucket");
|
|
memcpy(cp_arc->buckets, arc->buckets, sizeof(EmbedBucket) * cp_arc->bcount);
|
|
|
|
/* copy faces map */
|
|
cp_arc->faces = BLI_ghash_ptr_new("copyArc gh");
|
|
mergeArcFaces(rg, cp_arc, arc);
|
|
|
|
/* find corresponding head and tail */
|
|
for (node = rg->nodes.first; node && (cp_arc->head == NULL || cp_arc->tail == NULL); node = node->next)
|
|
{
|
|
if (node->index == arc->head->index)
|
|
{
|
|
cp_arc->head = node;
|
|
}
|
|
else if (node->index == arc->tail->index)
|
|
{
|
|
cp_arc->tail = node;
|
|
}
|
|
}
|
|
|
|
BLI_addtail(&rg->arcs, cp_arc);
|
|
|
|
return cp_arc;
|
|
}
|
|
|
|
static ReebGraph *copyReebGraph(ReebGraph *rg, int level)
|
|
{
|
|
ReebNode *node;
|
|
ReebArc *arc;
|
|
ReebGraph *cp_rg = newReebGraph();
|
|
|
|
cp_rg->resolution = rg->resolution;
|
|
cp_rg->length = rg->length;
|
|
cp_rg->link_up = rg;
|
|
cp_rg->multi_level = level;
|
|
|
|
/* Copy nodes */
|
|
for (node = rg->nodes.first; node; node = node->next)
|
|
{
|
|
ReebNode *cp_node = copyNode(cp_rg, node);
|
|
cp_node->multi_level = level;
|
|
}
|
|
|
|
/* Copy arcs */
|
|
for (arc = rg->arcs.first; arc; arc = arc->next)
|
|
{
|
|
copyArc(cp_rg, arc);
|
|
}
|
|
|
|
BLI_buildAdjacencyList((BGraph *)cp_rg);
|
|
|
|
return cp_rg;
|
|
}
|
|
#endif
|
|
|
|
ReebGraph *BIF_graphForMultiNode(ReebGraph *rg, ReebNode *node)
|
|
{
|
|
ReebGraph *multi_rg = rg;
|
|
|
|
while (multi_rg && multi_rg->multi_level != node->multi_level) {
|
|
multi_rg = multi_rg->link_up;
|
|
}
|
|
|
|
return multi_rg;
|
|
}
|
|
|
|
#if 0 /* UNUSED */
|
|
static ReebEdge *copyEdge(ReebEdge *edge)
|
|
{
|
|
ReebEdge *newEdge = NULL;
|
|
|
|
newEdge = MEM_callocN(sizeof(ReebEdge), "reeb edge");
|
|
memcpy(newEdge, edge, sizeof(ReebEdge));
|
|
|
|
newEdge->next = NULL;
|
|
newEdge->prev = NULL;
|
|
|
|
return newEdge;
|
|
}
|
|
|
|
static void printArc(ReebArc *arc)
|
|
{
|
|
ReebEdge *edge;
|
|
ReebNode *head = (ReebNode *)arc->head;
|
|
ReebNode *tail = (ReebNode *)arc->tail;
|
|
printf("arc: (%i) %f -> (%i) %f\n", head->index, head->weight, tail->index, tail->weight);
|
|
|
|
for (edge = arc->edges.first; edge; edge = edge->next)
|
|
{
|
|
printf("\tedge (%i, %i)\n", edge->v1->index, edge->v2->index);
|
|
}
|
|
}
|
|
|
|
static void flipArc(ReebArc *arc)
|
|
{
|
|
ReebNode *tmp;
|
|
tmp = arc->head;
|
|
arc->head = arc->tail;
|
|
arc->tail = tmp;
|
|
|
|
flipArcBuckets(arc);
|
|
}
|
|
|
|
#ifdef DEBUG_REEB_NODE
|
|
static void NodeDegreeDecrement(ReebGraph *UNUSED(rg), ReebNode *node)
|
|
{
|
|
node->degree--;
|
|
|
|
// if (node->degree == 0)
|
|
// {
|
|
// printf("would remove node %i\n", node->index);
|
|
// }
|
|
}
|
|
|
|
static void NodeDegreeIncrement(ReebGraph *UNUSED(rg), ReebNode *node)
|
|
{
|
|
// if (node->degree == 0)
|
|
// {
|
|
// printf("first connect node %i\n", node->index);
|
|
// }
|
|
|
|
node->degree++;
|
|
}
|
|
|
|
#else
|
|
# define NodeDegreeDecrement(rg, node) {node->degree--; } (void)0
|
|
# define NodeDegreeIncrement(rg, node) {node->degree++; } (void)0
|
|
#endif
|
|
|
|
void repositionNodes(ReebGraph *rg)
|
|
{
|
|
BArc *arc = NULL;
|
|
BNode *node = NULL;
|
|
|
|
// Reset node positions
|
|
for (node = rg->nodes.first; node; node = node->next) {
|
|
node->p[0] = node->p[1] = node->p[2] = 0;
|
|
}
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
if (((ReebArc *)arc)->bcount > 0) {
|
|
float p[3];
|
|
|
|
copy_v3_v3(p, ((ReebArc *)arc)->buckets[0].p);
|
|
mul_v3_fl(p, 1.0f / arc->head->degree);
|
|
add_v3_v3(arc->head->p, p);
|
|
|
|
copy_v3_v3(p, ((ReebArc *)arc)->buckets[((ReebArc *)arc)->bcount - 1].p);
|
|
mul_v3_fl(p, 1.0f / arc->tail->degree);
|
|
add_v3_v3(arc->tail->p, p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void verifyNodeDegree(ReebGraph *rg)
|
|
{
|
|
#ifdef DEBUG_REEB
|
|
ReebNode *node = NULL;
|
|
ReebArc *arc = NULL;
|
|
|
|
for (node = rg->nodes.first; node; node = node->next) {
|
|
int count = 0;
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
if (arc->head == node || arc->tail == node) {
|
|
count++;
|
|
}
|
|
}
|
|
if (count != node->degree) {
|
|
printf("degree error in node %i: expected %i got %i\n", node->index, count, node->degree);
|
|
}
|
|
if (node->degree == 0) {
|
|
printf("zero degree node %i with weight %f\n", node->index, node->weight);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void verifyBucketsArc(ReebGraph *UNUSED(rg), ReebArc *arc)
|
|
{
|
|
ReebNode *head = (ReebNode *)arc->head;
|
|
ReebNode *tail = (ReebNode *)arc->tail;
|
|
|
|
if (arc->bcount > 0) {
|
|
int i;
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
if (arc->buckets[i].nv == 0) {
|
|
printArc(arc);
|
|
printf("count error in bucket %i/%i\n", i + 1, arc->bcount);
|
|
}
|
|
}
|
|
|
|
if (ceilf(head->weight) != arc->buckets[0].val) {
|
|
printArc(arc);
|
|
printf("alloc error in first bucket: %f should be %f\n", arc->buckets[0].val, ceil(head->weight));
|
|
}
|
|
if (floorf(tail->weight) != arc->buckets[arc->bcount - 1].val) {
|
|
printArc(arc);
|
|
printf("alloc error in last bucket: %f should be %f\n", arc->buckets[arc->bcount - 1].val, floor(tail->weight));
|
|
}
|
|
}
|
|
}
|
|
|
|
void verifyBuckets(ReebGraph *rg)
|
|
{
|
|
#ifdef DEBUG_REEB
|
|
ReebArc *arc = NULL;
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
verifyBucketsArc(rg, arc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void verifyFaces(ReebGraph *rg)
|
|
{
|
|
#ifdef DEBUG_REEB
|
|
int total = 0;
|
|
ReebArc *arc = NULL;
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
total += BLI_ghash_size(arc->faces);
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
void verifyArcs(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc;
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
if (arc->head->weight > arc->tail->weight) {
|
|
printf("FLIPPED ARC!\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void verifyMultiResolutionLinks(ReebGraph *rg, int level)
|
|
{
|
|
#ifdef DEBUG_REEB
|
|
ReebGraph *lower_rg = rg->link_up;
|
|
|
|
if (lower_rg) {
|
|
ReebArc *arc;
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
if (BLI_findindex(&lower_rg->arcs, arc->link_up) == -1) {
|
|
printf("missing arc %p for level %i\n", (void *)arc->link_up, level);
|
|
printf("Source arc was ---\n");
|
|
printArc(arc);
|
|
|
|
arc->link_up = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
verifyMultiResolutionLinks(lower_rg, level + 1);
|
|
}
|
|
#endif
|
|
}
|
|
/***************************************** BUCKET UTILS **********************************************/
|
|
|
|
static void addVertToBucket(EmbedBucket *b, float co[3])
|
|
{
|
|
b->nv++;
|
|
interp_v3_v3v3(b->p, b->p, co, 1.0f / b->nv);
|
|
}
|
|
|
|
#if 0 /* UNUSED 2.5 */
|
|
static void removeVertFromBucket(EmbedBucket *b, float co[3])
|
|
{
|
|
mul_v3_fl(b->p, (float)b->nv);
|
|
sub_v3_v3(b->p, co);
|
|
b->nv--;
|
|
mul_v3_fl(b->p, 1.0f / (float)b->nv);
|
|
}
|
|
#endif
|
|
|
|
static void mergeBuckets(EmbedBucket *bDst, EmbedBucket *bSrc)
|
|
{
|
|
if (bDst->nv > 0 && bSrc->nv > 0) {
|
|
bDst->nv += bSrc->nv;
|
|
interp_v3_v3v3(bDst->p, bDst->p, bSrc->p, (float)bSrc->nv / (float)(bDst->nv));
|
|
}
|
|
else if (bSrc->nv > 0) {
|
|
bDst->nv = bSrc->nv;
|
|
copy_v3_v3(bDst->p, bSrc->p);
|
|
}
|
|
}
|
|
|
|
static void mergeArcBuckets(ReebArc *aDst, ReebArc *aSrc, float start, float end)
|
|
{
|
|
if (aDst->bcount > 0 && aSrc->bcount > 0) {
|
|
int indexDst = 0, indexSrc = 0;
|
|
|
|
start = max_fff(start, aDst->buckets[0].val, aSrc->buckets[0].val);
|
|
|
|
while (indexDst < aDst->bcount && aDst->buckets[indexDst].val < start) {
|
|
indexDst++;
|
|
}
|
|
|
|
while (indexSrc < aSrc->bcount && aSrc->buckets[indexSrc].val < start) {
|
|
indexSrc++;
|
|
}
|
|
|
|
for (; indexDst < aDst->bcount &&
|
|
indexSrc < aSrc->bcount &&
|
|
aDst->buckets[indexDst].val <= end &&
|
|
aSrc->buckets[indexSrc].val <= end
|
|
|
|
; indexDst++, indexSrc++)
|
|
{
|
|
mergeBuckets(aDst->buckets + indexDst, aSrc->buckets + indexSrc);
|
|
}
|
|
}
|
|
}
|
|
|
|
void flipArcBuckets(ReebArc *arc)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0, j = arc->bcount - 1; i < j; i++, j--) {
|
|
EmbedBucket tmp;
|
|
|
|
tmp = arc->buckets[i];
|
|
arc->buckets[i] = arc->buckets[j];
|
|
arc->buckets[j] = tmp;
|
|
}
|
|
}
|
|
|
|
static int countArcBuckets(ReebArc *arc)
|
|
{
|
|
return (int)(floor(arc->tail->weight) - ceil(arc->head->weight)) + 1;
|
|
}
|
|
|
|
static void allocArcBuckets(ReebArc *arc)
|
|
{
|
|
int i;
|
|
float start = ceil(arc->head->weight);
|
|
arc->bcount = countArcBuckets(arc);
|
|
|
|
if (arc->bcount > 0) {
|
|
arc->buckets = MEM_callocN(sizeof(EmbedBucket) * arc->bcount, "embed bucket");
|
|
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
arc->buckets[i].val = start + i;
|
|
}
|
|
}
|
|
else {
|
|
arc->buckets = NULL;
|
|
}
|
|
}
|
|
|
|
static void resizeArcBuckets(ReebArc *arc)
|
|
{
|
|
EmbedBucket *oldBuckets = arc->buckets;
|
|
int oldBCount = arc->bcount;
|
|
|
|
if (countArcBuckets(arc) == oldBCount) {
|
|
return;
|
|
}
|
|
|
|
allocArcBuckets(arc);
|
|
|
|
if (oldBCount != 0 && arc->bcount != 0) {
|
|
int oldStart = (int)oldBuckets[0].val;
|
|
int oldEnd = (int)oldBuckets[oldBCount - 1].val;
|
|
int newStart = (int)arc->buckets[0].val;
|
|
int newEnd = (int)arc->buckets[arc->bcount - 1].val;
|
|
int oldOffset = 0;
|
|
int newOffset = 0;
|
|
int len;
|
|
|
|
if (oldStart < newStart) {
|
|
oldOffset = newStart - oldStart;
|
|
}
|
|
else {
|
|
newOffset = oldStart - newStart;
|
|
}
|
|
|
|
len = MIN2(oldEnd - (oldStart + oldOffset) + 1, newEnd - (newStart - newOffset) + 1);
|
|
|
|
memcpy(arc->buckets + newOffset, oldBuckets + oldOffset, len * sizeof(EmbedBucket));
|
|
}
|
|
|
|
if (oldBuckets != NULL) {
|
|
MEM_freeN(oldBuckets);
|
|
}
|
|
}
|
|
|
|
static void reweightBuckets(ReebArc *arc)
|
|
{
|
|
int i;
|
|
float start = ceil((arc->head)->weight);
|
|
|
|
if (arc->bcount > 0) {
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
arc->buckets[i].val = start + i;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void interpolateBuckets(ReebArc *arc, float *start_p, float *end_p, int start_index, int end_index)
|
|
{
|
|
int total;
|
|
int j;
|
|
|
|
total = end_index - start_index + 2;
|
|
|
|
for (j = start_index; j <= end_index; j++) {
|
|
EmbedBucket *empty = arc->buckets + j;
|
|
empty->nv = 1;
|
|
interp_v3_v3v3(empty->p, start_p, end_p, (float)(j - start_index + 1) / total);
|
|
}
|
|
}
|
|
|
|
static void fillArcEmptyBuckets(ReebArc *arc)
|
|
{
|
|
float *start_p, *end_p;
|
|
int start_index = 0, end_index = 0;
|
|
int missing = 0;
|
|
int i;
|
|
|
|
start_p = arc->head->p;
|
|
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
EmbedBucket *bucket = arc->buckets + i;
|
|
|
|
if (missing) {
|
|
if (bucket->nv > 0) {
|
|
missing = 0;
|
|
|
|
end_p = bucket->p;
|
|
end_index = i - 1;
|
|
|
|
interpolateBuckets(arc, start_p, end_p, start_index, end_index);
|
|
}
|
|
}
|
|
else {
|
|
if (bucket->nv == 0) {
|
|
missing = 1;
|
|
|
|
if (i > 0) {
|
|
start_p = arc->buckets[i - 1].p;
|
|
}
|
|
start_index = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (missing) {
|
|
end_p = arc->tail->p;
|
|
end_index = arc->bcount - 1;
|
|
|
|
interpolateBuckets(arc, start_p, end_p, start_index, end_index);
|
|
}
|
|
}
|
|
|
|
static void ExtendArcBuckets(ReebArc *arc)
|
|
{
|
|
ReebArcIterator arc_iter;
|
|
BArcIterator *iter = (BArcIterator *)&arc_iter;
|
|
EmbedBucket *last_bucket, *first_bucket;
|
|
float *previous = NULL;
|
|
float average_length = 0, length;
|
|
int padding_head = 0, padding_tail = 0;
|
|
|
|
if (arc->bcount == 0) {
|
|
return; /* failsafe, shouldn't happen */
|
|
}
|
|
|
|
initArcIterator(iter, arc, arc->head);
|
|
IT_next(iter);
|
|
previous = iter->p;
|
|
|
|
for (IT_next(iter);
|
|
IT_stopped(iter) == 0;
|
|
previous = iter->p, IT_next(iter)
|
|
)
|
|
{
|
|
average_length += len_v3v3(previous, iter->p);
|
|
}
|
|
average_length /= (arc->bcount - 1);
|
|
|
|
first_bucket = arc->buckets;
|
|
last_bucket = arc->buckets + (arc->bcount - 1);
|
|
|
|
length = len_v3v3(first_bucket->p, arc->head->p);
|
|
if (length > 2 * average_length) {
|
|
padding_head = (int)floor(length / average_length);
|
|
}
|
|
|
|
length = len_v3v3(last_bucket->p, arc->tail->p);
|
|
if (length > 2 * average_length) {
|
|
padding_tail = (int)floor(length / average_length);
|
|
}
|
|
|
|
if (padding_head + padding_tail > 0) {
|
|
EmbedBucket *old_buckets = arc->buckets;
|
|
|
|
arc->buckets = MEM_callocN(sizeof(EmbedBucket) * (padding_head + arc->bcount + padding_tail), "embed bucket");
|
|
memcpy(arc->buckets + padding_head, old_buckets, arc->bcount * sizeof(EmbedBucket));
|
|
|
|
arc->bcount = padding_head + arc->bcount + padding_tail;
|
|
|
|
MEM_freeN(old_buckets);
|
|
}
|
|
|
|
if (padding_head > 0) {
|
|
interpolateBuckets(arc, arc->head->p, first_bucket->p, 0, padding_head);
|
|
}
|
|
|
|
if (padding_tail > 0) {
|
|
interpolateBuckets(arc, last_bucket->p, arc->tail->p, arc->bcount - padding_tail, arc->bcount - 1);
|
|
}
|
|
}
|
|
|
|
/* CALL THIS ONLY AFTER FILTERING, SINCE IT MESSES UP WEIGHT DISTRIBUTION */
|
|
static void extendGraphBuckets(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc;
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
ExtendArcBuckets(arc);
|
|
}
|
|
}
|
|
|
|
/**************************************** LENGTH CALCULATIONS ****************************************/
|
|
|
|
static void calculateArcLength(ReebArc *arc)
|
|
{
|
|
ReebArcIterator arc_iter;
|
|
BArcIterator *iter = (BArcIterator *)&arc_iter;
|
|
float *vec0, *vec1;
|
|
|
|
arc->length = 0;
|
|
|
|
initArcIterator(iter, arc, arc->head);
|
|
|
|
vec0 = arc->head->p;
|
|
vec1 = arc->head->p; /* in case there's no embedding */
|
|
|
|
while (IT_next(iter)) {
|
|
vec1 = iter->p;
|
|
|
|
arc->length += len_v3v3(vec0, vec1);
|
|
|
|
vec0 = vec1;
|
|
}
|
|
|
|
arc->length += len_v3v3(arc->tail->p, vec1);
|
|
}
|
|
|
|
static void calculateGraphLength(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc;
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
calculateArcLength(arc);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**************************************** SYMMETRY HANDLING ******************************************/
|
|
|
|
void REEB_RadialSymmetry(BNode *root_node, RadialArc *ring, int count)
|
|
{
|
|
ReebNode *node = (ReebNode *)root_node;
|
|
float axis[3];
|
|
int i;
|
|
|
|
copy_v3_v3(axis, root_node->symmetry_axis);
|
|
|
|
/* first pass, merge incrementally */
|
|
for (i = 0; i < count - 1; i++) {
|
|
ReebNode *node1, *node2;
|
|
ReebArc *arc1, *arc2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
int j = i + 1;
|
|
|
|
add_v3_v3v3(tangent, ring[i].n, ring[j].n);
|
|
cross_v3_v3v3(normal, tangent, axis);
|
|
|
|
node1 = (ReebNode *)BLI_otherNode(ring[i].arc, root_node);
|
|
node2 = (ReebNode *)BLI_otherNode(ring[j].arc, root_node);
|
|
|
|
arc1 = (ReebArc *)ring[i].arc;
|
|
arc2 = (ReebArc *)ring[j].arc;
|
|
|
|
/* mirror first node and mix with the second */
|
|
BLI_mirrorAlongAxis(node1->p, root_node->p, normal);
|
|
interp_v3_v3v3(node2->p, node2->p, node1->p, 1.0f / (j + 1));
|
|
|
|
/* Merge buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (arc1->bcount > 0 && arc2->bcount > 0) {
|
|
ReebArcIterator arc_iter1, arc_iter2;
|
|
BArcIterator *iter1 = (BArcIterator *)&arc_iter1;
|
|
BArcIterator *iter2 = (BArcIterator *)&arc_iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(iter1, arc1, (ReebNode *)root_node);
|
|
initArcIterator(iter2, arc2, (ReebNode *)root_node);
|
|
|
|
bucket1 = IT_next(iter1);
|
|
bucket2 = IT_next(iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while (bucket1 && bucket2 && bucket1->val < bucket2->val) {
|
|
bucket1 = IT_next(iter1);
|
|
}
|
|
|
|
while (bucket1 && bucket2 && bucket2->val < bucket1->val) {
|
|
bucket2 = IT_next(iter2);
|
|
}
|
|
|
|
|
|
for (; bucket1 && bucket2; bucket1 = IT_next(iter1), bucket2 = IT_next(iter2)) {
|
|
bucket2->nv += bucket1->nv; /* add counts */
|
|
|
|
/* mirror on axis */
|
|
BLI_mirrorAlongAxis(bucket1->p, root_node->p, normal);
|
|
/* add bucket2 in bucket1 */
|
|
interp_v3_v3v3(bucket2->p, bucket2->p, bucket1->p, (float)bucket1->nv / (float)(bucket2->nv));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* second pass, mirror back on previous arcs */
|
|
for (i = count - 1; i > 0; i--) {
|
|
ReebNode *node1, *node2;
|
|
ReebArc *arc1, *arc2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
int j = i - 1;
|
|
|
|
add_v3_v3v3(tangent, ring[i].n, ring[j].n);
|
|
cross_v3_v3v3(normal, tangent, axis);
|
|
|
|
node1 = (ReebNode *)BLI_otherNode(ring[i].arc, root_node);
|
|
node2 = (ReebNode *)BLI_otherNode(ring[j].arc, root_node);
|
|
|
|
arc1 = (ReebArc *)ring[i].arc;
|
|
arc2 = (ReebArc *)ring[j].arc;
|
|
|
|
/* copy first node than mirror */
|
|
copy_v3_v3(node2->p, node1->p);
|
|
BLI_mirrorAlongAxis(node2->p, root_node->p, normal);
|
|
|
|
/* Copy buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (arc1->bcount > 0 && arc2->bcount > 0) {
|
|
ReebArcIterator arc_iter1, arc_iter2;
|
|
BArcIterator *iter1 = (BArcIterator *)&arc_iter1;
|
|
BArcIterator *iter2 = (BArcIterator *)&arc_iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(iter1, arc1, node);
|
|
initArcIterator(iter2, arc2, node);
|
|
|
|
bucket1 = IT_next(iter1);
|
|
bucket2 = IT_next(iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while (bucket1 && bucket1->val < bucket2->val) {
|
|
bucket1 = IT_next(iter1);
|
|
}
|
|
|
|
while (bucket2 && bucket2->val < bucket1->val) {
|
|
bucket2 = IT_next(iter2);
|
|
}
|
|
|
|
|
|
for (; bucket1 && bucket2; bucket1 = IT_next(iter1), bucket2 = IT_next(iter2)) {
|
|
/* copy and mirror back to bucket2 */
|
|
bucket2->nv = bucket1->nv;
|
|
copy_v3_v3(bucket2->p, bucket1->p);
|
|
BLI_mirrorAlongAxis(bucket2->p, node->p, normal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void REEB_AxialSymmetry(BNode *root_node, BNode *node1, BNode *node2, struct BArc *barc1, BArc *barc2)
|
|
{
|
|
ReebArc *arc1, *arc2;
|
|
float nor[3], p[3];
|
|
|
|
arc1 = (ReebArc *)barc1;
|
|
arc2 = (ReebArc *)barc2;
|
|
|
|
copy_v3_v3(nor, root_node->symmetry_axis);
|
|
|
|
/* mirror node2 along axis */
|
|
copy_v3_v3(p, node2->p);
|
|
BLI_mirrorAlongAxis(p, root_node->p, nor);
|
|
|
|
/* average with node1 */
|
|
add_v3_v3(node1->p, p);
|
|
mul_v3_fl(node1->p, 0.5f);
|
|
|
|
/* mirror back on node2 */
|
|
copy_v3_v3(node2->p, node1->p);
|
|
BLI_mirrorAlongAxis(node2->p, root_node->p, nor);
|
|
|
|
/* Merge buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (arc1->bcount > 0 && arc2->bcount > 0) {
|
|
ReebArcIterator arc_iter1, arc_iter2;
|
|
BArcIterator *iter1 = (BArcIterator *)&arc_iter1;
|
|
BArcIterator *iter2 = (BArcIterator *)&arc_iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(iter1, arc1, (ReebNode *)root_node);
|
|
initArcIterator(iter2, arc2, (ReebNode *)root_node);
|
|
|
|
bucket1 = IT_next(iter1);
|
|
bucket2 = IT_next(iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while (bucket1 && bucket1->val < bucket2->val) {
|
|
bucket1 = IT_next(iter1);
|
|
}
|
|
|
|
while (bucket2 && bucket2->val < bucket1->val) {
|
|
bucket2 = IT_next(iter2);
|
|
}
|
|
|
|
|
|
for (; bucket1 && bucket2; bucket1 = IT_next(iter1), bucket2 = IT_next(iter2)) {
|
|
bucket1->nv += bucket2->nv; /* add counts */
|
|
|
|
/* mirror on axis */
|
|
BLI_mirrorAlongAxis(bucket2->p, root_node->p, nor);
|
|
/* add bucket2 in bucket1 */
|
|
interp_v3_v3v3(bucket1->p, bucket1->p, bucket2->p, (float)bucket2->nv / (float)(bucket1->nv));
|
|
|
|
/* copy and mirror back to bucket2 */
|
|
bucket2->nv = bucket1->nv;
|
|
copy_v3_v3(bucket2->p, bucket1->p);
|
|
BLI_mirrorAlongAxis(bucket2->p, root_node->p, nor);
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************** ADJACENCY LIST *************************************************/
|
|
|
|
|
|
/****************************************** SMOOTHING **************************************************/
|
|
|
|
#if 0 /* UNUSED */
|
|
void postprocessGraph(ReebGraph *rg, char mode)
|
|
{
|
|
ReebArc *arc;
|
|
float fac1 = 0, fac2 = 1, fac3 = 0;
|
|
|
|
switch (mode)
|
|
{
|
|
case SKGEN_AVERAGE:
|
|
fac1 = fac2 = fac3 = 1.0f / 3.0f;
|
|
break;
|
|
case SKGEN_SMOOTH:
|
|
fac1 = fac3 = 0.25f;
|
|
fac2 = 0.5f;
|
|
break;
|
|
case SKGEN_SHARPEN:
|
|
fac1 = fac3 = -0.25f;
|
|
fac2 = 1.5f;
|
|
break;
|
|
default:
|
|
// XXX
|
|
// error("Unknown post processing mode");
|
|
return;
|
|
}
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next)
|
|
{
|
|
EmbedBucket *buckets = arc->buckets;
|
|
int bcount = arc->bcount;
|
|
int index;
|
|
|
|
for (index = 1; index < bcount - 1; index++)
|
|
{
|
|
interp_v3_v3v3(buckets[index].p, buckets[index].p, buckets[index - 1].p, fac1 / (fac1 + fac2));
|
|
interp_v3_v3v3(buckets[index].p, buckets[index].p, buckets[index + 1].p, fac3 / (fac1 + fac2 + fac3));
|
|
}
|
|
}
|
|
}
|
|
|
|
/********************************************SORTING****************************************************/
|
|
|
|
static int compareNodesWeight(void *vnode1, void *vnode2)
|
|
{
|
|
ReebNode *node1 = (ReebNode *)vnode1;
|
|
ReebNode *node2 = (ReebNode *)vnode2;
|
|
|
|
if (node1->weight < node2->weight)
|
|
{
|
|
return -1;
|
|
}
|
|
if (node1->weight > node2->weight)
|
|
{
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void sortNodes(ReebGraph *rg)
|
|
{
|
|
BLI_listbase_sort(&rg->nodes, compareNodesWeight);
|
|
}
|
|
|
|
static int compareArcsWeight(void *varc1, void *varc2)
|
|
{
|
|
ReebArc *arc1 = (ReebArc *)varc1;
|
|
ReebArc *arc2 = (ReebArc *)varc2;
|
|
ReebNode *node1 = (ReebNode *)arc1->head;
|
|
ReebNode *node2 = (ReebNode *)arc2->head;
|
|
|
|
if (node1->weight < node2->weight)
|
|
{
|
|
return -1;
|
|
}
|
|
if (node1->weight > node2->weight)
|
|
{
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void sortArcs(ReebGraph *rg)
|
|
{
|
|
BLI_listbase_sort(&rg->arcs, compareArcsWeight);
|
|
}
|
|
/******************************************* JOINING ***************************************************/
|
|
|
|
static void reweightArc(ReebGraph *rg, ReebArc *arc, ReebNode *start_node, float start_weight)
|
|
{
|
|
ReebNode *node;
|
|
float old_weight;
|
|
float end_weight = start_weight + ABS(arc->tail->weight - arc->head->weight);
|
|
int i;
|
|
|
|
node = (ReebNode *)BLI_otherNode((BArc *)arc, (BNode *)start_node);
|
|
|
|
/* prevent backtracking */
|
|
if (node->flag == 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (arc->tail == start_node)
|
|
{
|
|
flipArc(arc);
|
|
}
|
|
|
|
start_node->flag = 1;
|
|
|
|
for (i = 0; i < node->degree; i++)
|
|
{
|
|
ReebArc *next_arc = node->arcs[i];
|
|
|
|
reweightArc(rg, next_arc, node, end_weight);
|
|
}
|
|
|
|
/* update only if needed */
|
|
if (arc->head->weight != start_weight || arc->tail->weight != end_weight)
|
|
{
|
|
old_weight = arc->head->weight; /* backup head weight, other arcs need it intact, it will be fixed by the source arc */
|
|
|
|
arc->head->weight = start_weight;
|
|
arc->tail->weight = end_weight;
|
|
|
|
reweightBuckets(arc);
|
|
resizeArcBuckets(arc);
|
|
fillArcEmptyBuckets(arc);
|
|
|
|
arc->head->weight = old_weight;
|
|
}
|
|
}
|
|
|
|
static void reweightSubgraph(ReebGraph *rg, ReebNode *start_node, float start_weight)
|
|
{
|
|
int i;
|
|
|
|
BLI_flagNodes((BGraph *)rg, 0);
|
|
|
|
for (i = 0; i < start_node->degree; i++)
|
|
{
|
|
ReebArc *next_arc = start_node->arcs[i];
|
|
|
|
reweightArc(rg, next_arc, start_node, start_weight);
|
|
}
|
|
start_node->weight = start_weight;
|
|
}
|
|
|
|
static int joinSubgraphsEnds(ReebGraph *rg, float threshold, int nb_subgraphs)
|
|
{
|
|
int joined = 0;
|
|
int subgraph;
|
|
|
|
for (subgraph = 1; subgraph <= nb_subgraphs; subgraph++)
|
|
{
|
|
ReebNode *start_node, *end_node;
|
|
ReebNode *min_node_start = NULL, *min_node_end = NULL;
|
|
float min_distance = FLT_MAX;
|
|
|
|
for (start_node = rg->nodes.first; start_node; start_node = start_node->next)
|
|
{
|
|
if (start_node->subgraph_index == subgraph && start_node->degree == 1)
|
|
{
|
|
|
|
for (end_node = rg->nodes.first; end_node; end_node = end_node->next)
|
|
{
|
|
if (end_node->subgraph_index != subgraph)
|
|
{
|
|
float distance = len_v3v3(start_node->p, end_node->p);
|
|
|
|
if (distance < threshold && distance < min_distance)
|
|
{
|
|
min_distance = distance;
|
|
min_node_end = end_node;
|
|
min_node_start = start_node;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
end_node = min_node_end;
|
|
start_node = min_node_start;
|
|
|
|
if (end_node && start_node)
|
|
{
|
|
ReebArc *start_arc /* , *end_arc */ /* UNUSED */;
|
|
int merging = 0;
|
|
|
|
start_arc = start_node->arcs[0];
|
|
/* end_arc = end_node->arcs[0]; */ /* UNUSED */
|
|
|
|
if (start_arc->tail == start_node)
|
|
{
|
|
reweightSubgraph(rg, end_node, start_node->weight);
|
|
|
|
start_arc->tail = end_node;
|
|
|
|
merging = 1;
|
|
}
|
|
else if (start_arc->head == start_node)
|
|
{
|
|
reweightSubgraph(rg, start_node, end_node->weight);
|
|
|
|
start_arc->head = end_node;
|
|
|
|
merging = 2;
|
|
}
|
|
|
|
if (merging)
|
|
{
|
|
BLI_ReflagSubgraph((BGraph *)rg, end_node->flag, subgraph);
|
|
|
|
resizeArcBuckets(start_arc);
|
|
fillArcEmptyBuckets(start_arc);
|
|
|
|
NodeDegreeIncrement(rg, end_node);
|
|
BLI_rebuildAdjacencyListForNode((BGraph *)rg, (BNode *)end_node);
|
|
|
|
BLI_removeNode((BGraph *)rg, (BNode *)start_node);
|
|
}
|
|
|
|
joined = 1;
|
|
}
|
|
}
|
|
|
|
return joined;
|
|
}
|
|
|
|
/* Reweight graph from smallest node, fix fliped arcs */
|
|
static void fixSubgraphsOrientation(ReebGraph *rg, int nb_subgraphs)
|
|
{
|
|
int subgraph;
|
|
|
|
for (subgraph = 1; subgraph <= nb_subgraphs; subgraph++)
|
|
{
|
|
ReebNode *node;
|
|
ReebNode *start_node = NULL;
|
|
|
|
for (node = rg->nodes.first; node; node = node->next)
|
|
{
|
|
if (node->subgraph_index == subgraph)
|
|
{
|
|
if (start_node == NULL || node->weight < start_node->weight)
|
|
{
|
|
start_node = node;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (start_node)
|
|
{
|
|
reweightSubgraph(rg, start_node, start_node->weight);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int joinSubgraphs(ReebGraph *rg, float threshold)
|
|
{
|
|
int nb_subgraphs;
|
|
int joined = 0;
|
|
|
|
BLI_buildAdjacencyList((BGraph *)rg);
|
|
|
|
if (BLI_isGraphCyclic((BGraph *)rg)) {
|
|
/* don't deal with cyclic graphs YET */
|
|
return 0;
|
|
}
|
|
|
|
/* sort nodes before flagging subgraphs to make sure root node is subgraph 0 */
|
|
sortNodes(rg);
|
|
|
|
nb_subgraphs = BLI_FlagSubgraphs((BGraph *)rg);
|
|
|
|
/* Harmonic function can create flipped arcs, take the occasion to fix them */
|
|
// XXX
|
|
// if (G.scene->toolsettings->skgen_options & SKGEN_HARMONIC)
|
|
// {
|
|
fixSubgraphsOrientation(rg, nb_subgraphs);
|
|
// }
|
|
|
|
if (nb_subgraphs > 1)
|
|
{
|
|
joined |= joinSubgraphsEnds(rg, threshold, nb_subgraphs);
|
|
|
|
if (joined)
|
|
{
|
|
removeNormalNodes(rg);
|
|
BLI_buildAdjacencyList((BGraph *)rg);
|
|
}
|
|
}
|
|
|
|
return joined;
|
|
}
|
|
|
|
/****************************************** FILTERING **************************************************/
|
|
|
|
static float lengthArc(ReebArc *arc)
|
|
{
|
|
#if 0
|
|
ReebNode *head = (ReebNode *)arc->head;
|
|
ReebNode *tail = (ReebNode *)arc->tail;
|
|
|
|
return tail->weight - head->weight;
|
|
#else
|
|
return arc->length;
|
|
#endif
|
|
}
|
|
|
|
static int compareArcs(void *varc1, void *varc2)
|
|
{
|
|
ReebArc *arc1 = (ReebArc *)varc1;
|
|
ReebArc *arc2 = (ReebArc *)varc2;
|
|
float len1 = lengthArc(arc1);
|
|
float len2 = lengthArc(arc2);
|
|
|
|
if (len1 < len2) {
|
|
return -1;
|
|
}
|
|
if (len1 > len2) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void filterArc(ReebGraph *rg, ReebNode *newNode, ReebNode *removedNode, ReebArc *srcArc, int merging)
|
|
{
|
|
ReebArc *arc = NULL, *nextArc = NULL;
|
|
|
|
if (merging) {
|
|
/* first pass, merge buckets for arcs that spawned the two nodes into the source arc*/
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
if (arc->head == srcArc->head && arc->tail == srcArc->tail && arc != srcArc) {
|
|
ReebNode *head = srcArc->head;
|
|
ReebNode *tail = srcArc->tail;
|
|
mergeArcBuckets(srcArc, arc, head->weight, tail->weight);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* second pass, replace removedNode by newNode, remove arcs that are collapsed in a loop */
|
|
arc = rg->arcs.first;
|
|
while (arc) {
|
|
nextArc = arc->next;
|
|
|
|
if (arc->head == removedNode || arc->tail == removedNode) {
|
|
if (arc->head == removedNode) {
|
|
arc->head = newNode;
|
|
}
|
|
else {
|
|
arc->tail = newNode;
|
|
}
|
|
|
|
// Remove looped arcs
|
|
if (arc->head == arc->tail) {
|
|
// v1 or v2 was already newNode, since we're removing an arc, decrement degree
|
|
NodeDegreeDecrement(rg, newNode);
|
|
|
|
// If it's srcArc, it'll be removed later, so keep it for now
|
|
if (arc != srcArc) {
|
|
BLI_remlink(&rg->arcs, arc);
|
|
REEB_freeArc((BArc *)arc);
|
|
}
|
|
}
|
|
else {
|
|
/* flip arcs that flipped, can happen on diamond shapes, mostly on null arcs */
|
|
if (arc->head->weight > arc->tail->weight) {
|
|
flipArc(arc);
|
|
}
|
|
//newNode->degree++; // incrementing degree since we're adding an arc
|
|
NodeDegreeIncrement(rg, newNode);
|
|
mergeArcFaces(rg, arc, srcArc);
|
|
|
|
if (merging) {
|
|
ReebNode *head = arc->head;
|
|
ReebNode *tail = arc->tail;
|
|
|
|
// resize bucket list
|
|
resizeArcBuckets(arc);
|
|
mergeArcBuckets(arc, srcArc, head->weight, tail->weight);
|
|
|
|
/* update length */
|
|
arc->length += srcArc->length;
|
|
}
|
|
}
|
|
}
|
|
|
|
arc = nextArc;
|
|
}
|
|
}
|
|
|
|
void filterNullReebGraph(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc = NULL, *nextArc = NULL;
|
|
|
|
arc = rg->arcs.first;
|
|
while (arc) {
|
|
nextArc = arc->next;
|
|
// Only collapse arcs too short to have any embed bucket
|
|
if (arc->bcount == 0) {
|
|
ReebNode *newNode = (ReebNode *)arc->head;
|
|
ReebNode *removedNode = (ReebNode *)arc->tail;
|
|
float blend;
|
|
|
|
blend = (float)newNode->degree / (float)(newNode->degree + removedNode->degree); // blending factors
|
|
|
|
interp_v3_v3v3(newNode->p, removedNode->p, newNode->p, blend);
|
|
|
|
filterArc(rg, newNode, removedNode, arc, 0);
|
|
|
|
// Reset nextArc, it might have changed
|
|
nextArc = arc->next;
|
|
|
|
BLI_remlink(&rg->arcs, arc);
|
|
REEB_freeArc((BArc *)arc);
|
|
|
|
BLI_removeNode((BGraph *)rg, (BNode *)removedNode);
|
|
}
|
|
|
|
arc = nextArc;
|
|
}
|
|
}
|
|
|
|
static int filterInternalExternalReebGraph(ReebGraph *rg, float threshold_internal, float threshold_external)
|
|
{
|
|
ReebArc *arc = NULL, *nextArc = NULL;
|
|
int value = 0;
|
|
|
|
BLI_listbase_sort(&rg->arcs, compareArcs);
|
|
|
|
for (arc = rg->arcs.first; arc; arc = nextArc) {
|
|
nextArc = arc->next;
|
|
|
|
/* Only collapse non-terminal arcs that are shorter than threshold */
|
|
if ((threshold_internal > 0) &&
|
|
(arc->head->degree > 1) &&
|
|
(arc->tail->degree > 1) &&
|
|
(lengthArc(arc) < threshold_internal))
|
|
{
|
|
ReebNode *newNode = NULL;
|
|
ReebNode *removedNode = NULL;
|
|
|
|
/* Always remove lower node, so arcs don't flip */
|
|
newNode = arc->head;
|
|
removedNode = arc->tail;
|
|
|
|
filterArc(rg, newNode, removedNode, arc, 1);
|
|
|
|
// Reset nextArc, it might have changed
|
|
nextArc = arc->next;
|
|
|
|
BLI_remlink(&rg->arcs, arc);
|
|
REEB_freeArc((BArc *)arc);
|
|
|
|
BLI_removeNode((BGraph *)rg, (BNode *)removedNode);
|
|
value = 1;
|
|
}
|
|
|
|
// Only collapse terminal arcs that are shorter than threshold
|
|
else if ((threshold_external > 0) &&
|
|
(arc->head->degree == 1 || arc->tail->degree == 1) &&
|
|
(lengthArc(arc) < threshold_external))
|
|
{
|
|
ReebNode *terminalNode = NULL;
|
|
ReebNode *middleNode = NULL;
|
|
ReebNode *removedNode = NULL;
|
|
|
|
// Assign terminal and middle nodes
|
|
if (arc->head->degree == 1) {
|
|
terminalNode = arc->head;
|
|
middleNode = arc->tail;
|
|
}
|
|
else {
|
|
terminalNode = arc->tail;
|
|
middleNode = arc->head;
|
|
}
|
|
|
|
if (middleNode->degree == 2 && middleNode != rg->nodes.first) {
|
|
#if 1
|
|
// If middle node is a normal node, it will be removed later
|
|
// Only if middle node is not the root node
|
|
/* USE THIS IF YOU WANT TO PROLONG ARCS TO THEIR TERMINAL NODES
|
|
* FOR HANDS, THIS IS NOT THE BEST RESULT
|
|
* */
|
|
continue;
|
|
#else
|
|
removedNode = terminalNode;
|
|
|
|
// removing arc, so we need to decrease the degree of the remaining node
|
|
NodeDegreeDecrement(rg, middleNode);
|
|
#endif
|
|
}
|
|
// Otherwise, just plain remove of the arc
|
|
else {
|
|
removedNode = terminalNode;
|
|
|
|
// removing arc, so we need to decrease the degree of the remaining node
|
|
NodeDegreeDecrement(rg, middleNode);
|
|
}
|
|
|
|
// Reset nextArc, it might have changed
|
|
nextArc = arc->next;
|
|
|
|
BLI_remlink(&rg->arcs, arc);
|
|
REEB_freeArc((BArc *)arc);
|
|
|
|
BLI_removeNode((BGraph *)rg, (BNode *)removedNode);
|
|
value = 1;
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static int filterCyclesReebGraph(ReebGraph *rg, float UNUSED(distance_threshold))
|
|
{
|
|
ReebArc *arc1, *arc2;
|
|
ReebArc *next2;
|
|
int filtered = 0;
|
|
|
|
for (arc1 = rg->arcs.first; arc1; arc1 = arc1->next) {
|
|
for (arc2 = arc1->next; arc2; arc2 = next2) {
|
|
next2 = arc2->next;
|
|
if (arc1 != arc2 && arc1->head == arc2->head && arc1->tail == arc2->tail) {
|
|
mergeArcEdges(rg, arc1, arc2, MERGE_APPEND);
|
|
mergeArcFaces(rg, arc1, arc2);
|
|
mergeArcBuckets(arc1, arc2, arc1->head->weight, arc1->tail->weight);
|
|
|
|
NodeDegreeDecrement(rg, arc1->head);
|
|
NodeDegreeDecrement(rg, arc1->tail);
|
|
|
|
BLI_remlink(&rg->arcs, arc2);
|
|
REEB_freeArc((BArc *)arc2);
|
|
|
|
filtered = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
}
|
|
|
|
int filterSmartReebGraph(ReebGraph *UNUSED(rg), float UNUSED(threshold))
|
|
{
|
|
int value = 0;
|
|
#if 0 //XXX
|
|
ReebArc *arc = NULL, *nextArc = NULL;
|
|
|
|
BLI_listbase_sort(&rg->arcs, compareArcs);
|
|
|
|
#ifdef DEBUG_REEB
|
|
{
|
|
EditFace *efa;
|
|
for (efa = G.editMesh->faces.first; efa; efa = efa->next) {
|
|
efa->tmp.fp = -1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
arc = rg->arcs.first;
|
|
while (arc)
|
|
{
|
|
nextArc = arc->next;
|
|
|
|
/* need correct normals and center */
|
|
recalc_editnormals();
|
|
|
|
// Only test terminal arcs
|
|
if (arc->head->degree == 1 || arc->tail->degree == 1)
|
|
{
|
|
GHashIterator ghi;
|
|
int merging = 0;
|
|
int total = BLI_ghash_size(arc->faces);
|
|
float avg_angle = 0;
|
|
float avg_vec[3] = {0, 0, 0};
|
|
|
|
for (BLI_ghashIterator_init(&ghi, arc->faces);
|
|
BLI_ghashIterator_done(&ghi) == false;
|
|
BLI_ghashIterator_step(&ghi))
|
|
{
|
|
EditFace *efa = BLI_ghashIterator_getValue(&ghi);
|
|
|
|
#if 0
|
|
ReebArcIterator arc_iter;
|
|
BArcIterator *iter = (BArcIterator *)&arc_iter;
|
|
EmbedBucket *bucket = NULL;
|
|
EmbedBucket *previous = NULL;
|
|
float min_distance = -1;
|
|
float angle = 0;
|
|
|
|
initArcIterator(iter, arc, arc->head);
|
|
|
|
bucket = nextBucket(iter);
|
|
|
|
while (bucket != NULL)
|
|
{
|
|
float *vec0 = NULL;
|
|
float *vec1 = bucket->p;
|
|
float midpoint[3], tangent[3];
|
|
float distance;
|
|
|
|
/* first bucket. Previous is head */
|
|
if (previous == NULL)
|
|
{
|
|
vec0 = arc->head->p;
|
|
}
|
|
/* Previous is a valid bucket */
|
|
else {
|
|
vec0 = previous->p;
|
|
}
|
|
|
|
copy_v3_v3(midpoint, vec1);
|
|
|
|
distance = len_v3v3(midpoint, efa->cent);
|
|
|
|
if (min_distance == -1 || distance < min_distance)
|
|
{
|
|
min_distance = distance;
|
|
|
|
sub_v3_v3v3(tangent, vec1, vec0);
|
|
normalize_v3(tangent);
|
|
|
|
angle = dot_v3v3(tangent, efa->n);
|
|
}
|
|
|
|
previous = bucket;
|
|
bucket = nextBucket(iter);
|
|
}
|
|
|
|
avg_angle += saacos(fabs(angle));
|
|
#ifdef DEBUG_REEB
|
|
efa->tmp.fp = saacos(fabs(angle));
|
|
#endif
|
|
#else
|
|
add_v3_v3(avg_vec, efa->n);
|
|
#endif
|
|
}
|
|
|
|
|
|
#if 0
|
|
avg_angle /= total;
|
|
#else
|
|
mul_v3_fl(avg_vec, 1.0 / total);
|
|
avg_angle = dot_v3v3(avg_vec, avg_vec);
|
|
#endif
|
|
|
|
arc->angle = avg_angle;
|
|
|
|
if (avg_angle > threshold)
|
|
merging = 1;
|
|
|
|
if (merging) {
|
|
ReebNode *terminalNode = NULL;
|
|
ReebNode *middleNode = NULL;
|
|
ReebNode *newNode = NULL;
|
|
ReebNode *removedNode = NULL;
|
|
int merging = 0;
|
|
|
|
/* Assign terminal and middle nodes */
|
|
if (arc->head->degree == 1) {
|
|
terminalNode = arc->head;
|
|
middleNode = arc->tail;
|
|
}
|
|
else {
|
|
terminalNode = arc->tail;
|
|
middleNode = arc->head;
|
|
}
|
|
|
|
/* If middle node is a normal node, merge to terminal node */
|
|
if (middleNode->degree == 2) {
|
|
merging = 1;
|
|
newNode = terminalNode;
|
|
removedNode = middleNode;
|
|
}
|
|
/* Otherwise, just plain remove of the arc */
|
|
else {
|
|
merging = 0;
|
|
newNode = middleNode;
|
|
removedNode = terminalNode;
|
|
}
|
|
|
|
/* Merging arc */
|
|
if (merging) {
|
|
filterArc(rg, newNode, removedNode, arc, 1);
|
|
}
|
|
else {
|
|
/* removing arc, so we need to decrease the degree of the remaining node
|
|
*newNode->degree--; */
|
|
NodeDegreeDecrement(rg, newNode);
|
|
}
|
|
|
|
/* Reset nextArc, it might have changed */
|
|
nextArc = arc->next;
|
|
|
|
BLI_remlink(&rg->arcs, arc);
|
|
REEB_freeArc((BArc *)arc);
|
|
|
|
BLI_freelinkN(&rg->nodes, removedNode);
|
|
value = 1;
|
|
}
|
|
}
|
|
|
|
arc = nextArc;
|
|
}
|
|
|
|
#endif
|
|
|
|
return value;
|
|
}
|
|
|
|
static void filterGraph(ReebGraph *rg, short options, float threshold_internal, float threshold_external)
|
|
{
|
|
bool done = true;
|
|
|
|
calculateGraphLength(rg);
|
|
|
|
if ((options & SKGEN_FILTER_EXTERNAL) == 0) {
|
|
threshold_external = 0;
|
|
}
|
|
|
|
if ((options & SKGEN_FILTER_INTERNAL) == 0) {
|
|
threshold_internal = 0;
|
|
}
|
|
|
|
if (threshold_internal > 0 || threshold_external > 0) {
|
|
/* filter until there's nothing more to do */
|
|
while (done == true) {
|
|
done = false; /* no work done yet */
|
|
|
|
done = filterInternalExternalReebGraph(rg, threshold_internal, threshold_external);
|
|
}
|
|
}
|
|
|
|
if (options & SKGEN_FILTER_SMART) {
|
|
filterSmartReebGraph(rg, 0.5);
|
|
filterCyclesReebGraph(rg, 0.5);
|
|
}
|
|
|
|
repositionNodes(rg);
|
|
|
|
/* Filtering might have created degree 2 nodes, so remove them */
|
|
removeNormalNodes(rg);
|
|
}
|
|
|
|
static void finalizeGraph(ReebGraph *rg, char passes, char method)
|
|
{
|
|
int i;
|
|
|
|
BLI_buildAdjacencyList((BGraph *)rg);
|
|
|
|
sortNodes(rg);
|
|
|
|
sortArcs(rg);
|
|
|
|
for (i = 0; i < passes; i++) {
|
|
postprocessGraph(rg, method);
|
|
}
|
|
|
|
extendGraphBuckets(rg);
|
|
}
|
|
|
|
/************************************** WEIGHT SPREADING ***********************************************/
|
|
|
|
static int compareVerts(const void *a, const void *b)
|
|
{
|
|
EditVert *va = *(EditVert **)a;
|
|
EditVert *vb = *(EditVert **)b;
|
|
int value = 0;
|
|
|
|
if (weightData(va) < weightData(vb)) {
|
|
value = -1;
|
|
}
|
|
else if (weightData(va) > weightData(vb)) {
|
|
value = 1;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static void spreadWeight(EditMesh *em)
|
|
{
|
|
EditVert **verts, *eve;
|
|
float lastWeight = 0.0f;
|
|
int totvert = BLI_listbase_count(&em->verts);
|
|
int i;
|
|
int work_needed = 1;
|
|
|
|
verts = MEM_callocN(sizeof(EditVert *) * totvert, "verts array");
|
|
|
|
for (eve = em->verts.first, i = 0; eve; eve = eve->next, i++) {
|
|
verts[i] = eve;
|
|
}
|
|
|
|
while (work_needed == 1) {
|
|
work_needed = 0;
|
|
qsort(verts, totvert, sizeof(EditVert *), compareVerts);
|
|
|
|
for (i = 0; i < totvert; i++) {
|
|
eve = verts[i];
|
|
|
|
if (i == 0 || (weightData(eve) - lastWeight) > FLT_EPSILON) {
|
|
lastWeight = weightData(eve);
|
|
}
|
|
else {
|
|
work_needed = 1;
|
|
weightSetData(eve, lastWeight + FLT_EPSILON * 2);
|
|
lastWeight = weightData(eve);
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeN(verts);
|
|
}
|
|
|
|
/******************************************** EXPORT ***************************************************/
|
|
|
|
static void exportNode(FILE *f, const char *text, ReebNode *node)
|
|
{
|
|
fprintf(f, "%s i:%i w:%f d:%i %f %f %f\n", text, node->index, node->weight, node->degree, node->p[0], node->p[1], node->p[2]);
|
|
}
|
|
|
|
void REEB_exportGraph(ReebGraph *rg, int count)
|
|
{
|
|
ReebArc *arc;
|
|
char filename[128];
|
|
FILE *f;
|
|
|
|
if (count == -1) {
|
|
strcpy(filename, "test.txt");
|
|
}
|
|
else {
|
|
sprintf(filename, "test%05i.txt", count);
|
|
}
|
|
f = BLI_fopen(filename, "w");
|
|
|
|
for (arc = rg->arcs.first; arc; arc = arc->next) {
|
|
int i;
|
|
float p[3];
|
|
|
|
exportNode(f, "v1", arc->head);
|
|
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
fprintf(f, "b nv:%i %f %f %f\n", arc->buckets[i].nv, arc->buckets[i].p[0], arc->buckets[i].p[1], arc->buckets[i].p[2]);
|
|
}
|
|
|
|
add_v3_v3v3(p, arc->tail->p, arc->head->p);
|
|
mul_v3_fl(p, 0.5f);
|
|
|
|
fprintf(f, "angle %0.3f %0.3f %0.3f %0.3f %i\n", p[0], p[1], p[2], arc->angle, BLI_ghash_size(arc->faces));
|
|
exportNode(f, "v2", arc->tail);
|
|
}
|
|
|
|
fclose(f);
|
|
}
|
|
|
|
/***************************************** MAIN ALGORITHM **********************************************/
|
|
|
|
/* edges alone will create zero degree nodes, use this function to remove them */
|
|
static void removeZeroNodes(ReebGraph *rg)
|
|
{
|
|
ReebNode *node, *next_node;
|
|
|
|
for (node = rg->nodes.first; node; node = next_node) {
|
|
next_node = node->next;
|
|
|
|
if (node->degree == 0) {
|
|
BLI_removeNode((BGraph *)rg, (BNode *)node);
|
|
}
|
|
}
|
|
}
|
|
|
|
void removeNormalNodes(ReebGraph *rg)
|
|
{
|
|
ReebArc *arc, *nextArc;
|
|
|
|
// Merge degree 2 nodes
|
|
for (arc = rg->arcs.first; arc; arc = nextArc) {
|
|
nextArc = arc->next;
|
|
|
|
while (arc->head->degree == 2 || arc->tail->degree == 2) {
|
|
// merge at v1
|
|
if (arc->head->degree == 2) {
|
|
ReebArc *connectedArc = (ReebArc *)BLI_findConnectedArc((BGraph *)rg, (BArc *)arc, (BNode *)arc->head);
|
|
|
|
/* If arcs are one after the other */
|
|
if (arc->head == connectedArc->tail) {
|
|
/* remove furthest arc */
|
|
if (arc->tail->weight < connectedArc->head->weight) {
|
|
mergeConnectedArcs(rg, arc, connectedArc);
|
|
nextArc = arc->next;
|
|
}
|
|
else {
|
|
mergeConnectedArcs(rg, connectedArc, arc);
|
|
break; /* arc was removed, move to next */
|
|
}
|
|
}
|
|
/* Otherwise, arcs are side by side */
|
|
else {
|
|
/* Don't do anything, we need to keep the lowest node, even if degree 2 */
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* merge at v2 */
|
|
if (arc->tail->degree == 2) {
|
|
ReebArc *connectedArc = (ReebArc *)BLI_findConnectedArc((BGraph *)rg, (BArc *)arc, (BNode *)arc->tail);
|
|
|
|
/* If arcs are one after the other */
|
|
if (arc->tail == connectedArc->head) {
|
|
/* remove furthest arc */
|
|
if (arc->head->weight < connectedArc->tail->weight) {
|
|
mergeConnectedArcs(rg, arc, connectedArc);
|
|
nextArc = arc->next;
|
|
}
|
|
else {
|
|
mergeConnectedArcs(rg, connectedArc, arc);
|
|
break; /* arc was removed, move to next */
|
|
}
|
|
}
|
|
/* Otherwise, arcs are side by side */
|
|
else {
|
|
/* Don't do anything, we need to keep the lowest node, even if degree 2 */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
static int edgeEquals(ReebEdge *e1, ReebEdge *e2)
|
|
{
|
|
return (e1->v1 == e2->v1 && e1->v2 == e2->v2);
|
|
}
|
|
|
|
static ReebArc *nextArcMappedToEdge(ReebArc *arc, ReebEdge *e)
|
|
{
|
|
ReebEdge *nextEdge = NULL;
|
|
ReebEdge *edge = NULL;
|
|
ReebArc *result = NULL;
|
|
|
|
/* Find the ReebEdge in the edge list */
|
|
for (edge = arc->edges.first; edge && !edgeEquals(edge, e); edge = edge->next) { }
|
|
|
|
nextEdge = edge->nextEdge;
|
|
|
|
if (nextEdge != NULL) {
|
|
result = nextEdge->arc;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void addFacetoArc(ReebArc *arc, EditFace *efa)
|
|
{
|
|
BLI_ghash_insert(arc->faces, efa, efa);
|
|
}
|
|
|
|
void mergeArcFaces(ReebGraph *UNUSED(rg), ReebArc *aDst, ReebArc *aSrc)
|
|
{
|
|
GHashIterator ghi;
|
|
|
|
for (BLI_ghashIterator_init(&ghi, aSrc->faces);
|
|
BLI_ghashIterator_done(&ghi) == false;
|
|
BLI_ghashIterator_step(&ghi))
|
|
{
|
|
EditFace *efa = BLI_ghashIterator_getValue(&ghi);
|
|
BLI_ghash_insert(aDst->faces, efa, efa);
|
|
}
|
|
}
|
|
|
|
void mergeArcEdges(ReebGraph *rg, ReebArc *aDst, ReebArc *aSrc, MergeDirection direction)
|
|
{
|
|
ReebEdge *e = NULL;
|
|
|
|
if (direction == MERGE_APPEND) {
|
|
for (e = aSrc->edges.first; e; e = e->next) {
|
|
e->arc = aDst; // Edge is stolen by new arc
|
|
}
|
|
|
|
BLI_movelisttolist(&aDst->edges, &aSrc->edges);
|
|
}
|
|
else {
|
|
for (e = aSrc->edges.first; e; e = e->next) {
|
|
ReebEdge *newEdge = copyEdge(e);
|
|
|
|
newEdge->arc = aDst;
|
|
|
|
BLI_addtail(&aDst->edges, newEdge);
|
|
|
|
if (direction == MERGE_LOWER) {
|
|
void **p = BLI_edgehash_lookup_p(rg->emap, e->v1->index, e->v2->index);
|
|
|
|
newEdge->nextEdge = e;
|
|
|
|
// if edge was the first in the list, point the edit edge to the new reeb edge instead.
|
|
if (*p == e) {
|
|
*p = (void *)newEdge;
|
|
}
|
|
// otherwise, advance in the list until the predecessor is found then insert it there
|
|
else {
|
|
ReebEdge *previous = (ReebEdge *)*p;
|
|
|
|
while (previous->nextEdge != e) {
|
|
previous = previous->nextEdge;
|
|
}
|
|
|
|
previous->nextEdge = newEdge;
|
|
}
|
|
}
|
|
else {
|
|
newEdge->nextEdge = e->nextEdge;
|
|
e->nextEdge = newEdge;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// return 1 on full merge
|
|
int mergeConnectedArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1)
|
|
{
|
|
int result = 0;
|
|
ReebNode *removedNode = NULL;
|
|
|
|
a0->length += a1->length;
|
|
|
|
mergeArcEdges(rg, a0, a1, MERGE_APPEND);
|
|
mergeArcFaces(rg, a0, a1);
|
|
|
|
// Bring a0 to the combine length of both arcs
|
|
if (a0->tail == a1->head) {
|
|
removedNode = a0->tail;
|
|
a0->tail = a1->tail;
|
|
}
|
|
else if (a0->head == a1->tail) {
|
|
removedNode = a0->head;
|
|
a0->head = a1->head;
|
|
}
|
|
|
|
resizeArcBuckets(a0);
|
|
// Merge a1 in a0
|
|
mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight);
|
|
|
|
// remove a1 from graph
|
|
BLI_remlink(&rg->arcs, a1);
|
|
REEB_freeArc((BArc *)a1);
|
|
|
|
BLI_removeNode((BGraph *)rg, (BNode *)removedNode);
|
|
result = 1;
|
|
|
|
return result;
|
|
}
|
|
// return 1 on full merge
|
|
int mergeArcs(ReebGraph *rg, ReebArc *a0, ReebArc *a1)
|
|
{
|
|
int result = 0;
|
|
/* TRIANGLE POINTS DOWN */
|
|
if (a0->head->weight == a1->head->weight) { /* heads are the same */
|
|
if (a0->tail->weight == a1->tail->weight) { /* tails also the same, arcs can be totally merge together */
|
|
mergeArcEdges(rg, a0, a1, MERGE_APPEND);
|
|
mergeArcFaces(rg, a0, a1);
|
|
|
|
mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight);
|
|
|
|
// Adjust node degree
|
|
//a1->head->degree--;
|
|
NodeDegreeDecrement(rg, a1->head);
|
|
//a1->tail->degree--;
|
|
NodeDegreeDecrement(rg, a1->tail);
|
|
|
|
// remove a1 from graph
|
|
BLI_remlink(&rg->arcs, a1);
|
|
|
|
REEB_freeArc((BArc *)a1);
|
|
result = 1;
|
|
}
|
|
else if (a0->tail->weight > a1->tail->weight) { /* a1->tail->weight is in the middle */
|
|
mergeArcEdges(rg, a1, a0, MERGE_LOWER);
|
|
mergeArcFaces(rg, a1, a0);
|
|
|
|
// Adjust node degree
|
|
//a0->head->degree--;
|
|
NodeDegreeDecrement(rg, a0->head);
|
|
//a1->tail->degree++;
|
|
NodeDegreeIncrement(rg, a1->tail);
|
|
|
|
mergeArcBuckets(a1, a0, a1->head->weight, a1->tail->weight);
|
|
a0->head = a1->tail;
|
|
resizeArcBuckets(a0);
|
|
}
|
|
else { /* a0>n2 is in the middle */
|
|
mergeArcEdges(rg, a0, a1, MERGE_LOWER);
|
|
mergeArcFaces(rg, a0, a1);
|
|
|
|
// Adjust node degree
|
|
//a1->head->degree--;
|
|
NodeDegreeDecrement(rg, a1->head);
|
|
//a0->tail->degree++;
|
|
NodeDegreeIncrement(rg, a0->tail);
|
|
|
|
mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight);
|
|
a1->head = a0->tail;
|
|
resizeArcBuckets(a1);
|
|
}
|
|
}
|
|
/* TRIANGLE POINTS UP */
|
|
else if (a0->tail->weight == a1->tail->weight) { /* tails are the same */
|
|
if (a0->head->weight > a1->head->weight) { /* a0->head->weight is in the middle */
|
|
mergeArcEdges(rg, a0, a1, MERGE_HIGHER);
|
|
mergeArcFaces(rg, a0, a1);
|
|
|
|
// Adjust node degree
|
|
//a1->tail->degree--;
|
|
NodeDegreeDecrement(rg, a1->tail);
|
|
//a0->head->degree++;
|
|
NodeDegreeIncrement(rg, a0->head);
|
|
|
|
mergeArcBuckets(a0, a1, a0->head->weight, a0->tail->weight);
|
|
a1->tail = a0->head;
|
|
resizeArcBuckets(a1);
|
|
}
|
|
else { /* a1->head->weight is in the middle */
|
|
mergeArcEdges(rg, a1, a0, MERGE_HIGHER);
|
|
mergeArcFaces(rg, a1, a0);
|
|
|
|
// Adjust node degree
|
|
//a0->tail->degree--;
|
|
NodeDegreeDecrement(rg, a0->tail);
|
|
//a1->head->degree++;
|
|
NodeDegreeIncrement(rg, a1->head);
|
|
|
|
mergeArcBuckets(a1, a0, a1->head->weight, a1->tail->weight);
|
|
a0->tail = a1->head;
|
|
resizeArcBuckets(a0);
|
|
}
|
|
}
|
|
else {
|
|
/* Need something here (OR NOT) */
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void glueByMergeSort(ReebGraph *rg, ReebArc *a0, ReebArc *a1, ReebEdge *e0, ReebEdge *e1)
|
|
{
|
|
int total = 0;
|
|
while (total == 0 && a0 != a1 && a0 != NULL && a1 != NULL) {
|
|
total = mergeArcs(rg, a0, a1);
|
|
|
|
if (total == 0) // if it wasn't a total merge, go forward {
|
|
if (a0->tail->weight < a1->tail->weight) {
|
|
a0 = nextArcMappedToEdge(a0, e0);
|
|
}
|
|
else {
|
|
a1 = nextArcMappedToEdge(a1, e1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void mergePaths(ReebGraph *rg, ReebEdge *e0, ReebEdge *e1, ReebEdge *e2)
|
|
{
|
|
ReebArc *a0, *a1, *a2;
|
|
a0 = e0->arc;
|
|
a1 = e1->arc;
|
|
a2 = e2->arc;
|
|
|
|
glueByMergeSort(rg, a0, a1, e0, e1);
|
|
glueByMergeSort(rg, a0, a2, e0, e2);
|
|
}
|
|
|
|
static ReebEdge *createArc(ReebGraph *rg, ReebNode *node1, ReebNode *node2)
|
|
{
|
|
ReebEdge *edge;
|
|
|
|
edge = BLI_edgehash_lookup(rg->emap, node1->index, node2->index);
|
|
|
|
// Only add existing edges that haven't been added yet
|
|
if (edge == NULL) {
|
|
ReebArc *arc;
|
|
ReebNode *v1, *v2;
|
|
float len, offset;
|
|
int i;
|
|
|
|
arc = MEM_callocN(sizeof(ReebArc), "reeb arc");
|
|
edge = MEM_callocN(sizeof(ReebEdge), "reeb edge");
|
|
|
|
arc->flag = 0; // clear flag on init
|
|
arc->symmetry_level = 0;
|
|
arc->faces = BLI_ghash_ptr_new("createArc gh");
|
|
|
|
if (node1->weight <= node2->weight) {
|
|
v1 = node1;
|
|
v2 = node2;
|
|
}
|
|
else {
|
|
v1 = node2;
|
|
v2 = node1;
|
|
}
|
|
|
|
arc->head = v1;
|
|
arc->tail = v2;
|
|
|
|
// increase node degree
|
|
//v1->degree++;
|
|
NodeDegreeIncrement(rg, v1);
|
|
//v2->degree++;
|
|
NodeDegreeIncrement(rg, v2);
|
|
|
|
BLI_edgehash_insert(rg->emap, node1->index, node2->index, edge);
|
|
|
|
edge->arc = arc;
|
|
edge->nextEdge = NULL;
|
|
edge->v1 = v1;
|
|
edge->v2 = v2;
|
|
|
|
BLI_addtail(&rg->arcs, arc);
|
|
BLI_addtail(&arc->edges, edge);
|
|
|
|
/* adding buckets for embedding */
|
|
allocArcBuckets(arc);
|
|
|
|
offset = arc->head->weight;
|
|
len = arc->tail->weight - arc->head->weight;
|
|
|
|
#if 0
|
|
/* This is the actual embedding filling described in the paper
|
|
* the problem is that it only works with really dense meshes
|
|
*/
|
|
if (arc->bcount > 0)
|
|
{
|
|
addVertToBucket(&(arc->buckets[0]), arc->head->co);
|
|
addVertToBucket(&(arc->buckets[arc->bcount - 1]), arc->tail->co);
|
|
}
|
|
#else
|
|
for (i = 0; i < arc->bcount; i++) {
|
|
float co[3];
|
|
float f = (arc->buckets[i].val - offset) / len;
|
|
|
|
interp_v3_v3v3(co, v1->p, v2->p, f);
|
|
addVertToBucket(&(arc->buckets[i]), co);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return edge;
|
|
}
|
|
|
|
static void addTriangleToGraph(ReebGraph *rg, ReebNode *n1, ReebNode *n2, ReebNode *n3, EditFace *efa)
|
|
{
|
|
ReebEdge *re1, *re2, *re3;
|
|
ReebEdge *e1, *e2, *e3;
|
|
float len1, len2, len3;
|
|
|
|
re1 = createArc(rg, n1, n2);
|
|
re2 = createArc(rg, n2, n3);
|
|
re3 = createArc(rg, n3, n1);
|
|
|
|
addFacetoArc(re1->arc, efa);
|
|
addFacetoArc(re2->arc, efa);
|
|
addFacetoArc(re3->arc, efa);
|
|
|
|
len1 = (float)fabs(n1->weight - n2->weight);
|
|
len2 = (float)fabs(n2->weight - n3->weight);
|
|
len3 = (float)fabs(n3->weight - n1->weight);
|
|
|
|
/* The rest of the algorithm assumes that e1 is the longest edge */
|
|
|
|
if (len1 >= len2 && len1 >= len3) {
|
|
e1 = re1;
|
|
e2 = re2;
|
|
e3 = re3;
|
|
}
|
|
else if (len2 >= len1 && len2 >= len3) {
|
|
e1 = re2;
|
|
e2 = re1;
|
|
e3 = re3;
|
|
}
|
|
else {
|
|
e1 = re3;
|
|
e2 = re2;
|
|
e3 = re1;
|
|
}
|
|
|
|
/* And e2 is the lowest edge
|
|
* If e3 is lower than e2, swap them
|
|
*/
|
|
if (e3->v1->weight < e2->v1->weight) {
|
|
ReebEdge *etmp = e2;
|
|
e2 = e3;
|
|
e3 = etmp;
|
|
}
|
|
|
|
|
|
mergePaths(rg, e1, e2, e3);
|
|
}
|
|
|
|
ReebGraph *generateReebGraph(EditMesh *em, int subdivisions)
|
|
{
|
|
ReebGraph *rg;
|
|
EditVert *eve;
|
|
EditFace *efa;
|
|
int index;
|
|
/*int totvert;*/
|
|
|
|
#ifdef DEBUG_REEB
|
|
int totfaces;
|
|
int countfaces = 0;
|
|
#endif
|
|
|
|
rg = newReebGraph();
|
|
|
|
rg->resolution = subdivisions;
|
|
|
|
/*totvert = BLI_listbase_count(&em->verts);*/ /*UNUSED*/
|
|
#ifdef DEBUG_REEB
|
|
totfaces = BLI_listbase_count(&em->faces);
|
|
#endif
|
|
|
|
renormalizeWeight(em, 1.0f);
|
|
|
|
/* Spread weight to minimize errors */
|
|
spreadWeight(em);
|
|
|
|
renormalizeWeight(em, (float)rg->resolution);
|
|
|
|
/* Adding vertice */
|
|
for (index = 0, eve = em->verts.first; eve; eve = eve->next) {
|
|
if (eve->h == 0) {
|
|
addNode(rg, eve);
|
|
eve->f2 = 0;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
/* Adding face, edge per edge */
|
|
for (efa = em->faces.first; efa; efa = efa->next) {
|
|
if (efa->h == 0) {
|
|
ReebNode *n1, *n2, *n3;
|
|
|
|
n1 = nodeData(efa->v1);
|
|
n2 = nodeData(efa->v2);
|
|
n3 = nodeData(efa->v3);
|
|
|
|
addTriangleToGraph(rg, n1, n2, n3, efa);
|
|
|
|
if (efa->v4) {
|
|
ReebNode *n4 = nodeData(efa->v4);
|
|
addTriangleToGraph(rg, n1, n3, n4, efa);
|
|
}
|
|
#ifdef DEBUG_REEB
|
|
countfaces++;
|
|
if (countfaces % 100 == 0) {
|
|
printf("\rface %i of %i", countfaces, totfaces);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
removeZeroNodes(rg);
|
|
|
|
removeNormalNodes(rg);
|
|
|
|
return rg;
|
|
}
|
|
|
|
/***************************************** WEIGHT UTILS **********************************************/
|
|
|
|
void renormalizeWeight(EditMesh *em, float newmax)
|
|
{
|
|
EditVert *eve;
|
|
float minimum, maximum, range;
|
|
|
|
if (em == NULL || BLI_listbase_is_empty(&em->verts))
|
|
return;
|
|
|
|
/* First pass, determine maximum and minimum */
|
|
eve = em->verts.first;
|
|
minimum = weightData(eve);
|
|
maximum = minimum;
|
|
for (; eve; eve = eve->next) {
|
|
maximum = MAX2(maximum, weightData(eve));
|
|
minimum = MIN2(minimum, weightData(eve));
|
|
}
|
|
|
|
range = maximum - minimum;
|
|
|
|
/* Normalize weights */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
float weight = (weightData(eve) - minimum) / range * newmax;
|
|
weightSetData(eve, weight);
|
|
}
|
|
}
|
|
|
|
|
|
int weightFromLoc(EditMesh *em, int axis)
|
|
{
|
|
EditVert *eve;
|
|
|
|
if (em == NULL || BLI_listbase_is_empty(&em->verts) || axis < 0 || axis > 2)
|
|
return 0;
|
|
|
|
/* Copy coordinate in weight */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
weightSetData(eve, eve->co[axis]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void addTriangle(LinearSolver *context, EditVert *v1, EditVert *v2, EditVert *v3, int e1, int e2, int e3)
|
|
{
|
|
/* Angle opposite e1 */
|
|
float t1 = cotangent_tri_weight_v3(v1->co, v2->co, v3->co) / e2;
|
|
|
|
/* Angle opposite e2 */
|
|
float t2 = cotangent_tri_weight_v3(v2->co, v3->co, v1->co) / e3;
|
|
|
|
/* Angle opposite e3 */
|
|
float t3 = cotangent_tri_weight_v3(v3->co, v1->co, v2->co) / e1;
|
|
|
|
int i1 = indexData(v1);
|
|
int i2 = indexData(v2);
|
|
int i3 = indexData(v3);
|
|
|
|
EIG_linear_solver_matrix_add(context, i1, i1, t2 + t3);
|
|
EIG_linear_solver_matrix_add(context, i2, i2, t1 + t3);
|
|
EIG_linear_solver_matrix_add(context, i3, i3, t1 + t2);
|
|
|
|
EIG_linear_solver_matrix_add(context, i1, i2, -t3);
|
|
EIG_linear_solver_matrix_add(context, i2, i1, -t3);
|
|
|
|
EIG_linear_solver_matrix_add(context, i2, i3, -t1);
|
|
EIG_linear_solver_matrix_add(context, i3, i2, -t1);
|
|
|
|
EIG_linear_solver_matrix_add(context, i3, i1, -t2);
|
|
EIG_linear_solver_matrix_add(context, i1, i3, -t2);
|
|
}
|
|
|
|
int weightToHarmonic(EditMesh *em, EdgeIndex *indexed_edges)
|
|
{
|
|
LinearSolver *context;
|
|
NLboolean success;
|
|
EditVert *eve;
|
|
EditEdge *eed;
|
|
EditFace *efa;
|
|
int totvert = 0;
|
|
int index;
|
|
int rval;
|
|
|
|
/* Find local extrema */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
totvert++;
|
|
}
|
|
|
|
/* Solve */
|
|
|
|
context = EIG_linear_solver_new(, 0, totvert, 1);
|
|
|
|
/* Find local extrema */
|
|
for (index = 0, eve = em->verts.first; eve; index++, eve = eve->next) {
|
|
if (eve->h == 0) {
|
|
EditEdge *eed;
|
|
int maximum = 1;
|
|
int minimum = 1;
|
|
|
|
NextEdgeForVert(indexed_edges, -1); /* Reset next edge */
|
|
for (eed = NextEdgeForVert(indexed_edges, index); eed && (maximum || minimum); eed = NextEdgeForVert(indexed_edges, index)) {
|
|
EditVert *eve2;
|
|
|
|
if (eed->v1 == eve) {
|
|
eve2 = eed->v2;
|
|
}
|
|
else {
|
|
eve2 = eed->v1;
|
|
}
|
|
|
|
if (eve2->h == 0) {
|
|
/* Adjacent vertex is bigger, not a local maximum */
|
|
if (weightData(eve2) > weightData(eve)) {
|
|
maximum = 0;
|
|
}
|
|
/* Adjacent vertex is smaller, not a local minimum */
|
|
else if (weightData(eve2) < weightData(eve)) {
|
|
minimum = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (maximum || minimum) {
|
|
float w = weightData(eve);
|
|
eve->f1 = 0;
|
|
EIG_linear_solver_variable_set(context, 0, index, w);
|
|
EIG_linear_solver_variable_lock(context, index);
|
|
}
|
|
else {
|
|
eve->f1 = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Zero edge weight */
|
|
for (eed = em->edges.first; eed; eed = eed->next) {
|
|
eed->tmp.l = 0;
|
|
}
|
|
|
|
/* Add faces count to the edge weight */
|
|
for (efa = em->faces.first; efa; efa = efa->next) {
|
|
if (efa->h == 0) {
|
|
efa->e1->tmp.l++;
|
|
efa->e2->tmp.l++;
|
|
efa->e3->tmp.l++;
|
|
|
|
if (efa->e4) {
|
|
efa->e4->tmp.l++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Add faces angle to the edge weight */
|
|
for (efa = em->faces.first; efa; efa = efa->next) {
|
|
if (efa->h == 0) {
|
|
if (efa->v4 == NULL) {
|
|
addTriangle(context, efa->v1, efa->v2, efa->v3, efa->e1->tmp.l, efa->e2->tmp.l, efa->e3->tmp.l);
|
|
}
|
|
else {
|
|
addTriangle(context, efa->v1, efa->v2, efa->v3, efa->e1->tmp.l, efa->e2->tmp.l, 2);
|
|
addTriangle(context, efa->v3, efa->v4, efa->v1, efa->e3->tmp.l, efa->e4->tmp.l, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
success = EIG_linear_solver_solve(context);
|
|
|
|
if (success) {
|
|
rval = 1;
|
|
for (index = 0, eve = em->verts.first; eve; index++, eve = eve->next) {
|
|
weightSetData(eve, EIG_linear_solver_variable_get(context, 0, index));
|
|
}
|
|
}
|
|
else {
|
|
rval = 0;
|
|
}
|
|
|
|
EIG_linear_solver_delete(context);
|
|
|
|
return rval;
|
|
}
|
|
|
|
|
|
EditEdge *NextEdgeForVert(EdgeIndex *indexed_edges, int index)
|
|
{
|
|
static int offset = -1;
|
|
|
|
/* Reset method, call with NULL mesh pointer */
|
|
if (index == -1) {
|
|
offset = -1;
|
|
return NULL;
|
|
}
|
|
|
|
/* first pass, start at the head of the list */
|
|
if (offset == -1) {
|
|
offset = indexed_edges->offset[index];
|
|
}
|
|
/* subsequent passes, start on the next edge */
|
|
else {
|
|
offset++;
|
|
}
|
|
|
|
return indexed_edges->edges[offset];
|
|
}
|
|
|
|
static void shortestPathsFromVert(EditMesh *em, EditVert *starting_vert, EdgeIndex *indexed_edges)
|
|
{
|
|
Heap *edge_heap;
|
|
EditVert *current_eve = NULL;
|
|
EditEdge *eed = NULL;
|
|
EditEdge *select_eed = NULL;
|
|
|
|
edge_heap = BLI_heap_new();
|
|
|
|
current_eve = starting_vert;
|
|
|
|
/* insert guard in heap, when that is returned, no more edges */
|
|
BLI_heap_insert(edge_heap, FLT_MAX, NULL);
|
|
|
|
/* Initialize edge flag */
|
|
for (eed = em->edges.first; eed; eed = eed->next) {
|
|
eed->f1 = 0;
|
|
}
|
|
|
|
while (BLI_heap_size(edge_heap) > 0) {
|
|
float current_weight;
|
|
|
|
current_eve->f1 = 1; /* mark vertex as selected */
|
|
|
|
/* Add all new edges connected to current_eve to the list */
|
|
NextEdgeForVert(indexed_edges, -1); // Reset next edge
|
|
for (eed = NextEdgeForVert(indexed_edges, indexData(current_eve)); eed; eed = NextEdgeForVert(indexed_edges, indexData(current_eve))) {
|
|
if (eed->f1 == 0) {
|
|
BLI_heap_insert(edge_heap, weightData(current_eve) + eed->tmp.fp, eed);
|
|
eed->f1 = 1;
|
|
}
|
|
}
|
|
|
|
/* Find next shortest edge with unselected verts */
|
|
do {
|
|
current_weight = BLI_heap_node_value(BLI_heap_top(edge_heap));
|
|
select_eed = BLI_heap_popmin(edge_heap);
|
|
} while (select_eed != NULL && select_eed->v1->f1 != 0 && select_eed->v2->f1);
|
|
|
|
if (select_eed != NULL) {
|
|
select_eed->f1 = 2;
|
|
|
|
if (select_eed->v1->f1 == 0) /* v1 is the new vertex */ {
|
|
current_eve = select_eed->v1;
|
|
}
|
|
else { /* otherwise, it's v2 */
|
|
current_eve = select_eed->v2;
|
|
}
|
|
|
|
weightSetData(current_eve, current_weight);
|
|
}
|
|
}
|
|
|
|
BLI_heap_free(edge_heap, NULL);
|
|
}
|
|
|
|
static void freeEdgeIndex(EdgeIndex *indexed_edges)
|
|
{
|
|
MEM_freeN(indexed_edges->offset);
|
|
MEM_freeN(indexed_edges->edges);
|
|
}
|
|
|
|
static void buildIndexedEdges(EditMesh *em, EdgeIndex *indexed_edges)
|
|
{
|
|
EditVert *eve;
|
|
EditEdge *eed;
|
|
int totvert = 0;
|
|
int tot_indexed = 0;
|
|
int offset = 0;
|
|
|
|
totvert = BLI_listbase_count(&em->verts);
|
|
|
|
indexed_edges->offset = MEM_callocN(totvert * sizeof(int), "EdgeIndex offset");
|
|
|
|
for (eed = em->edges.first; eed; eed = eed->next) {
|
|
if (eed->v1->h == 0 && eed->v2->h == 0) {
|
|
tot_indexed += 2;
|
|
indexed_edges->offset[indexData(eed->v1)]++;
|
|
indexed_edges->offset[indexData(eed->v2)]++;
|
|
}
|
|
}
|
|
|
|
tot_indexed += totvert;
|
|
|
|
indexed_edges->edges = MEM_callocN(tot_indexed * sizeof(EditEdge *), "EdgeIndex edges");
|
|
|
|
/* setting vert offsets */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
if (eve->h == 0) {
|
|
int d = indexed_edges->offset[indexData(eve)];
|
|
indexed_edges->offset[indexData(eve)] = offset;
|
|
offset += d + 1;
|
|
}
|
|
}
|
|
|
|
/* adding edges in array */
|
|
for (eed = em->edges.first; eed; eed = eed->next) {
|
|
if (eed->v1->h == 0 && eed->v2->h == 0) {
|
|
int i;
|
|
for (i = indexed_edges->offset[indexData(eed->v1)]; i < tot_indexed; i++) {
|
|
if (indexed_edges->edges[i] == NULL) {
|
|
indexed_edges->edges[i] = eed;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i = indexed_edges->offset[indexData(eed->v2)]; i < tot_indexed; i++) {
|
|
if (indexed_edges->edges[i] == NULL) {
|
|
indexed_edges->edges[i] = eed;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int weightFromDistance(EditMesh *em, EdgeIndex *indexed_edges)
|
|
{
|
|
EditVert *eve;
|
|
int totedge = 0;
|
|
int totvert = 0;
|
|
int vCount = 0;
|
|
|
|
totvert = BLI_listbase_count(&em->verts);
|
|
|
|
if (em == NULL || totvert == 0) {
|
|
return 0;
|
|
}
|
|
|
|
totedge = BLI_listbase_count(&em->edges);
|
|
|
|
if (totedge == 0) {
|
|
return 0;
|
|
}
|
|
|
|
/* Initialize vertice flag and find at least one selected vertex */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
eve->f1 = 0;
|
|
if (eve->f & SELECT) {
|
|
vCount = 1;
|
|
}
|
|
}
|
|
|
|
if (vCount == 0) {
|
|
return 0; /* no selected vert, failure */
|
|
}
|
|
else {
|
|
EditEdge *eed;
|
|
int allDone = 0;
|
|
|
|
/* Calculate edge weight */
|
|
for (eed = em->edges.first; eed; eed = eed->next) {
|
|
if (eed->v1->h == 0 && eed->v2->h == 0) {
|
|
eed->tmp.fp = len_v3v3(eed->v1->co, eed->v2->co);
|
|
}
|
|
}
|
|
|
|
/* Apply dijkstra spf for each selected vert */
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
if (eve->f & SELECT) {
|
|
shortestPathsFromVert(em, eve, indexed_edges);
|
|
}
|
|
}
|
|
|
|
/* connect unselected islands */
|
|
while (allDone == 0) {
|
|
EditVert *selected_eve = NULL;
|
|
float selected_weight = 0;
|
|
float min_distance = FLT_MAX;
|
|
|
|
allDone = 1;
|
|
|
|
for (eve = em->verts.first; eve; eve = eve->next) {
|
|
/* for every vertex visible that hasn't been processed yet */
|
|
if (eve->h == 0 && eve->f1 != 1) {
|
|
EditVert *closest_eve;
|
|
|
|
/* find the closest processed vertex */
|
|
for (closest_eve = em->verts.first; closest_eve; closest_eve = closest_eve->next) {
|
|
/* vertex is already processed and distance is smaller than current minimum */
|
|
if (closest_eve->f1 == 1) {
|
|
float distance = len_v3v3(closest_eve->co, eve->co);
|
|
if (distance < min_distance) {
|
|
min_distance = distance;
|
|
selected_eve = eve;
|
|
selected_weight = weightData(closest_eve);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selected_eve) {
|
|
allDone = 0;
|
|
|
|
weightSetData(selected_eve, selected_weight + min_distance);
|
|
shortestPathsFromVert(em, selected_eve, indexed_edges);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (eve = em->verts.first; eve && vCount == 0; eve = eve->next) {
|
|
if (eve->f1 == 0) {
|
|
printf("vertex not reached\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
/****************************************** BUCKET ITERATOR **************************************************/
|
|
|
|
static void *headNode(void *arg);
|
|
static void *tailNode(void *arg);
|
|
static void *nextBucket(void *arg);
|
|
static void *nextNBucket(void *arg, int n);
|
|
static void *peekBucket(void *arg, int n);
|
|
static void *previousBucket(void *arg);
|
|
static int iteratorStopped(void *arg);
|
|
|
|
static void initIteratorFct(ReebArcIterator *iter)
|
|
{
|
|
iter->head = headNode;
|
|
iter->tail = tailNode;
|
|
iter->peek = peekBucket;
|
|
iter->next = nextBucket;
|
|
iter->nextN = nextNBucket;
|
|
iter->previous = previousBucket;
|
|
iter->stopped = iteratorStopped;
|
|
}
|
|
|
|
static void setIteratorValues(ReebArcIterator *iter, EmbedBucket *bucket)
|
|
{
|
|
if (bucket) {
|
|
iter->p = bucket->p;
|
|
iter->no = bucket->no;
|
|
}
|
|
else {
|
|
iter->p = NULL;
|
|
iter->no = NULL;
|
|
}
|
|
iter->size = 0;
|
|
}
|
|
|
|
void initArcIterator(BArcIterator *arg, ReebArc *arc, ReebNode *head)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
|
|
initIteratorFct(iter);
|
|
iter->arc = arc;
|
|
|
|
if (head == arc->head) {
|
|
iter->start = 0;
|
|
iter->end = arc->bcount - 1;
|
|
iter->stride = 1;
|
|
}
|
|
else {
|
|
iter->start = arc->bcount - 1;
|
|
iter->end = 0;
|
|
iter->stride = -1;
|
|
}
|
|
|
|
iter->length = arc->bcount;
|
|
|
|
iter->index = -1;
|
|
}
|
|
|
|
void initArcIteratorStart(BArcIterator *arg, struct ReebArc *arc, struct ReebNode *head, int start)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
|
|
initIteratorFct(iter);
|
|
iter->arc = arc;
|
|
|
|
if (head == arc->head) {
|
|
iter->start = start;
|
|
iter->end = arc->bcount - 1;
|
|
iter->stride = 1;
|
|
}
|
|
else {
|
|
iter->start = arc->bcount - 1 - start;
|
|
iter->end = 0;
|
|
iter->stride = -1;
|
|
}
|
|
|
|
iter->index = -1;
|
|
|
|
iter->length = arc->bcount - start;
|
|
|
|
if (start >= arc->bcount) {
|
|
iter->start = iter->end; /* stop iterator since it's past its end */
|
|
}
|
|
}
|
|
|
|
void initArcIterator2(BArcIterator *arg, ReebArc *arc, int start, int end)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
|
|
initIteratorFct(iter);
|
|
iter->arc = arc;
|
|
|
|
iter->start = start;
|
|
iter->end = end;
|
|
|
|
if (end > start) {
|
|
iter->stride = 1;
|
|
}
|
|
else {
|
|
iter->stride = -1;
|
|
}
|
|
|
|
iter->index = -1;
|
|
|
|
iter->length = abs(iter->end - iter->start) + 1;
|
|
}
|
|
|
|
static void *headNode(void *arg)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
ReebNode *node;
|
|
|
|
if (iter->start < iter->end) {
|
|
node = iter->arc->head;
|
|
}
|
|
else {
|
|
node = iter->arc->tail;
|
|
}
|
|
|
|
iter->p = node->p;
|
|
iter->no = node->no;
|
|
iter->size = 0;
|
|
|
|
return node;
|
|
}
|
|
|
|
static void *tailNode(void *arg)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
ReebNode *node;
|
|
|
|
if (iter->start < iter->end) {
|
|
node = iter->arc->tail;
|
|
}
|
|
else {
|
|
node = iter->arc->head;
|
|
}
|
|
|
|
iter->p = node->p;
|
|
iter->no = node->no;
|
|
iter->size = 0;
|
|
|
|
return node;
|
|
}
|
|
|
|
static void *nextBucket(void *arg)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
EmbedBucket *result = NULL;
|
|
|
|
iter->index++;
|
|
|
|
if (iter->index < iter->length) {
|
|
result = &(iter->arc->buckets[iter->start + (iter->stride * iter->index)]);
|
|
}
|
|
|
|
setIteratorValues(iter, result);
|
|
return result;
|
|
}
|
|
|
|
static void *nextNBucket(void *arg, int n)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
EmbedBucket *result = NULL;
|
|
|
|
iter->index += n;
|
|
|
|
/* check if passed end */
|
|
if (iter->index < iter->length) {
|
|
result = &(iter->arc->buckets[iter->start + (iter->stride * iter->index)]);
|
|
}
|
|
|
|
setIteratorValues(iter, result);
|
|
return result;
|
|
}
|
|
|
|
static void *peekBucket(void *arg, int n)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
EmbedBucket *result = NULL;
|
|
int index = iter->index + n;
|
|
|
|
/* check if passed end */
|
|
if (index < iter->length) {
|
|
result = &(iter->arc->buckets[iter->start + (iter->stride * index)]);
|
|
}
|
|
|
|
setIteratorValues(iter, result);
|
|
return result;
|
|
}
|
|
|
|
static void *previousBucket(void *arg)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
EmbedBucket *result = NULL;
|
|
|
|
if (iter->index > 0) {
|
|
iter->index--;
|
|
result = &(iter->arc->buckets[iter->start + (iter->stride * iter->index)]);
|
|
}
|
|
|
|
setIteratorValues(iter, result);
|
|
return result;
|
|
}
|
|
|
|
static int iteratorStopped(void *arg)
|
|
{
|
|
ReebArcIterator *iter = (ReebArcIterator *)arg;
|
|
|
|
if (iter->index >= iter->length) {
|
|
return 1;
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/************************ PUBLIC FUNCTIONS *********************************************/
|
|
|
|
ReebGraph *BIF_ReebGraphMultiFromEditMesh(bContext *C)
|
|
{
|
|
(void)C;
|
|
return NULL;
|
|
#if 0
|
|
Scene *scene = CTX_data_scene(C);
|
|
Object *obedit = CTX_data_edit_object(C);
|
|
EditMesh *em = BKE_mesh_get_editmesh(((Mesh *)obedit->data));
|
|
EdgeIndex indexed_edges;
|
|
VertexData *data;
|
|
ReebGraph *rg = NULL;
|
|
ReebGraph *rgi, *previous;
|
|
int i, nb_levels = REEB_MAX_MULTI_LEVEL;
|
|
|
|
if (em == NULL)
|
|
return NULL;
|
|
|
|
data = allocVertexData(em);
|
|
|
|
buildIndexedEdges(em, &indexed_edges);
|
|
|
|
if (weightFromDistance(em, &indexed_edges) == 0)
|
|
{
|
|
// XXX error("No selected vertex\n");
|
|
freeEdgeIndex(&indexed_edges);
|
|
return NULL;
|
|
}
|
|
|
|
renormalizeWeight(em, 1.0f);
|
|
|
|
if (scene->toolsettings->skgen_options & SKGEN_HARMONIC)
|
|
{
|
|
weightToHarmonic(em, &indexed_edges);
|
|
}
|
|
|
|
freeEdgeIndex(&indexed_edges);
|
|
|
|
rg = generateReebGraph(em, scene->toolsettings->skgen_resolution);
|
|
|
|
/* Remove arcs without embedding */
|
|
filterNullReebGraph(rg);
|
|
|
|
/* smart filter and loop filter on basic level */
|
|
filterGraph(rg, SKGEN_FILTER_SMART, 0, 0);
|
|
|
|
repositionNodes(rg);
|
|
|
|
/* Filtering might have created degree 2 nodes, so remove them */
|
|
removeNormalNodes(rg);
|
|
|
|
joinSubgraphs(rg, 1.0);
|
|
|
|
BLI_buildAdjacencyList((BGraph *)rg);
|
|
|
|
/* calc length before copy, so we have same length on all levels */
|
|
BLI_calcGraphLength((BGraph *)rg);
|
|
|
|
previous = NULL;
|
|
for (i = 0; i <= nb_levels; i++)
|
|
{
|
|
rgi = rg;
|
|
|
|
/* don't filter last level */
|
|
if (i > 0)
|
|
{
|
|
float internal_threshold;
|
|
float external_threshold;
|
|
|
|
/* filter internal progressively in second half only*/
|
|
if (i > nb_levels / 2)
|
|
{
|
|
internal_threshold = rg->length * scene->toolsettings->skgen_threshold_internal;
|
|
}
|
|
else {
|
|
internal_threshold = rg->length * scene->toolsettings->skgen_threshold_internal * (2 * i / (float)nb_levels);
|
|
}
|
|
|
|
external_threshold = rg->length * scene->toolsettings->skgen_threshold_external * (i / (float)nb_levels);
|
|
|
|
filterGraph(rgi, scene->toolsettings->skgen_options, internal_threshold, external_threshold);
|
|
}
|
|
|
|
if (i < nb_levels)
|
|
{
|
|
rg = copyReebGraph(rgi, i + 1);
|
|
}
|
|
|
|
finalizeGraph(rgi, scene->toolsettings->skgen_postpro_passes, scene->toolsettings->skgen_postpro);
|
|
|
|
BLI_markdownSymmetry((BGraph *)rgi, rgi->nodes.first, scene->toolsettings->skgen_symmetry_limit);
|
|
|
|
if (previous != NULL)
|
|
{
|
|
relinkNodes(rgi, previous);
|
|
}
|
|
previous = rgi;
|
|
}
|
|
|
|
verifyMultiResolutionLinks(rg, 0);
|
|
|
|
MEM_freeN(data);
|
|
|
|
/* no need to load the editmesh back into the object, just
|
|
* free it (avoids ngon conversion issues too going back the other way) */
|
|
free_editMesh(em);
|
|
MEM_freeN(em);
|
|
|
|
return rg;
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
|
|
ReebGraph *BIF_ReebGraphFromEditMesh(void)
|
|
{
|
|
EditMesh *em = G.editMesh;
|
|
EdgeIndex indexed_edges;
|
|
VertexData *data;
|
|
ReebGraph *rg = NULL;
|
|
|
|
if (em == NULL)
|
|
return NULL;
|
|
|
|
data = allocVertexData(em);
|
|
|
|
buildIndexedEdges(em, &indexed_edges);
|
|
|
|
if (weightFromDistance(em, &indexed_edges) == 0)
|
|
{
|
|
error("No selected vertex\n");
|
|
freeEdgeIndex(&indexed_edges);
|
|
freeEdgeIndex(&indexed_edges);
|
|
return NULL;
|
|
}
|
|
|
|
renormalizeWeight(em, 1.0f);
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_HARMONIC)
|
|
{
|
|
weightToHarmonic(em, &indexed_edges);
|
|
}
|
|
|
|
freeEdgeIndex(&indexed_edges);
|
|
|
|
#ifdef DEBUG_REEB
|
|
// weightToVCol(em, 1);
|
|
#endif
|
|
|
|
rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution);
|
|
|
|
|
|
/* Remove arcs without embedding */
|
|
filterNullReebGraph(rg);
|
|
|
|
/* smart filter and loop filter on basic level */
|
|
filterGraph(rg, SKGEN_FILTER_SMART, 0, 0);
|
|
|
|
repositionNodes(rg);
|
|
|
|
/* Filtering might have created degree 2 nodes, so remove them */
|
|
removeNormalNodes(rg);
|
|
|
|
joinSubgraphs(rg, 1.0);
|
|
|
|
BLI_buildAdjacencyList((BGraph *)rg);
|
|
|
|
/* calc length before copy, so we have same length on all levels */
|
|
BLI_calcGraphLength((BGraph *)rg);
|
|
|
|
filterGraph(rg, G.scene->toolsettings->skgen_options, G.scene->toolsettings->skgen_threshold_internal, G.scene->toolsettings->skgen_threshold_external);
|
|
|
|
finalizeGraph(rg, G.scene->toolsettings->skgen_postpro_passes, G.scene->toolsettings->skgen_postpro);
|
|
|
|
#ifdef DEBUG_REEB
|
|
REEB_exportGraph(rg, -1);
|
|
|
|
arcToVCol(rg, em, 0);
|
|
//angleToVCol(em, 1);
|
|
#endif
|
|
|
|
printf("DONE\n");
|
|
printf("%i subgraphs\n", BLI_FlagSubgraphs((BGraph *)rg));
|
|
|
|
MEM_freeN(data);
|
|
|
|
return rg;
|
|
}
|
|
|
|
void BIF_GlobalReebFree()
|
|
{
|
|
if (GLOBAL_RG != NULL)
|
|
{
|
|
REEB_freeGraph(GLOBAL_RG);
|
|
GLOBAL_RG = NULL;
|
|
}
|
|
}
|
|
|
|
void BIF_GlobalReebGraphFromEditMesh(void)
|
|
{
|
|
ReebGraph *rg;
|
|
|
|
BIF_GlobalReebFree();
|
|
|
|
rg = BIF_ReebGraphMultiFromEditMesh();
|
|
|
|
GLOBAL_RG = rg;
|
|
}
|
|
|
|
void REEB_draw()
|
|
{
|
|
ReebGraph *rg;
|
|
ReebArc *arc;
|
|
int i = 0;
|
|
|
|
if (GLOBAL_RG == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (GLOBAL_RG->link_up && G.scene->toolsettings->skgen_options & SKGEN_DISP_ORIG)
|
|
{
|
|
for (rg = GLOBAL_RG; rg->link_up; rg = rg->link_up) ;
|
|
}
|
|
else {
|
|
i = G.scene->toolsettings->skgen_multi_level;
|
|
|
|
for (rg = GLOBAL_RG; rg->multi_level != i && rg->link_up; rg = rg->link_up) ;
|
|
}
|
|
|
|
glPointSize(BIF_GetThemeValuef(TH_VERTEX_SIZE));
|
|
|
|
glDisable(GL_DEPTH_TEST);
|
|
for (arc = rg->arcs.first; arc; arc = arc->next, i++)
|
|
{
|
|
ReebArcIterator arc_iter;
|
|
BArcIterator *iter = (BArcIterator *)&arc_iter;
|
|
float vec[3];
|
|
char text[128];
|
|
char *s = text;
|
|
|
|
glLineWidth(BIF_GetThemeValuef(TH_VERTEX_SIZE) + 2);
|
|
glColor3f(0, 0, 0);
|
|
glBegin(GL_LINE_STRIP);
|
|
glVertex3fv(arc->head->p);
|
|
|
|
if (arc->bcount)
|
|
{
|
|
initArcIterator(iter, arc, arc->head);
|
|
for (IT_next(iter); IT_stopped(iter) == 0; IT_next(iter))
|
|
{
|
|
glVertex3fv(iter->p);
|
|
}
|
|
}
|
|
|
|
glVertex3fv(arc->tail->p);
|
|
glEnd();
|
|
|
|
glLineWidth(BIF_GetThemeValuef(TH_VERTEX_SIZE));
|
|
|
|
if (arc->symmetry_level == 1)
|
|
{
|
|
glColor3f(1, 0, 0);
|
|
}
|
|
else if (arc->symmetry_flag == SYM_SIDE_POSITIVE || arc->symmetry_flag == SYM_SIDE_NEGATIVE)
|
|
{
|
|
glColor3f(1, 0.5f, 0);
|
|
}
|
|
else if (arc->symmetry_flag >= SYM_SIDE_RADIAL)
|
|
{
|
|
glColor3f(0.5f, 1, 0);
|
|
}
|
|
else {
|
|
glColor3f(1, 1, 0);
|
|
}
|
|
glBegin(GL_LINE_STRIP);
|
|
glVertex3fv(arc->head->p);
|
|
|
|
if (arc->bcount)
|
|
{
|
|
initArcIterator(iter, arc, arc->head);
|
|
for (iter->next(iter); IT_stopped(iter) == 0; iter->next(iter))
|
|
{
|
|
glVertex3fv(iter->p);
|
|
}
|
|
}
|
|
|
|
glVertex3fv(arc->tail->p);
|
|
glEnd();
|
|
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_DISP_EMBED)
|
|
{
|
|
glColor3f(1, 1, 1);
|
|
glBegin(GL_POINTS);
|
|
glVertex3fv(arc->head->p);
|
|
glVertex3fv(arc->tail->p);
|
|
|
|
glColor3f(0.5f, 0.5f, 1);
|
|
if (arc->bcount)
|
|
{
|
|
initArcIterator(iter, arc, arc->head);
|
|
for (iter->next(iter); IT_stopped(iter) == 0; iter->next(iter))
|
|
{
|
|
glVertex3fv(iter->p);
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_DISP_INDEX)
|
|
{
|
|
mid_v3_v3v3(vec, arc->head->p, arc->tail->p);
|
|
s += sprintf(s, "%i (%i-%i-%i) ", i, arc->symmetry_level, arc->symmetry_flag, arc->symmetry_group);
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_DISP_WEIGHT)
|
|
{
|
|
s += sprintf(s, "w:%0.3f ", arc->tail->weight - arc->head->weight);
|
|
}
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_DISP_LENGTH)
|
|
{
|
|
s += sprintf(s, "l:%0.3f", arc->length);
|
|
}
|
|
|
|
glColor3f(0, 1, 0);
|
|
glRasterPos3fv(vec);
|
|
BMF_DrawString(G.fonts, text);
|
|
}
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_DISP_INDEX)
|
|
{
|
|
sprintf(text, " %i", arc->head->index);
|
|
glRasterPos3fv(arc->head->p);
|
|
BMF_DrawString(G.fonts, text);
|
|
|
|
sprintf(text, " %i", arc->tail->index);
|
|
glRasterPos3fv(arc->tail->p);
|
|
BMF_DrawString(G.fonts, text);
|
|
}
|
|
}
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
#endif
|