Skip to content

Commit

Permalink
Merge pull request #617 from vizzuhq/axis_refactor_santa_special
Browse files Browse the repository at this point in the history
Axis refactor v11e - Cross interlacing, NaN handling, off dimLabel out
  • Loading branch information
schaumb authored Nov 26, 2024
2 parents 5ff6c82 + 0219fba commit 3c6b916
Show file tree
Hide file tree
Showing 22 changed files with 372 additions and 107 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
- Enable dimension axis ticks and interlacing.
- Enable measure axis guides.
- Fix dimension axis guides on sorted chart.
- Fix NaN handling on axes and size measures other aggregators than sum.
- Add meaning to crossing interlacing.
- Do not draw dimension axis labels when the middle of the text is off the plot.

## [0.15.0] - 2024-10-28

Expand Down
2 changes: 1 addition & 1 deletion src/base/type/uniquelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ template <class T> class UniqueList
struct CommonIterateVal
{
const T &value;
const std::size_t *othIx;
const std::size_t *otherIx;
};

struct common_iterator
Expand Down
4 changes: 3 additions & 1 deletion src/chart/generator/axis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,9 @@ bool DimensionAxis::setLabels(double step)
auto currStep = 0.0;

for (auto curr = int{}; auto &&item : sortedItems()) {
if (++curr <= currStep) continue;
if (auto mid = item.get().range.middle();
std::signbit(mid) || mid > 1.0 || ++curr <= currStep)
continue;
currStep += step;
item.get().label = true;
hasLabel = true;
Expand Down
3 changes: 2 additions & 1 deletion src/chart/generator/axis.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ struct ChannelStats

void track(ChannelId at, const double &value)
{
std::get<0>(tracked[at]).include(value);
if (std::isfinite(value))
std::get<0>(tracked[at]).include(value);
}

template <ChannelIdLike Id>
Expand Down
5 changes: 3 additions & 2 deletions src/chart/generator/buckets.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ struct Buckets
});
}

[[nodiscard]] bool operator!=(const const_iterator &oth) const
[[nodiscard]] bool operator!=(
const const_iterator &other) const
{
return data.data() != oth.data.data();
return data.data() != other.data.data();
}

const_iterator &operator++();
Expand Down
3 changes: 3 additions & 0 deletions src/chart/generator/marker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ Marker::Marker(const Options &options,
data.joinDimensionValues(labelChannel.dimensions(),
index)};
}

if (!std::isfinite(position.x) || !std::isfinite(position.y))
enabled = false;
}

bool Marker::connectMarkers(bool first,
Expand Down
16 changes: 11 additions & 5 deletions src/chart/generator/plotbuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,16 @@ PlotBuilder::PlotBuilder(const Data::DataTable &dataTable,
std::size_t mainBucketSize{};
auto &&subBuckets = generateMarkers(mainBucketSize);

if (!plot->getOptions()->getChannels().anyAxisSet())
if (!plot->getOptions()->getChannels().anyAxisSet()) {
addSpecLayout(subBuckets);
else
normalizeSizes();
}
else {
normalizeSizes();
addAxisLayout(subBuckets, mainBucketSize, dataTable);
}

normalizeColors();
normalizeSizes();
calcLegendAndLabel(dataTable);
}

Expand Down Expand Up @@ -551,8 +554,11 @@ void PlotBuilder::normalizeSizes()
|| plot->getOptions()->geometry == ShapeType::line) {
Math::Range<> size;

for (auto &marker : plot->markers)
if (marker.enabled) size.include(marker.sizeFactor);
for (auto &marker : plot->markers) {
if (std::isnan(marker.sizeFactor)) marker.enabled = false;
if (!marker.enabled) continue;
size.include(marker.sizeFactor);
}

size = plot->getOptions()
->getChannels()
Expand Down
3 changes: 2 additions & 1 deletion src/chart/rendering/drawaxes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ const DrawAxes &&DrawAxes::init() &&
!item.startPos.isAuto() && *item.startPos,
!item.endPos.isAuto() && *item.endPos,
axis.dimension.factor);
needSeparators && sepWeight > 0)
needSeparators && sepWeight > 0
&& item.range.getMin() > 0)
separators.emplace_back(item.range.getMin(),
sepWeight);
}
Expand Down
139 changes: 115 additions & 24 deletions src/chart/rendering/drawinterlacing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const
|| guides.interlacings == false)
return;

