From fce575a51ba1f26418869c20e63b9f388e118ab6 Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Mon, 6 Dec 2021 11:04:57 -0600 Subject: [PATCH] More miscellaneous changes. - Implemented trim tool to work with line segments - Added delete tool - Added MRU list for files - Added ability to move objects to different layers - Fixed dimension tool to respect the line width when adding --- src/applicationwindow.cpp | 125 ++++++++++++++++++--- src/applicationwindow.h | 12 ++ src/drawingview.cpp | 225 ++++++++++++++++++++++---------------- src/drawingview.h | 3 + src/geometry.cpp | 45 +++++++- src/global.cpp | 1 + src/global.h | 1 + src/mathconstants.h | 2 + src/utils.cpp | 19 ++++ src/utils.h | 1 + 10 files changed, 318 insertions(+), 116 deletions(-) diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index 2f78dfc..47f8149 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -59,10 +59,8 @@ ApplicationWindow::ApplicationWindow(): aboutWin = new AboutWindow(this); -// ((TTEdit *)qApp)->charWnd = new CharWindow(this); - setWindowIcon(QIcon(":/res/atns-icon.png")); - setWindowTitle("Architektonas"); +// setWindowTitle("Architektonas"); CreateActions(); CreateMenus(); @@ -98,6 +96,8 @@ ApplicationWindow::ApplicationWindow(): statusBar()->showMessage(tr("Ready")); ReadSettings(); + // Make sure the bottom left corner gets owned by the right side: + setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); setUnifiedTitleAndToolBarOnMac(true); Global::font = new QFont("Verdana", 15, QFont::Bold); @@ -166,6 +166,21 @@ void ApplicationWindow::FileOpen(void) if (filename.isEmpty()) return; + LoadFile(filename); +} + +void ApplicationWindow::FileOpenRecent(void) +{ + QAction * action = qobject_cast(sender()); + + if (!action) + return; + + LoadFile(action->data().toString()); +} + +void ApplicationWindow::LoadFile(QString filename) +{ FILE * file = fopen(filename.toUtf8().data(), "r"); if (file == 0) @@ -192,7 +207,6 @@ void ApplicationWindow::FileOpen(void) return; } -//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); emit ReloadLayers(); @@ -203,7 +217,8 @@ void ApplicationWindow::FileOpen(void) drawing->dirty = false; drawing->update(); documentName = filename; - setWindowTitle(QString("Architektonas - %1").arg(documentName)); +// setWindowTitle(QString("Architektonas - %1").arg(documentName)); + AdjustMRU(filename); statusBar()->showMessage(tr("Drawing loaded.")); } @@ -241,7 +256,8 @@ void ApplicationWindow::FileSave(void) } drawing->dirty = false; - setWindowTitle(QString("Architektonas - %1").arg(documentName)); +// setWindowTitle(QString("Architektonas - %1").arg(documentName)); + AdjustMRU(documentName); statusBar()->showMessage(tr("Drawing saved.")); } @@ -305,16 +321,46 @@ void ApplicationWindow::HandlePrintRequest(QPrinter * printer) Global::screenSize = screenSizeSave; } +// +// Right-click menu +// void ApplicationWindow::contextMenuEvent(QContextMenuEvent * event) { // Proof of concept, still need to code up the real thing QMenu menu(this); + + VPVector hovered = drawing->GetHovered(); + + if ((drawing->select.size() > 0) || (hovered.size() > 0)) + { + QMenu * layerMenu = menu.addMenu("To layer"); + + layerAct.clear(); + + for(int i=0; isetData(i); + layerMenu->addAction(act); + layerAct.append(act); + connect(act, SIGNAL(triggered()), this, SLOT(MoveToLayer())); + } + } + menu.addAction(mirrorAct); menu.addAction(rotateAct); menu.addAction(trimAct); menu.exec(event->globalPos()); } +void ApplicationWindow::MoveToLayer(void) +{ + QAction * act = qobject_cast(sender()); + + drawing->MoveSelectedToLayer(act->data().toInt()); + drawing->update(); +} + void ApplicationWindow::SnapToGridTool(void) { Global::snapToGrid = snapToGridAct->isChecked(); @@ -334,7 +380,6 @@ void ApplicationWindow::DeleteTool(void) { // 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->select.size() > 0) { DeleteSelectedObjects(drawing->document.objects); @@ -346,6 +391,7 @@ void ApplicationWindow::DeleteTool(void) // Otherwise, toggle the state of the tool ClearUIToolStatesExcept(deleteAct); SetInternalToolStates(); + Global::toolSuppressCrosshair = true; } void ApplicationWindow::DimensionTool(void) @@ -380,6 +426,7 @@ void ApplicationWindow::TrimTool(void) { ClearUIToolStatesExcept(trimAct); SetInternalToolStates(); + Global::toolSuppressCrosshair = true; } void ApplicationWindow::ParallelTool(void) @@ -479,7 +526,6 @@ void ApplicationWindow::UpdateZoom(void) else Global::gridSpacing = 0.015625; -// drawing->SetGridSize((double)(Global::gridSpacing * Global::zoom)); drawing->update(); zoomIndicator->setText(QString("Zoom: %1% Grid: %2\" BU: Inch").arg(Global::zoom * 100.0).arg(Global::gridSpacing)); @@ -506,9 +552,11 @@ void ApplicationWindow::ClearUIToolStatesExcept(QAction * exception) void ApplicationWindow::SetInternalToolStates(void) { // We can be sure that if we've come here, then either an active tool is - // being deactivated, or a new tool is being created. In either case, the - // old tool needs to be deleted. + // being deactivated, or a new tool is being activated. In either case, + // the tool state needs to be reset. Also, we reset the crosshair + // suppression flag. Global::toolState = TSNone; + Global::toolSuppressCrosshair = false; if (addLineAct->isChecked()) Global::tool = TTLine; @@ -713,11 +761,8 @@ void ApplicationWindow::HandleGridSizeInBaseUnits(QString text) if (!ok || value == 0) return; -// drawing->gridSpacing = value; -// Painter::zoom = drawing->gridPixels / drawing->gridSpacing; Global::gridSpacing = value; Global::zoom = drawing->gridPixels / Global::gridSpacing; -// drawing->UpdateGridBackground(); drawing->update(); } @@ -891,6 +936,14 @@ void ApplicationWindow::CreateActions(void) group->addAction(addLineAct); group->addAction(addCircleAct); group->addAction(addArcAct);//*/ + + for(int i=0; isetVisible(false); + connect(rfa, SIGNAL(triggered()), this, SLOT(FileOpenRecent())); + mruAct.append(rfa); + } } // @@ -933,6 +986,14 @@ void ApplicationWindow::CreateMenus(void) QMenu * menu = menuBar()->addMenu(tr("&File")); menu->addAction(fileNewAct); menu->addAction(fileOpenAct); + + QMenu * recentMenu = menu->addMenu(tr("Open &Recent")); + + for(int i=0; iaddAction(mruAct.at(i)); + + UpdateMRUActionList(); + menu->addAction(fileSaveAct); menu->addAction(fileSaveAsAct); menu->addAction(fileCloseAct); @@ -986,7 +1047,6 @@ void ApplicationWindow::CreateToolbars(void) toolbar->addAction(fileSaveAct); toolbar->addAction(fileSaveAsAct); toolbar->addAction(fileCloseAct); -// toolbar->addAction(exitAct); toolbar = addToolBar(tr("View")); toolbar->setObjectName("View"); @@ -995,7 +1055,6 @@ void ApplicationWindow::CreateToolbars(void) QSpinBox * spinbox = new QSpinBox; toolbar->addWidget(spinbox); -// QLineEdit * lineedit = new QLineEdit; toolbar->addWidget(baseUnitInput); toolbar->addWidget(dimensionSizeInput); @@ -1039,7 +1098,6 @@ void ApplicationWindow::CreateToolbars(void) connect(pw, SIGNAL(WidthSelected(float)), drawing, SLOT(HandlePenWidth(float))); connect(pw, SIGNAL(StyleSelected(int)), drawing, SLOT(HandlePenStyle(int))); connect(pw, SIGNAL(ColorSelected(uint32_t)), drawing, SLOT(HandlePenColor(uint32_t))); -// connect(pw, SIGNAL(StampSelected(void)), drawing, SLOT(HandlePenStamp(QAction *))); connect(pw->tbStamp, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenStamp(QAction *))); connect(pw->tbDropper, SIGNAL(triggered(QAction *)), drawing, SLOT(HandlePenDropper(QAction *))); } @@ -1063,3 +1121,38 @@ void ApplicationWindow::WriteSettings(void) settings.setValue("useAntialiasing", drawing->useAntialiasing); settings.setValue("snapToGrid", snapToGridAct->isChecked()); } + +void ApplicationWindow::UpdateMRUActionList(void) +{ + QStringList mruFilePaths = settings.value("recentFiles").toStringList(); + + int mruSize = (mruFilePaths.size() <= MRU_MAX ? mruFilePaths.size() : MRU_MAX); + + for(int i=0; isetText(filename); + mruAct.at(i)->setData(mruFilePaths.at(i)); + mruAct.at(i)->setVisible(true); + } + + for(int i=mruSize; isetVisible(false); +} + +void ApplicationWindow::AdjustMRU(const QString & filePath) +{ + documentName = filePath; + setWindowFilePath(documentName); + + QStringList mruFilePaths = settings.value("recentFiles").toStringList(); + mruFilePaths.removeAll(filePath); + mruFilePaths.prepend(filePath); + + while (mruFilePaths.size() > MRU_MAX) + mruFilePaths.removeLast(); + + settings.setValue("recentFiles", mruFilePaths); + + UpdateMRUActionList(); +} diff --git a/src/applicationwindow.h b/src/applicationwindow.h index a83e937..d5a9c08 100644 --- a/src/applicationwindow.h +++ b/src/applicationwindow.h @@ -4,6 +4,10 @@ #include #include +// Useful definitions + +#define MRU_MAX 10 + // Forward declarations class AboutWindow; @@ -25,6 +29,7 @@ class ApplicationWindow: public QMainWindow private slots: void FileNew(void); void FileOpen(void); + void FileOpenRecent(void); void FileSave(void); void FileSaveAs(void); void PrintPreview(void); @@ -59,11 +64,13 @@ class ApplicationWindow: public QMainWindow void EditPaste(void); void SelectAllObjects(void); void UpdateZoom(void); + void MoveToLayer(void); signals: void ReloadLayers(void); private: + void LoadFile(QString); void ClearUIToolStatesExcept(QAction *); void SetInternalToolStates(void); void CreateActions(void); @@ -75,6 +82,8 @@ class ApplicationWindow: public QMainWindow void CreateToolbars(void); void ReadSettings(void); void WriteSettings(void); + void UpdateMRUActionList(void); + void AdjustMRU(const QString & filePath); AboutWindow * aboutWin; QLabel * zoomIndicator; @@ -117,6 +126,9 @@ class ApplicationWindow: public QMainWindow QAction * editPasteAct; QAction * selectAllAct; QAction * printPreviewAct; +// QAction * moveToLayerAct; + QList layerAct; + QList mruAct; std::vector clipboard; diff --git a/src/drawingview.cpp b/src/drawingview.cpp index 9acf00c..4ae021c 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -159,12 +159,12 @@ void DrawingView::DrawBackground(Painter * painter) DrawSubGrid(painter, 0xB8ECFF, 0.125, start, size); if (Global::gridSpacing <= 0.25) - DrawSubGrid(painter, 0xDBDBFF, 0.25, start, size); + DrawSubGrid(painter, 0xE6E6FF, 0.25, start, size); if (Global::gridSpacing <= 0.5) - DrawSubGrid(painter, 0xDBDBFF, 0.5, start, size); + DrawSubGrid(painter, 0xE6E6FF, 0.5, start, size); - painter->SetPen(QPen(QColor(0xD2, 0xD2, 0xFF), 2.0, Qt::SolidLine)); + painter->SetPen(QPen(QColor(0xE0, 0xE0, 0xFF), 2.0, Qt::SolidLine)); for(double i=0; i<=w; i+=spacing) painter->DrawVLine(leftx + i); @@ -387,8 +387,12 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/) // Do tool rendering, if any... if (Global::tool) { - painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine)); - painter.DrawCrosshair(oldPoint); + if (Global::toolSuppressCrosshair == false) + { + painter.SetPen(QPen(QColor(200, 100, 0, 255), 1.0, Qt::DashLine)); + painter.DrawCrosshair(oldPoint); + } + ToolDraw(&painter); } @@ -773,6 +777,17 @@ VPVector DrawingView::GetHovered(bool exclude/*= false*/) return v; } +void DrawingView::MoveSelectedToLayer(int layer) +{ + for(VPVectorIter i=document.objects.begin(); i!=document.objects.end(); i++) + { + Object * obj = (Object *)(*i); + + if (obj->selected || obj->hovered) + obj->layer = layer; + } +} + void DrawingView::resizeEvent(QResizeEvent * /*event*/) { Global::screenSize = Vector(size().width(), size().height()); @@ -795,6 +810,8 @@ void DrawingView::ToolHandler(int mode, Point p) MirrorHandler(mode, p); else if (Global::tool == TTDimension) DimensionHandler(mode, p); + else if (Global::tool == TTDelete) + DeleteHandler(mode, p); else if (Global::tool == TTTriangulate) TriangulateHandler(mode, p); else if (Global::tool == TTTrim) @@ -938,7 +955,7 @@ void DrawingView::ToolDraw(Painter * painter) informativeText += " (Copy)"; } } - if (Global::tool == TTDimension) + else if (Global::tool == TTDimension) { if (Global::toolState == TSNone) { @@ -960,6 +977,20 @@ void DrawingView::ToolDraw(Painter * painter) informativeText = text.arg(absLength).arg(absAngle); } } + else if (Global::tool == TTTrim) + { + if (toolObj[0] != NULL) + { + // We're assuming ATM it's just a line... + painter->SetPen(0xAF0000, 3.0, LSSolid); + painter->DrawLine(toolPoint[0], toolPoint[1]); +// QString text = tr("Arc span: %1") + QChar(0x00B0); +// informativeText = text.arg(RADIANS_TO_DEGREES * span); + } + } + else if (Global::tool == TTParallel) + { + } } void DrawingView::LineHandler(int mode, Point p) @@ -1489,7 +1520,7 @@ void DrawingView::DimensionHandler(int mode, Point p) } else { - Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear); + Dimension * d = new Dimension(toolPoint[0], toolPoint[1], DTLinear, 0, Global::penWidth); d->layer = Global::activeLayer; document.objects.push_back(d); Global::toolState = TSNone; @@ -1497,7 +1528,37 @@ void DrawingView::DimensionHandler(int mode, Point p) } } -void DrawingView::TriangulateHandler(int mode, Point/*p*/) +void DrawingView::DeleteHandler(int mode, Point /*p*/) +{ + switch (mode) + { + case ToolMouseDown: + { + VPVector hovered = GetHovered(); + + RemoveHoveredObjects(document.objects); + DeleteContents(hovered); + } + break; + + case ToolMouseMove: + break; + + case ToolMouseUp: + break; + + case ToolKeyDown: + break; + + case ToolKeyUp: + break; + + case ToolCleanup: + break; + } +} + +void DrawingView::TriangulateHandler(int mode, Point /*p*/) { switch (mode) { @@ -1586,89 +1647,15 @@ void DrawingView::TriangulateHandler(int mode, Point/*p*/) void DrawingView::TrimHandler(int mode, Point p) { -/* -N.B.: this code is lifted straight out of the old oo code. needs to be updated. - Also: trim tool should ignore snap. -*/ switch (mode) { case ToolMouseDown: { -#if 0 - Object * toTrim = doc->lastObjectHovered; - - if (toTrim == NULL) - return; - - Vector v(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint); - - // Check to see which case we have... - // We're trimming point #1... - if (t == 0) - { - ((Line *)toTrim)->position = ((Line *)toTrim)->position + (v * u); - } - else if (u == 1.0) - { - ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t); - } - else - { - Point p1 = ((Line *)toTrim)->position + (v * t); - Point p2 = ((Line *)toTrim)->position + (v * u); - Point p3 = ((Line *)toTrim)->endpoint; - ((Line *)toTrim)->endpoint = p1; - Line * line = new Line(p2, p3); - emit ObjectReady(line); - } - - doc->lastObjectHovered = NULL; -#endif } break; case ToolMouseMove: { -#if 0 - Object * toTrim = doc->lastObjectHovered; - t = 0, u = 1.0; - - if (toTrim == NULL) - return; - - if (toTrim->type != OTLine) - return; - - double pointHoveredT = Geometry::ParameterOfLineAndPoint(((Line *)toTrim)->position, ((Line *)toTrim)->endpoint, point); - - std::vector::iterator i; - - for(i=doc->objects.begin(); i!=doc->objects.end(); i++) - { - // Can't trim against yourself... :-P - if (*i == toTrim) - continue; - - Object * trimAgainst = *i; - double t1;//, u1; - - if ((toTrim->type != OTLine) || (trimAgainst->type != OTLine)) - continue; - - int intersects = Geometry::Intersects((Line *)toTrim, (Line *)trimAgainst, &t1);//, &u1); - - if (intersects) - { - // Now what? We don't know which side to trim! - // ... now we do, we know which side of the Line we're on! - if ((t1 > t) && (t1 < pointHoveredT)) - t = t1; - - if ((t1 < u) && (t1 > pointHoveredT)) - u = t1; - } - } -#endif // Bail out if nothing hovered... if (numHovered != 1) { @@ -1688,6 +1675,7 @@ N.B.: this code is lifted straight out of the old oo code. needs to be updated. toolObj[0] = obj; double hoveredParam = Geometry::ParameterOfLineAndPoint(obj->p[0], obj->p[1], p); + double t = 0, u = 1.0; // Currently only deal with line against line trimming, can expand to // others as well (line/circle, circle/circle, line/arc, etc) @@ -1705,13 +1693,70 @@ N.B.: this code is lifted straight out of the old oo code. needs to be updated. if (Global::numIntersectParams > 0) { + // Skip endpoint-endpoint intersections + if ((Global::numIntersectParams == 2) + && (Global::intersectParam[0] == 0 + || Global::intersectParam[0] == 1.0) + && (Global::intersectParam[1] == 0 + || Global::intersectParam[1] == 1.0)) + continue; + // Mark the line segment somehow (the side the mouse is on) so that it can be drawn & trimmed when we hit ToolMouseDown. + if ((Global::intersectParam[0] > t) && (Global::intersectParam[0] < hoveredParam)) + t = Global::intersectParam[0]; + + if ((Global::intersectParam[0] < u) && (Global::intersectParam[0] > hoveredParam)) + u = Global::intersectParam[0]; } } + + toolParam[0] = t; + toolParam[1] = u; + toolPoint[0] = Geometry::GetPointForParameter(toolObj[0], t); + toolPoint[1] = Geometry::GetPointForParameter(toolObj[0], u); } break; case ToolMouseUp: + { + // Bail out if there's no object to trim + if (toolObj[0] == NULL) + return; + + Vector v(toolObj[0]->p[0], toolObj[0]->p[1]); + + // Check to see which case we have. + if ((toolParam[0] == 0) && (toolParam[1] == 1.0)) + { + // There was no intersection, so delete the object + toolObj[0]->selected = true; + DeleteSelectedObjects(document.objects); + } + else if (toolParam[0] == 0) + { + // We delete the end near point #1 + toolObj[0]->p[0] = toolObj[0]->p[0] + (v * toolParam[1]); + } + else if (toolParam[1] == 1.0) + { + // We delete the end near point #2 + toolObj[0]->p[1] = toolObj[0]->p[0] + (v * toolParam[0]); + } + else + { + // We delete the segment in between, and create a new line in the process + Point p1 = toolObj[0]->p[0] + (v * toolParam[0]); + Point p2 = toolObj[0]->p[0] + (v * toolParam[1]); + Point p3 = toolObj[0]->p[1]; + toolObj[0]->p[1] = p1; + Line * l = new Line(p2, p3, toolObj[0]->thickness, toolObj[0]->color, toolObj[0]->style); + document.objects.push_back(l); +// Global::toolState = TSNone; + } + + toolObj[0]->hitObject = toolObj[0]->hitPoint[0] = toolObj[0]->hitPoint[1] = false; + toolObj[0] = NULL; + } break; case ToolKeyDown: @@ -1766,12 +1811,6 @@ void DrawingView::mousePressEvent(QMouseEvent * event) else if (Global::snapToGrid) point = SnapPointToGrid(point); - //Also, may want to figure out if hovering over a snap point on an - //object, snap to grid if not. - // Snap to object point if valid... -// if (Global::snapPointIsValid) -// point = Global::snapPoint; - ToolHandler(ToolMouseDown, point); return; } @@ -1779,7 +1818,6 @@ void DrawingView::mousePressEvent(QMouseEvent * event) // Clear the selection only if CTRL isn't being held on click if (!ctrlDown) ClearSelected(document.objects); -// ClearSelection(); // If any objects are being hovered on click, add them to the selection // & return @@ -1794,8 +1832,6 @@ void DrawingView::mousePressEvent(QMouseEvent * event) //printf("mousePressEvent::numHovered > 0 (hover2[0]=$%llx, type=%s)\n", dragged, objName[dragged->type]); // Alert the pen widget -// Maybe do this with an eyedropper tool on the pen bar? [YES] -// emit ObjectSelected(dragged); if (Global::penDropper) { Global::penColor = dragged->color; @@ -2081,16 +2117,15 @@ void DrawingView::mouseReleaseEvent(QMouseEvent * event) return; } -// if (Global::selectionInProgress) - Global::selectionInProgress = false; - + Global::selectionInProgress = false; informativeText.clear(); + // Should we be doing this automagically? Hmm... // Clear our vectors // select.clear(); //// hover.clear(); - // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it. Also, tools use it too. But we can move it out of here) + // Scoop 'em up (do we need to??? Seems we do, because keyboard movement uses it. Also, tools use it too. But we can move it out of here [where to???]) select = GetSelection(); draggingObject = false; diff --git a/src/drawingview.h b/src/drawingview.h index 344428a..136380c 100644 --- a/src/drawingview.h +++ b/src/drawingview.h @@ -25,6 +25,7 @@ class DrawingView: public QWidget void AddHoveredToSelection(void); VPVector GetSelection(void); VPVector GetHovered(bool exclude = false); + void MoveSelectedToLayer(int); void ToolHandler(int, Point); void ToolDraw(Painter *); void LineHandler(int, Point); @@ -33,6 +34,7 @@ class DrawingView: public QWidget void RotateHandler(int, Point); void MirrorHandler(int, Point); void DimensionHandler(int, Point); + void DeleteHandler(int, Point); void TriangulateHandler(int, Point); void TrimHandler(int, Point); void ParallelHandler(int, Point); @@ -110,6 +112,7 @@ class DrawingView: public QWidget VPVector toolScratch2; Point toolPoint[32]; Object * toolObj[32]; + double toolParam[32]; Point intersectionPoint; Point hoverPoint; bool hoverPointValid; diff --git a/src/geometry.cpp b/src/geometry.cpp index 86d1906..31c5257 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -44,7 +44,7 @@ double Geometry::DistanceToLineFromPoint(Point tail, Point head, Point point) // Interpretation: given a line in the form x = a + tu, where u is the // unit vector of the line, a is the tail and t is a parameter which // describes the line, the distance of a point p to the line is given by: - // || (a - p) - ((a - p) . u) u || + // || (a - p) - ((a - p) • u) u || // We go an extra step: we set the sign to reflect which side of the line // it's on (+ == to the left if head points away from you, - == to the // right) @@ -143,14 +143,37 @@ void Geometry::CheckLineToLineIntersection(Object * l1, Object * l2) double rxs = (r.x * s.y) - (s.x * r.y); double t, u; + // The angle is zero, so the lines probably overlap, or are parallel. Either way, there is either INFINITE intersections, or zero. Either way, the result is useless to us. It does present a bit of an inconsistency though: lines connected at their endpoints that aren't at a zero angle will show as overlapping (the only case this could even REMOTELY be useful is when the angle between is 180°--not zero, as zero is the degenerate case). :-P if (rxs == 0) { - double qpxr = (v1.x * r.y) - (r.x * v1.y); +// WHY would you do this??? The lines overlap at an INFINITE number of points! +// The assumption is that the angle is 180°, not 0°. + // If we want to detect the degenerate case, we can check the parameter of the endpoints of the one line to the other. If any of the parameters are in (0, 1), then it's the degenerate case (we would check the endpoints of the shorter segment against the longer). +/* double qpxr = (v1.x * r.y) - (r.x * v1.y); - // Lines are parallel, so no intersection... + // Line segments are parallel, so no intersection... if (qpxr != 0) return; + // Otherwise, the segments are colinear. Need to check for the 0° (degenerate) vs the 180° (OK) case. + Object * larger = l1; + Object * smaller = l2; + + if (r->Magnitude() < s->Magnitude()) + { + larger = l2; + smaller = l1; + } + + double param1 = ParameterOfLineAndPoint(larger->p[0], larger->p[1], smaller->p[0]); + double param2 = ParameterOfLineAndPoint(larger->p[0], larger->p[1], smaller->p[1]); + + // Check for the degenerate case, and return if found + if ((param1 > 0 && param1 < 1.0) || (param2 > 0 && param2 < 1.0)) + return; + +//// or just use AngleBetween: Nah, won't work... + // Check to see which endpoints are connected... Four possibilities: if (l1->p[0] == l2->p[0]) t = 0, u = 0; @@ -160,7 +183,7 @@ void Geometry::CheckLineToLineIntersection(Object * l1, Object * l2) t = 1.0, u = 0; else if (l1->p[1] == l2->p[1]) t = 1.0, u = 1.0; - else + else*/ return; } else @@ -169,12 +192,24 @@ void Geometry::CheckLineToLineIntersection(Object * l1, Object * l2) u = ((v1.x * r.y) - (r.x * v1.y)) / rxs; } + // Check that the parameters are above the epsilon, otherwise clamp them to + // zero or one, as the case may be + if (fabs(t) < EPSILON) + t = 0; + else if (fabs(1.0 - t) < EPSILON) + t = 1.0; + + if (fabs(u) < EPSILON) + u = 0; + else if (fabs(1.0 - u) < EPSILON) + u = 1.0; + Global::intersectParam[0] = t; Global::intersectParam[1] = u; // If the parameters are in range, we have overlap! if ((t >= 0) && (t <= 1.0) && (u >= 0) && (u <= 1.0)) - Global::numIntersectParams = 1; + Global::numIntersectParams = 2; } void Geometry::CheckCircleToCircleIntersection(Object * c1, Object * c2) diff --git a/src/global.cpp b/src/global.cpp index 9ec2a7b..8323641 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -25,6 +25,7 @@ QRectF Global::selection; int Global::tool = TTNone; int Global::toolState = TSNone; +bool Global::toolSuppressCrosshair = false; double Global::gridSpacing; Point Global::snapPoint; diff --git a/src/global.h b/src/global.h index 796cc45..18faa97 100644 --- a/src/global.h +++ b/src/global.h @@ -41,6 +41,7 @@ class Global static uint32_t objectID; static int tool; static int toolState; + static bool toolSuppressCrosshair; static Point origin; static double zoom; diff --git a/src/mathconstants.h b/src/mathconstants.h index 2385861..92afde1 100644 --- a/src/mathconstants.h +++ b/src/mathconstants.h @@ -27,3 +27,5 @@ #define HALF_TAU (TAU_2QTR) #define QTR_TAU (TAU_1QTR) #define THREE_QTR_TAU (TAU_3QTR) + +#define EPSILON 1e-6 diff --git a/src/utils.cpp b/src/utils.cpp index 53f5c73..c038f36 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -316,6 +316,25 @@ void RemoveSelectedObjects(VPVector & v) } } +// +// This is used to remove hovered objects from one container in order to delete +// them from the same container. +// +void RemoveHoveredObjects(VPVector & v) +{ + VPVectorIter i = v.begin(); + + while (i != v.end()) + { + Object * obj = (Object *)(*i); + + if (obj->hovered) + v.erase(i); + else + i++; + } +} + void SavePointsFrom(VPVector & v, std::vector & save) { save.clear(); diff --git a/src/utils.h b/src/utils.h index 6fd59d0..8c36c04 100644 --- a/src/utils.h +++ b/src/utils.h @@ -18,6 +18,7 @@ void SelectAll(VPVector & v); void DeleteContents(VPVector & v); void DeleteSelectedObjects(VPVector & v); void RemoveSelectedObjects(VPVector & v); +void RemoveHoveredObjects(VPVector & v); void SavePointsFrom(VPVector & v, std::vector & s); void RestorePointsTo(VPVector & v, std::vector & s); void RestorePointsTo(VPVector & v, VPVector & s); -- 2.37.2