Part 1 of this article dealt with the idea of moveable/resizable graphics.

I wrote about contour presentation and explained the design of common and special types of contours, which allow you to apply them to the widest variety of objects. I used simple examples to illustrate the technique of involving these objects in moving/resizing. In part 2, I describe complicated cases of moveable/resizable graphics, e.g., engineering plotting, as well as objects involved in both forward moving and rotation. I also explain how you can apply the same technique to controls and how you can base customization of the forms on moveable/resizable objects.

Visualization of Contours

With the help of contours, you can make any object in a program moveable/resizable. To get the best results from these new features, users have to know the following:

  • Any object inside an application is moveable/resizable.
  • How to use these features.

The first problem is more psychological than technical. How can engineers and scientists imagine that everything in my application TuneableGraphics is moveable/resizable if they have never seen a single scientific program with moveable plotting? Lewis Carroll’s solution to the problem with information simply written on top-Eat me-is definitely the best for the user, but not for the programming world. In the world of applications, perhaps the best solution is to rely on human nature. So in the TuneableGraphics application, I put a small button in the top-left corner of each form; I hope that people will click this button, even if just out of curiosity, and then they will see the contours. From there, they will try to find out what these lines are for and everything else will become clear.

If users are familiar with the idea of moveable/resizable objects and are expecting all objects to have such features, then they will be looking for the most likely places for nodes and connections. Users may not even know these terms; they will be deciding where the most likely places are for moving and reconfiguring objects. The designers’ responsibility is to organize contours according to these expectations. Still, the easy way of switching contours ON/OFF is useful in nearly any situation, so perhaps it is not bad to have that tiny button in the corner.

The requirement for a good visualization of the contours in any possible situation is a problem in itself. For solving it, I set some default parameters, which will give good results in a majority of situations, and added some methods for changing those parameters.

Contours consist of nodes and their connections. By default:

  • A node is a small square with the side equal to 6 pixels.
  • A contour is shown in red; this will be the color of connections and borders for all the nodes.
  • The inner areas of all nodes are filled in white.

What were the ideas behind these default parameters? Contours are very helpful, but they are instruments; instruments must be obvious, but they can’t be the main part of the view. Six pixels square is small enough not to disturb any image, but big enough to grab easily with the mouse. Big images are rarely drawn in red, so this color will be visible in the majority of situations. Filling the inner part of the node with white plus the red border of this tiny area informs the user about this additional but artificial thing. Contours do their work perfectly, but the programmer must give users an easy instrument (one click) to switch the contours ON and OFF.

You can change all the default contour’s settings. I have already mentioned in part 1 how to change the shape of the node from a square to a circle. If you don’t want to fill the inside of the node with white, you have to switch the node’s flag into false:

ContourApex [] ca = new ContourApex [1];
ca [0] = new ContourApex (0, …);
ca [0] .SenseAreaClearance = false;

There is also an easy way to change the color of the contour, but I’ll show you how to do it later. For now, I want to discuss the code for painting contours.

The decision about what to draw and in which order is usually done inside the Paint event of the form. Here is the code from one form in the TuneableGraphics application, which shows a very interesting graphical object-Skyscrapers. You can also see the picture from this form (Figure 1), which will make the explanation much more obvious:

Figure 1:  Skyscrapers, coordinate system and its contour, switched ON.
Figure 1: Skyscrapers, coordinate system and its contour, switched ON.
private void OnPaint (object sender,
                      PaintEventArgs e)
{
  Graphics grfx = e .Graphics;
  if (bShowAxes)
  {
    xyzCoor .Draw (grfx);
    // coordinates
  }
  skyscr .Draw (grfx, xyzCoor);
  // Skyscrapers
  if (bShowContours)
  {
    Movers [0] .DrawContour (grfx);
    // contour
  }
}

There are two major graphical objects in this view. Skyscrapers, which are used for financial analysis, do not belong to moveable and resizable graphical types, about which I am writing here; the object of this class simply resides under the jurisdiction of XYZcoordinates and is doing whatever it is told to do by the last one. Class XYZcoordinates is included into MoveGraphLibrary.dll and described among other classes in MoveGraphLibrary_Classes.doc, so you can write your own classes and receive all the benefits of XYZcoordinates by using them together. XYZcoordinates is a class of moveable/resizable objects:

public class XYZcoordinates : 
             GraphicalObject 

As a class derived from GraphicalObject, the XYZcoordinates object has all the features described before: you can see its contour on the picture, the contour of coordinate system copies the axes, and the nodes are at the ends of the axes and in the crossing of coordinates. With the three nodes at the ends of axes, you can change all the sizes of this picture. By the nature of this plotting, the towers in front will often block from view the towers behind; by moving around the single node at the crossing of axes, you can find the best view for any data set. Excellent, but that is where the problem can occur.

Contour belongs to XYZcoordinates, but if it was automatically shown together with the object to which it belongs (in this case xyzCoor), then the picture of Skyscrapers will block it from view and it would be difficult to locate that very useful node. It is still working in the same way: either it is in view or not and the changing mouse cursor will signal you that you have at last located it. You can press the mouse button and rotate the whole view, but it will still be a bit tricky to locate this tiny node behind the towers.

This is a very important feature of showing contours: as a programmer you have to not only decide about the queue of any objects you want to draw in your form, but you also have to decide about the order of contours in the same queue. More often than not, the contours will not be next to their “parent” objects in this queue. Even more: you can exclude the “parent” objects from drawing. By switching OFF one parameter in the Skyscrapers’ tuning form, you can take axes out of the picture; this will not change anything else. However, you still have to decide when it would be best to show contours. So in the code for drawing the Skyscrapers picture, you can see that I first drew the coordinate system, next Skyscrapers, and then the contour. Though the contour here belongs to xyzCoor (an object, derived from GraphicalObject), you can see from the last piece of code that the object responsible for drawing the contour is not xyzCoor, but Mover. At first it may look very strange, but here is an explanation for this situation.

