Skip to content

Instantly share code, notes, and snippets.

@gszauer
Last active May 20, 2021
Embed
What would you like to do?
LineBlog
#include "Line.h"
#include <cmath>
#include <iostream>
inline int max(int a, int b) {
if (a > b) {
return a;
}
return b;
}
inline void swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
void PutPixel(MonoImage& image, int x, int y, unsigned char val) {
#if _DEBUG
if (y * image.width + x > image.width * image.height) {
std::cout << "About to break\n";
}
#endif
image.data[y * image.width + x] = val;
}
void DrawLine_Reference(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = (dx > dy ? dx : -dy);
while (PutPixel(image, x0, y0, val), x0 != x1 || y0 != y1) {
int e2 = err;
if (e2 > -dx << 1) { err -= dy; x0 += sx; }
if (e2 < dy << 1) { err += dx; y0 += sy; }
}
}
void DrawLine_Implicit_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
float m = (x0 == x1) ? 0.0f : (float(y1 - y0) / float(x1 - x0));
float b = float(y0) - m * float(x0);
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
if (abs(x1 - x0) > abs(y1 - y0)) {
for (int x = x0; x != x1 + xStep; x += xStep) {
float y = m * float(x) + b;
PutPixel(image, x, int(y + 0.5f), val);
}
}
else {
for (int y = y0; y != y1 + yStep; y += yStep) {
float x = (x0 == x1) ? float(x0) : ((float(y) - b) / m);
PutPixel(image, int(x + 0.5f), y, val);
}
}
}
void DrawLine_Incremental_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
float m = (x0 == x1) ? 0.0f : (float(y1 - y0) / float(x1 - x0));
float changeX = (y0 == y1) ? 0.0f : (float(x1 - x0) / float(y1 - y0));
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
if (abs(x1 - x0) > abs(y1 - y0)) {
float y = float(y0);
for (int x = x0; x != x1 + xStep; x += xStep) {
PutPixel(image, x, int(y), val);
y += m * xStep;
}
}
else {
float x = float(x0);
for (int y = y0; y != y1 + yStep; y += yStep) {
PutPixel(image, int(x), y, val);
x += changeX * yStep;
}
}
}
void DrawLine_DDA_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
float xDelta = float(x1 - x0);
float yDelta = float(y1 - y0);
int steps = max(abs(x1 - x0), abs(y1 - y0));
float xStep = xDelta / float(steps);
float yStep = yDelta / float(steps);
float x = float(x0);
float y = float(y0);
for (int i = 0; i <= steps; ++i) {
PutPixel(image, (int)(x), (int)(y), val);
y += yStep;
x += xStep;
}
}
void DrawLine_DDA_Int(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
int xDelta = x1 - x0;
int yDelta = y1 - y0;
int steps = max(abs(x1 - x0), abs(y1 - y0));
// rational number x
int xWhole = x0;
int xNumerator = 0;
int xDenominator = steps;
// rational number y
int yWhole = y0;
int yNumerator = 0;
int yDenominator = steps;
for (int i = 0; i <= steps; ++i) {
PutPixel(image, xWhole, yWhole, val);
xNumerator += xDelta;
if (xNumerator >= xDenominator) {
xWhole += 1;
xNumerator -= xDenominator;
}
else if (xNumerator < 0) {
xWhole -= 1;
xNumerator += xDenominator;
}
yNumerator += yDelta;
if (yNumerator >= yDenominator) {
yWhole += 1;
yNumerator -= yDenominator;
}
else if (yNumerator < 0) {
yWhole -= 1;
yNumerator += yDenominator;
}
}
}
void DrawLine_DDA_Int_Shorter(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
int absXDelta = abs(x1 - x0);
int absYDelta = abs(y1 - y0);
int steps = max(absXDelta, absYDelta);
int xDir = x0 < x1 ? 1 : -1;
int yDir = y0 < y1 ? 1 : -1;
int x = x0, xNumerator = 0;
int y = y0, yNumerator = 0;
for (int i = 0; i <= steps; ++i) {
PutPixel(image, x, y, val);
xNumerator += absXDelta;
if (xNumerator >= steps) {
x += xDir;
xNumerator -= steps;
}
yNumerator += absYDelta;
if (yNumerator >= steps) {
y += yDir;
yNumerator -= steps;
}
}
}
void DrawLine_Error_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
float abs_m = (x0 == x1) ? 0.0f : fabsf(float(y1 - y0) / float(x1 - x0));
float abs_cx = (y0 == y1) ? 0.0f : fabsf(float(x1 - x0) / float(y1 - y0));
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
float error = 0.0f;
#if 1
if (abs(x1 - x0) > abs(y1 - y0)) {
float y = float(y0);
for (int x = x0; x != x1 + xStep; x += xStep) {
PutPixel(image, x, int(y + 0.5f), val);
error += abs_m;
if (error >= 0.5f) {
y += float(yStep);
error -= 1.0f;
}
}
}
else {
float x = float(x0);
for (int y = y0; y != y1 + yStep; y += yStep) {
PutPixel(image, int(x + 0.5f), y, val);
error += abs_cx;
if (error >= 0.5f) {
x += float(xStep);
error -= 1.0f;
}
}
}
#else
bool steep = abs(x1 - x0) > abs(y1 - y0);
int steps = max(abs(x1 - x0), abs(y1 - y0));
float y = float(y0);
float x = float(x0);
for (int step = 0; step <= steps; ++step) {
PutPixel(image, int(x + 0.5f), int(y + 0.5f), val);
if (steep) {
x += float(xStep);
error += abs_m;
if (error >= 0.5f) {
y += float(yStep);
error -= 1.0f;
}
}
else {
y += float(yStep);
error += abs_cx;
if (error >= 0.5f) {
x += float(xStep);
error -= 1.0f;
}
}
}
#endif
}
void DrawLine_Error_Int(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
int error = 0;
#if 1
if (dx > dy) {
int m = 2 * dy;
int scale = 2 * dx;
for (int x = x0, y = y0; x != x1 + xStep; x += xStep) {
PutPixel(image, x, y, val);
error += m;
if (error >= dx) {
y += yStep;
error -= scale;
}
}
}
else {
int m = 2 * dx;
int scale = 2 * dy;
for (int y = y0, x = x0; y != y1 + yStep; y += yStep) {
PutPixel(image, x, y, val);
error += m;
if (error >= dy) {
x += xStep;
error -= scale;
}
}
}
#else
bool steep = dx > dy;
int steps = max(dx, dy);
int y = y0;
int x = x0;
int m = 2 * dx;
int scale = 2 * dy;
if (steep) {
m = 2 * dy;
scale = 2 * dx;
}
for (int step = 0; step <= steps; ++step) {
PutPixel(image, x, y, val);
if (steep) {
x += xStep;
error += m;
if (error >= dx) {
y += yStep;
error -= scale;
}
}
else {
y += yStep;
error += m;
if (error >= dy) {
x += xStep;
error -= scale;
}
}
}
#endif
}
void DrawLine_Midpoint_Implicit_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
#if 0
float m = (x0 == x1) ? 0.0f : (float(y1 - y0) / float(x1 - x0));
float b = float(y0) - m * float(x0);
float A = float(y1 - y0);
float B = -float(x1 - x0);
float C = b * float(x1 - x0);
int y = y0;
for (int x = x0; x <= x1; x += 1) {
PutPixel(image, x, y, val);
float d = A * float(x + 1) + B * (float(y) + 0.5f) + C;
if (d >= 0) {
y += 1;
}
}
#else
bool steep = abs(y1 - y0) >= abs(x1 - x0);
if (!steep && x1 < x0) {
swap(x1, x0);
swap(y1, y0);
}
else if (steep && y1 < y0) {
swap(x1, x0);
swap(y1, y0);
}
float m = (x0 == x1) ? 0.0f : (float(y1 - y0) / float(x1 - x0));
float b = float(y0) - m * float(x0);
float A = float(y1 - y0);
float B = -float(x1 - x0);
float C = b * float(x1 - x0);
int yStep = y1 < y0 ? -1 : 1;
int xStep = x1 < x0 ? -1 : 1;
if (!steep) {
float yHalf = y1 < y0 ? -0.5f : 0.5f;
for (int x = x0, y = y0; x <= x1; x += 1) {
PutPixel(image, x, y, val);
float d = A * float(x + 1) + B * (float(y) + yHalf) + C;
if (d * float(yStep) >= 0.0f) {
y += yStep;
}
}
}
else {
float xHalf = x1 < x0 ? -0.5f : 0.5f;
for (int x = x0, y = y0; y <= y1; y += 1) {
PutPixel(image, x, y, val);
float d = A * (float(x) + xHalf) + B * float(y + 1) + C;
if (d * float(xStep) < 0.0f) {
x += xStep;
}
}
}
#endif
}
void DrawLine_Thick(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
int dx = abs(x1 - x0);
int dy = abs(y1 - y0);
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
int error = 0;
if (dx > dy) {
int m = 2 * dy;
int scale = 2 * dx;
for (int x = x0, y = y0; x != x1 + xStep; x += xStep) {
PutPixel(image, x, y, val);
PutPixel(image, x, y + yStep, val);
error += m;
if (error >= dx) {
y += yStep;
error -= scale;
}
}
}
else {
int m = 2 * dx;
int scale = 2 * dy;
for (int y = y0, x = x0; y != y1 + yStep; y += yStep) {
PutPixel(image, x, y, val);
PutPixel(image, x + xStep, y, val);
error += m;
if (error >= dy) {
x += xStep;
error -= scale;
}
}
}
}
void DrawLine_Thick_Brush(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
if (x0 == x1 && y0 == y1) {
PutPixel(image, x0, y0, val);
return;
}
int absXDelta = abs(x1 - x0);
int absYDelta = abs(y1 - y0);
int steps = max(absXDelta, absYDelta);
int xDir = x0 < x1 ? 1 : -1;
int yDir = y0 < y1 ? 1 : -1;
int x = x0, xNumerator = 0;
int y = y0, yNumerator = 0;
for (int i = 0; i <= steps; ++i) {
PutPixel(image, x, y, val);
PutPixel(image, x + 1, y, val);
PutPixel(image, x + 1, y + 1, val);
PutPixel(image, x, y + 1, val);
PutPixel(image, x + 1, y, val);
PutPixel(image, x + 2, y, val);
PutPixel(image, x + 1, y + 1, val);
PutPixel(image, x + 1, y + 2, val);
PutPixel(image, x + 2, y + 2, val);
PutPixel(image, x, y + 1, val);
PutPixel(image, x, y + 2, val);
xNumerator += absXDelta;
if (xNumerator >= steps) {
x += xDir;
xNumerator -= steps;
}
yNumerator += absYDelta;
if (yNumerator >= steps) {
y += yDir;
yNumerator -= steps;
}
}
}
void DrawLine_Wu_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val) {
float abs_m = (x0 == x1) ? 0.0f : fabsf(float(y1 - y0) / float(x1 - x0));
float abs_cx = (y0 == y1) ? 0.0f : fabsf(float(x1 - x0) / float(y1 - y0));
int xStep = x0 < x1 ? 1 : -1;
int yStep = y0 < y1 ? 1 : -1;
float error = 0.0f;
if (abs(x1 - x0) > abs(y1 - y0)) {
float y = float(y0);
for (int x = x0; x != x1 + xStep; x += xStep) {
float scale1 = 1.0f - fabsf(error);
float scale2 = 1.0f - scale1;
int errorStep = (error < 0.0f ? -1 : 1) * yStep;
PutPixel(image, x, int(y + 0.5f), (unsigned char)(float(val) * scale1));
PutPixel(image, x, int(y + 0.5f) + errorStep, (unsigned char)(float(val) * scale2));
error += abs_m;
if (error >= 0.5f) {
y += float(yStep);
error -= 1.0f;
}
}
}
else {
float x = float(x0);
for (int y = y0; y != y1 + yStep; y += yStep) {
float scale1 = 1.0f - fabsf(error);
float scale2 = 1.0f - scale1;
int errorStep = (error < 0.0f ? -1 : 1) * xStep;
PutPixel(image, int(x + 0.5f), y, (unsigned char)(float(val) * scale1));
PutPixel(image, int(x + 0.5f) + errorStep, y, (unsigned char)(float(val) * scale2));
error += abs_cx;
if (error >= 0.5f) {
x += float(xStep);
error -= 1.0f;
}
}
}
}
inline unsigned char GetClipCode(const Point& p, const Point& min, const Point& max) {
return (
(p.x < min.x? 8 : 0) | (p.x > max.x? 4 : 0) |
(p.y < min.y? 2 : 0) | (p.y > max.y? 1 : 0)
);
}
// Returns true if the line is visible, false if it's outside of clip rect.
bool Clip_Line(Point& a, Point& b, const Point& min, const Point& max) {
unsigned char clipA = GetClipCode(a, min, max);
unsigned char clipB = GetClipCode(b, min, max);
float deltaX, deltaY;
while ((clipA | clipB) != 0) { // Loop until clipped
// Trivial reject case if both points are on
// the same side of a clip plane, we can reject them
if ((clipA & clipB) != 0) {
return false;
}
deltaX = b.x - a.x;
deltaY = b.y - a.y;
if (clipA != 0) {
if ((clipA & 8) == 8) {
a.y += (min.x - a.x) * deltaY / deltaX;
a.x = min.x;
}
else if ((clipA & 4) == 4) {
a.y += (max.x - a.x) * deltaY / deltaX;
a.x = max.x;
}
else if ((clipA & 2) == 2) {
a.x += (min.y - a.y) * deltaX / deltaY;
a.y = min.y;
}
else if ((clipA & 1) == 1) {
a.x += (max.y - a.y) * deltaX / deltaY;
a.y = max.y;
}
clipA = GetClipCode(a, min, max);
}
else if (clipB != 0) {
if ((clipB & 8) == 8) {
b.y += (min.x - b.x) * deltaY / deltaX;
b.x = min.x;
}
else if ((clipB & 4) == 4) {
b.y += (max.x - b.x) * deltaY / deltaX;
b.x = max.x;
}
else if ((clipB & 2) == 2) {
b.x += (min.y - b.y) * deltaX / deltaY;
b.y = min.y;
}
else if ((clipB & 1) == 1) {
b.x += (max.y - b.y) * deltaX / deltaY;
b.y = max.y;
}
clipB = GetClipCode(b, min, max);
}
}
return true;
}
#pragma once
struct MonoImage {
unsigned char* data;
int width;
int height;
inline MonoImage() : data(0), width(0), height(0) { }
};
struct Point {
float x;
float y;
inline Point() : x(0.0f), y(0.0f) { }
inline Point(float _x, float _y) : x(_x), y(_y) { }
inline Point(int _x, int _y) : x(_x), y(_y) { }
inline int xAsInt() { return x; }
inline int yAsInt() { return y; }
};
typedef void (*fnDrawLine)(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void PutPixel(MonoImage& image, int x, int y, unsigned char val);
void DrawLine_Reference(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Implicit_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Incremental_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_DDA_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_DDA_Int(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_DDA_Int_Shorter(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Error_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Error_Int(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Midpoint_Implicit_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Thick(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Thick_Brush(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
void DrawLine_Wu_Real(MonoImage& image, int x0, int y0, int x1, int y1, unsigned char val);
// Returns true if the line is visible, false if it's outside of clip rect.
bool Clip_Line(Point& a, Point& b, const Point& min, const Point& max);
#define PI 3.14159265359f
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <Windows.h>
#include <iostream>
#include <limits>
#include <cmath>
#include "Line.h"
#include "stb_image_write.h"
#undef max
double ProfileDrawingMethod(fnDrawLine DrawLine, const char* output) {
LARGE_INTEGER timerFrequency;
if (!QueryPerformanceFrequency(&timerFrequency)) {
std::cout << "WinMain: QueryPerformanceFrequency failed\n";
return 0.0;
}
MonoImage image;
image.width = 512;
image.height = 512;
image.data = new unsigned char[image.width * image.height];
for (int x = 0; x < image.width; ++x) {
for (int y = 0; y < image.height; ++y) {
PutPixel(image, x, y, 42);
}
}
int val = 116;
LARGE_INTEGER timerStart = { 0 };
LARGE_INTEGER timerStop = { 0 };
QueryPerformanceCounter(&timerStart);
Point lineA, lineB;
Point clipMin = Point(150, 150);
Point clipMax = Point(image.width - 150, image.height - 150);
for (int angle = 0; angle < 360; angle += 1) {
float radians = float(angle) * 0.01745329;
float s = sinf(radians);
float c = cosf(radians);
if (angle % 10 == 0) {
lineA = Point(20.0f * s + 256.0f, 20.0f * c + 256.0f);
lineB = Point(60.0f * s + 256.0f, 60.0f * c + 256.0f);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
}
if (angle % 7 == 0) {
lineA = Point(70.0f * s + 256.0f, 70.0f * c + 256.0f);
lineB = Point(110.0f * s + 256.0f, 110.0f * c + 256.0f);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
}
if (angle % 4 == 0) {
lineA = Point(120.0f * s + 256.0f, 120.0f * c + 256.0f);
lineB = Point(160.0f * s + 256.0f, 160.0f * c + 256.0f);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
}
if (angle % 2 == 0) {
lineA = Point(170.0f * s + 256.0f, 170.0f * c + 256.0f);
lineB = Point(210.0f * s + 256.0f, 210.0f * c + 256.0f);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
}
lineA = Point(220.0f * s + 256.0f, 220.0f * c + 256.0f);
lineB = Point(250.0f * s + 256.0f, 250.0f * c + 256.0f);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
}
lineA = Point(256 - 15, 256);
lineB = Point(256 + 15, 256);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
lineA = Point(256, 256 - 15);
lineB = Point(256, 256 + 15);
if (Clip_Line(lineA, lineB, clipMin, clipMax)) {
DrawLine(image, lineA.xAsInt(), lineA.yAsInt(), lineB.xAsInt(), lineB.yAsInt(), val);
}
QueryPerformanceCounter(&timerStop);
LONGLONG timerDiff = timerStop.QuadPart - timerStart.QuadPart;
double ms = (double)timerDiff * 1000.0 / (double)timerFrequency.QuadPart;
int write_result = stbi_write_png(output, image.width, image.height, 1, image.data, 0);
if (write_result == 0) {
std::cout << "Error writing file: " << output << "\n";
}
delete[] image.data;
return ms;
}
int main(int argc, char** argv) {
double time = ProfileDrawingMethod(DrawLine_Reference, "Line_Reference.png");
std::cout << "DrawLine_Reference: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Implicit_Real, "Line_Implicit_Real.png");
std::cout << "DrawLine_SlopeIntercept_Real: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Incremental_Real, "Line_Incremental_Real.png");
std::cout << "Line_Incremental_Real: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_DDA_Real, "Line_DDA_Real.png");
std::cout << "DrawLine_DDA_Real: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_DDA_Int_Shorter, "Line_DDA_Int_Shorter.png");
std::cout << "DrawLine_DDA_Int: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Error_Real, "Line_Error_Real.png");
std::cout << "DrawLine_Error_Real: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Error_Int, "Line_Error_Int.png");
std::cout << "DrawLine_Error_Int: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Midpoint_Implicit_Real, "Line_Midpoint_Implicit_Real.png");
std::cout << "DrawLine_Midpoint_Real: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Thick, "Line_Thick.png");
std::cout << "DrawLine_Thick: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Thick_Brush, "Line_Thick_Brush.png");
std::cout << "DrawLine_Thick_Brush: " << time << " ms.\n";
time = ProfileDrawingMethod(DrawLine_Wu_Real, "Line_Wu_Real.png");
std::cout << "DrawLine_Wu_Real: " << time << " ms.\n";
std::cout << "Press ENTER to continue... " << std::flush;
std::cin.ignore(std::numeric_limits <std::streamsize> ::max(), '\n');
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment