Add dfl.chart.LineGraphRenderer

Update TableRenderer
This commit is contained in:
haru-s 2024-04-07 00:32:59 +09:00
parent d2e832ec54
commit 262711208a
13 changed files with 829 additions and 36 deletions

View file

@ -5,10 +5,10 @@ DFL is a Win32 windowing library for the D language.
## Recent major features
- **Module "dfl.chart" is now comming.**
- **TableRenderer (with example)**
- ~LineGraphRenderer~
- TableRenderer (with example)
- **LineGraphRenderer (with example)**
- ~TimeChartRenderer~
- **Add simple clock "Dclock" as an example of DFL application.**
- Add simple clock "Dclock" as an example of DFL application.
- Module "dfl.printing" is now comming.
- PrintDialog
- PrintSetupDialog
@ -40,6 +40,7 @@ DFL is a Win32 windowing library for the D language.
![screen shot](./examples/richtextbox/image/screenshot.png "screen shot")
![screen shot](./examples/dclock/image/screenshot.png "screen shot")
![screen shot](./examples/tablerenderer/image/screenshot.png "screen shot")
![screen shot](./examples/linegraphrenderer/image/screenshot.png "screen shot")
## Build and Install (dfl.lib and dfl_debug.lib)
### 1. Set environment variables

16
examples/linegraphrenderer/.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
.dub
docs.json
__dummy.html
docs/
/hello_dfl
hello_dfl.so
hello_dfl.dylib
hello_dfl.dll
hello_dfl.a
hello_dfl.lib
hello_dfl-test-*
*.exe
*.pdb
*.o
*.obj
*.lst

View file

@ -0,0 +1,13 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "C++ Launch (Windows) linegraphrenderer",
"type": "cppvsdbg",
"request": "launch",
"cwd": "${workspaceRoot}",
"program": "./bin/linegraphrenderer.exe",
"console": "internalConsole"
}
]
}

View file

@ -0,0 +1,20 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "dub",
"run": false,
"cwd": ".",
"compiler": "$current",
"archType": "$current",
"buildType": "$current",
"configuration": "$current",
"problemMatcher": [
"$dmd"
],
"group": "build",
"label": "dub: Build linegraphrenderer_sample",
"detail": "dub build --compiler=dmd.EXE -a=x86_64 -b=debug -c=application"
}
]
}

View file

@ -0,0 +1,2 @@
# Screen Shot
![screen shot](./image/screenshot.png "screen shot")

View file

@ -0,0 +1,16 @@
{
"authors": ["haru-s"],
"copyright": "Copyright (C) 2024 haru-s",
"description": "DFL sample code.",
"name": "linegraphrenderer",
"targetType": "executable",
"targetPath": "bin",
"dependencies": {
"dfl": {
"path": "../../../dfl"
}
},
"lflags-windows-x86_omf-dmd": ["/exet:nt/su:windows:6.0"],
"lflags-windows-x86_mscoff-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
"lflags-windows-x86_64-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View file

@ -0,0 +1,12 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"d.projectImportPaths": [
"..\\..\\..\\dfl\\source"
]
}
}

View file

@ -0,0 +1,3 @@
set dmd_path=c:\d\dmd2\windows
set dmc_path=c:\dmc\dm
cmd

View file

