X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fdrawingview.cpp;h=5ae9c9a9081e5ccf121c82958eac22e6634d6400;hb=6a7baa2814a8b4d0b93df776a4c99689bcfb3ffa;hp=21c85dd48236cacb7d1cac4904ab5403af51ff77;hpb=e06428cc60e07d9ef33af8b559d60f23894130fb;p=architektonas diff --git a/src/drawingview.cpp b/src/drawingview.cpp index 21c85dd..5ae9c9a 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -20,6 +20,7 @@ // STILL TO BE DONE: // // - Lots of stuff +// - Layer locking (hiding works) // // Uncomment this for debugging... @@ -35,12 +36,11 @@ #include "mathconstants.h" #include "painter.h" #include "structs.h" +#include "utils.h" #define BACKGROUND_MAX_SIZE 512 -enum { ToolMouseDown, ToolMouseMove, ToolMouseUp }; - // Class variable //Container DrawingView::document(Vector(0, 0)); @@ -50,8 +50,8 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent), useAntialiasing(true), numSelected(0), numHovered(0), shiftDown(false), ctrlDown(false), gridBackground(BACKGROUND_MAX_SIZE, BACKGROUND_MAX_SIZE), - scale(1.0), offsetX(-10), offsetY(-10),// document(Vector(0, 0)), - gridPixels(0), collided(false)//, toolAction(NULL) + scale(1.0), offsetX(-10), offsetY(-10), + gridPixels(0), collided(false), hoveringIntersection(false) { // document.isTopLevelContainer = true; //wtf? doesn't work except in c++11??? document = { 0 }; @@ -79,12 +79,13 @@ DrawingView::DrawingView(QWidget * parent/*= NULL*/): QWidget(parent), #endif #else Line * line = new Line;//(Vector(5, 5), Vector(50, 40), &document); - line->p1 = Vector(5, 5); - line->p2 = Vector(50, 40); + line->p[0] = Vector(5, 5); + line->p[1] = Vector(50, 40); line->type = OTLine; line->thickness = 2.0; line->style = LSDash; line->color = 0xFF7F00; + line->layer = 0; document.objects.push_back(line); document.objects.push_back(new Line(Vector(50, 40), Vector(10, 83))); document.objects.push_back(new Line(Vector(10, 83), Vector(17, 2))); @@ -266,28 +267,74 @@ zero; so we do another modulus operation on the result to achieve this. } -void DrawingView::AddNewObjectToDocument(Object * object) +void DrawingView::SetCurrentLayer(int /*layer*/) +{ +//Not needed anymore... +// Global::currentLayer = layer; +//printf("DrawingView::CurrentLayer = %i\n", layer); +} + + +// +// Basically, we just make a single pass through the Container. If the layer # +// is less than the layer # being deleted, then do nothing. If the layer # is +// equal to the layer # being deleted, then delete the object. If the layer # +// is greater than the layer # being deleted, then set the layer # to its layer +// # - 1. +// +void DrawingView::DeleteCurrentLayer(int layer) { - if (object) +//printf("DrawingView::DeleteCurrentLayer(): currentLayer = %i\n", layer); + std::vector::iterator i = document.objects.begin(); + + while (i != document.objects.end()) { -// object->Reparent(&document); -// document.Add(object); - update(); + Object * obj = (Object *)(*i); + + if (obj->layer < layer) + i++; + else if (obj->layer == layer) + { + document.objects.erase(i); + delete obj; + } + else + { + obj->layer--; + i++; + } } -//printf("DrawingView::AddNewObjectToDocument(). object=%08X\n", object); + + // We've just done a destructive action, so update the screen! + update(); } -void DrawingView::HandleActionUpdate(void) +void DrawingView::HandleLayerToggle(void) { + // A layer's visibility was toggled, so update the screen... update(); } -void DrawingView::SetCurrentLayer(int layer) +// +// A layer was moved up or down in the layer list, so we have to swap the +// document's object's layer numbers in the layers that were swapped. +// +void DrawingView::HandleLayerSwap(int layer1, int layer2) { - Global::currentLayer = layer; -//printf("DrawingView::CurrentLayer = %i\n", layer); +//printf("DrawingView: Swapping layers %i and %i.\n", layer1, layer2); + std::vector::iterator i; + + for(i=document.objects.begin(); i!=document.objects.end(); i++) + { + Object * obj = (Object *)(*i); + + if (obj->layer == layer1) + obj->layer = layer2; + else if (obj->layer == layer2) + obj->layer = layer1; + } } @@ -324,88 +371,107 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/) painter.DrawLine(0, -16384, 0, 16384); painter.DrawLine(-16384, 0, 16384, 0); - // The top level document takes care of rendering for us... -// document.Draw(&painter); - // Not any more it doesn't... - RenderObjects(&painter, &document); - -#if 0 - if (toolAction) + // Do object rendering... + for(int i=0; iDraw(&painter); + if (Global::layerHidden[i] == false) + RenderObjects(&painter, document.objects, i); } -#else + + // Do tool rendering, if any... if (Global::tool) { painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine)); painter.DrawCrosshair(oldPoint); ToolDraw(&painter); } -#endif -#if 1 + // Do selection rectangle rendering, if any if (Global::selectionInProgress) { painter.SetPen(QPen(QColor(255, 127, 0, 255))); painter.SetBrush(QBrush(QColor(255, 127, 0, 100))); painter.DrawRect(Global::selection); } -#endif + + if (hoveringIntersection) + painter.DrawHandle(intersectionPoint); + + if (!informativeText.isEmpty()) + painter.DrawInformativeText(informativeText); } -void DrawingView::RenderObjects(Painter * painter, Container * c) +// +// Renders objects in the passed in vector +// +void DrawingView::RenderObjects(Painter * painter, std::vector & v, int layer) { std::vector::iterator i; - for(i=c->objects.begin(); i!=c->objects.end(); i++) + for(i=v.begin(); i!=v.end(); i++) { Object * obj = (Object *)(*i); float scaledThickness = Global::scale * obj->thickness; - painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style); - painter->SetBrush(obj->color); - if (obj->selected || obj->hitObject) - painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash); + // If the object isn't on the current layer being drawn, skip it + if (obj->layer != layer) + continue; + + if ((Global::tool == TTRotate) && ctrlDown && obj->selected) + { + painter->SetPen(0x00FF00, 2.0, LSSolid); + } + else + { + painter->SetPen(obj->color, Global::zoom * scaledThickness, obj->style); + painter->SetBrush(obj->color); + + if (obj->selected || obj->hitObject) + painter->SetPen(0xFF0000, Global::zoom * scaledThickness, LSDash); + } switch (obj->type) { case OTLine: - { - Line * l = (Line *)obj; - painter->DrawLine(l->p1, l->p2); + painter->DrawLine(obj->p[0], obj->p[1]); - if (l->hitPoint[0]) - painter->DrawHandle(l->p1); + if (obj->hitPoint[0]) + painter->DrawHandle(obj->p[0]); - if (l->hitPoint[1]) - painter->DrawHandle(l->p2); + if (obj->hitPoint[1]) + painter->DrawHandle(obj->p[1]); break; - } case OTCircle: - { - Circle * ci = (Circle *)obj; painter->SetBrush(QBrush(Qt::NoBrush)); - painter->DrawEllipse(ci->p1, ci->radius, ci->radius); + painter->DrawEllipse(obj->p[0], obj->radius[0], obj->radius[0]); + + if (obj->hitPoint[0]) + painter->DrawHandle(obj->p[0]); + break; - } case OTArc: - { - Arc * a = (Arc *)obj; - painter->DrawArc(a->p1, a->radius, a->angle1, a->angle2); + painter->DrawArc(obj->p[0], obj->radius[0], obj->angle[0], obj->angle[1]); + + if (obj->hitPoint[0]) + painter->DrawHandle(obj->p[0]); + + if (obj->hitPoint[1]) + painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0]), sin(obj->angle[0])) * obj->radius[0])); + + if (obj->hitPoint[2]) + painter->DrawHandle(obj->p[0] + (Vector(cos(obj->angle[0] + obj->angle[1]), sin(obj->angle[0] + obj->angle[1])) * obj->radius[0])); + break; - } case OTDimension: { Dimension * d = (Dimension *)obj; - Vector v(d->p1, d->p2); + Vector v(d->p[0], d->p[1]); double angle = v.Angle(); Vector unit = v.Unit(); - Vector linePt1 = d->p1, linePt2 = d->p2; + Vector linePt1 = d->p[0], linePt2 = d->p[1]; Vector ortho; double x1, y1, length; @@ -413,41 +479,41 @@ void DrawingView::RenderObjects(Painter * painter, Container * c) { if ((angle < 0) || (angle > PI)) { - x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x); - y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y); + x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x); + y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y); ortho = Vector(1.0, 0); angle = PI3_OVER_2; } else { - x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x); - y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y); + x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x); + y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y); ortho = Vector(-1.0, 0); angle = PI_OVER_2; } linePt1.x = linePt2.x = x1; - length = fabs(d->p1.y - d->p2.y); + length = fabs(d->p[0].y - d->p[1].y); } else if (d->subtype == DTLinearHorz) { if ((angle < PI_OVER_2) || (angle > PI3_OVER_2)) { - x1 = (d->p1.x > d->p2.x ? d->p1.x : d->p2.x); - y1 = (d->p1.y > d->p2.y ? d->p1.y : d->p2.y); + x1 = (d->p[0].x > d->p[1].x ? d->p[0].x : d->p[1].x); + y1 = (d->p[0].y > d->p[1].y ? d->p[0].y : d->p[1].y); ortho = Vector(0, 1.0); angle = 0; } else { - x1 = (d->p1.x > d->p2.x ? d->p2.x : d->p1.x); - y1 = (d->p1.y > d->p2.y ? d->p2.y : d->p1.y); + x1 = (d->p[0].x > d->p[1].x ? d->p[1].x : d->p[0].x); + y1 = (d->p[0].y > d->p[1].y ? d->p[1].y : d->p[0].y); ortho = Vector(0, -1.0); angle = PI; } linePt1.y = linePt2.y = y1; - length = fabs(d->p1.x - d->p2.x); + length = fabs(d->p[0].x - d->p[1].x); } else if (d->subtype == DTLinear) { @@ -462,8 +528,8 @@ void DrawingView::RenderObjects(Painter * painter, Container * c) Point p2 = linePt2 + (ortho * 10.0 * scaledThickness); Point p3 = linePt1 + (ortho * 16.0 * scaledThickness); Point p4 = linePt2 + (ortho * 16.0 * scaledThickness); - Point p5 = d->p1 + (ortho * 4.0 * scaledThickness); - Point p6 = d->p2 + (ortho * 4.0 * scaledThickness); + Point p5 = d->p[0] + (ortho * 4.0 * scaledThickness); + Point p6 = d->p[1] + (ortho * 4.0 * scaledThickness); /* The numbers hardcoded into here, what are they? @@ -478,8 +544,8 @@ void DrawingView::RenderObjects(Painter * painter, Container * c) double t = Geometry::ParameterOfLineAndPoint(linePt1, linePt2, linePt2 - (unit * 9.0 * scaledThickness)); //printf("Dimension::Draw(): t = %lf\n", t); - // On the screen, it's acting like this is actually 58%... - // This is correct, we want it to happen at > 50% + // On the screen, it's acting like this is actually 58%... + // This is correct, we want it to happen at > 50% if (t > 0.58) { // Draw main dimension line + arrowheads @@ -528,7 +594,7 @@ void DrawingView::RenderObjects(Painter * painter, Container * c) case OTText: { Text * t = (Text *)obj; - painter->DrawTextObject(t->p1, t->s.c_str(), scaledThickness); + painter->DrawTextObject(t->p[0], t->s.c_str(), scaledThickness); break; } default: @@ -538,34 +604,6 @@ void DrawingView::RenderObjects(Painter * painter, Container * c) } -void DrawingView::DeleteSelectedItems(void) -{ - std::vector::iterator i = document.objects.begin(); - - while (i != document.objects.end()) - { - Object * obj = (Object *)(*i); - - if (obj->selected) - { - delete obj; - document.objects.erase(i); - } - else - i++; - } -} - - -void DrawingView::ClearSelection(void) -{ - std::vector::iterator i; - - for(i=document.objects.begin(); i!=document.objects.end(); i++) - ((Object *)(*i))->selected = false; -} - - void DrawingView::AddHoveredToSelection(void) { std::vector::iterator i; @@ -614,10 +652,18 @@ void DrawingView::resizeEvent(QResizeEvent * /*event*/) } -void DrawingView::ToolMouse(int mode, Point p) +void DrawingView::ToolHandler(int mode, Point p) { if (Global::tool == TTLine) LineHandler(mode, p); + else if (Global::tool == TTCircle) + CircleHandler(mode, p); + else if (Global::tool == TTArc) + ArcHandler(mode, p); + else if (Global::tool == TTRotate) + RotateHandler(mode, p); + else if (Global::tool == TTMirror) + MirrorHandler(mode, p); } @@ -642,8 +688,142 @@ void DrawingView::ToolDraw(Painter * painter) double absAngle = v.Angle() * RADIANS_TO_DEGREES; double absLength = v.Magnitude(); QString text = tr("Length: %1 in.\n") + QChar(0x2221) + tr(": %2"); - text = text.arg(absLength).arg(absAngle); - painter->DrawInformativeText(text); + informativeText = text.arg(absLength).arg(absAngle); + } + } + else if (Global::tool == TTCircle) + { + if (Global::toolState == TSNone) + { + painter->DrawHandle(toolPoint[0]); + } + else if ((Global::toolState == TSPoint2) && shiftDown) + { + painter->DrawHandle(toolPoint[1]); + } + else + { + 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); + } + } + else if (Global::tool == TTArc) + { + if (Global::toolState == TSNone) + { + painter->DrawHandle(toolPoint[0]); + } + else if (Global::toolState == TSPoint2) + { + double length = Vector::Magnitude(toolPoint[0], toolPoint[1]); + painter->SetBrush(QBrush(Qt::NoBrush)); + painter->DrawEllipse(toolPoint[0], length, length); + painter->DrawLine(toolPoint[0], toolPoint[1]); + painter->DrawHandle(toolPoint[1]); + QString text = tr("Radius: %1 in."); + informativeText = text.arg(length); + } + else if (Global::toolState == TSPoint3) + { + double angle = Vector::Angle(toolPoint[0], toolPoint[2]); + painter->DrawLine(toolPoint[0], toolPoint[2]); + painter->SetBrush(QBrush(Qt::NoBrush)); + painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x); + painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x)); + QString text = tr("Angle start: %1") + QChar(0x00B0); + informativeText = text.arg(RADIANS_TO_DEGREES * angle); + } + else + { + double angle = Vector::Angle(toolPoint[0], toolPoint[3]); + double span = angle - toolPoint[2].x; + + if (span < 0) + span += PI_TIMES_2; + + painter->DrawLine(toolPoint[0], toolPoint[3]); + painter->SetBrush(QBrush(Qt::NoBrush)); + painter->DrawEllipse(toolPoint[0], toolPoint[1].x, toolPoint[1].x); + painter->SetPen(0xFF00FF, 2.0, LSSolid); + painter->DrawArc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span); + painter->DrawHandle(toolPoint[0] + (Vector(cos(angle), sin(angle)) * toolPoint[1].x)); + QString text = tr("Arc span: %1") + QChar(0x00B0); + informativeText = text.arg(RADIANS_TO_DEGREES * span); + } + } + else if (Global::tool == TTRotate) + { + if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1)) + painter->DrawHandle(toolPoint[0]); + else if ((Global::toolState == TSPoint2) && shiftDown) + painter->DrawHandle(toolPoint[1]); + else + { + if (toolPoint[0] == toolPoint[1]) + 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"); + informativeText = text.arg(absAngle); + + if (ctrlDown) + informativeText += " (Copy)"; + } + } + else if (Global::tool == TTMirror) + { + if ((Global::toolState == TSNone) || (Global::toolState == TSPoint1)) + painter->DrawHandle(toolPoint[0]); + else if ((Global::toolState == TSPoint2) && shiftDown) + painter->DrawHandle(toolPoint[1]); + else + { + if (toolPoint[0] == toolPoint[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; + + if (absAngle > 180.0) + absAngle -= 180.0; + + QString text = QChar(0x2221) + QObject::tr(": %1"); + informativeText = text.arg(absAngle); + + if (ctrlDown) + informativeText += " (Copy)"; } } } @@ -683,6 +863,7 @@ void DrawingView::LineHandler(int mode, Point p) else { Line * l = new Line(toolPoint[0], toolPoint[1]); + l->layer = Global::activeLayer; document.objects.push_back(l); toolPoint[0] = toolPoint[1]; } @@ -690,6 +871,325 @@ void DrawingView::LineHandler(int mode, Point p) } +void DrawingView::CircleHandler(int mode, Point p) +{ + switch (mode) + { + case ToolMouseDown: + if (Global::toolState == TSNone) + toolPoint[0] = p; + else + toolPoint[1] = p; + + break; + case ToolMouseMove: + if (Global::toolState == TSNone) + toolPoint[0] = p; + else + toolPoint[1] = p; + + break; + case ToolMouseUp: + if (Global::toolState == TSNone) + { + Global::toolState = TSPoint2; + // Prevent spurious line from drawing... + toolPoint[1] = toolPoint[0]; + } + else if ((Global::toolState == TSPoint2) && shiftDown) + { + // Key override is telling us to make a new line, not continue the + // previous one. + toolPoint[0] = toolPoint[1]; + } + else + { + double length = Vector::Magnitude(toolPoint[0], toolPoint[1]); + Circle * c = new Circle(toolPoint[0], length); + c->layer = Global::activeLayer; + document.objects.push_back(c); + toolPoint[0] = toolPoint[1]; + Global::toolState = TSNone; + } + } +} + + +void DrawingView::ArcHandler(int mode, Point p) +{ + switch (mode) + { + case ToolMouseDown: + if (Global::toolState == TSNone) + toolPoint[0] = p; + else if (Global::toolState == TSPoint2) + toolPoint[1] = p; + else if (Global::toolState == TSPoint3) + toolPoint[2] = p; + else + toolPoint[3] = p; + + break; + case ToolMouseMove: + if (Global::toolState == TSNone) + toolPoint[0] = p; + else if (Global::toolState == TSPoint2) + toolPoint[1] = p; + else if (Global::toolState == TSPoint3) + toolPoint[2] = p; + else + toolPoint[3] = p; + + break; + case ToolMouseUp: + if (Global::toolState == TSNone) + { + // Prevent spurious line from drawing... + toolPoint[1] = toolPoint[0]; + Global::toolState = TSPoint2; + } + else if (Global::toolState == TSPoint2) + { + if (shiftDown) + { + // Key override is telling us to start circle at new center, not + // continue the current one. + toolPoint[0] = toolPoint[1]; + return; + } + + // Set the radius in toolPoint[1].x + toolPoint[1].x = Vector::Magnitude(toolPoint[0], toolPoint[1]); + Global::toolState = TSPoint3; + } + else if (Global::toolState == TSPoint3) + { + // Set the angle in toolPoint[2].x + toolPoint[2].x = Vector::Angle(toolPoint[0], toolPoint[2]); + Global::toolState = TSPoint4; + } + else + { + double endAngle = Vector::Angle(toolPoint[0], toolPoint[3]); + double span = endAngle - toolPoint[2].x; + + if (span < 0) + span += PI_TIMES_2; + + Arc * arc = new Arc(toolPoint[0], toolPoint[1].x, toolPoint[2].x, span); + arc->layer = Global::activeLayer; + document.objects.push_back(arc); + Global::toolState = TSNone; + } + } +} + + +void DrawingView::RotateHandler(int mode, Point p) +{ + switch (mode) + { + case ToolMouseDown: + if (Global::toolState == TSNone) + { + toolPoint[0] = p; + SavePointsFrom(select, toolScratch); + Global::toolState = TSPoint1; + } + else if (Global::toolState == TSPoint1) + toolPoint[0] = p; + else + toolPoint[1] = p; + + break; + case ToolMouseMove: + if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone)) + toolPoint[0] = p; + else if (Global::toolState == TSPoint2) + { + toolPoint[1] = p; + + if (shiftDown) + return; + + double angle = Vector(toolPoint[0], toolPoint[1]).Angle(); + std::vector::iterator j = select.begin(); + std::vector::iterator i = toolScratch.begin(); + + for(; i!=toolScratch.end(); i++, j++) + { + Object obj = *i; + Point p1 = Geometry::RotatePointAroundPoint(obj.p[0], toolPoint[0], angle); + Point p2 = Geometry::RotatePointAroundPoint(obj.p[1], toolPoint[0], angle); + Object * obj2 = (Object *)(*j); + obj2->p[0] = p1; + obj2->p[1] = p2; + + if (obj.type == OTArc) + { + obj2->angle[0] = obj.angle[0] + angle; + + if (obj2->angle[0] > PI_TIMES_2) + obj2->angle[0] -= PI_TIMES_2; + } + } + } + + break; + case ToolMouseUp: + if (Global::toolState == TSPoint1) + { + Global::toolState = TSPoint2; + // Prevent spurious line from drawing... + toolPoint[1] = toolPoint[0]; + } + else if ((Global::toolState == TSPoint2) && shiftDown) + { + // Key override is telling us to make a new line, not continue the + // previous one. + toolPoint[0] = toolPoint[1]; + } + else + { + // Either we're finished with our rotate, or we're stamping a copy. + if (ctrlDown) + { + // Stamp a copy of the selection at the current rotation & bail + std::vector temp; + CopyObjects(select, temp); + ClearSelected(temp); + AddObjectsTo(document.objects, temp); + RestorePointsTo(select, toolScratch); + return; + } + + toolPoint[0] = p; + Global::toolState = TSPoint1; + SavePointsFrom(select, toolScratch); + } + + break; + case ToolKeyDown: + // Reset the selection if shift held down... + if (shiftDown) + RestorePointsTo(select, toolScratch); + + break; + case ToolKeyUp: + // Reset selection when key is let up + if (!shiftDown) + RotateHandler(ToolMouseMove, toolPoint[1]); + + break; + case ToolCleanup: + RestorePointsTo(select, toolScratch); + } +} + + +void DrawingView::MirrorHandler(int mode, Point p) +{ + switch (mode) + { + case ToolMouseDown: + if (Global::toolState == TSNone) + { + toolPoint[0] = p; + SavePointsFrom(select, toolScratch); + Global::toolState = TSPoint1; + } + else if (Global::toolState == TSPoint1) + toolPoint[0] = p; + else + toolPoint[1] = p; + + break; + case ToolMouseMove: + if ((Global::toolState == TSPoint1) || (Global::toolState == TSNone)) + toolPoint[0] = p; + else if (Global::toolState == TSPoint2) + { + toolPoint[1] = p; + + if (shiftDown) + return; + + double angle = Vector(toolPoint[0], toolPoint[1]).Angle(); + std::vector::iterator j = select.begin(); + std::vector::iterator i = toolScratch.begin(); + + for(; i!=toolScratch.end(); i++, j++) + { + Object obj = *i; + Point p1 = Geometry::MirrorPointAroundLine(obj.p[0], toolPoint[0], toolPoint[1]); + Point p2 = Geometry::MirrorPointAroundLine(obj.p[1], toolPoint[0], toolPoint[1]); + Object * obj2 = (Object *)(*j); + obj2->p[0] = p1; + obj2->p[1] = p2; + + if (obj.type == OTArc) + { + // This is 2*mirror angle - obj angle - obj span + obj2->angle[0] = (2.0 * angle) - obj.angle[0] - obj.angle[1]; + + if (obj2->angle[0] > PI_TIMES_2) + obj2->angle[0] -= PI_TIMES_2; + } + } + } + + break; + case ToolMouseUp: + if (Global::toolState == TSPoint1) + { + Global::toolState = TSPoint2; + // Prevent spurious line from drawing... + toolPoint[1] = toolPoint[0]; + } + else if ((Global::toolState == TSPoint2) && shiftDown) + { + // Key override is telling us to make a new line, not continue the + // previous one. + toolPoint[0] = toolPoint[1]; + } + else + { + // Either we're finished with our rotate, or we're stamping a copy. + if (ctrlDown) + { + // Stamp a copy of the selection at the current rotation & bail + std::vector temp; + CopyObjects(select, temp); + ClearSelected(temp); + AddObjectsTo(document.objects, temp); + RestorePointsTo(select, toolScratch); + return; + } + + toolPoint[0] = p; + Global::toolState = TSPoint1; + SavePointsFrom(select, toolScratch); + } + + break; + case ToolKeyDown: + // Reset the selection if shift held down... + if (shiftDown) + RestorePointsTo(select, toolScratch); + + break; + case ToolKeyUp: + // Reset selection when key is let up + if (!shiftDown) + MirrorHandler(ToolMouseMove, toolPoint[1]); + + break; + case ToolCleanup: + RestorePointsTo(select, toolScratch); + } +} + + void DrawingView::mousePressEvent(QMouseEvent * event) { if (event->button() == Qt::LeftButton) @@ -708,13 +1208,14 @@ void DrawingView::mousePressEvent(QMouseEvent * event) // if (Global::snapPointIsValid) // point = Global::snapPoint; - ToolMouse(ToolMouseDown, point); + ToolHandler(ToolMouseDown, point); return; } // Clear the selection only if CTRL isn't being held on click if (!ctrlDown) - ClearSelection(); + ClearSelected(document.objects); +// ClearSelection(); // If any objects are being hovered on click, add them to the selection // & return @@ -725,6 +1226,7 @@ void DrawingView::mousePressEvent(QMouseEvent * event) GetHovered(hover); // prolly needed // Needed for grab & moving objects + // We do it *after*... why? (doesn't seem to confer any advantage...) if (Global::snapToGrid) oldPoint = SnapPointToGrid(point); @@ -752,6 +1254,7 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event) Global::selection.setBottomRight(QPointF(point.x, point.y)); // Only needs to be done here, as mouse down is always preceded by movement Global::snapPointIsValid = false; + hoveringIntersection = false; // Scrolling... if (event->buttons() & Qt::MiddleButton) @@ -780,38 +1283,12 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event) } // Handle object movement (left button down & over an object) - if ((event->buttons() & Qt::LeftButton) && numHovered) + if ((event->buttons() & Qt::LeftButton) && numHovered && !Global::tool) { if (Global::snapToGrid) point = SnapPointToGrid(point); - Point delta = point - oldPoint; - Object * obj = (Object *)hover[0]; -//printf("Object type = %i (size=%i), ", obj->type, hover.size()); -//printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false")); - - switch (obj->type) - { - case OTLine: - { - Line * l = (Line *)obj; - - if (l->hitPoint[0]) - l->p1 = point; - else if (l->hitPoint[1]) - l->p2 = point; - else if (l->hitObject) - { - l->p1 += delta; - l->p2 += delta; - } - - break; - } - default: - break; - } - + HandleObjectMovement(point); update(); oldPoint = point; return; @@ -820,13 +1297,49 @@ void DrawingView::mouseMoveEvent(QMouseEvent * event) // Do object hit testing... bool needUpdate = HitTestObjects(point); + // Check for multi-hover... + 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]; + double u = Global::intersectParam[1]; + + if (numIntersecting > 0) + { + Vector v1 = Geometry::GetPointForParameter((Object *)hover[0], t); + Vector v2 = Geometry::GetPointForParameter((Object *)hover[1], u); + QString text = tr("Intersection t=%1 (%3, %4), u=%2 (%5, %6)"); + informativeText = text.arg(t).arg(u).arg(v1.x).arg(v1.y).arg(v2.x).arg(v2.y); + + hoveringIntersection = true; + intersectionPoint = v1; + } + + numIntersecting = Global::numIntersectPoints; + + if (numIntersecting > 0) + { + Vector v1 = Global::intersectPoint[0]; + QString text = tr("Intersection <%1, %2>"); + informativeText = text.arg(v1.x).arg(v1.y); + + hoveringIntersection = true; + intersectionPoint = v1; + } + } + // Do tool handling, if any are active... if (Global::tool) { if (Global::snapToGrid) point = SnapPointToGrid(point); - ToolMouse(ToolMouseMove, point); + ToolHandler(ToolMouseMove, point); } // This is used to draw the tool crosshair... @@ -841,51 +1354,41 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event) { if (event->button() == Qt::LeftButton) { -#if 0 - document.PointerReleased(); -#endif - //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: // if (document.NeedsUpdate()) + // Do an update if collided with at least *one* object in the document // if (collided) - update(); // Do an update if collided with at least *one* object in the document + update(); -#if 0 - if (toolAction) - toolAction->MouseReleased(); -#else if (Global::tool) { Vector point = Painter::QtToCartesianCoords(Vector(event->x(), event->y())); -// ToolMouseUp(point); - ToolMouse(ToolMouseUp, point); + ToolHandler(ToolMouseUp, point); return; } -#endif if (Global::selectionInProgress) - { - // Select all the stuff inside of selection Global::selectionInProgress = false; - // Clear our vectors - select.clear(); - hover.clear(); + informativeText.clear(); +// Should we be doing this automagically? Hmm... + // Clear our vectors + select.clear(); + hover.clear(); - // Scoop 'em up - std::vector::iterator i; + // Scoop 'em up + std::vector::iterator i; - for(i=document.objects.begin(); i!=document.objects.end(); i++) - { - if (((Object *)(*i))->selected) - select.push_back(*i); + for(i=document.objects.begin(); i!=document.objects.end(); i++) + { + if (((Object *)(*i))->selected) + select.push_back(*i); //hmm, this is no good, too late to do any good :-P -// if ((*i)->hovered) -// hover.push_back(*i); - } +// if ((*i)->hovered) +// hover.push_back(*i); } } else if (event->button() == Qt::MiddleButton) @@ -917,22 +1420,16 @@ void DrawingView::wheelEvent(QWheelEvent * event) Global::zoom /= zoomFactor; } -#if 1 // Global::gridSpacing = gridPixels / Painter::zoom; // UpdateGridBackground(); SetGridSize(Global::gridSpacing * Global::zoom); update(); // zoomIndicator->setText(QString("Grid: %1\", BU: Inch").arg(Global::gridSpacing)); -#endif } void DrawingView::keyPressEvent(QKeyEvent * event) { -#if 0 - if (toolAction) - toolAction->KeyDown(event->key()); -#endif bool oldShift = shiftDown; bool oldCtrl = ctrlDown; @@ -942,16 +1439,17 @@ void DrawingView::keyPressEvent(QKeyEvent * event) ctrlDown = true; if ((oldShift != shiftDown) || (oldCtrl != ctrlDown)) + { + if (Global::tool) + ToolHandler(ToolKeyDown, Point(0, 0)); + update(); + } } void DrawingView::keyReleaseEvent(QKeyEvent * event) { -#if 0 - if (toolAction) - toolAction->KeyReleased(event->key()); -#endif bool oldShift = shiftDown; bool oldCtrl = ctrlDown; @@ -961,9 +1459,15 @@ void DrawingView::keyReleaseEvent(QKeyEvent * event) ctrlDown = false; if ((oldShift != shiftDown) || (oldCtrl != ctrlDown)) + { + if (Global::tool) + ToolHandler(ToolKeyUp, Point(0, 0)); + update(); + } } + // // This looks strange, but it's really quite simple: We want a point that's // more than half-way to the next grid point to snap there while conversely we @@ -1000,7 +1504,7 @@ void DrawingView::CheckObjectBounds(void) { Line * l = (Line *)obj; - if (Global::selection.contains(l->p1.x, l->p1.y) && Global::selection.contains(l->p2.x, l->p2.y)) + if (Global::selection.contains(l->p[0].x, l->p[0].y) && Global::selection.contains(l->p[1].x, l->p[1].y)) l->selected = true; break; @@ -1009,7 +1513,7 @@ void DrawingView::CheckObjectBounds(void) { Circle * c = (Circle *)obj; - if (Global::selection.contains(c->p1.x - c->radius, c->p1.y - c->radius) && Global::selection.contains(c->p1.x + c->radius, c->p1.y + c->radius)) + if (Global::selection.contains(c->p[0].x - c->radius[0], c->p[0].y - c->radius[0]) && Global::selection.contains(c->p[0].x + c->radius[0], c->p[0].y + c->radius[0])) c->selected = true; break; @@ -1018,8 +1522,8 @@ void DrawingView::CheckObjectBounds(void) { Arc * a = (Arc *)obj; - double start = a->angle1; - double end = start + a->angle2; + double start = a->angle[0]; + double end = start + a->angle[1]; QPointF p1(cos(start), sin(start)); QPointF p2(cos(end), sin(end)); QRectF bounds(p1, p2); @@ -1070,9 +1574,9 @@ void DrawingView::CheckObjectBounds(void) if ((start < ((3.0 * PI) + PI_OVER_2)) && (end > ((3.0 * PI) + PI_OVER_2))) bounds.setBottom(-1.0); - bounds.setTopLeft(QPointF(bounds.left() * a->radius, bounds.top() * a->radius)); - bounds.setBottomRight(QPointF(bounds.right() * a->radius, bounds.bottom() * a->radius)); - bounds.translate(a->p1.x, a->p1.y); + bounds.setTopLeft(QPointF(bounds.left() * a->radius[0], bounds.top() * a->radius[0])); + bounds.setBottomRight(QPointF(bounds.right() * a->radius[0], bounds.bottom() * a->radius[0])); + bounds.translate(a->p[0].x, a->p[0].y); if (Global::selection.contains(bounds)) a->selected = true; @@ -1103,13 +1607,12 @@ bool DrawingView::HitTestObjects(Point point) { case OTLine: { - Line * l = (Line *)obj; - bool oldHP0 = l->hitPoint[0], oldHP1 = l->hitPoint[1], oldHO = l->hitObject; - l->hitPoint[0] = l->hitPoint[1] = l->hitObject = false; - Vector lineSegment = l->p2 - l->p1; - Vector v1 = point - l->p1; - Vector v2 = point - l->p2; - double t = Geometry::ParameterOfLineAndPoint(l->p1, l->p2, point); + 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) @@ -1122,34 +1625,71 @@ bool DrawingView::HitTestObjects(Point point) / lineSegment.Magnitude()); if ((v1.Magnitude() * Global::zoom) < 8.0) - { - l->hitPoint[0] = true; -// snapPoint = l->p1; -// snapPointIsValid = true; - } + obj->hitPoint[0] = true; else if ((v2.Magnitude() * Global::zoom) < 8.0) - { - l->hitPoint[1] = true; -// snapPoint = l->p2; -// snapPointIsValid = true; - } + obj->hitPoint[1] = true; else if ((distance * Global::zoom) < 5.0) - l->hitObject = true; + obj->hitObject = true; -// bool oldHovered = l->hovered; - l->hovered = (l->hitPoint[0] || l->hitPoint[1] || l->hitObject ? true : false); -// l->hovered = l->hitObject; + obj->hovered = (obj->hitPoint[0] || obj->hitPoint[1] || obj->hitObject ? true : false); -// if (oldHovered != l->hovered) - if ((oldHP0 != l->hitPoint[0]) || (oldHP1 != l->hitPoint[1]) || (oldHO != l->hitObject)) + if ((oldHP0 != obj->hitPoint[0]) || (oldHP1 != obj->hitPoint[1]) || (oldHO != obj->hitObject)) needUpdate = true; break; } case OTCircle: { - Circle * c = (Circle *)obj; + 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 += PI_TIMES_2; + + // 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; } @@ -1168,6 +1708,117 @@ bool DrawingView::HitTestObjects(Point point) } +void DrawingView::HandleObjectMovement(Point point) +{ + Point delta = point - oldPoint; +//printf("HOM: old = (%f,%f), new = (%f, %f), delta = (%f, %f)\n", oldPoint.x, oldPoint.y, point.x, point.y, delta.x, delta.y); + Object * obj = (Object *)hover[0]; +//printf("Object type = %i (size=%i), ", obj->type, hover.size()); +//printf("Object (%X) move: hp1=%s, hp2=%s, hl=%s\n", obj, (obj->hitPoint[0] ? "true" : "false"), (obj->hitPoint[1] ? "true" : "false"), (obj->hitObject ? "true" : "false")); + + switch (obj->type) + { + case OTLine: + if (obj->hitPoint[0]) + obj->p[0] = point; + else if (obj->hitPoint[1]) + obj->p[1] = point; + else if (obj->hitObject) + { + obj->p[0] += delta; + obj->p[1] += delta; + } + + break; + case OTCircle: + if (obj->hitPoint[0]) + obj->p[0] = point; + else if (obj->hitObject) + { +//this doesn't work. we need to save this on mouse down for this to work correctly! +// double oldRadius = obj->radius[0]; + obj->radius[0] = Vector::Magnitude(obj->p[0], point); + + QString text = QObject::tr("Radius: %1");//\nScale: %2%"); + informativeText = text.arg(obj->radius[0], 0, 'd', 4);//.arg(obj->radius[0] / oldRadius * 100.0, 0, 'd', 0); + } + + break; + case OTArc: + if (obj->hitPoint[0]) + obj->p[0] = point; + else if (obj->hitPoint[1]) + { + // Change the Arc's span (handle #1) + if (shiftDown) + { + double angle = Vector::Angle(obj->p[0], point); + double delta = angle - obj->angle[0]; + + if (delta < 0) + delta += PI_TIMES_2; + + obj->angle[1] -= delta; + obj->angle[0] = angle; + + if (obj->angle[1] < 0) + obj->angle[1] += PI_TIMES_2; + + QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0); + informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2); + return; + } + + double angle = Vector::Angle(obj->p[0], point); + obj->angle[0] = angle; + QString text = QObject::tr("Start angle: %1") + QChar(0x00B0); + informativeText = text.arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 4); + } + else if (obj->hitPoint[2]) + { + // Change the Arc's span (handle #2) + if (shiftDown) + { + double angle = Vector::Angle(obj->p[0], point); + obj->angle[1] = angle - obj->angle[0]; + + if (obj->angle[1] < 0) + obj->angle[1] += PI_TIMES_2; + + QString text = QObject::tr("Span: %1") + QChar(0x00B0) + QObject::tr("\n%2") + QChar(0x00B0) + QObject::tr(" - %3") + QChar(0x00B0); + informativeText = text.arg(obj->angle[1] * RADIANS_TO_DEGREES, 0, 'd', 4).arg(obj->angle[0] * RADIANS_TO_DEGREES, 0, 'd', 2).arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 2); + return; + } + + double angle = Vector::Angle(obj->p[0], point); + obj->angle[0] = angle - obj->angle[1]; + + if (obj->angle[0] < 0) + obj->angle[0] += PI_TIMES_2; + + QString text = QObject::tr("End angle: %1") + QChar(0x00B0); + informativeText = text.arg((obj->angle[0] + obj->angle[1]) * RADIANS_TO_DEGREES, 0, 'd', 4); + } + else if (obj->hitObject) + { + if (shiftDown) + { + return; + } + + obj->radius[0] = Vector::Magnitude(obj->p[0], point); + QString text = QObject::tr("Radius: %1"); + informativeText = text.arg(obj->radius[0], 0, 'd', 4); + } + + break; + default: + break; + } +} + + + #if 0 // This returns true if we've moved over an object... if (document.PointerMoved(point)) // <-- This