Skip to content

Commit c71aaf3

Browse files
committed
only max+min measure ; tests
1 parent dec4e97 commit c71aaf3

File tree

13 files changed

+234
-150
lines changed

13 files changed

+234
-150
lines changed

src/base/math/range.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ template <std::floating_point T = double> struct Range
4646
&& !is_lt(std::weak_order(max, value));
4747
}
4848

49-
[[nodiscard]] T rescale(const T &value, T def = 0.5) const
49+
[[nodiscard]] T rescale(const T &value, T def = T{0.5}) const
5050
{
5151
auto s = size();
5252
return is_zero(s) ? def : (value - min) / s;
@@ -64,7 +64,7 @@ template <std::floating_point T = double> struct Range
6464

6565
[[nodiscard]] T normalize(const T &value) const
6666
{
67-
return is_zero(max) ? 0 : value / max;
67+
return is_zero(max) ? T{} : value / max;
6868
}
6969

7070
bool operator==(const Range &other) const
@@ -97,14 +97,15 @@ template <std::floating_point T = double> struct Range
9797
return {min / factor, max / factor};
9898
}
9999

100-
Range operator*(const Transform &transf)
100+
[[nodiscard]] Range operator*(const Transform &transf) const
101101
{
102102
return *this * transf.factor + transf.shift;
103103
}
104104

105-
Transform operator/(const Range range)
105+
[[nodiscard]] Transform operator/(const Range range) const
106106
{
107-
auto factor = range.size() != 0 ? size() / range.size() : 0;
107+
auto factor =
108+
is_zero(range.size()) ? T{} : size() / range.size();
108109
auto shift = min - range.min * factor;
109110
return Transform{factor, shift};
110111
}

src/chart/generator/axis.cpp

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,8 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor)
380380

381381
using PartPair = SplitAxis::Parts::value_type;
382382

383-
auto needMerge = op0.seriesName() == op1.seriesName();
383+
auto needMerge = op0.seriesName() == op1.seriesName()
384+
&& op0.measure.unit == op1.measure.unit;
384385

385386
auto &&merger = [](double factor)
386387
{
@@ -393,27 +394,20 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor)
393394
};
394395

