From a114effe543ca9f61df23c7d450e6e960066f891 Mon Sep 17 00:00:00 2001 From: Bogdan Popa Date: Sat, 4 Nov 2023 22:03:36 +0200 Subject: [PATCH] core: add support for setting chart scales --- FranzCocoa/Backend.swift | 30 +++++++++++------------------- FranzCocoa/ResultDetail.swift | 22 +++++++++++++++++++--- FranzCross/result-detail.rkt | 17 +++++++++++++++-- core/extlib/render.lua | 29 +++++++++++++++++++++++++++++ core/script.rkt | 22 +++++++++++++++++----- manual/index.scrbl | 10 ++++++++++ 6 files changed, 101 insertions(+), 29 deletions(-) diff --git a/FranzCocoa/Backend.swift b/FranzCocoa/Backend.swift index c61621c..f815ba2 100644 --- a/FranzCocoa/Backend.swift +++ b/FranzCocoa/Backend.swift @@ -40,17 +40,12 @@ public enum AuthMechanism: Readable, Writable { } public enum ChartScale: Readable, Writable { - case categorical([String]) case numerical(Float64, Float64, ChartScaleType) public static func read(from inp: InputPort, using buf: inout Data) -> ChartScale { let tag = UVarint.read(from: inp, using: &buf) switch tag { case 0x0000: - return .categorical( - [String].read(from: inp, using: &buf) - ) - case 0x0001: return .numerical( Float64.read(from: inp, using: &buf), Float64.read(from: inp, using: &buf), @@ -63,11 +58,8 @@ public enum ChartScale: Readable, Writable { public func write(to out: OutputPort) { switch self { - case .categorical(let categories): - UVarint(0x0000).write(to: out) - categories.write(to: out) case .numerical(let lo, let hi, let type): - UVarint(0x0001).write(to: out) + UVarint(0x0000).write(to: out) lo.write(to: out) hi.write(to: out) type.write(to: out) @@ -504,27 +496,27 @@ public struct Broker: Readable, Writable { public struct Chart: Readable, Writable { public let style: ChartStyle - public let xDomain: ChartScale? + public let xScale: ChartScale? public let xLabel: String public let xs: [ChartValue] - public let yDomain: ChartScale? + public let yScale: ChartScale? public let yLabel: String public let ys: [ChartValue] public init( style: ChartStyle, - xDomain: ChartScale?, + xScale: ChartScale?, xLabel: String, xs: [ChartValue], - yDomain: ChartScale?, + yScale: ChartScale?, yLabel: String, ys: [ChartValue] ) { self.style = style - self.xDomain = xDomain + self.xScale = xScale self.xLabel = xLabel self.xs = xs - self.yDomain = yDomain + self.yScale = yScale self.yLabel = yLabel self.ys = ys } @@ -532,10 +524,10 @@ public struct Chart: Readable, Writable { public static func read(from inp: InputPort, using buf: inout Data) -> Chart { return Chart( style: ChartStyle.read(from: inp, using: &buf), - xDomain: ChartScale?.read(from: inp, using: &buf), + xScale: ChartScale?.read(from: inp, using: &buf), xLabel: String.read(from: inp, using: &buf), xs: [ChartValue].read(from: inp, using: &buf), - yDomain: ChartScale?.read(from: inp, using: &buf), + yScale: ChartScale?.read(from: inp, using: &buf), yLabel: String.read(from: inp, using: &buf), ys: [ChartValue].read(from: inp, using: &buf) ) @@ -543,10 +535,10 @@ public struct Chart: Readable, Writable { public func write(to out: OutputPort) { style.write(to: out) - xDomain.write(to: out) + xScale.write(to: out) xLabel.write(to: out) xs.write(to: out) - yDomain.write(to: out) + yScale.write(to: out) yLabel.write(to: out) ys.write(to: out) } diff --git a/FranzCocoa/ResultDetail.swift b/FranzCocoa/ResultDetail.swift index 7efdf25..74252b1 100644 --- a/FranzCocoa/ResultDetail.swift +++ b/FranzCocoa/ResultDetail.swift @@ -35,7 +35,13 @@ fileprivate struct ChartResult: View { } var body: some View { - Charts.Chart(pairs(chart.xs, chart.ys)) { p in + AnyView(chartView()) + .padding(.all, 20) + .frame(minWidth: 640, minHeight: 320) + } + + private func chartView() -> any View { + let view = Charts.Chart(pairs(chart.xs, chart.ys)) { p in switch chart.style { case .bar: p.barMark(xLabel: chart.xLabel, yLabel: chart.yLabel) @@ -45,8 +51,18 @@ fileprivate struct ChartResult: View { p.pointMark(xLabel: chart.xLabel, yLabel: chart.yLabel) } } - .padding(.all, 20) - .frame(minWidth: 640, minHeight: 320) + switch (chart.xScale, chart.yScale) { + case (.none, .none): + return view + case (.numerical(let lo, let hi, _), .none): + return view.chartXScale(domain: lo...hi, type: .linear) + case (.none, .numerical(let lo, let hi, _)): + return view.chartYScale(domain: lo...hi, type: .linear) + case (.numerical(let xlo, let xhi, _), .numerical(let ylo, let yhi, _)): + return view + .chartXScale(domain: xlo...xhi, type: .linear) + .chartYScale(domain: ylo...yhi, type: .linear) + } } private struct Pair: Identifiable { diff --git a/FranzCross/result-detail.rkt b/FranzCross/result-detail.rkt index 5c53f02..07272b4 100644 --- a/FranzCross/result-detail.rkt +++ b/FranzCross/result-detail.rkt @@ -25,6 +25,11 @@ [(ReduceResult.text s) (text-view s)])) +(define (ChartScale-> v) + (match v + [#f (values #f #f)] + [(ChartScale.numerical lo hi _) (values lo hi)])) + (define (ChartValue-> v) (match v [(ChartValue.categorical category) category] @@ -34,11 +39,19 @@ (hpanel #:min-size '(640 480) (snip #f (λ (_ width height) + (define-values (x-min x-max) + (ChartScale-> (Chart-x-scale c))) + (define-values (y-min y-max) + (ChartScale-> (Chart-y-scale c))) (apply plot-snip #:width width #:height height + #:x-min x-min + #:x-max x-max #:x-label (Chart-x-label c) + #:y-min y-min + #:y-max y-max #:y-label (Chart-y-label c) (list ((match (Chart-style c) @@ -79,10 +92,10 @@ ['chart (ReduceResult.chart (make-Chart #:style (ChartStyle.bar) - #:x-domain #f + #:x-scale #f #:x-label "x" #:xs (map ChartValue.categorical '("a" "b" "c")) - #:y-domain #f + #:y-scale #f #:y-label "y" #:ys (map ChartValue.numerical '(4 5 6))))] ['number (ReduceResult.number 42)] diff --git a/core/extlib/render.lua b/core/extlib/render.lua index 5032a50..184ee8b 100644 --- a/core/extlib/render.lua +++ b/core/extlib/render.lua @@ -28,11 +28,40 @@ local function makeChartClass(name) return self end + function check_type(who, typ) + if typ ~= nil and + typ ~= "linear" and + typ ~= "log" then + error(who .. ": type must be 'linear' or 'log'") + end + end + + local function make_scale(who, lo, hi, typ) + check_type(who, typ) + return { + lo = lo, + hi = hi, + typ = typ or "linear" + } + end + + function Chart:setxscale(...) + check_type("Chart:setxscale", typ) + self.xscale = make_scale("Chart:setxscale", ...) + return self + end + function Chart:setxs(xs) self.xs = xs return self end + function Chart:setyscale(...) + check_type("Chart:setyscale", typ) + self.yscale = make_scale("Chart:setyscale", ...) + return self + end + function Chart:setys(ys) self.ys = ys return self diff --git a/core/script.rkt b/core/script.rkt index d279627..8eba6bb 100644 --- a/core/script.rkt +++ b/core/script.rkt @@ -34,7 +34,6 @@ [log]) (define-enum ChartScale - [categorical {categories : (Listof String)}] [numerical {lo : Float64} {hi : Float64} @@ -51,10 +50,10 @@ (define-record Chart [style : ChartStyle] - [x-domain : (Optional ChartScale)] + [x-scale : (Optional ChartScale)] [x-label : String] [xs : (Listof ChartValue)] - [y-domain : (Optional ChartScale)] + [y-scale : (Optional ChartScale)] [y-label : String] [ys : (Listof ChartValue)]) @@ -71,6 +70,19 @@ [(output #"") : Bytes] [(reduced #f) : (Optional ReduceResult)]) +(define (->ChartScale v) + (cond + [(nil? v) #f] + [(table? v) + (ChartScale.numerical + (table-ref v #"lo") + (table-ref v #"hi") + (case (table-ref v #"typ") + [(#"linear") (ChartScaleType.linear)] + [(#"log") (ChartScaleType.log)] + [else (error '->ChartScale "invalid scale type")]))] + [else (error '->ChartScale "invalid scale value: ~s" v)])) + (define (->ChartStyle v) (case v [(#"BarChart") (ChartStyle.bar)] @@ -98,10 +110,10 @@ (ReduceResult.chart (make-Chart #:style (->ChartStyle type) - #:x-domain #f + #:x-scale (->ChartScale (table-ref v #"xscale")) #:x-label (table-ref-string v #"xlabel") #:xs (table->list (table-ref v #"xs") ->ChartValue) - #:y-domain #f + #:y-scale (->ChartScale (table-ref v #"yscale")) #:y-label (table-ref-string v #"ylabel") #:ys (table->list (table-ref v #"ys") ->ChartValue)))] [(equal? #"Table") diff --git a/manual/index.scrbl b/manual/index.scrbl index b0d5d55..ab3851b 100644 --- a/manual/index.scrbl +++ b/manual/index.scrbl @@ -841,10 +841,20 @@ See @secref["rendering-a-bar-chart"] for a usage example. numbers, but the types should be internally consistent per axis. } +@deflua[Chart:setxscale (lo hi) Chart]{ + Sets the scale of the x axis. Undefined when the x values are + strings. +} + @deflua[Chart:setxs (xs) Chart]{ Replaces all the x-axis values with @tt{xs}. } +@deflua[Chart:setyscale (lo hi) Chart]{ + Sets the scale of the y axis. Undefined when the y values are + strings. +} + @deflua[Chart:setys (ys) Chart]{ Replaces all the y-axis values with @tt{ys}. }