Skip to content

Commit 3b99f21

Browse files
committed
add filter + BucketSeparationInfo
1 parent 4694ee8 commit 3b99f21

File tree

10 files changed

+275
-128
lines changed

10 files changed

+275
-128
lines changed

src/chart/generator/axis.cpp

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,8 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor)
348348
interpolate(static_cast<const Axis &>(op0),
349349
static_cast<const Axis &>(op1),
350350
factor);
351-
if (!op0.parts.empty() && !op1.parts.empty()) {
351+
if (!op0.parts.empty() && !op1.parts.empty()
352+
&& op0.seriesName() == op1.seriesName()) {
352353
using PartPair = const decltype(res.parts)::value_type;
353354
Alg::union_foreach(
354355
op0.parts,
@@ -360,69 +361,128 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor)
360361
switch (type) {
361362
case Alg::union_call_t::only_left: {
362363
auto from = lhs->second.range.min;
363-
res.parts[lhs->first] = {
364-
.weight = interpolate(lhs->second.weight,
365-
0.0,
366-
factor),
367-
.range = interpolate(lhs->second.range,
368-
Math::Range<>{from, from},
369-
factor)};
364+
res.parts.insert({lhs->first,
365+
{.weight = interpolate(lhs->second.weight,
366+
0.0,
367+
factor),
368+
.range = interpolate(lhs->second.range,
369+
Math::Range<>{from, from},
370+
factor),
371+
.measureRange =
372+
interpolate(lhs->second.measureRange,
373+
Math::Range<>{0, 1},
374+
factor)}});
370375
break;
371376
}
372377
case Alg::union_call_t::only_right: {
373378
auto from = rhs->second.range.min;
374-
res.parts[rhs->first] = {
375-
.weight = interpolate(0.0,
376-
rhs->second.weight,
377-
factor),
378-
.range =
379-
interpolate(Math::Range<>{from, from},
380-
rhs->second.range,
381-
factor)};
379+
res.parts.insert({rhs->first,
380+
{.weight = interpolate(0.0,
381+
rhs->second.weight,
382+
factor),
383+
.range =
384+
interpolate(Math::Range<>{from, from},
385+
rhs->second.range,
386+
factor),
387+
.measureRange =
388+
interpolate(Math::Range<>{0, 1},
389+
rhs->second.measureRange,
390+
factor)}});
382391
break;
383392
}
384393
default:
385394
case Alg::union_call_t::both: {
386-
res.parts[lhs->first] =
387-
interpolate(lhs->second, rhs->second, factor);
395+
res.parts.insert({lhs->first,
396+
interpolate(lhs->second,
397+
rhs->second,
398+
factor)});
388399
break;
389400
}
390401
}
391402
},
392403
res.parts.value_comp());
404+
405+
return res;
393406
}
394-
else if (!op0.parts.empty()) {
395-
auto begin = op0.parts.begin();
396-
res.parts[begin->first] = {
397-
.weight = interpolate(begin->second.weight, 1.0, factor),
398-
.range = interpolate(begin->second.range,
399-
Math::Range<>{0, 1},
400-
factor)};
401-
while (++begin != op0.parts.end()) {
402-
res.parts[begin->first] = {
403-
.weight =
404-
interpolate(begin->second.weight, 0.0, factor),
405-
.range = interpolate(begin->second.range,
406-
Math::Range<>{0, 1},
407-
factor)};
407+
408+
if (!op0.parts.empty()) {
409+
if (op0.seriesName() != op1.seriesName()) {
410+
for (auto &&[index, part] : op0.parts)
411+
res.parts.insert({index,
412+
{.weight = part.weight * (1 - factor),
413+
.range = part.range,
414+
.measureRange = part.measureRange}});
415+
}
416+
else {
417+
auto begin = op0.parts.begin();
418+
res.parts.insert({begin->first,
419+
{.weight = interpolate(begin->second.weight,
420+
1.0,
421+
factor),
422+
.range = interpolate(begin->second.range,
423+
Math::Range<>{0, 1},
424+
factor),
425+
.measureRange =
426+
interpolate(begin->second.measureRange,
427+
Math::Range<>{0, 1},
428+
factor)}});
429+
while (++begin != op0.parts.end()) {
430+
res.parts.insert({begin->first,
431+
{.weight = interpolate(begin->second.weight,
432+
0.0,
433+
factor),
434+
.range = interpolate(begin->second.range,
435+
Math::Range<>{0, 1},
436+
factor),
437+
.measureRange =
438+
interpolate(begin->second.measureRange,
439+
Math::Range<>{0, 1},
440+
factor)}});
441+
}
408442
}
409443
}
410-
else if (!op1.parts.empty()) {
411-
auto begin = op1.parts.begin();
412-
res.parts[begin->first] = {
413-
.weight = interpolate(1.0, begin->second.weight, factor),
414-
.range = interpolate(Math::Range<>{0, 1},
415-
begin->second.range,
416-
factor)};
417-
while (++begin != op1.parts.end()) {
418-
res.parts[begin->first] = {
419-
.weight =
420-
interpolate(0.0, begin->second.weight, factor),
421-
.range = interpolate(Math::Range<>{0, 1},
422-
begin->second.range,
423-
factor)};
444+
else if (!op1.parts.empty()
445+
&& op0.seriesName() != op1.seriesName())
446+
res.parts.insert({std::nullopt, {.weight = 1 - factor}});
447+
448+
if (!op1.parts.empty()) {
449+
if (op0.seriesName() != op1.seriesName()) {
450+
for (auto &&[index, part] : op1.parts)
451+
res.parts.insert({index,
452+
{.weight = part.weight * factor,
453+
.range = part.range,
454+
.measureRange = part.measureRange}});
455+
}
456+
else {
457+
auto begin = op1.parts.begin();
458+
res.parts.insert({begin->first,
459+
{.weight = interpolate(1.0,
460+
begin->second.weight,
461+
factor),
462+
.range = interpolate(Math::Range<>{0, 1},
463+
begin->second.range,
464+
factor),
465+
.measureRange = interpolate(Math::Range<>{0, 1},
466+
begin->second.measureRange,
467+
factor)}});
468+
while (++begin != op1.parts.end()) {
469+
res.parts.insert({begin->first,
470+
{.weight = interpolate(0.0,
471+
begin->second.weight,
472+
factor),
473+
.range = interpolate(Math::Range<>{0, 1},
474+
begin->second.range,
475+
factor),
476+
.measureRange =
477+
interpolate(Math::Range<>{0, 1},
478+
begin->second.measureRange,
479+
factor)}});
480+
}
424481
}
425482
}
483+
else if (!op0.parts.empty()
484+
&& op0.seriesName() != op1.seriesName())
485+
res.parts.insert({std::nullopt, {.weight = factor}});
426486

427487
return res;
428488
}

src/chart/generator/axis.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,14 @@ struct SplitAxis : Axis
202202
{
203203
double weight{1.0};
204204
Math::Range<> range{0, 1};
205+
Math::Range<> measureRange{0, 1};
205206

206207
[[nodiscard]] bool operator==(
207208
const Part &other) const = default;
208209
};
209210

210-
using Parts = std::map<std::size_t, Part>;
211+
using Parts =
212+
std::multimap<std::optional<Data::SliceIndex>, Part>;
211213
Parts parts;
212214

213215
[[nodiscard]] bool operator==(

src/chart/generator/plotbuilder.cpp

Lines changed: 75 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,11 @@ Buckets PlotBuilder::generateMarkers(std::size_t &mainBucketSize,
145145
return Buckets{plot->markers};
146146
}
147147

148-
std::vector<PlotBuilder::BucketInfo> PlotBuilder::sortedBuckets(
148+
std::vector<PlotBuilder::BucketSortInfo> PlotBuilder::sortedBuckets(
149149
const Buckets &buckets,
150150
AxisId axisIndex) const
151151
{
152-
std::vector<BucketInfo> sorted;
152+
std::vector<BucketSortInfo> sorted;
153153

154154
for (auto &&bucket : buckets)
155155
for (auto &&[marker, idx] : bucket) {
@@ -159,7 +159,7 @@ std::vector<PlotBuilder::BucketInfo> PlotBuilder::sortedBuckets(
159159
auto it = std::ranges::lower_bound(sorted,
160160
idx.itemId,
161161
{},
162-
&BucketInfo::index);
162+
&BucketSortInfo::index);
163163
if (it == sorted.end() || it->index != idx.itemId)
164164
it = sorted.emplace(it, idx.itemId, 0.0);
165165

@@ -170,7 +170,7 @@ std::vector<PlotBuilder::BucketInfo> PlotBuilder::sortedBuckets(
170170
if (plot->getOptions()->getChannels().axisPropsAt(axisIndex).sort
171171
== Sort::byValue)
172172
std::ranges::stable_sort(sorted,
173-
[](const BucketInfo &lhs, const BucketInfo &rhs)
173+
[](const BucketSortInfo &lhs, const BucketSortInfo &rhs)
174174
{
175175
return Math::Floating::less(lhs.size, rhs.size);
176176
});
@@ -345,14 +345,19 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable,
345345
(mainAxis == AxisId::x ? subBoundRect : mainBoundRect)
346346
.min));
347347

348+
if (!mainRanges.empty())
349+
plot->getOptions()->mainAxis().range = {};
350+
if (!subRanges.empty()) plot->getOptions()->subAxis().range = {};
351+
348352
mainBoundRect =
349353
plot->getOptions()->mainAxis().range.getRange(mainBoundRect);
350354
subBoundRect =
351355
plot->getOptions()->subAxis().range.getRange(subBoundRect);
352356

353-
for (auto &&[axis, ranges, boundSize] :
354-
{std::tuple{mainAxis, &mainRanges, mainBoundRect},
355-
{!mainAxis, &subRanges, subBoundRect}}) {
357+
for (auto &&[axis, needRanges, boundSize] :
358+
{std::tuple{mainAxis, mainRanges.empty(), mainBoundRect},
359+
{!mainAxis, subRanges.empty(), subBoundRect}}) {
360+
356361
for (auto &marker : plot->markers) {
357362
auto &&markerSize = marker.getSizeBy(axis);
358363
if (!boundSize.positive().intersects(
@@ -363,12 +368,20 @@ void PlotBuilder::calcAxises(const Data::DataTable &dataTable,
363368
{boundSize.rescale(markerSize.min, 0.0),
364369
boundSize.rescale(markerSize.max, 0.0)});
365370
}
366-
367-
stats.setIfRange(axis, boundSize);
371+
if (needRanges) stats.setIfRange(axis, boundSize);
368372
}
369373

370-
for (const AxisId &ch : {AxisId::x, AxisId::y})
374+
for (auto &&[ch, ranges] :
375+
{std::pair{mainAxis, &mainRanges}, {!mainAxis, &subRanges}}) {
371376
calcAxis(dataTable, ch);
377+
if (ranges->empty()) continue;
378+
379+
auto &axis = plot->axises.at(ch);
380+
for (auto &&range : *ranges)
381+
if (range.enabled)
382+
axis.parts.insert({range.index,
383+
{1.0, range.atRange, range.containsValues}});
384+
}
372385
}
373386

374387
void PlotBuilder::calcLegendAndLabel(const Data::DataTable &dataTable)
@@ -534,52 +547,85 @@ void PlotBuilder::addAlignment(const Buckets &buckets,
534547
}
535548
}
536549

537-
std::vector<Math::Range<>> PlotBuilder::addSeparation(
538-
const Buckets &buckets,
550+
std::vector<PlotBuilder::BucketSeparationInfo>
551+
PlotBuilder::addSeparation(const Buckets &buckets,
539552
AxisId axisIndex,
540-
const std::size_t &otherBucketSize) const
553+
const std::size_t &otherBucketSize)
541554
{
542555
if (!plot->getOptions()->isSplit(axisIndex)) return {};
543556

544557
const auto &axisProps =
545558
plot->getOptions()->getChannels().axisPropsAt(axisIndex);
546559
auto align = axisProps.align;
547560

548-
std::vector ranges{otherBucketSize, Math::Range<>{{}, {}}};
549-
std::vector<bool> anyEnabled(otherBucketSize);
561+
std::vector<BucketSeparationInfo> res(otherBucketSize);
550562

551563
for (auto &&bucket : buckets)
552564
for (auto &&[marker, idx] : bucket) {
553565
if (!marker.enabled) continue;
554-
auto i = idx.itemId;
555-
ranges[i].include(marker.getSizeBy(axisIndex).size());
556-
anyEnabled[i] = true;
566+
auto &resItem = res[idx.itemId];
567+
if (!resItem.index && idx.label)
568+
resItem.index = idx.label;
569+
570+
resItem.containsValues.include(
571+
marker.getSizeBy(axisIndex).size());
572+
resItem.enabled = true;
557573
}
558574

559575
auto max = Math::Range<>{{}, {}};
560-
for (auto i = 0U; i < ranges.size(); ++i)
561-
if (anyEnabled[i]) max = max + ranges[i];
576+
auto maxRange = Math::Range<>{{}, {}};
577+
for (auto &resItem : res) {
578+
if (!resItem.enabled) continue;
579+
max = max + resItem.containsValues;
580+
maxRange.include(resItem.containsValues);
581+
}
562582

563583
auto splitSpace =
564584
plot->getStyle().plot.getAxis(axisIndex).spacing->get(
565585
max.size(),
566586
plot->getStyle().calculatedSize());
567587

568-
auto onMax = ranges[0].max;
569-
ranges[0] = ranges[0] - ranges[0].min;
570-
for (auto i = 1U; i < ranges.size(); ++i) {
571-
onMax += anyEnabled[i - 1] ? splitSpace : 0;
572-
ranges[i] = ranges[i] + onMax - ranges[i].min * 2;
573-
onMax += ranges[i].size();
588+
res[0].atRange =
589+
res[0].containsValues - res[0].containsValues.min * 2;
590+
auto onMax = res[0].containsValues.size();
591+
for (auto i = 1U; i < res.size(); ++i) {
592+
onMax += res[i - 1].enabled ? splitSpace : 0;
593+
res[i].atRange = res[i].containsValues + onMax
594+
- res[i].containsValues.min * 2;
595+
onMax += res[i].containsValues.size();
574596
}
575597

576598
for (auto &&bucket : buckets)
577599
for (auto &&[marker, idx] : bucket)
578600
marker.setSizeBy(axisIndex,
579-
Base::Align{align, ranges[idx.itemId]}.getAligned(
580-
marker.getSizeBy(axisIndex)));
601+
Base::Align{align, res.at(idx.itemId).atRange}
602+
.getAligned(marker.getSizeBy(axisIndex)));
603+
604+
auto alignedRange =
605+
Base::Align{align, {maxRange.min, maxRange.min}}.getAligned(
606+
maxRange);
607+
608+
res[0].atRange = res[0].atRange
609+
- maxRange.min * res[0].containsValues.size()
610+
/ maxRange.size();
611+
for (auto &resItem : res) {
612+
if (!resItem.enabled) continue;
613+
614+
resItem.atRange =
615+
((resItem.atRange + resItem.containsValues.min
616+
- resItem.atRange.min)
617+
* maxRange.size() / resItem.containsValues.size()
618+
+ resItem.atRange.min - resItem.containsValues.min
619+
+ maxRange.min)
620+
/ onMax;
621+
622+
resItem.containsValues = {
623+
maxRange.rescale(resItem.containsValues.min),
624+
maxRange.rescale(resItem.containsValues.max)};
625+
}
581626

582-
return ranges;
627+
stats.setIfRange(axisIndex, alignedRange);
628+
return res;
583629
}
584630

585631
void PlotBuilder::normalizeSizes()

0 commit comments

Comments
 (0)