如果我正确理解,这里有我们所拥有的:
- 不同的数字,都有控制点
- UI允许绘制图形并拖动控制点
我在这里的建议是说,图形的特征在模型层中,UI部分在视图/控制器层中。
该模型更进一步:
-
数字应该实现一个接口:
public interface Figure {
List<Segment> segments();
List<ControlPoint> controlPoints();
void drag(ControlPoint point, Pos newPos);
void rotate(ControlPoint point, Pos newPos, Pos center); // or rotate(Pos center, double angle);
}
Segment
是一个抽象,可以表示线段、弧或贝塞尔曲线
-
a 对实现有意义,并具有当前ControlPoint
Figure
Pos
public interface ControlPoint{
Figure parent();
void drag(Pos newPos); // unsure if it must exist in both interfaces
Pos position();
ToolHint toolHint();
}
应该指示哪个工具可以使用控制点以及用于哪种用途 - 根据您的要求,旋转工具应将中心视为特殊。ToolHint
- a 表示 x,y 坐标
Pos
这样,UI就不必知道数字的实际情况。
对于 ,UI 获取列表,并简单地独立绘制每个段,并在每个控制点添加一个标记。拖动控制点时,UI 会为 新位置并重新绘制它。它应该能够在将其重新绘制到新位置之前擦除,或者(更简单但更慢)它可以在每次操作时全部重绘draw
Figure
Segment
Figure
Figure
使用该方法,我们只能在单个形状上拖动一个简单的控制点。它易于扩展,但必须为每个工具添加扩展。例如,我已经添加了一种方法,该方法允许通过移动一个具有定义中心的控制点来旋转形状。还可以添加缩放方法。drag
rotate
多种形状
如果要将转换应用于一组形状,可以使用矩形的子类。您可以构建一个矩形,其边平行于坐标包含所有形状的轴。我建议添加一个方法,该方法返回一个(合理的小)封闭矩形,其边为parralel以协调轴,以简化多形状矩形的创建。然后,当您将转换应用于聚集矩形时,它只会将转换报告给其所有元素。但是,我们来到这里要介绍无法通过拖动控制点完成的转换,因为拖动的点不属于内部形状。Figure
内部转型
到目前为止,我只处理过UI和模型之间的接口。但是对于多形状,我们看到我们需要应用任意仿射变换(平移凸起矩形的点或凸起矩形的缩放)或旋转。如果我们选择实现旋转,因为包含的形状的旋转已经完成。因此,我们只需要实现仿射变换rotate(center, angle)
class AffineTransform {
private double a, b, c, d;
/* creators, getters, setters omitted, but we probably need to implement
one creator by use case */
Pos transform(Pos pos) {
Pos newpos;
newpos.x = a * pos.x + b;
newpos.y = c * pos.y + d;
return newpos;
}
}
这样,要将仿射变换应用于 ,我们只需要以一种简单地应用定义结构的所有点的方式实现。Figure
transform(AffineTransform txform)
现在的数字是:
public interface Figure {
List<Segment> segments();
List<ControlPoint> controlPoints();
void drag(ControlPoint point, Pos newPos);
void rotate(Pos center, double angle);
// void rotate(ControlPoint point, double angle); if ControlPoint does not implement Pos
Figure getEnclosingRectangle();
void transform(AffineTransform txform);
}
总结:
这只是一般的想法,但它应该是允许工具以低耦合作用于任意形状的基础知识。