Beginning of a framework for drawing charts. Works a bit already, but there are
lots of loose ends. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30501 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -12,12 +12,14 @@ MergeObject DebugAnalyzer_gui.o
|
||||
SubWindow.cpp
|
||||
SubWindowManager.cpp
|
||||
:
|
||||
DebugAnalyzer_gui_chart.o
|
||||
DebugAnalyzer_gui_main_window.o
|
||||
DebugAnalyzer_gui_table.o
|
||||
DebugAnalyzer_gui_thread_window.o
|
||||
;
|
||||
|
||||
|
||||
HaikuSubInclude chart ;
|
||||
HaikuSubInclude main_window ;
|
||||
HaikuSubInclude table ;
|
||||
HaikuSubInclude thread_window ;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/BigtimeChartAxisLegendSource.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "chart/ChartDataRange.h"
|
||||
#include "chart/StringChartLegend.h"
|
||||
|
||||
|
||||
struct decomposed_time {
|
||||
bigtime_t hours;
|
||||
int minutes;
|
||||
int seconds;
|
||||
int micros;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
decompose_time(bigtime_t time, decomposed_time& decomposed)
|
||||
{
|
||||
// microsecs
|
||||
decomposed.micros = time % 1000000;
|
||||
time /= 1000000;
|
||||
// secs
|
||||
decomposed.seconds = time % 60;
|
||||
time /= 60;
|
||||
// mins
|
||||
decomposed.minutes = time % 60;
|
||||
time /= 60;
|
||||
// hours
|
||||
decomposed.hours = time;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
BigtimeChartAxisLegendSource::GetAxisLegends(const ChartDataRange& range,
|
||||
ChartLegend** legends, double* values, int32 maxLegends)
|
||||
{
|
||||
// interpret range as time range
|
||||
bigtime_t startTime = (bigtime_t)range.min;
|
||||
bigtime_t endTime = (bigtime_t)range.max;
|
||||
printf("BigtimeChartAxisLegendSource::GetAxisLegends(): range: %lld - %lld\n", startTime, endTime);
|
||||
// TODO: Handle sub-microsecs ranges!
|
||||
if (startTime >= endTime)
|
||||
return 0;
|
||||
|
||||
bigtime_t positionFactors[4];
|
||||
positionFactors[3] = 1;
|
||||
positionFactors[2] = 1000000;
|
||||
positionFactors[1] = positionFactors[2] * 60;
|
||||
positionFactors[0] = positionFactors[1] * 60;
|
||||
|
||||
// find the main position (h, m, s, us) we want to play with
|
||||
int32 position = 0;
|
||||
bigtime_t rangeTime = endTime - startTime;
|
||||
while (rangeTime / positionFactors[position] + 1 < maxLegends / 2
|
||||
&& position < 3) {
|
||||
position++;
|
||||
}
|
||||
|
||||
// adjust the factor so that we get maxLegends / 2 to maxLegends legends
|
||||
bigtime_t baseInterval = positionFactors[position];
|
||||
bigtime_t relativeFactor = 1;
|
||||
while (rangeTime / (baseInterval * relativeFactor) >= maxLegends) {
|
||||
if (relativeFactor == 1) {
|
||||
relativeFactor = 2;
|
||||
} else if (relativeFactor == 2) {
|
||||
relativeFactor = 5;
|
||||
} else if (relativeFactor == 5) {
|
||||
baseInterval *= 10;
|
||||
relativeFactor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// generate the legends
|
||||
int32 count = 0;
|
||||
bigtime_t interval = baseInterval * relativeFactor;
|
||||
bigtime_t time = (startTime + interval - 1) / interval * interval;
|
||||
for (; time <= endTime; time += interval) {
|
||||
decomposed_time decomposed;
|
||||
decompose_time(time, decomposed);
|
||||
char buffer[128];
|
||||
snprintf(buffer, sizeof(buffer), "%02lld:%02d:%02d.%06d",
|
||||
decomposed.hours, decomposed.minutes, decomposed.seconds,
|
||||
decomposed.micros);
|
||||
// TODO: Drop superfluous micro seconds digits, or even microseconds and seconds
|
||||
// completely.
|
||||
|
||||
StringChartLegend* legend = new(std::nothrow) StringChartLegend(buffer,
|
||||
1);
|
||||
if (legend == 0)
|
||||
return count;
|
||||
|
||||
legends[count] = legend;
|
||||
values[count++] = (double)time;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef BIGTIME_CHART_AXIS_LEGEND_SOURCE_H
|
||||
#define BIGTIME_CHART_AXIS_LEGEND_SOURCE_H
|
||||
|
||||
#include "chart/ChartAxisLegendSource.h"
|
||||
|
||||
|
||||
class BigtimeChartAxisLegendSource : public ChartAxisLegendSource {
|
||||
public:
|
||||
virtual int32 GetAxisLegends(const ChartDataRange& range,
|
||||
ChartLegend** legends, double* values,
|
||||
int32 maxLegends);
|
||||
};
|
||||
|
||||
|
||||
#endif // BIGTIME_CHART_AXIS_LEGEND_SOURCE_H
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/Chart.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <ControlLook.h>
|
||||
#include <Region.h>
|
||||
|
||||
#include "chart/ChartAxis.h"
|
||||
#include "chart/ChartDataSource.h"
|
||||
#include "chart/ChartRenderer.h"
|
||||
|
||||
|
||||
// #pragma mark - Chart::AxisInfo
|
||||
|
||||
|
||||
Chart::AxisInfo::AxisInfo()
|
||||
:
|
||||
axis(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::AxisInfo::SetFrame(float left, float top, float right, float bottom)
|
||||
{
|
||||
frame.Set(left, top, right, bottom);
|
||||
if (axis != NULL)
|
||||
axis->SetFrame(frame);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::AxisInfo::SetRange(const ChartDataRange& range)
|
||||
{
|
||||
if (axis != NULL)
|
||||
axis->SetRange(range);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::AxisInfo::Render(BView* view, const BRect& updateRect)
|
||||
{
|
||||
if (axis != NULL)
|
||||
axis->Render(view, updateRect);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Chart
|
||||
|
||||
|
||||
Chart::Chart(ChartRenderer* renderer, const char* name)
|
||||
:
|
||||
BView(name, B_FRAME_EVENTS | B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
|
||||
fRenderer(renderer)
|
||||
{
|
||||
SetViewColor(B_TRANSPARENT_32_BIT);
|
||||
|
||||
// fRenderer->SetFrame(Bounds());
|
||||
}
|
||||
|
||||
|
||||
Chart::~Chart()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Chart::AddDataSource(ChartDataSource* dataSource, int32 index,
|
||||
ChartRendererDataSourceConfig* config)
|
||||
{
|
||||
printf("Chart::AddDataSource(%p)\n", dataSource);
|
||||
if (dataSource == NULL)
|
||||
return false;
|
||||
|
||||
if (index < 0 || index > fDataSources.CountItems())
|
||||
index = fDataSources.CountItems();
|
||||
|
||||
if (!fDataSources.AddItem(dataSource, index))
|
||||
return false;
|
||||
|
||||
if (!fRenderer->AddDataSource(dataSource, index, config)) {
|
||||
fDataSources.RemoveItemAt(index);
|
||||
return false;
|
||||
}
|
||||
|
||||
_UpdateDomainAndRange();
|
||||
|
||||
InvalidateLayout();
|
||||
Invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Chart::AddDataSource(ChartDataSource* dataSource,
|
||||
ChartRendererDataSourceConfig* config)
|
||||
{
|
||||
return AddDataSource(dataSource, -1, config);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Chart::RemoveDataSource(ChartDataSource* dataSource)
|
||||
{
|
||||
if (dataSource == NULL)
|
||||
return false;
|
||||
|
||||
return RemoveDataSource(fDataSources.IndexOf(dataSource));
|
||||
}
|
||||
|
||||
|
||||
ChartDataSource*
|
||||
Chart::RemoveDataSource(int32 index)
|
||||
{
|
||||
printf("Chart::RemoveDataSource(%ld)\n", index);
|
||||
if (index < 0 || index >= fDataSources.CountItems())
|
||||
return NULL;
|
||||
|
||||
ChartDataSource* dataSource = fDataSources.RemoveItemAt(index);
|
||||
|
||||
fRenderer->RemoveDataSource(dataSource);
|
||||
|
||||
_UpdateDomainAndRange();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::RemoveAllDataSources()
|
||||
{
|
||||
printf("Chart::RemoveAllDataSources()\n");
|
||||
int32 count = fDataSources.CountItems();
|
||||
for (int32 i = count - 1; i >= 0; i--)
|
||||
fRenderer->RemoveDataSource(fDataSources.ItemAt(i));
|
||||
|
||||
fDataSources.MakeEmpty();
|
||||
|
||||
_UpdateDomainAndRange();
|
||||
|
||||
InvalidateLayout();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::SetAxis(ChartAxisLocation location, ChartAxis* axis)
|
||||
{
|
||||
switch (location) {
|
||||
case CHART_AXIS_LEFT:
|
||||
fLeftAxis.axis = axis;
|
||||
break;
|
||||
case CHART_AXIS_TOP:
|
||||
fTopAxis.axis = axis;
|
||||
break;
|
||||
case CHART_AXIS_RIGHT:
|
||||
fRightAxis.axis = axis;
|
||||
break;
|
||||
case CHART_AXIS_BOTTOM:
|
||||
fBottomAxis.axis = axis;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
axis->SetLocation(location);
|
||||
|
||||
InvalidateLayout();
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
ChartDataRange
|
||||
Chart::Domain() const
|
||||
{
|
||||
return fDataSource != NULL ? fDataSource->Domain() : ChartDataRange();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::SetDisplayDomain(const ChartDataRange& domain)
|
||||
{
|
||||
fRenderer->SetDomain(domain);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::SetDisplayDomain(const ChartDataRange& range)
|
||||
{
|
||||
fRenderer->SetRange(range);
|
||||
Invalidate();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
Chart::FrameResized(float newWidth, float newHeight)
|
||||
{
|
||||
//printf("Chart::FrameResized(%f, %f)\n", newWidth, newHeight);
|
||||
// fRenderer->SetFrame(Bounds());
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::Draw(BRect updateRect)
|
||||
{
|
||||
printf("Chart::Draw((%f, %f) - (%f, %f))\n", updateRect.left, updateRect.top, updateRect.right, updateRect.bottom);
|
||||
rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
|
||||
rgb_color color;
|
||||
|
||||
// clear the axes background
|
||||
if (fLeftAxis.axis != NULL || fTopAxis.axis != NULL
|
||||
|| fRightAxis.axis != NULL || fBottomAxis.axis != NULL) {
|
||||
SetHighColor(background);
|
||||
BRegion clippingRegion(Bounds());
|
||||
clippingRegion.Exclude(fChartFrame);
|
||||
ConstrainClippingRegion(&clippingRegion);
|
||||
FillRect(Bounds());
|
||||
ConstrainClippingRegion(NULL);
|
||||
}
|
||||
|
||||
// render the axes
|
||||
fLeftAxis.Render(this, updateRect);
|
||||
fTopAxis.Render(this, updateRect);
|
||||
fRightAxis.Render(this, updateRect);
|
||||
fBottomAxis.Render(this, updateRect);
|
||||
|
||||
// draw the frame around the chart area and clear the background
|
||||
BRect chartFrame(fChartFrame);
|
||||
be_control_look->DrawBorder(this, chartFrame, updateRect, background,
|
||||
B_PLAIN_BORDER);
|
||||
// DrawBorder() insets the supplied rect
|
||||
SetHighColor(color.set_to(255, 255, 255, 255));
|
||||
FillRect(chartFrame);
|
||||
|
||||
// render the chart
|
||||
BRegion clippingRegion(chartFrame);
|
||||
ConstrainClippingRegion(&clippingRegion);
|
||||
fRenderer->Render(this, updateRect);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
BSize
|
||||
Chart::MinSize()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
Chart::MaxSize()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
Chart::PreferredSize()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void
|
||||
Chart::DoLayout()
|
||||
{
|
||||
// get size in pixels
|
||||
BSize size = Bounds().Size();
|
||||
printf("Chart::DoLayout(%f, %f)\n", size.width, size.height);
|
||||
int32 width = size.IntegerWidth() + 1;
|
||||
int32 height = size.IntegerHeight() + 1;
|
||||
|
||||
// compute the axis insets
|
||||
int32 left = 0;
|
||||
int32 right = 0;
|
||||
int32 top = 0;
|
||||
int32 bottom = 0;
|
||||
|
||||
if (fLeftAxis.axis != NULL)
|
||||
left = fLeftAxis.axis->PreferredSize(this).IntegerWidth() + 1;
|
||||
if (fRightAxis.axis != NULL)
|
||||
right = fRightAxis.axis->PreferredSize(this).IntegerWidth() + 1;
|
||||
if (fTopAxis.axis != NULL)
|
||||
top = fTopAxis.axis->PreferredSize(this).IntegerHeight() + 1;
|
||||
if (fBottomAxis.axis != NULL)
|
||||
bottom = fBottomAxis.axis->PreferredSize(this).IntegerHeight() + 1;
|
||||
|
||||
fChartFrame = BRect(left, top, width - right - 1, height - bottom - 1);
|
||||
fRenderer->SetFrame(fChartFrame.InsetByCopy(1, 1));
|
||||
printf(" fChartFrame: (%f, %f) - (%f, %f)\n", fChartFrame.left, fChartFrame.top, fChartFrame.right, fChartFrame.bottom);
|
||||
|
||||
fLeftAxis.SetFrame(0, fChartFrame.top, fChartFrame.left - 1,
|
||||
fChartFrame.bottom);
|
||||
fRightAxis.SetFrame(fChartFrame.right + 1, fChartFrame.top, width - 1,
|
||||
fChartFrame.bottom);
|
||||
fTopAxis.SetFrame(fChartFrame.left, 0, fChartFrame.right,
|
||||
fChartFrame.top - 1);
|
||||
fBottomAxis.SetFrame(fChartFrame.left, fChartFrame.bottom + 1,
|
||||
fChartFrame.right, height - 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Chart::_UpdateDomainAndRange()
|
||||
{
|
||||
if (fDataSources.IsEmpty()) {
|
||||
fDomain = ChartDataRange();
|
||||
fRange = ChartDataRange();
|
||||
return;
|
||||
} else {
|
||||
ChartDataSource* firstSource = fDataSources.ItemAt(0);
|
||||
fDomain = firstSource->Domain();
|
||||
fRange = firstSource->Range();
|
||||
|
||||
for (int32 i = 1; ChartDataSource* source = fDataSources.ItemAt(i);
|
||||
i++) {
|
||||
fDomain.Extend(source->Domain());
|
||||
fRange.Extend(source->Range());
|
||||
}
|
||||
}
|
||||
|
||||
fRenderer->SetDomain(fDomain);
|
||||
fRenderer->SetRange(fRange);
|
||||
|
||||
fLeftAxis.SetRange(fRange);
|
||||
fTopAxis.SetRange(fDomain);
|
||||
fRightAxis.SetRange(fRange);
|
||||
fBottomAxis.SetRange(fDomain);
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_H
|
||||
#define CHART_H
|
||||
|
||||
#include <View.h>
|
||||
|
||||
#include <ObjectList.h>
|
||||
|
||||
#include "chart/ChartDataRange.h"
|
||||
#include "chart/ChartDefs.h"
|
||||
|
||||
|
||||
class ChartAxis;
|
||||
class ChartDataSource;
|
||||
class ChartRenderer;
|
||||
class ChartRendererDataSourceConfig;
|
||||
|
||||
|
||||
class Chart : public BView {
|
||||
public:
|
||||
Chart(ChartRenderer* renderer,
|
||||
const char* name = NULL);
|
||||
virtual ~Chart();
|
||||
|
||||
bool AddDataSource(ChartDataSource* dataSource,
|
||||
int32 index,
|
||||
ChartRendererDataSourceConfig* config
|
||||
= NULL);
|
||||
bool AddDataSource(ChartDataSource* dataSource,
|
||||
ChartRendererDataSourceConfig* config
|
||||
= NULL);
|
||||
bool RemoveDataSource(
|
||||
ChartDataSource* dataSource);
|
||||
ChartDataSource* RemoveDataSource(int32 index);
|
||||
void RemoveAllDataSources();
|
||||
|
||||
void SetAxis(ChartAxisLocation location,
|
||||
ChartAxis* axis);
|
||||
|
||||
#if 0
|
||||
ChartDataRange Domain() const;
|
||||
|
||||
void SetDisplayDomain(const ChartDataRange& domain);
|
||||
void SetDisplayRange(const ChartDataRange& range);
|
||||
#endif
|
||||
|
||||
virtual void FrameResized(float newWidth, float newHeight);
|
||||
virtual void Draw(BRect updateRect);
|
||||
|
||||
#if 0
|
||||
virtual BSize MinSize();
|
||||
virtual BSize MaxSize();
|
||||
virtual BSize PreferredSize();
|
||||
#endif
|
||||
|
||||
virtual void DoLayout();
|
||||
|
||||
private:
|
||||
typedef BObjectList<ChartDataSource> DataSourceList;
|
||||
|
||||
struct AxisInfo {
|
||||
ChartAxis* axis;
|
||||
BRect frame;
|
||||
|
||||
AxisInfo();
|
||||
void SetFrame(float left, float top, float right,
|
||||
float bottom);
|
||||
void SetRange(const ChartDataRange& range);
|
||||
void Render(BView* view, const BRect& updateRect);
|
||||
};
|
||||
|
||||
private:
|
||||
void _UpdateDomainAndRange();
|
||||
|
||||
private:
|
||||
ChartRenderer* fRenderer;
|
||||
DataSourceList fDataSources;
|
||||
AxisInfo fLeftAxis;
|
||||
AxisInfo fTopAxis;
|
||||
AxisInfo fRightAxis;
|
||||
AxisInfo fBottomAxis;
|
||||
ChartDataRange fDomain;
|
||||
ChartDataRange fRange;
|
||||
#if 0
|
||||
ChartDataRange fDisplayDomain;
|
||||
ChartDataRange fDisplayRange;
|
||||
#endif
|
||||
BRect fChartFrame;
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_H
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/ChartAxis.h"
|
||||
|
||||
|
||||
// #pragma mark - ChartAxis
|
||||
|
||||
|
||||
ChartAxis::~ChartAxis()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_AXIS_H
|
||||
#define CHART_AXIS_H
|
||||
|
||||
#include <Rect.h>
|
||||
|
||||
#include "chart/ChartDefs.h"
|
||||
|
||||
|
||||
class BView;
|
||||
class ChartDataRange;
|
||||
|
||||
|
||||
class ChartAxis {
|
||||
public:
|
||||
virtual ~ChartAxis();
|
||||
|
||||
virtual void SetLocation(ChartAxisLocation location) = 0;
|
||||
virtual void SetRange(const ChartDataRange& range) = 0;
|
||||
virtual void SetFrame(BRect frame) = 0;
|
||||
virtual BSize PreferredSize(BView* view) = 0;
|
||||
virtual void Render(BView* view, BRect updateRect) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_AXIS_H
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/ChartAxisLegendSource.h"
|
||||
|
||||
|
||||
ChartAxisLegendSource::~ChartAxisLegendSource()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_AXIS_LEGEND_SOURCE_H
|
||||
#define CHART_AXIS_LEGEND_SOURCE_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
|
||||
class ChartDataRange;
|
||||
class ChartLegend;
|
||||
|
||||
|
||||
class ChartAxisLegendSource {
|
||||
public:
|
||||
virtual ~ChartAxisLegendSource();
|
||||
|
||||
virtual int32 GetAxisLegends(const ChartDataRange& range,
|
||||
ChartLegend** legends, double* values,
|
||||
int32 maxLegends) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_AXIS_LEGEND_SOURCE_H
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_DATA_RANGE_H
|
||||
#define CHART_DATA_RANGE_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
class ChartDataRange {
|
||||
public:
|
||||
double min;
|
||||
double max;
|
||||
|
||||
ChartDataRange()
|
||||
:
|
||||
min(0),
|
||||
max(0)
|
||||
{
|
||||
}
|
||||
|
||||
ChartDataRange(double min, double max)
|
||||
:
|
||||
min(min),
|
||||
max(max)
|
||||
{
|
||||
}
|
||||
|
||||
ChartDataRange(const ChartDataRange& other)
|
||||
:
|
||||
min(other.min),
|
||||
max(other.max)
|
||||
{
|
||||
}
|
||||
|
||||
ChartDataRange& Extend(const ChartDataRange& other)
|
||||
{
|
||||
min = std::min(min, other.min);
|
||||
max = std::max(max, other.max);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ChartDataRange& operator=(const ChartDataRange& other)
|
||||
{
|
||||
min = other.min;
|
||||
max = other.max;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ChartDataRange& other) const
|
||||
{
|
||||
return min == other.min && max == other.max;
|
||||
}
|
||||
|
||||
bool operator!=(const ChartDataRange& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_DATA_RANGE_H
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/ChartDataSource.h"
|
||||
|
||||
|
||||
ChartDataSource::~ChartDataSource()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_DATA_SOURCE_H
|
||||
#define CHART_DATA_SOURCE_H
|
||||
|
||||
#include <SupportDefs.h>
|
||||
|
||||
#include "chart/ChartDataRange.h"
|
||||
|
||||
|
||||
class ChartDataSource {
|
||||
public:
|
||||
virtual ~ChartDataSource();
|
||||
|
||||
virtual ChartDataRange Domain() const = 0;
|
||||
virtual ChartDataRange Range() const = 0;
|
||||
|
||||
virtual void GetSamples(double start, double end,
|
||||
double* samples, int32 count) = 0;
|
||||
};
|
||||
|
||||
#endif // CHART_DATA_SOURCE_H
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_DEFS_H
|
||||
#define CHART_DEFS_H
|
||||
|
||||
|
||||
enum ChartAxisLocation {
|
||||
CHART_AXIS_LEFT,
|
||||
CHART_AXIS_TOP,
|
||||
CHART_AXIS_RIGHT,
|
||||
CHART_AXIS_BOTTOM
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_DEFS_H
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/ChartLegend.h"
|
||||
|
||||
|
||||
// #pragma mark - ChartLegend
|
||||
|
||||
|
||||
ChartLegend::ChartLegend(int32 level)
|
||||
:
|
||||
fLevel(level)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
ChartLegend::~ChartLegend()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ChartLegendRenderer
|
||||
|
||||
|
||||
ChartLegendRenderer::~ChartLegendRenderer()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_LEGEND_H
|
||||
#define CHART_LEGEND_H
|
||||
|
||||
#include <Point.h>
|
||||
#include <Size.h>
|
||||
|
||||
|
||||
class BView;
|
||||
class ChartDataRange;
|
||||
|
||||
|
||||
class ChartLegend {
|
||||
public:
|
||||
ChartLegend(int32 level = 0);
|
||||
// A lower level means more likely to be
|
||||
// shown. <= 0 is mandatory.
|
||||
virtual ~ChartLegend();
|
||||
|
||||
int32 Level() { return fLevel; }
|
||||
|
||||
private:
|
||||
int32 fLevel;
|
||||
};
|
||||
|
||||
|
||||
class ChartLegendRenderer {
|
||||
public:
|
||||
virtual ~ChartLegendRenderer();
|
||||
|
||||
virtual void GetMinimumLegendSpacing(BView* view,
|
||||
float* horizontal, float* vertical) = 0;
|
||||
virtual BSize MaximumLegendSize(BView* view) = 0;
|
||||
|
||||
virtual BSize LegendSize(ChartLegend* legend,
|
||||
BView* view) = 0;
|
||||
virtual void RenderLegend(ChartLegend* legend, BView* view,
|
||||
BPoint point) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_LEGEND_H
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/ChartRenderer.h"
|
||||
|
||||
|
||||
// #pragma mark - ChartRenderer
|
||||
|
||||
|
||||
ChartRenderer::~ChartRenderer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - ChartRendererDataSourceConfig
|
||||
|
||||
|
||||
ChartRendererDataSourceConfig::~ChartRendererDataSourceConfig()
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CHART_RENDERER_H
|
||||
#define CHART_RENDERER_H
|
||||
|
||||
#include <Rect.h>
|
||||
|
||||
|
||||
class BView;
|
||||
class ChartDataRange;
|
||||
class ChartDataSource;
|
||||
|
||||
|
||||
class ChartRendererDataSourceConfig {
|
||||
public:
|
||||
virtual ~ChartRendererDataSourceConfig();
|
||||
};
|
||||
|
||||
|
||||
class ChartRenderer {
|
||||
public:
|
||||
virtual ~ChartRenderer();
|
||||
|
||||
virtual bool AddDataSource(
|
||||
ChartDataSource* dataSource,
|
||||
int32 index,
|
||||
ChartRendererDataSourceConfig* config) = 0;
|
||||
virtual void RemoveDataSource(
|
||||
ChartDataSource* dataSource) = 0;
|
||||
|
||||
virtual void SetFrame(const BRect& frame) = 0;
|
||||
virtual void SetDomain(const ChartDataRange& domain) = 0;
|
||||
virtual void SetRange(const ChartDataRange& range) = 0;
|
||||
|
||||
virtual void Render(BView* view, BRect updateRect) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif // CHART_RENDERER_H
|
||||
@@ -0,0 +1,20 @@
|
||||
SubDir HAIKU_TOP src apps debuganalyzer gui chart ;
|
||||
|
||||
UsePrivateHeaders shared ;
|
||||
|
||||
UseHeaders $(HAIKU_DEBUG_ANALYZER_HEADERS) ;
|
||||
|
||||
|
||||
MergeObject DebugAnalyzer_gui_chart.o
|
||||
:
|
||||
BigtimeChartAxisLegendSource.cpp
|
||||
Chart.cpp
|
||||
ChartAxis.cpp
|
||||
ChartAxisLegendSource.cpp
|
||||
ChartDataSource.cpp
|
||||
ChartLegend.cpp
|
||||
ChartRenderer.cpp
|
||||
LegendChartAxis.cpp
|
||||
LineChartRenderer.cpp
|
||||
StringChartLegend.cpp
|
||||
;
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/LegendChartAxis.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
|
||||
#include <Font.h>
|
||||
#include <View.h>
|
||||
|
||||
#include "chart/ChartLegend.h"
|
||||
#include "chart/ChartAxisLegendSource.h"
|
||||
|
||||
|
||||
static const int32 kChartRulerDistance = 2;
|
||||
static const int32 kRulerSize = 3;
|
||||
static const int32 kRulerMarkSize = 3;
|
||||
static const int32 kRulerLegendDistance = 2;
|
||||
static const int32 kChartLegendDistance
|
||||
= kChartRulerDistance + kRulerSize + kRulerMarkSize + kRulerLegendDistance;
|
||||
|
||||
|
||||
|
||||
struct LegendChartAxis::LegendInfo {
|
||||
ChartLegend* legend;
|
||||
double value;
|
||||
BSize size;
|
||||
|
||||
LegendInfo(ChartLegend* legend, double value, BSize size)
|
||||
:
|
||||
legend(legend),
|
||||
value(value),
|
||||
size(size)
|
||||
{
|
||||
}
|
||||
|
||||
~LegendInfo()
|
||||
{
|
||||
delete legend;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
float
|
||||
LegendChartAxis::_LegendPosition(double value, float legendSize,
|
||||
float totalSize, double scale)
|
||||
{
|
||||
float position = (value - fRange.min) * scale - legendSize / 2;
|
||||
if (position + legendSize > totalSize)
|
||||
position = totalSize - legendSize;
|
||||
if (position < 0)
|
||||
position = 0;
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::_FilterLegends(int32 totalSize, int32 spacing,
|
||||
float BSize::* sizeField)
|
||||
{
|
||||
printf("LegendChartAxis::_FilterLegends(%ld, %ld)\n", totalSize, spacing);
|
||||
// compute the min/max legend levels
|
||||
int32 legendCount = fLegends.CountItems();
|
||||
int32 minLevel = INT_MAX;
|
||||
int32 maxLevel = 0;
|
||||
|
||||
for (int32 i = 0; i < legendCount; i++) {
|
||||
LegendInfo* info = fLegends.ItemAt(i);
|
||||
int32 level = info->legend->Level();
|
||||
if (level < minLevel)
|
||||
minLevel = level;
|
||||
if (level > maxLevel)
|
||||
maxLevel = level;
|
||||
}
|
||||
|
||||
if (maxLevel <= 0)
|
||||
return;
|
||||
|
||||
double rangeSize = fRange.max - fRange.min;
|
||||
if (rangeSize == 0)
|
||||
rangeSize = 1.0;
|
||||
double scale = (double)totalSize / rangeSize;
|
||||
|
||||
// Filter out all higher level legends colliding with lower level or
|
||||
// preceeding same-level legends. We iterate backwards from the lower to
|
||||
// the higher levels
|
||||
for (int32 level = std::max(minLevel, 0L); level <= maxLevel;) {
|
||||
printf(" level: %ld\n", level);
|
||||
legendCount = fLegends.CountItems();
|
||||
|
||||
// get the first legend position/end
|
||||
LegendInfo* info = fLegends.ItemAt(0);
|
||||
float position = _LegendPosition(info->value, info->size.*sizeField,
|
||||
(float)totalSize, scale);;
|
||||
|
||||
int32 previousEnd = (int32)ceilf(position + info->size.*sizeField);
|
||||
int32 previousLevel = info->legend->Level();
|
||||
printf(" first legend: pos: %f, size: %f\n", position, info->size.*sizeField);
|
||||
|
||||
for (int32 i = 1; (info = fLegends.ItemAt(i)) != NULL; i++) {
|
||||
float position = _LegendPosition(info->value, info->size.*sizeField,
|
||||
(float)totalSize, scale);;
|
||||
printf(" legend %ld: pos: %f, size: %f\n", i, position, info->size.*sizeField);
|
||||
|
||||
if (position - spacing < previousEnd
|
||||
&& (previousLevel <= level
|
||||
|| info->legend->Level() <= level)
|
||||
&& std::max(previousLevel, info->legend->Level()) > 0) {
|
||||
// The item intersects with the previous one -- remove the
|
||||
// one at the higher level.
|
||||
if (info->legend->Level() >= previousLevel) {
|
||||
// This item is at the higher level -- remove it.
|
||||
delete fLegends.RemoveItemAt(i);
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The previous item is at the higher level -- remove it.
|
||||
delete fLegends.RemoveItemAt(i - 1);
|
||||
i--;
|
||||
}
|
||||
|
||||
if (i == 0 && position < 0)
|
||||
position = 0;
|
||||
previousEnd = (int32)ceilf(position + info->size.*sizeField);
|
||||
previousLevel = info->legend->Level();
|
||||
}
|
||||
|
||||
// repeat with the level as long as we've removed something
|
||||
if (legendCount == fLegends.CountItems())
|
||||
level++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LegendChartAxis::LegendChartAxis(ChartAxisLegendSource* legendSource,
|
||||
ChartLegendRenderer* legendRenderer)
|
||||
:
|
||||
fLegendSource(legendSource),
|
||||
fLegendRenderer(legendRenderer),
|
||||
fLocation(CHART_AXIS_BOTTOM),
|
||||
fRange(),
|
||||
fFrame(),
|
||||
fLegends(20, true),
|
||||
fHorizontalSpacing(20),
|
||||
fVerticalSpacing(10),
|
||||
fLayoutValid(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LegendChartAxis::~LegendChartAxis()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::SetLocation(ChartAxisLocation location)
|
||||
{
|
||||
if (location != fLocation) {
|
||||
fLocation = location;
|
||||
_InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::SetRange(const ChartDataRange& range)
|
||||
{
|
||||
if (range != fRange) {
|
||||
fRange = range;
|
||||
_InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::SetFrame(BRect frame)
|
||||
{
|
||||
printf("LegendChartAxis::SetFrame((%f, %f) - (%f, %f))\n", frame.left, frame.top, frame.right, frame.bottom);
|
||||
if (frame != fFrame) {
|
||||
fFrame = frame;
|
||||
_InvalidateLayout();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
LegendChartAxis::PreferredSize(BView* view)
|
||||
{
|
||||
// TODO: Implement for real!
|
||||
BSize size = fLegendRenderer->MaximumLegendSize(view);
|
||||
if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT) {
|
||||
size.width += kChartLegendDistance;
|
||||
size.height = std::max(size.height * 4, 100.0f);
|
||||
} else {
|
||||
size.width = std::max(size.width * 4, 100.0f);
|
||||
size.height += kChartLegendDistance;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::Render(BView* view, BRect updateRect)
|
||||
{
|
||||
printf("LegendChartAxis::Render()\n");
|
||||
if (!_ValidateLayout(view))
|
||||
return;
|
||||
|
||||
if (fLocation == CHART_AXIS_BOTTOM) {
|
||||
printf(" rendering...\n");
|
||||
float totalSize = floorf(fFrame.Width()) + 1;
|
||||
double rangeSize = fRange.max - fRange.min;
|
||||
if (rangeSize == 0)
|
||||
rangeSize = 1.0;
|
||||
double scale = (double)totalSize / rangeSize;
|
||||
float BSize::* sizeField = &BSize::width;
|
||||
|
||||
// draw the ruler
|
||||
float rulerLeft = fFrame.left;
|
||||
float rulerTop = fFrame.top + kChartRulerDistance;
|
||||
float rulerRight = fFrame.right;
|
||||
float rulerBottom = rulerTop + kRulerSize;
|
||||
printf(" ruler: (%f, %f) - (%f, %f)\n", rulerLeft, rulerTop, rulerRight, rulerBottom);
|
||||
|
||||
rgb_color black = { 0, 0, 0, 255 };
|
||||
view->BeginLineArray(3 + fLegends.CountItems());
|
||||
view->AddLine(BPoint(rulerLeft, rulerTop),
|
||||
BPoint(rulerLeft, rulerBottom), black);
|
||||
view->AddLine(BPoint(rulerLeft, rulerBottom),
|
||||
BPoint(rulerRight, rulerBottom), black);
|
||||
view->AddLine(BPoint(rulerRight, rulerBottom),
|
||||
BPoint(rulerRight, rulerTop), black);
|
||||
|
||||
// marks
|
||||
for (int32 i = 0; LegendInfo* info = fLegends.ItemAt(i); i++) {
|
||||
float position = (info->value - fRange.min) * scale;
|
||||
position += rulerLeft;
|
||||
printf(" drawing mark at (%f, %f)\n", position, rulerBottom);
|
||||
view->AddLine(BPoint(position, rulerBottom),
|
||||
BPoint(position, rulerBottom + kRulerMarkSize), black);
|
||||
}
|
||||
view->EndLineArray();
|
||||
|
||||
// draw the legends
|
||||
float legendTop = rulerBottom + kRulerMarkSize + kRulerLegendDistance;
|
||||
|
||||
for (int32 i = 0; LegendInfo* info = fLegends.ItemAt(i); i++) {
|
||||
float position = _LegendPosition(info->value, info->size.*sizeField,
|
||||
(float)totalSize, scale);;
|
||||
printf(" legend %ld: position: (%f, %f), size: (%f, %f)\n", i, position, legendTop, info->size.width, info->size.height);
|
||||
|
||||
fLegendRenderer->RenderLegend(info->legend, view,
|
||||
BPoint(rulerLeft + position, legendTop));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LegendChartAxis::_InvalidateLayout()
|
||||
{
|
||||
fLayoutValid = false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LegendChartAxis::_ValidateLayout(BView* view)
|
||||
{
|
||||
if (fLayoutValid)
|
||||
return true;
|
||||
printf("LegendChartAxis::_ValidateLayout()\n");
|
||||
|
||||
fLegends.MakeEmpty();
|
||||
|
||||
int32 width = fFrame.IntegerWidth() + 1;
|
||||
int32 height = fFrame.IntegerHeight() + 1;
|
||||
printf(" width: %ld, height: %ld\n", width, height);
|
||||
|
||||
fLegendRenderer->GetMinimumLegendSpacing(view, &fHorizontalSpacing,
|
||||
&fVerticalSpacing);
|
||||
|
||||
// estimate the maximum legend count we might need
|
||||
int32 maxLegends;
|
||||
if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT)
|
||||
maxLegends = height / (10 + fVerticalSpacing);
|
||||
else
|
||||
maxLegends = width / (20 + fHorizontalSpacing);
|
||||
|
||||
printf(" max %ld legends\n", maxLegends);
|
||||
if (maxLegends == 0)
|
||||
{
|
||||
printf(" too small for any legends!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the legends
|
||||
ChartLegend* legends[maxLegends];
|
||||
double values[maxLegends];
|
||||
|
||||
int32 legendCount = fLegendSource->GetAxisLegends(fRange, legends, values,
|
||||
maxLegends);
|
||||
printf(" got %ld legends\n", legendCount);
|
||||
if (legendCount == 0)
|
||||
{
|
||||
printf(" got no legends from source!\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf(" range: %f - %f\n", fRange.min, fRange.max);
|
||||
// create legend infos
|
||||
for (int32 i = 0; i < legendCount; i++) {
|
||||
ChartLegend* legend = legends[i];
|
||||
BSize size = fLegendRenderer->LegendSize(legend, view);
|
||||
LegendInfo* info = new(std::nothrow) LegendInfo(legend, values[i],
|
||||
size);
|
||||
printf(" legend %ld: size: (%f, %f), value: %f\n", i, size.width, size.height, info->value);
|
||||
if (info == NULL || !fLegends.AddItem(info)) {
|
||||
// TODO: Report error!
|
||||
delete info;
|
||||
for (int32 k = i; k < legendCount; k++)
|
||||
delete legends[k];
|
||||
printf(" failed to create legend info!\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fLocation == CHART_AXIS_LEFT || fLocation == CHART_AXIS_RIGHT)
|
||||
_FilterLegends(height, fVerticalSpacing, &BSize::height);
|
||||
else
|
||||
_FilterLegends(width, fHorizontalSpacing, &BSize::width);
|
||||
|
||||
fLayoutValid = true;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef LEGEND_CHART_AXIS_H
|
||||
#define LEGEND_CHART_AXIS_H
|
||||
|
||||
#include <ObjectList.h>
|
||||
|
||||
#include "chart/ChartAxis.h"
|
||||
#include "chart/ChartDataRange.h"
|
||||
|
||||
|
||||
class ChartAxisLegendSource;
|
||||
class ChartLegendRenderer;
|
||||
|
||||
|
||||
class LegendChartAxis : public ChartAxis {
|
||||
public:
|
||||
LegendChartAxis(
|
||||
ChartAxisLegendSource* legendSource,
|
||||
ChartLegendRenderer* legendRenderer);
|
||||
virtual ~LegendChartAxis();
|
||||
|
||||
virtual void SetLocation(ChartAxisLocation location);
|
||||
virtual void SetRange(const ChartDataRange& range);
|
||||
virtual void SetFrame(BRect frame);
|
||||
virtual BSize PreferredSize(BView* view);
|
||||
virtual void Render(BView* view, BRect updateRect);
|
||||
|
||||
private:
|
||||
struct LegendInfo;
|
||||
typedef BObjectList<LegendInfo> LegendList;
|
||||
private:
|
||||
void _InvalidateLayout();
|
||||
bool _ValidateLayout(BView* view);
|
||||
inline float _LegendPosition(double value, float legendSize,
|
||||
float totalSize, double scale);
|
||||
inline void _FilterLegends(int32 totalSize, int32 spacing,
|
||||
float BSize::* sizeField);
|
||||
|
||||
private:
|
||||
ChartAxisLegendSource* fLegendSource;
|
||||
ChartLegendRenderer* fLegendRenderer;
|
||||
ChartAxisLocation fLocation;
|
||||
ChartDataRange fRange;
|
||||
BRect fFrame;
|
||||
LegendList fLegends;
|
||||
float fHorizontalSpacing;
|
||||
float fVerticalSpacing;
|
||||
bool fLayoutValid;
|
||||
};
|
||||
|
||||
|
||||
#endif // LEGEND_CHART_AXIS_H
|
||||
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/LineChartRenderer.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <View.h>
|
||||
|
||||
#include "chart/ChartDataSource.h"
|
||||
|
||||
|
||||
// #pragma mark - DataSourceInfo
|
||||
|
||||
|
||||
struct LineChartRenderer::DataSourceInfo {
|
||||
public:
|
||||
ChartDataSource* source;
|
||||
double* samples;
|
||||
int32 sampleCount;
|
||||
bool samplesValid;
|
||||
LineChartRendererDataSourceConfig config;
|
||||
|
||||
public:
|
||||
DataSourceInfo(ChartDataSource* source,
|
||||
LineChartRendererDataSourceConfig* config);
|
||||
~DataSourceInfo();
|
||||
|
||||
bool UpdateSamples(const ChartDataRange& domain,
|
||||
int32 sampleCount);
|
||||
};
|
||||
|
||||
|
||||
LineChartRenderer::DataSourceInfo::DataSourceInfo(ChartDataSource* source,
|
||||
LineChartRendererDataSourceConfig* config)
|
||||
:
|
||||
source(source),
|
||||
samples(NULL),
|
||||
sampleCount(0),
|
||||
samplesValid(false)
|
||||
{
|
||||
if (config != NULL)
|
||||
this->config = *config;
|
||||
}
|
||||
|
||||
|
||||
LineChartRenderer::DataSourceInfo::~DataSourceInfo()
|
||||
{
|
||||
delete[] samples;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LineChartRenderer::DataSourceInfo::UpdateSamples(const ChartDataRange& domain,
|
||||
int32 sampleCount)
|
||||
{
|
||||
if (samplesValid)
|
||||
return true;
|
||||
|
||||
// check the sample count -- we might need to realloc the sample array
|
||||
if (sampleCount != this->sampleCount) {
|
||||
delete[] samples;
|
||||
this->sampleCount = 0;
|
||||
|
||||
samples = new(std::nothrow) double[sampleCount];
|
||||
if (samples == NULL)
|
||||
return false;
|
||||
|
||||
this->sampleCount = sampleCount;
|
||||
}
|
||||
|
||||
// get the new samples
|
||||
source->GetSamples(domain.min, domain.max, samples, sampleCount);
|
||||
|
||||
samplesValid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - LineChartRenderer
|
||||
|
||||
|
||||
LineChartRenderer::LineChartRenderer()
|
||||
:
|
||||
fFrame(),
|
||||
fDomain(),
|
||||
fRange()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
LineChartRenderer::~LineChartRenderer()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LineChartRenderer::AddDataSource(ChartDataSource* dataSource, int32 index,
|
||||
ChartRendererDataSourceConfig* config)
|
||||
{
|
||||
DataSourceInfo* info = new(std::nothrow) DataSourceInfo(dataSource,
|
||||
dynamic_cast<LineChartRendererDataSourceConfig*>(config));
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
if (!fDataSources.AddItem(info, index)) {
|
||||
delete info;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::RemoveDataSource(ChartDataSource* dataSource)
|
||||
{
|
||||
for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) {
|
||||
info->samplesValid = false;
|
||||
if (info->source == dataSource) {
|
||||
delete fDataSources.RemoveItemAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::SetFrame(const BRect& frame)
|
||||
{
|
||||
fFrame = frame;
|
||||
_InvalidateSamples();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::SetDomain(const ChartDataRange& domain)
|
||||
{
|
||||
if (domain != fDomain) {
|
||||
fDomain = domain;
|
||||
_InvalidateSamples();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::SetRange(const ChartDataRange& range)
|
||||
{
|
||||
if (range != fRange) {
|
||||
fRange = range;
|
||||
_InvalidateSamples();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::Render(BView* view, BRect updateRect)
|
||||
{
|
||||
if (!updateRect.IsValid() || updateRect.left > fFrame.right
|
||||
|| fFrame.left > updateRect.right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fDomain.min >= fDomain.max || fRange.min >= fRange.max)
|
||||
return;
|
||||
|
||||
if (!_UpdateSamples())
|
||||
return;
|
||||
|
||||
// get the range to draw (draw one more sample on each side)
|
||||
int32 left = (int32)fFrame.left;
|
||||
int32 first = (int32)updateRect.left - left - 1;
|
||||
int32 last = (int32)updateRect.right - left + 1;
|
||||
if (first < 0)
|
||||
first = 0;
|
||||
if (last > fFrame.IntegerWidth())
|
||||
last = fFrame.IntegerWidth();
|
||||
if (first > last)
|
||||
return;
|
||||
|
||||
double minRange = fRange.min;
|
||||
double sampleRange = fRange.max - minRange;
|
||||
if (sampleRange == 0) {
|
||||
minRange = fRange.min - 0.5;
|
||||
sampleRange = 1;
|
||||
}
|
||||
double scale = (double)fFrame.IntegerHeight() / sampleRange;
|
||||
|
||||
// draw
|
||||
for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) {
|
||||
printf("LineChartRenderer::Render(): %p\n", info);
|
||||
view->SetHighColor(info->config.Color());
|
||||
|
||||
float bottom = fFrame.bottom;
|
||||
view->MovePenTo(left + first,
|
||||
bottom - ((info->samples[first] - minRange) * scale));
|
||||
|
||||
for (int32 i = first; i <= last; i++) {
|
||||
view->StrokeLine(BPoint(float(left + i),
|
||||
float(bottom - ((info->samples[i] - minRange) * scale))));
|
||||
//printf(" %f: %f (%f)\n", info->samples[i] - minRange, float(bottom - ((info->samples[i] - minRange) * scale)), bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRenderer::_InvalidateSamples()
|
||||
{
|
||||
for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++)
|
||||
info->samplesValid = false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
LineChartRenderer::_UpdateSamples()
|
||||
{
|
||||
int32 width = fFrame.IntegerWidth() + 1;
|
||||
if (width <= 0)
|
||||
return false;
|
||||
|
||||
for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) {
|
||||
if (!info->UpdateSamples(fDomain, width))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - LineChartRendererDataSourceConfig
|
||||
|
||||
|
||||
LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig()
|
||||
{
|
||||
fColor.red = 0;
|
||||
fColor.green = 0;
|
||||
fColor.blue = 0;
|
||||
fColor.alpha = 255;
|
||||
}
|
||||
|
||||
|
||||
LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig(
|
||||
const rgb_color& color)
|
||||
{
|
||||
fColor = color;
|
||||
}
|
||||
|
||||
|
||||
LineChartRendererDataSourceConfig::~LineChartRendererDataSourceConfig()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const rgb_color&
|
||||
LineChartRendererDataSourceConfig::Color() const
|
||||
{
|
||||
return fColor;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
LineChartRendererDataSourceConfig::SetColor(const rgb_color& color)
|
||||
{
|
||||
fColor = color;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef LINE_CHART_RENDERER_H
|
||||
#define LINE_CHART_RENDERER_H
|
||||
|
||||
#include <InterfaceDefs.h>
|
||||
#include <Rect.h>
|
||||
|
||||
#include <ObjectList.h>
|
||||
|
||||
#include "chart/ChartDataRange.h"
|
||||
#include "chart/ChartRenderer.h"
|
||||
|
||||
|
||||
class BView;
|
||||
|
||||
|
||||
class LineChartRenderer : public ChartRenderer {
|
||||
public:
|
||||
class DataSourceConfig;
|
||||
|
||||
public:
|
||||
LineChartRenderer();
|
||||
virtual ~LineChartRenderer();
|
||||
|
||||
virtual bool AddDataSource(ChartDataSource* dataSource,
|
||||
int32 index,
|
||||
ChartRendererDataSourceConfig* config);
|
||||
virtual void RemoveDataSource(ChartDataSource* dataSource);
|
||||
|
||||
virtual void SetFrame(const BRect& frame);
|
||||
virtual void SetDomain(const ChartDataRange& domain);
|
||||
virtual void SetRange(const ChartDataRange& range);
|
||||
|
||||
virtual void Render(BView* view, BRect updateRect);
|
||||
|
||||
private:
|
||||
struct DataSourceInfo;
|
||||
|
||||
typedef BObjectList<DataSourceInfo> DataSourceList;
|
||||
|
||||
private:
|
||||
void _InvalidateSamples();
|
||||
bool _UpdateSamples();
|
||||
|
||||
private:
|
||||
DataSourceList fDataSources;
|
||||
BRect fFrame;
|
||||
ChartDataRange fDomain;
|
||||
ChartDataRange fRange;
|
||||
};
|
||||
|
||||
|
||||
class LineChartRendererDataSourceConfig : public ChartRendererDataSourceConfig {
|
||||
public:
|
||||
LineChartRendererDataSourceConfig();
|
||||
LineChartRendererDataSourceConfig(
|
||||
const rgb_color& color);
|
||||
virtual ~LineChartRendererDataSourceConfig();
|
||||
|
||||
const rgb_color& Color() const;
|
||||
void SetColor(const rgb_color& color);
|
||||
|
||||
private:
|
||||
rgb_color fColor;
|
||||
};
|
||||
|
||||
|
||||
#endif // LINE_CHART_RENDERER_H
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "chart/StringChartLegend.h"
|
||||
|
||||
#include <Font.h>
|
||||
#include <View.h>
|
||||
|
||||
|
||||
// #pragma mark - StringChartLegend
|
||||
|
||||
|
||||
StringChartLegend::StringChartLegend(const char* string, int32 level)
|
||||
:
|
||||
ChartLegend(level),
|
||||
fString(string)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - StringChartLegendRenderer
|
||||
|
||||
|
||||
void
|
||||
StringChartLegendRenderer::GetMinimumLegendSpacing(BView* view,
|
||||
float* horizontal, float* vertical)
|
||||
{
|
||||
// TODO: Implement for real!
|
||||
*horizontal = 40;
|
||||
*vertical = 20;
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
StringChartLegendRenderer::MaximumLegendSize(BView* view)
|
||||
{
|
||||
// TODO: Implement for real!
|
||||
return BSize(100, 20);
|
||||
}
|
||||
|
||||
|
||||
BSize
|
||||
StringChartLegendRenderer::LegendSize(ChartLegend* _legend, BView* view)
|
||||
{
|
||||
StringChartLegend* legend = dynamic_cast<StringChartLegend*>(_legend);
|
||||
if (legend == NULL)
|
||||
return BSize();
|
||||
|
||||
// TODO: It's a bit expensive to do that for each legend!
|
||||
// TODO: Have a font per renderer?
|
||||
BFont font;
|
||||
view->GetFont(&font);
|
||||
font_height fh;
|
||||
font.GetHeight(&fh);
|
||||
float fontHeight = ceilf(fh.ascent) + ceilf(fh.descent);
|
||||
float width = ceilf(font.StringWidth(legend->String()));
|
||||
|
||||
return BSize(width, fontHeight);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
StringChartLegendRenderer::RenderLegend(ChartLegend* _legend, BView* view,
|
||||
BPoint point)
|
||||
{
|
||||
StringChartLegend* legend = dynamic_cast<StringChartLegend*>(_legend);
|
||||
if (legend == NULL)
|
||||
return;
|
||||
|
||||
BFont font;
|
||||
view->GetFont(&font);
|
||||
font_height fh;
|
||||
font.GetHeight(&fh);
|
||||
point.y += fh.ascent;
|
||||
|
||||
view->SetHighColor(rgb_color().set_to(0, 0, 0, 255));
|
||||
view->DrawString(legend->String(), point);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef STRING_CHART_LEGEND_H
|
||||
#define STRING_CHART_LEGEND_H
|
||||
|
||||
#include <String.h>
|
||||
|
||||
#include "chart/ChartLegend.h"
|
||||
|
||||
|
||||
class StringChartLegend : public ChartLegend {
|
||||
public:
|
||||
StringChartLegend(const char* string,
|
||||
int32 level = 0);
|
||||
|
||||
const char* String() const { return fString.String(); }
|
||||
|
||||
private:
|
||||
BString fString;
|
||||
};
|
||||
|
||||
|
||||
class StringChartLegendRenderer : public ChartLegendRenderer {
|
||||
public:
|
||||
virtual void GetMinimumLegendSpacing(BView* view,
|
||||
float* horizontal, float* vertical);
|
||||
virtual BSize MaximumLegendSize(BView* view);
|
||||
|
||||
virtual BSize LegendSize(ChartLegend* legend,
|
||||
BView* view);
|
||||
virtual void RenderLegend(ChartLegend* legend, BView* view,
|
||||
BPoint point);
|
||||
};
|
||||
|
||||
|
||||
#endif // STRING_CHART_LEGEND_H
|
||||
Reference in New Issue
Block a user