|
//
|
|
// Created by mark on 6/5/25.
|
|
//
|
|
|
|
#include <ReportsAndCharts//WChartJSDataSeries.h>
|
|
|
|
#include <Wt/Json/Value.h>
|
|
#include <Wt/Json/Object.h>
|
|
|
|
// This is a variadic template that can accept any number of types (Ts...).
|
|
// In our case, the types will be the unique types of our lambdas.
|
|
template<class... Ts>
|
|
struct overloaded : Ts... { // <--- (1) Inherits from all lambda types
|
|
using Ts::operator()...; // <--- (2) Brings all call operators into scope
|
|
};
|
|
|
|
// This is a "Class Template Argument Deduction" (CTAD) guide, added in C++17.
|
|
template<class... Ts>
|
|
overloaded(Ts...) -> overloaded<Ts...>; // <--- (3) Makes usage clean
|
|
|
|
WChartJSDataSeries::WChartJSDataSeries(ChartJSType type) : chartType_(type) {
|
|
setSeriesChartJSType(chartType_);
|
|
}
|
|
|
|
void WChartJSDataSeries::setSeriesChartJSType(ChartJSType type) {
|
|
// This is used primarily for mixed bar/line where you must tag the actual
|
|
// data series.
|
|
switch (chartType_) {
|
|
case ChartJSType::BAR:
|
|
seriesOptions_["type"] = Wt::Json::Value("bar");
|
|
break;
|
|
case ChartJSType::BUBBLE:
|
|
seriesOptions_["type"] = Wt::Json::Value("bubble");
|
|
break;
|
|
case ChartJSType::DOUGHNUT:
|
|
seriesOptions_["type"] = Wt::Json::Value("doughnut");
|
|
break;
|
|
case ChartJSType::PIE:
|
|
seriesOptions_["type"] = Wt::Json::Value("pie");
|
|
break;
|
|
case ChartJSType::LINE:
|
|
seriesOptions_["type"] = Wt::Json::Value("line");
|
|
break;
|
|
case ChartJSType::POLAR:
|
|
seriesOptions_["type"] = Wt::Json::Value("polarArea");
|
|
break;
|
|
case ChartJSType::RADAR:
|
|
seriesOptions_["type"] = Wt::Json::Value("radar");
|
|
break;
|
|
case ChartJSType::SCATTER:
|
|
seriesOptions_["type"] = Wt::Json::Value("scatter");
|
|
}
|
|
}
|
|
// Use the following method to add an option not covered, or one that requires
|
|
// a depth of Json structure more than the top level depth
|
|
void WChartJSDataSeries::addSeriesOption(const std::string& propertyName, const Wt::Json::Object& option) {
|
|
seriesOptions_[propertyName] = option;
|
|
}
|
|
|
|
Wt::Json::Object WChartJSDataSeries::getJson() {
|
|
return seriesOptions_;
|
|
}
|
|
|
|
void WChartJSDataSeries::disableYaxisAnimation() {
|
|
Wt::Json::Object animations;
|
|
Wt::Json::Object yAxis;
|
|
Wt::Json::Object yAxisOption;
|
|
yAxisOption["duration"] = 0;
|
|
yAxis["y"] = Wt::Json::Value(yAxisOption);
|
|
animations["animations"] = Wt::Json::Value(yAxis);
|
|
seriesOptions_["animation"] = Wt::Json::Value(animations);
|
|
}
|
|
|
|
// --- Common Dataset Options Implementation ---
|
|
|
|
void WChartJSDataSeries::setLineBarCircleSeriesColor(const std::string& color) {
|
|
lineBarCircleSeriesMainColor_ = color;
|
|
seriesOptions_["backgroundColor"] = getBackgroundColorAsJson();
|
|
}
|
|
|
|
void WChartJSDataSeries::setLineBarCircleSeriesColor(const std::vector<std::string>& colors) {
|
|
lineBarCircleSeriesMainColor_ = colors;
|
|
// Wt::Json::Array colorArray;
|
|
// for (const auto& color : lineBarCircleSeriesMainColor_) {
|
|
// colorArray.push_back(color);
|
|
// }
|
|
seriesOptions_["backgroundColor"] = getBackgroundColorAsJson();
|
|
}
|
|
|
|
Wt::Json::Value WChartJSDataSeries::getBackgroundColorAsJson() const {
|
|
// Create an instance of our visitor by passing it two lambdas.
|
|
// One for std::string, one for std::vector<std::string>.
|
|
auto visitor = overloaded {
|
|
// This lambda is called if the variant holds a std::string
|
|
[](const std::string& color) -> Wt::Json::Value {
|
|
return Wt::Json::Value(color);
|
|
},
|
|
// This lambda is called if the variant holds a std::vector<std::string>
|
|
[](const std::vector<std::string>& colors) -> Wt::Json::Value {
|
|
Wt::Json::Array colorArray;
|
|
for (const auto& color : colors) {
|
|
colorArray.push_back(Wt::Json::Value(color));
|
|
}
|
|
return Wt::Json::Value(colorArray);
|
|
}
|
|
};
|
|
|
|
// std::visit calls the appropriate lambda from our visitor
|
|
// based on the type currently held in lineBarCircleSeriesMainColor_.
|
|
return std::visit(visitor, lineBarCircleSeriesMainColor_);
|
|
}
|
|
|
|
Wt::Json::Value WChartJSDataSeries::getBorderColorAsJson() const {
|
|
// Create an instance of our visitor by passing it two lambdas.
|
|
// One for std::string, one for std::vector<std::string>.
|
|
auto visitor = overloaded {
|
|
// This lambda is called if the variant holds a std::string
|
|
[](const std::string& color) -> Wt::Json::Value {
|
|
return Wt::Json::Value(color);
|
|
},
|
|
// This lambda is called if the variant holds a std::vector<std::string>
|
|
[](const std::vector<std::string>& colors) -> Wt::Json::Value {
|
|
Wt::Json::Array colorArray;
|
|
for (const auto& color : colors) {
|
|
colorArray.push_back(Wt::Json::Value(color));
|
|
}
|
|
return Wt::Json::Value(colorArray);
|
|
}
|
|
};
|
|
|
|
// std::visit calls the appropriate lambda from our visitor
|
|
// based on the type currently held in lineBarCircleSeriesMainColor_.
|
|
return std::visit(visitor, lineBarCircleSeriesBorderColor_);
|
|
}
|
|
|
|
void WChartJSDataSeries::setLineBarCircleSeriesBorderColor(const std::string& color) {
|
|
lineBarCircleSeriesBorderColor_ = color;
|
|
seriesOptions_["borderColor"] = getBorderColorAsJson();
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderWidth(int width) {
|
|
borderWidth_ = width;
|
|
seriesOptions_["borderWidth"] = Wt::Json::Value(width);
|
|
}
|
|
|
|
void WChartJSDataSeries::setClip(int clip) {
|
|
clip_ = clip;
|
|
seriesOptions_["clip"] = Wt::Json::Value(clip);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHidden(bool isHidden) {
|
|
hidden_ = isHidden;
|
|
seriesOptions_["hidden"] = Wt::Json::Value(isHidden);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHoverBackgroundColor(const std::string& color) {
|
|
hoverBackgroundColor_ = color;
|
|
seriesOptions_["hoverBackgroundColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHoverBorderColor(const std::string& color) {
|
|
hoverBorderColor_ = color;
|
|
seriesOptions_["hoverBorderColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHoverBorderWidth(int width) {
|
|
hoverBorderWidth_ = width;
|
|
seriesOptions_["hoverBorderWidth"] = Wt::Json::Value(width);
|
|
}
|
|
|
|
void WChartJSDataSeries::setIndexAxis(const std::string& axis) {
|
|
indexAxis_ = axis;
|
|
seriesOptions_["indexAxis"] = Wt::Json::Value(axis);
|
|
}
|
|
|
|
void WChartJSDataSeries::setOrder(int order) {
|
|
order_ = order;
|
|
seriesOptions_["order"] = Wt::Json::Value(order);
|
|
}
|
|
|
|
void WChartJSDataSeries::setParsing(bool shouldParse) {
|
|
parsing_ = shouldParse;
|
|
seriesOptions_["parsing"] = Wt::Json::Value(shouldParse);
|
|
}
|
|
|
|
void WChartJSDataSeries::setStack(const std::string& stackId) {
|
|
stack_ = stackId;
|
|
seriesOptions_["stack"] = Wt::Json::Value(stackId);
|
|
}
|
|
|
|
void WChartJSDataSeries::setXAxisID(const std::string& id) {
|
|
xAxisID_ = id;
|
|
seriesOptions_["xAxisID"] = Wt::Json::Value(id);
|
|
}
|
|
|
|
void WChartJSDataSeries::setYAxisID(const std::string& id) {
|
|
yAxisID_ = id;
|
|
seriesOptions_["yAxisID"] = Wt::Json::Value(id);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHeaderTextPosition(HeaderText headerOrientation) {
|
|
if (headerOrientation == HeaderText::HORIZONTAL) {
|
|
headerTextOrientation_ = 'H';
|
|
seriesOptions_["headerTextOrientation"] = "H";
|
|
} else {
|
|
seriesOptions_["headerTextOrientation"] = "V";
|
|
headerTextOrientation_ = 'V';
|
|
}
|
|
}
|
|
|
|
// --- Line Chart Specific Options Implementation ---
|
|
|
|
void WChartJSDataSeries::setBorderCapStyle(const std::string& style) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
borderCapStyle_ = style;
|
|
seriesOptions_["borderCapStyle"] = Wt::Json::Value(style);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderDash(const Wt::Json::Array& dashPattern) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
borderDash_ = dashPattern;
|
|
seriesOptions_["borderDash"] = Wt::Json::Array(dashPattern);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderDashOffset(double offset) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
borderDashOffset_ = offset;
|
|
seriesOptions_["borderDashOffset"] = Wt::Json::Value(offset);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderJoinStyle(const std::string& style) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
borderJoinStyle_ = style;
|
|
seriesOptions_["borderJoinStyle"] = Wt::Json::Value(style);
|
|
}
|
|
|
|
void WChartJSDataSeries::setCubicInterpolationMode(const std::string& mode) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
cubicInterpolationMode_ = mode;
|
|
seriesOptions_["cubicInterpolationMode"] = Wt::Json::Value(mode);
|
|
}
|
|
|
|
void WChartJSDataSeries::setFill(bool fill) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
fill_ = fill;
|
|
seriesOptions_["fill"] = Wt::Json::Value(fill);
|
|
}
|
|
|
|
void WChartJSDataSeries::setFill(const std::string& fill) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
fill_ = fill;
|
|
seriesOptions_["fill"] = Wt::Json::Value(fill);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointBackgroundColor(const std::string& color) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointBackgroundColor_ = color;
|
|
seriesOptions_["pointBackgroundColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointBorderColor(const std::string& color) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointBorderColor_ = color;
|
|
seriesOptions_["pointBorderColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointBorderWidth(int width) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointBorderWidth_ = width;
|
|
seriesOptions_["pointBorderWidth"] = Wt::Json::Value(width);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointHitRadius(int radius) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointHitRadius_ = radius;
|
|
seriesOptions_["pointHitRadius"] = Wt::Json::Value(radius);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointHoverBackgroundColor(const std::string& color) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointHoverBackgroundColor_ = color;
|
|
seriesOptions_["pointHoverBackgroundColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointHoverBorderColor(const std::string& color) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointHoverBorderColor_ = color;
|
|
seriesOptions_["pointHoverBorderColor"] = Wt::Json::Value(color);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointHoverBorderWidth(int width) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointHoverBorderWidth_ = width;
|
|
seriesOptions_["pointHoverBorderWidth"] = Wt::Json::Value(width);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointHoverRadius(int radius) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointHoverRadius_ = radius;
|
|
seriesOptions_["pointHoverRadius"] = Wt::Json::Value(radius);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointRadius(int radius) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointRadius_ = radius;
|
|
seriesOptions_["pointRadius"] = Wt::Json::Value(radius);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointRotation(int angle) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointRotation_ = angle;
|
|
seriesOptions_["pointRotation"] = Wt::Json::Value(angle);
|
|
}
|
|
|
|
void WChartJSDataSeries::setPointStyle(const std::string& style) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
pointStyle_ = style;
|
|
seriesOptions_["pointStyle"] = Wt::Json::Value(style);
|
|
}
|
|
|
|
void WChartJSDataSeries::setShowLine(bool show) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
showLine_ = show;
|
|
seriesOptions_["showLine"] = Wt::Json::Value(show);
|
|
}
|
|
|
|
void WChartJSDataSeries::setSpanGaps(bool span) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
spanGaps_ = span;
|
|
seriesOptions_["spanGaps"] = Wt::Json::Value(span);
|
|
}
|
|
|
|
void WChartJSDataSeries::setStepped(bool stepped) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
stepped_ = stepped;
|
|
seriesOptions_["stepped"] = Wt::Json::Value(stepped);
|
|
}
|
|
|
|
void WChartJSDataSeries::setStepped(const std::string& stepped) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
stepped_ = stepped;
|
|
seriesOptions_["stepped"] = Wt::Json::Value(stepped);
|
|
}
|
|
|
|
void WChartJSDataSeries::setTension(double tension) {
|
|
if (chartType_ != ChartJSType::LINE) assert(false);
|
|
tension_ = tension;
|
|
seriesOptions_["tension"] = Wt::Json::Value(tension);
|
|
}
|
|
|
|
|
|
// --- Bar Chart Specific Options Implementation ---
|
|
|
|
void WChartJSDataSeries::setBase(double baseValue) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
base_ = baseValue;
|
|
seriesOptions_["base"] = Wt::Json::Value(baseValue);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBarPercentage(double percentage) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
barPercentage_ = percentage;
|
|
seriesOptions_["barPercentage"] = Wt::Json::Value(percentage);
|
|
}
|
|
|
|
void WChartJSDataSeries::setCategoryPercentage(double percentage) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
categoryPercentage_ = percentage;
|
|
seriesOptions_["categoryPercentage"] = Wt::Json::Value(percentage);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderRadius(int radius) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
borderRadius_ = radius;
|
|
seriesOptions_["borderRadius"] = Wt::Json::Value(radius);
|
|
}
|
|
|
|
void WChartJSDataSeries::setBorderSkipped(const std::string& edge) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
borderSkipped_ = edge;
|
|
seriesOptions_["borderSkipped"] = Wt::Json::Value(edge);
|
|
}
|
|
|
|
void WChartJSDataSeries::setGrouped(bool isGrouped) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
grouped_ = isGrouped;
|
|
seriesOptions_["grouped"] = Wt::Json::Value(isGrouped);
|
|
}
|
|
|
|
void WChartJSDataSeries::setMinBarLength(int length) {
|
|
if (chartType_ != ChartJSType::BAR) assert(false);
|
|
minBarLength_ = length;
|
|
seriesOptions_["minBarLength"] = Wt::Json::Value(length);
|
|
}
|
|
|
|
|
|
// --- Pie & Doughnut Chart Specific Options Implementation ---
|
|
|
|
void WChartJSDataSeries::setBorderAlign(const std::string& alignment) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
borderAlign_ = alignment;
|
|
seriesOptions_["borderAlign"] = Wt::Json::Value(alignment);
|
|
}
|
|
|
|
void WChartJSDataSeries::setHoverOffset(int offset) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
hoverOffset_ = offset;
|
|
seriesOptions_["hoverOffset"] = Wt::Json::Value(offset);
|
|
}
|
|
|
|
void WChartJSDataSeries::setOffset(int offset) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
offset_ = offset;
|
|
seriesOptions_["offset"] = Wt::Json::Value(offset);
|
|
}
|
|
|
|
void WChartJSDataSeries::setSpacing(int spacing) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
spacing_ = spacing;
|
|
seriesOptions_["spacing"] = Wt::Json::Value(spacing);
|
|
}
|
|
|
|
void WChartJSDataSeries::setWeight(double weight) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
weight_ = weight;
|
|
seriesOptions_["weight"] = Wt::Json::Value(weight);
|
|
}
|
|
|
|
void WChartJSDataSeries::setCutout(const std::string& cutout) {
|
|
if (chartType_ != ChartJSType::PIE && chartType_ != ChartJSType::DOUGHNUT) assert(false);
|
|
cutout_ = cutout;
|
|
seriesOptions_["cutout"] = Wt::Json::Value(cutout);
|
|
}
|