3 // Part of the Architektonas Project
4 // (C) 2011 Underground Software
5 // See the README and GPLv3 files for licensing and warranty information
7 // JLH = James Hammons <jlhamm@acm.org>
10 // --- ---------- -------------------------------------------------------------
11 // JLH 03/22/2011 Created this file
12 // JLH 09/29/2011 Added middle mouse button panning
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]
24 // Uncomment this for debugging...
26 //#define DEBUGFOO // Various tool debugging...
27 //#define DEBUGTP // Toolpalette debugging...
29 #include "drawingview.h"
32 #include "mathconstants.h"
36 #include "dimension.h"
37 #include "drawcircleaction.h"
38 #include "drawdimensionaction.h"
39 #include "drawlineaction.h"
44 DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
45 // The value in the settings file will override this.
46 useAntialiasing(true),
47 gridBackground(512, 512),
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),
57 document.isTopLevelContainer = true;
58 setBackgroundRole(QPalette::Base);
59 setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
61 // toolPalette = new ToolWindow();
63 // setCursor(cur[TOOLSelect]);
64 // setMouseTracking(true);
66 Line * line = new Line(Vector(5, 5), Vector(50, 40), &document);
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));
75 Dimension * dimension = new Dimension(Vector(0, 0), Vector(0, 0), DTLinear, &document);
76 line->SetDimensionOnLine(dimension);
77 document.Add(dimension);
79 // Alternate way to do the above...
80 line->SetDimensionOnLine();
84 Here we set the grid size in pixels--12 in this case. Initially, we have our
85 zoom set to make this represent 12 inches at a zoom factor of 25%. (This is
86 arbitrary.) So, to be able to decouple the grid size from the zoom, we need
87 to be able to set the size of the background grid (which we do here at an
88 arbitrary 12 pixels) to anything we want (within reason, of course :-).
90 The drawing enforces the grid spacing through the drawing->gridSpacing variable.
92 drawing->gridSpacing = 12.0 / Painter::zoom;
94 Painter::zoom is the zoom factor for the drawing, and all mouse clicks are
95 translated to Cartesian coordinates through this. (Initially, Painter::zoom is
96 set to 1.0. SCREEN_ZOOM is set to 1.0/4.0.)
98 Really, the 100% zoom level can be set at *any* zoom level, it's more of a
99 convenience function than any measure of absolutes. Doing things that way we
100 could rid ourselves of the whole SCREEN_ZOOM parameter and all the attendant
101 shittyness that comes with it.
103 However, it seems that SCREEN_ZOOM is used to make text and arrow sizes show up
104 a certain way, which means we should probably create something else in those
105 objects to take its place--like some kind of scale factor. This would seem to
106 imply that certain point sizes actually *do* tie things like fonts to absolute
107 sizes on the screen, but not necessarily because you could have an inch scale
108 with text that is quite small relative to other objects on the screen, which
109 currently you have to zoom in to see (and which blows up the text). Point sizes
110 in an application like this are a bit meaningless; even though an inch is an
111 inch regardless of the zoom level a piece of text can be larger or smaller than
112 this. Maybe this is the case for having a base unit and basing point sizes off
117 QPainter pmp(&gridBackground);
118 pmp.fillRect(0, 0, 512, 512, QColor(240, 240, 240));
119 pmp.setPen(QPen(QColor(210, 210, 255), 2.0, Qt::SolidLine));
121 for(int i=0; i<511; i+=12)
123 pmp.drawLine(i, 0, i, 511);
124 pmp.drawLine(0, i, 511, i);
128 UpdateGridBackground();
132 void DrawingView::SetRotateToolActive(bool state/*= true*/)
139 void DrawingView::SetAddLineToolActive(bool state/*= true*/)
143 toolAction = new DrawLineAction();
144 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
145 SLOT(AddNewObjectToDocument(Object *)));
149 //printf("DrawingView::SetAddLineToolActive(). toolAction=%08X\n", toolAction);
153 void DrawingView::SetAddCircleToolActive(bool state/*= true*/)
157 toolAction = new DrawCircleAction();
158 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
159 SLOT(AddNewObjectToDocument(Object *)));
166 void DrawingView::SetAddDimensionToolActive(bool state/*= true*/)
170 toolAction = new DrawDimensionAction();
171 connect(toolAction, SIGNAL(ObjectReady(Object *)), this,
172 SLOT(AddNewObjectToDocument(Object *)));
179 void DrawingView::UpdateGridBackground(void)
182 #define BG_BRUSH_SPAN 72
183 // Transform the origin to Qt coordinates
184 Vector pixmapOrigin = Painter::CartesianToQtCoords(Vector());
185 int x = (int)pixmapOrigin.x;
186 int y = (int)pixmapOrigin.y;
187 // Use mod arithmetic to grab the correct swatch of background
188 // Problem with mod 128: Negative numbers screw it up... [FIXED]
189 x = (x < 0 ? 0 : BG_BRUSH_SPAN - 1) - (x % BG_BRUSH_SPAN);
190 y = (y < 0 ? 0 : BG_BRUSH_SPAN - 1) - (y % BG_BRUSH_SPAN);
192 // Here we grab a section of the bigger pixmap, so that the background
193 // *looks* like it's scrolling...
194 QPixmap pm = gridBackground.copy(x, y, BG_BRUSH_SPAN, BG_BRUSH_SPAN);
195 QPalette pal = palette();
196 pal.setBrush(backgroundRole(), QBrush(pm));
197 setAutoFillBackground(true);
202 void DrawingView::AddNewObjectToDocument(Object * object)
206 object->Reparent(&document);
207 document.Add(object);
210 //printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object);
214 QPoint DrawingView::GetAdjustedMousePosition(QMouseEvent * event)
216 // This is undoing the transform, e.g. going from client coords to local coords.
217 // In essence, the height - y is height + (y * -1), the (y * -1) term doing the
218 // conversion of the y-axis from increasing bottom to top.
219 return QPoint(offsetX + event->x(), offsetY + (size().height() - event->y()));
223 QPoint DrawingView::GetAdjustedClientPosition(int x, int y)
225 // VOODOO ALERT (ON Y COMPONENT!!!!) (eh?)
226 // No voodoo here, it's just grouped wrong to see it. It should be:
227 // -offsetY + (size.height() + (y * -1.0)) <-- this is wrong, offsetY should be positive
228 return QPoint(-offsetX + x, (size().height() - (-offsetY + y)) * +1.0);
232 void DrawingView::paintEvent(QPaintEvent * /*event*/)
234 QPainter qtPainter(this);
235 Painter painter(&qtPainter);
238 qtPainter.setRenderHint(QPainter::Antialiasing);
240 Painter::screenSize = Vector(size().width(), size().height());
241 Object::SetViewportHeight(size().height());
243 // Draw coordinate axes
245 painter.SetPen(QPen(Qt::blue, 1.0, Qt::DotLine));
246 painter.DrawLine(0, -16384, 0, 16384);
247 painter.DrawLine(-16384, 0, 16384, 0);
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 // [We can do that by making the document a class object...]
255 painter.SetPen(QPen(QColor(0, 200, 0), 2.0, Qt::SolidLine));
256 painter.DrawLine(rx - 10, ry, rx + 10, ry);
257 painter.DrawLine(rx, ry - 10, rx, ry + 10);
260 // Maybe we can make the grid into a background brush instead, and let Qt deal
263 // The top level document takes care of rendering for us...
264 document.Draw(&painter);
267 toolAction->Draw(&painter);
269 if (Object::selectionInProgress)
271 // painter.SetPen(QPen(Qt::green, 1.0, Qt::SolidLine));
272 painter.SetPen(QPen(QColor(255, 127, 0, 255)));
273 // painter.SetBrush(QBrush(Qt::NoBrush));
274 painter.SetBrush(QBrush(QColor(255, 127, 0, 100)));
275 painter.DrawRect(Object::selection);
280 void DrawingView::mousePressEvent(QMouseEvent * event)
282 if (event->button() == Qt::LeftButton)
284 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
285 collided = document.Collided(point);
288 update(); // Do an update if collided with at least *one* object in the document
291 toolAction->MouseDown(point);
293 // Didn't hit any object and not using a tool, so do a selection rectangle
294 if (!(collided || toolAction))
296 Object::selectionInProgress = true;
297 Object::selection.setTopLeft(QPointF(point.x, point.y));
298 Object::selection.setBottomRight(QPointF(point.x, point.y));
301 else if (event->button() == Qt::MiddleButton)
304 oldPoint = Vector(event->x(), event->y());
305 // Should also change the mouse pointer as well...
306 setCursor(Qt::SizeAllCursor);
311 void DrawingView::mouseMoveEvent(QMouseEvent * event)
313 Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y()));
314 Object::selection.setBottomRight(QPointF(point.x, point.y));
316 if (event->buttons() & Qt::MiddleButton)
318 point = Vector(event->x(), event->y());
319 // Since we're using Qt coords for scrolling, we have to adjust them here to
320 // conform to Cartesian coords, since the origin is using Cartesian. :-)
321 Vector delta(point, oldPoint);
322 delta /= Painter::zoom;
324 Painter::origin -= delta;
326 UpdateGridBackground();
332 // Grid processing...
334 // This looks strange, but it's really quite simple: We want a point that's
335 // more than half-way to the next grid point to snap there while conversely
336 // we want a point that's less than half-way to to the next grid point then
337 // snap to the one before it. So we add half of the grid spacing to the
338 // point, then divide by it so that we can remove the fractional part, then
339 // multiply it back to get back to the correct answer.
340 if (event->buttons() & Qt::LeftButton)
342 point += gridSpacing / 2.0; // *This* adds to Z!!!
343 point /= gridSpacing;
344 //200% is ok, gridSpacing = 6 in this case...
345 //won't run into problems until gridSpacing = 1.5 (zoom = 800%)
346 //run into problems with this approach: when zoom level is 200% this truncates to
347 //integers, which is *not* what's wanted here...
348 point.x = floor(point.x);//need to fix this for negative numbers...
349 point.y = floor(point.y);
350 point.z = 0; // Make *sure* Z doesn't go anywhere!!!
351 point *= gridSpacing;
354 //we should keep track of the last point here and only pass this down *if* the point
356 document.PointerMoved(point);
358 if (document.NeedsUpdate() || Object::selectionInProgress)
363 toolAction->MouseMoved(point);
369 void DrawingView::mouseReleaseEvent(QMouseEvent * event)
371 if (event->button() == Qt::LeftButton)
373 document.PointerReleased();
375 //We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
376 //could set it up to use the document's update function (assumes that all object updates
377 //are being reported correctly:
378 // if (document.NeedsUpdate())
380 update(); // Do an update if collided with at least *one* object in the document
383 toolAction->MouseReleased();
385 if (Object::selectionInProgress)
387 // Select all the stuff inside of selection
388 Object::selectionInProgress = false;
391 else if (event->button() == Qt::MiddleButton)
394 setCursor(Qt::ArrowCursor);