395396
using MergerType = decltype(merger(0.0));
396-
auto &&one_side =
397-
[needMerge](const MergerType &merger, bool needOther)
397+
auto &&one_side = [](const MergerType &merger,
398+
bool needOther,
399+
const Math::Range<>::Transform &transform)
398400
{
399-
return
400-
[needMerge, &merger, needOther, firstSpecial = needOther](
401-
const PartPair &val) mutable -> PartPair
401+
return [&merger, &transform, needOther](
402+
const PartPair &val) -> PartPair
402403
{
403-
if (needMerge) {
404-
if (firstSpecial) {
405-
firstSpecial = false;
406-
return merger(val,
407-
PartPair{std::nullopt, SplitAxis::Part{}});
408-
}
409-
410-
Math::Range<> range{0.0, 1.0};
411-
if (!needOther)
412-
range = {val.second.range.max,
413-
val.second.range.max};
414-
415-
return merger(val, {{}, {0.0, range}});
416-
}
404+
if (needOther)
405+
return merger(val,
406+
{{},
407+
{0.0,
408+
(Math::Range<>{0, 1} * transform),
409+
(val.second.measureRange * 1.000001
410+
* transform)}});
417411
return merger(val,
418412
{{},
419413
{0.0,
@@ -431,24 +425,45 @@ interpolate(const SplitAxis &op0, const SplitAxis &op1, double factor)
431425
std::identity,
432426
const std::optional<Data::SliceIndex> PartPair::*,
433427
decltype(std::weak_order),
434-
decltype(one_side(merger({}), {})),
435-
decltype(one_side(merger({}), {})),
428+
decltype(one_side(merger({}), {}, {})),
429+
decltype(one_side(merger({}), {}, {})),
436430
Alg::Merge::always,
437431
const MergerType &>
438432
// }
439433
{.projection = &PartPair::first,
440-
.transformer_1 =
441-
one_side(merger(factor), op1.parts.size() <= 1),
442-
.transformer_2 =
443-
one_side(merger(1 - factor), op0.parts.size() <= 1),
434+
.transformer_1 = one_side(merger(factor),
435+
needMerge && op1.parts.empty(),
436+
(op0.measure.range - op0.measure.range.min)
437+
/ (op1.measure.range - op1.measure.range.min)),
438+
.transformer_2 = one_side(merger(1 - factor),
439+
needMerge && op0.parts.empty(),
440+
(op1.measure.range - op1.measure.range.min)
441+
/ (op0.measure.range - op0.measure.range.min)),
444442
.need_merge = {needMerge},
445443
.merger = merger(factor)});
446444

447-
if (!needMerge && op0.parts.empty() != op1.parts.empty()
445+
if (op0.parts.empty() != op1.parts.empty()
448446
&& (op0.dimension.hasMarker || op0.measure.enabled.get())
449-
&& (op1.dimension.hasMarker || op1.measure.enabled.get()))
450-
res.parts.insert({std::nullopt,
451-
{.weight = op0.parts.empty() ? 1 - factor : factor}});
447+
&& (op1.dimension.hasMarker || op1.measure.enabled.get())) {
448+
if (needMerge) {
449+
const auto &ref =
450+
(op0.parts.empty() ? op1.parts : op0.parts)
451+
.begin()
452+
->second.range;
453+
res.parts
454+
.insert(
455+
merger(op0.parts.empty()
456+
? 1 - factor
457+
: factor)({{}, {0.0, ref}}, {{}, {}}))
458+
->second.unique = true;
459+
}
460+
else
461+
res.parts
462+
.insert({std::nullopt,
463+
{.weight =
464+
op0.parts.empty() ? 1 - factor : factor}})
465+
->second.unique = true;
466+
}
452467

453468
return res;
454469
}

src/chart/generator/axis.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,18 @@ struct SplitAxis : Axis
204204
double weight{1.0};
205205
Math::Range<> range{0, 1};
206206
Math::Range<> measureRange{0, 1};
207+
bool unique{};
207208

208209
[[nodiscard]] bool operator==(
209210
const Part &other) const = default;
211+
212+
[[nodiscard]] consteval static auto members()
213+
{
214+
return std::tuple{&Part::weight,
215+
&Part::range,
216+
&Part::measureRange,
217+
std::ignore};
218+
}
210219
};
211220

212221
using Parts =

src/chart/rendering/drawaxes.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ void DrawAxes::drawGeometries() const
5757

5858
DrawInterlacing{*this}.drawGeometries(Gen::AxisId::y,
5959
ySplit.measureRange,
60+
xSplit.measureRange,
6061
tr,
6162
weight);
6263
DrawInterlacing{*this}.drawGeometries(Gen::AxisId::x,
6364
xSplit.measureRange,
65+
ySplit.measureRange,
6466
tr,
6567
weight);
6668

@@ -79,41 +81,47 @@ void DrawAxes::drawGeometries() const
7981

8082
DrawGuides{*this}.draw(Gen::AxisId::x,
8183
xSplit.measureRange,
84+
ySplit.measureRange,
8285
tr,
8386
weight);
8487
DrawGuides{*this}.draw(Gen::AxisId::y,
8588
ySplit.measureRange,
89+
xSplit.measureRange,
8690
tr,
8791
weight);
8892
}
8993
}
9094

