From 86e10680c0f0c4d9f421ece6ec84cd898913af79 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 4 Jan 2014 00:08:59 +0000 Subject: 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 --- src/Canvas.cpp | 78 +++++++++++++++++++--------------------------- src/fdgl.hpp | 91 ++++++++++++++++++++++++++++++++++++++++++------------ src/ganv-private.h | 3 +- src/node.c | 3 +- 4 files changed, 105 insertions(+), 70 deletions(-) (limited to 'src') 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, ®.pos.x, ®.pos.y, ®.area.x, ®.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 #include -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 } -- cgit v1.2.1