Summary Files Commits Branches @commit @diff @stats
path: root / src / LEDVideoWall.cpp
blob: abdbcf03ffe1e6ff17b1c90c0974147a6fd530b9 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "LEDVideoWall.h"
#include "cli.hpp"
#include <sstream>

LEDVideoWall::~LEDVideoWall() { }

LEDVideoWall* LEDVideoWall::Freeform(mm panel_width, mm panel_height, mm wall_max_width, mm wall_max_height) {
	unsigned int columns = wall_max_width / panel_width;
	unsigned int rows = wall_max_height / panel_height;
	return new LEDVideoWall(
		columns * panel_width,
		rows * panel_height,
		columns,
		rows);
}

#define CMP_EPSILON 0.0000001

// GCD is available in stl from C++17 onwards, but I'm still reimplementing it
unsigned int gcd(unsigned int a, unsigned int b) {
	// Eucledian algorithm https://en.wikipedia.org/wiki/Greatest_common_divisor#Euclidean_algorithm
	if (b == 0) return a;
	return gcd(b, a % b);
}

LEDVideoWall* LEDVideoWall::Aspect(mm panel_width, mm panel_height, unsigned int aspect_width, unsigned int aspect_height) {
	// Simplify the fraction (aspect), because we want the smallest screen size
	// For example, turn 16:10 into 8:5
	unsigned int aspect_gcd = gcd(aspect_width, aspect_height);
	aspect_width /= aspect_gcd;
	aspect_height /= aspect_gcd;

	return new LEDVideoWall(
			aspect_width * panel_width,
			aspect_height * panel_height,
			aspect_width,
			aspect_height);
}

LEDVideoWall::LEDVideoWall(
	mm width,
	mm height,
	unsigned int columns_of_panels,
	unsigned int rows_of_panels) :
		Screen(width, height),
		columns_of_panels(columns_of_panels),
		rows_of_panels(rows_of_panels)
{ }

enum class LEDVideoWallType {
	Freeform = 0, Aspect
};

LEDVideoWall* LEDVideoWall::fromCommandLine() {
	mm ipanel_width = 0, ipanel_height = 0;
	do {
		cli::scanTwoValues(
			"%dx%d",
			ipanel_width,
			ipanel_height,
			"Enter panel size (in the format 'WIDTHxHEIGHT'): ",
			"Invalid size, try again!");

		if (ipanel_width == 0 || ipanel_height == 0) {
			std::cerr << "The panel width or height cannot be zero!" << std::endl;
			continue;
		}
		break;
	} while (true);

	unsigned int itype = 2;
	cli::readUserInputMax(
		itype,
		(unsigned int)2,
		"Select an LED video wall type [0 for Freeform, 1 for Aspect]: ",
		"Invalid type, try again!");

	LEDVideoWallType type = static_cast<LEDVideoWallType>(itype);
	switch (type) {
		case LEDVideoWallType::Freeform: {
			mm iwall_max_width = 0, iwall_max_height = 0;
			do {
				cli::scanTwoValues(
					"%dx%d",
					iwall_max_width,
					iwall_max_height,
					"Enter singe wall size (in the format 'WIDTHxHEIGHT'): ",
					"Invalid size, try again!");

				if (iwall_max_width == 0 || iwall_max_height == 0) {
					std::cerr << "The wall width or height cannot be zero!" << std::endl;
					continue;
				}
				if (iwall_max_width < ipanel_width || iwall_max_height < ipanel_height) {
					std::cerr << "The wall cannot be smaller than the panel!" << std::endl;
					continue;
				}
				break;
			} while (true);

			return LEDVideoWall::Freeform(ipanel_width, ipanel_height, iwall_max_width, iwall_max_height);
		};

		case LEDVideoWallType::Aspect: {
			mm iaspect_width = 0, iaspect_height = 0;
			do {
				cli::scanTwoValues(
					"%d:%d",
					iaspect_width,
					iaspect_height,
					"Enter aspect ratio (in the format 'WIDTH:HEIGHT'): ",
					"Invalid input, try again!");

				if (iaspect_width == 0 || iaspect_height == 0) {
					std::cerr << "The aspect ratio width or height cannot be zero!" << std::endl;
					continue;
				}
				break;
			} while (true);

			return LEDVideoWall::Aspect(ipanel_width, ipanel_height, iaspect_width, iaspect_height);
		};
	}
	throw std::logic_error("Implementation error: didn't handle all types of LED video walls!");
}

std::string LEDVideoWall::extraOutput() const {
	std::ostringstream output;
	output << " (" << this->columns_of_panels << " columns by " << this->rows_of_panels << " rows of tiles)";
	return output.str();
}