auto orientation = !+axisIndex;
auto otherWeights = getInterlacingWeights(!axisIndex);
auto &&otherInterlacingColor =
*parent.rootStyle.plot.getAxis(!axisIndex).interlacing.color;

parent.painter.setPolygonToCircleFactor(0);
parent.painter.setPolygonStraightFactor(0);
Expand All @@ -48,30 +50,42 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex) const
1.0,
Math::Floating::less)
- clippedBottom;
auto rect = Geom::Rect{
Geom::Point::Coord(orientation, 0.0, clippedBottom),
{Geom::Size::Coord(orientation, 1.0, clippedSize)}};

auto interlacingColor =
*axisStyle.interlacing.color
* Math::FuzzyBool::And<double>(interval.weight,
interval.isSecond,
guides.interlacings);

auto &canvas = parent.canvas;
canvas.save();
canvas.setLineColor(Gfx::Color::Transparent());
canvas.setBrushColor(interlacingColor);
if (auto &&eventTarget =
Events::Targets::axisInterlacing(axisIndex);
parent.rootEvents.draw.plot.axis.interlacing->invoke(
Events::OnRectDrawEvent(*eventTarget,
{rect, true}))) {
parent.painter.drawPolygon(rect.points());
parent.renderedChart.emplace(Rect{rect, true},
std::move(eventTarget));

auto rect = [&, orientation = +axisIndex](const double &from,
const double &to)
{
return Geom::Rect{
Geom::Point::Coord(orientation, clippedBottom, from),
{Geom::Size::Coord(orientation,
clippedSize,
to - from)}};
};

auto weight = Math::FuzzyBool::And<double>(interval.weight,
interval.isSecond,
guides.interlacings);
auto interlacingColor = *axisStyle.interlacing.color * weight;

for (auto first = otherWeights.begin(),
next = std::next(first),
last = otherWeights.end();
next != last;
++next, ++first) {
if (Math::Floating::is_zero(first->second))
drawInterlacing(axisIndex,
interlacingColor,
rect(first->first, next->first));
else if (axisIndex == Gen::AxisId::y) {
drawInterlacing(first->second > weight ? !axisIndex
: axisIndex,
getCrossingInterlacingColor(
*axisStyle.interlacing.color,
weight,
otherInterlacingColor,
first->second),
rect(first->first, next->first));
}
}
canvas.restore();
}
}

Expand Down Expand Up @@ -119,6 +133,26 @@ void DrawInterlacing::drawTexts(Gen::AxisId axisIndex) const
}
}

void DrawInterlacing::drawInterlacing(Gen::AxisId axisIndex,
const Gfx::Color &interlacingColor,
const Geom::Rect &rect) const
{
auto &canvas = parent.canvas;
canvas.save();
canvas.setLineColor(Gfx::Color::Transparent());
canvas.setLineWidth(0);
canvas.setBrushColor(interlacingColor);
if (auto &&eventTarget =
Events::Targets::axisInterlacing(axisIndex);
parent.rootEvents.draw.plot.axis.interlacing->invoke(
Events::OnRectDrawEvent(*eventTarget, {rect, true}))) {
parent.painter.drawPolygon(rect.points());
parent.renderedChart.emplace(Rect{rect, true},
std::move(eventTarget));
}
canvas.restore();
}

void DrawInterlacing::drawDataLabel(
const ::Anim::Interpolated<bool> &axisEnabled,
Gen::AxisId axisIndex,
Expand Down Expand Up @@ -226,4 +260,61 @@ void DrawInterlacing::drawSticks(double tickLength,
canvas.restore();
}

std::map<double, double> DrawInterlacing::getInterlacingWeights(
Gen::AxisId axisIndex) const
{
std::map<double, double> weights{{0.0, 0.0}, {1.0, 0.0}};

auto &&guides = parent.plot->guides.at(axisIndex);
auto &&axisStyle = parent.rootStyle.plot.getAxis(axisIndex);
if (axisStyle.interlacing.color->isTransparent()
|| guides.interlacings == false)
return weights;

for (auto &&interval : parent.getIntervals(axisIndex)) {
if (Math::Floating::is_zero(interval.isSecond)) continue;
auto min = std::max(interval.range.getMin(), 0.0);
auto max = std::min(interval.range.getMax(), 1.0);
auto mprev = std::prev(weights.upper_bound(min));
auto mnext = weights.lower_bound(max);

if (mprev->first < min)
mprev = weights.try_emplace(mprev, min, mprev->second);
if (mnext->first > max)
mnext = weights.try_emplace(mnext,
max,
std::prev(mnext)->second);

while (mprev != mnext)
mprev++->second +=
Math::FuzzyBool::And<double>(interval.weight,
interval.isSecond,
guides.interlacings);
}
return weights;
}

Gfx::Color DrawInterlacing::getCrossingInterlacingColor(
const Gfx::Color &mainColor,
double mainWeight,
const Gfx::Color &otherColor,
double otherWeight)
{
auto color = mainColor * mainWeight + otherColor * otherWeight;

color.alpha = 1
- (1 - mainColor.alpha * mainWeight)
* (1 - otherColor.alpha * otherWeight);

if (mainWeight + otherWeight > 1.0)
color = Math::Niebloid::interpolate(color,
color
* std::max({std::abs(mainColor.red - otherColor.red),
std::abs(mainColor.green - otherColor.green),
std::abs(mainColor.blue - otherColor.blue)}),
mainWeight + otherWeight - 1.0);

return color;
}

}
13 changes: 13 additions & 0 deletions src/chart/rendering/drawinterlacing.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class DrawInterlacing
const DrawAxes &parent;

