The preceding analysis is summarized into two words: dependency. Our design begins with dependency. If you draw mutually dependent elements into a graph, this graph has two forms: one is a tree (like the expression parsing we made earlier) and the other is a network. Obviously, the relationship between each element in geometric plot is a network, so it is difficult for us to illustrate it by analogy with the organizational structure of a tree-like employee, or use the network of interpersonal relationships, for example, to be more appropriate.
Many people talk about network color changes, because there is a closed circuit in the network. Most of our network short circuits or program endless loops are thanks to him, so we should pay attention to this issue first. For example, if we have two free verbs A and B on the screen, and the line AB dependent on them, we can drag the free verbs A with the mouse, and the line AB will change accordingly. If we drag the line segment with the mouse, a will also move, and the movement of a will drive the change of the line AB, well, waiting for the braid to jump.
In order to prevent such a situation, we must strictly prohibit the mouse from dragging non-free elements, such as the line, circle, and intersection. In this way, the corresponding interface is born:
public interface IMovable { void Move(LogicalPoint newPosition); }
We only allow the elements that inherit imovable to be controlled by the mouse. Obviously, only freepoint, pointonline, and pointoncircle have such attributes. Then we can write the mouse event as OK:
this.MouseLeftButtonDown += (s, e) => { var item = this.HitTest<Ellipse>(e.GetPosition(null)); if (item != null && item.Tag != null && item.Tag is IMovable) { SeletedItem = item.Tag as IMovable; } };
With this in mind, we have basically implemented major bugs such as endless loops from the source, that is, we have avoided the problem of dependency being reversed, next, let's take a look at how to handle normal dependencies. Because they are all graphic elements in the coordinate system, we use the icoordinate interface to regulate it. First, we will analyze the common properties of each element:
- An associated coordinate system. Otherwise, the logical coordinates and screen coordinates cannot be converted.
- Updatevisual)
- Center)
- Related graphic interface elements such as (ellipse, line)
- Dependencies)
- Stacked order
In addition, there are other attributes such as color and visibility. We will not take this test for the moment. According to these rules, we define our classes. to simplify the code, we should first define an abstract base class, then, each specific class can be extended from the base class. In addition, because there are many types of vertices and most of their properties are the same, we define them as an abstract class pointshape, and the specific vertices inherit from it, the relationship becomes clearer:
Because each element has many commonalities, the functions are basically implemented in the coordinatebase class. The following describes how coordinatebase handles dependencies:
First, register dependencies with the parent element. For example, if line AB depends on a and B, add AB to the dependency list of A and B respectively:
public List<ICoordinate> Dependencies { get; set; } public void RegisterDependencies(params ICoordinate[] parents) { foreach (var parent in parents) { if (parent.Dependencies == null) parent.Dependencies = new List<ICoordinate>(); parent.Dependencies.Add(this); } }
Let's take a look at the use of dependencies. It only occurs in the updatevisual () function. Any subclass must call this method of the base class and can only be called at the end of their updatevisual, this ensures that the dependency can be processed in order:
public virtual void UpdateVisual() { if (!Center.Exists()) Shape.Opacity = 0.1; else if (visible) Shape.Opacity = Opacity; if (Dependencies != null) foreach (var dep in Dependencies) dep.UpdateVisual(); }
Let's take a look at the implementation of pointshape:
public abstract class PointShape : CoordinateBase { private const double Size = 20; public override FrameworkElement CreateShape() { return new Ellipse { Width = Size, Height = Size, Fill = Fill, Stroke = Brushes.Black, Tag = this }; } public override int ZIndex { get { return (int)MathEngine.ZIndex.FreePoint; } } public override void UpdateVisual() { Shape.CenterTo(Center.ToPhysical(CS).Coordinate); base.UpdateVisual(); } }
The code implementation of specific classes is quite simple. In fact, we basically implemented it during the analysis. Here we only made necessary refactoring and related performance optimization. During the refactoring process, I separated the code of each demo, so that the new demo does not overwrite the old version, for example, this demo is presented as an example of a triangle circle, which is placed in a separate static class:
// Example of triangle external circle public static class trianglecircumcircledemo {public static void play (coordinatesystem CS) {CS. children. clear (); // create three free points A, B, and C var A = cs. drawfreepoint (New logicalpoint (1, 2); var B = cs. drawfreepoint (New logicalpoint (-2, 3); var c = cs. drawfreepoint (New logicalpoint (0, 0); // create three sides of the triangle ABC var AB = cs. drawline (a, B); var BC = cs. drawline (B, c); var CA = cs. drawline (C, A); // create a vertical line var O1 = Cs in the AB edge. drawcircle (A, AB); var O2 = cs. drawcircle (B, AB); var d = cs. drawintersectionpoint (O1, O2, 1); var E = cs. drawintersectionpoint (O1, O2, 2); var de = cs. drawline (d, e); // var F = cs. drawintersectionpoint (AB, de); // the midpoint of the AB edge // create the vertical line var O3 = cs in the BC side. drawcircle (B, BC); var O4 = cs. drawcircle (C, BC); var G = cs. drawintersectionpoint (O3, O4, 1); var H = cs. drawintersectionpoint (O3, O4, 2); var GH = cs. drawline (G, H); // var I = cs. drawintersectionpoint (BC, GH); // BC edge midpoint var o = cs. drawintersectionpoint (De, GH); // vertical line intersection (Circle Center) var OA = cs. drawline (O, a); // the radius of the outer circle var circleabc = cs. drawcircle (O, OA); // Triangle ABC outer circle // var auxiliaryfigures = new coordinatebase [] {O1, O2, D, E, de, O3, O4, G, H, GH, OA}; foreach (VAR figure in auxiliaryfigures) figure. opacity = 0.2 ;}}
From the example, we can see that, except the three vertices A, B, and C are free points, all others rely on dependencies. I have some feelings from the outer circle of a triangle: in fact, freedom is relative, because A, B, and C are essentially free points, but they can never escape their own outer circle!
[Source code and demo address]
This is the principle of geometric plot. In addition to the external circle, you can also draw its incircle, subcircle, pseudo incircle, nine-point circle, and so on. In short, it can be self-bound. Haha! The theory is over. Next time, let's take a look at what is actually useful. How can we draw images interactively with the mouse instead of using code? interface programming is a bit challenging, and solutions will be provided in subsequent chapters!