Pixel Game Engine v1.15
Add practical polymorphism video + Dashed Lines + Mouse Wheel support
This commit is contained in:
parent
846141209e
commit
d281b2f900
536
OneLoneCoder_PGE_Polymorphism.cpp
Normal file
536
OneLoneCoder_PGE_Polymorphism.cpp
Normal file
@ -0,0 +1,536 @@
|
||||
/*
|
||||
OLC::CAD - A practical example of Polymorphism
|
||||
"Damn Gorbette, you made us giggle..." - javidx9
|
||||
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2018-2019 OneLoneCoder.com
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above
|
||||
copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce
|
||||
the above copyright notice. This list of conditions and the following
|
||||
disclaimer must be reproduced in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Instructions:
|
||||
~~~~~~~~~~~~~
|
||||
Press & Hold middle mouse mutton to PAN
|
||||
Use Scroll wheel (or Q & A) to zoom in & out
|
||||
Press L to start drawing a line
|
||||
Press C to start drawing a circle
|
||||
Press B to start drawing a box
|
||||
Press S to start drawing a curve
|
||||
Press M to move node under cursor
|
||||
|
||||
Relevant Video: https://youtu.be/kxKKHKSMGIg
|
||||
|
||||
Links
|
||||
~~~~~
|
||||
YouTube: https://www.youtube.com/javidx9
|
||||
https://www.youtube.com/javidx9extra
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
Twitter: https://www.twitter.com/javidx9
|
||||
Twitch: https://www.twitch.tv/javidx9
|
||||
GitHub: https://www.github.com/onelonecoder
|
||||
Patreon: https://www.patreon.com/javidx9
|
||||
Homepage: https://www.onelonecoder.com
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
David Barr, aka javidx9, ©OneLoneCoder 2019
|
||||
*/
|
||||
|
||||
#define OLC_PGE_APPLICATION
|
||||
#include "olcPixelGameEngine.h"
|
||||
|
||||
// Forward declare shape, since we use it in sNode
|
||||
struct sShape;
|
||||
|
||||
// Define a node
|
||||
struct sNode
|
||||
{
|
||||
sShape *parent;
|
||||
olc::vf2d pos;
|
||||
};
|
||||
|
||||
// Our BASE class, defines the interface for all shapes
|
||||
struct sShape
|
||||
{
|
||||
// Shapes are defined by the placment of nodes
|
||||
std::vector<sNode> vecNodes;
|
||||
uint32_t nMaxNodes = 0;
|
||||
|
||||
// The colour of the shape
|
||||
olc::Pixel col = olc::GREEN;
|
||||
|
||||
// All shapes share word to screen transformation
|
||||
// coefficients, so share them staically
|
||||
static float fWorldScale;
|
||||
static olc::vf2d vWorldOffset;
|
||||
|
||||
// Convert coordinates from World Space --> Screen Space
|
||||
void WorldToScreen(const olc::vf2d &v, int &nScreenX, int &nScreenY)
|
||||
{
|
||||
nScreenX = (int)((v.x - vWorldOffset.x) * fWorldScale);
|
||||
nScreenY = (int)((v.y - vWorldOffset.y) * fWorldScale);
|
||||
}
|
||||
|
||||
// This is a PURE function, which makes this class abstract. A sub-class
|
||||
// of this class must provide an implementation of this function by
|
||||
// overriding it
|
||||
virtual void DrawYourself(olc::PixelGameEngine *pge) = 0;
|
||||
|
||||
// Shapes are defined by nodes, the shape is responsible
|
||||
// for issuing nodes that get placed by the user. The shape may
|
||||
// change depending on how many nodes have been placed. Once the
|
||||
// maximum number of nodes for a shape have been placed, it returns
|
||||
// nullptr
|
||||
sNode* GetNextNode(const olc::vf2d &p)
|
||||
{
|
||||
if (vecNodes.size() == nMaxNodes)
|
||||
return nullptr; // Shape is complete so no new nodes to be issued
|
||||
|
||||
// else create new node and add to shapes node vector
|
||||
sNode n;
|
||||
n.parent = this;
|
||||
n.pos = p;
|
||||
vecNodes.push_back(n);
|
||||
|
||||
// Beware! - This normally is bad! But see sub classes
|
||||
return &vecNodes[vecNodes.size() - 1];
|
||||
}
|
||||
|
||||
// Test to see if supplied coordinate exists at same location
|
||||
// as any of the nodes for this shape. Return a pointer to that
|
||||
// node if it does
|
||||
sNode* HitNode(olc::vf2d &p)
|
||||
{
|
||||
for (auto &n : vecNodes)
|
||||
{
|
||||
if ((p - n.pos).mag() < 0.01f)
|
||||
return &n;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Draw all of the nodes that define this shape so far
|
||||
void DrawNodes(olc::PixelGameEngine *pge)
|
||||
{
|
||||
for (auto &n : vecNodes)
|
||||
{
|
||||
int sx, sy;
|
||||
WorldToScreen(n.pos, sx, sy);
|
||||
pge->FillCircle(sx, sy, 2, olc::RED);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// We must provide an implementation of our static variables
|
||||
float sShape::fWorldScale = 1.0f;
|
||||
olc::vf2d sShape::vWorldOffset = { 0,0 };
|
||||
|
||||
|
||||
|
||||
// LINE sub class, inherits from sShape
|
||||
struct sLine : public sShape
|
||||
{
|
||||
sLine()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes); // We're gonna be getting pointers to vector elements
|
||||
// though we have defined already how much capacity our vector will have. This makes
|
||||
// it safe to do this as we know the vector will not be maniupulated as we add nodes
|
||||
// to it. Is this bad practice? Possibly, but as with all thing programming, if you
|
||||
// know what you are doing, it's ok :D
|
||||
}
|
||||
|
||||
// Implements custom DrawYourself Function, meaning the shape
|
||||
// is no longer abstract
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// BOX
|
||||
struct sBox : public sShape
|
||||
{
|
||||
sBox()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawRect(sx, sy, ex - sx, ey - sy, col);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// CIRCLE
|
||||
struct sCircle : public sShape
|
||||
{
|
||||
sCircle()
|
||||
{
|
||||
nMaxNodes = 2;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
float fRadius = (vecNodes[0].pos - vecNodes[1].pos).mag();
|
||||
int sx, sy, ex, ey;
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// Note the radius is also scaled so it is drawn appropriately
|
||||
pge->DrawCircle(sx, sy, (int32_t)(fRadius * fWorldScale), col);
|
||||
}
|
||||
};
|
||||
|
||||
// BEZIER SPLINE - requires 3 nodes to be defined fully
|
||||
struct sCurve : public sShape
|
||||
{
|
||||
sCurve()
|
||||
{
|
||||
nMaxNodes = 3;
|
||||
vecNodes.reserve(nMaxNodes);
|
||||
}
|
||||
|
||||
void DrawYourself(olc::PixelGameEngine *pge) override
|
||||
{
|
||||
int sx, sy, ex, ey;
|
||||
|
||||
if (vecNodes.size() < 3)
|
||||
{
|
||||
// Can only draw line from first to second
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
}
|
||||
|
||||
if (vecNodes.size() == 3)
|
||||
{
|
||||
// Can draw line from first to second
|
||||
WorldToScreen(vecNodes[0].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[1].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// Can draw second structural line
|
||||
WorldToScreen(vecNodes[1].pos, sx, sy);
|
||||
WorldToScreen(vecNodes[2].pos, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col, 0xFF00FF00);
|
||||
|
||||
// And bezier curve
|
||||
olc::vf2d op = vecNodes[0].pos;
|
||||
olc::vf2d np = op;
|
||||
for (float t = 0; t < 1.0f; t += 0.01f)
|
||||
{
|
||||
np = (1 - t)*(1 - t)*vecNodes[0].pos + 2 * (1 - t)*t*vecNodes[1].pos + t * t * vecNodes[2].pos;
|
||||
WorldToScreen(op, sx, sy);
|
||||
WorldToScreen(np, ex, ey);
|
||||
pge->DrawLine(sx, sy, ex, ey, col);
|
||||
op = np;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// APPLICATION STARTS HERE
|
||||
|
||||
class Polymorphism : public olc::PixelGameEngine
|
||||
{
|
||||
public:
|
||||
Polymorphism()
|
||||
{
|
||||
sAppName = "Polymorphism";
|
||||
}
|
||||
|
||||
private:
|
||||
// Pan & Zoom variables
|
||||
olc::vf2d vOffset = { 0.0f, 0.0f };
|
||||
olc::vf2d vStartPan = { 0.0f, 0.0f };
|
||||
float fScale = 10.0f;
|
||||
float fGrid = 1.0f;
|
||||
|
||||
// Convert coordinates from World Space --> Screen Space
|
||||
void WorldToScreen(const olc::vf2d &v, int &nScreenX, int &nScreenY)
|
||||
{
|
||||
nScreenX = (int)((v.x - vOffset.x) * fScale);
|
||||
nScreenY = (int)((v.y - vOffset.y) * fScale);
|
||||
}
|
||||
|
||||
// Convert coordinates from Screen Space --> World Space
|
||||
void ScreenToWorld(int nScreenX, int nScreenY, olc::vf2d &v)
|
||||
{
|
||||
v.x = (float)(nScreenX) / fScale + vOffset.x;
|
||||
v.y = (float)(nScreenY) / fScale + vOffset.y;
|
||||
}
|
||||
|
||||
|
||||
// A pointer to a shape that is currently being defined
|
||||
// by the placment of nodes
|
||||
sShape* tempShape = nullptr;
|
||||
|
||||
// A list of pointers to all shapes which have been drawn
|
||||
// so far
|
||||
std::list<sShape*> listShapes;
|
||||
|
||||
// A pointer to a node that is currently selected. Selected
|
||||
// nodes follow the mouse cursor
|
||||
sNode *selectedNode = nullptr;
|
||||
|
||||
// "Snapped" mouse location
|
||||
olc::vf2d vCursor = { 0, 0 };
|
||||
|
||||
// NOTE! No direct instances of lines, circles, boxes or curves,
|
||||
// the application is only aware of the existence of shapes!
|
||||
// THIS IS THE POWER OF POLYMORPHISM!
|
||||
|
||||
public:
|
||||
bool OnUserCreate() override
|
||||
{
|
||||
// Configure world space (0,0) to be middle of screen space
|
||||
vOffset = { (float)(-ScreenWidth() / 2) / fScale, (float)(-ScreenHeight() / 2) / fScale };
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnUserUpdate(float fElapsedTime) override
|
||||
{
|
||||
// Get mouse location this frame
|
||||
olc::vf2d vMouse = { (float)GetMouseX(), (float)GetMouseY() };
|
||||
|
||||
|
||||
// Handle Pan & Zoom
|
||||
if (GetMouse(2).bPressed)
|
||||
{
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
if (GetMouse(2).bHeld)
|
||||
{
|
||||
vOffset -= (vMouse - vStartPan) / fScale;
|
||||
vStartPan = vMouse;
|
||||
}
|
||||
|
||||
olc::vf2d vMouseBeforeZoom;
|
||||
ScreenToWorld((int)vMouse.x, (int)vMouse.y, vMouseBeforeZoom);
|
||||
|
||||
if (GetKey(olc::Key::Q).bHeld || GetMouseWheel() > 0)
|
||||
{
|
||||
fScale *= 1.1f;
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::A).bHeld || GetMouseWheel() < 0)
|
||||
{
|
||||
fScale *= 0.9f;
|
||||
}
|
||||
|
||||
olc::vf2d vMouseAfterZoom;
|
||||
ScreenToWorld((int)vMouse.x, (int)vMouse.y, vMouseAfterZoom);
|
||||
vOffset += (vMouseBeforeZoom - vMouseAfterZoom);
|
||||
|
||||
|
||||
// Snap mouse cursor to nearest grid interval
|
||||
vCursor.x = floorf((vMouseAfterZoom.x + 0.5f) * fGrid);
|
||||
vCursor.y = floorf((vMouseAfterZoom.y + 0.5f) * fGrid);
|
||||
|
||||
|
||||
if (GetKey(olc::Key::L).bPressed)
|
||||
{
|
||||
tempShape = new sLine();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
|
||||
if (GetKey(olc::Key::B).bPressed)
|
||||
{
|
||||
tempShape = new sBox();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::C).bPressed)
|
||||
{
|
||||
// Create new shape as a temporary
|
||||
tempShape = new sCircle();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
if (GetKey(olc::Key::S).bPressed)
|
||||
{
|
||||
// Create new shape as a temporary
|
||||
tempShape = new sCurve();
|
||||
|
||||
// Place first node at location of keypress
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
|
||||
// Get Second node
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
}
|
||||
|
||||
// Search for any node that exists under the cursor, if one
|
||||
// is found then select it
|
||||
if (GetKey(olc::Key::M).bPressed)
|
||||
{
|
||||
selectedNode = nullptr;
|
||||
for (auto &shape : listShapes)
|
||||
{
|
||||
selectedNode = shape->HitNode(vCursor);
|
||||
if (selectedNode != nullptr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If a node is selected, make it follow the mouse cursor
|
||||
// by updating its position
|
||||
if (selectedNode != nullptr)
|
||||
{
|
||||
selectedNode->pos = vCursor;
|
||||
}
|
||||
|
||||
|
||||
// As the user left clicks to place nodes, the shape can grow
|
||||
// until it requires no more nodes, at which point it is completed
|
||||
// and added to the list of completed shapes.
|
||||
if (GetMouse(0).bReleased)
|
||||
{
|
||||
if (tempShape != nullptr)
|
||||
{
|
||||
selectedNode = tempShape->GetNextNode(vCursor);
|
||||
if (selectedNode == nullptr)
|
||||
{
|
||||
tempShape->col = olc::WHITE;
|
||||
listShapes.push_back(tempShape);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Clear Screen
|
||||
Clear(olc::VERY_DARK_BLUE);
|
||||
|
||||
int sx, sy;
|
||||
int ex, ey;
|
||||
|
||||
// Get visible world
|
||||
olc::vf2d vWorldTopLeft, vWorldBottomRight;
|
||||
ScreenToWorld(0, 0, vWorldTopLeft);
|
||||
ScreenToWorld(ScreenWidth(), ScreenHeight(), vWorldBottomRight);
|
||||
|
||||
// Get values just beyond screen boundaries
|
||||
vWorldTopLeft.x = floor(vWorldTopLeft.x);
|
||||
vWorldTopLeft.y = floor(vWorldTopLeft.y);
|
||||
vWorldBottomRight.x = ceil(vWorldBottomRight.x);
|
||||
vWorldBottomRight.y = ceil(vWorldBottomRight.y);
|
||||
|
||||
// Draw Grid dots
|
||||
for (float x = vWorldTopLeft.x; x < vWorldBottomRight.x; x += fGrid)
|
||||
{
|
||||
for (float y = vWorldTopLeft.y; y < vWorldBottomRight.y; y += fGrid)
|
||||
{
|
||||
WorldToScreen({ x, y }, sx, sy);
|
||||
Draw(sx, sy, olc::BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw World Axis
|
||||
WorldToScreen({ 0,vWorldTopLeft.y }, sx, sy);
|
||||
WorldToScreen({ 0,vWorldBottomRight.y }, ex, ey);
|
||||
DrawLine(sx, sy, ex, ey, olc::GREY, 0xF0F0F0F0);
|
||||
WorldToScreen({ vWorldTopLeft.x,0 }, sx, sy);
|
||||
WorldToScreen({ vWorldBottomRight.x,0 }, ex, ey);
|
||||
DrawLine(sx, sy, ex, ey, olc::GREY, 0xF0F0F0F0);
|
||||
|
||||
// Update shape translation coefficients
|
||||
sShape::fWorldScale = fScale;
|
||||
sShape::vWorldOffset = vOffset;
|
||||
|
||||
// Draw All Existing Shapes
|
||||
for (auto &shape : listShapes)
|
||||
{
|
||||
shape->DrawYourself(this);
|
||||
shape->DrawNodes(this);
|
||||
}
|
||||
|
||||
// Draw shape currently being defined
|
||||
if (tempShape != nullptr)
|
||||
{
|
||||
tempShape->DrawYourself(this);
|
||||
tempShape->DrawNodes(this);
|
||||
}
|
||||
|
||||
// Draw "Snapped" Cursor
|
||||
WorldToScreen(vCursor, sx, sy);
|
||||
DrawCircle(sx, sy, 3, olc::YELLOW);
|
||||
|
||||
// Draw Cursor Position
|
||||
DrawString(10, 10, "X=" + std::to_string(vCursor.x) + ", Y=" + std::to_string(vCursor.y), olc::YELLOW, 2);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
Polymorphism demo;
|
||||
if (demo.Construct(1600, 960, 1, 1))
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
olcPixelGameEngine.h
|
||||
|
||||
+-------------------------------------------------------------+
|
||||
| OneLoneCoder Pixel Game Engine v1.14 |
|
||||
| OneLoneCoder Pixel Game Engine v1.15 |
|
||||
| "Like the command prompt console one, but not..." - javidx9 |
|
||||
+-------------------------------------------------------------+
|
||||
|
||||
@ -461,9 +461,9 @@ namespace olc // All OneLoneCoder stuff will now exist in the "olc" namespace
|
||||
// Draws a single Pixel
|
||||
virtual bool Draw(int32_t x, int32_t y, Pixel p = olc::WHITE);
|
||||
// Draws a line from (x1,y1) to (x2,y2)
|
||||
void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE);
|
||||
void DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p = olc::WHITE, uint32_t pattern = 0xFFFFFFFF);
|
||||
// Draws a circle located at (x,y) with radius
|
||||
void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE);
|
||||
void DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE, uint8_t mask = 0xFF);
|
||||
// Fills a circle located at (x,y) with radius
|
||||
void FillCircle(int32_t x, int32_t y, int32_t radius, Pixel p = olc::WHITE);
|
||||
// Draws a rectangle at (x,y) to (x+w,y+h)
|
||||
@ -1223,17 +1223,23 @@ namespace olc
|
||||
fSubPixelOffsetY = oy * fPixelY;
|
||||
}
|
||||
|
||||
void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p)
|
||||
void PixelGameEngine::DrawLine(int32_t x1, int32_t y1, int32_t x2, int32_t y2, Pixel p, uint32_t pattern)
|
||||
{
|
||||
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
|
||||
dx = x2 - x1; dy = y2 - y1;
|
||||
|
||||
auto rol = [&](void)
|
||||
{
|
||||
pattern = (pattern << 1) | (pattern >> 31);
|
||||
return pattern & 1;
|
||||
};
|
||||
|
||||
// straight lines idea by gurkanctn
|
||||
if (dx == 0) // Line is vertical
|
||||
{
|
||||
if (y2 < y1) std::swap(y1, y2);
|
||||
for (y = y1; y <= y2; y++)
|
||||
Draw(x1, y, p);
|
||||
if (rol()) Draw(x1, y, p);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1241,7 +1247,7 @@ namespace olc
|
||||
{
|
||||
if (x2 < x1) std::swap(x1, x2);
|
||||
for (x = x1; x <= x2; x++)
|
||||
Draw(x, y1, p);
|
||||
if (rol()) Draw(x, y1, p);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1259,7 +1265,7 @@ namespace olc
|
||||
x = x2; y = y2; xe = x1;
|
||||
}
|
||||
|
||||
Draw(x, y, p);
|
||||
if (rol()) Draw(x, y, p);
|
||||
|
||||
for (i = 0; x<xe; i++)
|
||||
{
|
||||
@ -1271,7 +1277,7 @@ namespace olc
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0)) y = y + 1; else y = y - 1;
|
||||
px = px + 2 * (dy1 - dx1);
|
||||
}
|
||||
Draw(x, y, p);
|
||||
if (rol()) Draw(x, y, p);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1285,7 +1291,7 @@ namespace olc
|
||||
x = x2; y = y2; ye = y1;
|
||||
}
|
||||
|
||||
Draw(x, y, p);
|
||||
if (rol()) Draw(x, y, p);
|
||||
|
||||
for (i = 0; y<ye; i++)
|
||||
{
|
||||
@ -1297,12 +1303,12 @@ namespace olc
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0)) x = x + 1; else x = x - 1;
|
||||
py = py + 2 * (dx1 - dy1);
|
||||
}
|
||||
Draw(x, y, p);
|
||||
if (rol()) Draw(x, y, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p)
|
||||
void PixelGameEngine::DrawCircle(int32_t x, int32_t y, int32_t radius, Pixel p, uint8_t mask)
|
||||
{
|
||||
int x0 = 0;
|
||||
int y0 = radius;
|
||||
@ -1311,14 +1317,14 @@ namespace olc
|
||||
|
||||
while (y0 >= x0) // only formulate 1/8 of circle
|
||||
{
|
||||
Draw(x - x0, y - y0, p);//upper left left
|
||||
Draw(x - y0, y - x0, p);//upper upper left
|
||||
Draw(x + y0, y - x0, p);//upper upper right
|
||||
Draw(x + x0, y - y0, p);//upper right right
|
||||
Draw(x - x0, y + y0, p);//lower left left
|
||||
Draw(x - y0, y + x0, p);//lower lower left
|
||||
Draw(x + y0, y + x0, p);//lower lower right
|
||||
Draw(x + x0, y + y0, p);//lower right right
|
||||
if (mask & 0x01) Draw(x + x0, y - y0, p);
|
||||
if (mask & 0x02) Draw(x + y0, y - x0, p);
|
||||
if (mask & 0x04) Draw(x + y0, y + x0, p);
|
||||
if (mask & 0x08) Draw(x + x0, y + y0, p);
|
||||
if (mask & 0x10) Draw(x - x0, y + y0, p);
|
||||
if (mask & 0x20) Draw(x - y0, y + x0, p);
|
||||
if (mask & 0x40) Draw(x - y0, y - x0, p);
|
||||
if (mask & 0x80) Draw(x - x0, y - y0, p);
|
||||
if (d < 0) d += 4 * x0++ + 6;
|
||||
else d += 4 * (x0++ - y0--) + 10;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user