Commit 2312e40b authored by JasonPries's avatar JasonPries
Browse files

Fix memory leaks

parent dc0aee72
......@@ -139,7 +139,7 @@ double CircularArc::a(double s, bool orientation) const {
}
double CircularArc::da(double s, bool orientation) const {
// #TODO, Maybe should calculate this by taking the derivative of the angle in the gradient direction and projection, present implementation may cause errors in the future
// TODO: Maybe should calculate this by taking the derivative of the angle in the gradient direction and projection, present implementation may cause errors in the future
double sgn = SIGN(arc_angle());
if (orientation) {
......@@ -172,7 +172,7 @@ std::pair<double, double> CircularArc::supremum() const {
yy = v.y();
val = sqrt(xx * xx + yy * yy);
if(val > sup) {
if (val > sup) {
x = xx;
y = yy;
sup = val;
......@@ -183,7 +183,7 @@ std::pair<double, double> CircularArc::supremum() const {
double ang = s_to_a(par);
double cross = abs(x * cos(ang) + y * sin(ang)) / sup; // cross product of vector from origin to point and tangent vector
return std::pair<double,double>(sup, cross);
return std::pair<double, double>(sup, cross);
}
bool CircularArc::on_manifold(const double x, const double y) const {
......@@ -199,32 +199,22 @@ bool CircularArc::on_manifold(const double x, const double y) const {
}
}
bool CircularArc::is_identical(std::shared_ptr<Curve> const &c) const {
//const CircularArc *cc = dynamic_cast<const CircularArc *>(c);
auto cc = std::dynamic_pointer_cast<CircularArc>(c);
Direction CircularArc::is_identical(std::shared_ptr<Curve> const &c) const {
std::shared_ptr<CircularArc> cc = std::dynamic_pointer_cast<CircularArc>(c);
if (cc.get() == nullptr) {
return false;
return Direction::None;
} else {
return is_identical(
cc->radius(),
cc->center()->x(),
cc->center()->y(),
cc->start()->x(),
cc->start()->y(),
cc->end()->x(),
cc->end()->y());
return is_identical(cc->radius(), cc->center()->x(), cc->center()->y(), cc->start()->x(), cc->start()->y(),
cc->end()->x(), cc->end()->y());
}
}
bool CircularArc::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const {
//const CircularArc *cc = dynamic_cast<const CircularArc *>(c);
auto cc = std::dynamic_pointer_cast<CircularArc>(c);
Direction CircularArc::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const {
std::shared_ptr<CircularArc> cc = std::dynamic_pointer_cast<CircularArc>(c);
if (cc.get() == nullptr) {
return false;
return Direction::None;
} else {
double xc, yc, xs, ys, xe, ye;
......@@ -236,23 +226,21 @@ bool CircularArc::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<
}
}
bool CircularArc::is_identical(const double r, const double xc, const double yc, const double xs, const double ys,
const double xe, const double ye) const {
Direction CircularArc::is_identical(const double r, const double xc, const double yc, const double xs, const double ys,
const double xe, const double ye) const {
double tol = FLT_EPSILON * radius();
return abs(radius() - r) < tol
&& abs(center()->x() - xc) < tol
&& abs(center()->y() - yc) < tol
&& abs(start()->x() - xs) < tol
&& abs(start()->y() - ys) < tol
&& abs(end()->x() - xe) < tol
&& abs(end()->y() - ye) < tol;
if (abs(radius() - r) < tol && abs(center()->x() - xc) < tol && abs(center()->y() - yc) < tol &&
abs(start()->x() - xs) < tol && abs(start()->y() - ys) < tol && abs(end()->x() - xe) < tol &&
abs(end()->y() - ye) < tol) {
return Direction::Forward;
} else {
return Direction::None;
}
}
bool CircularArc::is_coincident(std::shared_ptr<Curve> const &c) const {
//const CircularArc *cc = dynamic_cast<const CircularArc *>(c);
auto cc = std::dynamic_pointer_cast<CircularArc>(c);
std::shared_ptr<CircularArc> cc = std::dynamic_pointer_cast<CircularArc>(c);
if (cc.get() == nullptr) {
return false;
......
......@@ -27,10 +27,16 @@ public:
double radius() const { return Radius->value(); };
void get_verticies(std::list<std::shared_ptr<Vertex>> &v) const override {
v.push_back(Start);
v.push_back(End);
v.push_back(Center);
void get_verticies(std::list<std::shared_ptr<Vertex>> &v, Direction dir = Direction::Forward) const override {
if (dir == Direction::Forward) {
v.push_back(Start);
v.push_back(End);
v.push_back(Center);
} else if (dir == Direction::Reverse) {
v.push_back(End);
v.push_back(Start);
v.push_back(Center);
}
};
size_t set_equation_index(size_t i) override {
......@@ -60,9 +66,9 @@ public:
using Curve::on_segment;
bool is_identical(std::shared_ptr<Curve> const &c) const override;
Direction is_identical(std::shared_ptr<Curve> const &c) const override;
bool is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const override;
Direction is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const override;
bool is_coincident(std::shared_ptr<Curve> const &c) const override;
......@@ -78,7 +84,7 @@ protected:
bool on_segment(const double x, const double y) const override;
bool is_identical(const double r, const double xc, const double yc, const double xs, const double ys, const double xe, const double ye) const;
Direction is_identical(const double r, const double xc, const double yc, const double xs, const double ys, const double xe, const double ye) const;
private:
double s_to_a(double s) const;
......
......@@ -3,7 +3,14 @@
#include "Sketch.h"
class Curve : public SketchElement {
enum class Direction : signed int {
None = 0,
Forward = 1,
Reverse = -1
};
class Curve : public SketchElement { // TODO: Add bool Direction. Use direction in MirrorCopy and RotateCopy
public:
friend class MirrorCopy;
......@@ -22,7 +29,7 @@ public:
std::shared_ptr<Vertex> end() const { return End; };
virtual void get_verticies(std::list<std::shared_ptr<Vertex>> &v) const = 0;
virtual void get_verticies(std::list<std::shared_ptr<Vertex>> &v, Direction dir = Direction::Forward) const = 0;
// Calculation
// #TODO: Add const to double_t and bool arguments where appropriate
......@@ -46,8 +53,8 @@ public:
virtual bool on_segment(std::shared_ptr<Vertex> const &v, std::shared_ptr<Vertex> const &origin, double const angle) const final;
// Curve-Curve Comparison
virtual bool is_identical(std::shared_ptr<Curve> const &c) const = 0; // true if (input curve) XOR (object curve) is a set with measure < tol
virtual bool is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const = 0;
virtual Direction is_identical(std::shared_ptr<Curve> const &c) const = 0; // true if (input curve) XOR (object curve) is a set with measure < tol
virtual Direction is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const = 0;
// #TODO: virtual bool is_overlapping(const Curve* c) const = 0; // true if (input curve) AND (object curve) is a set with measure > tol
// #TODO: virtual bool is_overlapping(const Curve* c, const Vertex* origin, const double_t angle) const = 0;
......
......@@ -56,7 +56,7 @@ std::pair<double, double> LineSegment::supremum() const {
cross = abs(xe * dy - ye * dx) / le;
}
return std::pair<double,double>(sup,cross);
return std::pair<double, double>(sup, cross);
};
bool LineSegment::on_manifold(const double x, const double y) const {
......@@ -105,21 +105,21 @@ bool LineSegment::on_segment(const double x, const double y) const {
}
}
bool LineSegment::is_identical(std::shared_ptr<Curve> const &c) const {
auto l = std::dynamic_pointer_cast<LineSegment>(c);
Direction LineSegment::is_identical(std::shared_ptr<Curve> const &c) const {
std::shared_ptr<LineSegment> l = std::dynamic_pointer_cast<LineSegment>(c);
if (l.get() == nullptr) {
return false;
return Direction::None;
} else {
return is_identical(l->start()->x(), l->start()->y(), l->end()->x(), l->end()->y());
}
}
bool LineSegment::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const {
auto l = std::dynamic_pointer_cast<LineSegment>(c);
Direction LineSegment::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const {
std::shared_ptr<LineSegment> l = std::dynamic_pointer_cast<LineSegment>(c);
if (l.get() == nullptr) {
return false;
return Direction::None;
} else {
double xs, ys, xe, ye;
......@@ -130,7 +130,7 @@ bool LineSegment::is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<
}
}
bool LineSegment::is_identical(const double x0, const double y0, const double x1, const double y1) const {
Direction LineSegment::is_identical(const double x0, const double y0, const double x1, const double y1) const {
double xs = start()->x();
double ys = start()->y();
......@@ -139,12 +139,17 @@ bool LineSegment::is_identical(const double x0, const double y0, const double x1
double tol = FLT_EPSILON * std::fmax(abs(xs - xe), abs(ys - ye)); // #TODO: L1 norm is more efficient tolerance strategy
return (abs(xs - x0) < tol && abs(ys - y0) < tol && abs(xe - x1) < tol && abs(ye - y1) < tol)
|| (abs(xs - x1) < tol && abs(ys - y1) < tol && abs(xe - x0) < tol && abs(ye - y0) < tol);
if (abs(xs - x0) < tol && abs(ys - y0) < tol && abs(xe - x1) < tol && abs(ye - y1) < tol) {
return Direction::Forward;
} else if (abs(xs - x1) < tol && abs(ys - y1) < tol && abs(xe - x0) < tol && abs(ye - y0) < tol) {
return Direction::Reverse;
} else {
return Direction::None;
}
}
bool LineSegment::is_coincident(std::shared_ptr<Curve> const &c) const {
auto l = std::dynamic_pointer_cast<LineSegment>(c);
std::shared_ptr<LineSegment> l = std::dynamic_pointer_cast<LineSegment>(c);
if (l.get() == nullptr) {
return false;
......
......@@ -19,17 +19,20 @@ public:
friend class Vertical;
//Constructors
LineSegment() : Curve() {};
LineSegment(LineSegment const *l) : Curve(l->Start, l->End, l->ForConstruction) {};
LineSegment(std::shared_ptr<Vertex> v0, std::shared_ptr<Vertex> v1, bool fc = false) : Curve(v0, v1, fc) {};
// Virtual Function Implementation
void get_verticies(std::list<std::shared_ptr<Vertex>> &v) const override {
v.push_back(Start);
v.push_back(End);
void get_verticies(std::list<std::shared_ptr<Vertex>> &v, Direction dir = Direction::Forward) const override {
if (dir == Direction::Forward) {
v.push_back(Start);
v.push_back(End);
} else if (dir == Direction::Reverse) {
v.push_back(End);
v.push_back(Start);
}
};
size_t set_equation_index(size_t i) override {
......@@ -41,7 +44,6 @@ public:
void update(Eigen::MatrixXd &J, Eigen::VectorXd &r) override {};
// Calculation
sPoint point(double s) const override;
Vertex tangent(double s, bool orientation) const override;
......@@ -56,19 +58,16 @@ public:
std::pair<double, double> supremum() const override;
// Curve-Vertex Comparison
using Curve::on_manifold;
using Curve::on_segment;
// Curve-Curve Comparison
bool is_identical(std::shared_ptr<Curve> const &c) const override;
Direction is_identical(std::shared_ptr<Curve> const &c) const override;
bool is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const override;
Direction is_identical(std::shared_ptr<Curve> const &c, std::shared_ptr<Vertex> const &origin, double const angle) const override;
bool is_coincident(std::shared_ptr<Curve> const &c) const override;
// Modification
std::shared_ptr<Curve> clone() const override { return std::make_shared<LineSegment>(this); };
void replace_verticies(std::vector<std::shared_ptr<Vertex>> oldv, std::vector<std::shared_ptr<Vertex>> newv) override;
......@@ -78,7 +77,7 @@ protected:
bool on_segment(const double x, const double y) const override;
bool is_identical(const double x0, const double y0, const double x1, const double y1) const;
Direction is_identical(const double x0, const double y0, const double x1, const double y1) const;
};
#endif //OERSTED_LINESEGMENT_H
#include "Sketch.hpp"
RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_ptr<Vertex> center, double angle, size_t copies, bool remove_internal) {
RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_ptr<Vertex> center, double angle,
size_t copies, bool remove_internal) {
// Creates rotated copies of the input curves about an vertex
// #TODO: Need to rearrange code and reserve vector sizes in a way that makes more sense (much code copied from MirrorCopy constructor)
// #TODO: Restructure to obviate the need for local_curves and local_verticies
// #TODO: Three Groups: Leading Curves, Lagging Curves, Internal Curves
// #TODO: Three Groups (?): Leading Verticies, Lagging Verticies, Internal Verticies
// TODO: Three Groups (?): Leading Verticies, Lagging Verticies, Internal Verticies
// TODO: Check for complete elimination of leading/lagging curves
// Assign properties
Input = input;
......@@ -19,84 +18,82 @@ RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_pt
std::vector<std::shared_ptr<Curve>> leading_curves;
std::vector<std::shared_ptr<Curve>> lagging_curves;
std::vector<bool> is_lead_or_lag(input.size(),false);
std::vector<bool> is_lead_or_lag(input.size(), false);
// Find leading/lagging curve pairs
// Find leading/lagging curve and vertex pairs
std::list<std::shared_ptr<Vertex>> lead_vlist;
std::list<std::shared_ptr<Vertex>> lag_vlist;
for (size_t i = 0; i != input.size(); ++i) {
for (size_t j = 0; j != input.size(); ++j) {
if (!(i == j || is_lead_or_lag[i] || is_lead_or_lag[j])) { //
if (input[i]->is_identical(input[j], center, angle)) {
if (!(i == j || is_lead_or_lag[i] || is_lead_or_lag[j])) {
Direction dir = input[i]->is_identical(input[j], center, angle);
if(dir != Direction::None){
leading_curves.push_back(input[i]);
is_lead_or_lag[i] = true;
input[i]->get_verticies(lead_vlist);
// dir indicates order which lagging curve verticies match the leading curve verticies
lagging_curves.push_back(input[j]);
is_lead_or_lag[j] = true;
break;
input[j]->get_verticies(lag_vlist, dir);
}
}
}
}
// If not leading/lagging, curve is internal
std::vector<std::shared_ptr<Curve>> internal_curves;
for (size_t i =0; i != input.size(); ++i) {
if(!is_lead_or_lag[i]) {
internal_curves.push_back(input[i]);
}
}
// TODO: Check for complete elimination of leading/lagging curves
// Get leading/lagging verticies
// Sort leading/lagging verticies
std::vector<std::shared_ptr<Vertex>> leading_verticies;
std::vector<std::shared_ptr<Vertex>> lagging_verticies;
{
std::list<std::shared_ptr<Vertex>> local_verts;
for (auto i : leading_curves) {
i->get_verticies(local_verts);
lead_vlist.remove(center);
lag_vlist.remove(center);
std::list<std::pair<std::shared_ptr<Vertex>, std::shared_ptr<Vertex>>> vpairs;
auto j = lag_vlist.begin();
for (auto i : lead_vlist) {
vpairs.push_back(std::pair<std::shared_ptr<Vertex>, std::shared_ptr<Vertex>>(i, *j));
++j;
}
local_verts.sort(); // remove duplicates
local_verts.unique();
local_verts.remove(center);
vpairs.sort();
vpairs.unique();
leading_verticies.assign(local_verts.begin(),local_verts.end()); // assign to vector
for (auto &i : vpairs) {
leading_verticies.push_back(i.first);
lagging_verticies.push_back(i.second);
}
}
std::vector<std::shared_ptr<Vertex>> lagging_verticies;
{
std::list<std::shared_ptr<Vertex>> local_verts;
for (auto i : lagging_curves) {
i->get_verticies(local_verts);
// If not leading/lagging, curve is internal
std::vector<std::shared_ptr<Curve>> internal_curves;
for (size_t i = 0; i != input.size(); ++i) {
if (!is_lead_or_lag[i]) {
internal_curves.push_back(input[i]);
}
local_verts.sort(); // remove duplicates
local_verts.unique();
local_verts.remove(center);
lagging_verticies.assign(local_verts.begin(),local_verts.end()); // assign to vector
}
// Get internal verticies
std::vector<std::shared_ptr<Vertex>> internal_verticies;
{
std::list<std::shared_ptr<Vertex>> local_verts;
std::list<std::shared_ptr<Vertex>> local_vlist;
for (auto c : internal_curves) {
c->get_verticies(local_verts);
c->get_verticies(local_vlist);
}
local_verts.sort(); // remove duplicates
local_verts.unique();
local_verts.remove(center);
local_vlist.sort(); // remove duplicates
local_vlist.unique();
local_vlist.remove(center);
// Trim leading verticies
for(auto v : leading_verticies) {
local_verts.remove(v);
for (auto v : leading_verticies) {
local_vlist.remove(v);
}
// Trim lagging verticies
for(auto v : lagging_verticies) {
local_verts.remove(v);
for (auto v : lagging_verticies) {
local_vlist.remove(v);
}
// Copy to vector
internal_verticies.assign(local_verts.begin(),local_verts.end());
internal_verticies.assign(local_vlist.begin(), local_vlist.end());
}
// Make rotated copies
......@@ -118,7 +115,6 @@ RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_pt
if (!last_iteration) {
Curves.back()->ForConstruction = true;
} else {
//const_cast<Curve *>(c)->ForConstruction = true; // TODO: const_cast is ugly
c->ForConstruction = true;
}
}
......@@ -132,14 +128,13 @@ RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_pt
double y0 = Center->y();
std::vector<std::shared_ptr<Vertex>> rotated_leading;
for(auto v : leading_verticies) {
for (auto v : leading_verticies) {
double dx = v->x() - x0;
double dy = v->y() - y0;
double vx = cosa * dx - sina * dy + x0;
double vy = sina * dx + cosa * dy + y0;
//rotated_leading.push_back(new Vertex(vx, vy));
rotated_leading.push_back(std::make_shared<Vertex>(vx, vy));
......@@ -149,16 +144,15 @@ RotateCopy::RotateCopy(std::vector<std::shared_ptr<Curve>> input, std::shared_pt
}
std::vector<std::shared_ptr<Vertex>> rotated_internal;
for(auto v : internal_verticies) {
for (auto v : internal_verticies) {
double dx = v->x() - x0;
double dy = v->y() - y0;
double vx = cosa * dx - sina * dy + x0;
double vy = sina * dx + cosa * dy + y0;
//rotated_internal.push_back(new Vertex(vx, vy));
rotated_internal.push_back(std::make_shared<Vertex>(vx, vy));
Verticies.push_back(rotated_internal.back());
Constraints.push_back(std::make_shared<Rotation>(v, rotated_internal.back(), Center, Angle * (i + 1)));
......
......@@ -35,7 +35,7 @@ public:
void update(Eigen::MatrixXd &J, Eigen::VectorXd &r) override {};
inline bool operator==(Vertex const &v) { return (v.X == X) && (v.Y == Y); };
bool operator==(Vertex const &v) { return (v.X == X) && (v.Y == Y); };
const double x() const { return X->value(); };
......
......@@ -383,17 +383,17 @@ TEST(CircularArc, is_identical) {
auto c0 = s.new_element<CircularArc>(v1, v2, v0, 1.0);
// True
EXPECT_TRUE(c->is_identical(c));
EXPECT_TRUE(c->is_identical(c0));
EXPECT_TRUE(c->is_identical(c) == Direction::Forward);
EXPECT_TRUE(c->is_identical(c0) == Direction::Forward);
// #TODO: Radius does not match distance of endpoints from center
// Could be identical depending on other constraints
// Behavior is undefined unless Sketch::solve() is called
auto c1 = s.new_element<CircularArc>(vc0, vc1, vcc, 0.5);
EXPECT_FALSE(c->is_identical(c1));
EXPECT_TRUE(c->is_identical(c1) == Direction::None);
auto c2 = s.new_element<CircularArc>(v1, v2, v0, 0.5);
EXPECT_FALSE(c->is_identical(c2));
EXPECT_TRUE(c->is_identical(c2) == Direction::None);
// False
auto c3 = s.new_element<CircularArc>(vc1, vc0, vcc, 0.5);
......@@ -401,10 +401,10 @@ TEST(CircularArc, is_identical) {
auto c5 = s.new_element<CircularArc>(v1, v2, v3, 1.0);
auto c6 = s.new_element<CircularArc>(v2, v1, v3, 1.0);
EXPECT_FALSE(c->is_identical(c3));
EXPECT_FALSE(c->is_identical(c4));
EXPECT_FALSE(c->is_identical(c5));
EXPECT_FALSE(c->is_identical(c6));
EXPECT_TRUE(c->is_identical(c3) == Direction::None);
EXPECT_TRUE(c->is_identical(c4) == Direction::None);
EXPECT_TRUE(c->is_identical(c5) == Direction::None);
EXPECT_TRUE(c->is_identical(c6) == Direction::None);
s.delete_me();
}
......@@ -424,10 +424,10 @@ TEST(CircularArc, is_identical) {
auto c0 = s.new_element<CircularArc>(vs0, ve0, vc0, 1.0);
EXPECT_TRUE(c->is_identical(c0, vc0, 180.0));
EXPECT_TRUE(c->is_identical(c0, vc0, -180.0));
EXPECT_FALSE(c->is_identical(c0, vc0, 179.0));
EXPECT_FALSE(c->is_identical(c0, vc0, 181.0));
EXPECT_TRUE(c->is_identical(c0, vc0, 180.0) == Direction::Forward);
EXPECT_TRUE(c->is_identical(c0, vc0, -180.0) == Direction::Forward);
EXPECT_TRUE(c->is_identical(c0, vc0, 179.0) == Direction::None);
EXPECT_TRUE(c->is_identical(c0, vc0, 181.0) == Direction::None);
auto v1origin = s.new_element<Vertex>(2.0, 2.0);
auto vc1 = s.new_element<Vertex>(1.0, 3.0);
......@@ -436,20 +436,20 @@ TEST(CircularArc, is_identical) {
auto c1 = s.new_element<CircularArc>(vs1, ve1, vc1, 1.0);
EXPECT_TRUE(c->is_identical(c1, v1origin, 90.0));
EXPECT_TRUE(c->is_identical(c1, v1origin, -270.0));
EXPECT_FALSE(c->is_identical(c1, v1origin, 89.0));
EXPECT_FALSE(c->is_identical(c1, v1origin, 91.0));
EXPECT_TRUE(c->is_identical(c1, v1origin, 90.0) == Direction::Forward);
EXPECT_TRUE(c->is_identical(c1, v1origin, -270.0) == Direction::Forward);
EXPECT_TRUE(c->is_identical(c1, v1origin, 89.0) == Direction::None);
EXPECT_TRUE(c->is_identical(c1, v1origin, 91.0) == Direction::None);
auto c2 = s.new_element<CircularArc>(ve0, vs0, vc0, 1.0); // reverse
EXPECT_FALSE(c->is_identical(c2, vc0, 180.0));
EXPECT_FALSE(c->is_identical(c2, vc0, -180.0));
EXPECT_TRUE(c->is_identical(c2, vc0, 180.0) == Direction::None);
EXPECT_TRUE(c->is_identical(c2, vc0, -180.0) == Direction::None);
auto c3 = s.new_element<CircularArc>(ve1, vs1, vc1, 1.0);
EXPECT_FALSE(c->is_identical(c3, v1origin, 90.0));
EXPECT_FALSE(c->is_identical(c3, v1origin, -270.0));
EXPECT_TRUE(c->is_identical(c3, v1origin, 90.0) == Direction::None);