Quite simple: The user should be able to drag a line onto an object and drop it there to connect. Connection points should be only for marking specific places. Problems: Backward compatibility -- how do we store this virtual connpoint? What if we want e.g. zigzaglines pointing to other parts of the object that straight towards the center? Do we want to support that this way now, or should we just let that be done with explicit connpoints? There was some work in this direction under the 'line gap' code, however I'm not sure if that's the best approach. In particular, it doesn't work for other than lines, we would want this virtual connpoint to work for any connector. This would require ObjectHighlighting in order to be comprehensible for the user. -- LarsClausen - 19 Mar 2004 ObjectHighlighting works in the dev branch as of now. Used when connecting. Quite nifty. So we can start looking at virtual connpoints any time now.Naming connection points
Starting to look at the virtual connection points. First thing is to be able to name connectionpoints, that way we can get away from the backwards compatibility problems. Will probably end up making loading a little slower, if we have to search each object's cp's to find the right name, but I think it'll be worth it. Internally in Dia we will still have the index, so we won't be slowed down by this. Note that naming can also be used for other things, such as naming the connectors on a CPU. Using names in general allows us to add cp's without having to worry about what happens if the object wants to add a cp as well. It does mean, however, that objects that currently use the ordering of cp's (such as UML Class) will have to name them with their numbers, or they'll get messed up. Note that by naming the old cp's with their numbers, newly created diagrams can still be read by old versions of Dia and have those cp's work. New cp's, of course, will not work. Current test implementation (in the dev branch) uses the ordering for cp's that are saved as just a number, and other cp's are searched for. It'd help speed things up a little if we can keep that, but it's not a requirement. Since virtual connectionpoints should be added after the object is created, I think we're ok. Except maybe for objects like UML Class that can have more cp's depending on its data, in which case we would want to be sure that the class-generated cp's are placed in their proper order, i.e. before virtual cp's, when loaded. Important to remember that the number-name of a cp should correspond to the old placement, and not simply take the index given. Otherwise the addition of a virtual cp will make later regular cp's not work. I could of course ruthlessly break every single connection in existence. However, I think I will reserve that option for a time when there's no other way out.
- Named connection points; why not. Maybe a better way would be to dissociate the the connection point names and the connection point identifiers (like gschem does). Some connection points are better left anonymous, and I can see a very useful reason why you could want multiple connection points with the same name (see below). We'd need to let the use to know what connection point names are, and to change them.
- The connection point identifier just needs to remain unique within an object instance for the life of this instance. Perhaps a library routine would do some good here; nobody really needs to care about what the ID is, so a 32-bit int will certainly do.
- -- CyrilleChepelov - 25 Mar 2004
Virtual connection points
Now that we can add connection points without worry, let's think about what virtual connection points we want. We have found three different kinds:Of these, the latter two need to work with the resize functionality, which makes them harder. Most things just resize easily, and the cp's can be automatically calculated based on the ratio. Some things like the UML Component, have areas that don't resize with the rest. How should the cp's deal with these?
- Center-connected
- Edge-connected
- Anywhere-connected (chickenpox cp's)
- Also, some things, like most polygons, polyline and bezier-derived objects, can be bent. These too will need to move the additional connection points in a sensible way.
- (An example. Suppose you have a polyshape, with two straight edges and one bezier. Somehow, you created an edge connection point (eCP) close to the middle of the bezier, and a chickenpox connection point (cCP) somewhere in the shape, somewhat close to an edge. Now grab any handle and change the polyshape's shape. Where do you expect the eCP to be (I'd think, same place on the edge within the Bezier parametric space), and where should the cCP be?)
- I think a solution would be to let objects define a parametric space of their own; they would also need to provide ways to convert a current relative X-Y position into that parametric space, and back. Upon connection point creation, dia would ask the object what are the parametric coordinates for such a point. Unlike the geometric coordinates, the parametric coordinates will likely be constant throughout the object's lifetime. Every time the object is resized (or sheared), in fact every time we today ask objects to reorganise the static connection points (sCP), we'll need to convert every dynamic CP from parametric space to X-Y (another method).
- Typical parametric spaces would be: U=(float)0..1, V=(float)0..1 for normal "easy case resize" elements. We'd have then X=U * obj.width, Y=V * obj.height. Lines, arcs would have a single U=(float)0..1. Polylines and polybeziers could have U=(int)segment#, V=(float)0..1. Polygons/polyshapes could have U=(int)base1_segment#, V=(float)0..1, S=(int)base2_segment#, T=(float)0..1 (where those segments would be chosen as the closest base segments allowing to map X,Y to U,V,S,T at cCP creation time); UML Component could have T=(bool)in_fixed_area,U=0..1,W=0..1 (with X = U * (T?obj-<fixed_area_width:obj->width) and Y likewise).
- Waaah, that's complicated; I'm starting to add some figures from outer ParametricSpace, to 'splain it a bit better.
- -- CyrilleChepelov - 25 Mar 2004
- we need to be able to allocate storage for these parametric coordinates; this storage spaceAnother method each class should provide. Persistence, too: add two other methods.
- in summary:
- size_t ( * GetParametricCPSizeFunc)(Object * obj);
- void ( * CPMapToParametric)(Object * obj, const Point * pt, void * pcp_pt);
- void ( * CPMapFromParametric)(Object * obj, const void * pcp_pt, Point * pt);
- void ( * CPSaveParametric)(Object * obj, const void *pcp_pt, ParametricCPNode? * node);
- bool ( * CPLoadParametric)(Object * obj, const void *pcp_pt, ParametricCPNode? * node);
The center-connected cp's don't have to worry about those things, as long as they can get a working 'distance from object' number. That'll allow them to have the connector go to exactly the edge of the object regardless of how it resizes, and will remove the need for twiddling the connectors when objects are rearranged. The lines will also mostly go in at a reasonable angle, except for really strange objects. Main downside is that the user cannot have lines go in 'side by side' in parallel. For that, they'll still have to use regular cp's. I absolutely, totally, 100% want the center-connected points, especially after the dogfood I had this morning. The others are, IMHO, not nearly as useful, as the suffer from the rearrangement problem that normal cp's also have, and I wouldn't mind Dia 1.0 not having those.
- -- CyrilleChepelov - 25 Mar 2004
- I disagree; while centre-connected points are no doubt very useful, they can't be appropriate for any kind of shape (certainly great for ellipses and boxes). I wonder if some combination mode wouldn't be a killer too (connect to centre point; rearrangement takes place to the best (closest) candidate point).
- Named connection points, and "centre-connected auto-arranging connections which actually connect only on real connection points, not just any edge"; I can see a killer application for this. Imagine that we have a rule that all connection points with the same name within an object are equivalent, and that dia is allowed to move a connector from a connection point to any another connection point within that equivalence class. For convenience, dropping a connector on an object's body (while chickenpox is disabled) is equivalent to mean "connect to the closest connection point with an empty name".
- For simple or assorted shapes, the connection points would always be with an empty name (anonymous but identified, as from now on CPs will always be identified). However, for things like UML objects, you clearly do NOT want this magic auto-arrange to create havoc; so you'd give them all a unique name (such as methodName on the left and right of a method, up, down, etc.)
- we may want to run the connection point names through gettext() when we first instanciate an object (not when we reload).
- It will be useful to let shapes define connection point names.
- -- CyrilleChepelov - 25 Mar 2004
- omni-graffle has the best implementation of connection points i've seen, not comparable with anything else i've used. It's a combination of the above mentioned 3 types. if you haven't seen it, i suggest you check it out. (I started hacking UML objects today, since i'm not happy with some of the assumptions made in the UML set, so i'm learning how to change it and possibly propose a patch for it. hope you don't mind my comment here. oh, i ended up here looking for documentation on object API) -- ToniPrug - 25 Jul 2004
Implementation of center-connected virtual connection points.
We need three things for center-connected virtual connection points (center-points!):Creation is simple: When you drop a line end over an object, a new center-point is created, and the line is connected to it. Deletion is just as simple: When you disconnect the line from a center-point, that center-point disappears. Obviously, only one line can connect to any one center-point. Update is the interesting part. Just after creation, and every time either the connected object or the other end of the line moves, the position must be updated. Note that normal cp's don't move when the other end of the line moves, but since the placement here depends on the line angle, we have to update it. So here's how to update: We make a line that goes from the center to the "other end" of the connector ("other end" defined below). Along that line, we use the distance_from function to do binary search for the edge of the object, i.e. where the distance to the object is >0 but <0.00001 or so. The center-point moves to there, along with the connector.
- Creation
- Update position
- Deletion
- do we have a usefully working "distance from" function to an arbitrary shape object? (I'm thinking of polybeziers bent in a concave way, like a potatoish "C" letter rendered in a single polybezier). If yes, OK, if not, there might be a better way. We can reasonably easily compute the distance to a line, arc or bezier (and the closest point within that segment). We can also tell whether we're "inside" one of the graphic primitives (rectangle, pie, ellipse, polygon, polybezier). With a custom renderer, we can ask the object to render itself, and compare any arbitrary point to every primitive edge and every primitive filled shape. At the end of the algorithm, we know whether the input point is "inside" the object, we know its distance to the object's closest edge, we know where is the closest point on the edge.
- For the simple standard objects, this can't be much slower than the "distance from" computation if we keep a single instance of that magic renderer at hand; for more complex object, it has the potential of allowing to dispose of the "distance from" computation, by implementing it as a generic "object_distance_from_using_magic_renderer()" method implementation: less code.
- At creation (or maybe at parametric-to-geometric reevaluation time), we can reuse this algorithm to guess the preferred directions.
What is the "other end"? For a line, it's just the other end. For a polyline or zigzagline, it's the other end of the last segment. For a bezier or arc, it's more complex, and I will think about that later.
- -- CyrilleChepelov - 25 Mar 2004
There's potential interference between this and the autorouting for zigzaglines. Must think about that, too. -- LarsClausen - 23 Mar 2004 After some pushing and half a patch I got the Right Way [tm] to do center-points (known as main points). It's two flags on a connection point, one that says that the connection gathers handles from the entire object, and one that says that the line connected should stop at the edge. The two together allows the user to 'point at an object' rather than at a connection point, if so desired. It also lets objects be rearranged without having to rearrange lines in most instances. Those two behaviours are what I mostly wanted, it makes simple diagrams simpler. It is implemented by adding another connectionpoint to most objects, manually going through the code. Mostly, the cp is just in the middle of the object, though in some cases (polygon, beziergon) the middle may be outside the object:( The lines then take care of going only to the edge (autogapping). -- LarsClausen - 03 Apr 2005
- It's never more complex if you go to parametric space ;-)
- -- CyrilleChepelov - 25 Mar 2004
| Edit -:- Attach -:- Ref-By -:- Printable -:- More |