9195
void DrawAxes::drawLabels() const
9296
{
93-
for (auto &&ySplit : std::views::values(splits[Gen::AxisId::y])) {
97+
auto &&yValues = std::views::values(splits[Gen::AxisId::y]);
98+
for (auto &&ySplit : yValues) {
9499
const Geom::AffineTransform tr{1,
95100
0.0,
96-
0,
101+
0.0,
97102
0.0,
98103
ySplit.range.size(),
99104
ySplit.range.min};
100105
DrawInterlacing{*this}.drawTexts(Gen::AxisId::y,
101106
ySplit.measureRange,
102107
tr,
103-
ySplit.weight);
108+
ySplit.weight,
109+
yValues.size() > 1 && !ySplit.unique);
104110
}
105111

106-
for (auto &&xSplit : std::views::values(splits[Gen::AxisId::x])) {
112+
auto &&xValues = std::views::values(splits[Gen::AxisId::x]);
113+
for (auto &&xSplit : xValues) {
107114
const Geom::AffineTransform tr{xSplit.range.size(),
108115
0.0,
109116
xSplit.range.min,
110117
0.0,
111-
1,
112-
0};
118+
1.0,
119+
0.0};
113120
DrawInterlacing{*this}.drawTexts(Gen::AxisId::x,
114121
xSplit.measureRange,
115122
tr,
116-
xSplit.weight);
123+
xSplit.weight,
124+
xValues.size() > 1 && !xSplit.unique);
117125
}
118126

119127
drawDimensionLabels(Gen::AxisId::x);

