maptool icon indicating copy to clipboard operation
maptool copied to clipboard

Decompose drawing, expose, and topology tools

Open kwvanderlinde opened this issue 4 months ago • 0 comments

Identify the Bug or Feature request

Supports #3691 Partially addresses #5004 (just for drawings) Fixes #4218

Description of the Change

This PR replaces the deep AbstractDrawingTool hierarchy strategy-based classes. The goal here is to consolidate each tool's logic so it takes fewer lines of code and isn't spread across several classes, while still reusing code. Future work for #3691 will build off this now that it is easier to change the rendering logic of each tool.

The drawing tools, FoW expose tools, and topology tools have all been refactored. There are now only three tool classes for all of them:

  • DrawingTool, for any drawing tool
  • ExposeTool, for any FoW expose tool
  • TopologyTool for any topology tool

The different shape are obtained by strategies that can be plugged into the above tools:

  • RectangleStrategy for drawing any type of rectangle
  • OvalStrategy for drawing any type of ellipse
  • IsoRectangleStrategy for drawing any type of isometric rectangle
  • PolyLineStrategy for drawing any type of polylines, polygons, and freehand lines.
  • CrossStrategy for drawing any type of X (only used for topology right now)

These strategies produce idealized shapes, which in practice are Rectangle and Path2D. They can be used for either hollow or filled shapes, which is why there is only one of each. It is up to the tool itself to decide whether to fill the shape, whether it needs a stroke, and what the final shape means. The strategies can also optionally provide measurements of their shape, which is how the drawing and expose tools now show those.

These new tools provide the user with more of a WYSIWYG experience: the temporary shape drawn by the tool will exactly match the final outcome. This effects a lot of the tools, but is especially noticable for the ellipse topology tool - it results in a decagon, and the tool now shows the user the exact decagon they will end up with, rather than showing them a larger idealized ellipse.

The implementation of the template tools have not been touched as they are quite different and would be a lot of extra effort. I plan to do something about them in a separate PR.

Supporting changes

Toolbox

The toolbox used to only work with tool classes, which forces the need for the deep tool hierarchies. In order to make it work for the new strategized tools, the toolbox was extended to allow adding Tool instances. The behaviour is much the same, except that it is not possible to look up an instance-based tool by its Class<?>.

Legacy drawings

These Drawable implementations were only used by the tools and therefore are no longer used:- Cross

  • Oval
  • Rectangle

They have all been marked @Deprecated to discourage any further use. Because they might have somehow been serialized in existing campaigns, they have been kept around, but reduced to just their fields and no behaviour. They all use readResolve() to replace themselves with a ShapeDrawable instead.

Protobuf

The set of shapes representable in protobuf was expanded slightly:

  • Path2D can now be sent in addition to Area. This was originally meant to be used in this change, though in the end it was not necessary. But I decided it was still worth keeping around since Path2D and Area share the same logic.
  • Any Ellipse2D and be sent, not just Ellipse2D.Float. On the other end, it will be mapped back to a Ellipse2D.Double.

The Ellipse2D had some knock-on effects. The trivial one is that we now use Ellipse2D.Double everywhere and never use Ellipse2D..Float anymore. Another issue is that we actually rely on our ellipses being backed by a class with the simple name "Float", as it is for in Ellipse2D.Float. Some of these examples were doing type checks this way and have been updated to use instanceof Ellipse2D instead. Other places genuinely needed a string from a ShapeDrawable, so there is now a ShapeDrawable.getShapeTypeName() that will return a name for any support type, with ellipses being given the name "Oval".

Possible Drawbacks

This is a big change, so there's a chance I missed something in testing that doesn't work quite how it used to.

Because of the slight differences in drawing, expose, and topolgy tool behaviour (see #5002), there is some logic duplication between these tools. It's more of a mess to try avoid this. If we ever implement #5002, the tools can be simplified quite a bit further.

Documentation Notes

N/A

Release Notes

  • Updated drawing, FoW, and topology tools to show the actual shape that will be produced.
  • Fixed a bug where the snap-to-grid and eraser toggle buttons for drawings were specific to each tool.

This change is Reviewable

kwvanderlinde avatar Oct 21 '24 05:10 kwvanderlinde