Also, add new icons for cut, copy & paste actions.
(actually I think this should be more of the state code handling. Have to
see.)
- Add OSD to Object creation
- - Add layers
- Add blocks
- Add page layout
- Add fill/hatch to Objects
- Trim/Slice tool (to be able to click on a line segment crossing another, and
remove it and it only, or to cut the entity at other entities crossing)
- Make Architektonas an MDI application
+ - Parallel tool (be able to make copy of object that's parallel to original)
Stuff That's Done
-----------------
+ - Add layers [Shamus 2015ish]
- Add pen color/style/width to Objects [Shamus 2017ish]
- Fix loading and saving code [Shamus 2016ish]
- Manipulate Dimension [Shamus 2014-03-20]
# Architektonas Qt project file
#
# by James Hammons
-# Copyright (C) 2015 Underground Software
+# Copyright (C) 2020 Underground Software
#
# See the README and GPLv3 files for licensing and warranty information
#
<file>layer-up.png</file>
<file>layer-down.png</file>
<file>block-import.png</file>
+ <file>editcut2.png</file>
+ <file>editcopy2.png</file>
+ <file>editpaste2.png</file>
</qresource>
</RCC>
}
+Point Geometry::Midpoint(Line * line)
+{
+ return Point((line->p[0].x + line->p[1].x) / 2.0,
+ (line->p[0].y + line->p[1].y) / 2.0);
+}
+
+
/*
How to find the tangent of a point off a circle:
static void CheckLineToCircleIntersection(Object *, Object *);
static void FindAnglesForSides(double s1, double s2, double s3, double * a1, double * a2, double * a3);
static Point GetPointForParameter(Object *, double);
+ static Point Midpoint(Line *);
};
#endif // __GEOMETRY_H__
// WHO WHEN WHAT
// --- ---------- -----------------------------------------------------------
// JLH 04/01/2011 Created this file
-// JLH 03/15/2016 Added Tau constants, removed Pi constants
+// JLH 03/15/2016 Added Tau constants, removed Pi constants (Pi is wrong)
//
#define TAU 6.28318530717958647692528676655
// objectwidget.cpp: Object tweaking widget
//
// Part of the Architektonas Project
-// (C) 2016 Underground Software
+// (C) 2020 Underground Software
// See the README and GPLv3 files for licensing and warranty information
//
// JLH = James Hammons <jlhamm@acm.org>
ObjectWidget::ObjectWidget(void): QWidget()
{
-#if 0
- QListWidget * qlw = new QListWidget;
- QListWidgetItem * qli1 = new QListWidgetItem(qlw);
- QListWidgetItem * qli2 = new QListWidgetItem(qlw);
- QListWidgetItem * qli3 = new QListWidgetItem(qlw);
- QListWidgetItem * qli4 = new QListWidgetItem(qlw);
- QListWidgetItem * qli5 = new QListWidgetItem(qlw);
-#endif
label = new QLabel;
-#if 0
- QPushButton * pb1 = new QPushButton("+");
- QPushButton * pb2 = new QPushButton("-");
- QPushButton * pb3 = new QPushButton("Edit");
- QPushButton * pb4 = new QPushButton("Import");
-#else
QToolButton * pb1 = new QToolButton;
QToolButton * pb2 = new QToolButton;
QToolButton * pb3 = new QToolButton;
pb2->setToolTip(tr("Remove block"));
pb3->setToolTip(tr("Edit block"));
pb4->setToolTip(tr("Import block"));
-#endif
QHBoxLayout * hbox1 = new QHBoxLayout;
hbox1->addWidget(pb1);
QVBoxLayout * mainLayout = new QVBoxLayout;
mainLayout->addWidget(label);
+ mainLayout->addStretch();
mainLayout->addLayout(hbox1);
setLayout(mainLayout);
void ObjectWidget::ShowInfo(Object * obj)
{
- const char objName[OTCount][16] = {
- "None", "Line", "Circle", "Ellipse", "Arc", "Polygon", "Dimension", "Spline", "Text", "Container"
- };
- const char dimName[DTCount][32] = {
- "Linear", "Vertical", "Horizontal", "Radial", "Diametric",
- "Circumferential", "Angular", "Leader"
- };
-
// Sanity check
if (obj == NULL)
return;
- QString s = QString("%1<br><br>").arg(QString(objName[obj->type]));
+ QString s = QString("<b>%1</b><br><br>").arg(QString(objName[obj->type]));
switch (obj->type)
{
case OTArc:
s += QString("Center: <%1, %2><br>Radius: %3<br>Start: %4°<br>Span: %5°<br>").arg(obj->p[0].x).arg(obj->p[0].y).arg(obj->radius[0]).arg(obj->angle[0] * RADIANS_TO_DEGREES).arg(obj->angle[1] * RADIANS_TO_DEGREES);
break;
- break;
case OTPolygon:
break;
case OTDimension:
+ {
+ Dimension * d = (Dimension *)obj;
+ s += QString("Type: %1<br>").arg(dimName[d->subtype]);
break;
+ }
case OTSpline:
break;
+++ /dev/null
-// triangulateaction.cpp: Action class for creating triangles from lines
-//
-// Part of the Architektonas Project
-// (C) 2014 Underground Software
-// See the README and GPLv3 files for licensing and warranty information
-//
-// JLH = James Hammons <jlhamm@acm.org>
-//
-// WHO WHEN WHAT
-// --- ---------- ------------------------------------------------------------
-// JLH 05/07/2014 Created this file
-//
-
-#include "triangulateaction.h"
-#include "applicationwindow.h"
-#include "circle.h"
-#include "container.h"
-#include "drawingview.h"
-#include "geometry.h"
-#include "line.h"
-#include "mathconstants.h"
-#include "painter.h"
-
-
-enum { FIRST_LINE, SECOND_LINE, THIRD_LINE };
-
-
-TriangulateAction::TriangulateAction(): state(FIRST_LINE),// t(0), u(1.0),
- line1(NULL), line2(NULL), line3(NULL),
- doc(&(ApplicationWindow::drawing->document))
-//, line(NULL),
-// shiftWasPressedOnNextPoint(false), ctrlWasPressed(false),
-// mirror(new Container(Vector()))
-{
-// ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
-// mirror->Save();
-}
-
-
-TriangulateAction::~TriangulateAction()
-{
-}
-
-
-/*virtual*/ void TriangulateAction::Draw(Painter * painter)
-{
- Object * obj = doc->lastObjectHovered;
-
- if (obj == NULL)
- return;
-
- // This assumes a Line, but it might not be!
- painter->SetPen(QPen(Qt::blue, 2.0, Qt::DotLine));
-// Vector v(((Line *)obj)->position, ((Line *)obj)->endpoint);
- Point p1 = ((Line *)obj)->position;
- Point p2 = ((Line *)obj)->endpoint;
- painter->DrawLine(p1, p2);
-#if 0
- if (state == FIRST_POINT)
- {
- painter->DrawHandle(p1);
- }
- else
- {
- Vector reflectedP2 = -(p2 - p1);
- Point newP2 = p1 + reflectedP2;
- painter->DrawLine(newP2, p2);
- painter->DrawHandle(p1);
-
- double absAngle = (Vector(p2 - p1).Angle()) * RADIANS_TO_DEGREES;
-
- // Keep the angle between 0 and 180 degrees
- if (absAngle > 180.0)
- absAngle -= 180.0;
-
- QString text = QChar(0x2221) + QObject::tr(": %1");
- text = text.arg(absAngle);
-
- if (ctrlWasPressed)
- text += " (Copy)";
-
- painter->DrawInformativeText(text);
-
- // Draw the mirror only if there's been a line to mirror around
- if (p1 != p2)
- mirror->Draw(painter);
- }
-#endif
-}
-
-
-/*virtual*/ void TriangulateAction::MouseDown(Vector /*point*/)
-{
-#if 0
-// this is not accurate enough. need to use the actual intersection point, not
-// just the parameter(s).
- Object * toTrim = doc->lastObjectHovered;
-
- if (toTrim == NULL)
- return;
-
-//it would be nice to do it like this, but if we bisect the object, we have to
-//create an extra one...
-// toTrim->Trim(t, u);
-
- 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);
-// u = 1.0;
- }
- else if (u == 1.0)
- {
- ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
-// t = 0;
- }
- 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);
-// t = 0, u = 1.0;
- }
-
- doc->lastObjectHovered = NULL;
-#endif
-}
-
-
-/*virtual*/ void TriangulateAction::MouseMoved(Vector point)
-{
-#if 0
- if (state == FIRST_POINT)
- p1 = point;
-// else
-// {
-// p2 = point;
-// mirror->Restore();
-// mirror->Mirror(p1, p2);
-// }
-#endif
-
-#if 0
-// Container & doc = ApplicationWindow::drawing->document;
-// int items = doc.ItemsSelected();
- Object * toTrim = doc->lastObjectHovered;
-// double closestPt1 = 0, closestPt2 = 1.0;
- 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<Object *>::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
-}
-
-
-/*virtual*/ void TriangulateAction::MouseReleased(void)
-{
-#if 0
- if (state == FIRST_POINT)
- {
- p2 = p1;
- state = NEXT_POINT;
- }
- else if (state == NEXT_POINT)
- {
- if (!ctrlWasPressed)
- {
- state = FIRST_POINT;
- ApplicationWindow::drawing->document.MirrorSelected(p1, p2);
-
- mirror->Clear();
- ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
- mirror->Save();
- }
- else
- {
- mirror->CopyContentsTo(&(ApplicationWindow::drawing->document));
- }
- }
-#endif
- Object * obj = doc->lastObjectHovered;
-
- if (obj == NULL)
- return;
-
- if (obj->type != OTLine)
- return;
-
- if (state == FIRST_LINE)
- {
- line1 = (Line *)obj;
- line1->state = OSSelected;
- state = SECOND_LINE;
- }
- else if (state == SECOND_LINE)
- {
- line2 = (Line *)obj;
- line2->state = OSSelected;
- state = THIRD_LINE;
- }
- else if (state == THIRD_LINE)
- {
- line3 = (Line *)obj;
- state = FIRST_LINE;
- Triangulate();
- }
-}
-
-
-/*virtual*/ void TriangulateAction::KeyDown(int /*key*/)
-{
-#if 0
- if ((key == Qt::Key_Shift) && (state == NEXT_POINT))
- {
- shiftWasPressedOnNextPoint = true;
- p1Save = p1;
- p1 = p2;
- state = FIRST_POINT;
- emit NeedRefresh();
- }
- else if (key == Qt::Key_Control)
- {
- ctrlWasPressed = true;
- emit NeedRefresh();
- }
-#endif
-#if 0
- if ((t == 0) && (u == 1.0))
- return;
-
- t = 0, u = 1.0;
-#endif
-}
-
-
-/*virtual*/ void TriangulateAction::KeyReleased(int /*key*/)
-{
-#if 0
- if ((key == Qt::Key_Shift) && shiftWasPressedOnNextPoint)
- {
- shiftWasPressedOnNextPoint = false;
- p2 = p1;
- p1 = p1Save;
- state = NEXT_POINT;
- emit NeedRefresh();
- }
- else if (key == Qt::Key_Control)
- {
- ctrlWasPressed = false;
- emit NeedRefresh();
- }
-#endif
-}
-
-
-void TriangulateAction::Triangulate(void)
-{
-// N.B.: Should connect the line segments together, once made into a triangle...
- double length2 = Vector(line2->position, line2->endpoint).Magnitude();
- double length3 = Vector(line3->position, line3->endpoint).Magnitude();
-#if 0
- double angle1, angle2, angle3;
- double length1 = Vector(line1->position, line1->endpoint).Magnitude();
- Geometry::FindAnglesForSides(length1, length2, length3, &angle1, &angle2, &angle3);
-printf("Triangulate: l1=%lf, l2=%lf, l3=%lf, a1=%lf, a2=%lf, a3=%lf\n", length1, length2, length3, angle1, angle2, angle3);
-
- // We use line1 as the base. Move the other lines to it.
- double line1Angle = Vector(line1->position, line1->endpoint).Angle();
- double newLine2Angle = line1Angle + angle3;
- Vector v;
- v.SetAngleAndLength(newLine2Angle, length2);
-printf(" line1Angle=%lf, newLine2Angle=%lf\n", line1Angle, newLine2Angle);
-
- line2->position = line1->endpoint;
- line2->endpoint = line1->endpoint - v;
-
-// double line2Angle = Vector(line2->position, line2->endpoint).Angle();
-// double newLine3Angle = line2Angle + angle1;
-// Vector v2;
-// v2.SetAngleAndLength(newLine3Angle, length3);
-#else
- Circle c1(line1->position, length2), c2(line1->endpoint, length3);
- Point p1, p2;
- int n = Geometry::Intersects(&c1, &c2, 0, 0, 0, 0, &p1, &p2);
-//printf("Circle-circle intersections: n=%i, <%lf, %lf, %lf>, <%lf, %lf, %lf>\n", n, p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);
-#endif
- line2->position = line1->endpoint;
- line2->endpoint = p1;
-
- line3->position = line2->endpoint;
- line3->endpoint = line1->position;
-
- line1->state = OSInactive;
- line2->state = OSInactive;
-}
-
+++ /dev/null
-#ifndef __TRIANGULATEACTION_H__
-#define __TRIANGULATEACTION_H__
-
-#include "action.h"
-
-class Container;
-class Line;
-
-class TriangulateAction: public Action
-{
- public:
- TriangulateAction();
- ~TriangulateAction();
-
- virtual void Draw(Painter *);
- virtual void MouseDown(Vector);
- virtual void MouseMoved(Vector);
- virtual void MouseReleased(void);
- virtual void KeyDown(int);
- virtual void KeyReleased(int);
-
- private:
- void Triangulate(void);
-
- private:
- int state;
- Line * line1, * line2, * line3;
-// Vector p1, p2, p1Save;
-// double t, u;
- Container * doc;
-};
-
-#endif // __TRIANGULATEACTION_H__
-
+++ /dev/null
-// trimaction.cpp: Action class for mirroring selected objects
-//
-// Part of the Architektonas Project
-// (C) 2011 Underground Software
-// See the README and GPLv3 files for licensing and warranty information
-//
-// JLH = James Hammons <jlhamm@acm.org>
-//
-// WHO WHEN WHAT
-// --- ---------- ------------------------------------------------------------
-// JLH 08/27/2011 Created this file
-//
-
-#include "trimaction.h"
-#include "applicationwindow.h"
-#include "container.h"
-#include "drawingview.h"
-#include "geometry.h"
-#include "line.h"
-#include "mathconstants.h"
-#include "painter.h"
-
-
-enum { FIRST_POINT, NEXT_POINT };
-
-
-TrimAction::TrimAction(): state(FIRST_POINT), t(0), u(1.0),
- doc(&(ApplicationWindow::drawing->document))
-//, line(NULL),
-// shiftWasPressedOnNextPoint(false), ctrlWasPressed(false),
-// mirror(new Container(Vector()))
-{
-// ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
-// mirror->Save();
-}
-
-
-TrimAction::~TrimAction()
-{
-}
-
-
-/*virtual*/ void TrimAction::Draw(Painter * painter)
-{
- Object * obj = doc->lastObjectHovered;
-
- if (obj == NULL)
- return;
-
- // This assumes a Line, but it might not be!
- painter->SetPen(QPen(Qt::black, 2.0, Qt::DotLine));
- Vector v(((Line *)obj)->position, ((Line *)obj)->endpoint);
- Point p1 = ((Line *)obj)->position + (v * t);
- Point p2 = ((Line *)obj)->position + (v * u);
- painter->DrawLine(p1, p2);
-#if 0
- if (state == FIRST_POINT)
- {
- painter->DrawHandle(p1);
- }
- else
- {
- Vector reflectedP2 = -(p2 - p1);
- Point newP2 = p1 + reflectedP2;
- painter->DrawLine(newP2, p2);
- painter->DrawHandle(p1);
-
- double absAngle = (Vector(p2 - p1).Angle()) * RADIANS_TO_DEGREES;
-
- // Keep the angle between 0 and 180 degrees
- if (absAngle > 180.0)
- absAngle -= 180.0;
-
- QString text = QChar(0x2221) + QObject::tr(": %1");
- text = text.arg(absAngle);
-
- if (ctrlWasPressed)
- text += " (Copy)";
-
- painter->DrawInformativeText(text);
-
- // Draw the mirror only if there's been a line to mirror around
- if (p1 != p2)
- mirror->Draw(painter);
- }
-#endif
-}
-
-
-/*virtual*/ void TrimAction::MouseDown(Vector /*point*/)
-{
-// this is not accurate enough. need to use the actual intersection point, not
-// just the parameter(s).
- Object * toTrim = doc->lastObjectHovered;
-
- if (toTrim == NULL)
- return;
-
-//it would be nice to do it like this, but if we bisect the object, we have to
-//create an extra one...
-// toTrim->Trim(t, u);
-
- 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);
-// u = 1.0;
- }
- else if (u == 1.0)
- {
- ((Line *)toTrim)->endpoint = ((Line *)toTrim)->position + (v * t);
-// t = 0;
- }
- 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);
-// t = 0, u = 1.0;
- }
-
- doc->lastObjectHovered = NULL;
-}
-
-
-/*virtual*/ void TrimAction::MouseMoved(Vector point)
-{
-#if 0
- if (state == FIRST_POINT)
- p1 = point;
-// else
-// {
-// p2 = point;
-// mirror->Restore();
-// mirror->Mirror(p1, p2);
-// }
-#endif
-// Container & doc = ApplicationWindow::drawing->document;
-// int items = doc.ItemsSelected();
- Object * toTrim = doc->lastObjectHovered;
-// double closestPt1 = 0, closestPt2 = 1.0;
- 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<Object *>::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;
- }
- }
-}
-
-
-/*virtual*/ void TrimAction::MouseReleased(void)
-{
-#if 0
- if (state == FIRST_POINT)
- {
- p2 = p1;
- state = NEXT_POINT;
- }
- else if (state == NEXT_POINT)
- {
- if (!ctrlWasPressed)
- {
- state = FIRST_POINT;
- ApplicationWindow::drawing->document.MirrorSelected(p1, p2);
-
- mirror->Clear();
- ApplicationWindow::drawing->document.CopySelectedContentsTo(mirror);
- mirror->Save();
- }
- else
- {
- mirror->CopyContentsTo(&(ApplicationWindow::drawing->document));
- }
- }
-#endif
-}
-
-
-/*virtual*/ void TrimAction::KeyDown(int /*key*/)
-{
-#if 0
- if ((key == Qt::Key_Shift) && (state == NEXT_POINT))
- {
- shiftWasPressedOnNextPoint = true;
- p1Save = p1;
- p1 = p2;
- state = FIRST_POINT;
- emit NeedRefresh();
- }
- else if (key == Qt::Key_Control)
- {
- ctrlWasPressed = true;
- emit NeedRefresh();
- }
-#endif
- if ((t == 0) && (u == 1.0))
- return;
-
- t = 0, u = 1.0;
-}
-
-
-/*virtual*/ void TrimAction::KeyReleased(int /*key*/)
-{
-#if 0
- if ((key == Qt::Key_Shift) && shiftWasPressedOnNextPoint)
- {
- shiftWasPressedOnNextPoint = false;
- p2 = p1;
- p1 = p1Save;
- state = NEXT_POINT;
- emit NeedRefresh();
- }
- else if (key == Qt::Key_Control)
- {
- ctrlWasPressed = false;
- emit NeedRefresh();
- }
-#endif
-}
-
+++ /dev/null
-#ifndef __TRIMACTION_H__
-#define __TRIMACTION_H__
-
-#include "action.h"
-
-class Container;
-class Line;
-
-class TrimAction: public Action
-{
- public:
- TrimAction();
- ~TrimAction();
-
- virtual void Draw(Painter *);
- virtual void MouseDown(Vector);
- virtual void MouseMoved(Vector);
- virtual void MouseReleased(void);
- virtual void KeyDown(int);
- virtual void KeyReleased(int);
-
- private:
- int state;
- Vector p1, p2, p1Save;
- double t, u;
- Container * doc;
-};
-
-#endif // __TRIMACTION_H__
-
{
double mag = Magnitude();
- // If the magnitude of the vector is zero, then the Unit vector is undefined...
+ // If the magnitude of the vector is zero, then the Unit vector is
+ // undefined...
if (mag == 0)
return Vector(0, 0, 0);
double Vector::Angle(void)
{
- // acos returns a value between zero and PI, which means we don't know which
- // quadrant the angle is in... Though, if the y-coordinate of the vector is
- // negative, that means that the angle is in quadrants III - IV.
+ // acos returns a value between zero and TAU/2, which means we don't know
+ // which quadrant the angle is in... However, if the y-coordinate of the
+ // vector is negative, that means that the angle is in quadrants III - IV.
double rawAngle = acos(Unit().x);
-#if 0
- double correctedAngle = (y < 0 ? (2.0 * PI) - rawAngle : rawAngle);
-#else
double correctedAngle = (y < 0 ? TAU - rawAngle : rawAngle);
-#endif
return correctedAngle;
}
// This is done using the following formula:
// (a . b) = ||a|| ||b|| cos(theta)
// However, have to check for two degenerate cases, where a = cb:
- // 1, if c > 0, theta = 0; 2, if c < 0, theta = 180°.
+ // 1) if c > 0, theta = 0; 2) if c < 0, theta = 180°.
// Also, the vectors a & b have to be non-zero.
// Also, have to check using an epsilon because acos will not return an
- // exact value if the vectors are orthogonal
+ // exact value if the vectors are orthogonal.
if (a.isZero() || b.isZero())
return 0;
// Various structures used for 3 dimensional imaging
//
// by James Hammons
-// (C) 2001, 2014 Underground Software
+// (C) 2001, 2018 Underground Software
//
#ifndef __VECTOR_H__