-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path3DConsoleGrapher.h
196 lines (172 loc) · 6.83 KB
/
3DConsoleGrapher.h
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
---------------------------------------------------
Created by Matej Hamas on October 2014.
Copyright (c) 2014 Matej Hamas. All rights reserved.
---------------------------------------------------
------------------3DConsoleGrapher------------------
Simple, header only, library for displaying 3D function in console.
2 possible representations
1. Band representation
Choose number X and the range of output will be proportionally split to
2X + 1 bands. Values generated by the functions will be represented by integers
from set [-X, X].
NB: This representation ensures that symbol 0 describes band with values in the middle
of the chosen output range. There is special ZERO_SYMBOL which denotes the band
where true 0 lies.
2. True value representation
True output values are displayed in the plot.
USAGE
1. Set constants to meet your need.
2. Creat instance of the grapher.
3. Call plot() on the instance
- you may specify which representation you want (default is false = band representation)
*/
#include <stdio.h>
#include <iostream>
#include <vector>
#include <sstream>
const std::string ORIGIN = "\u2514"; // bottom left corner
const std::string VERTICAL_BAR = "\u2502";
const std::string HORIZONTAL_BAR = "\u2500";
const std::string TOO_SMALL_SYMBOL = "-"; // if Z value below lower bound of Z
const std::string TOO_LARGE_SYMBOL = "+"; // if Z value above upper bound of Z
const std::string ZERO_SYMBOL = "."; // if Z value falls into the band where true 0 lies
class ConsoleGrapher3D
{
private:
const int NUM_OF_POS_BANDS = 9; // if bands are used (opposite to true values), this set the max positive band
const int NUM_OF_DEC_PLACES_Y = 2; // number of decimal places on labels on Y axis
const int WIDTH_OF_LABEL_Y = 5; // width of labels on Y axis
const int WIDTH_OF_MARK = 6; // width of labels shown in plot (both band or true values)
/* Size of the plot */
const int _numOfRows = 50;
const int _numOfCols = 30;
const double _lowerBoundX;
const double _upperBoundX;
const double _lowerBoundY;
const double _upperBoundY;
const double _lowerBoundZ;
const double _upperBoundZ;
double _stepX;
double _stepY;
double _stepZ;
std::vector<std::string> _symbols; // symbols used for band representation
std::function<double(double, double)> _fun;
/**
* Evaluates Z value to string symbol.
* Used for Band representation exclusively
*/
std::string evalToString(double val)
{
if (val < _lowerBoundZ)
{
std::stringstream ss;
ss << std::setw(WIDTH_OF_MARK) << TOO_SMALL_SYMBOL;
return ss.str();
}
else
{
const int numOfSymbols = (int)_symbols.size();
for (int i = 0; i < numOfSymbols; i++)
{
if (val <= _lowerBoundZ + (i + 1) * _stepZ)
{
return _symbols[i];
}
}
}
std::stringstream ss;
ss << std::setw(WIDTH_OF_MARK) << TOO_LARGE_SYMBOL;
return ss.str();
}
public:
ConsoleGrapher3D(const std::function<double(double, double)>& fun,
const double lowerBoundX,
const double upperBoundX,
const double lowerBoundY,
const double upperBoundY,
const double lowerBoundZ,
const double upperBoundZ)
: _fun(fun),
_lowerBoundX(lowerBoundX),
_upperBoundX(upperBoundX),
_lowerBoundY(lowerBoundY),
_upperBoundY(upperBoundY),
_lowerBoundZ(lowerBoundZ),
_upperBoundZ(upperBoundZ)
{
_stepX = (double)(_upperBoundX - _lowerBoundX) / (double)_numOfCols;
_stepY = (double)(_upperBoundY - _lowerBoundY) / (double)_numOfRows;
_stepZ = (_upperBoundZ - _lowerBoundZ) / (double)(2 * NUM_OF_POS_BANDS + 1);
/* Initialize band symbols */
for (int i = -NUM_OF_POS_BANDS; i <= NUM_OF_POS_BANDS; i++)
{
std::stringstream ss;
ss << std::setw(WIDTH_OF_MARK) << i;
_symbols.push_back(ss.str());
}
/* Replace band symbol where true 0 lies with chosen symbol */
int zeroBandIndex = -1;
if (_lowerBoundZ <= 0 && 0 <= upperBoundZ) {
zeroBandIndex = - (int)_lowerBoundZ / _stepZ;
}
if (0 <= zeroBandIndex && zeroBandIndex < _symbols.size()) {
std::stringstream ss;
ss << std::setw(WIDTH_OF_MARK) << ZERO_SYMBOL;
std::cout << "Symbol " << _symbols[zeroBandIndex] << " exchanged for " << ZERO_SYMBOL << std::endl;;
_symbols[zeroBandIndex] = ss.str();
}
}
/**
* Plots the graph of 3D function.
* If parameters ommited, band representation used.
*
* numOfDecimalPlaces - for true value representation, to set the number of decimal places
* shown in the plot
*/
void plot(bool showTrueValues = false, int numOfDecimalPlaces = 1)
{
double x = _lowerBoundX;
double y = _upperBoundY;
for (double i = 0; i <= _numOfRows + 1; i++, y -= _stepY)
{
if (i < _numOfRows) {
std::cout << std::setw(WIDTH_OF_LABEL_Y) << std::setprecision(NUM_OF_DEC_PLACES_Y) << std::fixed << y << VERTICAL_BAR;
} else if (i == _numOfRows) {
std::cout << std::setw(WIDTH_OF_LABEL_Y) << " " << ORIGIN;
} else if (i == _numOfRows + 1) {
std::cout << std::setw(WIDTH_OF_LABEL_Y + 1) << " ";
}
x = _lowerBoundX;
for (int j = 0; j < _numOfCols; j++, x += _stepX)
{
if(i < _numOfRows)
{
if (showTrueValues)
{
std::cout << std::setw(WIDTH_OF_MARK) << std::setprecision(numOfDecimalPlaces) << _fun(x,y);
} else
{
std::cout << evalToString(_fun(x,y));
}
}
else if (i == _numOfRows)
{
std::stringstream ss;
for (int i = 0; i < WIDTH_OF_MARK; i++)
{
ss << HORIZONTAL_BAR;
}
std::cout << ss.str();
}
else if (i == _numOfRows + 1)
{
if(j % 2 == 0) {
std::cout << std::setw(2 * WIDTH_OF_MARK) << std::setprecision(2) << x;
}
}
}
std::cout << std::endl;
}
}
};