1013 lines
23 KiB
C
1013 lines
23 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 *****
|
|
* graph.c: Common graph interface and methods
|
|
*/
|
|
|
|
/** \file blender/blenlib/intern/graph.c
|
|
* \ingroup bli
|
|
*/
|
|
|
|
#include <float.h>
|
|
#include <math.h>
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_graph.h"
|
|
#include "BLI_math.h"
|
|
|
|
|
|
static void testRadialSymmetry(BGraph *graph, BNode *root_node, RadialArc *ring, int total, float axis[3], float limit, int group);
|
|
|
|
static void handleAxialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit);
|
|
static void testAxialSymmetry(BGraph *graph, BNode *root_node, BNode *node1, BNode *node2, BArc *arc1, BArc *arc2, float axis[3], float limit, int group);
|
|
static void flagAxialSymmetry(BNode *root_node, BNode *end_node, BArc *arc, int group);
|
|
|
|
void BLI_freeNode(BGraph *graph, BNode *node)
|
|
{
|
|
if (node->arcs) {
|
|
MEM_freeN(node->arcs);
|
|
}
|
|
|
|
if (graph->free_node) {
|
|
graph->free_node(node);
|
|
}
|
|
}
|
|
|
|
void BLI_removeNode(BGraph *graph, BNode *node)
|
|
{
|
|
BLI_freeNode(graph, node);
|
|
BLI_freelinkN(&graph->nodes, node);
|
|
}
|
|
|
|
BNode *BLI_otherNode(BArc *arc, BNode *node)
|
|
{
|
|
return (arc->head == node) ? arc->tail : arc->head;
|
|
}
|
|
|
|
void BLI_removeArc(BGraph *graph, BArc *arc)
|
|
{
|
|
if (graph->free_arc) {
|
|
graph->free_arc(arc);
|
|
}
|
|
|
|
BLI_freelinkN(&graph->arcs, arc);
|
|
}
|
|
|
|
void BLI_flagNodes(BGraph *graph, int flag)
|
|
{
|
|
BNode *node;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
node->flag = flag;
|
|
}
|
|
}
|
|
|
|
void BLI_flagArcs(BGraph *graph, int flag)
|
|
{
|
|
BArc *arc;
|
|
|
|
for (arc = graph->arcs.first; arc; arc = arc->next) {
|
|
arc->flag = flag;
|
|
}
|
|
}
|
|
|
|
static void addArcToNodeAdjacencyList(BNode *node, BArc *arc)
|
|
{
|
|
node->arcs[node->flag] = arc;
|
|
node->flag++;
|
|
}
|
|
|
|
void BLI_buildAdjacencyList(BGraph *graph)
|
|
{
|
|
BNode *node;
|
|
BArc *arc;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->arcs != NULL) {
|
|
MEM_freeN(node->arcs);
|
|
}
|
|
|
|
node->arcs = MEM_callocN((node->degree) * sizeof(BArc *), "adjacency list");
|
|
|
|
/* temporary use to indicate the first index available in the lists */
|
|
node->flag = 0;
|
|
}
|
|
|
|
for (arc = graph->arcs.first; arc; arc = arc->next) {
|
|
addArcToNodeAdjacencyList(arc->head, arc);
|
|
addArcToNodeAdjacencyList(arc->tail, arc);
|
|
}
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->degree != node->flag) {
|
|
printf("error in node [%p]. Added only %i arcs out of %i\n", (void *)node, node->flag, node->degree);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BLI_rebuildAdjacencyListForNode(BGraph *graph, BNode *node)
|
|
{
|
|
BArc *arc;
|
|
|
|
if (node->arcs != NULL) {
|
|
MEM_freeN(node->arcs);
|
|
}
|
|
|
|
node->arcs = MEM_callocN((node->degree) * sizeof(BArc *), "adjacency list");
|
|
|
|
/* temporary use to indicate the first index available in the lists */
|
|
node->flag = 0;
|
|
|
|
for (arc = graph->arcs.first; arc; arc = arc->next) {
|
|
if (arc->head == node) {
|
|
addArcToNodeAdjacencyList(arc->head, arc);
|
|
}
|
|
else if (arc->tail == node) {
|
|
addArcToNodeAdjacencyList(arc->tail, arc);
|
|
}
|
|
}
|
|
|
|
if (node->degree != node->flag) {
|
|
printf("error in node [%p]. Added only %i arcs out of %i\n", (void *)node, node->flag, node->degree);
|
|
}
|
|
}
|
|
|
|
void BLI_freeAdjacencyList(BGraph *graph)
|
|
{
|
|
BNode *node;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->arcs != NULL) {
|
|
MEM_freeN(node->arcs);
|
|
node->arcs = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int BLI_hasAdjacencyList(BGraph *graph)
|
|
{
|
|
BNode *node;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->arcs == NULL) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void BLI_replaceNodeInArc(BGraph *graph, BArc *arc, BNode *node_src, BNode *node_replaced)
|
|
{
|
|
if (arc->head == node_replaced) {
|
|
arc->head = node_src;
|
|
node_src->degree++;
|
|
}
|
|
|
|
if (arc->tail == node_replaced) {
|
|
arc->tail = node_src;
|
|
node_src->degree++;
|
|
}
|
|
|
|
if (arc->head == arc->tail) {
|
|
node_src->degree -= 2;
|
|
|
|
graph->free_arc(arc);
|
|
BLI_freelinkN(&graph->arcs, arc);
|
|
}
|
|
|
|
if (node_replaced->degree == 0) {
|
|
BLI_removeNode(graph, node_replaced);
|
|
}
|
|
}
|
|
|
|
void BLI_replaceNode(BGraph *graph, BNode *node_src, BNode *node_replaced)
|
|
{
|
|
BArc *arc, *next_arc;
|
|
|
|
for (arc = graph->arcs.first; arc; arc = next_arc) {
|
|
next_arc = arc->next;
|
|
|
|
if (arc->head == node_replaced) {
|
|
arc->head = node_src;
|
|
node_replaced->degree--;
|
|
node_src->degree++;
|
|
}
|
|
|
|
if (arc->tail == node_replaced) {
|
|
arc->tail = node_src;
|
|
node_replaced->degree--;
|
|
node_src->degree++;
|
|
}
|
|
|
|
if (arc->head == arc->tail) {
|
|
node_src->degree -= 2;
|
|
|
|
graph->free_arc(arc);
|
|
BLI_freelinkN(&graph->arcs, arc);
|
|
}
|
|
}
|
|
|
|
if (node_replaced->degree == 0) {
|
|
BLI_removeNode(graph, node_replaced);
|
|
}
|
|
}
|
|
|
|
void BLI_removeDoubleNodes(BGraph *graph, float limit)
|
|
{
|
|
BNode *node_src, *node_replaced;
|
|
|
|
for (node_src = graph->nodes.first; node_src; node_src = node_src->next) {
|
|
for (node_replaced = graph->nodes.first; node_replaced; node_replaced = node_replaced->next) {
|
|
if (node_replaced != node_src && len_v3v3(node_replaced->p, node_src->p) <= limit) {
|
|
BLI_replaceNode(graph, node_src, node_replaced);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
BNode *BLI_FindNodeByPosition(BGraph *graph, const float p[3], const float limit)
|
|
{
|
|
BNode *closest_node = NULL, *node;
|
|
float min_distance = 0.0f;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
float distance = len_v3v3(p, node->p);
|
|
if (distance <= limit && (closest_node == NULL || distance < min_distance)) {
|
|
closest_node = node;
|
|
min_distance = distance;
|
|
}
|
|
}
|
|
|
|
return closest_node;
|
|
}
|
|
/************************************* SUBGRAPH DETECTION **********************************************/
|
|
|
|
static void flagSubgraph(BNode *node, int subgraph)
|
|
{
|
|
if (node->subgraph_index == 0) {
|
|
BArc *arc;
|
|
int i;
|
|
|
|
node->subgraph_index = subgraph;
|
|
|
|
for (i = 0; i < node->degree; i++) {
|
|
arc = node->arcs[i];
|
|
flagSubgraph(BLI_otherNode(arc, node), subgraph);
|
|
}
|
|
}
|
|
}
|
|
|
|
int BLI_FlagSubgraphs(BGraph *graph)
|
|
{
|
|
BNode *node;
|
|
int subgraph = 0;
|
|
|
|
if (BLI_hasAdjacencyList(graph) == 0) {
|
|
BLI_buildAdjacencyList(graph);
|
|
}
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
node->subgraph_index = 0;
|
|
}
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->subgraph_index == 0) {
|
|
subgraph++;
|
|
flagSubgraph(node, subgraph);
|
|
}
|
|
}
|
|
|
|
return subgraph;
|
|
}
|
|
|
|
void BLI_ReflagSubgraph(BGraph *graph, int old_subgraph, int new_subgraph)
|
|
{
|
|
BNode *node;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
if (node->flag == old_subgraph) {
|
|
node->flag = new_subgraph;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************** CYCLE DETECTION ***********************************************/
|
|
|
|
static int detectCycle(BNode *node, BArc *src_arc)
|
|
{
|
|
int value = 0;
|
|
|
|
if (node->flag == 0) {
|
|
int i;
|
|
|
|
/* mark node as visited */
|
|
node->flag = 1;
|
|
|
|
for (i = 0; i < node->degree && value == 0; i++) {
|
|
BArc *arc = node->arcs[i];
|
|
|
|
/* don't go back on the source arc */
|
|
if (arc != src_arc) {
|
|
value = detectCycle(BLI_otherNode(arc, node), arc);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
value = 1;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
int BLI_isGraphCyclic(BGraph *graph)
|
|
{
|
|
BNode *node;
|
|
int value = 0;
|
|
|
|
/* NEED TO CHECK IF ADJACENCY LIST EXIST */
|
|
|
|
/* Mark all nodes as not visited */
|
|
BLI_flagNodes(graph, 0);
|
|
|
|
/* detectCycles in subgraphs */
|
|
for (node = graph->nodes.first; node && value == 0; node = node->next) {
|
|
/* only for nodes in subgraphs that haven't been visited yet */
|
|
if (node->flag == 0) {
|
|
value = value || detectCycle(node, NULL);
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
BArc *BLI_findConnectedArc(BGraph *graph, BArc *arc, BNode *v)
|
|
{
|
|
BArc *nextArc;
|
|
|
|
for (nextArc = graph->arcs.first; nextArc; nextArc = nextArc->next) {
|
|
if (arc != nextArc && (nextArc->head == v || nextArc->tail == v)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return nextArc;
|
|
}
|
|
|
|
/*********************************** GRAPH AS TREE FUNCTIONS *******************************************/
|
|
|
|
static int subtreeShape(BNode *node, BArc *rootArc, int include_root)
|
|
{
|
|
int depth = 0;
|
|
|
|
node->flag = 1;
|
|
|
|
if (include_root) {
|
|
BNode *newNode = BLI_otherNode(rootArc, node);
|
|
return subtreeShape(newNode, rootArc, 0);
|
|
}
|
|
else {
|
|
/* Base case, no arcs leading away */
|
|
if (node->arcs == NULL || *(node->arcs) == NULL) {
|
|
return 0;
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *arc = node->arcs[i];
|
|
BNode *newNode = BLI_otherNode(arc, node);
|
|
|
|
/* stop immediate and cyclic backtracking */
|
|
if (arc != rootArc && newNode->flag == 0) {
|
|
depth += subtreeShape(newNode, arc, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
return SHAPE_RADIX * depth + 1;
|
|
}
|
|
}
|
|
|
|
int BLI_subtreeShape(BGraph *graph, BNode *node, BArc *rootArc, int include_root)
|
|
{
|
|
BLI_flagNodes(graph, 0);
|
|
return subtreeShape(node, rootArc, include_root);
|
|
}
|
|
|
|
float BLI_subtreeLength(BNode *node)
|
|
{
|
|
float length = 0;
|
|
int i;
|
|
|
|
node->flag = 0; /* flag node as visited */
|
|
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *arc = node->arcs[i];
|
|
BNode *other_node = BLI_otherNode(arc, node);
|
|
|
|
if (other_node->flag != 0) {
|
|
float subgraph_length = arc->length + BLI_subtreeLength(other_node);
|
|
length = MAX2(length, subgraph_length);
|
|
}
|
|
}
|
|
|
|
return length;
|
|
}
|
|
|
|
void BLI_calcGraphLength(BGraph *graph)
|
|
{
|
|
float length = 0;
|
|
int nb_subgraphs;
|
|
int i;
|
|
|
|
nb_subgraphs = BLI_FlagSubgraphs(graph);
|
|
|
|
for (i = 1; i <= nb_subgraphs; i++) {
|
|
BNode *node;
|
|
|
|
for (node = graph->nodes.first; node; node = node->next) {
|
|
/* start on an external node of the subgraph */
|
|
if (node->subgraph_index == i && node->degree == 1) {
|
|
float subgraph_length = BLI_subtreeLength(node);
|
|
length = MAX2(length, subgraph_length);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
graph->length = length;
|
|
}
|
|
|
|
/********************************* SYMMETRY DETECTION **************************************************/
|
|
|
|
static void markdownSymmetryArc(BGraph *graph, BArc *arc, BNode *node, int level, float limit);
|
|
|
|
void BLI_mirrorAlongAxis(float v[3], float center[3], float axis[3])
|
|
{
|
|
float dv[3], pv[3];
|
|
|
|
sub_v3_v3v3(dv, v, center);
|
|
project_v3_v3v3(pv, dv, axis);
|
|
mul_v3_fl(pv, -2);
|
|
add_v3_v3(v, pv);
|
|
}
|
|
|
|
static void testRadialSymmetry(BGraph *graph, BNode *root_node, RadialArc *ring, int total, float axis[3], float limit, int group)
|
|
{
|
|
int symmetric = 1;
|
|
int i;
|
|
|
|
/* sort ring by angle */
|
|
for (i = 0; i < total - 1; i++) {
|
|
float minAngle = FLT_MAX;
|
|
int minIndex = -1;
|
|
int j;
|
|
|
|
for (j = i + 1; j < total; j++) {
|
|
float angle = dot_v3v3(ring[i].n, ring[j].n);
|
|
|
|
/* map negative values to 1..2 */
|
|
if (angle < 0) {
|
|
angle = 1 - angle;
|
|
}
|
|
|
|
if (angle < minAngle) {
|
|
minIndex = j;
|
|
minAngle = angle;
|
|
}
|
|
}
|
|
|
|
/* swap if needed */
|
|
if (minIndex != i + 1) {
|
|
RadialArc tmp;
|
|
tmp = ring[i + 1];
|
|
ring[i + 1] = ring[minIndex];
|
|
ring[minIndex] = tmp;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < total && symmetric; i++) {
|
|
BNode *node1, *node2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
float p[3];
|
|
int j = (i + 1) % total; /* next arc in the circular list */
|
|
|
|
add_v3_v3v3(tangent, ring[i].n, ring[j].n);
|
|
cross_v3_v3v3(normal, tangent, axis);
|
|
|
|
node1 = BLI_otherNode(ring[i].arc, root_node);
|
|
node2 = BLI_otherNode(ring[j].arc, root_node);
|
|
|
|
copy_v3_v3(p, node2->p);
|
|
BLI_mirrorAlongAxis(p, root_node->p, normal);
|
|
|
|
/* check if it's within limit before continuing */
|
|
if (len_v3v3(node1->p, p) > limit) {
|
|
symmetric = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (symmetric) {
|
|
/* mark node as symmetric physically */
|
|
copy_v3_v3(root_node->symmetry_axis, axis);
|
|
root_node->symmetry_flag |= SYM_PHYSICAL;
|
|
root_node->symmetry_flag |= SYM_RADIAL;
|
|
|
|
/* FLAG SYMMETRY GROUP */
|
|
for (i = 0; i < total; i++) {
|
|
ring[i].arc->symmetry_group = group;
|
|
ring[i].arc->symmetry_flag = SYM_SIDE_RADIAL + i;
|
|
}
|
|
|
|
if (graph->radial_symmetry) {
|
|
graph->radial_symmetry(root_node, ring, total);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleRadialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit)
|
|
{
|
|
RadialArc *ring = NULL;
|
|
RadialArc *unit;
|
|
int total = 0;
|
|
int group;
|
|
int first;
|
|
int i;
|
|
|
|
/* mark topological symmetry */
|
|
root_node->symmetry_flag |= SYM_TOPOLOGICAL;
|
|
|
|
/* total the number of arcs in the symmetry ring */
|
|
for (i = 0; i < root_node->degree; i++) {
|
|
BArc *connectedArc = root_node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->symmetry_level == -depth) {
|
|
total++;
|
|
}
|
|
}
|
|
|
|
ring = MEM_callocN(sizeof(RadialArc) * total, "radial symmetry ring");
|
|
unit = ring;
|
|
|
|
/* fill in the ring */
|
|
for (unit = ring, i = 0; i < root_node->degree; i++) {
|
|
BArc *connectedArc = root_node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->symmetry_level == -depth) {
|
|
BNode *otherNode = BLI_otherNode(connectedArc, root_node);
|
|
float vec[3];
|
|
|
|
unit->arc = connectedArc;
|
|
|
|
/* project the node to node vector on the symmetry plane */
|
|
sub_v3_v3v3(unit->n, otherNode->p, root_node->p);
|
|
project_v3_v3v3(vec, unit->n, axis);
|
|
sub_v3_v3v3(unit->n, unit->n, vec);
|
|
|
|
normalize_v3(unit->n);
|
|
|
|
unit++;
|
|
}
|
|
}
|
|
|
|
/* sort ring by arc length
|
|
* using a rather bogus insertion sort
|
|
* butrings will never get too big to matter
|
|
* */
|
|
for (i = 0; i < total; i++) {
|
|
int j;
|
|
|
|
for (j = i - 1; j >= 0; j--) {
|
|
BArc *arc1, *arc2;
|
|
|
|
arc1 = ring[j].arc;
|
|
arc2 = ring[j + 1].arc;
|
|
|
|
if (arc1->length > arc2->length) {
|
|
/* swap with smaller */
|
|
RadialArc tmp;
|
|
|
|
tmp = ring[j + 1];
|
|
ring[j + 1] = ring[j];
|
|
ring[j] = tmp;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Dispatch to specific symmetry tests */
|
|
first = 0;
|
|
group = 0;
|
|
|
|
for (i = 1; i < total; i++) {
|
|
int dispatch = 0;
|
|
int last = i - 1;
|
|
|
|
if (fabsf(ring[first].arc->length - ring[i].arc->length) > limit) {
|
|
dispatch = 1;
|
|
}
|
|
|
|
/* if not dispatching already and on last arc
|
|
* Dispatch using current arc as last
|
|
*/
|
|
if (dispatch == 0 && i == total - 1) {
|
|
last = i;
|
|
dispatch = 1;
|
|
}
|
|
|
|
if (dispatch) {
|
|
int sub_total = last - first + 1;
|
|
|
|
group += 1;
|
|
|
|
if (sub_total == 1) {
|
|
group -= 1; /* not really a group so decrement */
|
|
/* NOTHING TO DO */
|
|
}
|
|
else if (sub_total == 2) {
|
|
BArc *arc1, *arc2;
|
|
BNode *node1, *node2;
|
|
|
|
arc1 = ring[first].arc;
|
|
arc2 = ring[last].arc;
|
|
|
|
node1 = BLI_otherNode(arc1, root_node);
|
|
node2 = BLI_otherNode(arc2, root_node);
|
|
|
|
testAxialSymmetry(graph, root_node, node1, node2, arc1, arc2, axis, limit, group);
|
|
}
|
|
else if (sub_total != total) /* allocate a new sub ring if needed */ {
|
|
RadialArc *sub_ring = MEM_callocN(sizeof(RadialArc) * sub_total, "radial symmetry ring");
|
|
int sub_i;
|
|
|
|
/* fill in the sub ring */
|
|
for (sub_i = 0; sub_i < sub_total; sub_i++) {
|
|
sub_ring[sub_i] = ring[first + sub_i];
|
|
}
|
|
|
|
testRadialSymmetry(graph, root_node, sub_ring, sub_total, axis, limit, group);
|
|
|
|
MEM_freeN(sub_ring);
|
|
}
|
|
else if (sub_total == total) {
|
|
testRadialSymmetry(graph, root_node, ring, total, axis, limit, group);
|
|
}
|
|
|
|
first = i;
|
|
}
|
|
}
|
|
|
|
|
|
MEM_freeN(ring);
|
|
}
|
|
|
|
static void flagAxialSymmetry(BNode *root_node, BNode *end_node, BArc *arc, int group)
|
|
{
|
|
float vec[3];
|
|
|
|
arc->symmetry_group = group;
|
|
|
|
sub_v3_v3v3(vec, end_node->p, root_node->p);
|
|
|
|
if (dot_v3v3(vec, root_node->symmetry_axis) < 0) {
|
|
arc->symmetry_flag |= SYM_SIDE_NEGATIVE;
|
|
}
|
|
else {
|
|
arc->symmetry_flag |= SYM_SIDE_POSITIVE;
|
|
}
|
|
}
|
|
|
|
static void testAxialSymmetry(BGraph *graph, BNode *root_node, BNode *node1, BNode *node2, BArc *arc1, BArc *arc2, float axis[3], float limit, int group)
|
|
{
|
|
float nor[3], vec[3], p[3];
|
|
|
|
sub_v3_v3v3(p, node1->p, root_node->p);
|
|
cross_v3_v3v3(nor, p, axis);
|
|
|
|
sub_v3_v3v3(p, root_node->p, node2->p);
|
|
cross_v3_v3v3(vec, p, axis);
|
|
add_v3_v3(vec, nor);
|
|
|
|
cross_v3_v3v3(nor, vec, axis);
|
|
|
|
if (abs(nor[0]) > abs(nor[1]) && abs(nor[0]) > abs(nor[2]) && nor[0] < 0) {
|
|
negate_v3(nor);
|
|
}
|
|
else if (abs(nor[1]) > abs(nor[0]) && abs(nor[1]) > abs(nor[2]) && nor[1] < 0) {
|
|
negate_v3(nor);
|
|
}
|
|
else if (abs(nor[2]) > abs(nor[1]) && abs(nor[2]) > abs(nor[0]) && nor[2] < 0) {
|
|
negate_v3(nor);
|
|
}
|
|
|
|
/* mirror node2 along axis */
|
|
copy_v3_v3(p, node2->p);
|
|
BLI_mirrorAlongAxis(p, root_node->p, nor);
|
|
|
|
/* check if it's within limit before continuing */
|
|
if (len_v3v3(node1->p, p) <= limit) {
|
|
/* mark node as symmetric physically */
|
|
copy_v3_v3(root_node->symmetry_axis, nor);
|
|
root_node->symmetry_flag |= SYM_PHYSICAL;
|
|
root_node->symmetry_flag |= SYM_AXIAL;
|
|
|
|
/* flag side on arcs */
|
|
flagAxialSymmetry(root_node, node1, arc1, group);
|
|
flagAxialSymmetry(root_node, node2, arc2, group);
|
|
|
|
if (graph->axial_symmetry) {
|
|
graph->axial_symmetry(root_node, node1, node2, arc1, arc2);
|
|
}
|
|
}
|
|
else {
|
|
/* NOT SYMMETRIC */
|
|
}
|
|
}
|
|
|
|
static void handleAxialSymmetry(BGraph *graph, BNode *root_node, int depth, float axis[3], float limit)
|
|
{
|
|
BArc *arc1 = NULL, *arc2 = NULL;
|
|
BNode *node1 = NULL, *node2 = NULL;
|
|
int i;
|
|
|
|
/* mark topological symmetry */
|
|
root_node->symmetry_flag |= SYM_TOPOLOGICAL;
|
|
|
|
for (i = 0; i < root_node->degree; i++) {
|
|
BArc *connectedArc = root_node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->symmetry_level == -depth) {
|
|
if (arc1 == NULL) {
|
|
arc1 = connectedArc;
|
|
node1 = BLI_otherNode(arc1, root_node);
|
|
}
|
|
else {
|
|
arc2 = connectedArc;
|
|
node2 = BLI_otherNode(arc2, root_node);
|
|
break; /* Can stop now, the two arcs have been found */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* shouldn't happen, but just to be sure */
|
|
if (node1 == NULL || node2 == NULL) {
|
|
return;
|
|
}
|
|
|
|
testAxialSymmetry(graph, root_node, node1, node2, arc1, arc2, axis, limit, 1);
|
|
}
|
|
|
|
static void markdownSecondarySymmetry(BGraph *graph, BNode *node, int depth, int level, float limit)
|
|
{
|
|
float axis[3] = {0, 0, 0};
|
|
int count = 0;
|
|
int i;
|
|
|
|
/* count the number of branches in this symmetry group
|
|
* and determinate the axis of symmetry
|
|
*/
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *connectedArc = node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->symmetry_level == -depth) {
|
|
count++;
|
|
}
|
|
/* If arc is on the axis */
|
|
else if (connectedArc->symmetry_level == level) {
|
|
add_v3_v3(axis, connectedArc->head->p);
|
|
sub_v3_v3v3(axis, axis, connectedArc->tail->p);
|
|
}
|
|
}
|
|
|
|
normalize_v3(axis);
|
|
|
|
/* Split between axial and radial symmetry */
|
|
if (count == 2) {
|
|
handleAxialSymmetry(graph, node, depth, axis, limit);
|
|
}
|
|
else {
|
|
handleRadialSymmetry(graph, node, depth, axis, limit);
|
|
}
|
|
|
|
/* markdown secondary symetries */
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *connectedArc = node->arcs[i];
|
|
|
|
if (connectedArc->symmetry_level == -depth) {
|
|
/* markdown symmetry for branches corresponding to the depth */
|
|
markdownSymmetryArc(graph, connectedArc, node, level + 1, limit);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void markdownSymmetryArc(BGraph *graph, BArc *arc, BNode *node, int level, float limit)
|
|
{
|
|
int i;
|
|
|
|
/* if arc is null, we start straight from a node */
|
|
if (arc) {
|
|
arc->symmetry_level = level;
|
|
|
|
node = BLI_otherNode(arc, node);
|
|
}
|
|
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *connectedArc = node->arcs[i];
|
|
|
|
if (connectedArc != arc) {
|
|
BNode *connectedNode = BLI_otherNode(connectedArc, node);
|
|
|
|
/* symmetry level is positive value, negative values is subtree depth */
|
|
connectedArc->symmetry_level = -BLI_subtreeShape(graph, connectedNode, connectedArc, 0);
|
|
}
|
|
}
|
|
|
|
arc = NULL;
|
|
|
|
for (i = 0; i < node->degree; i++) {
|
|
int issymmetryAxis = 0;
|
|
BArc *connectedArc = node->arcs[i];
|
|
|
|
/* only arcs not already marked as symetric */
|
|
if (connectedArc->symmetry_level < 0) {
|
|
int j;
|
|
|
|
/* true by default */
|
|
issymmetryAxis = 1;
|
|
|
|
for (j = 0; j < node->degree; j++) {
|
|
BArc *otherArc = node->arcs[j];
|
|
|
|
/* different arc, same depth */
|
|
if (otherArc != connectedArc && otherArc->symmetry_level == connectedArc->symmetry_level) {
|
|
/* not on the symmetry axis */
|
|
issymmetryAxis = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* arc could be on the symmetry axis */
|
|
if (issymmetryAxis == 1) {
|
|
/* no arc as been marked previously, keep this one */
|
|
if (arc == NULL) {
|
|
arc = connectedArc;
|
|
}
|
|
else if (connectedArc->symmetry_level < arc->symmetry_level) {
|
|
/* go with more complex subtree as symmetry arc */
|
|
arc = connectedArc;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* go down the arc continuing the symmetry axis */
|
|
if (arc) {
|
|
markdownSymmetryArc(graph, arc, node, level, limit);
|
|
}
|
|
|
|
|
|
/* secondary symmetry */
|
|
for (i = 0; i < node->degree; i++) {
|
|
BArc *connectedArc = node->arcs[i];
|
|
|
|
/* only arcs not already marked as symetric and is not the next arc on the symmetry axis */
|
|
if (connectedArc->symmetry_level < 0) {
|
|
/* subtree depth is store as a negative value in the symmetry */
|
|
markdownSecondarySymmetry(graph, node, -connectedArc->symmetry_level, level, limit);
|
|
}
|
|
}
|
|
}
|
|
|
|
void BLI_markdownSymmetry(BGraph *graph, BNode *root_node, float limit)
|
|
{
|
|
BNode *node;
|
|
BArc *arc;
|
|
|
|
if (root_node == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (BLI_isGraphCyclic(graph)) {
|
|
return;
|
|
}
|
|
|
|
/* mark down all arcs as non-symetric */
|
|
BLI_flagArcs(graph, 0);
|
|
|
|
/* mark down all nodes as not on the symmetry axis */
|
|
BLI_flagNodes(graph, 0);
|
|
|
|
node = root_node;
|
|
|
|
/* sanity check REMOVE ME */
|
|
if (node->degree > 0) {
|
|
arc = node->arcs[0];
|
|
|
|
if (node->degree == 1) {
|
|
markdownSymmetryArc(graph, arc, node, 1, limit);
|
|
}
|
|
else {
|
|
markdownSymmetryArc(graph, NULL, node, 1, limit);
|
|
}
|
|
|
|
|
|
|
|
/* mark down non-symetric arcs */
|
|
for (arc = graph->arcs.first; arc; arc = arc->next) {
|
|
if (arc->symmetry_level < 0) {
|
|
arc->symmetry_level = 0;
|
|
}
|
|
else {
|
|
/* mark down nodes with the lowest level symmetry axis */
|
|
if (arc->head->symmetry_level == 0 || arc->head->symmetry_level > arc->symmetry_level) {
|
|
arc->head->symmetry_level = arc->symmetry_level;
|
|
}
|
|
if (arc->tail->symmetry_level == 0 || arc->tail->symmetry_level > arc->symmetry_level) {
|
|
arc->tail->symmetry_level = arc->symmetry_level;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void *IT_head(void *arg)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->head(iter);
|
|
}
|
|
|
|
void *IT_tail(void *arg)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->tail(iter);
|
|
}
|
|
|
|
void *IT_peek(void *arg, int n)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
|
|
if (iter->index + n < 0) {
|
|
return iter->head(iter);
|
|
}
|
|
else if (iter->index + n >= iter->length) {
|
|
return iter->tail(iter);
|
|
}
|
|
else {
|
|
return iter->peek(iter, n);
|
|
}
|
|
}
|
|
|
|
void *IT_next(void *arg)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->next(iter);
|
|
}
|
|
|
|
void *IT_nextN(void *arg, int n)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->nextN(iter, n);
|
|
}
|
|
|
|
void *IT_previous(void *arg)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->previous(iter);
|
|
}
|
|
|
|
int IT_stopped(void *arg)
|
|
{
|
|
BArcIterator *iter = (BArcIterator *)arg;
|
|
return iter->stopped(iter);
|
|
}
|