PintaProject/Pinta — pull request #70 by AndrewDavis (2014-08-10 22:21:02+00:00)
Title: Extended Shape Capabilities

This is the summation of my (Andrew Davis's) GSoC (Google Summer of Code) 2014 work titled "Extended Shape Capabilities". Pull request made on 08-10-14 (August 10th, 2014).

Four tools were fundamentally revamped: the Line/Curve, Rectangle, Rounded Rectangle, and Ellipse Tools. I took my Line/Curve editability code from GSoC 2013, built upon it, and implemented cross-tool, cross-shape (re-)editability among these four tools. The bulk of the code is in a new BaseEditEngine class as well as its child EditEngine classes and the ShapeEngine class and its child classes.

If one wishes to gain a basic understanding of all of this code, they should start in the BaseEditEngine class. Each of the four tools has a BaseEditEngine child class in it. BaseEditEngine has a static ShapeEngineCollection called SEngines, which is where all of the ShapeEngines (shapes) are stored, regardless of the tool (hence the static). Then there is SelectedShapeIndex, which is a very, very important integer, as it is used to determine which shape is selected in the current tool (e.g. SelectedShapeEngine), as well as which shape is active (e.g. ActiveShapeEngine). The difference between these two can be seen by viewing the SelectedShapeEngine and ActiveShapeEngine properties in B.E.E.; that is, a particular point must also be selected (SelectedPointIndex) in order for a shape to be considered selected, whereas a shape could be active even if no particular point is selected but the shape itself is selected. As far as the user is concerned, having an "activated shape" is not even a concept, however it is very useful in some cases in the code, such as when a new shape is being created and no points are yet selected.

One of the most difficult problems I had to overcome (in a limited amount of time) was the fact that different shapes had to be drawn differently. The line/curve and rectangle shapes are drawn similarly, but both the ellipse and rounded line ("rectangle") shapes are drawn differently. This was solved primarily by implementing a ShapeTypes enumeration in BaseEditEngine and different GeneratePoints methods in the ShapeEngine child classes (which is where all of the fun/interesting code is and where I had to implement the new rounded line shape code, to account for shapes with greater or fewer than 4 control points). Additionally, some different toolbar options and shape properties/drawings (e.g. arrows and the rounded line radius) needed to be accounted for. This was solved primarily in the different EditEngine child classes.

It is critical to understand that for this very reason (different shape types/drawing/code), the best solution both my mentor Cameron and I could come up with for this particular scenario was also the first that came to my mind: switch tools whenever drawing or editing a shape of a different type than the current editable shape tool was meant to handle (in some cases switching temporarily and then switching back, and in other cases switching permanently). The ActivateCorrespondingTool and GetCorrespondingTool methods in B.E.E. are at the core of this process. I am well aware that a better solution would be to not require tool switching in order to edit or draw a shape of a different type, mostly for reasons of efficiency but also to prevent potential bugs and etc., however this is much more difficult than it sounds and I did not have sufficient time to revamp the code a second time (averaging about 97.68 lines of code changed per day!).

Five additional sections of code worth mentioning, especially for any developers intending to fix or improve this code, are: 1. The HandleBuildToolbar method in the EditEngines (especially BaseEditEngine - see shapeTypeButton.SelectedItemChanged). 2. The DrawAllShapes and finalizeAllShapes methods in B.E.E. 3. The two foreach loops in the Swap methods in ShapesModifyHistoryItem and ShapesHistoryItem in Pinta.Tools/HistoryItems, as well as the ReEditableLayer in Pinta.Core/Re-editable and its TryAddLayer and TryRemoveLayer methods (and its usage in Pinta.Core/UserLayer) - experience and testing says to trust that these are all working properly and any problems are somewhere else in the code. 4. The updateToolbarSettings, recallPreviousSettings, and storePreviousSettings methods in B.E.E., as well as their derivations in child classes. 5. The HandleMouseDown, HandleMouseMove, and HandleMouseUp methods in B.E.E. - especially how they interact with the drawing methods and when/where.

This should jumpstart any Pinta developer in an understanding of this editability system. My heavy commenting should also be a great guide. My email is andrew.3.1415@gmail.com