Any object derived from GraphicalObject has a contour, but this object may or may not be included in the list of moveable objects (Mover’s list). It is very common to have both moveable and unmovable objects in your form. For example, you can put small objects of moveable types (classes) somewhere at the side of the form, but these samples will be unmovable. By clicking any of these samples, you start the process of adding an identical moveable object, but the sample itself will stay in the same position all the time. These unmovable samples will have contours, as do all objects of their class, but since they are unmovable there is no sense in painting their contours. Mover has the list of all moveable objects on the form (of those that were included into its list); the object itself doesn’t know whether it was included into the list of moveable elements. Mover is the only one that knows about each and every contour that must be painted.

There can be a lot of moveable objects in your form and different situations when you want to show all the contours or only some of them. To show all contours use:

Movers .DrawContours (grfx);

To show a particular contour use:

Movers [i] .DrawContour (grfx);

A very common situation is when you want to show the contours of the moveable controls (I’ll write about this possibility later), but not to show the contours of the graphical objects. In this case use:

for (int i = 0; i < Movers .Count; i++)
{
    if (Movers [i] .Source ==
                    Source .Control)
    {
        Movers [i] .DrawContour (grfx);
    }
}

Your form can have a lot of different moveable objects. For some objects you don't need to show contours at all, as users will know that they can grab al such objects for movement and rotation at any point. You can decide about visualizing the contour or not by checking the type of the graphical object.

for (int i = 0; i < Movers .Count; i++)
{
    if (Movers [i] .SourceGraphical
                    is TextMR))
    {
        Movers [i] .DrawContour (grfx);
    }
}

If you switch ON the contours in the main form of the Test_MoveGraphLibrary application or play with different interesting forms of the TuneableGraphics application, you will see that all contours are shown in one color. You can assign each contour its own color; however, because the object delegates its painting to Mover, all contours are shown in the same color. You can change the color for all contours or for any particular contour by using one of Mover’s methods:

Movers .Color = Color .Magenta;
Movers [i] .Color = Color .Blue;

The Colored Doughnuts

Up to now, I’ve tried to illustrate everything with very pure samples that I especially designed to explain the whole mechanism as simply as possible step by step. (Skyscrapers class is an exception, but it came from another application.) In reality you may like to add contours to more complicated objects and use them with both forward movement and rotation, so let’s look at a more complicated sample. In one of the descriptions, I saw a picture of several coaxial rings colored by sectors. I assume that this type of plotting is used somewhere in finance; however, for me it became another exercise to check whether I have missed anything in the design of moveable/resizable graphics. You’ll find the description for class DoughnutSet in the file DoughnutSet.cs; Figure 2 and all code samples for working with this class are from Form_DoughnutSet.cs.

Figure 2:  A DoughnutSet object.
Figure 2: A DoughnutSet object.

Each ring consists of the number of sectors (minimum 2). To distinguish the sectors, each has its own color. You can show values as numbers or as percents and you can change the colors of sectors, text for each sector, all borders, and the font to show the text. You can change all viewing parameters; however, this ability is outside the scope of this article. You are free to add these features, but for the purposes of this application, the parameters are mostly selected at random:

