@@ -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
374387void 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
585631void PlotBuilder::normalizeSizes ()
0 commit comments