This article should eventually expand a bit on the parametric space idea I floated in VirtualConnectionPoints. The problem once we allow users to put arbitrary additional connection points (whether on edges or anywhere), is that when we scale or shear objects, we can sometimes have trouble finding where best to place these connection points. Some existing objects can have pretty weird transformations already, but they use their knowledge of their own internal structure to achieve a sane and consistent placement of their static connection points. What I want now is the ability to let each object describe in a "simple" and generic fashion how it behaves, so that when a disformation or a resize occurs, the core library can take care of computing the new connection point placements.The Problem
First, let's start with a simple square, on which we assume a dynamic connection point has somehow been created:
Now let's try with a triangle:
![]()
![]()
![]()
First a square. Let's identify where the connection point is, relative to the square and if we resize it, it's not too difficult to figure out the CP's new place. To solve the problem in a generic way, I propose we use a quick hop through a ParametricSpace adapted to each object class' behaviour.
![]()
![]()
![]()
A simple triangle Proportional resizing: we can figure out the CP's position now it becomes harder... Parametric Space?
Let's come back to the square example, and define an alternate reference, which I'll call U-V
This reference has one vector U, parallel to one of the horizontal edges, and with a length of whatever the rectangle's width is. V is parallel to a vertical edge, has a length of exactly the rectangle's height, and will have an origin in the same top-left corner as usual. We can now express the connection point's coordinates in this new reference. Its coordinates in the local X - Y reference were ( x , y ) = (1,1) (relative to the top-left corner). Since the horizontal distance from the left edge to the connection point is one fourth of the length of U, we derive a coordinate u = .25. Likewise along the V axis.
We can now say that the connection point's coordinates are ( u , v )=(0.25,0.25) in the alternate reference system we just defined. What happens if we resize the square?Since U and V are defined to have the same length as the width and height of the rectangle, respectively, we can notice that the coordinated of the updated connection point are still ( u , v )=(.25,.25). This is the key. If we can find, for every possible general object type, a reference system such as a connection point's coordinates in that system are constant no matter what geometric transform is applied to the object, and we can convert from the classic 2D euclidian space to that parametric space and back, then the problem is solved. I'm not a mathematician, as you can read here.
Some examples
I'll follow now with of examples. -- CyrilleChepelov - 25 Mar 2004Single-dimension objects
Shape Comments ![]()
A segmentThis is the simplest object; we define a single base vector U, of length equal to the segment's. Every point of segment can be described with a single coordinate u, with u = 0 => first point of the segment, u = 1 => second point, u = 0.5 => the middle point, etc. ![]()
An arcHere we'll define u as equal to (length from start point to CP / arc length). u = 0 => start point, u = 1 => end point, u = 0.5 => middle point (where dia puts a handle). ![]()
A hollow ellipseWe need to decide on an arbitrary start point for the ellipse, for instance the rightmost point intersecting with the ellipse's larger axis. We know the ellipse's circonference, and any point on the ellipse can be described as a fraction of the circonference from the start point. ![]()
A bezier line.Beziers are nice, in that every point of the bezier can is computed using a one-dimensional 0..1 coordinate system. We'll just use that. ![]()
Here, the intersection point could have two equally valid coordinates. We'll choose the lowest possible value for u (I estimate this will be around .30, in this case) Simple Two-dimension objects
Shape Comments ![]()
We already covered the rectangle case. We'll use ( u , v ) as described above ![]()
A filled ellipseWe already know how to describe points situated on the edge of the filled ellipse (that's the U coordinate). Any point within the bounds of the ellipse can be said to be on the edge of a smaller ellipse, scaled down by a V factor from the original ellipse (if the ellipse is a circle, this V factor is called the radius) Discrete coordinates
All the coordinates system we've seen so far use real number values. We'll see now some cases where certain axes can have discrete values:
Shape Comments ![]()
A polylineFor any point on a polyline, we can describe its position by the index I of the segment it sits on, and the single coordinate U of the point on that segment. The pair (I , U ) accurately describes the position of that point on the polyline. ![]()
A hollow beziergonIn fact, there is no inconvenient if the end of the last segment is the beginning of the first segment (closed polygon), or if a segment is not a segment of line but an arc or a bezier; we can use the same (I, U) coordinate system to describe any point on the edge of a beziergon. Filled polygons
Filled polygons are a bit more tricky. We could use a combination of the hollow polygon and the ellipse coordinate system, (I , U , V), with V = the "radius", or rather some factor to the barycentre of the shape. I don't think this would provide the best behaviour when users bend the polygon; I will propose a different coordinate system, (I, U, J, V) (yes, four dimensions). Let's take back our trusty rectangle, with its (U, V) coordinate system:
and assume for a moment that we're interested only in describing points on the rectangle's edge. We could then view this rectangle as a closed polygon, and indeed the (I,__U__) coordinate system would be adequate to describe a point on its boundary:
Now we want to describe any point within the rectangle's boundary. How did we do, originally? We picked two edge segments (almost arbitrarily: any two segments would have been adequate, provided that they were not parallel), and projected the connection point on each base segment, using a projection direction parallel to the other. We could say that we used the first and fourth segments as the base, and U and V are coordinates within that base
In this (I , U , J , V) coordinate system, our connection point's coordinates are (1,.25,4,.25). I contend that this coordinate system can describe any point within a polygon or polybezier, and in a way that brings meaningful behaviour to the end-user if we can choose i and j wisely. I propose the following rules:
(I guess but won't bother to prove that a bezier is parallel to a segment of line if its control points are all on one line parallel to the segment). Once we have chosen I and J , all we need to do is to compute the U and V coordinates. For line segments, it's fairly easy, for bezier segments we'll probably need some solver capabilities, but it should remain reasonably possible. Let's see if this holds up. We'll deform our rectangle into a trapeze, then a parallelogram. First we pull a remote point, to turn this rectangle into a trapeze:
- I is chosen as the index of the segment closest to the considered point, disregarding zero-length segments.
- J is chosen as the index of the segment second closest to the considered point, disregarding zero-length segments and segments parallel to the i th segment.
![]()
A nice property is that since (1,.25,4,.25) does not depend on the affected segments, it remains still close to its corner (had we used the barycentric coordinate system (I , U , V) as sketched in the beginning of this section, this CP would have moved). Next we'll pull the second end of V, to turn this trapeze into a parallelogram:
![]()
The connection point does move, but I think it does so in a way easily predictible by the user. There is no doubt that this may produce some weird effects with beziergons, but it will already happily handle the case of polygons, rounded rectangles, etc.More specialised coordinate systems
UML Component: boolean coordinates!
The UML Component object has a slightly unusual behaviour.It can be seen, graphically, as two rectangles, one on top of another. The upper rectangle can't be resized (unless the text changes), while the lower rectangle can be dragged to any size. If we used a simple (U , V) system as if it was just a rectangle, we'd probably see the brown connection point follow resizes in a satisfactory way, but the blue connection point would move too, which we don't want. A possible solution would be a (T , U , V) coordinate system, where the T axis is a boolean axis. The t coordinate can take two values, true meaning "this point is within the title area, and U and V are its coordinates (as if there was only the title rectangle), false meaning "this point" is within the main rectangle). (true , 0.5, 0.5) would describe a point in the centre of the title area, which wouldn't move if the object was resized, while (false, 0.5, 0.5) would always be in the middle of the main area.
In practice
I wrote a sort of an implementation plan in VirtualConnectionPoints; I should probably move it here. -- CyrilleChepelov - 25 Mar 2004
| Edit -:- Attach -:- Ref-By -:- Printable -:- More |