public class DoughnutSet : 
             GraphicalObject
{
  Point ptCenter;
  List<DoughnutRing> rings =
      new List<DoughnutRing> ();
  Title title;
  ….

Nodes are located in the points where the sectors’ borders cross with the inner or outer border of each ring (nodes are visible on a picture). Pairs of nodes on each border are connected to each other, but there are no connections between the pairs, so contour consists of an unlimited number of disjoint sets. (You can see the best illustration of this strange contour in the main form of the application. Switch ON the contours by the small button in the corner and move the picture of the DoughnutSet object to the same location where you can see Y(x) plotting. The picture of DoughnutSet will be blocked from view by that plotting, but its contour will be in perfect view above it.)

Press the left mouse button on any node of DoughnutSet will allow moving it along a radius, thus changing the width of the ring. Press the right mouse button on any node regardless of whether it belongs to an inner or outer circle of the ring can start the rotation of this ring. Grabbing any connection (left button press) will move the whole set of rings. There are several limitations on the personal movements of nodes (minimum radius for the inner border of the smallest ring, minimum distance between rings, minimum width of the ring), which will certainly affect the MoveContourPoint() method.

On the form there is a panel (moveable!) with several controls, which will allow you to add or delete rings, change the number of sectors in any ring, and renew the values for any ring.

DefineContour() method is relatively simple (Listing 1), but a bit longer than any piece of code I was demonstrating before; however, these remarks should help you to understand its code:

  • Nodes are calculated beginning from the inner ring.
  • Each new pair of nodes represents the line on the border of two sectors.
  • All even nodes are on the inner borders of the rings; odd nodes are on the outer borders.

Moving of the whole object (Move() method) means moving the centers of all the rings and the title:

public override void Move 
                     (int cx, int cy)
{
    Size size = new Size (cx, cy);
    ptCenter += size;
    foreach (DoughnutRing ring
                          in rings)
    {
        ring .Center = ptCenter;
    }
    title .Move (cx, cy);
}

MoveContourPoint() for DoughnutSet class is definitely more complicated than anything you were looking at before; for forward movement of any node I have to take into consideration all the previously mentioned limitations. By forward movement of the node I mean the movement along its radius; this will change the width of the ring. The code for forward movement is too long to include it here, but I will explain the code for you. For the individual movement of any node I have to:

  • Calculate three List<> containing: the number of sectors in each DoughnutRing, their inner radii, and outer radii.
  • Check which ring and which side of the ring this node belongs to.
  • Calculate the new radius to which this node tries to move according to the mouse movement.
  • Check if the node can really move to this radius according to all the limitations; for inner nodes of the rings there is a special situation for the inner ring and for outer borders there is a special situation for the biggest ring.

For rotation, the code is much shorter as all the rings are working under the same rule. First I calculate the angle of the current mouse position (first in radians, and then transfer it into degrees). If this angle differs from the starting degree of the corresponding sector, it changes the starting angle of the ring and you must redefine the whole contour:

else if (catcher == MouseButtons 
                    .Right)
{ 
                   // Rotation
  double angleNew_Radian = -Math .Atan2
                (ptM .Y - ptCenter .Y,
                 ptM .X - ptCenter .X);
  double angleNew_Deg =
                angleNew_Radian * 
                     180 / Math .PI;
  if (Math .Abs (angleNew_Deg -
              fStart_Deg 
              [iSectorInRing]) &gt; 1.0) {
    int nAddAngle = Convert.ToInt32 
    (angleNew_Deg - fStart_Deg 
    [iSectorInRing]);
    rings [jRing] .StartingAngle =
                      Auxi_Common 
                      .ChangedAngle (
         rings [jRing] .StartingAngle, 
         nAddAngle);
    DefineContour ();
  }
}

I mentioned before that for rotation I found it more accurate to base calculations on the mouse position rather than shifts; you can see from this code that I am using the mouse position ptM. In the longer part of the same method for forward movement, I base the calculations on shifts cx and cy.

If you suspect that you will have to write more complicated code for these three mouse events for this really complicated object, you will be disappointed. On the contrary, the code cannot be simpler than here; all the complexity was accomplished in the DefineContour() and two movement methods:

private void OnMouseDown 
             (object sender,
                     MouseEventArgs mea)
{
  Movers .CatchMover 
  (mea .Location, mea .Button);
}
private void OnMouseUp (object sender,
                     MouseEventArgs mea)
{
  Movers .ReleaseMover ();
}
private void OnMouseMove (object sender,
                     MouseEventArgs mea)
{
  Movers .MovingMover (mea .Location);
  if (Movers .MoverCaught)
  {
    Invalidate ();
  }
}

Pay extra attention to the OnMouseDown() method. Because you are working with both types of movements, you have to pass the pressed button as a second parameter to Movers.CatchMover().

Y(x) Plots and More

I wrote at the beginning that the huge demand for moveable/resizable graphics in the areas of scientific and engineering applications triggered this work. The results turned out to be extremely interesting for areas absolutely different from the original. However, for this particular area the new results are not simply an improvement, they are a revolutionary design for the most complicated applications.

The conflict between the widest variety of constantly changing users’ demands and the fixed design of engineering/scientific applications is obvious to both sides. In attempts to soften this conflict, programmers are looking for improvements in interface design, and there are huge achievements in this area. But all these achievements are accomplished within the misguided idea that the programmer can design something that would be best for each and every user. You can (and certainly must) give to the users what you think is best, but also give them the instruments to easily construct their own dream environment-that would be the most effective application for their work. That is the main idea of moveable/resizable graphics; the area of scientific and engineering applications may be one of the best to show the real difference in results.

In the Test_MoveGraphLibrary application, there are three forms that demonstrate different levels of adding new features to standard plotting. These three forms are available via submenu positions under Y(x) plots. All these forms are using the FullGrArea class, which was designed for the plotting of Y(x) functions and parametric functions; this class is included in MoveGraphLibrary.dll. You can find the detailed description of the FullGrArea class together with the tuning forms, which are also provided, in the file MoveGraphLibrary_Graphics.doc. Now let’s look at the different steps of turning unmovable standard plots into fully moveable and resizable.

The first form in this group is Form_UnmovablePlots.cs with two standard unmovable graphical objects. You can easily turn these two objects of the FullGrArea class into moveable/resizable, as any object of FullGrArea class has these features. Because these two objects were not added to the Mover list, they are not currently moveable/resizable; they can’t move by themselves, somebody has to look after them. Interestingly, there is a Mover in this form and it is working: the colored information panel in the form is moveable. If you want to eliminate all movements of this panel in order to receive the ordinary form with all fixed elements, you can do it easily: comment whatever is dealing with Movers. This includes the OnMouseDown() and OnMouseUp() methods and the second part of the OnMouseMove() method. The first part of the last method changes the mouse cursor to indicate the possible left-button double-click for opening of the tuning forms. This tuning mechanism is working regardless of whether the FullGrArea object is really moveable at the moment; you can see that the variable Movers is not mentioned in the OnMouseDoubleClick() method.

But if you want to transfer the unmovable (standard) plots into moveable, all you have to do is to include these plots into the Movers’ list. Form_OneMovablePlot.cs, the second form of this group, demonstrates this by adding one line of code:

Movers .Add (area);

Everything else in this form is absolutely the same, as in a previous one (well, one plot instead of two). It is nice to have moveable, resizable, and tuneable plotting, but the question of what to show here is still decided by the designer; I decided to show one plot, and it is impossible to see anything else without rewriting the code. This form is a demonstration of how you can quickly change the plotting in any engineering or scientific application from unmovable into moveable, but without changing the main design ideas.

The real power of moveable/resizable graphics for the area of scientific and engineering applications will be unleashed when users will have a chance to decide what, when, and how to show. This is the case of Form_MovablePlots.cs.

For any other form of the Test_MoveGraphLibrary application, I can easily show the picture and write that “this is how it will look”. Here you can organize any number of different plots; all of them are moveable and resizable, so I can’t predict how this form will look when you start playing with the functions. But as a designer, I have to provide the instrument with which users can organize and analyze different functions. In this application there is a small limitation, that users can select the functions only from a predefined set; in the main form of the TuneableGraphics application, the same exact instrument is realized without this limitation-there the user can explore any number of arbitrary functions.

That’s the main idea of applying the new moveable and resizable graphics to the area of scientific and engineering applications: the new technique gives a chance to turn the powerful calculators (our day programs) into real research instruments.

From a programming point of view, the implementation of moveable/resizable plotting for an unlimited number of areas only slightly differs from the previous case of a single plotting area. Here you have the same single Mover and the same simple methods for the MouseDown, MouseUp, and MouseMove events. To organize these without any problems (and mistakes) in moving, visualization, and tuning, I had to develop some reliable link between the function and the area where it is shown. This is done with the help of the additional class PlotOnScreen:

public class PlotOnScreen
{
  DemoFuncType funcType;
  FullGrArea grArea;

Functions, which you would decide to show on the screen, are organized into the List:

List&lt;PlotOnScreen&gt; plotInView =
              new List&lt;PlotOnScreen&gt; ();

All possible operations-adding functions, painting, moving, tuning, changing the order, deleting-are done only with the objects from this List, so there will be no discrepancies between the real function and its view.

There are two plots and two panels in Figure 3; all the plots are certainly moveable and resizable. Panels are also moveable, but only the one with the list of functions’ information is resizable; you can see the difference in the contours of the two panels.

Figure 3:  Moveable plots and panels with their visualized contours (form Form_MovablePlots.cs).
Figure 3: Moveable plots and panels with their visualized contours (form Form_MovablePlots.cs).

I have a lot of experience in the design of very complicated scientific and engineering applications and I understand all too well that converting these applications from designer-driven into really user-driven applications is not a five-minute job. The simple change of graphics, which will turn all plots into moveable/resizable, without any other changes in the design of the application is something you can do really quickly. It is the development of programs inside the new paradigm of user-driven applications that would require some thought by the designers. But the users’ benefits from the new applications will be enormous.

In the Test_MoveGraphLibrary application, under the same menu position, there are two special cases of Y(x) plotting; they have such special features that each one visually represents its own subgroup.

The first case is for the simultaneous plotting in the same area of absolutely different functions that have to use different scales. This is not a rare situation; for some complex analysis you have to compare different processes to look for similar behavior or abnormalities. You can certainly put two plots side by side for visual comparison, but this requires a lot of extra screen space; it would be much better to put them into the same graphical area. You can do this by using the previously mentioned FullGrArea class, as its methods of drawing Y(x) functions can use border values passed as additional parameters. Those methods only partly solve the problem as the user can change the visible scales but can do nothing with these additional parameters. So the better solution would be to use the MultiScaleGrArea class, which allows you to organize the same graphical area with an unlimited number of scales.

The behavior and tuning forms of the MultiScaleGrArea class are made as close as possible to the FullGrArea class, but there is an addition in contour. Each scale is represented in the contour by an additional segment with two nodes at the ends of its main line; by using these nodes, the user can put each scale in any appropriate position in relation to the main plotting area. Because each scale has its own tuning form, the user is now in full control of all the plotting details.

The second case is special not only for Y(x) plotting, but for the whole application. The Profile class perfectly fits the idea of contours presentation; however, it is a unique class within MoveGraphLibrary.dll: it is the only class of resizable, but not moveable objects. This object is not moveable separately (and this was done on purpose), but it can be moved along with the FullGrArea class, on which it can reside. Two different cases of using Profile are demonstrated in Form_Profiles.cs: in the top-left corner you can see the Profile object inside the Rectangle; two other Profile objects reside on the FullGrArea object, which is the subject of all standard moving/resizing transformations. Figure 4 shows the view of this form; the samples of code for an explanation of the Profile class are also from the Form_Profiles.cs file.

Figure 4:  Objects of resizable, but not moveable class Profile (form Form_Profiles.cs).
Figure 4: Objects of resizable, but not moveable class Profile (form Form_Profiles.cs).

The Profile object is a set of unconnected nodes; you can easily take out the lines that you see on the picture by using Pens.Transparent, and then you’ll have a set of isolated nodes-exactly what the Profile in reality is. Two end nodes are always located on the left and right borders of the rectangle and you can only move them up or down; you can move all intermediate nodes freely between upper and lower boundaries, but cannot move them farther to the sides than the neighboring nodes. Each node is characterized by its location (Point) and two double values from inside the ranges, defined by the borders’ values. You can initialize the Profile object either by two arrays of double values or by an array of points (Point []):

double [] xs = new double [9] { … };
double [] ys = new double [9] { … };
profile [1] = new Profile
              (rc, area.ValuesOnBorders,
               xs, ys, ContourApexForm 
               .Circle, 3);
pts = new Point [] { … };
profile [2] = new Profile
              (rc, area.ValuesOnBorders,
               pts, ContourApexForm
               .Square, 3);

As in all other cases, you organize the moving of the nodes with MouseDown, MouseMove, and MouseUp events in the same simple way. However, there are several additional lines of code in the OnMouseDown() method as the Profile object has one more unique aspect: for all other moveable/resizable objects the contour is organized at the moment of initialization and the user can only transform it later; with the Profile object the user can add intermediate nodes at any time or delete them. Each time the contour must be redesigned, which is done automatically inside a couple of methods:

private void OnMouseDown
            (object sender,
             MouseEventArgs e) 
{
  if (mea .Button ==
           MouseButtons .Left) {
    for (int i = 0;
         i &lt; profile .Length; i++) {
      int iDot = profile [i].
          InsideDot(e.Location);
      if (iDot &lt; 0) {
        iDot =profile [i].
        InsertNewDot(e.Location);
      }
      if (iDot &gt;= 0) {
        profile [i] 
        .Selected = iDot;
        break;
      }
    }
    Movers .CatchMover
    (e .Location);
  }

If the user has not pressed the Left mouse button inside any node, there is an additional check to see if it is close enough to any segment between the consecutive nodes to insert the new node there. If Yes, the application adds the new node, which is on the move as the user has already pressed the mouse button.

There are also several additional lines of code inside the OnMouseMove() method. If the Profile object resides on the FullGrArea object, the profile’s transformation depends on whether the host area was moved or resized. In the first case, the Profile is moved exactly in the same way as the host area; in the second case, the profile’s area is changed to the new area of the FullGrArea object.

Another Branch of Evolution

Throughout all the previous explanation, I tried my best to show that in order to turn the existing class of objects into moveable and resizable or in order to design the new class with these features, you must derive this class from GraphicalObject and override three methods of this abstract class. Now I am going to show that there can be another way with the same excellent results; look at this way as another branch of evolution.

C# does not support multiple inheritance of classes; in case you really need it, there are interfaces that will help you to go around this limitation. But if you have spent a lot of time on the design of your system of classes and the system is working perfectly in your applications, then the requirement of mandatory inheritance from GraphicalObject in order to implement moveable/resizable graphics will not inspire you at all. Here is the way to help you in such a case.

In the TuneableGraphics application, there is one form, which you can fill with an unlimited number of colored chatoyant polygons (Figure 5). You can move them around and reconfigure them in any possible way; you can switch ON their contours and see that they consist of standard nodes and connections. On initialization, each object is a regular polygon; nodes are in the apexes and in the center point. All nodes, including the center point, can be moved anywhere, producing very strange figures. Rotation is always around the center point, but for rotation you can press the right mouse button anywhere inside the figure (and not only on other nodes). The polygons look and behave like all other moveable and resizable objects that I have been writing about. “If it looks like a duck, quacks like a duck, and flies like a duck, then it must be a duck.” Usually this quote is right, but not in this case: ChatoyantPolygon is not derived from GraphicalObject:

Figure 5:  Chatoyant polygons.
Figure 5: Chatoyant polygons.
public class ChatoyantPolygon
{
  Graph graph;
  …
  public Graph GetGraph
  {
    get { return (graph); }
  }

To implement the whole moving/resizing algorithm, ChatoyantPolygon includes a field of the Graph class, which is derived from GraphicalObject:

public class Graph : GraphicalObject

The object of the Graph class is simply a set of points with the connections between them, so all three mandatory methods for this class are very simple. Move() means moving every point of the set and MoveContourPoint() is at the same level of simplicity, as there are no restrictions from the neighbors; any point can be simply moved and that’s all.

The objects of the ChatoyantPolygon class look interesting and you can transform them into really complicated figures, but from the point of involving them in moving and resizing they differ from all previously mentioned graphical objects only in the direction of simplicity. A paradox? All previously mentioned graphical objects had some basic primitive forms (Rectangle, Point) and some sizes; combination of these values gave us an initial shape to which you added the contour. While organizing those classes I had to write in their Move() methods some of these very simple, but needed lines of code for movement of those primitive forms. In the MoveContourPoint() methods, I had to take into consideration all possible restrictions from the neighboring nodes. In ChatoyantPolygon, there is simply nothing but Graph, so after writing the code for this class (and I mentioned that it is extremely primitive), I don’t need to add anything to ChatoyantPolygon.

The initialization of ChatoyantPolygon includes the initialization of a graph, thus calling its method DefineContour(). Class ChatoyantPolygon has the property GetGraph, which is returning this designed graph; Mover will be dealing with the contour of this graph.

Let’s say that you initialized in your program an object of the ChatoyantPolygon class:

ChatoyantPolygon poly=
         new ChatoyantPolygon (…);

You can’t register this object as moveable/resizable and add it to the Mover’s list, because the object itself is not derived from GraphicalObject, but you can try to use its graph:

Movers .Add
  (new MovableObject (poly .GetGraph));

The same three mouse events will be used and in absolutely the same way as with all other objects; there is only a minor addition for the case of rotation. On starting the rotation (and it can be started by a right button press at any inner point of the object), I remember the initial angle of the mouse and of all the nodes:

private void OnMouseDown 
            (object sender,
             MouseEventArgs e) {
  …
  else if (e .Button ==
           MouseButtons .Right) {
    iPoly = ClickedPolygon (e .Location);
    if (iPoly &gt;= 0) {
      Graph graph = 
            Polygons [iPoly] .GetGraph;
      Point ptC = graph .Center;
      Point [] ptAp = graph .LoopPoints;
      …
      fRotationStart_Radian = 
      -Math .Atan2 (e.Y - ptC.Y, e.X - ptC.X);
      for (int k = 0; k &lt; nApexOnMove; k++) {
        fApexRadius [k] =
           Auxi_Geometry .Distance
           (ptC, ptAp[k]);
        fApexStart_Rad [k] = 
        -Math .Atan2 (
        ptAp[k].Y - ptC.Y, ptAp[k].X - ptC.X);
      }

For any new mouse position, I know the change of its angle, use it to adjust the angles of all nodes, and then the whole polygon of any shape is rotating with the mouse. There is a catch: the Graph class has such a primitive MoveContourPoint() method that there is absolutely nothing for the case of rotation. So in this case, when the polygon is involved in rotation (Listing 2) and all nodes are adjusted to its turn, I get the new (correct!) graph from a rotated polygon and substitute the existing element in the Mover’s list with the new one:

Movers .RemoveAt (iPoly);
Movers .Insert (iPoly,
              new MovableObject
             (poly .GetGraph));

I can’t say that the classical way of organizing moveable/resizable graphical objects by deriving them from GraphicalObject is better or worse than this one. When I am designing the new classes, I use the classical way, but I don’t see any negative sides in this one also.

And Controls Too

All the code samples that I showed before and all the explanations were about the design of moveable/resizable graphical objects and working with such objects. But a lot of applications are based on the coexistence of graphical objects and controls. If the mechanism of making graphics alive will simply ignore controls, then the developed algorithm will lose a significant part of its value. I started the whole design under the idea of eliminating the difference between graphical objects and controls (that are moveable and resizable by default), so everything was designed using the main idea that it has to work identically with both types of elements. The designed mechanism of turning graphical objects into moveable and resizable can be easily applied to controls; you can see the moveable panels in nearly every form of the Test_MoveGraphLibrary application. However, there is a small difference in organizing contours for graphical objects and controls.

You organize the contour for a graphical object at the moment of the object’s initialization in the DefineContour() method. You must also develop two other methods for each class of graphical objects, which are going to be moveable and resizable:

  • Move() describes the movement of an object as a whole.
  • MoveContourPoint() describes separate movements of each node.

All three methods are used by Mover from the moment you include the graphical object into Mover’s list.

Controls do not have such methods and you don’t even need to think about them, but you can include controls in the list of moveable objects:

Mover .Add (Control control);

At this moment the Mover itself will organize the contour for this control. The contour will be slightly out of the control’s boundaries; all nodes of this contour will be designed with the parameter MovementFreedom.None, so the contour will be nonresizable. In a lot of situations, the thing that is really needed is to be able to move the control around the form; resizing is not required, so this variant of the method will be enough. But any control is resizable by default. In case you need to use this helpful feature, there is another way to organize the control’s contour:

Mover .Add (Control control, 
           ContourResize resize,
           int minW,
           int maxW,
           int minH,
           int maxH)

The second parameter defines the type of contour you want to link with the control. I hope that the names of the constants in this enumerator list are clearly explaining each case:

enum ContourResize { None, NS, WE, Any };

The last four parameters for organizing a control’s resizable contour define the ranges of possible changes in width and height of the control. There are also variants of the Mover.Add() method with an additional parameter defining the distance between the control’s borders and the contour around it.

All standard controls are rectangular and they will receive the contour slightly out of their borders. In the case of a nonresizable contour, it will simply be a rectangle with no visible nodes; all information panels in the application show this type of contour. If a contour is only resizable in one direction, there will be two small nodes in the middle of the two opposite sides. In case of a fully resizable contour, there will be eight standard nodes: four in the corners and the other four in the middle of the sides.

You can turn controls into resizable individually; I prefer to add a contour to a panel or GroupBox, thus giving one contour to a group of controls that I always want to keep together. You can easily organize the relocation of controls on the panel through the standard ClientSizeChange event for the panel; you can see an example of this panel and its associated code in Form_MovablePlots.cs. The possibility of moving groups of controls around the screen is so valuable that I use it widely.

Forms’ Customization

At first, including controls in the same mechanism that you use to turn graphical objects into moveable and resizable doesn’t look so significant: you already have moveable and resizable graphics, now you have controls with the same features. Implementation of these features will give users much wider flexibility in their work. You can see an excellent example of using these features in their pure form in Form_MovablePlots.cs, which I discussed above (see Y(x) Plots and More). If you take a complicated form with a lot of different controls and graphical objects and then make the main parts of this form moveable and resizable, you’ll see that quantity is really turning into quality. Implementation of this technique for all objects of the forms without any exceptions allows each user to have an unparalleled level of dialog customization. What is also very interesting is the idea that moveable/resizable graphics turned out to be of exceptional value for forms that may not include any graphics but consist of only (or mainly) a large number of different controls.

There is a huge demand for customization of forms for users’ purposes. There are tons of papers about the ideas in this area, but mostly there are two major directions for organizing this customization:

  • By using standard anchoring/docking features of all forms.
  • Via a context menu or by using several additional controls to allow users to set one of the predefined views of the form.

The first direction is called dynamic layout; in the last few years Microsoft has put huge effort into the development of dynamic layout tools for programmers and there are interesting results in this direction. The second direction is called by different names; programmers like to put new labels on already well known things (maybe because they are too busy to read books and prefer to reinvent the wheel); the most popular term recently is adaptive interface. The links between the two mentioned types of transformations and the two widely used terms are fairly tentative: dynamic layout is a subset of adaptive interface, but the two mentioned ways of forms’ transformations are using absolutely different techniques.

Implementation of moveable/resizable graphics shows the new way of forms’ transformation, but I want to emphasize that there is no epic battle of “Dynamic layout vs. Moveable graphics”; these two things are orthogonal. Dynamic layout is the programmer’s instrument to make forms look good under the changing conditions, but inside the dome of dynamic layout all transformations are predetermined; they are as good as the level of the programmer. Moveable/resizable graphics, if implemented by the programmer, become the user’s instrument. From this moment, each user determines what and how he wants to see. At the beginning of this article, I wrote about the Skyscrapers object (Figure 1); to illustrate the new ideas in forms’ customization, let’s look at the tuning form of this object.

The Skyscrapers’ tuning form (Figure 6) is as good as I could do it to make the tuning of any parameter easy. However, if the user has already set most parameters and is only going to change colors, and then there are a significant number of different colors, he will have to enlarge this form-thus covering a significant part of the screen. At the same time he is going to use only 15-20 percent of the form’s area (middle part of the original form), so to use the screen space in this way would be very inefficient. If the user only spends 2-3 seconds in moving and resizing the parts of this form, he can design an absolutely different view (Figure 7).

Figure 6:  The default view of a tuning form for the Skyscrapers object.
Figure 6: The default view of a tuning form for the Skyscrapers object.
Figure 7:  Customized tuning form for the Skyscrapers object.
Figure 7: Customized tuning form for the Skyscrapers object.

I want to emphasize that:

  • The two figures are showing the same form.
  • The second view is not one of predefined variants, designed by the programmer and saved somewhere.

There is no list of predefined variants here: there is one basic view (Figure 6), which the user can restore at any moment by clicking the button in the top-left corner. But there is also an easy way (press-move-resize) to change the whole form to whatever the user needs.

From the programmer’s point of view, to implement this customization there is nothing additional (new) to the methods that were already described. You use the same three mouse events and one Mover object that deals with the mixture of graphical objects and controls. The designer has only to decide what level of flexibility he is going to organize. I thought that it would be better to keep together several controls in this form, so I put them under the control of one moveable, but not resizable, frame (it was a brilliant idea of somebody centuries ago to sell shoes always in pairs). But I don’t want to put any more restrictions on the user’s work: the more chances for the user to customize the form means the user can be much more effective.

There are different ways to customize a form and the variety of applications is huge. There is no silver bullet, but using moveable/resizable graphics and applying this technique to controls and groups of controls may be extremely helpful in a lot of applications-especially in the most complicated forms.

Any New Problems from the New Features?

I have mentioned several times that adding new features to the existing graphical objects can be the cause of some conflicts; they are the same kind of problems that occur in adding new buildings into a populated urban area. The areas of our applications are well populated; you have a limited number of available mouse-generated events, each event is strongly (officially) linked with some expected reaction, and into this organized universe I am trying to add something absolutely new. Certainly, there will be conflicts.

I tried to eliminate or minimize these conflicts by careful design of contours and by shrinking the area of new commands to the proposed system of nodes and connections. However, there can be different ways of solving the conflicts between old and new. I want to mention here some of the rules (that I tried not to break) together with some thoughts for and against my decisions; maybe you’ll come to something better when you start using moveable/resizable graphics:

  • I use a left button press to start forward movement of the objects or to reconfigure them; I use a right button press to start rotation.
  • Use a left button press for forward movement of an object only in the sensitive area of any connection. Certainly, it’s not a problem to start this movement by pressing anywhere inside the object, but here is the first conflict: a left button press is widely used to change the order of objects and bring the touched one on top of all the others. I found it a very useful feature that in the current version of moveable graphics and with the implemented system of commands, I can grab and slightly move one plot somewhere underneath just to see its part without bringing it on top and destroying the whole view of the form. If the starting of forward movement will be transferred from the nodes’ area to the whole area of the object, then the object in motion will be automatically brought to the top position. That is exactly what Windows is doing now on the upper level, so it is one of the possible decisions, but still I think that possibility of moving some object on the lower layer without disturbing the whole hierarchy is very useful.
  • Rotation of the graphical object is usually started by a right button press inside the node. However, it is fairly easy to organize this start by pressing at any point inside the graphical object; for example, in the TuneableGraphics application, I use absolutely arbitrary polygons (not convex, but any) and the rotation of those polygons can be started at any point inside the object. Starting the rotation by a right button press anywhere inside the object would be much easier from the user’s point of view. The reason for not implementing this technique for PieChart, Ring and so on is simple-I am not sure that it would be correct. Usually the right-mouse click is used to pop up the context menu and I don’t like the idea of starting two different processes with the same mouse click (though with the mentioned polygons I am doing such a thing). And if I expand the starting of rotation to the whole object’s area and still restrict the starting of the forward movement to the area of connections, this will look really awkward.

I am introducing absolutely new moveable/resizable graphics. I think that this type of graphics will spread quickly enough (the advantages of such graphics is impossible to overestimate), but the development of applications for this kind of graphics will require some setting of commonly used rules for moving and rotating the objects. It will be much better for users if all the applications will implement movement/resizing based on the same system of commands.

Moving/Resizing Summary

Contours, described in part 1, make objects moveable/resizable. Here is the summary of how the whole moving/resizing process is organized:

  • To make any graphical object moveable/resizable, you must derive it from GraphicalObject.
  • You can include controls into moving/resizing as they are.
  • To organize a moving/resizing process, there must be an object of Mover class:
Mover Movers = new Mover ();
  • Mover will supervise the whole moving/resizing process, but only for graphical objects and controls that are included in its inner List<>:
Movers .Add (…);
Movers .Insert (…);
  • Moving/resizing is done with a mouse and you organize the whole process with three standard mouse events: MouseDown, MouseMove, and MouseUp.
  • MouseDown starts moving/resizing by grabbing either the whole object (moving) or a single node (resizing). The only mandatory line of code in this method is:
Movers .CatchMover (…);
  • MouseUp ends moving/resizing by releasing any object that could be involved in the process. The only mandatory line of code in this method is:
Movers .ReleaseMover (…);
  • MouseMove moves the whole object or a single node. There is one mandatory line of code in this method, but in order to see the movement you must call the Paint method:
Movers .MovingMover (mea .Location);
if (Movers .MoverCaught)
{
    Invalidate ();
}
  • If contours must be shown, then you must use one of the drawing methods, for example:
Movers .DrawContours (grfx);
  • If needed, you can use several Mover objects to organize the whole moving/resizing. Each Mover deals only with the objects from its own List<>.

Final Remarks and Some Ideas

Moveable/resizable graphics are a very powerful instrument. By adding these features to the objects inside your program, you are receiving on the inner level the same flexibility that the system of windows provides on the upper level. It’s not surprising at all that the result is very similar to what you have on the upper level. On the upper level all windows are included in the Z-order and the last one to be called is shown on top of all previous windows; when you click any window the system changes the Z-order and moves the touched one to the top. Now you have an instrument to do exactly the same at the inner level; the only difference is that the system is not going to do it somewhere behind the curtains; you, as a designer, are going to play this role, and I’ve given you all the instruments (methods) to do it easily.

The idea of moveable/resizable graphics is not simply new; it is so revolutionary that it’s difficult to put it into the basis of your design work in a week or a month. For a year and a half I have been using moveable/resizable graphics in a lot of applications and I still find myself from time to time in situations where I try to solve a problem in an ordinary way. Only later do I understand that with the help of moveable/resizable elements I could easily get a much better solution.

It’s very difficult to predict what moveable/resizable graphics can bring into most existing applications. Extrapolation is used as a one-way instrument. How about looking back and trying to imagine our PC world with the Windows system stripped of its flexibility? Can you imagine a version of Windows where the system (instead of you) decides what and where to show? That’s what you have now on the inner level. I am absolutely sure that after the idea of moveable/resizable graphics spreads, our modern-day fixed applications will look like DOS applications do to us today.

I designed the Test_MoveGraphLibrary application to show in detail this new technique of turning graphical objects (and controls) into moveable and resizable. To make things absolutely clear, I tried to keep all sample classes and all forms for demonstration as simple as possible-that’s why each form is working with only one particular moveable/resizable class of objects. However, in real applications you would often like to use different moveable objects together. To show that it is no problem to do so, I put into the Form_Main.cs form the objects of absolutely different types that are used for explanations in other parts of this application. It’s really strange to see in one form standard scientific plotting y(x), houses, sets of colored rings, and a couple of colored polygons. They have nothing in common except that their design is based on the same described algorithm and they can all live in the same space (form) without any conflicts.

The idea of easily turning all graphics into moveable/resizable is absolutely new. Though even the whole mechanism of design of new objects with such features was made very simple, and the work with such objects is even easier, still it is an absolutely new thing. For better understanding of what this thing will allow, look into the TuneableGraphics application, which includes a lot of different samples. The idea of that application was not to develop something extraordinary for one particular area; the main idea was to demonstrate the possibility of amazing results for absolutely different objects and areas.

Maybe the area that can benefit most of all from the using of moveable/resizable graphics will be “financial” graphics. Millions of people are analyzing financial data and the effectiveness of their analysis greatly depends on how close the view of all these numerous plots is to their expectation of the best and the most informative view. Nothing can be better than the system that allows everyone to look on the plots with the best view personally for him (or her). When you analyze a lot of information on the screen, then the best system is the one that allows you to resize and reorganize the whole view at any moment without stopping the application and going somewhere else for extra tuning.

The area of scientific and engineering applications, which triggered my work on moveable/resizable graphics, can gain much more than simple benefits from this new type of plotting. By using this new plotting, programmers can redesign the most complicated systems, turn them from designer-driven into user-driven applications, and bring the analysis of the most difficult engineering and scientific problems to another level. Instead of developing tiny details within several scenarios for calculations, programmers will have to design instruments, which will allow any possible scenario or variant of research work in one or another particular engineering/scientific area. I hope that, based on this new technique, the engineering/scientific applications can be turned more from being big powerful calculators into research instruments.

There can be other areas that will benefit from using these new moveable/resizable graphics. I designed a very powerful and flexible instrument; specialists in different areas will find the best way to use this new instrument for their applications.

There are several documents and programs that can help you in the understanding and using of moveable/resizable graphics. You can find all the related files at www.SourceForge.net in the project MoveableGraphics.

Acknowledgments

Many thanks to those with whom I was discussing for years the design of scientific plotting. Special thanks to Dr. Stanislav Shabunya from the Heat and Mass Transfer Institute. My work on tuneable graphics was triggered by our mutual project years ago and some significant improvements in clarity of this presentation were the results of his remarks during the fall of 2007.

You can read Design and Use of Moveable and Resizable Graphics, Part 1 by going to http://www.code-magazine.com/article.aspx?quickid=0803071 .

Listing 1: Defining the contour for DoughnutSet

public override void DefineContour ()
{
  int nSectorsTotal = 0;
  for (int i = 0; i &lt; rings .Count; i++)
  {
    nSectorsTotal += rings [i] .Sectors;
  }
  ContourApex [] ca = new ContourApex [nSectorsTotal * 2];
  int k = 0;
  for (int i = 0; i &lt; rings .Count; i++)
  {
    int radIn = rings [i] .InnerRadius;
    int radOut = rings [i] .OuterRadius;
    Rectangle rcIn = new Rectangle (ptCenter .X - radIn,
                       ptCenter .Y - radIn, 2 * radIn, 2 * radIn);
    Rectangle rcOut = new Rectangle (ptCenter .X - radOut,
                    ptCenter .Y - radOut, 2 * radOut, 2 * radOut);
    double [] fStartingAngles = rings [i] .StartingAngles;
    int nSectors = rings [i] .Sectors;
    for (int j = 0; j &lt; nSectors; j++)
    {
      float fStart = (float) (fStartingAngles [j]);
      Point pt0 = Auxi_Geometry .EllipsePoint (rcIn, fStart, 1);
      Point pt1 = Auxi_Geometry .EllipsePoint (rcOut, fStart, 1);
      ca [k] = new ContourApex (k, pt0, new Size (0, 0),
                             MovementFreedom .Any, Cursors .Hand);
      k++;
      ca [k] = new ContourApex (k, pt1, new Size (0, 0),
                             MovementFreedom .Any, Cursors .Hand);
      k++;
    }
  }
  ContourConnection [] cc = new ContourConnection [nSectorsTotal];
  for (int i = 0; i &lt; nSectorsTotal; i++)
  {
    cc [i] = new ContourConnection (i * 2, i * 2 + 1);
  }
  contour = new Contour (ca, cc);
}

Listing 2: Movement of a ChatoyantPolygon

private void OnMouseMove (object sender, MouseEventArgs mea)
{
    if (!bRotatePoly)
    {
        Cursor cursor = Cursors .Default;
        Movers .MovingMover (mea .Location);
        if (Movers .MoverCaught)
        {
            Invalidate ();
        }
    }
    else
    {
        // rotation - recalculate and redraw
        ChatoyantPolygon poly = Polygons [iPolyRotated];
        Point ptCenter = poly .GetGraph .Center;
        double fRot_Rad = -Math .Atan2 (mea .Y - ptCenter .Y,
                                        mea .X - ptCenter .X);
        // calculate each angle and each apex
        double dfApex = fRot_Rad - fRotationStart_Radian;
        double [] fApex_Rad = new double [nApexOnMove];
        Point [] pt = new Point [nApexOnMove];
        for (int k = 0; k &lt; nApexOnMove; k++)
        {
            fApex_Rad [k] = fApexStart_Rad [k] + dfApex;
            pt [k] = new Point (ptCenter .X + Convert .ToInt32 (
                      fApexRadius [k] * Math.Cos (fApex_Rad [k])),
                                ptCenter .Y - Convert .ToInt32 (
                      fApexRadius [k] * Math.Sin (fApex_Rad [k])));
            poly .SetApexPoint (k, pt [k]);
        }
        MovableObject mover = new MovableObject (poly .GetGraph);
        Movers .RemoveAt (iPolyRotated);
        Movers .Insert (iPolyRotated, mover);
        Invalidate (rcRedraw);
    }
}