src/chart/rendering/drawguides.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace Vizzu::Draw
1616
{
1717
void DrawGuides::draw(Gen::AxisId axisId,
1818
const Math::Range<> &filter,
19+
const Math::Range<> &otherFilter,
1920
const Geom::AffineTransform &tr,
2021
double w)
2122
{
@@ -31,6 +32,7 @@ void DrawGuides::draw(Gen::AxisId axisId,
3132
for (const auto &sep : parent.getSeparators(axisId, filter))
3233
drawGuide(axisId,
3334
sep.position,
35+
otherFilter,
3436
tr,
3537
baseColor
3638
* Math::FuzzyBool::And<double>(w,
@@ -43,15 +45,15 @@ void DrawGuides::draw(Gen::AxisId axisId,
4345

4446
void DrawGuides::drawGuide(Gen::AxisId axisId,
4547
double val,
48+
const Math::Range<> &otherFilter,
4649
const Geom::AffineTransform &tr,
4750
const Gfx::Color &color)
4851
{
49-
auto ident = Geom::Point::Ident(orientation(axisId));
50-
auto normal = Geom::Point::Ident(!orientation(axisId));
51-
auto relMax = ident * val;
52-
52+
auto o = orientation(axisId);
5353
parent.canvas.setLineColor(color);
54-
auto line = tr(Geom::Line{relMax, relMax + normal});
54+
auto line =
55+
tr(Geom::Line{Geom::Point::Coord(o, val, otherFilter.min),
56+
Geom::Point::Coord(o, val, otherFilter.max)});
5557
if (auto &&eventTarget = Events::Targets::axisGuide(axisId);
5658
parent.rootEvents.draw.plot.axis.guide->invoke(
5759
Events::OnLineDrawEvent(*eventTarget, {line, true}))) {

src/chart/rendering/drawguides.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class DrawGuides
1212
public:
1313
void draw(Gen::AxisId axisId,
1414
const Math::Range<> &filter,
15+
const Math::Range<> &otherFilter,
1516
const Geom::AffineTransform &tr,
1617
double w);
1718

@@ -20,6 +21,7 @@ class DrawGuides
2021
private:
2122
void drawGuide(Gen::AxisId axisId,
2223
double val,
24+
const Math::Range<> &otherFilter,
2325
const Geom::AffineTransform &tr,
2426
const Gfx::Color &color);
2527
};

src/chart/rendering/drawinterlacing.cpp

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,19 @@ namespace Vizzu::Draw
3030

3131
void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex,
3232
const Math::Range<> &filter,
33+
const Math::Range<> &otherFilter,
3334
const Geom::AffineTransform &tr,
3435
double w) const
3536
{
3637
const auto &guides = parent.plot->guides.at(axisIndex);
3738
const auto &axisStyle = parent.rootStyle.plot.getAxis(axisIndex);
3839
if (axisStyle.interlacing.color->isTransparent()
39-
|| guides.interlacings == false)
40+
|| guides.interlacings == false
41+
|| Math::Floating::is_zero(otherFilter.size()))
4042
return;
4143

42-
auto otherWeights = getInterlacingWeights(!axisIndex);
44+
auto otherWeights =
45+
getInterlacingWeights(!axisIndex, otherFilter);
4346
auto &&otherInterlacingColor =
4447
*parent.rootStyle.plot.getAxis(!axisIndex).interlacing.color;
4548

@@ -101,7 +104,8 @@ void DrawInterlacing::drawGeometries(Gen::AxisId axisIndex,
101104
void DrawInterlacing::drawTexts(Gen::AxisId axisIndex,
102105
const Math::Range<> &filter,
103106
const Geom::AffineTransform &tr,
104-
double w) const
107+
double w,
108+
bool onlyOne) const
105109
{
106110
const auto &axis = parent.getAxis(axisIndex).measure;
107111
auto orientation = !Gen::orientation(axisIndex);
@@ -123,10 +127,45 @@ void DrawInterlacing::drawTexts(Gen::AxisId axisIndex,
123127

124128
if (!needText && !needTick) return;
125129

130+
std::array<std::array<const DrawAxes::Separator *, 2>, 2>
131+
latests{};
132+
133+
if (needText && onlyOne)
134+
for (const auto &sep :
135+
parent.getSeparators(axisIndex, filter)) {
136+
if (!sep.label) continue;
137+
if (auto &&cmp = std::weak_order(*sep.label, 0.0);
138+
!is_eq(cmp)
139+
&& (!latests[is_lt(cmp)][0]
140+
|| std::abs(*latests[is_lt(cmp)][0]->label)
141+
< std::abs(*sep.label))) {
142+
auto &[f, s] = latests[is_lt(cmp)];
143+
if (!s)
144+
s = std::addressof(sep);
145+
else if (std::abs(*s->label) < std::abs(*sep.label)) {
146+
if (Math::AddTolerance(s->weight + sep.weight)
147+
== 1.0)
148+
f = s;
149+
else if (f
150+
&& Math::AddTolerance(
151+
f->weight + sep.weight)
152+
> 1.0)
153+
f = nullptr;
154+
s = std::addressof(sep);
155+
}
156+
else if (Math::AddTolerance(s->weight + sep.weight)
157+
== 1.0)
158+
f = std::addressof(sep);
159+
}
160+
}
161+
126162
for (const auto &sep : parent.getSeparators(axisIndex, filter)) {
127163
auto tickPos =
128164
Geom::Point::Coord(orientation, origo, sep.position);
129-
if (needText && sep.label)
165+
if (needText && sep.label
166+
&& (!onlyOne || &sep == latests[0][0]
167+
|| &sep == latests[0][1] || &sep == latests[1][0]
168+
|| &sep == latests[1][1]))
130169
drawDataLabel(axis.enabled,
131170
axisIndex,
132171
tickPos,
@@ -280,9 +319,11 @@ void DrawInterlacing::drawSticks(double tickLength,
280319
}
281320

282321
std::map<double, double> DrawInterlacing::getInterlacingWeights(
283-
Gen::AxisId axisIndex) const
322+
Gen::AxisId axisIndex,
323+
const Math::Range<> &filter) const
284324
{
285-
std::map<double, double> weights{{0.0, 0.0}, {1.0, 0.0}};
325+
std::map<double, double> weights{{filter.min, 0.0},
326+
{filter.max, 0.0}};
286327

287328
auto &&guides = parent.plot->guides.at(axisIndex);
288329
auto &&axisStyle = parent.rootStyle.plot.getAxis(axisIndex);
@@ -291,9 +332,12 @@ std::map<double, double> DrawInterlacing::getInterlacingWeights(
291332
return weights;
292333

293334
for (auto &&interval : parent.getIntervals(axisIndex)) {
294-
if (Math::Floating::is_zero(interval.isSecond)) continue;
295-
auto min = std::max(interval.range.min, 0.0);
296-
auto max = std::min(interval.range.max, 1.0);
335+
if (Math::Floating::is_zero(interval.isSecond)
336+
|| !interval.range.intersects(filter))
337+
continue;
338+
auto min = std::max(interval.range.min, filter.min);
339+
auto max = std::min(interval.range.max, filter.max);
340+
297341
auto mprev = std::prev(weights.upper_bound(min));
298342
auto mnext = weights.lower_bound(max);
299343

0 commit comments

Comments
 (0)