private:
void drawInterlacing(Gen::AxisId axisIndex,
const Gfx::Color &interlacingColor,
const Geom::Rect &rect) const;

void drawDataLabel(const ::Anim::Interpolated<bool> &enabled,
Gen::AxisId axisIndex,
const Geom::Point &tickPos,
Expand All @@ -27,6 +31,15 @@ class DrawInterlacing
const Gfx::Color &tickColor,
Gen::AxisId axisIndex,
const Geom::Point &tickPos) const;

[[nodiscard]] std::map<double, double> getInterlacingWeights(
Gen::AxisId axisIndex) const;

[[nodiscard]] static Gfx::Color getCrossingInterlacingColor(
const Gfx::Color &mainColor,
double mainWeight,
const Gfx::Color &otherColor,
double otherWeight);
};

}
Expand Down
9 changes: 7 additions & 2 deletions src/dataframe/impl/aggregators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,19 @@ get_aggregators() noexcept

auto &&aggrs = Refl::enum_names<aggregator_type>;
return {{{{aggrs[static_cast<std::size_t>(aggregator_type::sum)],
empty_double,
init_nan,
[](custom_aggregator::id_type &id,
cell_reference const &cell) -> double
{
auto &ref = *std::get_if<double>(&id);
const double &value =
*std::get_if<double>(&cell);
if (std::isfinite(value)) ref += value;
if (std::isfinite(value)) {
if (std::isnan(ref))
ref = value;
else
ref += value;
}

return ref;
}},
Expand Down
2 changes: 1 addition & 1 deletion src/dataframe/impl/dataframe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ void dataframe::add_series_by_other(std::string_view,
const std::function<cell_value(record_type, cell_reference)> &,
std::span<const std::pair<const char *, const char *>>) &
{
if (as_if()) error(error_type::unimplemented, "by oth");
if (as_if()) error(error_type::unimplemented, "by other");
}

void dataframe::remove_series(
Expand Down
8 changes: 4 additions & 4 deletions src/dataframe/interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ struct custom_aggregator
id_type (*create)();
double (*add)(id_type &, cell_reference const &);

auto operator<=>(const custom_aggregator &oth) const
auto operator<=>(const custom_aggregator &other) const
{
return name <=> oth.name;
return name <=> other.name;
}

auto operator==(const custom_aggregator &oth) const
auto operator==(const custom_aggregator &other) const
{
return name == oth.name;
return name == other.name;
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/dataframe/old/datatable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ void DataTable::pushRow(const std::span<const char *const> &cells)

std::string DataTable::getInfos() const { return df.as_string(); }

bool DataCube::iterator_t::operator!=(const iterator_t &oth) const
bool DataCube::iterator_t::operator!=(const iterator_t &other) const
{
return parent != oth.parent;
return parent != other.parent;
}

void DataCube::iterator_t::operator++() { parent->incr(*this); }
Expand Down
2 changes: 1 addition & 1 deletion src/dataframe/old/datatable.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class DataCube
const DataCube *parent{};
MultiIndex index;

[[nodiscard]] bool operator!=(const iterator_t &oth) const;
[[nodiscard]] bool operator!=(const iterator_t &other) const;

void operator++();

Expand Down
Loading

0 comments on commit 3c6b916

Please sign in to comment.