@ -0,0 +1,111 @@
import dfl;
version(Have_dfl) // For DUB.
{
}
else
{
pragma(lib, "dfl.lib");
}
class MainForm : Form
{
alias CustomLineGraphRenderer = LineGraphRenderer!(string,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int);
CustomLineGraphRenderer _graph;
alias CustomLineGraphRenderer2 = LineGraphRenderer!(int,int,int);
CustomLineGraphRenderer2 _graph2;
// alias CustomTableRenderer = TableRenderer!(string,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int);
// CustomTableRenderer _table;
// alias CustomTableRenderer2 = TableRenderer!(int,int,int);
// CustomTableRenderer2 _table2;
public this()
{
this.text = "LineGraphRenderer example";
this.size = Size(1000, 800);
string csv =
"教科,山田,佐藤,井上,田中,木下,藤原,山本,大森,伊藤,高橋,鈴木,中村,小林,松井,木村,近藤\n" ~
"国語,70,80,80,75,68,65,55,48,45,38,35,25,20,10,5,1\n" ~
"算数,60,90,80,75,68,65,55,48,45,38,35,25,20,10,5,1\n" ~
"理科,80,70,80,75,68,65,55,48,45,38,35,25,20,10,5,1\n" ~
"社会,90,60,80,75,68,65,55,48,45,38,35,25,20,10,5,1\n";
_graph = new CustomLineGraphRenderer(csv, 4);
_graph.showLegend = true;
_graph.legendLineHeight = 18;
_graph.chartMargins = ChartMargins(50, 50, 50, 50);
_graph.plotPointSize = 10;
_graph.verticalZeroPosition = VerticalZeroPosition.BOTTOM;
_graph.plotAreaAndLegendSpanX = 50;
_graph.plotAreaAndHorizontalScaleSpanY = 10;
_graph.plotAreaLeftPadding = 20;
_graph.plotAreaRightPadding = 20;
_graph.plotAreaHeightOnDisplay = 300;
_graph.hasHorizontalScale = true;
_graph.horizontalScaleSpan = 100;
_graph.horizonScaleLineInnerSide = 0;
_graph.horizonScaleLineOuterSide = 5;
_graph.horizontalScaleHeight = 12;
_graph.hasVerticalScale = true;
_graph.verticalMaxScale = 110;
_graph.verticalScaleLineOuterSide = 5;
_graph.verticalScaleLineInnerSide = 0;
_graph.verticalScaleSpan = 20;
_graph.verticalScaleWidth = 40;
_graph.backColor = Color.white;
_graph.plotAreaBoundsColor = Color.black;
_graph.plotLineColorPalette[0] = Color.black;
_graph.plotPointFormList[4..8] = PlotPointForm.CROSS;
_graph.plotPointFormList[8..12] = PlotPointForm.RECTANGLE;
_graph.plotPointFormList[12..16] = PlotPointForm.TRIANGLE;
_graph.relocate = Point(50, 50); // Relocate origin point based on top-left margins.
string csv2 =
"A,B,C\n" ~
"70,80,80\n" ~
"60,90,80\n" ~
"80,70,80\n" ~
"90,60,80\n";
_graph2 = new CustomLineGraphRenderer2(csv2, 4);
_graph2.chartMargins = ChartMargins(10, 10, 10, 10);
_graph2.relocate = Point(600, 50);
// _table = new CustomTableRenderer(csv, 4);
// _table.location = Point(50, 500);
// _table.hasHeader = true;
// _table.showHeader = true;
// _table.headerLine = true;
// _table.width[] = 40;
// _table2 = new CustomTableRenderer2(csv2, 4);
// _table2.hasHeader = true;
// _table2.showHeader = true;
// _table2.headerLine = true;
// _table2.width[] = 40;
// _table2.location = Point(680, 200);
}
protected override void onPaint(PaintEventArgs e)
{
if (_graph)
_graph.draw(e.graphics);
if (_graph2)
_graph2.draw(e.graphics);
// if (_table)
// _table.draw(e.graphics);
// if (_table2)
// _table2.draw(e.graphics);
}
}
static this()
{
Application.enableVisualStyles();
}
void main()
{
Application.run(new MainForm());
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

View file

@ -10,7 +10,7 @@ else
class MainForm : Form
{
alias CustomTableRenderer = TableRenderer!(string, string, string);
alias CustomTableRenderer = TableRenderer!(string, int, int);
CustomTableRenderer _table;
public this()
@ -18,19 +18,17 @@ class MainForm : Form
this.text = "TableRenderer example";
this.size = Size(450, 450);
string csv =
"ID,Name,Value\n" ~
"1,Kyoto,100\n" ~
"2,Osaka,50\n" ~
"3,Tokyo,20\n" ~
"4,Aomori,10\n";
"教科,大森,山田\n" ~
"国語,95,98\n" ~
"理科,75,80\n" ~
"算数,90,78\n" ~
"社会,80,76\n";
_table = new CustomTableRenderer(csv);
_table.height = 40;
_table.width[0] = 50;
_table.width[1] = 80;
_table.width[2] = 150;
_table.width[] = 80;
_table.paddingX = 10;
_table.paddingY = 12;
_table.margin = Point(20, 20);
_table.location = Point(20, 20);
_table.hasHeader = true; // true : 1st line is header.
_table.showHeader = true;
_table.firstRecord = 0;

View file

@ -12,6 +12,7 @@ private import std.csv;
private import std.typecons;
private import std.conv;
private import std.algorithm;
private import std.range;
///
class TableRenderer(T...)
@ -22,7 +23,14 @@ class TableRenderer(T...)
enum DEFAULT_PADDING_Y = 5; ///
///
this(string csv)
this(string csv, int numRecords)
{
this(csv);
_firstRecord = 0;
_lastRecord = numRecords - 1;
}
/// ditto
this(string csv) // deprecated
{
_csv = csv;
_columns = T.length;
@ -47,10 +55,10 @@ class TableRenderer(T...)
g.fillRectangle(new SolidBrush(_backColor), bounds);
// Draw top side line.
if (_topSideLine)
g.drawLine(new Pen(_lineColor), Point(margin.x, margin.y), Point(bounds.right, margin.y));
g.drawLine(new Pen(_lineColor), Point(location.x, location.y), Point(bounds.right, location.y));
// Draw header line.
if (_showHeader && _hasHeader && _headerLine)
g.drawLine(new Pen(_lineColor), Point(margin.x, margin.y + height), Point(bounds.right, margin.y + height));
g.drawLine(new Pen(_lineColor), Point(location.x, location.y + height), Point(bounds.right, location.y + height));
// Draw header.
int row; // -row- is line number in CSV.
int viewLine; // -viewLine- is line number on display.
@ -58,10 +66,10 @@ class TableRenderer(T...)
{
if (_showHeader)
{
int y = margin.y + viewLine * height + _paddingY;
int y = location.y + viewLine * height + _paddingY;
foreach (col, value; csvReader!(Tuple!T)(_csv, null).header)
{
int x = margin.x + sum(_width[0..col]) + _paddingX;
int x = location.x + sum(_width[0..col]) + _paddingX;
g.drawText(to!string(value), _headerFont, _textColor, Rect(x, y, _width[col] - _paddingX, _height - _paddingY), _headerTextFormat);
}
row++;
@ -79,17 +87,17 @@ class TableRenderer(T...)
int rows = (_hasHeader?1:0) + lastRecord - firstRecord + 1;
if (firstRecord + (_hasHeader?1:0) <= row && row <= rows)
{
int y = margin.y + viewLine * height + _paddingY;
int y = location.y + viewLine * height + _paddingY;
foreach (int col, value; record)
{
int x = margin.x + sum(_width[0..col]) + _paddingX;
int x = location.x + sum(_width[0..col]) + _paddingX;
g.drawText(to!string(value), _recordFont, _textColor, Rect(x, y, _width[col] - _paddingX, _height - _paddingY), _recordTextFormat);
}
// Draw horizontal line.
if (_horizontalLine && viewLine < lastRecord - firstRecord + (_showHeader?1:0))
{
int y2 = margin.y + height * (viewLine + 1);
g.drawLine(new Pen(_lineColor), Point(margin.x, y2), Point(bounds.right, y2));
int y2 = location.y + height * (viewLine + 1);
g.drawLine(new Pen(_lineColor), Point(location.x, y2), Point(bounds.right, y2));
}
row++;
viewLine++;
@ -102,22 +110,22 @@ class TableRenderer(T...)
}
// Draw left side line.
if (_leftSideLine)
g.drawLine(new Pen(_lineColor), Point(margin.x, margin.y), Point(margin.x, margin.y + height * viewLine));
g.drawLine(new Pen(_lineColor), Point(location.x, location.y), Point(location.x, location.y + height * viewLine));
// Draw right side line.
if (_rightSideLine)
g.drawLine(new Pen(_lineColor), Point(bounds.right, margin.y), Point(bounds.right, margin.y + height * viewLine));
g.drawLine(new Pen(_lineColor), Point(bounds.right, location.y), Point(bounds.right, location.y + height * viewLine));
// Draw vertical line.
if (_verticalLine)
{
for (int i; i < _columns - 1; i++)
{
int w = sum(_width[0..i+1]);
g.drawLine(new Pen(_lineColor), Point(margin.x + w, margin.y), Point(margin.x + w, margin.y + height * viewLine));
g.drawLine(new Pen(_lineColor), Point(location.x + w, location.y), Point(location.x + w, location.y + height * viewLine));
}
}
// Draw bottom side line.
if (_bottomSideLine)
g.drawLine(new Pen(_lineColor), Point(margin.x, margin.y + height * viewLine), Point(bounds.right, margin.y + height * viewLine));
g.drawLine(new Pen(_lineColor), Point(location.x, location.y + height * viewLine), Point(bounds.right, location.y + height * viewLine));
}
///
@ -180,19 +188,28 @@ class TableRenderer(T...)
Rect bounds() const
{
int rows = (_showHeader?1:0) + lastRecord - firstRecord + 1;
return Rect(_margin.x, _margin.y, sum(_width), height * rows);
return Rect(_location.x, _location.y, sum(_width), height * rows);
}
/// Left and Top margins.
void margin(Point pt)
/// Left and Top point.
void location(Point pt)
{
_margin.x = pt.x;
_margin.y = pt.y;
_location = pt;
}
/// ditto
Point margin() const
Point location() const
{
return _margin;
return _location;
}
/// ditto
deprecated void margin(Point pt)
{
location = pt;
}
/// ditto
deprecated Point margin()
{
return location;
}
///
@ -250,16 +267,26 @@ class TableRenderer(T...)
struct WidthObject // Internal struct.
{
///
this(ref int[] w)
this(int[] w)
{
_arr = w;
}
///
/// Assign operator forwarding.
void opIndexAssign(int value)
{
_arr[] = value;
}
/// ditto
void opIndexAssign(int value, size_t i)
{
_arr[i] = value;
}
/// ditto
void opSliceAssign(int value, size_t i, size_t j)
{
_arr[i..j] = value;
}
///
int opIndex(size_t i)
@ -321,7 +348,7 @@ class TableRenderer(T...)
private:
string _csv;
Point _margin;
Point _location;
int _paddingX;
int _paddingY;
int _columns;
@ -346,3 +373,577 @@ private:
TextFormat _headerTextFormat;
TextFormat _recordTextFormat;
}
///
class LineGraphRenderer(T...)
if (is(T[0] == string) && T.length <= 17 || !is(T[0] == string) && T.length <= 16) // Supported number of colors is 16.
{
///
this(string csv, int numRecords)
{
this(csv);
_firstRecord = 0;
_lastRecord = numRecords - 1;
}
/// ditto
this(string csv) // deprecated
{
_csv = csv;
_vZeroPos = VerticalZeroPosition.BOTTOM;
_chartMargins = ChartMargins(50, 50, 50, 50);
_backColor = Color.white;
_plotAreaBoundsColor = Color.black;
_plotAreaHeightOnDisplay = 100;
_plotAreaLeftPadding = 20;
_plotAreaRightPadding = 20;
_plotAreaAndLegendSpanX = 50;
_plotAreaAndHorizontalScaleSpanY = 10;
_plotPointSize = 10;
_plotLineColorPalette = [
Color.black, // 0
Color.blue, // 1
Color.red, // 2
Color.purple, // 3
Color.yellowGreen, // 4
Color.lightBlue, // 5
Color(0xFF,0xC2,0x0E), // 6: Use Himawari color as yellow.
Color.lightGray, // 7
Color.black, // 8
Color.darkBlue, // 9
Color.darkRed, // 10
Color.mediumPurple, // 11
Color.darkGreen, // 12
Color.darkSeaGreen, // 13
Color.darkOrange, // 14
Color.darkGray // 15
];
for (int i; i < T.length; i++)
{
_plotPointFormList ~= PlotPointForm.CIRCLE;
}
_legendWidth = 100;
_legendLineHeight = 18;
_hasVerticalScale = false;
_verticalScaleWidth = 40;
_verticalMaxScale = 100;
_verticalScaleLineOuterSide = 5;
_verticalScaleLineInnerSide = 5;
_hasHorizontalScale = false;
_horizontalScaleSpan = 50;
_horizonScaleLineInnerSide = 5;
_horizonScaleLineOuterSide = 5;
_horizontalScaleHeight = 25;
}
/// Draw records.
void draw(Graphics g)
{
// Draw background.
Rect backgroundRect = Rect(
plotAreaBounds.x - _chartMargins.left,
plotAreaBounds.y - _chartMargins.top,
plotAreaBounds.width + _chartMargins.left + _chartMargins.right,
plotAreaBounds.height + _chartMargins.top + _chartMargins.bottom
);
if (_showLegend)
{
backgroundRect.width = plotAreaBounds.width + _chartMargins.left + _legendWidth + _chartMargins.right;
int legendBottom = _chartMargins.top + _legendLineHeight * (1 + cast(int)T.length) + _chartMargins.bottom;
if (legendBottom > backgroundRect.bottom)
backgroundRect.height = legendBottom;
}
if (_hasVerticalScale)
{
backgroundRect.x = plotAreaBounds.x - _chartMargins.left - _verticalScaleWidth,
backgroundRect.width = plotAreaBounds.width + _chartMargins.left + _legendWidth + _chartMargins.right + _verticalScaleWidth;
}
if (_hasHorizontalScale)
{
backgroundRect.height += _horizontalScaleHeight + _plotAreaAndHorizontalScaleSpanY;
}
g.fillRectangle(new SolidBrush(_backColor), backgroundRect);
// Draw bounds of plot area.
g.drawRectangle(new Pen(_plotAreaBoundsColor), plotAreaBounds);
// Draw vertical scale.
int baseY = (_vZeroPos == VerticalZeroPosition.BOTTOM ? 0: _plotAreaHeightOnDisplay);
double vRatio = cast(double)_plotAreaHeightOnDisplay / _verticalMaxScale;
if (_hasVerticalScale)
{
enum LINE_HEIGHT = 12f;
auto scaleList = iota(0, _verticalMaxScale, _verticalScaleSpan);
Font f = new Font("MS Gothic", LINE_HEIGHT);
int x = _originPoint.x - _verticalScaleWidth;
int index;
foreach (s; scaleList)
{
int y = cast(int)(baseY + _originPoint.y - LINE_HEIGHT / 2 + index * vRatio * _verticalScaleSpan * (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1: 1));
// Draw vertical scalse label.
g.drawText(to!string(index * _verticalScaleSpan), f, Color.black, Rect(x, y, 100, 100));
// Draw vertical scalse line.
g.drawLine(
new Pen(_plotAreaBoundsColor),
_originPoint.x - _verticalScaleLineOuterSide,
cast(int)(y + LINE_HEIGHT / 2 + (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1: 0)),
_originPoint.x + _verticalScaleLineInnerSide,
cast(int)(y + LINE_HEIGHT / 2 + (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1: 0))
);
index++;
}
}
// Draw horizontal scale.
if (_hasHorizontalScale)
{
// Draw horizontal scale label.
static if (is(T[0] == string))
{{
int i;
foreach (label; csvReader!(Tuple!T)(_csv, null))
{
int x = _originPoint.x + _plotAreaLeftPadding + i * _horizontalScaleSpan - cast(int)_horizontalScaleHeight;
int y = baseY + _originPoint.y + (_vZeroPos == VerticalZeroPosition.BOTTOM ? _plotAreaAndHorizontalScaleSpanY: -_plotAreaAndHorizontalScaleSpanY - _horizontalScaleHeight);
g.drawText(
label[0],
new Font("MS Gothic", 12f),
_plotAreaBoundsColor,
Rect(x, y, _horizontalScaleSpan, _horizontalScaleHeight)
);
i++;
}
}}
//
for (int i; i < _lastRecord - _firstRecord + 1; i++)
{
int x = _originPoint.x + _plotAreaLeftPadding + i * _horizontalScaleSpan;
int y = baseY + _originPoint.y;
// Draw horizontal scale line.
g.drawLine(
new Pen(_plotAreaBoundsColor),
x,
y - _horizonScaleLineInnerSide * (_vZeroPos == VerticalZeroPosition.BOTTOM ? 1 : -1),
x,
y + _horizonScaleLineOuterSide * (_vZeroPos == VerticalZeroPosition.BOTTOM ? 1 : -1)
);
}
}
// Draw legend.
if (_showLegend)
{
int legendLine;
foreach (i, value; csvReader!(Tuple!T)(_csv, null).header)
{
static if (is(T[0] == string))
{
if (i == 0) continue;
}
int x = plotAreaBounds.right + _plotAreaAndLegendSpanX + _plotPointSize;
int y = plotAreaBounds.y + _legendLineHeight * cast(int)legendLine;
g.drawText(to!string(value), new Font("MS Gothic", 12f), Color.black, Rect(x, y, 100, 100));
_drawPlotPoint(
g,
new Pen(_plotLineColorPalette[legendLine]),
_plotPointFormList[legendLine],
_plotPointSize, x - _plotPointSize * 2, // Center X.
y + _legendLineHeight / 2 // Center Y.
);
legendLine++;
}
}
// Draw records.
Tuple!T prevRecord;
int x1 = _originPoint.x + _plotAreaLeftPadding;
int x2;
bool isFirstRecord = true;
auto csvRange = csvReader!(Tuple!T)(_csv, null).drop(_firstRecord);
foreach (currRecord; csvRange)
{
x2 = x1 + _horizontalScaleSpan;
bool isFirstColumn = true;
foreach (col, value; currRecord)
{
static if (!(is(T[0] == string) && col == 0))
{
int y1 = cast(int)(baseY + _originPoint.y + vRatio * prevRecord[col] * (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1: 1));
int y2 = cast(int)(baseY + _originPoint.y + vRatio * value * (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1: 1));
int seriesIndex = col - (is(T[0] == string)?1:0);
if (!isFirstRecord)
{
g.drawLine(
new Pen(_plotLineColorPalette[seriesIndex]),
x1 - _horizontalScaleSpan,
y1,
x2 - _horizontalScaleSpan,
y2
);
}
_drawPlotPoint(
g,
new Pen(_plotLineColorPalette[seriesIndex]),
_plotPointFormList[seriesIndex],
_plotPointSize,
x2 - _horizontalScaleSpan, // Center X.
y2 // Center Y.
);
}
isFirstColumn = false;
}
x1 = x1 + _horizontalScaleSpan;
prevRecord = currRecord;
isFirstRecord = false;
}
}
///
Rect plotAreaBounds() const
{
return Rect(
_originPoint.x,
_originPoint.y + _plotAreaHeightOnDisplay * (_vZeroPos == VerticalZeroPosition.BOTTOM ? -1 : 1),
cast(int)((_lastRecord - _firstRecord) * _horizontalScaleSpan) + _plotAreaLeftPadding + _plotAreaRightPadding,
_plotAreaHeightOnDisplay
);
}
///
void originPoint(Point pt)
{
_originPoint = pt;
}
///
void relocate(Point pt)
{
final switch (_vZeroPos)
{
case VerticalZeroPosition.BOTTOM:
int x = _chartMargins.left + _verticalScaleWidth + pt.x;
int y = _chartMargins.top + _plotAreaHeightOnDisplay + pt.y;
originPoint = Point(x, y);
break;
case VerticalZeroPosition.TOP:
int x = _chartMargins.left + _verticalScaleWidth + pt.x;
int y = _chartMargins.top + pt.y - _plotAreaHeightOnDisplay;
originPoint = Point(x, y);
}
}
///
void verticalZeroPosition(VerticalZeroPosition vZeroPos) // setter
{
_vZeroPos = vZeroPos;
}
///
void chartMargins(ChartMargins m)
{
_chartMargins = m;
}
///
void plotPointSize(int size)
{
_plotPointSize = size;
}
///
void showLegend(bool byes)
{
_showLegend = byes;
}
///
void plotLineColorPalette(Color[] colors)
{
_plotLineColorPalette = colors;
}
/// ditto
Color[] plotLineColorPalette()
{
return _plotLineColorPalette;
}
///
void plotAreaAndLegendSpanX(int x)
{
_plotAreaAndLegendSpanX = x;
}
///
void plotAreaAndHorizontalScaleSpanY(int y)
{
_plotAreaAndHorizontalScaleSpanY = y;
}
///
void legendLineHeight(int h)
{
_legendLineHeight = h;
}
///
void legendWidth(int w)
{
_legendWidth = w;
}
///
void plotAreaRightPadding(int x)
{
_plotAreaRightPadding = x;
}
///
void plotAreaLeftPadding(int x)
{
_plotAreaLeftPadding = x;
}
///
void firstRecord(int i)
{
_firstRecord = i;
}
///
void lastRecord(int i)
{
_lastRecord = i;
}
///
void backColor(Color c)
{
_backColor = c;
}
///
void plotAreaBoundsColor(Color c)
{
_plotAreaBoundsColor = c;
}
///
void horizontalScaleSpan(int x)
{
_horizontalScaleSpan = x;
}
///
void verticalMaxScale(int m)
{
_verticalMaxScale = m;
}
///
void plotAreaHeightOnDisplay(int h)
{
_plotAreaHeightOnDisplay = h;
}
///
void hasVerticalScale(bool byes)
{
_hasVerticalScale = byes;
}
///
void hasHorizontalScale(bool byes)
{
_hasHorizontalScale = byes;
}
///
void verticalScaleSpan(int scale)
{
_verticalScaleSpan = scale;
}
///
void verticalScaleWidth(int w)
{
_verticalScaleWidth = w;
}
///
void verticalScaleLineOuterSide(int w)
{
_verticalScaleLineOuterSide = w;
}
///
void verticalScaleLineInnerSide(int w)
{
_verticalScaleLineInnerSide = w;
}
///
void horizonScaleLineInnerSide(int h)
{
_horizonScaleLineInnerSide = h;
}
///
void horizonScaleLineOuterSide(int h)
{
_horizonScaleLineOuterSide = h;
}
///
void horizontalScaleHeight(int h)
{
_horizontalScaleHeight = h;
}
///
void plotPointFormList(PlotPointForm[] forms)
{
_plotPointFormList = forms;
}
/// ditto
PlotPointForm[] plotPointFormList()
{
return _plotPointFormList;
}
private:
string _csv;
int _firstRecord;
int _lastRecord;
VerticalZeroPosition _vZeroPos;
Point _originPoint;
ChartMargins _chartMargins;
bool _showLegend;
int _legendLineHeight;
int _legendWidth;
Color _backColor;
Color _plotAreaBoundsColor;
int _plotAreaLeftPadding;
int _plotAreaRightPadding;
int _plotAreaHeightOnDisplay;
int _plotAreaAndLegendSpanX;
int _plotAreaAndHorizontalScaleSpanY;
int _plotPointSize;
Color[] _plotLineColorPalette;
PlotPointForm[] _plotPointFormList;
bool _hasHorizontalScale;
int _horizontalScaleSpan;
int _horizontalScaleHeight;
int _horizonScaleLineInnerSide;
int _horizonScaleLineOuterSide;
bool _hasVerticalScale;
int _verticalMaxScale;
int _verticalScaleSpan;
int _verticalScaleWidth;
int _verticalScaleLineOuterSide;
int _verticalScaleLineInnerSide;
///
void _drawPlotPoint(Graphics g, Pen pen, PlotPointForm form, int plotPointSize, int centerX, int centerY)
{
final switch (form)
{
case PlotPointForm.CIRCLE:
g.drawEllipse(
pen,
centerX - _plotPointSize / 2,
centerY - _plotPointSize / 2,
plotPointSize, // width
plotPointSize // height
);
break;
case PlotPointForm.RECTANGLE:
g.drawRectangle(
pen,
centerX - _plotPointSize / 2,
centerY - _plotPointSize / 2,
plotPointSize, // width
plotPointSize // height
);
break;
case PlotPointForm.CROSS:
g.drawLine(
pen,
centerX - _plotPointSize / 2,
centerY - _plotPointSize / 2,
centerX + _plotPointSize / 2 + 1,
centerY + _plotPointSize / 2 + 1
);
g.drawLine(
pen,
centerX + _plotPointSize / 2,
centerY - _plotPointSize / 2,
centerX - _plotPointSize / 2 - 1,
centerY + _plotPointSize / 2 + 1
);
break;
case PlotPointForm.TRIANGLE:
g.drawLine(
pen,
centerX,
centerY - _plotPointSize * 2 / 3,
centerX + _plotPointSize / 2,
centerY + _plotPointSize / 3
);
g.drawLine(
pen,
centerX,
centerY - _plotPointSize * 2 / 3,
centerX - _plotPointSize / 2,
centerY + _plotPointSize / 3
);
g.drawLine(
pen,
centerX - _plotPointSize / 2,
centerY + _plotPointSize / 3,
centerX + _plotPointSize / 2,
centerY + _plotPointSize / 3
);
break;
}
}
}
///
struct ChartMargins
{
int left; /// Left margin.
int top; /// Top margin.
int right; /// Right margin.
int bottom; /// Bottom margin.
///
this(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
///
string toString() const
{
string str = "[";
str ~= to!string(left) ~ " ,";
str ~= to!string(top) ~ " ,";
str ~= to!string(right) ~ " ,";
str ~= to!string(bottom) ~ "]";
return str;
}
}
///
enum VerticalZeroPosition
{
TOP,
BOTTOM,
}
///
enum PlotPointForm
{
CIRCLE,
RECTANGLE,
CROSS,
TRIANGLE,
}