summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-01-04 00:08:59 +0000
committerDavid Robillard <d@drobilla.net>2014-01-04 00:08:59 +0000
commit86e10680c0f0c4d9f421ece6ec84cd898913af79 (patch)
treee41813b556b61a6ed9bd37c7a88690892ca166ab /src
parentcac7e4e5e5b4ad8331ba75649dcc84ead3f4a5b4 (diff)
downloadganv-86e10680c0f0c4d9f421ece6ec84cd898913af79.tar.gz
ganv-86e10680c0f0c4d9f421ece6ec84cd898913af79.tar.bz2
ganv-86e10680c0f0c4d9f421ece6ec84cd898913af79.zip
Fix select rectangle drag from bottom right to top left.
Simplify layout code. Calculate rectangle distance instead of center distance to minimize overlap. git-svn-id: http://svn.drobilla.net/lad/trunk/ganv@5250 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/Canvas.cpp78
-rw-r--r--src/fdgl.hpp91
-rw-r--r--src/ganv-private.h3
-rw-r--r--src/node.c3
4 files changed, 105 insertions, 70 deletions
diff --git a/src/Canvas.cpp b/src/Canvas.cpp
index 73e78cb..96b097f 100644
--- a/src/Canvas.cpp
+++ b/src/Canvas.cpp
@@ -138,6 +138,8 @@ struct GanvCanvasImpl {
, _drag_edge(NULL)
, _drag_node(NULL)
, _select_rect(NULL)
+ , _select_start_x(0.0)
+ , _select_start_y(0.0)
, _zoom(1.0)
, _font_size(0.0)
, _drag_state(NOT_DRAGGING)
@@ -267,7 +269,9 @@ struct GanvCanvasImpl {
GanvEdge* _drag_edge;
GanvNode* _drag_node;
- GanvBox* _select_rect; ///< Rectangle for drag selection
+ GanvBox* _select_rect; ///< Rectangle for drag selection
+ double _select_start_x; ///< Selection drag start x coordinate
+ double _select_start_y; ///< Selection drag start y coordinate
double _zoom; ///< Current zoom level
double _font_size; ///< Current font size in points
@@ -724,6 +728,7 @@ get_region(GanvNode* node)
ganv_item_get_bounds(item, &x1, &y1, &x2, &y2);
Region reg;
+ ganv_item_get_bounds(item, &reg.pos.x, &reg.pos.y, &reg.area.x, &reg.area.y);
reg.area.x = x2 - x1;
reg.area.y = y2 - y1;
reg.pos.x = item->x + (reg.area.x / 2.0);
@@ -752,7 +757,7 @@ GanvCanvasImpl::layout_iteration()
prev = now;
- const double QUANTUM = 0.1;
+ const double QUANTUM = 0.05;
double sym_time = 0.0;
while (sym_time + QUANTUM < time_to_run) {
if (!layout_calculate(QUANTUM, FALSE)) {
@@ -767,10 +772,8 @@ GanvCanvasImpl::layout_iteration()
gboolean
GanvCanvasImpl::layout_calculate(double dur, bool update)
{
- static const double SPRING_K = 64.0;
-
// A light directional force to push sources to the top left
- static const double DIR_MAGNITUDE = -2200.0;
+ static const double DIR_MAGNITUDE = -2000.0;
Vector dir = { 0.0, 0.0 };
switch (_gcanvas->direction) {
case GANV_DIRECTION_RIGHT: dir.x = DIR_MAGNITUDE; break;
@@ -792,15 +795,14 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
continue;
}
- head->impl->has_in_edges = TRUE;
- tail->impl->has_out_edges = TRUE;
+ head->impl->connected = tail->impl->connected = TRUE;
GanvEdgeCoords coords;
ganv_edge_get_coords(edge, &coords);
const Vector tpos = { coords.x1, coords.y1 };
const Vector hpos = { coords.x2, coords.y2 };
- apply_force(tail, head, edge_force(dir, hpos, tpos, 0.000000000001, SPRING_K));
+ apply_force(tail, head, edge_force(dir, hpos, tpos));
}
// Calculate repelling forces between nodes
@@ -808,30 +810,20 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
if (!GANV_IS_MODULE(*i) && !GANV_IS_CIRCLE(*i)) {
continue;
}
- GanvNode* const node = *i;
- const Region reg = get_region(node);
- GanvNode* partner = ganv_node_get_partner(node);
+ GanvNode* const node = *i;
+ GanvNode* partner = ganv_node_get_partner(node);
+ if (!partner && !node->impl->connected) {
+ continue;
+ }
+
+ const Region reg = get_region(node);
if (partner) {
// Add fake long spring to partner to line up as if connected
const Region preg = get_region(partner);
- apply_force(node, partner,
- edge_force(dir, preg.pos, reg.pos,
- preg.area.x + reg.area.x, SPRING_K / 2.0));
+ apply_force(node, partner, edge_force(dir, preg.pos, reg.pos));
}
- if (node->impl->is_source) {
- // Add fake weak spring from origin to sources to anchor graph layout
- const Vector anchor = { 0.0, 0.0 };
- node->impl->force = vec_add(
- node->impl->force,
- spring_force(anchor, reg.pos, 32.0, SPRING_K / 8.0));
- } else if (!node->impl->partner &&
- !node->impl->has_in_edges &&
- !node->impl->has_out_edges) {
- // Not a source and disconnected, don't repel other nodes
- continue;
- }
/* Add tide force which pulls all objects as if the layout is happening
on a flowing river surface. This prevents disconnected components
@@ -846,13 +838,7 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
if (i == j || (!GANV_IS_MODULE(*i) && !GANV_IS_CIRCLE(*i))) {
continue;
}
- GanvNode* const node2 = *j;
- if ((!node2->impl->has_in_edges && !node2->impl->has_out_edges) &&
- !node2->impl->is_source) {
- continue;
- }
-
- apply_force(node, node2, repel_force(reg, get_region(node2)));
+ apply_force(node, *j, repel_force(reg, get_region(*j)));
}
}
@@ -865,11 +851,9 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
GanvNode* const node = *i;
- static const float damp = 0.3; // Velocity damping
+ static const float damp = 0.2; // Velocity damping
- const bool has_edges = (node->impl->has_in_edges ||
- node->impl->has_out_edges);
- if (node->impl->grabbed || (!has_edges && !node->impl->is_source)) {
+ if (node->impl->grabbed || !node->impl->connected) {
node->impl->vel.x = 0.0;
node->impl->vel.y = 0.0;
} else {
@@ -877,7 +861,7 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
vec_mult(node->impl->force, dur));
node->impl->vel = vec_mult(node->impl->vel, damp);
- static const double MAX_VEL = 2000.0;
+ static const double MAX_VEL = 1000.0;
static const double MIN_COORD = 4.0;
// Clamp velocity
@@ -908,10 +892,9 @@ GanvCanvasImpl::layout_calculate(double dur, bool update)
}
// Reset forces for next time
- node->impl->force.x = 0.0;
- node->impl->force.y = 0.0;
- node->impl->has_in_edges = FALSE;
- node->impl->has_out_edges = FALSE;
+ node->impl->force.x = 0.0;
+ node->impl->force.y = 0.0;
+ node->impl->connected = FALSE;
}
if (update) {
@@ -1234,6 +1217,8 @@ GanvCanvasImpl::select_drag_handler(GdkEvent* event)
"fill-color", SELECT_RECT_FILL_COLOUR,
"border-color", SELECT_RECT_BORDER_COLOUR,
NULL));
+ _select_start_x = event->button.x;
+ _select_start_y = event->button.y;
ganv_item_grab(
GANV_ITEM(root()), GDK_POINTER_MOTION_MASK|GDK_BUTTON_RELEASE_MASK,
NULL, event->button.time);
@@ -1242,10 +1227,11 @@ GanvCanvasImpl::select_drag_handler(GdkEvent* event)
assert(_select_rect);
double x, y;
get_motion_coords(&event->motion, &x, &y);
- ganv_item_set(GANV_ITEM(_select_rect),
- "x2", x,
- "y2", y,
- NULL);
+ _select_rect->impl->coords.x1 = MIN(_select_start_x, x);
+ _select_rect->impl->coords.y1 = MIN(_select_start_y, y);
+ _select_rect->impl->coords.x2 = MAX(_select_start_x, x);
+ _select_rect->impl->coords.y2 = MAX(_select_start_y, y);
+ ganv_item_request_update(&_select_rect->node.item);
return true;
} else if (event->type == GDK_BUTTON_RELEASE && _drag_state == SELECT) {
// Normalize select rect
diff --git a/src/fdgl.hpp b/src/fdgl.hpp
index 83d9981..7d3d364 100644
--- a/src/fdgl.hpp
+++ b/src/fdgl.hpp
@@ -16,7 +16,9 @@
#include <float.h>
#include <math.h>
-static const double CHARGE_KE = 200000000.0;
+static const double CHARGE_KE = 4000000.0;
+static const double EDGE_K = 64.0;
+static const double EDGE_LEN = 1.0;
struct Region {
Vector pos;
@@ -76,13 +78,9 @@ spring_force(const Vector& a, const Vector& b, double length, double k)
/** Spring force with a directional force to align with flow direction. */
static const Vector
-edge_force(const Vector& dir,
- const Vector& hpos,
- const Vector& tpos,
- double length,
- double k)
+edge_force(const Vector& dir, const Vector& hpos, const Vector& tpos)
{
- return vec_add(dir, spring_force(hpos, tpos, length, k));
+ return vec_add(dir, spring_force(hpos, tpos, EDGE_LEN, EDGE_K));
}
/** Constant tide force, does not vary with distance. */
@@ -95,21 +93,74 @@ tide_force(const Vector& a, const Vector& b, double power)
return vec_mult(vec, G * power / mag);
}
-/**
- Repelling charge force.
+inline double
+rect_distance(Vector* vec,
+ const double ax1, const double ay1,
+ const double ax2, const double ay2,
+ const double bx1, const double by1,
+ const double bx2, const double by2)
+{
+ vec->x = 0.0;
+ vec->y = 0.0;
+
+ if (ax2 <= bx1) { // A is completely to the left of B
+ vec->x = ax2 - bx1;
+ if (ay2 <= by1) { // Top Left
+ const double dx = bx1 - ax2;
+ const double dy = by1 - ay2;
+ vec->y = ay2 - by1;
+ return sqrt(dx * dx + dy * dy);
+ } else if (ay1 >= by2) { // Bottom left
+ const double dx = bx1 - ax2;
+ const double dy = ay1 - by2;
+ vec->y = ay1 - by2;
+ return sqrt(dx * dx + dy * dy);
+ } else { // Left
+ return bx1 - ax2;
+ }
+ } else if (ax1 >= bx2) { // A is completely to the right of B
+ vec->x = ax1 - bx2;
+ if (ay2 <= by1) { // Top right
+ const double dx = ax1 - bx2;
+ const double dy = by1 - ay2;
+ vec->y = ay2 - by1;
+ return sqrt(dx * dx + dy * dy);
+ } else if (ay1 >= by2) { // Bottom right
+ const double dx = ax1 - bx2;
+ const double dy = ay1 - by2;
+ vec->y = ay1 - by2;
+ return sqrt(dx * dx + dy * dy);
+ } else { // Right
+ return ax1 - bx2;
+ }
+ } else if (ay2 <= by1) { // Top
+ vec->y = ay2 - by1;
+ return by1 - ay2;
+ } else if (ay1 >= by2) { // Bottom
+ vec->y = ay1 - by2;
+ return ay1 - by2;
+ } else { // Overlap
+ return 0.0;
+ }
+}
- Many FDGL algorithms use charge according to Coulomb's law, but here we use
- an inverse cube (not squared) law so influence drops off more rapidly with
- distance. This, in conjunction with a tide, keeps the layout compact.
-*/
+/** Repelling charge force, ala Coulomb's law. */
inline Vector
repel_force(const Region& a, const Region& b)
{
- const Vector vec = vec_sub(a.pos, b.pos);
- const double mag = vec_mag(vec);
- const Vector force = vec_mult(
- vec, (CHARGE_KE * 0.5 / (mag * mag * mag * mag * mag)));
- const Vector dforce = { force.x * (a.area.x * b.area.x),
- force.y * (a.area.y * b.area.y) };
- return dforce;
+ static const double MIN_DIST = DBL_EPSILON;
+
+ Vector vec;
+ double dist = rect_distance(
+ &vec,
+ a.pos.x - (a.area.x / 2.0), a.pos.y - (a.area.y / 2.0),
+ a.pos.x + (a.area.x / 2.0), a.pos.y + (a.area.y / 2.0),
+ b.pos.x - (b.area.x / 2.0), b.pos.y - (b.area.y / 2.0),
+ b.pos.x + (b.area.x / 2.0), b.pos.y + (b.area.y / 2.0));
+
+ if (dist <= MIN_DIST) {
+ dist = MIN_DIST;
+ vec = vec_sub(a.pos, b.pos);
+ }
+ return vec_mult(vec, (CHARGE_KE * 0.5 / (vec_mag(vec) * dist * dist)));
}
diff --git a/src/ganv-private.h b/src/ganv-private.h
index 1bbdddd..fdcd210 100644
--- a/src/ganv-private.h
+++ b/src/ganv-private.h
@@ -116,8 +116,7 @@ struct _GanvNodeImpl {
#ifdef GANV_FDGL
Vector force;
Vector vel;
- gboolean has_in_edges;
- gboolean has_out_edges;
+ gboolean connected;
#endif
};
diff --git a/src/node.c b/src/node.c
index f253e5b..72af575 100644
--- a/src/node.c
+++ b/src/node.c
@@ -76,8 +76,7 @@ ganv_node_init(GanvNode* node)
impl->force.y = 0.0;
impl->vel.x = 0.0;
impl->vel.y = 0.0;
- impl->has_in_edges = FALSE;
- impl->has_out_edges = FALSE;
+ impl->connected = FALSE;
#endif
}