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);
  }
}
Beispiel #2
0
/*!
 * \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;
  }
}
Beispiel #3
0
/*!
 * \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;
}