void object_draw_connectionpoints(DiaObject *obj, DDisplay *ddisp) { int i; static Color midpoint_color = { 1.0, 0.0, 0.0, 1.0 }; DiaRenderer *renderer = ddisp->renderer; DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (ddisp->renderer); DiaInteractiveRendererInterface *irenderer = DIA_GET_INTERACTIVE_RENDERER_INTERFACE (ddisp->renderer); /* this does not change for any of the points */ renderer_ops->set_linewidth (renderer, 0.0); renderer_ops->set_linestyle (renderer, LINESTYLE_SOLID); /* optimization to only draw the connection points at all if the size * of the object (bounding box) is bigger than the summed size of the * connection points - or some variation thereof ;) */ if (dia_object_get_num_connections(obj) > 1) { const Rectangle *bbox = dia_object_get_bounding_box (obj); real w = ddisplay_transform_length (ddisp, bbox->right - bbox->left); real h = ddisplay_transform_length (ddisp, bbox->bottom - bbox->top); int n = dia_object_get_num_connections(obj); /* just comparing the sizes is still drawing more CPs than useful - try 50% */ if (w * h < n * CONNECTIONPOINT_SIZE * CONNECTIONPOINT_SIZE * 2) { if (ddisp->mainpoint_magnetism) return; /* just draw the main point */ for (i = 0; i < n; ++i) { if (obj->connections[i]->flags & CP_FLAG_ANYPLACE) connectionpoint_draw(obj->connections[i], ddisp, renderer, irenderer, &midpoint_color); } return; } } for (i=0;i<dia_object_get_num_connections(obj);i++) { if ((obj->connections[i]->flags & CP_FLAG_ANYPLACE) == 0) connectionpoint_draw(obj->connections[i], ddisp, renderer, irenderer, &connectionpoint_color); else if (!ddisp->mainpoint_magnetism) /* draw the "whole object"/center connpoints, but only when we don't * have snap-to-grid */ connectionpoint_draw(obj->connections[i], ddisp, renderer, irenderer, &midpoint_color); } }
/*! * \brief Apply a good route (or none) to the given _OrthConn * * Calculate a 'pleasing' route between two connection points. * If a good route is found, updates the given OrthConn with the values * and returns TRUE. * Otherwise, the OrthConn is untouched, and the function returns FALSE. * Handles are not updated by this operation. * @param conn The orthconn object to autoroute for. * @param startconn The connectionpoint at the start of the orthconn, * or null if it is not connected there at the moment. * @param endconn The connectionpoint at the end (target) of the orthconn, * or null if it is not connected there at the moment. * @return TRUE if the orthconn could be laid out reasonably, FALSE otherwise. * * \ingroup Autorouting * * \callgraph */ gboolean autoroute_layout_orthconn(OrthConn *conn, ConnectionPoint *startconn, ConnectionPoint *endconn) { real min_badness = MAX_BADNESS; guint best_intersects = G_MAXINT; guint intersects; Point *best_layout = NULL; guint best_num_points = 0; int startdir, enddir; int fromdir, todir; Point frompos, topos; frompos = conn->points[0]; topos = conn->points[conn->numpoints-1]; if (startconn != NULL) { fromdir = startconn->directions; frompos = startconn->pos; } else fromdir = DIR_NORTH|DIR_EAST|DIR_SOUTH|DIR_WEST; if (endconn != NULL) { todir = endconn->directions; topos = endconn->pos; } else todir = DIR_NORTH|DIR_EAST|DIR_SOUTH|DIR_WEST; for (startdir = DIR_NORTH; startdir <= DIR_WEST; startdir *= 2) { for (enddir = DIR_NORTH; enddir <= DIR_WEST; enddir *= 2) { if ((fromdir & startdir) && (todir & enddir)) { real this_badness; Point *this_layout = NULL; guint this_num_points; guint normal_enddir; Point startpoint, endpoint; Point otherpoint; startpoint = autolayout_adjust_for_gap(&frompos, startdir, startconn); autolayout_adjust_for_arrow(&startpoint, startdir, conn->extra_spacing.start_trans); endpoint = autolayout_adjust_for_gap(&topos, enddir, endconn); autolayout_adjust_for_arrow(&endpoint, enddir, conn->extra_spacing.end_trans); /* printf("Startdir %d enddir %d orgstart %.2f, %.2f orgend %.2f, %.2f start %.2f, %.2f end %.2f, %.2f\n", startdir, enddir, frompos.x, frompos.y, topos.x, topos.y, startpoint.x, startpoint.y, endpoint.x, endpoint.y); */ normal_enddir = autolayout_normalize_points(startdir, enddir, startpoint, endpoint, &otherpoint); if (normal_enddir == DIR_NORTH ) { this_badness = autoroute_layout_parallel(&otherpoint, &this_num_points, &this_layout); } else if (normal_enddir == DIR_SOUTH) { this_badness = autoroute_layout_opposite(&otherpoint, &this_num_points, &this_layout); } else { this_badness = autoroute_layout_orthogonal(&otherpoint, normal_enddir, &this_num_points, &this_layout); } if (this_layout != NULL) { /* this_layout is eaten by unnormalize */ Point *unnormalized = autolayout_unnormalize_points(startdir, startpoint, this_layout, this_num_points); intersects = autolayout_calc_intersects ( startconn ? dia_object_get_bounding_box (startconn->object) : NULL, endconn ? dia_object_get_bounding_box (endconn->object) : NULL, unnormalized, this_num_points); if ( intersects <= best_intersects && this_badness-min_badness < -0.00001) { /* printf("Dir %d to %d badness %f < %f\n", startdir, enddir, this_badness, min_badness); */ min_badness = this_badness; if (best_layout != NULL) g_free(best_layout); best_layout = unnormalized; best_num_points = this_num_points; /* revert adjusting start and end point */ autolayout_adjust_for_arrow(&best_layout[0], startdir, -conn->extra_spacing.start_trans); autolayout_adjust_for_arrow(&best_layout[best_num_points-1], enddir, -conn->extra_spacing.end_trans); best_intersects = intersects; } else { g_free(unnormalized); } } } } } if (min_badness < MAX_BADNESS) { orthconn_set_points(conn, best_num_points, best_layout); g_free(best_layout); return TRUE; } else { g_free(best_layout); return FALSE; } }
/*! * \brief Calback function invoking layout algorithms from Dia's menu * \ingroup LayoutPlugin */ static ObjectChange * layout_callback (DiagramData *data, const gchar *filename, guint flags, /* further additions */ void *user_data, GraphCreateFunc func) { ObjectChange *changes = NULL; GList *nodes = NULL, *edges = NULL, *list; const char *algorithm = (const char*)user_data; /* from the selection create two lists */ list = data_get_sorted_selected (data); while (list) { DiaObject *o = (DiaObject *)list->data; if (!maybe_edge (o)) nodes = g_list_append (nodes, o); //FIXME: neither 1 nor num_handles-1 is guaranteed to be the second connection // it entirely depends on the objects implementation else if ( o->num_handles > 1 && o->handles[0]->connected_to && (o->handles[1]->connected_to || o->handles[o->num_handles-1]->connected_to)) edges = g_list_append (edges, o); list = g_list_next(list); } if (g_list_length (edges) < 1 || g_list_length (nodes) < 2) { message_warning (_("Please select edges and nodes to layout.")); } else { IGraph *g = func ? func () : NULL; if (!g) message_error (_("Graph creation failed")); else { std::vector<double> coords; /* transfer nodes and edges */ for (list = nodes; list != NULL; list = g_list_next(list)) { DiaObject *o = (DiaObject *)list->data; const Rectangle *bbox = dia_object_get_bounding_box (o); g->AddNode (bbox->left, bbox->top, bbox->right, bbox->bottom); } for (list = edges; list != NULL; list = g_list_next(list)) { DiaObject *o = (DiaObject *)list->data; DiaObject *src = o->handles[0]->connected_to->object; // see above: there is no guarantee ... DiaObject *dst = o->handles[1]->connected_to ? o->handles[1]->connected_to->object : o->handles[o->num_handles-1]->connected_to->object; if (_obj_get_bends (o, coords)) g->AddEdge (g_list_index (nodes, src), g_list_index (nodes, dst), &coords[0], coords.size()); else g->AddEdge (g_list_index (nodes, src), g_list_index (nodes, dst), NULL, 0); } IGraph::eResult res; if ((res = g->Layout (algorithm)) != IGraph::SUCCESS) { const char *sErr; switch (res) { case IGraph::NO_MODULE : sErr = _("No such module."); break; case IGraph::OUT_OF_MEMORY : sErr = _("Out of memory."); break; case IGraph::NO_TREE: sErr = _("Not a tree."); break; case IGraph::NO_FOREST: sErr = _("Not a forest."); break; case IGraph::FAILED_ALGORITHM: sErr = _("Failed algorithm."); break; case IGraph::FAILED_PRECONDITION: sErr = _("Failed precondition."); break; case IGraph::CRASHED : sErr = _("OGDF crashed."); break; default : sErr = _("Unknown reason"); break; } message_warning (_("Layout '%s' failed.\n%s"), (const char*)user_data, sErr); } else { changes = change_list_create (); /* transfer back information */ int n; for (n = 0, list = nodes; list != NULL; list = g_list_next (list), ++n) { Point pt; if (g->GetNodePosition (n, &pt.x, &pt.y)) { DiaObject *o = (DiaObject *)list->data; GPtrArray *props = g_ptr_array_new (); //FIXME: can't use "obj_pos", it is not read in usual update_data impementations // "elem_corner" will only work for Element derived classes, but that covers most // of the cases here ... prop_list_add_point (props, "elem_corner", &pt); change_list_add (changes, object_apply_props (o, props)); } } // first update to reuse the connected points diagram_update_connections_selection(DIA_DIAGRAM (data)); /* use edge bends, if any */ int e; for (e = 0, list = edges; list != NULL; list = g_list_next (list), ++e) { DiaObject *o = (DiaObject *)list->data; // number of bends / 2 is the number of points int n = g->GetEdgeBends (e, NULL, 0); if (n >= 0) { // with 0 it is just a reset of the exisiting line try { coords.resize (n); } catch (std::bad_alloc& ex) { g_warning ("%s", ex.what()); continue; } g->GetEdgeBends (e, &coords[0], n); change_list_add (changes, _obj_set_bends (o, coords)); } } /* update view */ diagram_update_connections_selection(DIA_DIAGRAM (data)); } g->Release (); } } g_list_free (nodes); g_list_free (edges); return changes; }