Skip to content

Commit 76bedca

Browse files
authored
Merge pull request #575 from vizzuhq/Translate_legend
Fix single value range interpolation
2 parents 4107ec3 + 50f3834 commit 76bedca

File tree

13 files changed

+281
-43
lines changed

13 files changed

+281
-43
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Fix axis interpolation. From now the axis and axis labels are following the markers.
1616
- Fix measure axis labels when the range started after the 2000th step value from origo.
1717
- Remove marker labels at intermediate steps.
18+
- Fix single value range interpolation and show.
1819

1920
### Added
2021

src/chart/generator/axis.cpp

+49-7
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,57 @@ MeasureAxis interpolate(const MeasureAxis &op0,
8282
const auto s0 = op0.range.size();
8383
const auto s1 = op1.range.size();
8484

85-
if (auto s0Zero = is_zero(s0); s0Zero || is_zero(s1)) {
85+
if (auto s0Zero = is_zero(s0), s1Zero = is_zero(s1);
86+
s0Zero && s1Zero) {
8687
res.range = Math::Range<double>::Raw(
8788
Math::interpolate(op0.range.getMin(),
8889
op1.range.getMin(),
8990
factor),
9091
Math::interpolate(op0.range.getMax(),
9192
op1.range.getMax(),
9293
factor));
93-
res.step = s0Zero ? op1.step : op0.step;
94+
res.step = interpolate(op0.step, op1.step, factor);
95+
}
96+
else if (s1Zero) {
97+
auto size = factor == 1.0 ? MAX : s0 / (1 - factor);
98+
99+
auto middleAt = Math::interpolate(
100+
op0.range.rescale(op1.range.middle()),
101+
0.0,
102+
factor);
103+
104+
res.range = Math::Range<double>::Raw(
105+
op1.range.middle() - middleAt * size,
106+
op1.range.middle()
107+
+ (factor == 1.0 ? 0.0 : (1 - middleAt) * size));
108+
109+
auto step = op0.step.get() / s0 * size;
110+
auto max = std::copysign(MAX, step);
111+
112+
res.step = interpolate(op0.step,
113+
Anim::Interpolated{max},
114+
Math::Range<double>::Raw(op0.step.get(), max)
115+
.rescale(step));
116+
}
117+
else if (s0Zero) {
118+
auto size = factor == 0.0 ? MAX : s1 / factor;
119+
120+
auto middleAt = Math::interpolate(0.0,
121+
op1.range.rescale(op0.range.middle()),
122+
factor);
123+
124+
res.range = Math::Range<double>::Raw(
125+
op0.range.middle() - middleAt * size,
126+
op0.range.middle()
127+
+ (factor == 0.0 ? 0.0 : (1 - middleAt) * size));
128+
129+
auto step = op1.step.get() / s1 * size;
130+
auto max = std::copysign(MAX, step);
131+
132+
res.step = interpolate(op1.step,
133+
Anim::Interpolated{max},
134+
Math::Range<double>::Raw(op1.step.get(), max)
135+
.rescale(step));
94136
}
95137
else {
96138
auto s0Inv = 1 / s0;
@@ -99,22 +141,22 @@ MeasureAxis interpolate(const MeasureAxis &op0,
99141
const auto interp =
100142
Math::interpolate(s0Inv, s1Inv, factor);
101143

102-
const auto s = is_zero(interp) ? MAX : 1 / interp;
144+
const auto size = is_zero(interp) ? MAX : 1 / interp;
103145

104146
res.range = Math::Range<double>::Raw(
105147
Math::interpolate(op0.range.getMin() * s0Inv,
106148
op1.range.getMin() * s1Inv,
107149
factor)
108-
* s,
150+
* size,
109151
Math::interpolate(op0.range.getMax() * s0Inv,
110152
op1.range.getMax() * s1Inv,
111153
factor)
112-
* s);
154+
* size);
113155

114156
auto step = Math::interpolate(op0.step.get() * s0Inv,
115157
op1.step.get() * s1Inv,
116158
factor)
117-
* s;
159+
* size;
118160

119161
if (auto op0sign = std::signbit(op0.step.get());
120162
op0sign == std::signbit(op1.step.get()))
@@ -166,7 +208,7 @@ bool DimensionAxis::add(const Data::SliceIndex &index,
166208
}
167209
values.emplace(std::piecewise_construct,
168210
std::tuple{index},
169-
std::tuple{range, value, enabled});
211+
std::tuple{range, value});
170212

171213
return true;
172214
}

src/chart/generator/axis.h

+4-6
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,12 @@ struct DimensionAxis
6868
std::string categoryValue;
6969
double weight;
7070

71-
Item(Math::Range<double> range,
72-
double value,
73-
double enabled) :
71+
Item(Math::Range<double> range, double value) :
7472
start(true),
7573
end(true),
7674
range(range),
7775
value(value),
78-
weight(enabled)
76+
weight(1.0)
7977
{}
8078

8179
Item(const Item &item, bool starter, double factor) :
@@ -86,12 +84,12 @@ struct DimensionAxis
8684
colorBase(item.colorBase),
8785
label(item.label),
8886
categoryValue(item.categoryValue),
89-
weight(item.weight * factor)
87+
weight(Math::FuzzyBool::And(item.weight, factor))
9088
{}
9189

9290
bool operator==(const Item &other) const
9391
{
94-
return range == other.range;
92+
return range == other.range && weight == other.weight;
9593
}
9694

9795
[[nodiscard]] bool presentAt(

src/chart/generator/plotbuilder.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -581,11 +581,18 @@ void PlotBuilder::normalizeColors()
581581
Math::Range<double> lightness;
582582
Math::Range<double> color;
583583

584+
bool wasValidMarker{};
584585
for (auto &marker : plot->markers) {
585586
if (!marker.enabled) continue;
586587
auto &&cbase = marker.colorBase.get();
587588
if (!cbase.isDiscrete()) color.include(cbase.getPos());
588589
lightness.include(cbase.getLightness());
590+
wasValidMarker = true;
591+
}
592+
593+
if (!wasValidMarker) {
594+
lightness.include(0.5);
595+
color.include(0);
589596
}
590597

591598
color = plot->getOptions()

src/chart/rendering/drawinterlacing.cpp

+22-18
Original file line numberDiff line numberDiff line change
@@ -133,32 +133,35 @@ void DrawInterlacing::draw(
133133
auto textAlpha =
134134
Math::FuzzyBool::And<double>(weight, enabled.labels);
135135

136-
if (std::signbit(rangeSize) != std::signbit(stepSize)
137-
|| Math::Floating::is_zero(rangeSize))
138-
return;
139-
140-
auto stripWidth = stepSize / rangeSize;
136+
auto singleLabelRange = Math::Floating::is_zero(rangeSize);
137+
138+
double stripWidth{};
139+
if (singleLabelRange)
140+
stepSize = 1.0;
141+
else {
142+
stripWidth = stepSize / rangeSize;
143+
if (stripWidth <= 0) return;
144+
}
141145

142146
auto axisBottom = axis.origo() + stripWidth;
143147

144148
auto iMin =
145-
axisBottom > 0 ? static_cast<int>(
146-
std::floor(-axis.origo() / (2 * stripWidth)))
147-
: static_cast<int>(
148-
(axis.range.getMin() - stepSize) / 2);
149+
axisBottom > 0
150+
? std::floor(-axis.origo() / (2 * stripWidth)) * 2
151+
: std::round(axis.range.getMin() - stepSize);
149152

150-
if (stripWidth <= 0) return;
151153
auto interlaceCount = 0U;
152154
const auto maxInterlaceCount = 1000U;
153-
for (int i = iMin; ++interlaceCount <= maxInterlaceCount;
154-
++i) {
155-
auto bottom = axisBottom + i * 2 * stripWidth;
156-
if (bottom >= 1.0) break;
155+
for (auto i = static_cast<int>(iMin);
156+
++interlaceCount <= maxInterlaceCount;
157+
i += 2) {
158+
auto bottom = axisBottom + i * stripWidth;
159+
if (bottom > 1.0) break;
157160
auto clippedBottom = bottom;
158161
auto top = bottom + stripWidth;
159162
auto clipTop = top > 1.0;
160163
auto clipBottom = bottom < 0.0;
161-
auto topUnderflow = top <= 0.0;
164+
auto topUnderflow = top < 0.0;
162165
if (clipTop) top = 1.0;
163166
if (clipBottom) clippedBottom = 0.0;
164167

@@ -174,7 +177,7 @@ void DrawInterlacing::draw(
174177
canvas.setFont(Gfx::Font{axisStyle.label});
175178

176179
if (!clipBottom) {
177-
auto value = (i * 2 + 1) * stepSize;
180+
auto value = (i + 1) * stepSize;
178181
auto tickPos =
179182
rect.bottomLeft().comp(!horizontal)
180183
+ origo.comp(horizontal);
@@ -192,8 +195,9 @@ void DrawInterlacing::draw(
192195
horizontal,
193196
tickPos);
194197
}
198+
if (singleLabelRange) break;
195199
if (!clipTop) {
196-
auto value = (i * 2 + 2) * stepSize;
200+
auto value = (i + 2) * stepSize;
197201
auto tickPos =
198202
rect.topRight().comp(!horizontal)
199203
+ origo.comp(horizontal);
@@ -212,7 +216,7 @@ void DrawInterlacing::draw(
212216
tickPos);
213217
}
214218
}
215-
else {
219+
else if (!singleLabelRange) {
216220
canvas.save();
217221

218222
canvas.setLineColor(Gfx::Color::Transparent());

src/chart/rendering/drawlegend.cpp

+16-10
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
#include "base/gfx/colortransform.h"
1717
#include "base/gfx/draw/roundedrect.h"
1818
#include "base/gfx/lineargradient.h"
19-
#include "base/math/floating.h"
2019
#include "base/math/fuzzybool.h"
20+
#include "base/math/range.h"
2121
#include "base/text/smartstring.h"
2222
#include "chart/generator/plot.h" // NOLINT(misc-include-cleaner)
2323
#include "chart/main/events.h"
@@ -70,12 +70,14 @@ void DrawLegend::draw(Gfx::ICanvas &canvas,
7070
{1.0, {}},
7171
}}}}};
7272

73-
info.properties.scrollHeight = markersLegendFullSize(info);
73+
auto &&range = markersLegendRange(info);
74+
info.properties.scrollHeight = range.size();
7475
auto yOverflow =
7576
info.properties.scrollHeight - markerWindowHeight;
7677
if (std::signbit(yOverflow)) yOverflow = 0.0;
7778
info.properties.scrollTop =
78-
style.translateY->get(yOverflow, info.itemHeight);
79+
style.translateY->get(yOverflow, info.itemHeight)
80+
+ range.getMin();
7981

8082
DrawBackground{{ctx()}}.draw(canvas,
8183
legendLayout,
@@ -324,17 +326,21 @@ Geom::Rect DrawLegend::getBarRect(const Info &info)
324326
return res;
325327
}
326328

327-
double DrawLegend::markersLegendFullSize(const Info &info)
329+
Math::Range<double> DrawLegend::markersLegendRange(const Info &info)
328330
{
329-
double itemCount{info.measureEnabled <= 0.0 ? 0.0 : 6.0};
331+
Math::Range<double> res;
332+
if (info.measureEnabled > 0.0) {
333+
res.include(0.0);
334+
res.include(6.0 * info.itemHeight);
335+
}
336+
330337
if (info.dimensionEnabled)
331338
for (const auto &value : info.dimension)
332-
if (auto itemPos = value.second.range.getMin() + 1;
333-
value.second.weight > 0
334-
&& Math::Floating::less(itemCount, itemPos))
335-
itemCount = itemPos;
339+
res.include({value.second.range.getMin()
340+
* info.itemHeight,
341+
(value.second.range.getMax() + 1) * info.itemHeight});
336342

337-
return itemCount * info.itemHeight;
343+
return res;
338344
}
339345

340346
void DrawLegend::colorBar(const Info &info,

src/chart/rendering/drawlegend.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class DrawLegend : public DrawingContext
7171
getLabelRect(const Info &info, const Geom::Rect &itemRect);
7272
[[nodiscard]] static Geom::Rect getBarRect(const Info &info);
7373

74-
[[nodiscard]] static double markersLegendFullSize(
74+
[[nodiscard]] static Math::Range<double> markersLegendRange(
7575
const Info &info);
7676

7777
void extremaLabel(const Info &info,

test/e2e/tests/config_tests.json

+12
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@
4848
},
4949
"geometry/animated_area-circle": {
5050
"refs": ["d037f4d"]
51+
},
52+
"channel_ranges/dim_axis": {
53+
"refs": ["0e9a9fd"]
54+
},
55+
"channel_ranges/dim_color": {
56+
"refs": ["c5b912f"]
57+
},
58+
"channel_ranges/dim_lightness": {
59+
"refs": ["b26b94f"]
60+
},
61+
"channel_ranges/meas_axis": {
62+
"refs": ["7e9e5ba"]
5163
}
5264
}
5365
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const testSteps = [
2+
(chart) => {
3+
const data = {
4+
series: [
5+
{ name: 'Foo', values: ['Alice', 'Bob', 'Ted'] },
6+
{ name: 'Bar', values: ['Happy', 'Happy', 'Sad'] },
7+
{ name: 'Baz', values: [1, 2, 3] }
8+
]
9+
}
10+
11+
return chart.animate({ data })
12+
},
13+
(chart) =>
14+
chart.animate({
15+
x: { set: 'Foo' },
16+
y: { set: 'Bar' }
17+
}),
18+
(chart) =>
19+
chart.animate({
20+
x: { range: { min: '100%', max: '0%' } },
21+
y: { range: { min: '100%', max: '0%' } }
22+
}),
23+
(chart) =>
24+
chart.animate({
25+
x: { attach: 'Baz', range: { min: 'auto', max: 'auto' } },
26+
y: { range: { min: '-100%', max: '110%' } },
27+
label: 'Foo',
28+
color: 'Foo'
29+
}),
30+
(chart) =>
31+
chart.animate({
32+
y: { range: { min: '1', max: '1' } }
33+
}),
34+
(chart) =>
35+
chart.animate({
36+
y: { range: { min: '100%', max: '-100%' } }
37+
})
38+
]
39+
40+
export default testSteps

0 commit comments

Comments
 (0)