]> Shamusworld >> Repos - architektonas/blob - src/spline.cpp
Added ability to manipulate Lines by click & drag.
[architektonas] / src / spline.cpp
1 // spline.cpp: NURBS object
2 //
3 // Part of the Architektonas Project
4 // (C) 2014 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // WHO  WHEN        WHAT
10 // ---  ----------  ------------------------------------------------------------
11 // JLH  03/20/2014  Created this file
12 //
13
14 #include "spline.h"
15
16 #include <QtGui>
17 #include "geometry.h"
18 #include "painter.h"
19
20 #warning "!!! This class is NOT CORRECT !!! FIX !!!"
21 Spline::Spline(Vector p1, double r, Object * p/*= NULL*/): Object(p1, p), radius(r),
22         draggingEdge(false), draggingCenter(false), hitCenter(false), hitSpline(false)
23 {
24         type = OTSpline;
25 }
26
27
28 Spline::~Spline()
29 {
30 }
31
32
33 /*virtual*/ void Spline::Draw(Painter * painter)
34 {
35         if (state == OSSelected || hitSpline || hitCenter)
36                 painter->SetPen(QPen(Qt::red, 2.0, Qt::DotLine));
37         else
38                 painter->SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
39
40         // Hatch/Fill...
41 //      QBrush brush(Qt::DiagCrossPattern);
42 //      brush.setColor(QColor(255, 255, 0));
43 //      painter->SetBrush(brush);
44         painter->SetBrush(QBrush(Qt::NoBrush));
45
46         // Draw the object...
47         painter->DrawEllipse(position, radius, radius);
48
49         // & draw handles (if needed)
50         if (state == OSSelected || hitCenter)
51                 painter->DrawHandle(position);
52
53         if (state == OSSelected && draggingEdge && objectWasDragged)
54                 painter->DrawHandle(dragPoint);
55
56         // If resizing the circle, draw an information panel showing the new radius.
57         if (draggingEdge)
58         {
59                 QString text = QObject::tr("Radius: %1\nScale: %2%");
60                 text = text.arg(radius, 0, 'd', 4).arg(radius / oldRadius * 100.0, 0, 'd', 0);
61 #if 0
62                 QPen pen = QPen(QColor(0x00, 0xFF, 0x00), 1.0, Qt::SolidLine);
63                 painter->SetPen(pen);
64                 painter->SetBrush(QBrush(QColor(0x40, 0xFF, 0x40, 0x9F)));
65                 QRectF textRect(10.0, 10.0, 270.0, 70.0);       // x, y, w, h
66                 painter->DrawRoundedRect(textRect, 7.0, 7.0);
67
68                 textRect.setLeft(textRect.left() + 14);
69                 painter->SetFont(*Object::font);
70                 pen = QPen(QColor(0x00, 0x5F, 0xDF));
71                 painter->SetPen(pen);
72                 painter->DrawText(textRect, Qt::AlignVCenter, text);
73 #else
74                 painter->DrawInformativeText(text);
75 #endif
76         }
77 }
78
79
80 /*virtual*/ Vector Spline::Center(void)
81 {
82         return position;
83 }
84
85
86 /*virtual*/ bool Spline::Collided(Vector point)
87 {
88         // Someone told us to fuck off, so we'll fuck off. :-)
89         if (ignoreClicks)
90                 return false;
91
92         // We can assume this, since this is a mouse down event here.
93         objectWasDragged = false;
94         HitTest(point);
95
96         // Now that we've done our hit testing on the non-snapped point, snap it if
97         // necessary...
98         if (snapToGrid)
99                 point = SnapPointToGrid(point);
100
101         draggingCenter = hitCenter;
102         draggingEdge = hitSpline;
103
104         if (hitCenter || hitSpline)
105         {
106                 dragPoint = point;
107                 oldState = state;
108                 state = OSSelected;
109                 oldRadius = radius;
110                 return true;
111         }
112
113         // We didn't hit anything, so deselect this object and report failure to hit
114         state = OSInactive;
115         return false;
116 }
117
118
119 /*virtual*/ bool Spline::PointerMoved(Vector point)
120 {
121         if (selectionInProgress)
122         {
123                 // Check for whether or not the rect contains this circle
124 //              if (selection.normalized().contains(Extents()))
125                 if (selection.contains(Extents()))
126                         state = OSSelected;
127                 else
128                         state = OSInactive;
129
130                 return false;
131         }
132
133         // Hit test tells us what we hit (if anything) through boolean variables. It
134         // also tells us whether or not the state changed.
135         SaveHitState();
136         bool hovered = HitTest(point);
137         needUpdate = HitStateChanged();
138         objectWasDragged = (draggingEdge | draggingCenter);
139
140         if (objectWasDragged)
141                 needUpdate = true;
142
143         if (draggingEdge)
144                 radius = Vector::Magnitude(point, position);
145         else if (draggingCenter)
146                 position = point;
147
148         // Save this point so the rendering code knows where to draw the handle...
149         dragPoint = point;
150         return hovered;
151 }
152
153
154 /*virtual*/ void Spline::PointerReleased(void)
155 {
156         // Mouse went up, so our dragging is done (if any *was* done, that is)
157         draggingEdge = draggingCenter = false;
158         hitCenter = hitSpline = false;
159
160         // If the object was dragged, then revert to the old state.
161         // Otherwise, we were probably just clicked, and want to stay in the selected state.
162         if (objectWasDragged)
163                 state = oldState;
164 }
165
166
167 /*virtual*/ bool Spline::HitTest(Point point)
168 {
169 //      SaveHitState();
170         hitCenter = hitSpline = false;
171         double length = Vector::Magnitude(position, point);
172 //printf("Spline::length = %lf, radius = %lf\n", length, radius);
173 //How to translate this into pixels from Document space???
174 //Maybe we need to pass a scaling factor in here from the caller? That would make sense, as
175 //the caller knows about the zoom factor and all that good kinda crap
176 /*
177 Document passes in the correct Cartesian coordinates being pointed to by the mouse.
178 So all we have to be concerned with is properly scaling our hot zones/handle sizes,
179 since we generally *don't* want those to scale with the zoom level. ;-)
180
181 What is going on here?
182 If we're zoomed out to, say, 50%, & our radius is 10.0 (absolute), then on screen
183 the radius will be 5.0. By multiplying the length by the zoom factor, we align our
184 pointed at length with our on screen length.
185 */
186         if ((length * Painter::zoom) < 8.0)
187                 hitCenter = true;
188 //wrong:        else if ((length < (radius + 2.0)) && (length > (radius - 2.0)))
189 /*NB: The following should be identical to what we have down below, but it doesn't work out that way... :-P */
190 //close, but no else if (((length * Painter::zoom) < ((radius * Painter::zoom) + 2.0)) && ((length * Painter::zoom) > ((radius * Painter::zoom) - 2.0)))
191 //really wrong! else if (((length * Painter::zoom) < (radius + 2.0)) && ((length * Painter::zoom) > (radius - 2.0)))
192 // close again, but sill no     else if (((length * Painter::zoom) < ((radius + 2.0) * Painter::zoom)) && ((length * Painter::zoom) > ((radius - 2.0) * Painter::zoom)))
193         else if ((fabs(length - radius) * Painter::zoom) < 2.0)
194                 hitSpline = true;
195
196 //      return HitStateChanged();
197         return (hitCenter || hitSpline ? true : false);
198 }
199
200
201 /*virtual*/ QRectF Spline::Extents(void)
202 {
203         return QRectF(QPointF(position.x - radius, position.y - radius), QPointF(position.x + radius, position.y + radius));
204 }
205
206
207 void Spline::SaveHitState(void)
208 {
209         oldHitCenter = hitCenter;
210         oldHitSpline = hitSpline;
211 }
212
213
214 bool Spline::HitStateChanged(void)
215 {
216         if ((hitCenter != oldHitCenter) || (hitSpline != oldHitSpline))
217                 return true;
218
219         return false;
220 }
221
222
223 /*virtual*/ void Spline::Enumerate(FILE * file)
224 {
225         fprintf(file, "SPLINE %i (%lf,%lf) %lf\n", layer, position.x, position.y, radius);
226 }
227
228
229 /*virtual*/ Object * Spline::Copy(void)
230 {
231 #warning "!!! This doesn't take care of attached Dimensions !!!"
232 /*
233 This is a real problem. While having a pointer in the Dimension to this line's points
234 is fast & easy, it creates a huge problem when trying to replicate an object like this.
235
236 Maybe a way to fix that then, is to have reference numbers instead of pointers. That
237 way, if you copy them, ... you might still have problems. Because you can't be sure if
238 a copy will be persistant or not, you then *definitely* do not want them to have the
239 same reference number.
240 */
241         return new Spline(position, radius, parent);
242 }
243
244
245 /*virtual*/ void Spline::Rotate(Point point, double angle)
246 {
247         Point c1 = Geometry::RotatePointAroundPoint(position, point, angle);
248         position = c1;
249 }
250
251
252 /*virtual*/ void Spline::Mirror(Point p1, Point p2)
253 {
254         Point c1 = Geometry::MirrorPointAroundLine(position, p1, p2);
255         position = c1;
256 }
257
258
259 /*virtual*/ void Spline::Save(void)
260 {
261         Object::Save();
262         oldRadius2 = radius;
263 }
264
265
266 /*virtual*/ void Spline::Restore(void)
267 {
268         Object::Restore();
269         radius = oldRadius2;
270 }
271