diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a5323ae..9f3a845cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Remove unused marker selection and selected marker coloring. - Remove cursor modification over logo. - Make `channel.step` option to work on dimensions. +- When X axis dimension labels are close to each other, they are rotated to avoid overlapping. ## [0.9.3] - 2023-12-20 diff --git a/src/apps/qutils/canvas.cpp b/src/apps/qutils/canvas.cpp index c7dbff13d..a4f1f6183 100644 --- a/src/apps/qutils/canvas.cpp +++ b/src/apps/qutils/canvas.cpp @@ -166,25 +166,7 @@ void BaseCanvas::setClipPolygon() void BaseCanvas::setFont(const Gfx::Font &newFont) { - auto font = painter.font(); - font.setPixelSize(static_cast(newFont.size)); - - if (!newFont.family.empty()) - font.setFamily(QString::fromStdString(newFont.family)); - - font.setWeight(newFont.weight == Gfx::Font::Weight::Bold() - ? QFont::Bold - : newFont.weight == Gfx::Font::Weight::Normal() - ? QFont::Normal - : static_cast(newFont.weight) / 10); - - font.setStyle(newFont.style == Gfx::Font::Style::italic - ? QFont::StyleItalic - : newFont.style == Gfx::Font::Style::oblique - ? QFont::StyleOblique - : QFont::StyleNormal); - - painter.setFont(font); + painter.setFont(fromGfxFont(newFont, painter.font())); } void BaseCanvas::setTextColor(const Gfx::Color &color) @@ -253,10 +235,12 @@ QPen BaseCanvas::brushToPen(const QBrush &brush) return pen; } -Geom::Size BaseCanvas::textBoundary(const std::string &text) +Geom::Size Gfx::ICanvas::textBoundary(const Gfx::Font &font, + const std::string &text) { auto res = - QFontMetrics{painter.font()}.boundingRect(QRect(0, 0, 0, 0), + QFontMetrics{BaseCanvas::fromGfxFont(font)}.boundingRect( + QRect(0, 0, 0, 0), Qt::AlignLeft, QString::fromStdString(text)); @@ -280,4 +264,25 @@ void BaseCanvas::transform(const Geom::AffineTransform &transform) void BaseCanvas::save() { painter.save(); } -void BaseCanvas::restore() { painter.restore(); } \ No newline at end of file +void BaseCanvas::restore() { painter.restore(); } + +QFont BaseCanvas::fromGfxFont(const Gfx::Font &newFont, QFont font) +{ + font.setPixelSize(static_cast(newFont.size)); + + if (!newFont.family.empty()) + font.setFamily(QString::fromStdString(newFont.family)); + + font.setWeight(newFont.weight == Gfx::Font::Weight::Bold() + ? QFont::Bold + : newFont.weight == Gfx::Font::Weight::Normal() + ? QFont::Normal + : static_cast(newFont.weight) / 10); + + font.setStyle(newFont.style == Gfx::Font::Style::italic + ? QFont::StyleItalic + : newFont.style == Gfx::Font::Style::oblique + ? QFont::StyleOblique + : QFont::StyleNormal); + return font; +} \ No newline at end of file diff --git a/src/apps/qutils/canvas.h b/src/apps/qutils/canvas.h index d563bf12e..4c3f5307e 100644 --- a/src/apps/qutils/canvas.h +++ b/src/apps/qutils/canvas.h @@ -17,8 +17,6 @@ class BaseCanvas : public Gfx::ICanvas, public Vizzu::Draw::Painter ~BaseCanvas() override; void init(QPaintDevice *device); - Geom::Size textBoundary(const std::string &text) override; - Gfx::ICanvas &getCanvas() override { return *this; } [[nodiscard]] Geom::Rect getClipRect() const override; @@ -65,6 +63,9 @@ class BaseCanvas : public Gfx::ICanvas, public Vizzu::Draw::Painter return static_cast(this); } + [[nodiscard]] static QFont fromGfxFont(const Gfx::Font &newFont, + QFont font = {}); + protected: QPainter painter; QFont font; diff --git a/src/apps/weblib/canvas.yaml b/src/apps/weblib/canvas.yaml index 8cf0a8507..dbf4c28e6 100644 --- a/src/apps/weblib/canvas.yaml +++ b/src/apps/weblib/canvas.yaml @@ -20,7 +20,6 @@ Canvas: rectangle: { x: number, y: number, sizex: number, sizey: number } circle: { x: number, y: number, radius: number } line: { x1: number, y1: number, x2: number, y2: number } - textBoundary: { text: C.CString, sizeX: C.CPointer/double *, sizeY: C.CPointer/double * } text: { x: number, y: number, sizex: number, sizey: number, text: C.CString } setBrushGradient: { diff --git a/src/apps/weblib/interface.js b/src/apps/weblib/interface.js index 67f1e2478..26ac0a794 100644 --- a/src/apps/weblib/interface.js +++ b/src/apps/weblib/interface.js @@ -1,5 +1,15 @@ mergeInto(LibraryManager.library, { openUrl: function (url) { window.open(UTF8ToString(url), '_blank') + }, + textBoundary: function (font, text, sizeX, sizeY) { + const dc = Module.measureCanvas + dc.font = UTF8ToString(font) + let metrics = dc.measureText(UTF8ToString(text)) + const width = metrics.width + metrics = dc.measureText('Op') + const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent + setValue(sizeX, width, 'double') + setValue(sizeY, height, 'double') } }) diff --git a/src/apps/weblib/interfacejs.h b/src/apps/weblib/interfacejs.h index 12f270026..0c037a6c2 100644 --- a/src/apps/weblib/interfacejs.h +++ b/src/apps/weblib/interfacejs.h @@ -3,6 +3,8 @@ extern "C" { extern void openUrl(const char *); +extern void +textBoundary(const char *, const char *, double *, double *); } #endif \ No newline at end of file diff --git a/src/apps/weblib/jscriptcanvas.cpp b/src/apps/weblib/jscriptcanvas.cpp index 0c569bc75..0b5a3c7ca 100644 --- a/src/apps/weblib/jscriptcanvas.cpp +++ b/src/apps/weblib/jscriptcanvas.cpp @@ -1,17 +1,11 @@ #include "jscriptcanvas.h" #include "canvas.h" +#include "interfacejs.h" namespace Vizzu::Main { -Geom::Size JScriptCanvas::textBoundary(const std::string &text) -{ - Geom::Size res; - ::canvas_textBoundary(this, text.c_str(), &res.x, &res.y); - return res; -} - Geom::Rect JScriptCanvas::getClipRect() const { return clipRect ? *clipRect : Geom::Rect::CenteredMax(); @@ -244,4 +238,14 @@ void JScriptCanvas::resetStates() clipRect = std::nullopt; } +} + +Geom::Size Gfx::ICanvas::textBoundary(const Gfx::Font &font, + const std::string &text) +{ + thread_local std::string fontCache; + fontCache = font.toCSS(); + Geom::Size res; + ::textBoundary(fontCache.c_str(), text.c_str(), &res.x, &res.y); + return res; } \ No newline at end of file diff --git a/src/apps/weblib/jscriptcanvas.h b/src/apps/weblib/jscriptcanvas.h index ecaa61330..ccbd4d1bd 100644 --- a/src/apps/weblib/jscriptcanvas.h +++ b/src/apps/weblib/jscriptcanvas.h @@ -16,8 +16,6 @@ class JScriptCanvas : public Gfx::ICanvas, public Draw::Painter JScriptCanvas() = default; ~JScriptCanvas() override = default; - Geom::Size textBoundary(const std::string &text) override; - [[nodiscard]] Geom::Rect getClipRect() const override; void setClipRect(const Geom::Rect &rect) override; void setClipCircle(const Geom::Circle &circle) override; diff --git a/src/apps/weblib/ts-api/cvizzu.types.d.ts b/src/apps/weblib/ts-api/cvizzu.types.d.ts index 8faf0edf0..2ac8c28c2 100644 --- a/src/apps/weblib/ts-api/cvizzu.types.d.ts +++ b/src/apps/weblib/ts-api/cvizzu.types.d.ts @@ -46,6 +46,7 @@ export interface ModuleOptions { export interface CVizzu { // decorations canvases: { [key: CPointer]: Canvas } + measureCanvas: CanvasRenderingContext2D // members HEAPU8: Uint8Array diff --git a/src/apps/weblib/ts-api/module/ccanvas.ts b/src/apps/weblib/ts-api/module/ccanvas.ts index 3f50e6565..deec7aeba 100644 --- a/src/apps/weblib/ts-api/module/ccanvas.ts +++ b/src/apps/weblib/ts-api/module/ccanvas.ts @@ -1,7 +1,7 @@ import { CEnv, CObject } from './cenv.js' import { CPointerClosure } from './objregistry.js' import { CColorGradient } from './ccolorgradient.js' -import { CString, CPointer, CColorGradientPtr } from '../cvizzu.types' +import { CString, CColorGradientPtr } from '../cvizzu.types' export class CCanvas extends CObject { constructor(env: CEnv, getId: CPointerClosure) { @@ -15,8 +15,4 @@ export class CCanvas extends CObject { getString(text: CString): string { return this._wasm.UTF8ToString(text) } - - setNumber(cNumber: CPointer, value: number): void { - this._wasm.setValue(cNumber, value, 'double') - } } diff --git a/src/apps/weblib/ts-api/module/module.ts b/src/apps/weblib/ts-api/module/module.ts index d9c73901e..35793df2a 100644 --- a/src/apps/weblib/ts-api/module/module.ts +++ b/src/apps/weblib/ts-api/module/module.ts @@ -12,6 +12,9 @@ import { Canvas } from './canvas' export class Module extends CEnv { constructor(wasm: CVizzu) { super(wasm, new ObjectRegistry(wasm._object_free)) + const context2D = (document.createElement('canvas')).getContext('2d') + if (!context2D) throw new Error('Failed to get 2D context') + this._wasm.measureCanvas = context2D this._wasm.canvases = {} this.setLogging(false) } diff --git a/src/apps/weblib/ts-api/render.ts b/src/apps/weblib/ts-api/render.ts index 0fc2c7f3f..739838872 100644 --- a/src/apps/weblib/ts-api/render.ts +++ b/src/apps/weblib/ts-api/render.ts @@ -1,4 +1,4 @@ -import { CPointer, CString, CColorGradientPtr } from './cvizzu.types' +import { CString, CColorGradientPtr } from './cvizzu.types' import { Plugin, PluginApi } from './plugins.js' import { Canvas } from './module/canvas.js' import { Module } from './module/module.js' @@ -179,16 +179,6 @@ export class Render implements Plugin, Canvas { if (this._currentLineWidth !== 0) dc.stroke() } - textBoundary(text: CString, sizeX: CPointer, sizeY: CPointer): void { - const dc = this._canvas.context - let metrics = dc.measureText(this._ccanvas.getString(text)) - const width = metrics.width - metrics = dc.measureText('Op') - const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent - this._ccanvas.setNumber(sizeX, width) - this._ccanvas.setNumber(sizeY, height) - } - text(x: number, y: number, sizex: number, sizey: number, text: CString): void { const dc = this._canvas.context dc.textAlign = 'left' diff --git a/src/base/gfx/canvas.h b/src/base/gfx/canvas.h index 6b82e1764..fbc060db6 100644 --- a/src/base/gfx/canvas.h +++ b/src/base/gfx/canvas.h @@ -23,7 +23,6 @@ struct ICanvas { virtual ~ICanvas() = default; - virtual Geom::Size textBoundary(const std::string &string) = 0; [[nodiscard]] virtual Geom::Rect getClipRect() const = 0; virtual void setClipRect(const Geom::Rect &rect) = 0; virtual void setClipCircle(const Geom::Circle &circle) = 0; @@ -65,6 +64,9 @@ struct ICanvas virtual void frameEnd() = 0; virtual void *getPainter() = 0; + + static Geom::Size textBoundary(const Gfx::Font &, + const std::string &); }; } diff --git a/src/base/gfx/draw/textbox.cpp b/src/base/gfx/draw/textbox.cpp index ec1a2bfb6..717314c7f 100644 --- a/src/base/gfx/draw/textbox.cpp +++ b/src/base/gfx/draw/textbox.cpp @@ -164,7 +164,8 @@ Geom::Size TextBox::measure(ICanvas &canvas) line.height = 0; for (auto &text : line.texts) { canvas.setFont(text.font); - auto size = canvas.textBoundary(text.content); + auto size = + ICanvas::textBoundary(text.font, text.content); text.width = size.x; line.width += size.x; if (size.y > line.height) line.height = size.y; diff --git a/src/base/math/range.h b/src/base/math/range.h index 7a15908ef..9b122bccb 100644 --- a/src/base/math/range.h +++ b/src/base/math/range.h @@ -63,6 +63,11 @@ template struct Range return value >= min && value <= max; } + [[nodiscard]] bool includes(const Range &range) const + { + return range.max >= min && range.min <= max; + } + [[nodiscard]] T rescale(const T &value) const { return max == min ? 0.5 : (value - min) / size(); diff --git a/src/base/style/sheet.h b/src/base/style/sheet.h index da718e3b3..9abed5036 100644 --- a/src/base/style/sheet.h +++ b/src/base/style/sheet.h @@ -32,20 +32,6 @@ template class Sheet return Style::ParamRegistry::instance().listParams(); } - void setParamDefault(const std::string &path, - const std::string &value) - { - setParam(defaultParams, path, value); - } - - void setParam(const std::string &path, const std::string &value) - { - if (!activeParams) - throw std::logic_error("no active parameters set"); - - setParam(*activeParams, path, value); - } - void setParams(const std::string &path, const std::string &value) { if (!activeParams) @@ -54,28 +40,6 @@ template class Sheet setParams(*activeParams, path, value); } - static bool hasParam(const std::string &path) - { - return Style::ParamRegistry::instance().hasParam( - path); - } - - static void setParam(Params ¶ms, - const std::string &path, - const std::string &value) - { - if (!hasParam(path)) - throw std::logic_error( - path + "/" + value - + ": non-existent style parameter"); - - Style::ParamRegistry::instance().visit(path, - [&](auto &p) - { - p.fromString(params, value); - }); - } - static void setParams(Params ¶ms, const std::string &path, const std::string &value) @@ -99,7 +63,7 @@ template class Sheet + ": non-existent style parameter"); } - static std::string getParam(Params ¶ms, + [[nodiscard]] static std::string getParam(const Params ¶ms, const std::string &path) { auto ¶mReg = Style::ParamRegistry::instance(); diff --git a/src/chart/animator/animator.cpp b/src/chart/animator/animator.cpp index 97b29aa93..17e984f95 100644 --- a/src/chart/animator/animator.cpp +++ b/src/chart/animator/animator.cpp @@ -40,8 +40,7 @@ void Animator::animate(const ::Anim::Control::Option &options, running = true; stripActAnimation(); - actAnimation = nextAnimation; - nextAnimation = AnimationPtr(); + actAnimation = std::exchange(nextAnimation, {}); setupActAnimation(); actAnimation->animate(options, completionCallback); } diff --git a/src/chart/generator/channelstats.cpp b/src/chart/generator/channelstats.cpp index 13d19ed8c..6ae22d441 100644 --- a/src/chart/generator/channelstats.cpp +++ b/src/chart/generator/channelstats.cpp @@ -22,15 +22,6 @@ void ChannelStats::track(double value) range.include(value); } -void ChannelStats::trackSingle(double value) -{ - if (isDimension) - throw std::logic_error( - "internal error: invalid dimension channel tracking"); - - sum += value; -} - void ChannelStats::track(const Marker::Id &id) { if (isDimension) diff --git a/src/chart/generator/channelstats.h b/src/chart/generator/channelstats.h index 9e43c0dcd..8a5f98b38 100644 --- a/src/chart/generator/channelstats.h +++ b/src/chart/generator/channelstats.h @@ -17,14 +17,12 @@ class ChannelStats public: bool isDimension; Math::Range range; - double sum{0.0}; std::vector usedIndices; ChannelStats() : isDimension(true) {} ChannelStats(const Channel &channel, const Data::DataCube &cube); void track(double value); - void trackSingle(double value); void track(const Marker::Id &id); }; diff --git a/src/chart/generator/marker.cpp b/src/chart/generator/marker.cpp index 3c4f2d94f..27b8a3af4 100644 --- a/src/chart/generator/marker.cpp +++ b/src/chart/generator/marker.cpp @@ -220,10 +220,7 @@ double Marker::getValueForChannel(const Channels &channels, else value = singlevalue; - if (enabled) { - if (measure) stat.trackSingle(singlevalue); - stat.track(value); - } + if (enabled) { stat.track(value); } } return value; } diff --git a/src/chart/main/chart.cpp b/src/chart/main/chart.cpp index dc3f406f4..348164c81 100644 --- a/src/chart/main/chart.cpp +++ b/src/chart/main/chart.cpp @@ -9,7 +9,6 @@ namespace Vizzu Chart::Chart() : animator(std::make_shared()), stylesheet(Styles::Chart::def()), - computedStyles(stylesheet.getDefaultParams()), events(getEventDispatcher()) { stylesheet.setActiveParams(actStyles); @@ -66,7 +65,6 @@ void Chart::animate(const OnComplete &onComplete) else { *nextOptions = prevOptions; actStyles = prevStyles; - computedStyles = plot->getStyle(); } if (onComplete) onComplete(ok); }; @@ -120,13 +118,20 @@ Gen::PlotPtr Chart::plot(const Gen::PlotOptionsPtr &options) { options->setAutoParameters(); - computedStyles = - stylesheet.getFullParams(options, layout.boundary.size); - - return std::make_shared(table, + auto res = std::make_shared(table, options, - computedStyles, + stylesheet.getFullParams(options, layout.boundary.size), false); + + Styles::Sheet::setAfterStyles(*res, layout.boundary.size); + + return res; +} + +const Styles::Chart &Chart::getComputedStyles() const +{ + return actPlot ? actPlot->getStyle() + : stylesheet.getDefaultParams(); } } \ No newline at end of file diff --git a/src/chart/main/chart.h b/src/chart/main/chart.h index d693b1fbd..20c4a9193 100644 --- a/src/chart/main/chart.h +++ b/src/chart/main/chart.h @@ -37,7 +37,9 @@ class Chart Gen::OptionsSetter getSetter(); Styles::Sheet &getStylesheet() { return stylesheet; } Styles::Chart &getStyles() { return actStyles; } - Styles::Chart &getComputedStyles() { return computedStyles; } + + [[nodiscard]] const Styles::Chart &getComputedStyles() const; + void setStyles(const Styles::Chart &styles) { actStyles = styles; @@ -88,7 +90,6 @@ class Chart Styles::Sheet stylesheet; Styles::Chart actStyles; Styles::Chart prevStyles; - Styles::Chart computedStyles; Util::EventDispatcher eventDispatcher; Draw::RenderedChart renderedChart; Events events; diff --git a/src/chart/main/layout.h b/src/chart/main/layout.h index 56ccaa382..c4e31cc9a 100644 --- a/src/chart/main/layout.h +++ b/src/chart/main/layout.h @@ -20,8 +20,6 @@ class Layout Geom::Rect legend; Geom::Rect plot; Geom::Rect plotArea; - Geom::Rect xTitle; - Geom::Rect yTitle; Geom::Rect logo; void setBoundary(const Geom::Rect &boundary, diff --git a/src/chart/main/stylesheet.cpp b/src/chart/main/stylesheet.cpp index 912a3321a..10022cd2f 100644 --- a/src/chart/main/stylesheet.cpp +++ b/src/chart/main/stylesheet.cpp @@ -3,6 +3,9 @@ #include #include "base/style/impl.tpp" +#include "chart/generator/plot.h" + +#include "layout.h" template Style::ParamRegistry::ParamRegistry(); @@ -81,11 +84,16 @@ void Sheet::setAxis() void Sheet::setAxisLabels() { + auto &def = defaultParams.plot.xAxis.label; if (options->coordSystem.get() == Gen::CoordSystem::polar) { - auto &def = defaultParams.plot.xAxis.label; def.position = AxisLabel::Position::max_edge; def.side = AxisLabel::Side::positive; } + else if (const auto &xAxis = + options->getChannels().at(Gen::ChannelId::x); + !xAxis.isEmpty() && xAxis.isDimension() + && options->angle == 0) + def.angle.reset(); } void Sheet::setAxisTitle() @@ -174,4 +182,57 @@ void Sheet::setData() : 0.006; } +void Sheet::setAfterStyles(Gen::Plot &plot, const Geom::Size &size) +{ + auto &style = plot.getStyle(); + style.setup(); + + if (auto &xLabel = style.plot.xAxis.label; !xLabel.angle) { + auto plotX = size.x; + + auto em = style.calculatedSize(); + if (plot.getOptions()->legend.get()) + plotX -= style.legend.computedWidth(plotX, em); + + plotX -= style.plot.toMargin({plotX, 0}, em).getSpace().x; + + auto font = Gfx::Font{xLabel}; + + std::vector> ranges; + bool has_collision = false; + for (const auto &pair : + plot.dimensionAxises.at(Gen::ChannelId::x)) { + + if (pair.second.weight == 0) continue; + + auto textBoundary = Gfx::ICanvas::textBoundary(font, + pair.second.label.get()); + auto textXHalfMargin = + xLabel.toMargin(textBoundary, font.size).getSpace().x + / 2.0; + auto xHalfSize = + (textBoundary.x + textXHalfMargin) / plotX / 2.0; + + auto rangeCenter = pair.second.range.middle(); + + auto next_range = + Math::Range{rangeCenter - xHalfSize, + rangeCenter + xHalfSize}; + + if (std::any_of(ranges.begin(), + ranges.end(), + [&next_range](const Math::Range &other) + { + return other.includes(next_range); + })) { + has_collision = true; + break; + } + ranges.push_back(next_range); + } + + xLabel.angle.emplace(has_collision * M_PI / 4); + } +} + } \ No newline at end of file diff --git a/src/chart/main/stylesheet.h b/src/chart/main/stylesheet.h index bae269c28..e09564daa 100644 --- a/src/chart/main/stylesheet.h +++ b/src/chart/main/stylesheet.h @@ -6,6 +6,11 @@ #include "style.h" +namespace Vizzu::Gen +{ +class Plot; +} + namespace Vizzu::Styles { @@ -20,6 +25,9 @@ class Sheet : public Style::Sheet static double baseFontSize(const Geom::Size &size, bool rounded); + static void setAfterStyles(Gen::Plot &plot, + const Geom::Size &size); + private: using Base::getFullParams; diff --git a/src/chart/rendering/drawaxes.cpp b/src/chart/rendering/drawaxes.cpp index 1526faec4..f8fb1a3d7 100644 --- a/src/chart/rendering/drawaxes.cpp +++ b/src/chart/rendering/drawaxes.cpp @@ -172,7 +172,8 @@ void DrawAxes::drawTitle(Gen::ChannelId axisIndex) const const Gfx::Font font(titleStyle); canvas.setFont(font); - auto textBoundary = canvas.textBoundary(title.value); + auto textBoundary = + Gfx::ICanvas::textBoundary(font, title.value); auto textMargin = titleStyle.toMargin(textBoundary, font.size); auto size = textBoundary + textMargin.getSpace(); diff --git a/src/chart/rendering/drawlabel.cpp b/src/chart/rendering/drawlabel.cpp index c3ff75e11..350120637 100644 --- a/src/chart/rendering/drawlabel.cpp +++ b/src/chart/rendering/drawlabel.cpp @@ -27,11 +27,12 @@ void DrawLabel::draw(Gfx::ICanvas &canvas, auto contentRect = style.contentRect(relRect, style.calculatedSize()); - canvas.setFont(Gfx::Font{style}); + auto font = Gfx::Font{style}; + canvas.setFont(font); if (options.setColor) canvas.setTextColor(*style.color * options.alpha); - auto textSize = canvas.textBoundary(text); + auto textSize = Gfx::ICanvas::textBoundary(font, text); auto alignSize = textSize; alignSize.x = std::min(alignSize.x, contentRect.size.x); auto textRect = alignText(contentRect, style, alignSize); @@ -65,7 +66,7 @@ double DrawLabel::getHeight(const Styles::Label &style, { const Gfx::Font font(style); canvas.setFont(font); - auto textHeight = canvas.textBoundary("").y; + auto textHeight = Gfx::ICanvas::textBoundary(font, "").y; return style.paddingTop->get(textHeight, font.size) + style.paddingBottom->get(textHeight, font.size) + textHeight; diff --git a/src/chart/rendering/orientedlabel.cpp b/src/chart/rendering/orientedlabel.cpp index 61d211a24..7c3a44a37 100644 --- a/src/chart/rendering/orientedlabel.cpp +++ b/src/chart/rendering/orientedlabel.cpp @@ -16,7 +16,7 @@ OrientedLabel OrientedLabel::create(Gfx::ICanvas &canvas, const Gfx::Font font(labelStyle); canvas.setFont(font); - auto neededSize = canvas.textBoundary(text); + auto neededSize = Gfx::ICanvas::textBoundary(font, text); auto margin = labelStyle.toMargin(neededSize, font.size); auto paddedSize = neededSize + margin.getSpace(); diff --git a/test/e2e/test_cases/test_cases.json b/test/e2e/test_cases/test_cases.json index bc55229b8..3a60ebab6 100644 --- a/test/e2e/test_cases/test_cases.json +++ b/test/e2e/test_cases/test_cases.json @@ -185,13 +185,13 @@ "refs": ["42fb179"] }, "data_fault_and_formats/column_rectangle_less_disc": { - "refs": ["db55dd3"] + "refs": ["f93ac85"] }, "data_fault_and_formats/column_rectangle_more_conti": { "refs": ["c2c2362"] }, "data_fault_and_formats/column_rectangle_more_disc": { - "refs": ["2d933e7"] + "refs": ["ff2afa6"] }, "data_fault_and_formats/rectangle_data_cube": { "refs": ["4b66407"] @@ -317,7 +317,7 @@ "refs": ["ab5615c"] }, "shorthands/column_shorthands": { - "refs": ["00fe9df"] + "refs": ["70affdf"] }, "static_chart_types/cartesian_coo_sys/area_1dis_1con": { "refs": ["4454f52"] @@ -338,7 +338,7 @@ "refs": ["795ad09"] }, "static_chart_types/cartesian_coo_sys/column_stacked_rectangle_negative_2dis_1con": { - "refs": ["cd2b76f"] + "refs": ["b4aaf20"] }, "static_chart_types/cartesian_coo_sys/column_stacked_rectangle_negative_3dis_1con": { "refs": ["c71a56a"] @@ -506,7 +506,7 @@ "refs": ["03a5cfe"] }, "web_content/analytical_operations/compare/stream_stacked": { - "refs": ["f7f0fde"] + "refs": ["13a4f7f"] }, "web_content/analytical_operations/compare/waterfall": { "refs": ["16a3b23"] @@ -551,7 +551,7 @@ "refs": ["17b70ff"] }, "web_content/analytical_operations/distribute/newmeasure_dotplot_1": { - "refs": ["56f8887"] + "refs": ["477b699"] }, "web_content/analytical_operations/distribute/newmeasure_dotplot_2": { "refs": ["7d277a4"] @@ -620,7 +620,7 @@ "refs": ["669fa0b"] }, "web_content/analytical_operations/filter/stream_1": { - "refs": ["8d33040"] + "refs": ["9d5e039"] }, "web_content/analytical_operations/filter/stream_2": { "refs": ["42eb797"] @@ -860,7 +860,7 @@ "refs": ["e104842"] }, "web_content/infinite": { - "refs": ["f9fb8da"] + "refs": ["0336dc0"] }, "web_content/presets/chart/column": { "refs": ["6ba52a1"] @@ -1763,7 +1763,7 @@ "refs": ["705aa0e"] }, "ww_next_steps/next_steps/21_C_C_dotplot": { - "refs": ["c991435"] + "refs": ["2904e90"] }, "ww_next_steps/next_steps/22_C_C": { "refs": ["ec4da6c"] @@ -1772,7 +1772,7 @@ "refs": ["f396191"] }, "ww_next_steps/next_steps/35_C_A_violin": { - "refs": ["bdce840"] + "refs": ["25d77e1"] }, "ww_next_steps/next_steps/38_C_L_line": { "refs": ["a08c7c4"] @@ -1793,7 +1793,7 @@ "refs": ["80c5969"] }, "ww_next_steps/next_steps_Tests/21_C_C_dotplot": { - "refs": ["3551f7a"] + "refs": ["54295f5"] }, "ww_next_steps/next_steps_Tests/22_C_C": { "refs": ["81fcf00"] @@ -1871,7 +1871,7 @@ "refs": ["d5720b5"] }, "ww_next_steps/next_steps_byOperations/distribute/distribution_06": { - "refs": ["ad24e8f"] + "refs": ["4ef6a21"] }, "ww_next_steps/next_steps_byOperations/distribute/distribution_07": { "refs": ["f677f00"] @@ -3011,7 +3011,7 @@ "refs": ["508383a"] }, "ww_samples_for_presets/cartesian_coo_sys/12_C_R_matrix_chart": { - "refs": ["066f42f"] + "refs": ["02c960f"] }, "ww_samples_for_presets/cartesian_coo_sys/13_C_R_bar_chart_negative": { "refs": ["2fd66d1"] @@ -3038,7 +3038,7 @@ "refs": ["25c68b8"] }, "ww_samples_for_presets/cartesian_coo_sys/21_C_C_dot_plot_chart": { - "refs": ["714bb74"] + "refs": ["1466b36"] }, "ww_samples_for_presets/cartesian_coo_sys/22_C_C_scatter_plot": { "refs": ["7a79697"] @@ -3203,7 +3203,7 @@ "refs": ["810d76a"] }, "web_content/cookbook/chart_types/historgram": { - "refs": ["b048452"] + "refs": ["18079a9"] }, "web_content/cookbook/chart_types/network_graph": { "refs": ["ae6af16"] diff --git a/test/e2e/test_cases/web_content/analytical_operations/compare/stream_stacked.mjs b/test/e2e/test_cases/web_content/analytical_operations/compare/stream_stacked.mjs index 24601e5de..018951abb 100755 --- a/test/e2e/test_cases/web_content/analytical_operations/compare/stream_stacked.mjs +++ b/test/e2e/test_cases/web_content/analytical_operations/compare/stream_stacked.mjs @@ -36,6 +36,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { label: { numberScale: 'K, M, B, T' @@ -62,6 +67,15 @@ const testSteps = [ }, split: false + }, + style: { + plot: { + xAxis: { + label: { + angle: null + } + } + } } }), (chart) => { diff --git a/test/e2e/test_cases/web_content/analytical_operations/filter/stream_1.mjs b/test/e2e/test_cases/web_content/analytical_operations/filter/stream_1.mjs index 208c68dda..d919ef5f9 100755 --- a/test/e2e/test_cases/web_content/analytical_operations/filter/stream_1.mjs +++ b/test/e2e/test_cases/web_content/analytical_operations/filter/stream_1.mjs @@ -54,7 +54,16 @@ const testSteps = [ record.Format === 'Vinyl') && record.Year <= 1999 }, - config: {} + config: {}, + style: { + plot: { + xAxis: { + label: { + angle: 0 + } + } + } + } }), (chart) => diff --git a/test/e2e/test_cases/web_content/analytical_operations/filter/stream_2.mjs b/test/e2e/test_cases/web_content/analytical_operations/filter/stream_2.mjs index 3fec7fa13..9bf9c79b1 100755 --- a/test/e2e/test_cases/web_content/analytical_operations/filter/stream_2.mjs +++ b/test/e2e/test_cases/web_content/analytical_operations/filter/stream_2.mjs @@ -29,6 +29,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { label: { numberScale: 'K, M, B, T' diff --git a/test/e2e/test_cases/web_content/analytical_operations/sum/stream_stacked.mjs b/test/e2e/test_cases/web_content/analytical_operations/sum/stream_stacked.mjs index 08a0a3700..6c3c774d9 100755 --- a/test/e2e/test_cases/web_content/analytical_operations/sum/stream_stacked.mjs +++ b/test/e2e/test_cases/web_content/analytical_operations/sum/stream_stacked.mjs @@ -37,6 +37,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { label: { numberScale: 'K, M, B, T' @@ -66,6 +71,15 @@ const testSteps = [ split: false, sort: 'byValue', reverse: true + }, + style: { + plot: { + xAxis: { + label: { + angle: null + } + } + } } }), (chart) => { diff --git a/test/e2e/test_cases/web_content/cookbook/rendering/sparse_axis_labels.mjs b/test/e2e/test_cases/web_content/cookbook/rendering/sparse_axis_labels.mjs index 6e5683f22..e916e655d 100755 --- a/test/e2e/test_cases/web_content/cookbook/rendering/sparse_axis_labels.mjs +++ b/test/e2e/test_cases/web_content/cookbook/rendering/sparse_axis_labels.mjs @@ -15,6 +15,15 @@ const testSteps = [ y: 'Revenue', x: { set: 'Year', ticks: true }, title: 'Every 5th label shown on X' + }, + style: { + plot: { + xAxis: { + label: { + angle: 0 + } + } + } } }) } diff --git a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/32_C_A_stream_graph.mjs b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/32_C_A_stream_graph.mjs index 3cb49266c..fa729acfe 100755 --- a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/32_C_A_stream_graph.mjs +++ b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/32_C_A_stream_graph.mjs @@ -20,6 +20,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { interlacing: { color: '#ffffff00' }, label: { numberScale: 'K, M, B, T' } diff --git a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/34_C_A_violin_graph.mjs b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/34_C_A_violin_graph.mjs index 906d88d43..fc2b68571 100755 --- a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/34_C_A_violin_graph.mjs +++ b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/34_C_A_violin_graph.mjs @@ -21,6 +21,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { interlacing: { color: '#ffffff00' }, label: { numberScale: 'K, M, B, T' } diff --git a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/36_C_A_range_area_chart.mjs b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/36_C_A_range_area_chart.mjs index a2b88a8a1..561039a61 100755 --- a/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/36_C_A_range_area_chart.mjs +++ b/test/e2e/test_cases/ww_samples_for_presets/cartesian_coo_sys/36_C_A_range_area_chart.mjs @@ -21,6 +21,11 @@ const testSteps = [ }, style: { plot: { + xAxis: { + label: { + angle: 0 + } + }, yAxis: { label: { numberScale: 'K, M, B, T' diff --git a/test/e2e/test_cases/ww_samples_for_presets/without_coo_sys/602_W_R_heatmap3.mjs b/test/e2e/test_cases/ww_samples_for_presets/without_coo_sys/602_W_R_heatmap3.mjs index 71c10876c..1e82b2eae 100755 --- a/test/e2e/test_cases/ww_samples_for_presets/without_coo_sys/602_W_R_heatmap3.mjs +++ b/test/e2e/test_cases/ww_samples_for_presets/without_coo_sys/602_W_R_heatmap3.mjs @@ -15,6 +15,15 @@ const testSteps = [ lightness: 'Revenue' }, title: 'Heatmap2' + }, + style: { + plot: { + xAxis: { + label: { + angle: 0 + } + } + } } }) }, diff --git a/test/e2e/tests/config_tests.json b/test/e2e/tests/config_tests.json index 62b1b65bb..658815ced 100644 --- a/test/e2e/tests/config_tests.json +++ b/test/e2e/tests/config_tests.json @@ -45,6 +45,9 @@ }, "dimension_axis_density": { "refs": ["9b330fb"] + }, + "x_axis_label_autorotate": { + "refs": ["1dc0fcf"] } } } diff --git a/test/e2e/tests/config_tests/x_axis_label_autorotate.mjs b/test/e2e/tests/config_tests/x_axis_label_autorotate.mjs new file mode 100644 index 000000000..72965f483 --- /dev/null +++ b/test/e2e/tests/config_tests/x_axis_label_autorotate.mjs @@ -0,0 +1,41 @@ +const series = [ + { + name: 'Y', + type: 'measure', + values: [1, 2] + }, + { + name: 'X', + type: 'dimension', + values: ['|', '|'] + } +] + +const testSteps = [ + (chart) => + chart.animate({ + data: { + series + } + }) +] + +for (let i = 95; i <= 105; i++) { + series.push({ + name: 'X' + i, + type: 'dimension', + values: ['|'.repeat(i), '|'.repeat(i + 1)] + }) + testSteps.push((chart) => + chart.animate( + { + x: ['X' + i, 'X'], + y: 'Y', + title: 'Count of |: ' + i + }, + { regroupStrategy: 'drilldown', duration: 0.5 } + ) + ) +} + +export default testSteps diff --git a/test/qtest/window.cpp b/test/qtest/window.cpp index 7d3fba481..f3633b792 100644 --- a/test/qtest/window.cpp +++ b/test/qtest/window.cpp @@ -99,4 +99,13 @@ bool Window::eventFilter(QObject *, QEvent *event) printf("Unknown exception thrown at mouse event.\n"); } return false; -} \ No newline at end of file +} +void Window::resizeEvent(QResizeEvent *) +{ + try { + chart.getChart().getChart().setKeyframe(); + chart.getChart().getChart().animate(); + } + catch (...) { + } +} diff --git a/test/qtest/window.h b/test/qtest/window.h index 101d8cb0f..12e239ac0 100644 --- a/test/qtest/window.h +++ b/test/qtest/window.h @@ -19,6 +19,8 @@ class Window : public QMainWindow ~Window() override; void paintEvent(QPaintEvent *) override; + void resizeEvent(QResizeEvent *) override; + private: TestChart chart; std::unique_ptr ui;