home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 1999 mARCH
/
PCWK3A99.iso
/
Linux
/
DDD331
/
DDD-3_1_.000
/
DDD-3_1_
/
ddd-3.1.1
/
ddd
/
DispGraph.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-11-17
|
30KB
|
1,240 lines
// $Id: DispGraph.C,v 1.45 1998/11/17 11:22:32 zeller Exp $
// Store information about all displayed display expressions
// Copyright (C) 1995-1998 Technische Universitaet Braunschweig, Germany.
// Written by Dorothea Luetkehaus <luetke@ips.cs.tu-bs.de>
// and Andreas Zeller <zeller@ips.cs.tu-bs.de>
//
// This file is part of DDD.
//
// DDD 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.
//
// DDD 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 DDD -- see the file COPYING.
// If not, write to the Free Software Foundation, Inc.,
// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// DDD is the data display debugger.
// For details, see the DDD World-Wide-Web page,
// `http://www.cs.tu-bs.de/softech/ddd/',
// or send a mail to the DDD developers <ddd@ips.cs.tu-bs.de>.
char DispGraph_rcsid[] =
"$Id: DispGraph.C,v 1.45 1998/11/17 11:22:32 zeller Exp $";
#ifdef __GNUG__
#pragma implementation
#endif
//-----------------------------------------------------------------------------
// DispGraph stores information about all displayed display expressions.
//-----------------------------------------------------------------------------
#include "DispGraph.h"
#include <math.h>
#include "pi.h"
#include "hypot.h"
#include <X11/StringDefs.h>
#include "AppData.h"
#include "GraphEdit.h"
#include "assert.h"
#include "VarArray.h"
#include "AliasGE.h"
#include "HintGraphN.h"
#include "VoidArray.h"
#include "regexps.h"
#include "BoxEdgeA.h"
#include "annotation.h"
#include "DispBox.h"
#include "EdgeAPA.h"
DEFINE_TYPE_INFO_1(DispGraph, Graph)
//-------------------------------------------------------------------------
// Basics
//-------------------------------------------------------------------------
// Constructor
DispGraph::DispGraph()
: Graph(), idMap(), handlers(DispGraph_NTypes),
no_enabled(true), no_disabled(true)
{
DispNode::addHandler(DispNode_Disabled,
disp_node_disabledHP,
(void*)this);
}
// Destructor
DispGraph::~DispGraph()
{
DispGraph::clear();
}
// Delete all
void DispGraph::clear()
{
idMap.delete_all_contents();
}
//-------------------------------------------------------------------------
// Node management
//-------------------------------------------------------------------------
// Return number of nodes
int DispGraph::count_all (Displaying e) const
{
if (e == Both)
return idMap.length();
int count = 0;
MapRef ref;
for (int k = idMap.first_key(ref); k != 0; k =
idMap.next_key(ref))
{
switch (e)
{
case Both:
count++;
break;
case Enabled:
if (idMap.get(k)->enabled())
count++;
break;
case Disabled:
if (!(idMap.get(k)->enabled()))
count++;
break;
default:
// Falscher Fehler
assert (0);
break;
}
}
return count;
}
// Count selected nodes
int DispGraph::count_selected() const
{
int count = 0;
MapRef ref;
for (int k = idMap.first_key(ref); k != 0; k = idMap.next_key(ref)) {
if (!idMap.get(k)->selected())
count++;
}
return count;
}
// Add an event handler
void DispGraph::addHandler (unsigned type,
HandlerProc proc,
void* client_data)
{
handlers.add(type, proc, client_data);
}
// Remove an event handler
void DispGraph::removeHandler (unsigned type,
HandlerProc proc,
void *client_data)
{
handlers.remove(type, proc, client_data);
}
// Call event handlers
void DispGraph::callHandlers ()
{
handlers.call(DispGraph_Empty,
this,
(void*)(count_all() == 0));
handlers.call(NoEnabled,
this,
(void*)(no_enabled));
handlers.call(NoDisabled,
this,
(void*)(no_disabled));
}
// Add a new edge
void DispGraph::add_edge(DispNode *from, DispNode *to)
{
string a = annotation(from->name(), to->name());
BoxEdgeAnnotation *ann = 0;
if (a != "")
ann = new BoxEdgeAnnotation(DispBox::eval("annotation", a));
*this += new LineGraphEdge(from, to, ann);
}
// Insert NEW_DN into graph, return its number iff successful
int DispGraph::insert(int new_disp_nr, DispNode *new_dn, int depends_on)
{
if (idMap.contains(new_disp_nr))
return 0;
if (idMap.length() == 0)
handlers.call(DispGraph_Empty, this, (void*)false);
*this += new_dn;
if (depends_on != 0)
{
DispNode *old_dn = idMap.get(depends_on);
add_edge(old_dn, new_dn);
if (old_dn->clustered())
{
DispNode *cluster = idMap.get(old_dn->clustered());
if (cluster != 0)
add_edge(cluster, new_dn);
}
}
assert (Graph::OK());
idMap.insert (new_disp_nr, new_dn);
if (no_enabled) {
if (!( no_enabled = (count_all(Enabled) == 0) ))
handlers.call(NoEnabled, this, (void*)false);
}
if (no_disabled) {
if (!( no_disabled = (count_all(Disabled) == 0) ))
handlers.call(NoDisabled, this, (void*)false);
}
refresh_titles();
return new_disp_nr;
}
// Get a good position for NEW_NODE
BoxPoint DispGraph::adjust_position (DispNode *new_node,
Widget w,
BoxPoint pos,
BoxPoint offset,
BoxPoint grid) const
{
const GraphGC& graphGC = graphEditGetGraphGC(w);
// clog << "new node at " << pos << "\n";
BoxSize new_size(new_node->box()->size());
// Leave GRID space around the box
BoxRegion new_region(pos - (new_size + grid) / 2, new_size + grid);
// Make sure the new node is fully visible
while (new_region.origin()[X] <= 0)
{
pos += BoxPoint(grid[X], 0);
new_region.origin() += BoxPoint(grid[X], 0);
// clog << "new node now at " << pos << "\n";
}
while (new_region.origin()[Y] <= 0)
{
pos += BoxPoint(0, grid[Y]);
new_region.origin() += BoxPoint(0, grid[Y]);
// clog << "new node now at " << pos << "\n";
}
// Make sure the new node does not obscure existing nodes
GraphNode *n = firstVisibleNode();
while (n != 0)
{
const BoxRegion& region = n->region(graphGC);
if (new_region <= region)
{
pos += offset;
new_region.origin() += offset;
n = firstVisibleNode();
// clog << "new node now at " << pos << "\n";
}
else
n = nextVisibleNode(n);
}
return pos;
}
// Return a default position for NEW_NODE
BoxPoint DispGraph::default_pos(DispNode *new_node,
Widget w, int depends_on) const
{
Dimension grid_height = 16;
Dimension grid_width = 16;
Cardinal rotation = 0;
XtVaGetValues(w,
XtNgridHeight, &grid_height,
XtNgridWidth, &grid_width,
XtNrotation, &rotation,
NULL);
BoxPoint grid(max(grid_height, 1), max(grid_width, 1));
BoxPoint delta(grid[X] * 3, grid[Y] * 2);
bool horizontal = rotation % 90;
BoxPoint pos;
BoxPoint offset;
if (depends_on == 0)
{
// Default offset: create new displays orthogonal to
// dereference direction
offset = horizontal ? BoxPoint(grid[X], 0) : BoxPoint(0, grid[Y]);
// New node: start with the top-level visible position
Position x = 0;
Position y = 0;
XtVaGetValues(w, XtNx, &x, XtNy, &y, NULL);
pos = BoxPoint(max(-x, grid[X]), max(-y, grid[Y] * 2));
// Add size offset
BoxSize new_size(new_node->box()->size());
pos += new_size / 2;
// Round to nearest grid position
pos = graphEditFinalPosition(w, pos);
}
else
{
// Dependent node
// Default offset: create new displays in dereference direction
offset = horizontal ? BoxPoint(0, delta[Y]) : BoxPoint(delta[X], 0);
// Place new node below/on the right of original node, depending
// on last layout orientation.
//
// NODE -> (new)
BoxGraphNode *node = idMap.get(depends_on);
// clog << "node at " << node->pos() << "\n";
pos = node->pos() + offset;
assert(pos.isValid());
// Check if we already have a successor
BoxGraphNode *max_child = 0;
BoxGraphNode *next_max_child = 0;
// Find two greatest children
for (GraphEdge *edge = node->firstFrom();
edge != 0;
edge = node->nextFrom(edge))
{
BoxDimension d = horizontal ? X : Y;
GraphNode *child = edge->to();
while (child->isHint())
child = child->firstFrom()->to();
if (child->hidden())
continue;
if (child == new_node)
continue;
if (child->pos() == BoxPoint())
continue;
BoxGraphNode *bgn = ptr_cast(BoxGraphNode, child);
if (bgn == 0)
continue;
if (max_child == 0 || child->pos()[d] > max_child->pos()[d])
{
next_max_child = max_child;
max_child = bgn;
}
else if (next_max_child == 0
|| child->pos()[d] > next_max_child->pos()[d])
{
next_max_child = bgn;
}
}
if (max_child && next_max_child)
{
// Re-use offset between the two last children
//
// NODE -> .
// \-> .
// \-> .
// \-> NEXT_MAX_CHILD
// \-> MAX_CHILD
// \-> (new)
// clog << "max_child at " << max_child->pos() << "\n";
// clog << "next_max_child at " << next_max_child->pos() << "\n";
// Re-use offset between last two children
pos = max_child->pos()
+ (max_child->pos() - next_max_child->pos());
// If MAX_CHILD is on the right of NEXT_MAX_CHILD, place new
// node on the right; if MAX_CHILD is below NEXT_MAX_CHILD,
// place new node below. If position is occupied, try later
// in the same direction.
bool horizontal =
(abs(max_child->pos()[X] - next_max_child->pos()[X]) >
abs(max_child->pos()[Y] - next_max_child->pos()[Y]));
offset = horizontal ?
BoxPoint(delta[X], 0) : BoxPoint(0, delta[Y]);
}
else if (max_child)
{
// Place new child below last child
//
// NODE -> MAX_CHILD
// \-> (new)
// clog << "child at " << max_child->pos() << "\n";
// If MAX_CHILD is on the right of NODE, place new node below;
// if MAX_CHILD is below NODE, place new node on the right.
bool horizontal =
(abs(max_child->pos()[X] - node->pos()[X]) >
abs(max_child->pos()[Y] - node->pos()[Y]));
offset = horizontal ?
BoxPoint(0, delta[Y]) : BoxPoint(delta[X], 0);
pos = max_child->pos() + offset;
}
else
{
GraphEdge *edge = node->firstTo();
if (edge)
{
// We have a predecessor: use this offset instead
//
// PARENT -> NODE -> (new)
//
GraphNode *parent = edge->from();
assert(parent->pos().isValid());
// clog << "parent at " << parent->pos() << "\n";
// Re-use offset between parent and node
pos = node->pos() + (node->pos() - parent->pos());
// If NODE is on the right of PARENT, place new node on
// the right; if NODE is below PARENT, place new node
// below.
bool horizontal =
(abs(node->pos()[X] - parent->pos()[X]) >
abs(node->pos()[Y] - parent->pos()[Y]));
offset = horizontal ? BoxPoint(delta[X], 0)
: BoxPoint(0, delta[Y]);
}
}
}
assert(pos.isValid());
assert(offset.isValid());
return adjust_position(new_node, w, pos, offset, grid);
}
// Find all hints in edges leading to NODE; store them in HINTS
void DispGraph::find_hints_to(GraphNode *node, GraphNodePointerArray& hints)
{
for (GraphEdge *edge = node->firstTo();
edge != 0;
edge = node->nextTo(edge))
{
if (edge->from()->isHint())
{
find_hints_to(edge->from(), hints);
hints += edge->from();
}
}
}
// Find all hints in edges coming from NODE; store them in HINTS
void DispGraph::find_hints_from(GraphNode *node, GraphNodePointerArray& hints)
{
for (GraphEdge *edge = node->firstFrom();
edge != 0;
edge = node->nextFrom(edge))
{
if (edge->to()->isHint())
{
find_hints_from(edge->to(), hints);
hints += edge->to();
}
}
}
// Remove an edge from graph and from memory
void DispGraph::delete_edge(GraphEdge *edge)
{
*this -= edge;
delete edge;
}
// Remove a node and all its edges
void DispGraph::delete_node(GraphNode *node)
{
// Remove all edges
GraphEdge *e;
while ((e = node->firstFrom()) != 0)
delete_edge(e);
while ((e = node->firstTo()) != 0)
delete_edge(e);
// Remove node from graph
*this -= node;
delete node;
}
// Delete display
bool DispGraph::del (int disp_nr)
{
if (!idMap.contains (disp_nr))
return false;
unalias(disp_nr);
DispNode* dn = idMap.get(disp_nr);
GraphNodePointerArray hints;
find_hints_from(dn, hints);
find_hints_to(dn, hints);
for (int i = 0; i < hints.size(); i++)
delete_node(hints[i]);
delete_node(dn);
idMap.del(disp_nr);
if (idMap.length() == 0)
handlers.call(DispGraph_Empty, this, (void*)true);
if (!no_enabled)
if ((no_enabled = (count_all(Enabled) == 0)))
handlers.call(NoEnabled, this, (void*)true);
if (!no_disabled)
if ((no_disabled = (count_all(Disabled) == 0)))
handlers.call(NoDisabled, this, (void*)true);
refresh_titles();
return true;
}
// Return display number of NODE
int DispGraph::get_nr(BoxGraphNode *node) const
{
DispNode *dn = ptr_cast(DispNode, node);
return dn == 0 ? 0 : dn->disp_nr();
}
// Get number of node NAME
int DispGraph::get_by_name(const string& name) const
{
if (name.matches(rxint))
return atoi(name);
MapRef ref;
for (int k = idMap.first_key(ref); k != 0; k = idMap.next_key(ref))
if (idMap.get(k)->name() == name)
return k;
return 0;
}
// Return first node; 0 if not found
DispNode* DispGraph::first (MapRef& ref, Displaying e) const
{
for (DispNode* dn = idMap.first(ref); dn != 0; dn = idMap.next(ref)) {
switch (e) {
case Both:
return dn;
case Enabled:
if (dn->enabled())
return dn;
break;
case Disabled:
if (!dn->enabled())
return dn;
break;
default:
assert (0); // This can't happen
break;
}
}
return 0;
}
// Return next node; 0 if not found
DispNode* DispGraph::next (MapRef& ref, Displaying e) const
{
for (DispNode* dn = idMap.next(ref); dn != 0; dn = idMap.next(ref)) {
switch (e) {
case Both:
return dn;
case Enabled:
if (dn->enabled())
return dn;
break;
case Disabled:
if (!dn->enabled())
return dn;
break;
default:
assert (0); // This can't happen.
break;
}
}
return 0;
}
// Return number of first node; 0 if not found
int DispGraph::first_nr (MapRef& ref, Displaying e) const
{
for (int k = idMap.first_key(ref); k != 0; k = idMap.next_key(ref)) {
switch (e) {
case Both:
return k;
case Enabled:
if (idMap.get (k)->enabled())
return k;
break;
case Disabled:
if (!idMap.get (k)->enabled())
return k;
break;
default:
assert (0); // This can't happen.
break;
}
}
return 0;
}
// Return next node number; 0 if not found
int DispGraph::next_nr (MapRef& ref, Displaying e) const
{
for (int k = idMap.next_key(ref); k != 0; k = idMap.next_key(ref)) {
switch (e) {
case Both:
return k;
case Enabled:
if (idMap.get(k)->enabled())
return k;
break;
case Disabled:
if (!idMap.get(k)->enabled())
return k;
break;
default:
assert (0); // This can't happen
break;
}
}
return 0;
}
// Helper for disabling nodes
void DispGraph::disp_node_disabledHP (void*,
void* client_data,
void* call_data)
{
DispGraph* disp_graph = (DispGraph*) client_data;
bool disabled = bool(call_data);
if (disabled) {
if (disp_graph->no_disabled) {
disp_graph->no_disabled = false;
disp_graph->handlers.call(NoDisabled, disp_graph, (void*)false);
}
if (disp_graph->no_enabled = (disp_graph->count_all(Enabled) == 0))
disp_graph->handlers.call(NoEnabled, disp_graph, (void*)true);
}
else {
if (disp_graph->no_enabled) {
disp_graph->no_enabled = false;
disp_graph->handlers.call(NoEnabled, disp_graph, (void*)false);
}
if (disp_graph->no_disabled = (disp_graph->count_all(Disabled) == 0))
disp_graph->handlers.call(NoDisabled, disp_graph, (void*)true);
}
}
//-------------------------------------------------------------------------
// Alias handling
//-------------------------------------------------------------------------
// Make DISP_NR an alias of ALIAS_DISP_NR. Suppress ALIAS_DISP_NR.
bool DispGraph::alias(Widget w, int disp_nr, int alias_disp_nr)
{
DispNode *d0 = get(disp_nr);
DispNode *dn = get(alias_disp_nr);
if (d0 == 0 || dn == 0)
return false;
if (!dn->active())
{
// Already hidden because it is out of scope
return false;
}
if (dn->clustered())
{
// Already hidden because it is clustered
return false;
}
if (dn->hidden() && dn->alias_of == disp_nr)
{
// Already hidden as alias of DISP_NR
return false;
}
if (dn->hidden())
unalias(alias_disp_nr);
// Hide alias
dn->hidden() = true;
dn->alias_of = disp_nr;
// Clear any special selections in this alias
dn->select();
// Hide ordinary hints and insert new alias edges
GraphEdge *edge;
GraphNodePointerArray from_nodes;
GraphNodePointerArray to_nodes;
EdgeAnnotationPointerArray from_annotations;
EdgeAnnotationPointerArray to_annotations;
int i;
for (edge = dn->firstFrom(); edge != 0; edge = dn->nextFrom(edge))
{
GraphEdge *e = edge;
while (e->to()->isHint())
{
e->to()->hidden() = true;
e = e->to()->firstFrom();
}
to_nodes += e->to();
EdgeAnnotation *anno = 0;
LineGraphEdge *ge = ptr_cast(LineGraphEdge, e);
if (ge != 0)
anno = ge->annotation();
to_annotations += anno;
}
for (edge = dn->firstTo(); edge != 0; edge = dn->nextTo(edge))
{
GraphEdge *e = edge;
while (e->from()->isHint())
{
e->from()->hidden() = true;
e = e->from()->firstTo();
}
from_nodes += e->from();
EdgeAnnotation *anno = 0;
LineGraphEdge *ge = ptr_cast(LineGraphEdge, e);
if (ge != 0)
anno = ge->annotation();
from_annotations += anno;
}
for (i = 0; i < to_nodes.size(); i++)
add_alias_edge(w, alias_disp_nr,
d0, to_nodes[i], to_annotations[i]);
for (i = 0; i < from_nodes.size(); i++)
add_alias_edge(w, alias_disp_nr,
from_nodes[i], d0, from_annotations[i]);
// Propagate `selected' state to hints
for (GraphNode *node = firstNode(); node != 0; node = nextNode(node))
{
if (!node->isHint())
continue;
AliasGraphEdge *edge = ptr_cast(AliasGraphEdge, node->firstTo());
if (edge == 0)
continue;
if (edge->disp_nr() == alias_disp_nr)
node->selected() = dn->selected();
}
return true;
}
// Un-alias ALIAS_DISP_NR. Unsuppress ALIAS_DISP_NR. Return true iff
// changed.
bool DispGraph::unalias(int alias_disp_nr)
{
DispNode *dn = get(alias_disp_nr);
if (dn == 0 || !dn->active() || dn->clustered())
return false;
if (!dn->hidden())
return false;
// Unsuppress display
dn->hidden() = false;
// Delete all alias edges associated with this node
VoidArray kill_edges;
GraphEdge *edge;
for (edge = firstEdge(); edge != 0; edge = nextEdge(edge))
{
AliasGraphEdge *e = ptr_cast(AliasGraphEdge, edge);
if (e != 0 && e->disp_nr() == alias_disp_nr)
kill_edges += (void *)e;
}
for (int i = 0; i < kill_edges.size(); i++)
{
AliasGraphEdge *e = (AliasGraphEdge *)kill_edges[i];
if (e->to()->isHint())
{
*this -= e->to(); // Also removes E from graph
delete e->to();
}
else
{
*this -= e;
}
delete e;
}
// Unsuppress remaining (ordinary) hints
for (edge = dn->firstFrom(); edge != 0; edge = dn->nextFrom(edge))
{
GraphEdge *e = edge;
while (e->to()->isHint())
{
e->to()->hidden() = false;
e = e->to()->firstFrom();
}
}
for (edge = dn->firstTo(); edge != 0; edge = dn->nextTo(edge))
{
GraphEdge *e = edge;
while (e->from()->isHint())
{
e->from()->hidden() = false;
e = e->from()->firstTo();
}
}
dn->alias_of = 0;
return true;
}
//-----------------------------------------------------------------------------
// Routing
//-----------------------------------------------------------------------------
// True iff R->P1 and R->P2 have the same angle
bool DispGraph::same_angle(const BoxPoint& r,
const BoxPoint& p1,
const BoxPoint& p2)
{
if (p1 == r || p2 == r)
return false; // No angle to determine
double angle1 = atan2(double(r[Y] - p1[Y]), double(r[X] - p1[X]));
double angle2 = atan2(double(r[Y] - p2[Y]), double(r[X] - p2[X]));
const double epsilon = 0.1;
return fabs(angle1 - angle2) < epsilon;
}
// True iff NODE is attached to an edge with the same angle as P
bool DispGraph::has_angle(PosGraphNode *node, const BoxPoint& p)
{
GraphEdge *edge;
for (edge = node->firstFrom(); edge != 0; edge = node->nextFrom(edge))
{
if (same_angle(node->pos(), edge->to()->pos(), p))
return true;
}
for (edge = node->firstTo(); edge != 0; edge = node->nextTo(edge))
{
if (same_angle(node->pos(), edge->from()->pos(), p))
return true;
}
return false;
}
// Add a new edge in existing graph
void DispGraph::add_alias_edge(Widget w, int alias_disp_nr,
GraphNode *_from, GraphNode *_to,
EdgeAnnotation *anno)
{
PosGraphNode *from = ptr_cast(PosGraphNode, _from);
PosGraphNode *to = ptr_cast(PosGraphNode, _to);
if (anno != 0)
anno = anno->dup();
// Check whether the new edge may be hidden by existing edges
if (from == to || (from == 0 || to == 0))
{
// Self-referring edge or bad nodes
add_direct_alias_edge(w, alias_disp_nr, _from, _to, anno);
}
else
{
// Check for interferences with existing edge
add_routed_alias_edge(w, alias_disp_nr, from, to, anno);
}
}
// Add a direct edge from FROM to TO
void DispGraph::add_direct_alias_edge(Widget, int alias_disp_nr,
GraphNode *from, GraphNode *to,
EdgeAnnotation *anno)
{
*this += new AliasGraphEdge(alias_disp_nr, from, to, anno);
}
// Check whether P is obscured by any node
bool DispGraph::is_hidden(Widget w, const BoxPoint& p) const
{
const GraphGC& graphGC = graphEditGetGraphGC(w);
for (GraphNode *n = firstVisibleNode(); n != 0; n = nextVisibleNode(n))
{
RegionGraphNode *node = ptr_cast(RegionGraphNode, n);
if (node == 0)
continue;
if (p == node->pos() || p <= node->sensitiveRegion(graphGC))
return true;
}
return false;
}
// Rotate offset P by ANGLE (in degrees)
BoxPoint DispGraph::rotate_offset(const BoxPoint& p, int angle)
{
if (p == BoxPoint(0, 0))
return p;
double length = hypot(p[X], p[Y]);
double alpha = atan2(double(p[X]), double(p[Y]));
alpha += (2.0 * PI * angle / 360.0);
return BoxPoint(BoxCoordinate(length * cos(alpha)),
BoxCoordinate(length * sin(alpha)));
}
// Check whether POS1 and POS2 are okay as hint positions for FROM and TO
bool DispGraph::hint_positions_ok(Widget w,
PosGraphNode *from,
PosGraphNode *to,
const BoxPoint& pos1,
const BoxPoint& pos2) const
{
BoxPoint p1 = graphEditFinalPosition(w, pos1);
BoxPoint p2 = graphEditFinalPosition(w, pos2);
if (p1[X] <= 0 || p2[X] <= 0 || p1[Y] <= 0 || p2[Y] <= 0)
return false; // Bad coordinates
if (p1 == from->pos() && p2 == to->pos())
{
// Direct edge
if (has_angle(from, to->pos()))
return false; // New edge obscured by existing edge
if (has_angle(to, from->pos()))
return false; // New edge obscured by existing edge
}
else
{
// One or two hints
if (has_angle(from, p1))
return false; // New edge obscured by existing edge
if (has_angle(to, p2))
return false; // New edge obscured by existing edge
if (is_hidden(w, p1))
return false; // Hint obscured by existing node
if (is_hidden(w, p2))
return false; // Hint obscured by existing node
}
BoxPoint dist = p2 - p1;
if (dist[X] > 0 || dist[Y] > 0)
{
BoxPoint center = graphEditFinalPosition(w, p1 + dist / 2);
if (is_hidden(w, center)) // Center obscured by existing node
return false;
}
return true;
}
// Add edge from FROM to TO, inserting hints if required
void DispGraph::add_routed_alias_edge(Widget w, int alias_disp_nr,
PosGraphNode *from, PosGraphNode *to,
EdgeAnnotation *anno)
{
// Determine hint offsets
Dimension grid_height = 16;
Dimension grid_width = 16;
XtVaGetValues(w,
XtNgridHeight, &grid_height,
XtNgridWidth, &grid_width,
NULL);
BoxPoint dist = to->pos() - from->pos();
BoxPoint center = from->pos() + dist / 2;
double angle = atan2(double(dist[X]), double(dist[Y]));
BoxPoint grid_offset(BoxCoordinate(grid_width * cos(angle)),
BoxCoordinate(grid_height * sin(angle)));
const int LEFT = 1;
const int RIGHT = 0;
BoxPoint offsets[2];
offsets[LEFT] = rotate_offset(grid_offset, +90);
offsets[RIGHT] = rotate_offset(grid_offset, -90);
#if 0
clog << "offsets[LEFT] = " << offsets[LEFT] << "\n";
clog << "offsets[RIGHT] = " << offsets[RIGHT] << "\n";
#endif
// Try hint offsets
BoxPoint pos1, pos2;
bool found = false;
const bool try_direct = false;
const int max_iterations = 100;
for (int i = 0; i < max_iterations && !found; i++)
{
for (int side = RIGHT; !found && side <= LEFT; side++)
{
BoxPoint offset = offsets[side] * i;
if (try_direct && i == 0)
{
// Try direct edge
pos1 = from->pos() + offset;
pos2 = to->pos() + offset;
}
else
{
// Try one-hint edge
pos1 = pos2 = center + offset;
}
#if 0
clog << "#" << i << " - "
<< (side == LEFT ? "left side: " : "right side: ")
<< "trying pos1 = " << pos1
<< " and pos2 = " << pos2 << "\n";
#endif
found = hint_positions_ok(w, from, to, pos1, pos2);
}
}
if (!found)
{
// Give up
cerr << "Warning: could not find edge route after "
<< max_iterations << " iterations\n";
pos1 = from->pos();
pos2 = to->pos();
}
if (try_direct && pos1 == from->pos() && pos2 == to->pos())
{
// No need for hints - add direct edge
add_direct_alias_edge(w, alias_disp_nr, from, to, anno);
}
else
{
#if 0
assert(!try_direct || pos1 == pos2);
#endif
// Add single hint
HintGraphNode *hint = new HintGraphNode(pos1);
hint->hidden() = from->hidden() || to->hidden();
*this += hint;
// Add edges
*this += new AliasGraphEdge(alias_disp_nr, from, hint, anno);
*this += new AliasGraphEdge(alias_disp_nr, hint, to);
}
}
//-----------------------------------------------------------------------------
// Display clustering
//-----------------------------------------------------------------------------
// Hide/Unhide all alias edges of NODE according to its status
void DispGraph::update_alias_edges(DispNode *node)
{
for (GraphEdge *edge = firstEdge(); edge != 0; edge = nextEdge(edge))
{
AliasGraphEdge *e = ptr_cast(AliasGraphEdge, edge);
if (e != 0 && e->disp_nr() == node->disp_nr())
{
if (e->to()->isHint())
e->to()->hidden() = node->hidden();
e->hidden() = node->hidden();
}
}
}
void DispGraph::cluster(DispNode *dn, int cluster)
{
dn->cluster(cluster);
update_alias_edges(dn);
}
//-----------------------------------------------------------------------------
// Display activation
//-----------------------------------------------------------------------------
bool DispGraph::hide_inactive_displays = true;
bool DispGraph::make_inactive(DispNode *dn)
{
if (dn->active() && dn->enabled())
{
if (!hide_inactive_displays)
{
dn->disable();
}
else
{
dn->make_inactive();
update_alias_edges(dn);
}
return true;
}
return false;
}
bool DispGraph::make_active(DispNode *dn)
{
if (!dn->active())
{
dn->make_active();
update_alias_edges(dn);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Title stuff
//-----------------------------------------------------------------------------
bool DispGraph::refresh_titles() const
{
bool changed = false;
MapRef ref;
for (DispNode *dn = first(ref); dn != 0; dn = next(ref))
{
bool is_dependent = false;
for (GraphEdge *e = dn->firstTo(); e != 0; e = dn->nextTo(e))
{
if (e->from() == dn)
continue; // Self edge
if (ptr_cast(AliasGraphEdge, e) != 0)
continue; // Alias edge
if (e->from()->hidden())
continue; // Invisible edge
LineGraphEdge *le = ptr_cast(LineGraphEdge, e);
if (le != 0 && le->annotation() != 0)
{
is_dependent = true;
break;
}
}
bool need_title = false;
if (is_dependent && app_data.show_dependent_display_titles)
need_title = true;
else if (!is_dependent && app_data.show_base_display_titles)
need_title = true;
if (dn->set_title(need_title))
changed = true;
}
return changed;
}
//-----------------------------------------------------------------------------
// Plotting
//-----------------------------------------------------------------------------
// Print all plots
void DispGraph::print_plots(const string& filename, const GraphGC& gc) const
{
for (GraphNode *node = firstVisibleNode(); node != 0;
node = nextVisibleNode(node))
{
DispNode *dn = ptr_cast(DispNode, node);
if (dn == 0)
continue;
if (gc.printSelectedNodesOnly && !dn->selected())
continue;
dn->print_plots(filename, gc);
}
}