Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Separating Model and View/Controller in a drawing application

I am working on a vector drawing application (in java) and I am struggling with the the separation between my model classes and the view/controller classes.

Some background:

You can draw different shapes:
rectangles, lines and pie segments

There are 4 tools to manipulate the shapes on the canvas:
scale-tool, move-tool, rotate-tool and morph-tool

For this question the morph tool is the most interesting one: It lets you change a shape by dragging one of it's points and adjusting the other properties as shown in this graphic:

The different options of manipulating a shape

These Transformation rules are different for each shape and I think they are part of the model's business logic but in a way they need to be exposed to the view/controller (the tool classes) so they can apply the correct one.

Additionally the shapes are internally represented via different values: - The rectangle is stored as center, width, height, rotation - The line is stored as start and end point - The pie segment is stored as center, radius, angle1, angle2

I plan to add more shapes in the future, like stars, speech bubbles or arrows, each with their own control points.

I also plan to add more tools in the future, like rotating or scaling groups of shapes.

The control points for each tool are different. Eg when using the scale tool, you can not grab the center point, but each scaling control points needs to be associated with one pivot point (or multiple to let the user choose from).

For the simple shapes like rectangle, line and pie the control points are the same for each instance of the class but futures shapes like a bezier path or a star (with configurable spike count) would have a different amount of control points per instance.

So the question is whats a good way to model and implement these control points?

As they are slightly different for each tool and carry some tool/controller specific data they belong to the tool/controller in some way. But as they are also specific for each type of shape and carry very important domain logic they also belong to the model.

I would like to avoid the combinatoric explosion of adding a special type of control point for each combination of tool/shape whenever one tool or shape is added.


Update: To give another example: In the future it may occur that I have a idea for a new shape I want to support: the arc. It is similar to the pie segment but looks a bit different and behaves completely different when dragging the control points.

To implement this I would like to be able to just create a ArcShape class implementing my Shape interface and be done.

The behaviour of the arc shape

like image 326
Laszlo Korte Avatar asked May 10 '15 19:05

Laszlo Korte


1 Answers

Basic Considerations

First of all let us make some definitions for simplicity.

Entity is a Domain Model object, which defines all the structure and behaviour, i.e. logic. EntityUI is the graphical control that represents the Entity in the UI.

So basically, for Shape classes I think ShapeUI must be pretty much aware of the structure of the Shape. The structure is mainly composed of the control points I guess. In other words, having all the information about the control points (maybe vectors in future), the ShapeUI will be able to draw itself on the UI.

Initial Suggestions

What I would suggest for the Shape classes, is that the Shape class defines all the behaviour. The ShapeUI class will be aware of Shape class and keep a reference to one it is representing, by which it will have access to the control points, as well as be able to manipulate them, e.g. set their locations. The Observer pattern simply asks to be used in this context. Particularly, the Shape class may implement the Observable and the ShapeUI will implement Observer and subscribe to the corresponding Shape object.

So basically what will happen in this case, the ShapeUI object will handle all UI operations, and will be responsible for updating the Shape parameters, e.g. control point locations. Afterwards, as soon as a location update occurs, the Shape object executes its logic upon the state change and then blindly (without being aware of ShapeUI) notifies the ShapeUI about the updated state. So correspondingly the ShapeUI will draw the new state. Here you will gain low-coupled model and view.

As for the Tools, my own opinion is that each Tool must know how to manipulate each type of Shape, i.e. the per shape manipulation logic must be implemented inside the Tool class. For decoupling the view and the model, it is pretty much the same as for the Shape. The ToolUI class handles where the cursor is clicked, what ShapeUI was it clicked on, what control point was it clicked on, etc. By obtaining this information, ToolUI will pass it to the appropriate Tool object, which will then apply the logic based on the received parameters.

Handling Different Shape Types

Now when it comes to the Tool treating different Shapes in their own ways, I think the Abstract Factory pattern steps in. Each tool will implement an Abstract Factory where we will provide manipulation implementations for each type of Shape.

Summary

Based on what I suggested, here is the draft Domain Model:

Domain Model

To get the whole idea out of my suggestions, I am also posting the Sequence Diagram for a specific Use Case:

Using ToolUI the user clicks on ShapeUI's ControlPointUI

enter image description here

like image 197
bagrat Avatar answered Oct 28 '22 19:10

bagrat