src/baseunittab.h \
src/blockitemwidget.h \
src/blockwidget.h \
+ src/commandprocessor.h \
src/consolewidget.h \
src/drawingview.h \
src/fileio.h \
src/baseunittab.cpp \
src/blockitemwidget.cpp \
src/blockwidget.cpp \
+ src/commandprocessor.cpp \
src/consolewidget.cpp \
src/drawingview.cpp \
src/fileio.cpp \
#include <QPrintPreviewDialog>
#include "about.h"
#include "blockwidget.h"
+#include "commandprocessor.h"
#include "consolewidget.h"
#include "drawingview.h"
#include "fileio.h"
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);
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)
drawing->update();
}
+void ApplicationWindow::UpdateFromCommand(void)
+{
+ cw->SetToolPrompt();
+ drawing->update();
+}
+
void ApplicationWindow::SnapToGridTool(void)
{
Global::snapToGrid = snapToGridAct->isChecked();
{
ClearUIToolStatesExcept(parallelAct);
SetInternalToolStates();
+ Global::toolSuppressCrosshair = true;
}
void ApplicationWindow::TriangulateTool(void)
else
Global::tool = TTNone;
+ cw->SetToolPrompt();//cw->cmdline);
drawing->update();
}
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);
class AboutWindow;
class DrawingView;
class QLabel;
+class ConsoleWidget;
class ApplicationWindow: public QMainWindow
{
void SelectAllObjects(void);
void UpdateZoom(void);
void MoveToLayer(void);
+ void UpdateFromCommand(void);
signals:
void ReloadLayers(void);
QString documentName;
QLineEdit * baseUnitInput;
QLineEdit * dimensionSizeInput;
+ ConsoleWidget * cw;
QSettings settings;
--- /dev/null
+//
+// 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 <jlhamm@acm.org>
+//
+// 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 += "<br>";
+
+ response += QString("<font color=%2>%3: %1</font>").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;
+}
--- /dev/null
+#ifndef __COMMANDPROCESSOR_H__
+#define __COMMANDPROCESSOR_H__
+
+#include <QObject>
+#include <QString>
+
+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__
//
#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)));
void ConsoleWidget::Execute(void)
{
- screen->append(cmdline->text());
- screen->append("<font color=red>Error: don't know how to '" + cmdline->text() + "'</font>");
- 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);
}
#define __CONSOLEWIDGET_H__
#include <QtWidgets>
-#include "promptlineedit.h"
+
+class PromptLineEdit;
+class CommandProcessor;
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__
// 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);
}
}
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]);
+ }
+ }
+ }
+ }
}
}
}
}
-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:
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;
+}
static void FindTangents(Object *, Point);
static void FindTangents(Object *, Object *);
static Point NearestTo(Point, Point, Point);
+ static Vector GetNormalOfPointAndLine(Point, Line *);
};
#endif // __GEOMETRY_H__
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;
static int tool;
static int toolState;
static bool toolSuppressCrosshair;
+ static double parallelDist;
+ static int parallelNum;
+ static int parallelBU;
static Point origin;
static double zoom;
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"
"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; \