From: Shamus Hammons Date: Wed, 22 Dec 2021 22:53:57 +0000 (-0600) Subject: Added Parallel tool + command processing. X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=architektonas;a=commitdiff_plain;h=b8bab716d9302fbb04d97679ee499eac781f1b22 Added Parallel tool + command processing. - Added command line processor - Added Basic Units to structs.h - Fixed Parallel tool to actually do something The Parallel tool utilizes the command line processor to alter its parameters; currently it only works with lines, circles, and arcs. This will also be used for such things as the Polygon tool and probably lots of others; it will also probably be used to make it so that object primitives can be added via the command line. --- diff --git a/architektonas.pro b/architektonas.pro index 4a13c0a..86dd646 100644 --- a/architektonas.pro +++ b/architektonas.pro @@ -52,6 +52,7 @@ HEADERS = \ src/baseunittab.h \ src/blockitemwidget.h \ src/blockwidget.h \ + src/commandprocessor.h \ src/consolewidget.h \ src/drawingview.h \ src/fileio.h \ @@ -78,6 +79,7 @@ SOURCES = \ src/baseunittab.cpp \ src/blockitemwidget.cpp \ src/blockwidget.cpp \ + src/commandprocessor.cpp \ src/consolewidget.cpp \ src/drawingview.cpp \ src/fileio.cpp \ diff --git a/src/applicationwindow.cpp b/src/applicationwindow.cpp index ef77f8b..d3f014f 100644 --- a/src/applicationwindow.cpp +++ b/src/applicationwindow.cpp @@ -31,6 +31,7 @@ #include #include "about.h" #include "blockwidget.h" +#include "commandprocessor.h" #include "consolewidget.h" #include "drawingview.h" #include "fileio.h" @@ -80,7 +81,7 @@ ApplicationWindow::ApplicationWindow(): dock3->setWidget(ow); addDockWidget(Qt::RightDockWidgetArea, dock3); QDockWidget * dock4 = new QDockWidget(tr("Command"), this); - ConsoleWidget * cw = new ConsoleWidget; + cw = new ConsoleWidget; dock4->setWidget(cw); addDockWidget(Qt::BottomDockWidgetArea, dock4); @@ -108,6 +109,8 @@ ApplicationWindow::ApplicationWindow(): connect(drawing, SIGNAL(ObjectHovered(Object *)), ow, SLOT(ShowInfo(Object *))); connect(drawing, SIGNAL(NeedZoomUpdate()), this, SLOT(UpdateZoom())); + + connect(cw->cmdProc, SIGNAL(UpdateNeeded()), this, SLOT(UpdateFromCommand())); } void ApplicationWindow::closeEvent(QCloseEvent * event) @@ -362,6 +365,12 @@ void ApplicationWindow::MoveToLayer(void) drawing->update(); } +void ApplicationWindow::UpdateFromCommand(void) +{ + cw->SetToolPrompt(); + drawing->update(); +} + void ApplicationWindow::SnapToGridTool(void) { Global::snapToGrid = snapToGridAct->isChecked(); @@ -434,6 +443,7 @@ void ApplicationWindow::ParallelTool(void) { ClearUIToolStatesExcept(parallelAct); SetInternalToolStates(); + Global::toolSuppressCrosshair = true; } void ApplicationWindow::TriangulateTool(void) @@ -586,6 +596,7 @@ void ApplicationWindow::SetInternalToolStates(void) else Global::tool = TTNone; + cw->SetToolPrompt();//cw->cmdline); drawing->update(); } @@ -908,7 +919,7 @@ void ApplicationWindow::CreateActions(void) trimAct = CreateAction(tr("&Trim"), tr("Trim"), tr("Trim extraneous lines from selected objects."), QIcon(":/res/trim-tool.png"), QKeySequence("t,r"), true); connect(trimAct, SIGNAL(triggered()), this, SLOT(TrimTool())); - parallelAct = CreateAction(tr("&Parallel"), tr("Parallel"), tr("Create copies of objects parallel to the original."), QIcon(":/res/parallel-tool.png"), QKeySequence("p,a"), true); + parallelAct = CreateAction(tr("&Parallel"), tr("Parallel"), tr("Create copies of objects parallel to the original."), QIcon(":/res/parallel-tool.png"), QKeySequence("p,l"), true); connect(parallelAct, SIGNAL(triggered()), this, SLOT(ParallelTool())); triangulateAct = CreateAction(tr("&Triangulate"), tr("Triangulate"), tr("Make triangles from selected lines, preserving their lengths."), QIcon(":/res/triangulate-tool.png"), QKeySequence("t,g"), true); diff --git a/src/applicationwindow.h b/src/applicationwindow.h index 651add7..be245b0 100644 --- a/src/applicationwindow.h +++ b/src/applicationwindow.h @@ -13,6 +13,7 @@ class AboutWindow; class DrawingView; class QLabel; +class ConsoleWidget; class ApplicationWindow: public QMainWindow { @@ -65,6 +66,7 @@ class ApplicationWindow: public QMainWindow void SelectAllObjects(void); void UpdateZoom(void); void MoveToLayer(void); + void UpdateFromCommand(void); signals: void ReloadLayers(void); @@ -90,6 +92,7 @@ class ApplicationWindow: public QMainWindow QString documentName; QLineEdit * baseUnitInput; QLineEdit * dimensionSizeInput; + ConsoleWidget * cw; QSettings settings; diff --git a/src/commandprocessor.cpp b/src/commandprocessor.cpp new file mode 100644 index 0000000..8b2a01e --- /dev/null +++ b/src/commandprocessor.cpp @@ -0,0 +1,256 @@ +// +// commandprocessor.cpp: Command processor +// +// Part of the Architektonas Project +// (C) 2021 Underground Software +// See the README and GPLv3 files for licensing and warranty information +// +// JLH = James Hammons +// +// WHO WHEN WHAT +// --- ---------- ------------------------------------------------------------ +// JLH 12/16/2021 Created this file +// + +#include "commandprocessor.h" +#include "structs.h" + +CommandProcessor::CommandProcessor() +{ +} + +CommandProcessor::~CommandProcessor() +{ +} + +void CommandProcessor::Error(QString s) +{ + AddToResponse("Error", "red", s); + parseError = true; +} + +void CommandProcessor::Warning(QString s) +{ + AddToResponse("Warning", "orange", s); +} + +void CommandProcessor::AddToResponse(QString type, QString c, QString msg) +{ + if (response.length() > 0) + response += "
"; + + response += QString("%3: %1").arg(msg).arg(c).arg(type); +} + +void CommandProcessor::ClearWhitespace(QString & s) +{ + while (s.length() > 0) + { + if (s[0].isSpace() == false) + return; + + s.remove(0, 1); + } +} + +double CommandProcessor::GetDouble(QString & s) +{ +/* +N.B.: Still need to add parsing of fractional components, if any (nn/nn) +*/ + QString n; + bool ok; + parseError = false; + missingParam = false; + + ClearWhitespace(s); + + while (s.length() > 0) + { + if ((s[0].isNumber() == false) && (s[0] != QChar('.')) && (s[0] != QChar('+')) && (s[0] != QChar('-')) && (s[0] != QChar('e'))) + break; + + n.push_back(s[0]); + s.remove(0, 1); + } + + if (n.length() == 0) + { + missingParam = true; + return 0; + } + + double d = n.toDouble(&ok); + + if (!ok) + Error(QString("could not parse number '%1'").arg(n)); + + return d; +} + +int CommandProcessor::GetInt(QString & s) +{ + QString n; + bool ok; + parseError = false; + missingParam = false; + + ClearWhitespace(s); + + while (s.length() > 0) + { + if (s[0].isNumber() == false) + break; + + n.push_back(s[0]); + s.remove(0, 1); + } + + if (n.size() == 0) + { + missingParam = true; + return 0; + } + + int i = n.toInt(&ok); + + if (!ok) + Error(QString("could not parse number '%1'").arg(n)); + + return i; +} + +QString CommandProcessor::GetString(QString & s) +{ + QString r; + + ClearWhitespace(s); + + while (s.length() > 0) + { + if (s[0].isLetter() == false) + break; + + r.push_back(s[0]); + s.remove(0, 1); + } + + return r; +} + +int CommandProcessor::GetUnits(QString & s) +{ + parseError = false; + missingParam = false; + QString unit = GetString(s); + + if (unit.length() == 0) + { + missingParam = true; + return BUInch; + } + + BasicUnit bu = BUInch; + + if (unit == QString("inch") || unit == QString("in")) + { + bu = BUInch; + } + else if (unit == QString("foot") || unit == QString("ft")) + { + bu = BUFoot; + } + else if (unit == QString("yard") || unit == QString("yd")) + { + bu = BUYard; + } + else + { + Error(QString("bad unit '%1'").arg(unit)); + } + + return bu; +} + +bool CommandProcessor::GetComma(QString & s) +{ + ClearWhitespace(s); + + if ((s.length() > 0) && (s[0] == QChar(','))) + { + s.remove(0, 1); + return true; + } + + return false; +} + +QString CommandProcessor::Process(QString cmd) +{ + response.clear(); + cmd = cmd.toLower(); + + if (Global::tool == TTParallel) + { + response += QString("Parallel: %1").arg(cmd); + bool goodDist = false, goodNum = false, goodBU = false; + + double d = GetDouble(cmd); + int i = 0; + int bu = 0; + + if (!missingParam && !parseError) + { + goodDist = true; + bu = GetUnits(cmd); + + if (!missingParam && !parseError) + goodBU = true; + } + + if (!parseError) + { + if (GetComma(cmd) == false) + { + ClearWhitespace(cmd); + + if (cmd.length() > 0) + Error("missing comma"); + } + else + { + i = GetInt(cmd); + + if (!missingParam && !parseError) + { + goodNum = true; + + ClearWhitespace(cmd); + + if (cmd.length() > 0) + Warning(QString("extra junk '%1' at end ignored").arg(cmd)); + } + } + } + + if (!parseError) + { + if (goodBU) + Global::parallelBU = bu; + + if (goodDist) + Global::parallelDist = d * buInInches[Global::parallelBU]; + + if (goodNum) + Global::parallelNum = i; + + emit(UpdateNeeded()); + } + } + else + { + Error(QString("don't know how to '%1'").arg(cmd)); + } + + return response; +} diff --git a/src/commandprocessor.h b/src/commandprocessor.h new file mode 100644 index 0000000..4321a9f --- /dev/null +++ b/src/commandprocessor.h @@ -0,0 +1,36 @@ +#ifndef __COMMANDPROCESSOR_H__ +#define __COMMANDPROCESSOR_H__ + +#include +#include + +class CommandProcessor: public QObject +{ + Q_OBJECT + + public: + CommandProcessor(); + ~CommandProcessor(); + QString Process(QString); + + signals: + void UpdateNeeded(void); + + private: + void Error(QString); + void Warning(QString); + void AddToResponse(QString, QString, QString); + void ClearWhitespace(QString &); + double GetDouble(QString &); + int GetInt(QString &); + QString GetString(QString &); + int GetUnits(QString &); + bool GetComma(QString &); + + private: + QString response; + bool parseError; + bool missingParam; +}; + +#endif // __COMMANDPROCESSOR_H__ diff --git a/src/consolewidget.cpp b/src/consolewidget.cpp index ce55cc4..56784fc 100644 --- a/src/consolewidget.cpp +++ b/src/consolewidget.cpp @@ -13,24 +13,27 @@ // #include "consolewidget.h" +#include "commandprocessor.h" +#include "promptlineedit.h" +#include "structs.h" -ConsoleWidget::ConsoleWidget(QWidget * parent/*= NULL*/): QWidget(parent) +ConsoleWidget::ConsoleWidget(QWidget * parent/*= NULL*/): QWidget(parent), cmdLine(new PromptLineEdit(this)), screen(new QTextEdit), cmdProc(new CommandProcessor) { - cmdline = new PromptLineEdit(this); - cmdline->setFrame(false); +// cmdLine = new PromptLineEdit(this); + cmdLine->setFrame(false); - screen = new QTextEdit; +// screen = new QTextEdit; screen->setAlignment(Qt::AlignBottom | Qt::AlignLeft); screen->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); screen->setReadOnly(true); QVBoxLayout * mainLayout = new QVBoxLayout; mainLayout->addWidget(screen); - mainLayout->addWidget(cmdline); + mainLayout->addWidget(cmdLine); setLayout(mainLayout); - connect(cmdline, SIGNAL(returnPressed()), this, SLOT(Execute())); + connect(cmdLine, SIGNAL(returnPressed()), this, SLOT(Execute())); QScrollBar * scrollbar = screen->verticalScrollBar(); connect(scrollbar, SIGNAL(rangeChanged(int, int)), this, SLOT(MoveScrollBarToBottom(int, int))); @@ -42,17 +45,27 @@ ConsoleWidget::~ConsoleWidget() void ConsoleWidget::Execute(void) { - screen->append(cmdline->text()); - screen->append("Error: don't know how to '" + cmdline->text() + "'"); - cmdline->clear(); + QString response = cmdProc->Process(cmdLine->text()); + + if (Global::tool == TTNone) + screen->append(cmdLine->text()); + + if (response.length() > 0) + screen->append(response); + + cmdLine->clear(); } -void ConsoleWidget::MoveScrollBarToBottom(int min, int max) +void ConsoleWidget::SetToolPrompt(void) { - Q_UNUSED(min); - screen->verticalScrollBar()->setValue(max); + if (Global::tool == TTParallel) + cmdLine->SetPrompt(QString("Parallel: set distance, number of repeats [%1 [%3]][, %2]").arg(Global::parallelDist / buInInches[Global::parallelBU]).arg(Global::parallelNum).arg(buShortName[Global::parallelBU])); + else + cmdLine->SetPrompt("ATNS"); } -void ConsoleWidget::paintEvent(QPaintEvent * /*event*/) +void ConsoleWidget::MoveScrollBarToBottom(int min, int max) { + Q_UNUSED(min); + screen->verticalScrollBar()->setValue(max); } diff --git a/src/consolewidget.h b/src/consolewidget.h index 147b597..4d18887 100644 --- a/src/consolewidget.h +++ b/src/consolewidget.h @@ -2,7 +2,9 @@ #define __CONSOLEWIDGET_H__ #include -#include "promptlineedit.h" + +class PromptLineEdit; +class CommandProcessor; class ConsoleWidget: public QWidget { @@ -11,17 +13,16 @@ class ConsoleWidget: public QWidget public: ConsoleWidget(QWidget * parent = NULL); ~ConsoleWidget(); + void SetToolPrompt(void); public slots: void Execute(void); void MoveScrollBarToBottom(int min, int max); - protected: - void paintEvent(QPaintEvent * event); - public: - PromptLineEdit * cmdline; + PromptLineEdit * cmdLine; QTextEdit * screen; + CommandProcessor * cmdProc; }; #endif // __CONSOLEWIDGET_H__ diff --git a/src/drawingview.cpp b/src/drawingview.cpp index d8726fe..8ad5562 100644 --- a/src/drawingview.cpp +++ b/src/drawingview.cpp @@ -357,8 +357,8 @@ void DrawingView::paintEvent(QPaintEvent * /*event*/) // 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.SetPen(QPen(QColor(0xFF, 0x7F, 0x00, 0xFF))); + painter.SetBrush(QBrush(QColor(0xFF, 0x7F, 0x00, 0x64))); painter.DrawRect(Global::selection); } @@ -977,6 +977,34 @@ void DrawingView::ToolDraw(Painter * painter) } else if (Global::tool == TTParallel) { + if (Global::toolState == TSPoint1) + { + painter->SetPen(0xFF00FF, 2.0, LSSolid); + painter->SetBrush(QBrush(Qt::NoBrush)); + + double length = Vector::Magnitude(toolObj[0]->p[0], toolPoint[0]); + bool inside = (length >= toolObj[0]->radius[0] ? false : true); + + for(int i=1; i<=Global::parallelNum; i++) + { + if (toolObj[0]->type == OTLine) + { + painter->DrawLine(toolObj[0]->p[0] + (toolPoint[0] * Global::parallelDist * (double)i), toolObj[0]->p[1] + (toolPoint[0] * Global::parallelDist * (double)i)); + } + else if ((toolObj[0]->type == OTCircle) || (toolObj[0]->type == OTArc)) + { + double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ? -1.0 : 1.0)); + + if (radius > 0) + { + if (toolObj[0]->type == OTCircle) + painter->DrawEllipse(toolObj[0]->p[0], radius, radius); + else + painter->DrawArc(toolObj[0]->p[0], radius, toolObj[0]->angle[0], toolObj[0]->angle[1]); + } + } + } + } } } @@ -1757,14 +1785,85 @@ void DrawingView::TrimHandler(int mode, Point p) } } -void DrawingView::ParallelHandler(int mode, Point /*p*/) +void DrawingView::ParallelHandler(int mode, Point p) { switch (mode) { case ToolMouseDown: + if (numHovered == 1) + { + // New selection made... + VPVector hover = GetHovered(); + toolObj[0] = (Object *)hover[0]; + Global::toolState = TSNone; + } + else if ((numHovered == 0) && (toolObj[0] != NULL)) + { + double length = Vector::Magnitude(toolObj[0]->p[0], toolPoint[0]); + bool inside = (length >= toolObj[0]->radius[0] ? false : true); + + // Stamp out new parallel object(s)... + for(int i=1; i<=Global::parallelNum; i++) + { + if (toolObj[0]->type == OTLine) + { + Line * l = new Line(toolObj[0]->p[0] + (toolPoint[0] * Global::parallelDist * (double)i), toolObj[0]->p[1] + (toolPoint[0] * Global::parallelDist * (double)i), Global::penWidth, Global::penColor, Global::penStyle); + // Should probably have a user selection for this whether it goes into the selected objects layer or the global layer... + l->layer = toolObj[0]->layer; + document.objects.push_back(l); + } + else if (toolObj[0]->type == OTCircle) + { + double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ? -1.0 : 1.0)); + + if (radius > 0) + { + Circle * c = new Circle(toolObj[0]->p[0], radius, Global::penWidth, Global::penColor, Global::penStyle); + c->layer = toolObj[0]->layer; + document.objects.push_back(c); + } + } + else if (toolObj[0]->type == OTArc) + { + double radius = toolObj[0]->radius[0] + ((double)i * Global::parallelDist * (inside ? -1.0 : 1.0)); + + if (radius > 0) + { + Arc * a = new Arc(toolObj[0]->p[0], radius, toolObj[0]->angle[0], toolObj[0]->angle[1], Global::penWidth, Global::penColor, Global::penStyle); + a->layer = toolObj[0]->layer; + document.objects.push_back(a); + } + } + } + + // Then reset the state + toolObj[0]->selected = false; + toolObj[0] = NULL; + Global::toolState = TSNone; + } + break; case ToolMouseMove: + if ((numHovered == 0) && toolObj[0] != NULL) + Global::toolState = TSPoint1; + else + Global::toolState = TSNone; + + if (Global::toolState == TSPoint1) + { + // Figure out which side of the object we're on, and draw the preview on that side... + if (toolObj[0]->type == OTLine) + { + Vector normal = Geometry::GetNormalOfPointAndLine(p, (Line *)toolObj[0]); + toolPoint[0] = normal; + } + else if ((toolObj[0]->type == OTCircle) || (toolObj[0]->type == OTArc)) + { + toolPoint[0] = p; + } + } + break; case ToolMouseUp: diff --git a/src/geometry.cpp b/src/geometry.cpp index 31c5257..055c581 100644 --- a/src/geometry.cpp +++ b/src/geometry.cpp @@ -512,3 +512,15 @@ Point Geometry::NearestTo(Point point, Point p1, Point p2) return (l1 < l2 ? p1 : p2); } + +Vector Geometry::GetNormalOfPointAndLine(Point p, Line * l) +{ + Vector normal = Vector::Normal(l->p[0], l->p[1]); + normal.Unit(); + double dir = Vector::Dot(p - l->p[0], normal); + + if (dir < 0) + normal = -normal; + + return normal; +} diff --git a/src/geometry.h b/src/geometry.h index 58252b3..b5e6c6b 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -23,6 +23,7 @@ class Geometry static void FindTangents(Object *, Point); static void FindTangents(Object *, Object *); static Point NearestTo(Point, Point, Point); + static Vector GetNormalOfPointAndLine(Point, Line *); }; #endif // __GEOMETRY_H__ diff --git a/src/global.cpp b/src/global.cpp index 8323641..624c94f 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -26,6 +26,9 @@ QRectF Global::selection; int Global::tool = TTNone; int Global::toolState = TSNone; bool Global::toolSuppressCrosshair = false; +double Global::parallelDist = 1.0; +int Global::parallelNum = 1; +int Global::parallelBU = BUInch; double Global::gridSpacing; Point Global::snapPoint; diff --git a/src/global.h b/src/global.h index 18faa97..597cead 100644 --- a/src/global.h +++ b/src/global.h @@ -42,6 +42,9 @@ class Global static int tool; static int toolState; static bool toolSuppressCrosshair; + static double parallelDist; + static int parallelNum; + static int parallelBU; static Point origin; static double zoom; diff --git a/src/structs.h b/src/structs.h index 0d16ff8..62366ad 100644 --- a/src/structs.h +++ b/src/structs.h @@ -16,6 +16,8 @@ enum ToolType { TTNone, TTLine, TTCircle, TTEllipse, TTArc, TTDimension, TTText, enum ToolState { TSNone, TSPoint1, TSPoint2, TSPoint3, TSPoint4, TSDone }; +enum BasicUnit { BUInch = 0, BUFoot, BUYard, BUMile, BUMM, BUCM, BUM, BUKM, BUCount }; + const char objName[OTCount][16] = { "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension", "Spline", "Text", "Container" @@ -25,6 +27,12 @@ const char dimName[DTCount][32] = { "Circumferential", "Angular", "Leader" }; +const char buShortName[BUCount][8] = { + "in", "ft", "yd", "mi", "mm", "cm", "m", "km" +}; + +const double buInInches[BUCount] = { 1.0, 12.0, 36.0, 1.0/25.4, 1.0/2.54, 1.0/0.0254, 1.0/0.0000254 }; + #define OBJECT_COMMON \ int type; \ uint32_t id; \