#include "Polygon2D.h"
#include "Scene.h"
const char FILL = 'X';
const char EMPTY = '.';
// A 16:9 render ratio would be a 32:9 as the height is double of the width on the console character size, instead of being a perfect square
const Vector2D topLeft(0, 0);
const Vector2D bottomRight(160, 45);
int main()
{
/*
int width, height;
std::cout << "Width? ";
std::cin >> width;
std::cout << "Height? ";
std::cin >> height;
float scaleY = (float)height / preferredHeight;
float scaleX = scaleY;
*/
float scaleX = 3.0f, scaleY = 3.0f;
Polygon2D triangle0({ { 0, 4 }, { 12, 4 }, { 6, 13 } });
Polygon2D triangle1({ { 0, 10 }, { 12, 10 }, { 6, 1 } });
Polygon2D rectangle0({ { 0, 0 }, { 15, 0 }, { 15, 6 }, { 0, 6 } });
rectangle0.Translate(20, 3);
Scene scene;
scene.Add(&triangle0);
scene.Add(&triangle1);
scene.Add(&rectangle0);
scene.Scale(scaleX, scaleY);
scene.Render(topLeft, bottomRight, FILL, EMPTY);
return 0;
}
#pragma once
#include "IPlotable.h"
struct Vector2D : public IPlotable
{
public:
float x;
float y;
Vector2D(float x, float y);
void Scale(float scaleX, float scaleY) override;
bool Plot(const Vector2D & vertex) const override;
void Translate(float x, float y) override;
};
#include <cmath>
#include "Vector2D.h"
Vector2D::Vector2D(float x, float y) : x(x), y(y) { }
bool Vector2D::Plot(const Vector2D & vertex) const
{
return std::abs(vertex.x - x) < 0.5f && std::abs(vertex.y - y) < 0.5f;
}
void Vector2D::Scale(float scaleX, float scaleY)
{
x = std::round(x * scaleX);
y = std::round(y * scaleY);
}
void Vector2D::Translate(float x, float y)
{
this->x += x;
this->y += y;
}
#pragma once
//#include "Vector2D.h"
class Vector2D;
class IPlotable
{
public:
bool virtual Plot(const Vector2D & vertex) const = 0;
void virtual Scale(float scaleX, float scaleY) = 0;
void virtual Translate(float x, float y) = 0;
};
#pragma once
#include <vector>
#include "IPlotable.h"
#include "Vector2D.h"
class Scene
{
/*
Coordinate System:
==================
(0,0)
+-------------+
| |
| |
| |
+-------------+
(X,Y)
*/
private:
std::vector<IPlotable *> _plotables;
bool PlotAny(const Vector2D & point);
public:
void Add(IPlotable * plotable);
void Render(const Vector2D & topLeft, const Vector2D & bottomRight, char fill, char empty);
void Scale(float scaleX, float scaleY);
};
#include "Scene.h"
#include <iostream>
bool Scene::PlotAny(const Vector2D & point)
{
for (const IPlotable * plotable : _plotables)
if (plotable->Plot(point))
return true;
return false;
}
void Scene::Add(IPlotable * plotable)
{
_plotables.push_back(plotable);
}
void Scene::Render(const Vector2D & topLeft, const Vector2D & bottomRight, char fill, char empty)
{
for (int row = topLeft.y; row <= bottomRight.y; row++)
{
for (int column = topLeft.x; column <= bottomRight.x; column++)
{
std::cout << (PlotAny(Vector2D(column, row)) ? fill : empty);
}
std::cout << std::endl;
}
}
void Scene::Scale(float scaleX, float scaleY)
{
for (IPlotable * plotable : _plotables)
plotable->Scale(scaleX, scaleY);
}
#pragma once
#include "IPlotable.h"
#include "Vector2D.h"
class LineSegment : public IPlotable
{
private:
// The vertices of the line segment
Vector2D _v1;
Vector2D _v2;
float _m; // The slope of the line
float _b; // The point where the line would cross the Y axis
bool _leftToRight; // The vertical direction
bool _topToBottom; // The horizontal direction
bool _isVertical;
bool IsContainedX(float x) const;
bool IsContainedY(float y) const;
public:
LineSegment(const Vector2D & v1, const Vector2D & v2);
const Vector2D & GetV1() const;
const Vector2D & GetV2() const;
void CalculateParameters();
bool Plot(const Vector2D & vertex) const override;
void Scale(float scaleX, float scaleY) override;
void Translate(float x, float y) override;
};
#include "LineSegment.h"
#include <cmath>
bool LineSegment::IsContainedX(float x) const
{
return _leftToRight && _v1.x <= x && x <= _v2.x ||
!_leftToRight && _v2.x <= x && x <= _v1.x;
}
bool LineSegment::IsContainedY(float y) const
{
return _topToBottom && _v1.y <= y && y <= _v2.y ||
!_topToBottom && _v2.y <= y && y <= _v1.y;
}
const Vector2D & LineSegment::GetV1() const { return _v1; }
const Vector2D & LineSegment::GetV2() const { return _v2; }
LineSegment::LineSegment(const Vector2D & v1, const Vector2D & v2) :
_v1(v1),
_v2(v2)
{
CalculateParameters();
}
bool LineSegment::Plot(const Vector2D & vertex) const
{
// Math: The parametric equation for coordinates x and y is as below
// • y = mx + b
// • x = (y - b) / m;
if (_isVertical)
return vertex.x == _v1.x && IsContainedY(vertex.y);
return IsContainedX(vertex.x) && IsContainedY(vertex.y) &&
(-0.5f <= _m && _m <= 0.5f && std::abs(vertex.x * _m + _b - vertex.y) <= 0.5f || // Plot any point that is at least halfway in the given raster
(_m < -0.5f || 0.5f < _m) && std::abs((vertex.y - _b) / _m - vertex.x) <= 0.5f ); // Plot any point that is at least halfway in the given raster
}
void LineSegment::CalculateParameters()
{
float xOffset = _v2.x - _v1.x;
float yOffset = _v2.y - _v1.y;
_leftToRight = xOffset >= 0.0f;
_topToBottom = yOffset >= 0.0f;
_isVertical = xOffset == 0.0f;
if (!_isVertical)
{
_m = yOffset / xOffset;
_b = _v1.y - _m * _v1.x;
}
}
void LineSegment::Scale(float scaleX, float scaleY)
{
_v1.Scale(scaleX, scaleY);
_v2.Scale(scaleX, scaleY);
CalculateParameters();
}
void LineSegment::Translate(float x, float y)
{
_v1.Translate(x, y);
_v2.Translate(x, y);
CalculateParameters();
}
#pragma once
#include <vector>
#include "IPlotable.h"
#include "LineSegment.h"
#include "Vector2D.h"
class Polygon2D : public IPlotable
{
private:
// The line segments (sides) of the polygon
std::vector<LineSegment> _lineSegments;
// The verticies of the polygon. Stored for scaling purposes only.
std::vector<Vector2D> _vertices;
public:
Polygon2D(std::vector<Vector2D> vertices);
bool Plot(const Vector2D & vertex) const override;
void Scale(float scaleX, float scaleY) override;
void Translate(float x, float y) override;
};
#include "Polygon2D.h"
Polygon2D::Polygon2D(std::vector<Vector2D> vertices) :
_vertices(vertices)//,
//_lineSegments(vertices.size())
{
_lineSegments.reserve(_vertices.size());
size_t length = _vertices.size();
for (size_t i = 0u; i < length; i++)
_lineSegments.push_back(std::move(LineSegment(_vertices[i], _vertices[(i + 1u) % length])));
}
bool Polygon2D::Plot(const Vector2D & vertex) const
{
size_t length = _vertices.size();
for (size_t i = 0u; i < length; i++)
if (_lineSegments[i].Plot(vertex))
return true;
return false;
}
void Polygon2D::Scale(float scaleX, float scaleY)
{
size_t length = _vertices.size();
for (size_t i = 0u; i < length; i++)
_vertices[i].Scale(scaleX, scaleY);
for (size_t i = 0u; i < length; i++)
_lineSegments[i] = LineSegment(_vertices[i], _vertices[(i + 1u) % length]);
}
void Polygon2D::Translate(float x, float y)
{
size_t length = _vertices.size();
for (size_t i = 0u; i < length; i++)
_vertices[i].Translate(x, y);
for (size_t i = 0u; i < length; i++)
_lineSegments[i] = LineSegment(_vertices[i], _vertices[(i + 1u) % length]);
}