/* GTS - Library for the manipulation of triangulated surfaces
* Copyright (C) 1999 Stéphane Popinet
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <string.h>
#include "gts.h"
#define HEAP_INSERT_HSPLIT(h, e) ((e)->index = gts_eheap_insert (h, e))
#define HEAP_REMOVE_HSPLIT(h, e) (gts_eheap_remove (h, (e)->index),\
(e)->index = NULL)
static void hsplit_init (GtsHSplit * hsplit)
{
hsplit->index = NULL;
hsplit->parent = NULL;
hsplit->nchild = 0;
}
/**
* gts_hsplit_class:
*
* Returns: the #GtsHSplitClass.
*/
GtsHSplitClass * gts_hsplit_class (void)
{
static GtsHSplitClass * klass = NULL;
if (klass == NULL) {
GtsObjectClassInfo hsplit_info = {
"GtsHSplit",
sizeof (GtsHSplit),
sizeof (GtsHSplitClass),
(GtsObjectClassInitFunc) NULL,
(GtsObjectInitFunc) hsplit_init,
(GtsArgSetFunc) NULL,
(GtsArgGetFunc) NULL
};
klass = gts_object_class_new (GTS_OBJECT_CLASS (gts_split_class ()),
&hsplit_info);
}
return klass;
}
/**
* gts_hsplit_new:
* @klass: a #GtsHSplitClass.
* @vs: a #GtsSplit.
*
* Returns: a new #GtsHSplit, hierarchical extension of @vs.
*/
GtsHSplit * gts_hsplit_new (GtsHSplitClass * klass, GtsSplit * vs)
{
GtsHSplit * hs;
g_return_val_if_fail (vs != NULL, NULL);
hs = GTS_HSPLIT (gts_object_new (GTS_OBJECT_CLASS (klass)));
memcpy (hs, vs, sizeof (GtsSplit));
GTS_OBJECT (hs)->reserved = NULL;
return hs;
}
/**
* gts_hsplit_collapse:
* @hs: a #GtsHSplit.
* @hsurface: a #GtsHSurface.
*
* Collapses the #GtsSplit defined by @hs, updates the expandable and
* collapsable priority heaps of @hsurface.
*/
void gts_hsplit_collapse (GtsHSplit * hs,
GtsHSurface * hsurface)
{
GtsHSplit * parent;
GtsSplit * vs;
g_return_if_fail (hs != NULL);
g_return_if_fail (hs->nchild == 2);
g_return_if_fail (hsurface != NULL);
gts_split_collapse (GTS_SPLIT (hs), hsurface->s->edge_class, NULL);
hsurface->nvertex--;
hs->nchild = 0;
HEAP_REMOVE_HSPLIT (hsurface->collapsable, hs);
HEAP_INSERT_HSPLIT (hsurface->expandable, hs);
vs = GTS_SPLIT (hs);
if (GTS_IS_HSPLIT (vs->v1))
HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1));
if (GTS_IS_HSPLIT (vs->v2))
HEAP_REMOVE_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2));
parent = hs->parent;
if (parent && ++parent->nchild == 2)
HEAP_INSERT_HSPLIT (hsurface->collapsable, parent);
}
/**
* gts_hsplit_expand:
* @hs: a #GtsHSplit.
* @hsurface: a #GtsHSurface.
*
* Expands the #GtsSplit defined by @hs (which must be expandable)
* and updates the priority heaps of @hsurface.
*/
void gts_hsplit_expand (GtsHSplit * hs,
GtsHSurface * hsurface)
{
GtsHSplit * parent;
GtsSplit * vs;
g_return_if_fail (hs != NULL);
g_return_if_fail (hsurface != NULL);
g_return_if_fail (hs->nchild == 0);
gts_split_expand (GTS_SPLIT (hs), hsurface->s, hsurface->s->edge_class);
hsurface->nvertex++;
hs->nchild = 2;
HEAP_REMOVE_HSPLIT (hsurface->expandable, hs);
HEAP_INSERT_HSPLIT (hsurface->collapsable, hs);
vs = GTS_SPLIT (hs);
if (GTS_IS_HSPLIT (vs->v1))
HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v1));
if (GTS_IS_HSPLIT (vs->v2))
HEAP_INSERT_HSPLIT (hsurface->expandable, GTS_HSPLIT (vs->v2));
parent = hs->parent;
if (parent && parent->nchild-- == 2)
HEAP_REMOVE_HSPLIT (hsurface->collapsable, parent);
}
static void hsurface_destroy (GtsObject * object)
{
GtsHSurface * hs = GTS_HSURFACE (object);
gts_hsurface_traverse (hs, G_POST_ORDER, -1,
(GtsSplitTraverseFunc) gts_object_destroy,
NULL);
g_slist_free (hs->roots);
if (hs->expandable)
gts_eheap_destroy (hs->expandable);
if (hs->collapsable)
gts_eheap_destroy (hs->collapsable);
g_ptr_array_free (hs->split, TRUE);
(* GTS_OBJECT_CLASS (gts_hsurface_class ())->parent_class->destroy) (object);
}
static void hsurface_class_init (GtsObjectClass * klass)
{
klass->destroy = hsurface_destroy;
}
static void hsurface_init (GtsHSurface * hsurface)
{
hsurface->s = NULL;
hsurface->roots = NULL;
hsurface->expandable = hsurface->collapsable = NULL;
hsurface->split = g_ptr_array_new ();
hsurface->nvertex = 0;
}
/**
* gts_hsurface_class:
*
* Returns: the #GtsHSurfaceClass.
*/
GtsHSurfaceClass * gts_hsurface_class (void)
{
static GtsHSurfaceClass * klass = NULL;
if (klass == NULL) {
GtsObjectClassInfo hsurface_info = {
"GtsHSurface",
sizeof (GtsHSurface),
sizeof (GtsHSurfaceClass),
(GtsObjectClassInitFunc) hsurface_class_init,
(GtsObjectInitFunc) hsurface_init,
(GtsArgSetFunc) NULL,
(GtsArgGetFunc) NULL
};
klass = gts_object_class_new (gts_object_class (),
&hsurface_info);
}
return klass;
}
/**
* gts_hsurface_new:
* @klass: a #GtsHSurfaceClass.
* @hsplit_class: a #GtsHSplitClass.
* @psurface: a #GtsPSurface.
* @expand_key: a #GtsKeyFunc used to order the priority heap of expandable
* #GtsHSplit.
* @expand_data: data to be passed to @expand_key.
* @collapse_key: a #GtsKeyFunc used to order the priority heap of collapsable
* #GtsHSplit.
* @collapse_data: data to be passed to @collapsed_key.
*
* Returns: a new #GtsHSurface, hierarchical extension of @psurface
* and using #GtsHSplit of class @hsplit_class. Note that @psurface is
* destroyed in the process.
*/
GtsHSurface * gts_hsurface_new (GtsHSurfaceClass * klass,
GtsHSplitClass * hsplit_class,
GtsPSurface * psurface,
GtsKeyFunc expand_key,
gpointer expand_data,
GtsKeyFunc collapse_key,
gpointer collapse_data)
{
GtsHSurface * hsurface;
g_return_val_if_fail (klass != NULL, NULL);
g_return_val_if_fail (hsplit_class != NULL, NULL);
g_return_val_if_fail (psurface != NULL, NULL);
g_return_val_if_fail (expand_key != NULL, NULL);
g_return_val_if_fail (collapse_key != NULL, NULL);
hsurface = GTS_HSURFACE (gts_object_new (GTS_OBJECT_CLASS (klass)));
hsurface->s = psurface->s;
hsurface->expandable = gts_eheap_new (expand_key, expand_data);
hsurface->collapsable = gts_eheap_new (collapse_key, collapse_data);
g_ptr_array_set_size (hsurface->split, psurface->split->len);
while (gts_psurface_remove_vertex (psurface))
;
while (psurface->pos) {
GtsSplit * vs = g_ptr_array_index (psurface->split, psurface->pos - 1);
GtsHSplit * hs = gts_hsplit_new (hsplit_class, vs);
g_ptr_array_index (hsurface->split, psurface->pos - 1) = hs;
psurface->pos--;
hs->parent = GTS_OBJECT (vs)->reserved;
if (hs->parent) {
GtsSplit * vsp = GTS_SPLIT (hs->parent);
if (vsp->v1 == GTS_OBJECT (vs)) {
g_assert (vsp->v2 != GTS_OBJECT (vs));
vsp->v1 = GTS_OBJECT (hs);
}
else {
g_assert (vsp->v2 == GTS_OBJECT (vs));
vsp->v2 = GTS_OBJECT (hs);
}
}
else
hsurface->roots = g_slist_prepend (hsurface->roots, hs);
hs->nchild = 0;
if (GTS_IS_SPLIT (vs->v1))
GTS_OBJECT (vs->v1)->reserved = hs;
else
hs->nchild++;
if (GTS_IS_SPLIT (vs->v2))
GTS_OBJECT (vs->v2)->reserved = hs;
else
hs->nchild++;
gts_split_expand (vs, psurface->s, psurface->s->edge_class);
if (hs->nchild == 2)
HEAP_INSERT_HSPLIT (hsurface->collapsable, hs);
}
hsurface->nvertex = gts_surface_vertex_number (hsurface->s);
gts_object_destroy (GTS_OBJECT (psurface));
return hsurface;
}
/**
* gts_hsurface_traverse:
* @hsurface: a #GtsHSurface.
* @order: the order in which nodes are visited - G_PRE_ORDER or G_POST_ORDER.
* @depth: the maximum depth of the traversal. Nodes below this depth
* will not be visited. If max_depth is -1 all nodes in the tree are
* visited. If depth is 1, only the root is visited. If depth is 2,
* the root and its children are visited. And so on.
* @func: the function to call for each visited #GtsHSplit.
* @data: user data to pass to the function.
*
* Traverses a hierarchical surface starting from its roots. It calls
* the given function for each #GtsHSplit visited.
* See also gts_split_traverse().
*/
void gts_hsurface_traverse (GtsHSurface * hsurface,
GTraverseType order,
gint depth,
GtsSplitTraverseFunc func,
gpointer data)
{
GSList * i;
g_return_if_fail (hsurface != NULL);
g_return_if_fail (func != NULL);
g_return_if_fail (order < G_LEVEL_ORDER);
g_return_if_fail (depth == -1 || depth > 0);
i = hsurface->roots;
while (i) {
gts_split_traverse (i->data, order, depth, func, data);
i = i->next;
}
}
/**
* gts_hsurface_foreach:
* @hsurface: a #GtsHSurface.
* @order: the order in which #GtsHSplit are visited - G_PRE_ORDER or
* G_POST_ORDER.
* @func: the function to call for each visited #GtsHSplit.
* @data: user data to pass to the function.
*
* Starts by expanding all the #GtsHSplit of @hsurface. If @order is
* G_PRE_ORDER, calls @func for each #GtsHSplit and collapses it. If
* order is G_POST_ORDER, collapses each #GtsHSplit first and then
* calls @func. The traversal can be halted at any point by returning
* TRUE from func.
*/
void gts_hsurface_foreach (GtsHSurface * hsurface,
GTraverseType order,
GtsFunc func,
gpointer data)
{
GtsHSplit * hs;
guint i = 0, len;
gboolean stop = FALSE;
g_return_if_fail (hsurface != NULL);
g_return_if_fail (func != NULL);
g_return_if_fail (order == G_PRE_ORDER || order == G_POST_ORDER);
while ((hs = gts_eheap_top (hsurface->expandable, NULL)))
gts_hsplit_expand (hs, hsurface);
len = hsurface->split->len;
switch (order) {
case G_PRE_ORDER:
while (i < len && !stop) {
GtsHSplit * hs = g_ptr_array_index (hsurface->split, i);
stop = (*func) (hs, data);
if (!stop)
gts_hsplit_collapse (hs, hsurface);
i++;
}
break;
case G_POST_ORDER:
while (i < len && !stop) {
GtsHSplit * hs = g_ptr_array_index (hsurface->split, i);
gts_hsplit_collapse (hs, hsurface);
stop = (*func) (hs, data);
i++;
}
break;
default:
g_assert_not_reached ();
}
}
/**
* gts_hsurface_height:
* @hsurface: a #GtsHSurface.
*
* Returns: the maximum height of the tree described by @hsurface.
*/
guint gts_hsurface_height (GtsHSurface * hsurface)
{
GSList * i;
guint height = 0;
g_return_val_if_fail (hsurface != NULL, 0);
i = hsurface->roots;
while (i) {
guint tmp_height = gts_split_height (i->data);
if (tmp_height > height)
height = tmp_height;
i = i->next;
}
return height;
}
syntax highlighted by Code2HTML, v. 0.9.1