Added Racing Lines Video
This commit is contained in:
parent
68f774e340
commit
893b8ad68e
432
OneLoneCoder_RacingLines.cpp
Normal file
432
OneLoneCoder_RacingLines.cpp
Normal file
@ -0,0 +1,432 @@
|
||||
/*
|
||||
OneLoneCoder.com - Programming Racing Lines
|
||||
"Brake! Brake! Hard Left! " - @Javidx9
|
||||
|
||||
License
|
||||
~~~~~~~
|
||||
One Lone Coder Console Game Engine Copyright (C) 2018 Javidx9
|
||||
This program comes with ABSOLUTELY NO WARRANTY.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; See license for details.
|
||||
Original works located at:
|
||||
https://www.github.com/onelonecoder
|
||||
https://www.onelonecoder.com
|
||||
https://www.youtube.com/javidx9
|
||||
GNU GPLv3
|
||||
https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||||
|
||||
From Javidx9 :)
|
||||
~~~~~~~~~~~~~~~
|
||||
Hello! Ultimately I don't care what you use this for. It's intended to be
|
||||
educational, and perhaps to the oddly minded - a little bit of fun.
|
||||
Please hack this, change it and use it in any way you see fit. You acknowledge
|
||||
that I am not responsible for anything bad that happens as a result of
|
||||
your actions. However this code is protected by GNU GPLv3, see the license in the
|
||||
github repo. This means you must attribute me if you use it. You can view this
|
||||
license here: https://github.com/OneLoneCoder/videos/blob/master/LICENSE
|
||||
Cheers!
|
||||
|
||||
Background
|
||||
~~~~~~~~~~
|
||||
Algorithmically generating a racing line is quite tricky. This simple framework
|
||||
allows me to explore different methods. Use mouse to drag points, and A & S keys
|
||||
to change the number of iterations.
|
||||
|
||||
Author
|
||||
~~~~~~
|
||||
Twitter: @javidx9
|
||||
Blog: http://www.onelonecoder.com
|
||||
Discord: https://discord.gg/WhwHUMV
|
||||
|
||||
Video:
|
||||
~~~~~~
|
||||
https://youtu.be/FlieT66N9OM
|
||||
|
||||
Last Updated: 27/05/2018
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#include "olcConsoleGameEngine.h"
|
||||
|
||||
// See Programming Splines! Videos
|
||||
struct sPoint2D
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float length;
|
||||
};
|
||||
|
||||
struct sSpline
|
||||
{
|
||||
vector<sPoint2D> points;
|
||||
float fTotalSplineLength = 0.0f;
|
||||
bool bIsLooped = true;
|
||||
|
||||
sPoint2D GetSplinePoint(float t)
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
if (!bIsLooped)
|
||||
{
|
||||
p1 = (int)t + 1;
|
||||
p2 = p1 + 1;
|
||||
p3 = p2 + 1;
|
||||
p0 = p1 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = ((int)t) % points.size();
|
||||
p2 = (p1 + 1) % points.size();
|
||||
p3 = (p2 + 1) % points.size();
|
||||
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1;
|
||||
}
|
||||
|
||||
t = t - (int)t;
|
||||
|
||||
float tt = t * t;
|
||||
float ttt = tt * t;
|
||||
|
||||
float q1 = -ttt + 2.0f*tt - t;
|
||||
float q2 = 3.0f*ttt - 5.0f*tt + 2.0f;
|
||||
float q3 = -3.0f*ttt + 4.0f*tt + t;
|
||||
float q4 = ttt - tt;
|
||||
|
||||
float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4);
|
||||
float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4);
|
||||
|
||||
return{ tx, ty };
|
||||
}
|
||||
|
||||
sPoint2D GetSplineGradient(float t)
|
||||
{
|
||||
int p0, p1, p2, p3;
|
||||
if (!bIsLooped)
|
||||
{
|
||||
p1 = (int)t + 1;
|
||||
p2 = p1 + 1;
|
||||
p3 = p2 + 1;
|
||||
p0 = p1 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = ((int)t) % points.size();
|
||||
p2 = (p1 + 1) % points.size();
|
||||
p3 = (p2 + 1) % points.size();
|
||||
p0 = p1 >= 1 ? p1 - 1 : points.size() - 1;
|
||||
}
|
||||
|
||||
t = t - (int)t;
|
||||
|
||||
float tt = t * t;
|
||||
float ttt = tt * t;
|
||||
|
||||
float q1 = -3.0f * tt + 4.0f*t - 1.0f;
|
||||
float q2 = 9.0f*tt - 10.0f*t;
|
||||
float q3 = -9.0f*tt + 8.0f*t + 1.0f;
|
||||
float q4 = 3.0f*tt - 2.0f*t;
|
||||
|
||||
float tx = 0.5f * (points[p0].x * q1 + points[p1].x * q2 + points[p2].x * q3 + points[p3].x * q4);
|
||||
float ty = 0.5f * (points[p0].y * q1 + points[p1].y * q2 + points[p2].y * q3 + points[p3].y * q4);
|
||||
|
||||
return{ tx, ty };
|
||||
}
|
||||
|
||||
float CalculateSegmentLength(int node)
|
||||
{
|
||||
float fLength = 0.0f;
|
||||
float fStepSize = 0.1;
|
||||
|
||||
sPoint2D old_point, new_point;
|
||||
old_point = GetSplinePoint((float)node);
|
||||
|
||||
for (float t = 0; t < 1.0f; t += fStepSize)
|
||||
{
|
||||
new_point = GetSplinePoint((float)node + t);
|
||||
fLength += sqrtf((new_point.x - old_point.x)*(new_point.x - old_point.x)
|
||||
+ (new_point.y - old_point.y)*(new_point.y - old_point.y));
|
||||
old_point = new_point;
|
||||
}
|
||||
|
||||
return fLength;
|
||||
}
|
||||
|
||||
|
||||
float GetNormalisedOffset(float p)
|
||||
{
|
||||
// Which node is the base?
|
||||
int i = 0;
|
||||
while (p > points[i].length)
|
||||
{
|
||||
p -= points[i].length;
|
||||
i++;
|
||||
}
|
||||
|
||||
// The fractional is the offset
|
||||
return (float)i + (p / points[i].length);
|
||||
}
|
||||
|
||||
|
||||
void UpdateSplineProperties()
|
||||
{
|
||||
// Use to cache local spline lengths and overall spline length
|
||||
fTotalSplineLength = 0.0f;
|
||||
|
||||
if (bIsLooped)
|
||||
{
|
||||
// Each node has a succeeding length
|
||||
for (int i = 0; i < points.size(); i++)
|
||||
{
|
||||
points[i].length = CalculateSegmentLength(i);
|
||||
fTotalSplineLength += points[i].length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 1; i < points.size() - 2; i++)
|
||||
{
|
||||
points[i].length = CalculateSegmentLength(i);
|
||||
fTotalSplineLength += points[i].length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSelf(olcConsoleGameEngine* gfx, float ox, float oy, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
if (bIsLooped)
|
||||
{
|
||||
for (float t = 0; t < (float)points.size() - 0; t += 0.005f)
|
||||
{
|
||||
sPoint2D pos = GetSplinePoint(t);
|
||||
gfx->Draw(pos.x, pos.y, c, col);
|
||||
}
|
||||
}
|
||||
else // Not Looped
|
||||
{
|
||||
for (float t = 0; t < (float)points.size() - 3; t += 0.005f)
|
||||
{
|
||||
sPoint2D pos = GetSplinePoint(t);
|
||||
gfx->Draw(pos.x, pos.y, c, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class OneLoneCoder_RacingLine : public olcConsoleGameEngine
|
||||
{
|
||||
public:
|
||||
OneLoneCoder_RacingLine()
|
||||
{
|
||||
m_sAppName = L"Racing Line";
|
||||
}
|
||||
|
||||
private:
|
||||
sSpline path, trackLeft, trackRight, racingLine; // Various splines
|
||||
|
||||
int nNodes = 20; // Number of nodes in spline
|
||||
|
||||
float fDisplacement[20]; // Displacement along spline node normal
|
||||
|
||||
int nIterations = 1;
|
||||
float fMarker = 1.0f;
|
||||
int nSelectedNode = -1;
|
||||
|
||||
vector<pair<float, float>> vecModelCar;
|
||||
|
||||
protected:
|
||||
// Called by olcConsoleGameEngine
|
||||
virtual bool OnUserCreate()
|
||||
{
|
||||
for (int i = 0; i < nNodes; i++)
|
||||
{
|
||||
//path.points.push_back(
|
||||
// { 30.0f * sinf((float)i / (float)nNodes * 3.14159f * 2.0f) + ScreenWidth() / 2,
|
||||
// 30.0f * cosf((float)i / (float)nNodes * 3.14159f * 2.0f) + ScreenHeight() / 2 });
|
||||
|
||||
// Could use allocation functions for thes now, but just size via
|
||||
// append
|
||||
trackLeft.points.push_back({ 0.0f, 0.0f });
|
||||
trackRight.points.push_back({ 0.0f, 0.0f });
|
||||
racingLine.points.push_back({ 0.0f, 0.0f });
|
||||
}
|
||||
|
||||
// A hand crafted track
|
||||
path.points = { { 81.8f, 196.0f }, { 108.0f,210.0f }, { 152.0f,216.0f },
|
||||
{ 182.0f,185.6f }, { 190.0f,159.0f }, { 198.0f,122.0f }, { 226.0f,93.0f },
|
||||
{ 224.0f,41.0f }, { 204.0f,15.0f }, { 158.0f,24.0f }, { 146.0f,52.0f },
|
||||
{ 157.0f,93.0f }, { 124.0f,129.0f }, { 83.0f,104.0f }, { 77.0f,62.0f },
|
||||
{ 40.0f,57.0f }, { 21.0f,83.0f }, { 33.0f,145.0f }, { 30.0f,198.0f },
|
||||
{ 48.0f,210.0f } };
|
||||
|
||||
vecModelCar = { { 2,0 },{ 0,-1 },{ 0,1 } };
|
||||
|
||||
path.UpdateSplineProperties();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called by olcConsoleGameEngine
|
||||
virtual bool OnUserUpdate(float fElapsedTime)
|
||||
{
|
||||
// Clear Screen
|
||||
Fill(0, 0, ScreenWidth(), ScreenHeight(), PIXEL_SOLID, FG_DARK_GREEN);
|
||||
|
||||
// Handle iteration count
|
||||
if (m_keys[L'A'].bHeld) nIterations++;
|
||||
if (m_keys[L'S'].bHeld) nIterations--;
|
||||
if (nIterations < 0) nIterations = 0;
|
||||
|
||||
|
||||
// Check if node is selected with mouse
|
||||
if (GetMouse(0).bPressed)
|
||||
{
|
||||
for (int i = 0; i < path.points.size(); i++)
|
||||
{
|
||||
float d = sqrtf(powf(path.points[i].x - GetMouseX(), 2) + powf(path.points[i].y - GetMouseY(), 2));
|
||||
if (d < 5.0f)
|
||||
{
|
||||
nSelectedNode = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (GetMouse(0).bReleased)
|
||||
nSelectedNode = -1;
|
||||
|
||||
// Move selected node
|
||||
if (GetMouse(0).bHeld && nSelectedNode >= 0)
|
||||
{
|
||||
path.points[nSelectedNode].x = GetMouseX();
|
||||
path.points[nSelectedNode].y = GetMouseY();
|
||||
path.UpdateSplineProperties();
|
||||
}
|
||||
|
||||
// Move car around racing line
|
||||
fMarker += 2.0f * fElapsedTime;
|
||||
if (fMarker >= (float)racingLine.fTotalSplineLength)
|
||||
fMarker -= (float)racingLine.fTotalSplineLength;
|
||||
|
||||
// Calculate track boundary points
|
||||
float fTrackWidth = 10.0f;
|
||||
for (int i = 0; i < path.points.size(); i++)
|
||||
{
|
||||
sPoint2D p1 = path.GetSplinePoint(i);
|
||||
sPoint2D g1 = path.GetSplineGradient(i);
|
||||
float glen = sqrtf(g1.x*g1.x + g1.y*g1.y);
|
||||
|
||||
trackLeft.points[i].x = p1.x + fTrackWidth * (-g1.y / glen);
|
||||
trackLeft.points[i].y = p1.y + fTrackWidth * ( g1.x / glen);
|
||||
trackRight.points[i].x = p1.x - fTrackWidth * (-g1.y / glen);
|
||||
trackRight.points[i].y = p1.y - fTrackWidth * (g1.x / glen);
|
||||
}
|
||||
|
||||
// Draw Track
|
||||
float fRes = 0.2f;
|
||||
for (float t = 0.0f; t < path.points.size(); t += fRes)
|
||||
{
|
||||
sPoint2D pl1 = trackLeft.GetSplinePoint(t);
|
||||
sPoint2D pr1 = trackRight.GetSplinePoint(t);
|
||||
sPoint2D pl2 = trackLeft.GetSplinePoint(t + fRes);
|
||||
sPoint2D pr2 = trackRight.GetSplinePoint(t + fRes);
|
||||
|
||||
FillTriangle(pl1.x, pl1.y, pr1.x, pr1.y, pr2.x, pr2.y, PIXEL_SOLID, FG_GREY);
|
||||
FillTriangle(pl1.x, pl1.y, pl2.x, pl2.y, pr2.x, pr2.y, PIXEL_SOLID, FG_GREY);
|
||||
}
|
||||
|
||||
// Reset racing line
|
||||
for (int i = 0; i < racingLine.points.size(); i++)
|
||||
{
|
||||
racingLine.points[i] = path.points[i];
|
||||
fDisplacement[i] = 0;
|
||||
}
|
||||
racingLine.UpdateSplineProperties();
|
||||
|
||||
for (int n = 0; n < nIterations; n++)
|
||||
{
|
||||
for (int i = 0; i < racingLine.points.size(); i++)
|
||||
{
|
||||
// Get locations of neighbour nodes
|
||||
sPoint2D pointRight = racingLine.points[(i + 1) % racingLine.points.size()];
|
||||
sPoint2D pointLeft = racingLine.points[(i + racingLine.points.size() - 1) % racingLine.points.size()];
|
||||
sPoint2D pointMiddle = racingLine.points[i];
|
||||
|
||||
// Create vectors to neighbours
|
||||
sPoint2D vectorLeft = { pointLeft.x - pointMiddle.x, pointLeft.y - pointMiddle.y };
|
||||
sPoint2D vectorRight = { pointRight.x - pointMiddle.x, pointRight.y - pointMiddle.y };
|
||||
|
||||
// Normalise neighbours
|
||||
float lengthLeft = sqrtf(vectorLeft.x*vectorLeft.x + vectorLeft.y*vectorLeft.y);
|
||||
sPoint2D leftn = { vectorLeft.x / lengthLeft, vectorLeft.y / lengthLeft };
|
||||
float lengthRight = sqrtf(vectorRight.x*vectorRight.x + vectorRight.y*vectorRight.y);
|
||||
sPoint2D rightn = { vectorRight.x / lengthRight, vectorRight.y / lengthRight };
|
||||
|
||||
// Add together to create bisector vector
|
||||
sPoint2D vectorSum = { rightn.x + leftn.x, rightn.y + leftn.y };
|
||||
float len = sqrtf(vectorSum.x*vectorSum.x + vectorSum.y*vectorSum.y);
|
||||
vectorSum.x /= len; vectorSum.y /= len;
|
||||
|
||||
// Get point gradient and normalise
|
||||
sPoint2D g = path.GetSplineGradient(i);
|
||||
float glen = sqrtf(g.x*g.x + g.y*g.y);
|
||||
g.x /= glen; g.y /= glen;
|
||||
|
||||
// Project required correction onto point tangent to give displacment
|
||||
float dp = -g.y*vectorSum.x + g.x * vectorSum.y;
|
||||
|
||||
// Shortest path
|
||||
fDisplacement[i] += (dp * 0.3f);
|
||||
|
||||
// Curvature
|
||||
//fDisplacement[(i + 1) % racingLine.points.size()] += dp * -0.2f;
|
||||
//fDisplacement[(i - 1 + racingLine.points.size()) % racingLine.points.size()] += dp * -0.2f;
|
||||
|
||||
}
|
||||
|
||||
// Clamp displaced points to track width
|
||||
for (int i = 0; i < racingLine.points.size(); i++)
|
||||
{
|
||||
if (fDisplacement[i] >= fTrackWidth) fDisplacement[i] = fTrackWidth;
|
||||
if (fDisplacement[i] <= -fTrackWidth) fDisplacement[i] = -fTrackWidth;
|
||||
|
||||
sPoint2D g = path.GetSplineGradient(i);
|
||||
float glen = sqrtf(g.x*g.x + g.y*g.y);
|
||||
g.x /= glen; g.y /= glen;
|
||||
|
||||
racingLine.points[i].x = path.points[i].x + -g.y * fDisplacement[i];
|
||||
racingLine.points[i].y = path.points[i].y + g.x * fDisplacement[i];
|
||||
}
|
||||
}
|
||||
|
||||
path.DrawSelf(this, 0, 0);
|
||||
//trackLeft.DrawSelf(this, 0, 0);
|
||||
//trackRight.DrawSelf(this, 0, 0);
|
||||
|
||||
racingLine.UpdateSplineProperties();
|
||||
racingLine.DrawSelf(this, 0, 0, PIXEL_SOLID, FG_BLUE);
|
||||
|
||||
for (auto i : path.points)
|
||||
Fill(i.x - 1, i.y - 1, i.x + 2, i.y + 2, PIXEL_SOLID, FG_RED);
|
||||
|
||||
sPoint2D car_p = racingLine.GetSplinePoint(fMarker);
|
||||
sPoint2D car_g = racingLine.GetSplineGradient(fMarker);
|
||||
DrawWireFrameModel(vecModelCar, car_p.x, car_p.y, atan2f(car_g.y, car_g.x), 4.0f, FG_BLACK);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
OneLoneCoder_RacingLine demo;
|
||||
demo.ConstructConsole(256, 240, 4, 4);
|
||||
demo.Start();
|
||||
return 0;
|
||||
}
|
@ -51,9 +51,9 @@ Beginners Guide: https://youtu.be/u5BhrA8ED0o
|
||||
Shout Outs!
|
||||
~~~~~~~~~~~
|
||||
Thanks to cool people who helped with testing, bug-finding and fixing!
|
||||
wowLinh, JavaJack59, idkwid, kingtatgi, Return Null, CPP Guy
|
||||
wowLinh, JavaJack59, idkwid, kingtatgi, Return Null, CPP Guy, MaGetzUb
|
||||
|
||||
Last Updated: 18/03/2018
|
||||
Last Updated: 27/05/2018
|
||||
|
||||
Usage:
|
||||
~~~~~~
|
||||
@ -370,13 +370,13 @@ public:
|
||||
cfi.FontFamily = FF_DONTCARE;
|
||||
cfi.FontWeight = FW_NORMAL;
|
||||
|
||||
/*DWORD version = GetVersion();
|
||||
/* DWORD version = GetVersion();
|
||||
DWORD major = (DWORD)(LOBYTE(LOWORD(version)));
|
||||
DWORD minor = (DWORD)(HIBYTE(LOWORD(version)));
|
||||
DWORD minor = (DWORD)(HIBYTE(LOWORD(version)));*/
|
||||
|
||||
if ((major > 6) || ((major == 6) && (minor >= 2) && (minor < 4)))
|
||||
wcscpy_s(cfi.FaceName, L"Raster"); // Windows 8 :(
|
||||
else*/
|
||||
//if ((major > 6) || ((major == 6) && (minor >= 2) && (minor < 4)))
|
||||
// wcscpy_s(cfi.FaceName, L"Raster"); // Windows 8 :(
|
||||
//else
|
||||
wcscpy_s(cfi.FaceName, L"Lucida Console"); // Everything else :P
|
||||
|
||||
//wcscpy_s(cfi.FaceName, L"Liberation Mono");
|
||||
@ -461,27 +461,18 @@ public:
|
||||
void DrawLine(int x1, int y1, int x2, int y2, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
int x, y, dx, dy, dx1, dy1, px, py, xe, ye, i;
|
||||
dx = x2 - x1;
|
||||
dy = y2 - y1;
|
||||
dx1 = abs(dx);
|
||||
dy1 = abs(dy);
|
||||
px = 2 * dy1 - dx1;
|
||||
py = 2 * dx1 - dy1;
|
||||
dx = x2 - x1; dy = y2 - y1;
|
||||
dx1 = abs(dx); dy1 = abs(dy);
|
||||
px = 2 * dy1 - dx1; py = 2 * dx1 - dy1;
|
||||
if (dy1 <= dx1)
|
||||
{
|
||||
if (dx >= 0)
|
||||
{
|
||||
x = x1;
|
||||
y = y1;
|
||||
xe = x2;
|
||||
}
|
||||
{ x = x1; y = y1; xe = x2; }
|
||||
else
|
||||
{
|
||||
x = x2;
|
||||
y = y2;
|
||||
xe = x1;
|
||||
}
|
||||
{ x = x2; y = y2; xe = x1;}
|
||||
|
||||
Draw(x, y, c, col);
|
||||
|
||||
for (i = 0; x<xe; i++)
|
||||
{
|
||||
x = x + 1;
|
||||
@ -489,10 +480,7 @@ public:
|
||||
px = px + 2 * dy1;
|
||||
else
|
||||
{
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0))
|
||||
y = y + 1;
|
||||
else
|
||||
y = y - 1;
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0)) y = y + 1; else y = y - 1;
|
||||
px = px + 2 * (dy1 - dx1);
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
@ -501,18 +489,12 @@ public:
|
||||
else
|
||||
{
|
||||
if (dy >= 0)
|
||||
{
|
||||
x = x1;
|
||||
y = y1;
|
||||
ye = y2;
|
||||
}
|
||||
{ x = x1; y = y1; ye = y2; }
|
||||
else
|
||||
{
|
||||
x = x2;
|
||||
y = y2;
|
||||
ye = y1;
|
||||
}
|
||||
{ x = x2; y = y2; ye = y1; }
|
||||
|
||||
Draw(x, y, c, col);
|
||||
|
||||
for (i = 0; y<ye; i++)
|
||||
{
|
||||
y = y + 1;
|
||||
@ -520,10 +502,7 @@ public:
|
||||
py = py + 2 * dx1;
|
||||
else
|
||||
{
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0))
|
||||
x = x + 1;
|
||||
else
|
||||
x = x - 1;
|
||||
if ((dx<0 && dy<0) || (dx>0 && dy>0)) x = x + 1; else x = x - 1;
|
||||
py = py + 2 * (dx1 - dy1);
|
||||
}
|
||||
Draw(x, y, c, col);
|
||||
@ -531,6 +510,192 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void DrawTriangle(int x1, int y1, int x2, int y2, int x3, int y3, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
DrawLine(x1, y1, x2, y2, c, col);
|
||||
DrawLine(x2, y2, x3, y3, c, col);
|
||||
DrawLine(x3, y3, x1, y1, c, col);
|
||||
}
|
||||
|
||||
// https://www.avrfreaks.net/sites/default/files/triangles.c
|
||||
void FillTriangle(int x1, int y1, int x2, int y2, int x3, int y3, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
auto SWAP = [](int &x, int &y) { int t = x; x = y; y = t; };
|
||||
auto drawline = [&](int sx, int ex, int ny) { for (int i = sx; i <= ex; i++) Draw(i, ny, c, col); };
|
||||
|
||||
int t1x, t2x, y, minx, maxx, t1xp, t2xp;
|
||||
bool changed1 = false;
|
||||
bool changed2 = false;
|
||||
int signx1, signx2, dx1, dy1, dx2, dy2;
|
||||
int e1, e2;
|
||||
// Sort vertices
|
||||
if (y1>y2) { SWAP(y1, y2); SWAP(x1, x2); }
|
||||
if (y1>y3) { SWAP(y1, y3); SWAP(x1, x3); }
|
||||
if (y2>y3) { SWAP(y2, y3); SWAP(x2, x3); }
|
||||
|
||||
t1x = t2x = x1; y = y1; // Starting points
|
||||
dx1 = (int)(x2 - x1); if (dx1<0) { dx1 = -dx1; signx1 = -1; }
|
||||
else signx1 = 1;
|
||||
dy1 = (int)(y2 - y1);
|
||||
|
||||
dx2 = (int)(x3 - x1); if (dx2<0) { dx2 = -dx2; signx2 = -1; }
|
||||
else signx2 = 1;
|
||||
dy2 = (int)(y3 - y1);
|
||||
|
||||
if (dy1 > dx1) { // swap values
|
||||
SWAP(dx1, dy1);
|
||||
changed1 = true;
|
||||
}
|
||||
if (dy2 > dx2) { // swap values
|
||||
SWAP(dy2, dx2);
|
||||
changed2 = true;
|
||||
}
|
||||
|
||||
e2 = (int)(dx2 >> 1);
|
||||
// Flat top, just process the second half
|
||||
if (y1 == y2) goto next;
|
||||
e1 = (int)(dx1 >> 1);
|
||||
|
||||
for (int i = 0; i < dx1;) {
|
||||
t1xp = 0; t2xp = 0;
|
||||
if (t1x<t2x) { minx = t1x; maxx = t2x; }
|
||||
else { minx = t2x; maxx = t1x; }
|
||||
// process first line until y value is about to change
|
||||
while (i<dx1) {
|
||||
i++;
|
||||
e1 += dy1;
|
||||
while (e1 >= dx1) {
|
||||
e1 -= dx1;
|
||||
if (changed1) t1xp = signx1;//t1x += signx1;
|
||||
else goto next1;
|
||||
}
|
||||
if (changed1) break;
|
||||
else t1x += signx1;
|
||||
}
|
||||
// Move line
|
||||
next1:
|
||||
// process second line until y value is about to change
|
||||
while (1) {
|
||||
e2 += dy2;
|
||||
while (e2 >= dx2) {
|
||||
e2 -= dx2;
|
||||
if (changed2) t2xp = signx2;//t2x += signx2;
|
||||
else goto next2;
|
||||
}
|
||||
if (changed2) break;
|
||||
else t2x += signx2;
|
||||
}
|
||||
next2:
|
||||
if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x;
|
||||
if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x;
|
||||
drawline(minx, maxx, y); // Draw line from min to max points found on the y
|
||||
// Now increase y
|
||||
if (!changed1) t1x += signx1;
|
||||
t1x += t1xp;
|
||||
if (!changed2) t2x += signx2;
|
||||
t2x += t2xp;
|
||||
y += 1;
|
||||
if (y == y2) break;
|
||||
|
||||
}
|
||||
next:
|
||||
// Second half
|
||||
dx1 = (int)(x3 - x2); if (dx1<0) { dx1 = -dx1; signx1 = -1; }
|
||||
else signx1 = 1;
|
||||
dy1 = (int)(y3 - y2);
|
||||
t1x = x2;
|
||||
|
||||
if (dy1 > dx1) { // swap values
|
||||
SWAP(dy1, dx1);
|
||||
changed1 = true;
|
||||
}
|
||||
else changed1 = false;
|
||||
|
||||
e1 = (int)(dx1 >> 1);
|
||||
|
||||
for (int i = 0; i <= dx1; i++) {
|
||||
t1xp = 0; t2xp = 0;
|
||||
if (t1x<t2x) { minx = t1x; maxx = t2x; }
|
||||
else { minx = t2x; maxx = t1x; }
|
||||
// process first line until y value is about to change
|
||||
while (i<dx1) {
|
||||
e1 += dy1;
|
||||
while (e1 >= dx1) {
|
||||
e1 -= dx1;
|
||||
if (changed1) { t1xp = signx1; break; }//t1x += signx1;
|
||||
else goto next3;
|
||||
}
|
||||
if (changed1) break;
|
||||
else t1x += signx1;
|
||||
if (i<dx1) i++;
|
||||
}
|
||||
next3:
|
||||
// process second line until y value is about to change
|
||||
while (t2x != x3) {
|
||||
e2 += dy2;
|
||||
while (e2 >= dx2) {
|
||||
e2 -= dx2;
|
||||
if (changed2) t2xp = signx2;
|
||||
else goto next4;
|
||||
}
|
||||
if (changed2) break;
|
||||
else t2x += signx2;
|
||||
}
|
||||
next4:
|
||||
|
||||
if (minx>t1x) minx = t1x; if (minx>t2x) minx = t2x;
|
||||
if (maxx<t1x) maxx = t1x; if (maxx<t2x) maxx = t2x;
|
||||
drawline(minx, maxx, y);
|
||||
if (!changed1) t1x += signx1;
|
||||
t1x += t1xp;
|
||||
if (!changed2) t2x += signx2;
|
||||
t2x += t2xp;
|
||||
y += 1;
|
||||
if (y>y3) return;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-bresenham method
|
||||
/*void FillTriangle(int x1, int y1, int x2, int y2, int x3, int y3, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
if ((x1 == x2 && x1 == x3) || (y1 == y2) && (y1 == y3))
|
||||
return;
|
||||
|
||||
int tmp;
|
||||
if (y2 < y1){ tmp = y1; y1 = y2; y2 = tmp; tmp = x1; x1 = x2; x2 = tmp; }
|
||||
if (y3 < y1){ tmp = y1; y1 = y3; y3 = tmp; tmp = x1; x1 = x3; x3 = tmp; }
|
||||
if (y3 < y2){ tmp = y2; y2 = y3; y3 = tmp; tmp = x2; x2 = x3; x3 = tmp; }
|
||||
|
||||
int dy1 = y2 - y1, dx1 = x2 - x1;
|
||||
int dy2 = y3 - y1, dx2 = x3 - x1;
|
||||
|
||||
if (dy1)
|
||||
{
|
||||
for (int i = y1; i < y2; i++)
|
||||
{
|
||||
int ax = x1 + ((i - y1)*dx1) / dy1;
|
||||
int bx = x1 + ((i - y1)*dx2) / dy2;
|
||||
tmp = ax;
|
||||
if (ax > bx) ax = bx, bx = tmp;
|
||||
for (int j = ax; j < bx; j++) Draw(j, i, c, col);
|
||||
}
|
||||
}
|
||||
|
||||
dy1 = y3 - y2; dx1 = x3 - x2;
|
||||
|
||||
if (dy1)
|
||||
{
|
||||
for (int i = y2; i < y3; i++)
|
||||
{
|
||||
int ax = x2 + ((i - y2)*dx1) / dy1;
|
||||
int bx = x1 + ((i - y1)*dx2) / dy2;
|
||||
tmp = ax;
|
||||
if (ax > bx) ax = bx, bx = tmp;
|
||||
for (int j = ax; j < bx; j++) Draw(j, i, c, col);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
void DrawCircle(int xc, int yc, int r, wchar_t c = 0x2588, short col = 0x000F)
|
||||
{
|
||||
int x = 0;
|
||||
@ -904,4 +1069,4 @@ protected:
|
||||
|
||||
atomic<bool> olcConsoleGameEngine::m_bAtomActive(false);
|
||||
condition_variable olcConsoleGameEngine::m_cvGameFinished;
|
||||
mutex olcConsoleGameEngine::m_muxGame;
|
||||
mutex olcConsoleGameEngine::m_muxGame;
|
Loading…
x
Reference in New Issue
Block a user