]> Shamusworld >> Repos - architektonas/blob - src/drawingview.cpp
23c1d49100ad95f26a55aeb7e1a441a1a8875d0b
[architektonas] / src / drawingview.cpp
1 // drawingview.cpp
2 //
3 // Part of the Architektonas Project
4 // (C) 2011 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/22/2011  Created this file
12 // JLH  09/29/2011  Added middle mouse button panning
13 //
14
15 // FIXED:
16 //
17 // - Redo rendering code to *not* use Qt's transform functions, as they are tied
18 //   to a left-handed system and we need a right-handed one. [DONE]
19 //
20 // STILL TO BE DONE:
21 //
22 //
23
24 // Uncomment this for debugging...
25 //#define DEBUG
26 //#define DEBUGFOO                              // Various tool debugging...
27 //#define DEBUGTP                               // Toolpalette debugging...
28
29 #include "drawingview.h"
30
31 #include <stdint.h>
32 #include "mathconstants.h"
33
34 #include "arc.h"
35 #include "circle.h"
36 #include "dimension.h"
37 #include "drawcircleaction.h"
38 #include "drawdimensionaction.h"
39 #include "drawlineaction.h"
40 #include "line.h"
41 #include "painter.h"
42
43
44 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
45         // The value in the settings file will override this.
46         useAntialiasing(true),
47         gridBackground(256, 256),
48         scale(1.0), offsetX(-10), offsetY(-10),
49         document(Vector(0, 0)),
50 //      gridSpacing(32.0), collided(false), rotateTool(false), rx(150.0), ry(150.0),
51         gridSpacing(12.0), collided(false), rotateTool(false), rx(150.0), ry(150.0),
52         scrollDrag(false), addLineTool(false), addCircleTool(false),
53         addDimensionTool(false),
54 //      selectionInProgress(false),
55         toolAction(NULL)
56 {
57         document.isTopLevelContainer = true;
58         setBackgroundRole(QPalette::Base);
59         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
60
61 //      toolPalette = new ToolWindow();
62 //      CreateCursors();
63 //      setCursor(cur[TOOLSelect]);
64 //      setMouseTracking(true);
65
66         Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
67         document.Add(line);
68         document.Add(new Line(Vector(50, 40), Vector(10, 83), &document));
69         document.Add(new Line(Vector(10, 83), Vector(17, 2), &document));
70         document.Add(new Circle(Vector(100, 100), 36, &document));
71         document.Add(new Circle(Vector(50, 150), 49, &document));
72         document.Add(new Arc(Vector(300, 300), 32, PI / 4.0, PI * 1.3, &document)),
73         document.Add(new Arc(Vector(200, 200), 60, PI / 2.0, PI * 1.5, &document));
74 #if 1
75         Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
76         line->SetDimensionOnLine(dimension);
77         document.Add(dimension);
78 #else
79         // Alternate way to do the above...
80         line->SetDimensionOnLine();
81 #endif
82 //      connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
83 //              SLOT(AddNewObjectToDocument(Object *)));
84 //This works, now how to scroll it???
85 //      QPixmap pm(256, 256);
86         QPainter pmp(&gridBackground);
87 #if 0
88         pmp.fillRect(0, 0, 256, 256, Qt::lightGray);
89
90         pmp.fillRect(0,   64,  64, 64, Qt::darkGray);
91         pmp.fillRect(0,   192, 64, 64, Qt::darkGray);
92         pmp.fillRect(64,  0,   64, 64, Qt::darkGray);
93         pmp.fillRect(64,  128, 64, 64, Qt::darkGray);
94         pmp.fillRect(128, 64,  64, 64, Qt::darkGray);
95         pmp.fillRect(128, 192, 64, 64, Qt::darkGray);
96         pmp.fillRect(192, 0,   64, 64, Qt::darkGray);
97         pmp.fillRect(192, 128, 64, 64, Qt::darkGray);
98 #else
99         pmp.fillRect(0, 0, 256, 256, QColor(240, 240, 240));
100         pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
101         for(int i=0; i<255; i+=12)
102                 pmp.drawLine(i, 0, i, 255);
103         for(int i=0; i<255; i+=12)
104                 pmp.drawLine(0, i, 255, i);
105 #endif
106         pmp.end();
107         UpdateGridBackground();
108 }
109
110
111 void DrawingView::SetRotateToolActive(bool state/*= true*/)
112 {
113         rotateTool = state;
114         update();
115 }
116
117
118 void DrawingView::SetAddLineToolActive(bool state/*= true*/)
119 {
120         if (state)
121         {
122                 toolAction = new DrawLineAction();
123                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
124                         SLOT(AddNewObjectToDocument(Object *)));
125         }
126
127         update();
128 //printf("DrawingView::SetAddLineToolActive(). toolAction=%08X\n", toolAction);
129 }
130
131
132 void DrawingView::SetAddCircleToolActive(bool state/*= true*/)
133 {
134         if (state)
135         {
136                 toolAction = new DrawCircleAction();
137                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
138                         SLOT(AddNewObjectToDocument(Object *)));
139         }
140
141         update();
142 }
143
144
145 void DrawingView::SetAddDimensionToolActive(bool state/*= true*/)
146 {
147         if (state)
148         {
149                 toolAction = new DrawDimensionAction();
150                 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
151                         SLOT(AddNewObjectToDocument(Object *)));
152         }
153
154         update();
155 }
156
157
158 void DrawingView::UpdateGridBackground(void)
159 {
160 #if 0
161 // Shift the background to match our scrolling...
162 QBrush newBrush = *backgroundBrush;
163 //QMatrix brushMatrix = backgroundBrush->matrix();
164 QTransform brushMatrix = backgroundBrush->transform();
165 brushMatrix.translate(Painter::origin.x, Painter::origin.y);
166 //brushMatrix.translate(15.0, 15.0);
167 //backgroundBrush->setMatrix(brushMatrix);
168 //backgroundBrush->setTransform(brushMatrix);
169 newBrush.setTransform(brushMatrix);
170 QPalette pal = palette();
171 //pal.setBrush(backgroundRole(), *backgroundBrush);
172 pal.setBrush(backgroundRole(), newBrush);
173 setPalette(pal);
174 //Background painting does not honor the transformation matrix (either one)...
175 // So...
176 #else
177 //was: 128
178 #define BG_BRUSH_SPAN 72
179         // Transform the origin to Qt coordinates
180         Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
181         int x = (int)pixmapOrigin.x;
182         int y = (int)pixmapOrigin.y;
183         // Use mod arithmetic to grab the correct swatch of background
184         // Problem with mod 128: Negative numbers screw it up... [FIXED]
185         x = (x < 0 ? 0 : BG_BRUSH_SPAN - 1) - (x % BG_BRUSH_SPAN);
186         y = (y < 0 ? 0 : BG_BRUSH_SPAN - 1) - (y % BG_BRUSH_SPAN);
187
188         // Here we grab a section of the bigger pixmap, so that the background
189         // *looks* like it's scrolling...
190         QPixmap pm = gridBackground.copy(x, y, BG_BRUSH_SPAN, BG_BRUSH_SPAN);
191         QPalette pal = palette();
192         pal.setBrush(backgroundRole(), QBrush(pm));
193         setAutoFillBackground(true);
194         setPalette(pal);
195 #endif
196 }
197
198
199 void DrawingView::AddNewObjectToDocument(Object * object)
200 {
201         if (object)
202         {
203                 object->Reparent(&document);
204                 document.Add(object);
205                 update();
206         }
207 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
208 }
209
210
211 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
212 {
213         // This is undoing the transform, e.g. going from client coords to local coords.
214         // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
215         // conversion of the y-axis from increasing bottom to top.
216         return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
217 }
218
219
220 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
221 {
222         // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
223         // No voodoo here, it's just grouped wrong to see it. It should be:
224         // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
225         return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
226 }
227
228
229 void DrawingView::paintEvent(QPaintEvent * /*event*/)
230 {
231         QPainter qtPainter(this);
232         Painter painter(&qtPainter);
233
234 //      qtPainter.setBackground(QBrush(Qt::DiagCrossPattern));
235 //      qtPainter.setBackgroundMode(Qt::OpaqueMode);
236
237         if (useAntialiasing)
238                 qtPainter.setRenderHint(QPainter::Antialiasing);
239
240         Painter::screenSize = Vector(size().width(), size().height());
241         Object::SetViewportHeight(size().height());
242
243         // Draw coordinate axes
244
245         painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
246         painter.DrawLine(0, -16384, 0, 16384);
247         painter.DrawLine(-16384, 0, 16384, 0);
248
249         // Draw supplemental (tool related) points
250 // NOTE that this can be done as an action!
251 // In that case, we would need access to the document...
252         if (rotateTool)
253         {
254                 painter.SetPen(QPen(QColor(0, 200, 0), 2.0, Qt::SolidLine));
255                 painter.DrawLine(rx - 10, ry, rx + 10, ry);
256                 painter.DrawLine(rx, ry - 10, rx, ry + 10);
257         }
258
259 // Maybe we can make the grid into a background brush instead, and let Qt deal
260 // with it???
261         // Draw grid
262
263 #if 0
264         painter.setPen(QPen(QColor(90, 90, 90), 1.0, Qt::DotLine));
265
266         //these two loops kill performance!
267         // Also, these overwrite our coordinate axes
268         for(double x=0; x<size().width(); x+=gridSpacing*10.0)
269                 painter.drawLine((int)x, -16384, (int)x, 16384);
270
271         for(double y=0; y<size().height(); y+=gridSpacing*10.0)
272                 painter.drawLine(-16384, (int)y, 16384, (int)y);
273 #endif
274
275 //      painter.SetPen(QPen(Qt::black, 1.0, Qt::SolidLine));
276 //
277 //      for(double x=0; x<size().width(); x+=gridSpacing)
278 //              for(double y=0; y<size().height(); y+=gridSpacing)
279 //                      painter.DrawPoint((int)x, (int)y);
280
281         // The top level document takes care of rendering for us...
282         document.Draw(&painter);
283
284         if (toolAction)
285                 toolAction->Draw(&painter);
286
287         if (Object::selectionInProgress)
288         {
289 //              painter.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
290                 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
291 //              painter.SetBrush(QBrush(Qt::NoBrush));
292                 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
293                 painter.DrawRect(Object::selection);
294         }
295 }
296
297
298 void DrawingView::mousePressEvent(QMouseEvent * event)
299 {
300         if (event->button() == Qt::LeftButton)
301         {
302                 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
303                 collided = document.Collided(point);
304
305                 if (collided)
306                         update();       // Do an update if collided with at least *one* object in the document
307
308                 if (toolAction)
309                         toolAction->MouseDown(point);
310
311                 // Didn't hit any object and not using a tool, so do a selection rectangle
312                 if (!(collided || toolAction))
313                 {
314                         Object::selectionInProgress = true;
315                         Object::selection.setTopLeft(QPointF(point.x, point.y));
316                         Object::selection.setBottomRight(QPointF(point.x, point.y));
317                 }
318         }
319         else if (event->button() == Qt::MiddleButton)
320         {
321                 scrollDrag = true;
322                 oldPoint = Vector(event->x(), event->y());
323                 // Should also change the mouse pointer as well...
324                 setCursor(Qt::SizeAllCursor);
325         }
326 }
327
328
329 void DrawingView::mouseMoveEvent(QMouseEvent * event)
330 {
331         Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
332         Object::selection.setBottomRight(QPointF(point.x, point.y));
333
334         if (event->buttons() & Qt::MiddleButton)
335         {
336                 point = Vector(event->x(), event->y());
337                 // Since we're using Qt coords for scrolling, we have to adjust them here to
338                 // conform to Cartesian coords, since the origin is using Cartesian. :-)
339                 Vector delta(point, oldPoint);
340                 delta /= Painter::zoom;
341                 delta.y = -delta.y;
342                 Painter::origin -= delta;
343
344                 UpdateGridBackground();
345                 update();
346                 oldPoint = point;
347                 return;
348         }
349
350         // Grid processing...
351 #if 1
352         // This looks strange, but it's really quite simple: We want a point that's
353         // more than half-way to the next grid point to snap there while conversely
354         // we want a point that's less than half-way to to the next grid point then
355         // snap to the one before it. So we add half of the grid spacing to the
356         // point, then divide by it so that we can remove the fractional part, then
357         // multiply it back to get back to the correct answer.
358         if (event->buttons() & Qt::LeftButton)
359         {
360                 point += gridSpacing / 2.0;                                     // *This* adds to Z!!!
361                 point /= gridSpacing;
362 //200% is ok, gridSpacing = 6 in this case...
363 //won't run into problems until gridSpacing = 1.5 (zoom = 800%)
364 //run into problems with this approach: when zoom level is 200% this truncates to
365 //integers, which is *not* what's wanted here...
366                 point.x = floor(point.x);//need to fix this for negative numbers...
367                 point.y = floor(point.y);
368                 point.z = 0;                                                            // Make *sure* Z doesn't go anywhere!!!
369                 point *= gridSpacing;
370         }
371 #endif
372 //we should keep track of the last point here and only pass this down *if* the point
373 //changed...
374         document.PointerMoved(point);
375
376         if (document.NeedsUpdate() || Object::selectionInProgress)
377                 update();
378
379         if (toolAction)
380         {
381                 toolAction->MouseMoved(point);
382                 update();
383         }
384 }
385
386
387 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
388 {
389         if (event->button() == Qt::LeftButton)
390         {
391                 document.PointerReleased();
392
393 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
394 //could set it up to use the document's update function (assumes that all object updates
395 //are being reported correctly:
396 //              if (document.NeedsUpdate())
397 //              if (collided)
398                         update();       // Do an update if collided with at least *one* object in the document
399
400                 if (toolAction)
401                         toolAction->MouseReleased();
402
403                 if (Object::selectionInProgress)
404                 {
405                         // Select all the stuff inside of selection
406                         Object::selectionInProgress = false;
407                 }
408         }
409         else if (event->button() == Qt::MiddleButton)
410         {
411                 scrollDrag = false;
412                 setCursor(Qt::ArrowCursor);
413         }
414 }
415