src/layeritemwidget.h \
src/main.h \
src/mathconstants.h \
+ src/objectwidget.h \
src/painter.h \
+ src/rect.h \
src/settingsdialog.h \
src/structs.h \
src/utils.h \
src/layerwidget.cpp \
src/layeritemwidget.cpp \
src/main.cpp \
+ src/objectwidget.cpp \
src/painter.cpp \
+ src/rect.cpp \
src/settingsdialog.cpp \
src/utils.cpp \
src/vector.cpp
#include "generaltab.h"
#include "global.h"
#include "layerwidget.h"
+#include "objectwidget.h"
#include "painter.h"
#include "settingsdialog.h"
#include "structs.h"
BlockWidget * bw = new BlockWidget;
dock2->setWidget(bw);
addDockWidget(Qt::RightDockWidgetArea, dock2);
+ QDockWidget * dock3 = new QDockWidget(tr("Object"), this);
+ ObjectWidget * ow = new ObjectWidget;
+ dock3->setWidget(ow);
+ addDockWidget(Qt::RightDockWidgetArea, dock3);
// Needed for saveState()
dock1->setObjectName("Layers");
dock2->setObjectName("Blocks");
+ dock3->setObjectName("Object");
- // Create status bar
+ // Create status bar
zoomIndicator = new QLabel("Grid: 12.0\" BU: Inch");
statusBar()->addPermanentWidget(zoomIndicator);
statusBar()->showMessage(tr("Ready"));
connect(lw, SIGNAL(LayerDeleted(int)), drawing, SLOT(DeleteCurrentLayer(int)));
connect(lw, SIGNAL(LayerToggled()), drawing, SLOT(HandleLayerToggle()));
connect(lw, SIGNAL(LayersSwapped(int, int)), drawing, SLOT(HandleLayerSwap(int, int)));
+
+ connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *)));
}
void ApplicationWindow::FileNew(void)
{
- // Should warn the user if drawing hasn't been saved...
- drawing->document.objects.empty();
+ // Should warn the user if drawing hasn't been saved... !!! FIX !!!
+ DeleteContents(drawing->document.objects);
+ drawing->document.objects.clear();
drawing->update();
documentName.clear();
setWindowTitle("Architektonas - Untitled");
return;
}
- Container container;//(Vector(0, 0));
+ Container container;
bool successful = FileIO::LoadAtnsFile(file, &container);
fclose(file);
msg.setText(QString(tr("Could not load file \"%1\"!")).arg(filename));
msg.setIcon(QMessageBox::Critical);
msg.exec();
+ // Make sure to delete any hanging objects in the container...
+ DeleteContents(container.objects);
return;
}
-printf("FileOpen: container size = %li\n", container.objects.size());
+//printf("FileOpen: container size = %li\n", container.objects.size());
+ // Keep memory leaks from happening by getting rid of the old document
+ DeleteContents(drawing->document.objects);
+ // We can do this because the vector is just a bunch of pointers to our
+ // Objects, and the Containers (non-empty) can be moved way with no problem.
drawing->document = container;
drawing->update();
documentName = filename;
msg.setText(QString(tr("Could not save file \"%1\"!")).arg(documentName));
msg.setIcon(QMessageBox::Critical);
msg.exec();
- // In this case, we should unlink the created file, since it's not right...
+ // In this case, we should unlink the created file, since it's not
+ // right...
// unlink(documentName.toUtf8().data());
QFile::remove(documentName);
return;
{
// For this tool, we check first to see if anything is selected. If so, we
// delete those and *don't* select the delete tool.
- if (drawing->numSelected > 0)
+// if (drawing->numSelected > 0)
+ if (drawing->select.size() > 0)
{
-// drawing->DeleteSelectedItems();
DeleteSelectedObjects(drawing->document.objects);
drawing->update();
deleteAct->setChecked(false);
// Need the parent of the group, we're assuming here that the parent is
// the drawing's document. Does it matter? Maybe...
- // Could just stipulate that grouping like this only takes place where the
- // parent of the group is the drawing's document. Makes life much simpler.
+ // Could just stipulate that grouping like this only takes place where
+ // the parent of the group is the drawing's document. Makes life much
+ // simpler.
((Container *)object)->SelectAll();
((Container *)object)->MoveContentsTo(&(drawing->document));
drawing->document.Delete(object);
statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(itemsSelected));
}
#else
+ int numSelected = drawing->select.size();
+
+ // If nothing selected, do nothing
+ if (numSelected == 0)
+ {
+ statusBar()->showMessage(tr("No objects selected to make a group from."));
+ return;
+ }
+
+ // If it's a group that's selected, ungroup it and leave the objects in a
+ // selected state
+ if (numSelected == 1)
+ {
+ Object * obj = (Object *)drawing->select[0];
+
+ if (obj->type != OTContainer)
+ {
+ statusBar()->showMessage(tr("A group requires two or more selected objects."));
+ return;
+ }
+
+ // Need the parent of the group, we're assuming here that the parent is
+ // the drawing's document. Does it matter? Maybe...
+ // Could just stipulate that grouping like this only takes place where
+ // the parent of the group is the drawing's document. Makes life much
+ // simpler.
+// ((Container *)object)->SelectAll();
+// ((Container *)object)->MoveContentsTo(&(drawing->document));
+// drawing->document.Delete(object);
+ Container * c = (Container *)obj;
+//printf("Ungroup: container size = %li\n", c->objects.size());
+ SelectAll(c->objects);
+//printf("Ungroup: document size = %li (pre-AddObjectsTo)\n", drawing->document.objects.size());
+ RemoveSelectedObjects(drawing->document.objects);
+ AddObjectsTo(drawing->document.objects, c->objects);
+ drawing->select.clear();
+ AddObjectsTo(drawing->select, c->objects);
+ delete c;
+ statusBar()->showMessage(tr("Objects ungrouped."));
+//printf("Ungroup: document size = %li\n", drawing->document.objects.size());
+ }
+ // Otherwise, if it's a group of 2 or more objects (which can be groups too)
+ // group them and select the group
+ else if (numSelected > 1)
+ {
+// Container * container = new Container(Vector(), &(drawing->document));
+// drawing->document.MoveSelectedContentsTo(container);
+// drawing->document.Add(container);
+// container->DeselectAll();
+// container->state = OSSelected;
+
+ Container * c = new Container();
+// AddObjectsTo(c->objects, drawing->select);
+// RemoveSelectedObjects(drawing->document.objects);
+ MoveSelectedObjectsTo(c->objects, drawing->document.objects);
+ drawing->document.objects.push_back(c);
+ ClearSelected(c->objects);
+ c->selected = true;
+ c->layer = Global::currentLayer;
+ drawing->select.clear();
+ drawing->select.push_back(c);
+ statusBar()->showMessage(QString(tr("Grouped %1 objects.")).arg(numSelected));
+//printf("Group: document size = %li\n", drawing->document.objects.size());
+ }
#endif
drawing->update();
#define BACKGROUND_MAX_SIZE 512
-// Class variable
-//Container DrawingView::document(Vector(0, 0));
-
DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent),
// The value in the settings file will override this.
- useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false),
+ useAntialiasing(true), /*numSelected(0),*/ numHovered(0), shiftDown(false),
ctrlDown(false),
gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE),
- scale(1.0), offsetX(-10), offsetY(-10),
+ scale(1.0), offsetX(-10), offsetY(-10), document(true),
gridPixels(0), collided(false), hoveringIntersection(false)
{
-// document.isTopLevelContainer = true;
//wtf? doesn't work except in c++11??? document = { 0 };
setBackgroundRole(QPalette::Base);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//
// Renders objects in the passed in vector
//
-void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer)
+void DrawingView::RenderObjects(Painter * painter, std::vector<void *> & v, int layer, bool ignoreLayer/*= false*/)
{
std::vector<void *>::iterator i;
float scaledThickness = Global::scale * obj->thickness;
// If the object isn't on the current layer being drawn, skip it
- if (obj->layer != layer)
+ if (!ignoreLayer && (obj->layer != layer))
continue;
if ((Global::tool == TTRotate) && ctrlDown && obj->selected)
painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness);
break;
}
+ case OTSpline:
+ {
+ break;
+ }
+ case OTPolygon:
+ {
+ break;
+ }
+ case OTContainer:
+ {
+ // Containers require recursive rendering...
+ Container * c = (Container *)obj;
+ RenderObjects(painter, (*c).objects, layer);
+
+//printf("Container extents: <%lf, %lf>, <%lf, %lf>\nsize: %i\n", r.l, r.t, r.r, r.b, c->objects.size());
+ // Containers also have special indicators showing they are selected
+ if (c->selected || c->hitObject)
+ {
+ Rect r = GetObjectExtents(obj);
+ painter->DrawRectCorners(r);
+ }
+
+ break;
+ }
default:
break;
}
}
else
{
+ painter->DrawCross(toolPoint[0]);
double length = Vector::Magnitude(toolPoint[0], toolPoint[1]);
-// painter->DrawLine(toolPoint[0], toolPoint[1]);
-// painter->DrawHandle(toolPoint[1]);
painter->SetBrush(QBrush(Qt::NoBrush));
painter->DrawEllipse(toolPoint[0], length, length);
- QString text = tr("Radius: %1 in.");//\n") + QChar(0x2221) + tr(": %2");
- informativeText = text.arg(length);//.arg(absAngle);
+ QString text = tr("Radius: %1 in.");
+ informativeText = text.arg(length);
}
}
else if (Global::tool == TTArc)
return;
painter->DrawLine(toolPoint[0], toolPoint[1]);
- // Likely we need a tool container for this... (now we do!)
-#if 0
- if (ctrlDown)
- {
- painter->SetPen(0x00FF00, 2.0, LSSolid);
- overrideColor = true;
- }
-
- RenderObjects(painter, toolObjects);
- overrideColor = false;
-#endif
double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
QString text = QChar(0x2221) + QObject::tr(": %1");
return;
Point mirrorPoint = toolPoint[0] + Vector(toolPoint[1], toolPoint[0]);
-// painter->DrawLine(toolPoint[0], toolPoint[1]);
painter->DrawLine(mirrorPoint, toolPoint[1]);
- // Likely we need a tool container for this... (now we do!)
-#if 0
- if (ctrlDown)
- {
- painter->SetPen(0x00FF00, 2.0, LSSolid);
- overrideColor = true;
- }
-
- RenderObjects(painter, toolObjects);
- overrideColor = false;
-#endif
double absAngle = (Vector(toolPoint[1] - toolPoint[0]).Angle()) * RADIANS_TO_DEGREES;
// Handle tool processing, if any
if (Global::tool)
{
- if (Global::snapToGrid)
+ if (hoveringIntersection)
+ point = intersectionPoint;
+ else if (Global::snapToGrid)
point = SnapPointToGrid(point);
//Also, may want to figure out if hovering over a snap point on an object,
// Needed for grab & moving objects
// We do it *after*... why? (doesn't seem to confer any advantage...)
- if (Global::snapToGrid)
+ if (hoveringIntersection)
+ oldPoint = intersectionPoint;
+ else if (Global::snapToGrid)
oldPoint = SnapPointToGrid(point);
return;
if (event->buttons() & Qt::MiddleButton)
{
point = Vector(event->x(), event->y());
- // Since we're using Qt coords for scrolling, we have to adjust them here to
- // conform to Cartesian coords, since the origin is using Cartesian. :-)
+ // Since we're using Qt coords for scrolling, we have to adjust them
+ // here to conform to Cartesian coords, since the origin is using
+ // Cartesian. :-)
Vector delta(oldPoint, point);
delta /= Global::zoom;
delta.y = -delta.y;
// Handle object movement (left button down & over an object)
if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
{
- if (Global::snapToGrid)
+ if (hoveringIntersection)
+ point = intersectionPoint;
+ else if (Global::snapToGrid)
point = SnapPointToGrid(point);
HandleObjectMovement(point);
if (numHovered > 1)
{
GetHovered(hover);
-
-// double t, u;
-// int numIntersecting = Geometry::Intersects((Object *)hover[0], (Object *)hover[1], &t, &u);
Geometry::Intersects((Object *)hover[0], (Object *)hover[1]);
int numIntersecting = Global::numIntersectParams;
double t = Global::intersectParam[0];
}
}
+//this doesn't work down here for some reason... :-P
+//could be because the object being moved is part of the intersection, and this is screwing things up. In which case, we need to exclude the moving object somehow from the hit test function...
+#if 0
+ // Handle object movement (left button down & over an object)
+ if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool)
+ {
+ if (hoveringIntersection)
+ point = intersectionPoint;
+ else if (Global::snapToGrid)
+ point = SnapPointToGrid(point);
+
+ HandleObjectMovement(point);
+ update();
+ oldPoint = point;
+ return;
+ }
+#endif
+
// Do tool handling, if any are active...
if (Global::tool)
{
- if (Global::snapToGrid)
+ if (hoveringIntersection)
+ point = intersectionPoint;
+ else if (Global::snapToGrid)
point = SnapPointToGrid(point);
ToolHandler(ToolMouseMove, point);
if (event->button() == Qt::LeftButton)
{
//We need to update especially if nothing collided and the state needs to change. !!! FIX !!!
-//could set it up to use the document's update function (assumes that all object updates
-//are being reported correctly:
+//could set it up to use the document's update function (assumes that all object
+//updates are being reported correctly:
// if (document.NeedsUpdate())
// Do an update if collided with at least *one* object in the document
// if (collided)
}
+Rect DrawingView::GetObjectExtents(Object * obj)
+{
+ // Default to empty rect, if object checks below fail for some reason
+ Rect rect;
+
+ switch (obj->type)
+ {
+ case OTLine:
+ {
+ rect = Rect(obj->p[0], obj->p[1]);
+ break;
+ }
+ case OTCircle:
+ {
+ rect = Rect(obj->p[0], obj->p[0]);
+ rect.Expand(obj->radius[0]);
+ break;
+ }
+ case OTArc:
+ {
+ Arc * a = (Arc *)obj;
+
+ double start = a->angle[0];
+ double end = start + a->angle[1];
+ rect = Rect(Point(cos(start), sin(start)), Point(cos(end), sin(end)));
+
+ // If the end of the arc is before the beginning, add 360 degrees to it
+ if (end < start)
+ end += TAU;
+
+ // Adjust the bounds depending on which axes are crossed
+ if ((start < QTR_TAU) && (end > QTR_TAU))
+ rect.t = 1.0;
+
+ if ((start < HALF_TAU) && (end > HALF_TAU))
+ rect.l = -1.0;
+
+ if ((start < THREE_QTR_TAU) && (end > THREE_QTR_TAU))
+ rect.b = -1.0;
+
+ if ((start < TAU) && (end > TAU))
+ rect.r = 1.0;
+
+ if ((start < (TAU + QTR_TAU)) && (end > (TAU + QTR_TAU)))
+ rect.t = 1.0;
+
+ if ((start < (TAU + HALF_TAU)) && (end > (TAU + HALF_TAU)))
+ rect.l = -1.0;
+
+ if ((start < (TAU + THREE_QTR_TAU)) && (end > (TAU + THREE_QTR_TAU)))
+ rect.b = -1.0;
+
+ rect *= a->radius[0];
+ rect.Translate(a->p[0]);
+
+ break;
+ }
+ case OTContainer:
+ {
+ Container * c = (Container *)obj;
+ std::vector<void *>::iterator i = c->objects.begin();
+ rect = GetObjectExtents((Object *)*i);
+ i++;
+
+ for(; i!=c->objects.end(); i++)
+ rect |= GetObjectExtents((Object *)*i);
+ }
+ default:
+ break;
+ }
+
+ return rect;
+}
+
+
void DrawingView::CheckObjectBounds(void)
{
std::vector<void *>::iterator i;
- numSelected = 0;
for(i=document.objects.begin(); i!=document.objects.end(); i++)
{
default:
break;
}
-
- if (obj->selected)
- numSelected++;
}
}
{
Object * obj = (Object *)(*i);
+ if (HitTest(obj, point))
+ needUpdate = true;
+#if 0
switch (obj->type)
{
case OTLine:
break;
}
+ case OTContainer:
+ {
+ // Containers must be recursively tested...
+ Container * c = (Container *)obj;
+ std::vector<void *>::iterator i;
+
+ for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ {
+
+ }
+ }
default:
break;
}
+#endif
if (obj->hovered)
-// {
+ {
numHovered++;
//printf("MouseMove: OBJECT HOVERED (numHovered = %i)\n", numHovered);
-// }
+ emit(ObjectHovered(obj));
+ }
+ }
+
+ return needUpdate;
+}
+
+
+bool DrawingView::HitTest(Object * obj, Point point)
+{
+ bool needUpdate = false;
+
+ switch (obj->type)
+ {
+ case OTLine:
+ {
+ bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHO = obj->hitObject;
+ obj->hitPoint[0] = obj->hitPoint[1] = obj->hitObject = false;
+ Vector lineSegment = obj->p[1] - obj->p[0];
+ Vector v1 = point - obj->p[0];
+ Vector v2 = point - obj->p[1];
+ double t = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], point);
+ double distance;
+
+ if (t < 0.0)
+ distance = v1.Magnitude();
+ else if (t > 1.0)
+ distance = v2.Magnitude();
+ else
+ // distance = ?Det?(ls, v1) / |ls|
+ distance = fabs((lineSegment.x * v1.y - v1.x * lineSegment.y)
+ / lineSegment.Magnitude());
+
+ if ((v1.Magnitude() * Global::zoom) < 8.0)
+ obj->hitPoint[0] = true;
+ else if ((v2.Magnitude() * Global::zoom) < 8.0)
+ obj->hitPoint[1] = true;
+ else if ((distance * Global::zoom) < 5.0)
+ obj->hitObject = true;
+
+ obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false);
+
+ if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject))
+ needUpdate = true;
+
+ break;
+ }
+ case OTCircle:
+ {
+ bool oldHP = obj->hitPoint[0], oldHO = obj->hitObject;
+ obj->hitPoint[0] = obj->hitObject = false;
+ double length = Vector::Magnitude(obj->p[0], point);
+
+ if ((length * Global::zoom) < 8.0)
+ obj->hitPoint[0] = true;
+ else if ((fabs(length - obj->radius[0]) * Global::zoom) < 2.0)
+ obj->hitObject = true;
+
+ obj->hovered = (obj->hitPoint[0] || obj->hitObject ? true : false);
+
+ if ((oldHP != obj->hitPoint[0]) || (oldHO != obj->hitObject))
+ needUpdate = true;
+
+ break;
+ }
+ case OTArc:
+ {
+ bool oldHP0 = obj->hitPoint[0], oldHP1 = obj->hitPoint[1], oldHP2 = obj->hitPoint[2], oldHO = obj->hitObject;
+ obj->hitPoint[0] = obj->hitPoint[1] = obj->hitPoint[2] = obj->hitObject = false;
+ double length = Vector::Magnitude(obj->p[0], point);
+ double angle = Vector::Angle(obj->p[0], point);
+
+ // Make sure we get the angle in the correct spot
+ if (angle < obj->angle[0])
+ angle += TAU;
+
+ // Get the span that we're pointing at...
+ double span = angle - obj->angle[0];
+
+ // N.B.: Still need to hit test the arc start & arc span handles...
+ double spanAngle = obj->angle[0] + obj->angle[1];
+ Point handle1 = obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0]);
+ Point handle2 = obj->p[0] + (Vector(cos(spanAngle), sin(spanAngle)) * obj->radius[0]);
+ double length2 = Vector::Magnitude(point, handle1);
+ double length3 = Vector::Magnitude(point, handle2);
+
+ if ((length * Global::zoom) < 8.0)
+ obj->hitPoint[0] = true;
+ else if ((length2 * Global::zoom) < 8.0)
+ obj->hitPoint[1] = true;
+ else if ((length3 * Global::zoom) < 8.0)
+ obj->hitPoint[2] = true;
+ else if (((fabs(length - obj->radius[0]) * Global::zoom) < 2.0) && (span < obj->angle[1]))
+ obj->hitObject = true;
+
+ obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitPoint[2] || obj->hitObject ? true : false);
+
+ if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHP2 != obj->hitPoint[2]) || (oldHO != obj->hitObject))
+ needUpdate = true;
+
+ break;
+ }
+ case OTContainer:
+ {
+ // Containers must be recursively tested...
+ Container * c = (Container *)obj;
+ c->hitObject = false;
+ c->hovered = false;
+ std::vector<void *>::iterator i;
+
+ for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ {
+ Object * cObj = (Object *)*i;
+
+ if (HitTest(cObj, point))
+ needUpdate = true;
+
+ if (cObj->hitObject == true)
+ c->hitObject = true;
+
+ if (cObj->hovered == true)
+ c->hovered = true;
+ }
+
+ break;
+ }
+ default:
+ break;
}
return needUpdate;
informativeText = text.arg(obj->radius[0], 0, 'd', 4);
}
+ break;
+ case OTContainer:
+ // This is shitty, but works for now until I can code up something
+ // nicer :-)
+ TranslateObject(obj, delta);
+
break;
default:
break;
}
}
-
-
-#if 0
- // This returns true if we've moved over an object...
- if (document.PointerMoved(point)) // <-- This
- // This is where the object would do automagic dragging & shit. Since we don't
- // do that anymore, we need a strategy to handle it.
- {
-
-/*
-Now objects handle mouse move snapping as well. The code below mainly works only
-for tools; we need to fix it so that objects work as well...
-
-There's a problem with the object point snapping in that it's dependent on the
-order of the objects in the document. Most likely this is because it counts the
-selected object last and thus fucks up the algorithm. Need to fix this...
-
-
-*/
- // Do object snapping here. Grid snapping on mouse down is done in the
- // objects themselves, only because we have to hit test the raw point,
- // not the snapped point. There has to be a better way...!
- if (document.penultimateObjectHovered)
- {
- // Two objects are hovered, see if we have an intersection point
- if ((document.lastObjectHovered->type == OTLine) && (document.penultimateObjectHovered->type == OTLine))
- {
- double t;
- int n = Geometry::Intersects((Line *)document.lastObjectHovered, (Line *)document.penultimateObjectHovered, &t);
-
- if (n == 1)
- {
- Global::snapPoint = document.lastObjectHovered->GetPointAtParameter(t);
- Global::snapPointIsValid = true;
- }
- }
- else if ((document.lastObjectHovered->type == OTCircle) && (document.penultimateObjectHovered->type == OTCircle))
- {
- Point p1, p2;
- int n = Geometry::Intersects((Circle *)document.lastObjectHovered, (Circle *)document.penultimateObjectHovered, 0, 0, 0, 0, &p1, &p2);
-
- if (n == 1)
- {
- Global::snapPoint = p1;
- Global::snapPointIsValid = true;
- }
- else if (n == 2)
- {
- double d1 = Vector(point, p1).Magnitude();
- double d2 = Vector(point, p2).Magnitude();
-
- if (d1 < d2)
- Global::snapPoint = p1;
- else
- Global::snapPoint = p2;
-
- Global::snapPointIsValid = true;
- }
- }
- }
-// else
-// {
- // Otherwise, it was a single object hovered...
-// }
- }
-
- if (toolAction)
- {
- if (Global::snapToGrid)
- point = Global::SnapPointToGrid(point);
-
- // We always snap to object points, and they take precendence over
- // grid points...
- if (Global::snapPointIsValid)
- point = Global::snapPoint;
-
- toolAction->MouseMoved(point);
- }
-#else
-#endif
-
#include <QtWidgets>
#include <stdint.h>
+#include "rect.h"
#include "structs.h"
enum { ToolMouseDown, ToolMouseMove, ToolMouseUp, ToolKeyDown, ToolKeyUp, ToolCleanup };
void SetGridSize(uint32_t);
void UpdateGridBackground(void);
Point SnapPointToGrid(Point);
- void RenderObjects(Painter *, std::vector<void *> &, int);
+ void RenderObjects(Painter *, std::vector<void *> &, int, bool ignoreLayer = false);
void AddHoveredToSelection(void);
void GetSelection(std::vector<void *> &);
void GetHovered(std::vector<void *> &);
void ArcHandler(int, Point);
void RotateHandler(int, Point);
void MirrorHandler(int, Point);
+ Rect GetObjectExtents(Object *);
void CheckObjectBounds(void);
bool HitTestObjects(Point);
+ bool HitTest(Object *, Point);
void HandleObjectMovement(Point);
public slots:
void HandleLayerToggle(void);
void HandleLayerSwap(int, int);
+ signals:
+ void ObjectHovered(Object *);
+
protected:
void paintEvent(QPaintEvent * event);
void resizeEvent(QResizeEvent * event);
public:
bool useAntialiasing;
- uint32_t numSelected;
+// uint32_t numSelected;
uint32_t numHovered;
bool shiftDown;
bool ctrlDown;
*/
-enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
- OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
+//enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFLine, OTFCircle, OTFArc,
+// OTFDimension, OTFPolygon, OTFText, OTFImage, OTFBlock, OTFEndOfFile };
+enum ObjectTypeFile { OTFContainer, OTFContainerEnd, OTFObject, OTFEndOfFile };
-/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * object)
+// Instantiate class variables
+/*static*/ int FileIO::objectFileType = OTFObject;
+
+
+/*static*/ bool FileIO::SaveAtnsFile(FILE * file, Container * c)
{
- /* Approach: loop through the container, doing a depth-first traversal. Any extra
- containers found are looped through until there aren't any more down, then
- ordinary objects are described. This can be handled by a virtual Object function
- that reports the object by itself if it's a non-Container, otherwise it
- enumerates all objects within itself. */
+ /* Approach: loop through the container, doing a depth-first traversal. Any
+ extra containers found are looped through until there aren't any more
+ down, then ordinary objects are described. This can be handled by a
+ virtual Object function that reports the object by itself if it's a non-
+ Container, otherwise it enumerates all objects within itself. */
- fprintf(file, "ARCHITEKTONAS DRAWING V1.0\n");
+ fprintf(file, "ARCHITEKTONAS DRAWING V1.1\n");
#if 0
object->Enumerate(file);
- fprintf(file, "END\n");
- return true;
#else
- return false;
+ WriteObjectToFile(file, (Object *)c);
#endif
+ fprintf(file, "END\n");
+ return true;
}
/*static*/ bool FileIO::LoadAtnsFile(FILE * file, Container * drawing)
{
-// char buffer[256];
float version;
fscanf(file, "ARCHITEKTONAS DRAWING V%f", &version);
//printf("Load: version = %f\n", version);
- if (version != 1.0)
- return false;
- /* Approach: read each object in the file, one by one. If the object is a Container,
- add objects to it until an "endContainer" marker is found. This will require a
- stack to maintain the current Container. */
+// if (version != 1.0)
+// return false;
+
+ if (version == 1.0)
+ return LoadVersion1_0(file, drawing);
+ else if (version == 1.1)
+ return LoadVersion1_1(file, drawing);
+
+ return false;
+ /* Approach: read each object in the file, one by one. If the object is a
+ Container, add objects to it until an "endContainer" marker is found.
+ This will require a stack to maintain the current Container. */
#if 0
std::vector<Container *> containerStack;
return false;
// object->type down below can be replaced with objType.
- // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *, ObjectType *);
- // where the return value tells if it's a valid object, Object * returns the
- // reconstructed object and ObjectType * returns the object type.
+ // Above could be: bool FileIO::GetObjectFromFile(FILE *, Object *,
+ // ObjectType *); where the return value tells if it's a valid object,
+ // Object * returns the reconstructed object and ObjectType * returns
+ // the object type.
if (objectType == OTFEndOfFile)
{
}
-/*static*/ bool FileIO::GetObjectFromFile(FILE * file, Object * parent, Object ** object, int * objectType)
+/*static*/ bool FileIO::LoadVersion1_0(FILE * file, Container * drawing)
+{
+ // Approach: read each object in the file, one by one. If the object is a
+ // Container, add objects to it until an "endContainer" marker is found.
+ // This will require a stack to maintain the current Container.
+ std::vector<Container *> containerStack;
+ Container * currentTopContainer = drawing;
+
+ while (!feof(file))
+ {
+ // Reconstruct the object
+ Object * obj = GetObjectFromFile(file);
+
+ // objectFileType is set in GetObjectFromFile()...
+ if (objectFileType == OTFObject)
+ {
+ if (obj == NULL)
+ return false;
+
+ currentTopContainer->objects.push_back(obj);
+//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
+
+ // If the object is a container, push current TLC on the stack and
+ // set it as the new TLC
+ if (obj->type == OTContainer)
+ {
+ containerStack.push_back(currentTopContainer);
+ currentTopContainer = (Container *)obj;
+ }
+ }
+ else if (objectFileType == OTFContainerEnd)
+ {
+ // Container is done, so pop the stack to get back the previous TLC
+ currentTopContainer = containerStack.back();
+ containerStack.pop_back();
+ }
+ else if (objectFileType == OTFEndOfFile)
+ {
+//printf("Load: container size = %li\n", drawing->objects.size());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*static*/ bool FileIO::LoadVersion1_1(FILE * file, Container * drawing)
+{
+ // Approach: read each object in the file, one by one. If the object is a
+ // Container, add objects to it until an "endContainer" marker is found.
+ // This will require a stack to maintain the current Container.
+ std::vector<Container *> containerStack;
+ Container * currentTopContainer = drawing;
+
+ while (!feof(file))
+ {
+ // Reconstruct the object (extended format!)
+ Object * obj = GetObjectFromFile(file, true);
+
+ // objectFileType is set in GetObjectFromFile()...
+ if (objectFileType == OTFObject)
+ {
+ if (obj == NULL)
+ return false;
+
+ currentTopContainer->objects.push_back(obj);
+//printf("Load: Adding object. Container size = %li (%li)\n", drawing->objects.size(), currentTopContainer->objects.size());
+
+ // If the object is a container, push current TLC on the stack and
+ // set it as the new TLC
+ if (obj->type == OTContainer)
+ {
+ containerStack.push_back(currentTopContainer);
+ currentTopContainer = (Container *)obj;
+ }
+ }
+ else if (objectFileType == OTFContainerEnd)
+ {
+ // Container is done, so pop the stack to get back the previous TLC
+ currentTopContainer = containerStack.back();
+ containerStack.pop_back();
+ }
+ else if (objectFileType == OTFEndOfFile)
+ {
+//printf("Load: container size = %li\n", drawing->objects.size());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/*static*/ Object * FileIO::GetObjectFromFile(FILE * file, bool extended/*= false*/)
{
-#if 0
char buffer[256];
int foundLayer = 0;
- int num = fscanf(file, "%s", buffer);
- bool recognized = false;
- *object = NULL;
+ /*int num =*/ fscanf(file, "%s", buffer);
+ Object * obj = NULL;
+ objectFileType = OTFObject;
//printf("FileIO: fscanf returned %i, buffer = \"%s\"\n", num, buffer);
// The following fugliness is for troubleshooting. Can remove later.
if ((strcmp(buffer, "END") != 0) && (strcmp(buffer, "ENDCONTAINER") != 0))
{
errno = 0;
- num = fscanf(file, " %i ", &foundLayer);
+ /*num =*/ fscanf(file, " %i ", &foundLayer);
//printf("FileIO: fscanf returned %i, foundLayer = %i\n", num, foundLayer);
if (errno)
{
}
}
+ // Need to add pen attributes as well... do that for v1.1 :-P
if (strcmp(buffer, "LINE") == 0)
{
-//printf(" Found LINE.\n");
- recognized = true;
- Vector v1, v2;
- fscanf(file, "(%lf,%lf) (%lf,%lf)", &v1.x, &v1.y, &v2.x, &v2.y);
-//printf(" Number of params recognized: %i\n", n);
- *object = new Line(v1, v2, parent);
- *objectType = OTFLine;
+ Point p1, p2;
+ fscanf(file, "(%lf,%lf) (%lf,%lf)", &p1.x, &p1.y, &p2.x, &p2.y);
+ obj = (Object *)new Line(p1, p2);
}
else if (strcmp(buffer, "CIRCLE") == 0)
{
- recognized = true;
- Vector v;
+ Point p;
double r;
- fscanf(file, "(%lf,%lf) %lf", &v.x, &v.y, &r);
- *object = new Circle(v, r, parent);
- *objectType = OTFCircle;
+ fscanf(file, "(%lf,%lf) %lf", &p.x, &p.y, &r);
+ obj = (Object *)new Circle(p, r);
}
else if (strcmp(buffer, "ARC") == 0)
{
- recognized = true;
- Vector v;
+ Point p;
double r, a1, a2;
- fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &v.x, &v.y, &r, &a1, &a2);
- *object = new Arc(v, r, a1, a2, parent);
- *objectType = OTFArc;
+ fscanf(file, "(%lf,%lf) %lf, %lf, %lf", &p.x, &p.y, &r, &a1, &a2);
+ obj = (Object *)new Arc(p, r, a1, a2);
}
else if (strcmp(buffer, "DIMENSION") == 0)
{
- recognized = true;
- Vector v1, v2;
+ Point p1, p2;
DimensionType type;
- fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &v1.x, &v1.y, &v2.x, &v2.y, &type);
- *object = new Dimension(v1, v2, type, parent);
- *objectType = OTFDimension;
+ fscanf(file, "(%lf,%lf) (%lf,%lf) %i", &p1.x, &p1.y, &p2.x, &p2.y, (int *)&type);
+ obj = (Object *)new Dimension(p1, p2, type);
}
else if (strcmp(buffer, "CONTAINER") == 0)
{
- recognized = true;
- *objectType = OTFContainer;
+ obj = (Object *)new Container();
+// objectFileType = OTFContainer;
}
else if (strcmp(buffer, "ENDCONTAINER") == 0)
{
- recognized = true;
- *objectType = OTFContainerEnd;
+ objectFileType = OTFContainerEnd;
}
else if (strcmp(buffer, "END") == 0)
{
- recognized = true;
- *objectType = OTFEndOfFile;
+ objectFileType = OTFEndOfFile;
}
- if (*object)
- (*object)->layer = foundLayer;
+ if (obj != NULL)
+ {
+ obj->layer = foundLayer;
- return recognized;
-#else
- return false;
-#endif
+ if (extended && (obj->type != OTContainer))
+ {
+ fscanf(file, " (%i, %f, %i)", &obj->color, &obj->thickness, &obj->style);
+ }
+ }
+
+ return obj;
+}
+
+
+/*static*/ bool FileIO::WriteObjectToFile(FILE * file, Object * obj)
+{
+ // Sanity check
+ if (obj == NULL)
+ return false;
+
+ switch (obj->type)
+ {
+ case OTLine:
+ fprintf(file, "LINE %i (%lf,%lf) (%lf,%lf)", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y);
+ break;
+ case OTCircle:
+ fprintf(file, "CIRCLE %i (%lf,%lf) %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0]);
+ break;
+ case OTEllipse:
+ break;
+ case OTArc:
+ fprintf(file, "ARC %i (%lf,%lf) %lf, %lf, %lf", obj->layer, obj->p[0].x, obj->p[0].y, obj->radius[0], obj->angle[0], obj->angle[1]);
+ break;
+ case OTPolygon:
+ break;
+ case OTDimension:
+// fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i\n", layer, position.x, position.y, endpoint.x, endpoint.y, dimensionType);
+ fprintf(file, "DIMENSION %i (%lf,%lf) (%lf,%lf) %i", obj->layer, obj->p[0].x, obj->p[0].y, obj->p[1].x, obj->p[1].y, ((Dimension *)obj)->subtype);
+ break;
+ case OTSpline:
+ break;
+ case OTText:
+ fprintf(file, "TEXT %i (%lf,%lf) \"%s\"", obj->layer, obj->p[0].x, obj->p[0].y, ((Text *)obj)->s.c_str());
+ break;
+ case OTContainer:
+ {
+ Container * c = (Container *)obj;
+
+ if (c->topLevel == false)
+ fprintf(file, "CONTAINER %i\n", obj->layer);
+
+ std::vector<void *>::iterator i;
+
+ for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ WriteObjectToFile(file, (Object *)*i);
+
+ if (c->topLevel == false)
+ fprintf(file, "ENDCONTAINER\n");
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (obj->type != OTContainer)
+ fprintf(file, " (%i, %f, %i)\n", obj->color, obj->thickness, obj->style);
+
+ return true;
}
#include <stdio.h>
#include "structs.h"
-//class Container;
-//class Object;
// NB: The methods in this class are all static, so there's no need to
// instantiate an object of this type to use its functions.
static bool LoadAtnsFile(FILE *, Container *);
private:
- static bool GetObjectFromFile(FILE *, Object *, Object **, int *);
+ static bool LoadVersion1_0(FILE *, Container *);
+ static bool LoadVersion1_1(FILE *, Container *);
+// static bool GetObjectFromFile(FILE *, Object *, Object **, int *);
+ static Object * GetObjectFromFile(FILE *, bool extended = false);
+ static bool WriteObjectToFile(FILE *, Object *);
+
+ static int objectFileType;
};
#endif // __FILEIO_H__
// Radius is the hypotenuse, so we have to use c² = a² + b² => a² = c² - b²
double perpendicularLength = sqrt((c->radius[0] * c->radius[0]) - (distance * distance));
- // Now, find the points using the length, then check to see if they are on
- // the line segment
+ // Now, find the intersection points using the length...
Vector lineUnit = Vector(l->p[0], l->p[1]).Unit();
Point i1 = p + (lineUnit * perpendicularLength);
Point i2 = p - (lineUnit * perpendicularLength);
- // Now we have our intersection points, next we need to see if they are on
- // the line segment...
+ // Next we need to see if they are on the line segment...
double u = ParameterOfLineAndPoint(l->p[0], l->p[1], i1);
double v = ParameterOfLineAndPoint(l->p[0], l->p[1], i2);
--- /dev/null
+// objectwidget.cpp: Object tweaking widget
+//
+// Part of the Architektonas Project
+// (C) 2016 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 11/07/2016 Created this file
+//
+
+#include "objectwidget.h"
+#include "mathconstants.h"
+
+
+ObjectWidget::ObjectWidget(void): QWidget()
+{
+#if 0
+ QListWidget * qlw = new QListWidget;
+ QListWidgetItem * qli1 = new QListWidgetItem(qlw);
+ QListWidgetItem * qli2 = new QListWidgetItem(qlw);
+ QListWidgetItem * qli3 = new QListWidgetItem(qlw);
+ QListWidgetItem * qli4 = new QListWidgetItem(qlw);
+ QListWidgetItem * qli5 = new QListWidgetItem(qlw);
+#endif
+ label = new QLabel;
+
+#if 0
+ QPushButton * pb1 = new QPushButton("+");
+ QPushButton * pb2 = new QPushButton("-");
+ QPushButton * pb3 = new QPushButton("Edit");
+ QPushButton * pb4 = new QPushButton("Import");
+#else
+ QToolButton * pb1 = new QToolButton;
+ QToolButton * pb2 = new QToolButton;
+ QToolButton * pb3 = new QToolButton;
+ QToolButton * pb4 = new QToolButton;
+
+ pb1->setIcon(QIcon(":/res/layer-add.png"));
+ pb2->setIcon(QIcon(":/res/layer-delete.png"));
+ pb3->setIcon(QIcon(":/res/layer-edit.png"));
+ pb4->setIcon(QIcon(":/res/block-import.png"));
+
+ pb1->setToolTip(tr("Add block"));
+ pb2->setToolTip(tr("Remove block"));
+ pb3->setToolTip(tr("Edit block"));
+ pb4->setToolTip(tr("Import block"));
+#endif
+
+ QHBoxLayout * hbox1 = new QHBoxLayout;
+ hbox1->addWidget(pb1);
+ hbox1->addWidget(pb2);
+ hbox1->addWidget(pb3);
+ hbox1->addWidget(pb4);
+ hbox1->addStretch();
+
+ QVBoxLayout * mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(label);
+ mainLayout->addLayout(hbox1);
+
+ setLayout(mainLayout);
+}
+
+
+ObjectWidget::~ObjectWidget()
+{
+}
+
+
+void ObjectWidget::ShowInfo(Object * obj)
+{
+ const char objName[OTCount][16] = {
+ "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension", "Spline", "Text", "Container"
+ };
+
+ // Sanity check
+ if (obj == NULL)
+ return;
+
+ QString s = QString("%1<br><br>").arg(QString(objName[obj->type]));
+
+ switch (obj->type)
+ {
+ case OTLine:
+ {
+ Vector line(obj->p[0], obj->p[1]);
+ s += QString("<%1, %2> to <%3, %4><br>Length: %5<br>Angle: %6°<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->p[1].x).arg(obj->p[1].y).arg(line.Magnitude()).arg(line.Angle() * RADIANS_TO_DEGREES);
+ break;
+ }
+ case OTCircle:
+ s += QString("Center: <%1, %2><br>Radius: %3<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]);
+ break;
+ case OTEllipse:
+ break;
+ case OTArc:
+ s += QString("Center: <%1, %2><br>Radius: %3<br>Start: %4°<br>End: %5°<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]).arg(obj->angle[0] * RADIANS_TO_DEGREES).arg(obj->angle[1] * RADIANS_TO_DEGREES);
+ break;
+ break;
+ case OTPolygon:
+ break;
+ case OTDimension:
+ break;
+ case OTSpline:
+ break;
+ case OTText:
+ break;
+ case OTContainer:
+ break;
+ default:
+ break;
+ }
+
+ label->setText(s);
+}
+
--- /dev/null
+#ifndef __OBJECTWIDGET_H__
+#define __OBJECTWIDGET_H__
+
+#include <QtWidgets>
+#include "structs.h"
+
+
+class ObjectWidget: public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ObjectWidget(void);
+ ~ObjectWidget();
+
+ public slots:
+ void ShowInfo(Object *);
+
+ private:
+ QLabel * label;
+};
+
+#endif // __OBJECTWIDGET_H__
+
#include "mathconstants.h"
-// Set class variable defaults
-//Vector Painter::origin(-10.0, -10.0);
-//double Painter::zoom = 1.0;
-//Vector Painter::screenSize(200.0, 200.0);
-
-
Painter::Painter(QPainter * p/*= NULL*/): painter(p)
{
}
float yOffset = -12.0 * Global::zoom * size;
// Fix text so it isn't upside down...
-#if 0
- if ((angle > PI * 0.5) && (angle < PI * 1.5))
- {
- angle += PI;
- yOffset = 12.0 * Global::zoom * size;
- }
-#else
if ((angle > QTR_TAU) && (angle < THREE_QTR_TAU))
{
angle += HALF_TAU;
yOffset = 12.0 * Global::zoom * size;
}
-#endif
textBox.translate(0, yOffset);
painter->save();
void Painter::DrawArc(Vector center, double radius, double startAngle, double span)
{
+ if (!painter)
+ return;
+
center = CartesianToQtCoords(center);
// Need to multiply scalar quantities by the zoom factor as well...
radius *= Global::zoom;
void Painter::DrawEllipse(Vector center, double axis1, double axis2)
{
+ if (!painter)
+ return;
+
// Need to multiply scalar quantities by the zoom factor as well...
center = CartesianToQtCoords(center);
painter->drawEllipse(QPointF(center.x, center.y), axis1 * Global::zoom, axis2 * Global::zoom);
// we don't want our object handle size to depend on the zoom level!
void Painter::DrawHandle(Vector center)
{
+ if (!painter)
+ return;
+
center = CartesianToQtCoords(center);
painter->setPen(QPen(Qt::red, 2.0, Qt::DotLine));
painter->setBrush(Qt::NoBrush);
}
+// This function is for drawing feedback points without regard for zoom level;
+// we don't want our feedback point size to depend on the zoom level!
+void Painter::DrawCross(Vector point)
+{
+ if (!painter)
+ return;
+
+ point = CartesianToQtCoords(point);
+ painter->setPen(QPen(Qt::red, 2.0, Qt::SolidLine));
+ painter->drawLine(point.x - 8.0, point.y, point.x + 8.0, point.y);
+ painter->drawLine(point.x, point.y - 8.0, point.x, point.y + 8.0);
+}
+
+
+// This function is for drawing feedback points without regard for zoom level;
+// we don't want our feedback point size to depend on the zoom level!
+void Painter::DrawRectCorners(Rect rect)
+{
+ if (!painter)
+ return;
+
+// QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+
+ Vector v1 = CartesianToQtCoords(Vector(rect.l, rect.t));
+ Vector v2 = CartesianToQtCoords(Vector(rect.r, rect.b));
+// QRectF screenRect(QPointF(v1.x, v1.y), QPointF(v2.x, v2.y));
+// screenRect.adjust(-8, 8, 8, -8); // Left/top, right/bottom
+// painter->drawRect(screenRect);
+ v1 += Vector(-8.0, -8.0);
+ v2 += Vector(+8.0, +8.0);
+ painter->setPen(QPen(Qt::red, 2.0, Qt::DashLine));
+ painter->drawLine(v1.x, v1.y, v1.x + 24, v1.y);
+ painter->drawLine(v1.x, v1.y, v1.x, v1.y + 24);
+ painter->drawLine(v2.x, v1.y, v2.x - 24, v1.y);
+ painter->drawLine(v2.x, v1.y, v2.x, v1.y + 24);
+ painter->drawLine(v2.x, v2.y, v2.x - 24, v2.y);
+ painter->drawLine(v2.x, v2.y, v2.x, v2.y - 24);
+ painter->drawLine(v1.x, v2.y, v1.x + 24, v2.y);
+ painter->drawLine(v1.x, v2.y, v1.x, v2.y - 24);
+
+}
+
+
// This function is for drawing object handles without regard for zoom level;
// we don't want our object handle size to depend on the zoom level!
void Painter::DrawArrowHandle(Vector center, double angle)
{
+ if (!painter)
+ return;
+
center = CartesianToQtCoords(center);
QPolygonF arrow;
// Since we're drawing directly on the screen, the Y is inverted. So we use
// the mirror of the angle.
- double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
+ double orthoAngle = -angle + QTR_TAU;
Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
Vector unit = Vector(cos(-angle), sin(-angle));
// we don't want our object handle size to depend on the zoom level!
void Painter::DrawArrowToLineHandle(Vector center, double angle)
{
+ if (!painter)
+ return;
+
DrawArrowHandle(center, angle);
center = CartesianToQtCoords(center);
// Since we're drawing directly on the screen, the Y is inverted. So we use
// the mirror of the angle.
- double orthoAngle = -angle + QTR_TAU;//(PI / 2.0);
+ double orthoAngle = -angle + QTR_TAU;
Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
Vector unit = Vector(cos(-angle), sin(-angle));
// We draw the arrowhead aligned along the line from tail to head
double angle = Vector(head - tail).Angle();
- double orthoAngle = angle + QTR_TAU;//(PI / 2.0);
+ double orthoAngle = angle + QTR_TAU;
Vector orthogonal = Vector(cos(orthoAngle), sin(orthoAngle));
Vector unit = Vector(head - tail).Unit();
void Painter::DrawInformativeText(QString text)
{
+ if (!painter)
+ return;
+
painter->setFont(*Global::font);
QRectF bounds = painter->boundingRect(QRectF(), Qt::AlignVCenter, text);
bounds.moveTo(17.0, 17.0);
#include <stdint.h>
#include <QtWidgets>
+#include "rect.h"
#include "vector.h"
-//#define SCREEN_ZOOM (1.0 / 4.0)
-
// Forward declarations
class Painter
void DrawArc(Vector, double, double, double);
void DrawEllipse(Vector, double, double);
void DrawHandle(Vector);
+ void DrawCross(Vector);
+ void DrawRectCorners(Rect);
void DrawArrowHandle(Vector, double);
void DrawArrowToLineHandle(Vector, double);
void DrawLine(int, int, int, int);
static Vector CartesianToQtCoords(Vector);
static Vector QtToCartesianCoords(Vector);
- public:
- // Class variables
-// static Vector origin; // The window origin, not location of the origin
-// static double zoom; // Window zoom factor
-// static Vector screenSize; // Width & height of the window we're drawing on
-
private:
QPainter * painter;
};
#endif // __PAINTER_H__
+
--- /dev/null
+//
+// rect.cpp: Rectangle object implementation
+//
+// Part of the Architektonas Project
+// (C) 2016 Underground Software
+// See the README and GPLv3 files for licensing and warranty information
+//
+// JLH = James Hammons <jlhamm@acm.org>
+//
+// WHO WHEN WHAT
+// --- ---------- ------------------------------------------------------------
+// JLH 11/10/2016 Created this file
+//
+
+#include "rect.h"
+//#include <stdio.h>
+
+
+Rect::Rect(): l(0), r(0), t(0), b(0)
+{
+}
+
+
+Rect::Rect(double ll, double rr, double tt, double bb):
+ l(ll), r(rr), t(tt), b(bb)
+{
+ Normalize();
+}
+
+
+Rect::Rect(Point tl, Point br): l(tl.x), r(br.x), t(tl.y), b(br.y)
+{
+ Normalize();
+}
+
+
+Rect & Rect::operator*=(double scale)
+{
+ l *= scale;
+ r *= scale;
+ t *= scale;
+ b *= scale;
+ return *this;
+}
+
+
+Rect & Rect::operator|=(Rect r2)
+{
+//printf("operatore|=\nthis = (%lf, %lf, %lf, %lf), r = (%lf, %lf, %lf, %lf)\n", l, t, r, b, r2.l, r2.t, r2.r, r2.b);
+ if (r2.l < l)
+ l = r2.l;
+
+ if (r2.r > r)
+ r = r2.r;
+
+ if (r2.t > t)
+ t = r2.t;
+
+ if (r2.b < b)
+ b = r2.b;
+
+ return *this;
+}
+
+
+void Rect::Normalize(void)
+{
+ if (l > r)
+ {
+ double x = l;
+ l = r;
+ r = x;
+ }
+
+ if (b > t)
+ {
+ double x = b;
+ b = t;
+ t = x;
+ }
+}
+
+
+void Rect::Translate(Point p)
+{
+ l += p.x;
+ r += p.x;
+ t += p.y;
+ b += p.y;
+}
+
+
+void Rect::Expand(double amt)
+{
+ l -= amt;
+ r += amt;
+ t += amt;
+ b -= amt;
+}
+
--- /dev/null
+#ifndef __RECT_H__
+#define __RECT_H__
+
+//
+// We're doing this because the Qt implementation is non-Cartesian compliant.
+// Also, it auto-normalizes rects constructed using the constructors. :-)
+//
+
+#include "vector.h"
+
+struct Rect
+{
+ double l, r, t, b;
+
+ Rect();
+ Rect(double ll, double rr, double tt, double bb);
+ Rect(Point tl, Point br);
+ Rect & operator*=(double scale);
+ Rect & operator|=(Rect x);
+ void Normalize(void);
+ void Translate(Point p);
+ void Expand(double amt);
+};
+
+#endif // __RECT_H__
+
#include "global.h"
#include "vector.h"
-enum ObjectType { OTNone, OTLine, OTCircle, OTEllipse, OTArc, OTPolygon, OTDimension, OTSpline, OTText, OTContainer };
+enum ObjectType { OTNone = 0, OTLine, OTCircle, OTEllipse, OTArc, OTPolygon, OTDimension, OTSpline, OTText, OTContainer, OTCount };
enum DimensionType { DTLinear, DTLinearVert, DTLinearHorz, DTRadial, DTDiametric, DTCircumferential, DTAngular, DTLeader };
OBJECT_COMMON;
std::vector<void *> objects;
double scale;
+ bool topLevel;
- Container(): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false) {}
+ Container(bool tl = false): type(OTContainer), id(Global::objectID++), selected(false), hovered(false), hitObject(false), topLevel(tl) {}
+// void DeleteContents(void) {}
+/* void DeleteContents(Container * c)
+ {
+ std::vector<void *>::iterator i;
+
+ for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ {
+ Object * obj = (Object *)(*i);
+
+ if (obj->type == OTContainer)
+ DeleteContainer((Container *)obj);
+
+ delete *i;
+ }
+ }*/
};
struct Object {
OBJECT_COMMON;
};
-
#endif // __STRUCTS_H__
}
+void MoveSelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
+{
+ std::vector<void *>::iterator i = from.begin();
+
+ while (i != from.end())
+ {
+ Object * obj = (Object *)(*i);
+
+ if (obj->selected)
+ {
+ dest.push_back(*i);
+ from.erase(i);
+ }
+ else
+ i++;
+ }
+}
+
+
void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from)
{
for(std::vector<void *>::iterator i=from.begin(); i!=from.end(); i++)
}
+void SelectAll(std::vector<void *> & v)
+{
+ std::vector<void *>::iterator i;
+
+ for(i=v.begin(); i!=v.end(); i++)
+ ((Object *)(*i))->selected = true;
+}
+
+
+//
+// Recursively go down thru the Container's vectors, deleting all the objects
+// contained therein. Once that is done, the main Container can be deleted. We
+// don't have to worry about the underlying std::vectors, as they have their
+// own destructors--plus they don't take ownership of objects, which is why we
+// have to keep track of that stuff ourselves. :-P Believe it or not, this is a
+// Good Thing(TM). ;-)
+//
+void DeleteContents(std::vector<void *> & v)
+{
+ std::vector<void *>::iterator i;
+
+ for(i=v.begin(); i!=v.end(); i++)
+ {
+ Object * obj = (Object *)(*i);
+
+ if (obj->type == OTContainer)
+ DeleteContents(((Container *)obj)->objects);
+
+ delete obj;
+ }
+}
+
void DeleteSelectedObjects(std::vector<void *> & v)
{
std::vector<void *>::iterator i = v.begin();
}
+//
+// This is used to remove selected objects from one container in order to move
+// them to a different container.
+//
+void RemoveSelectedObjects(std::vector<void *> & v)
+{
+ std::vector<void *>::iterator i = v.begin();
+
+ while (i != v.end())
+ {
+ Object * obj = (Object *)(*i);
+
+ if (obj->selected)
+ v.erase(i);
+ else
+ i++;
+ }
+}
+
+
void SavePointsFrom(std::vector<void *> & v, std::vector<Object> & save)
{
save.clear();
}
+void TranslateObject(Object * obj, Point delta)
+{
+ if (obj->type == OTContainer)
+ {
+ Container * c = (Container *)obj;
+ std::vector<void *>::iterator i;
+
+ for(i=c->objects.begin(); i!=c->objects.end(); i++)
+ TranslateObject((Object *)*i, delta);
+ }
+ else
+ {
+ obj->p[0] += delta;
+ obj->p[1] += delta;
+ }
+}
+
+
void CopyObjects(std::vector<void *> & from, std::vector<void *> & to);
Object * CopyObject(Object * obj);
+void MoveSelectedObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
void AddObjectsTo(std::vector<void *> & dest, std::vector<void *> & from);
void ClearSelected(std::vector<void *> & v);
+void SelectAll(std::vector<void *> & v);
+void DeleteContents(std::vector<void *> & v);
void DeleteSelectedObjects(std::vector<void *> & v);
+void RemoveSelectedObjects(std::vector<void *> & v);
void SavePointsFrom(std::vector<void *> & v, std::vector<Object> & s);
void RestorePointsTo(std::vector<void *> & v, std::vector<Object> & s);
+void TranslateObject(Object * obj, Point delta);
#endif // __UTILS_H__