diff --git a/src/plot.typ b/src/plot.typ index c1e5b98..7c0fcf9 100644 --- a/src/plot.typ +++ b/src/plot.typ @@ -7,6 +7,7 @@ #import "/src/spine.typ" #import "/src/ticks.typ" #import "/src/sub-plot.typ" +#import "/src/compat.typ" #import "/src/plot/sample.typ": sample-fn, sample-fn2 #import "/src/plot/line.typ": add, add-hline, add-vline, add-fill-between @@ -256,18 +257,24 @@ ) if template != none and template in templates { - body += (templates.at(template))(ptx) + body = (templates.at(template))(ptx) + body } + // Wrap old style elements + body = body.map(elem => { + return if "type" in elem { + compat.wrap(elem) + } else { + elem + } + }) + let plot-elements = body .filter(elem => type(elem) == dictionary) .sorted(key: elem => elem.at("priority", default: 0)) let cetz-elements = body .filter(elem => type(elem) == function) - // Create axes - //ptx = plot-util.create-axes(ptx, plot-elements, options.named()) - for elem in plot-elements.filter(elem => elem.priority <= 0) { assert("fn" in elem, message: "Invalid plot element: " + repr(elem)) @@ -353,11 +360,13 @@ if ptx.legend != none { draw.scope({ + /* draw.set-origin("plot." + options.at("legend", default: "north-east")) draw.group(name: "legend", anchor: options.at("legend-anchor", default: "north-west"), { draw.anchor("default", (0,0)) draw-legend(ptx) }) + */ }) } @@ -400,6 +409,7 @@ for plot in ptx.plots { for proj in plot.projections { if axes.all(name => proj.axes.contains(name)) { + // FIXME: Broken let pt = (proj.transform)(position).first() ptx.anchors.push((name, pt)) } diff --git a/src/plot/legend.typ b/src/plot/legend.typ index 859504b..03091ca 100644 --- a/src/plot/legend.typ +++ b/src/plot/legend.typ @@ -243,5 +243,5 @@ // TODO: Stub #let draw-legend(ptx) = { - draw.rect((0,0), (1,1)) + //draw.rect((0,0), (1,1)) } diff --git a/src/plot/util.typ b/src/plot/util.typ index de28264..faf9895 100644 --- a/src/plot/util.typ +++ b/src/plot/util.typ @@ -247,7 +247,7 @@ (-float.inf, float.inf) } - clipped-paths(points, (x.min, y.min), (x.max, y.max), fill: true) + clipped-paths(points, (x-min, y-min), (x-max, y-max), fill: true) } /// Return points of a sampled catmull-rom through the @@ -328,34 +328,6 @@ return pts } -// Get the default axis orientation -// depending on the axis name -#let get-default-axis-horizontal(name) = { - return lower(name).starts-with("x") -} - -// Create axes specified by options -#let create-axes(ptx, elements, options) = { - import "/src/axis.typ" - for element in elements { - if "axes" in element { - for name in element.axes { - if not name in ptx.axes { - let mode = options.at(name + "-mode", default: "lin") - - ptx.axes.insert(name, if mode == "log" { - axis.logarithmic(name, none, none, 10) - } else { - axis.linear(name, none, none) - }) - } - } - } - } - - return ptx -} - // Setup axes dictionary // // - axis-dict (dictionary): Existing axis dictionary @@ -370,19 +342,6 @@ if v == auto { default } else { v } } - // Mode switching - for (name, ax) in axes { - let mode = get-opt(name, "mode", "lin") - if mode == "lin" { - ax.transform = axis._transform-lin - } else if mode == "log" { - ax.transform = axis._transform-log - ax.base = get-opt(name, "base", ax.at("base", default: 10)) - } else { - panic("Invalid axis mode: " + repr(mode)) - } - } - for (name, ax) in axes { ax.min = get-opt(name, "min", ax.min) ax.max = get-opt(name, "max", ax.max) @@ -395,6 +354,10 @@ ax.ticks.minor-step = get-opt(name, "minor-tick-step", ax.ticks.minor-step) ax.grid = get-opt(name, "grid", ax.grid) + if get-opt(name, "mode", none) != none { + panic("Mode switching is no longer supported. Use log-axis/lin-axis to create the axis.") + } + axes.at(name) = axis.prepare(ptx, ax) } diff --git a/src/ticks.typ b/src/ticks.typ index 9e3f380..e71a489 100644 --- a/src/ticks.typ +++ b/src/ticks.typ @@ -69,6 +69,10 @@ let num-negative = int((0 - min) / step) let num-positive = int((max - 0) / step) + if num-negative + num-positive > limit { + panic("Tick limit reached! Adjust axis ticks.limit or ticks.minor-limit.") + } + return range(-num-negative, num-positive + 1).map(t => { t * step }) @@ -92,12 +96,20 @@ /// - ax (axis): Axis /// -> List of ticks #let compute-logarithmic-ticks(ax) = { - let min = calc.log(calc.max(axis.min, util.float-epsilon), base: ax.base) - let max = calc.log(calc.max(axis.max, util.float-epsilon), base: ax.base) + let min = calc.log(calc.max(ax.min, util.float-epsilon), base: ax.base) + let max = calc.log(calc.max(ax.max, util.float-epsilon), base: ax.base) let compute-list(min, max, step, limit) = { + if step == none or step <= 0 or min == none or max == none { + return () + } + let num-positive = int((max - 0) / step) + if num-positive > limit { + panic("Tick limit reached! Adjust axis ticks.limit or ticks.minor-limit.") + } + // TODO return () diff --git a/tests/axes/log-mode/test.typ b/tests/axes/log-mode/test.typ index 0860810..37bcdaf 100644 --- a/tests/axes/log-mode/test.typ +++ b/tests/axes/log-mode/test.typ @@ -3,33 +3,29 @@ #import "/tests/helper.typ": * #import "/src/lib.typ": * #import cetz: draw, canvas -#import cetz-plot: axes, #test-case({ - import draw: * - plot.plot( size: (9, 6), - axis-style: "scientific", - y-mode: "log", y-base: 10, y-format: "sci", x-min: 1, x-max: 10, x-tick-step: 1, y-min: 1, y-max: 10000, y-tick-step: 1, y-minor-tick-step: 1, x-grid: "both", y-grid: "both", { + plot.log-axis("y", base: 10) + plot.add( domain: (0, 10), x => {calc.pow(10, x)}, samples: 100, - line: "raw", label: $ y=10^x $, ) + plot.add( domain: (1, 10), x => {x}, samples: 100, - line: "raw", hypograph: true, label: $ y=x $, ) @@ -37,6 +33,24 @@ ) }) +// Column chart test +#test-case({ + plot.plot( + size: (9, 6), + y-format: "sci", + x-min: -0.5, x-max: 4.5, x-tick-step: 1, + y-min: 0.1, y-max: 10000, step: 1, minor-step: 1, + x-grid: "both", + y-grid: "both", + { + plot.log-axis("y") + plot.add-bar( + (1, 10, 100, 1000, 10000).enumerate().map(((x,y))=>{(x,y)}), + bar-width: 0.8, + ) + } + ) +}) // Bode plot test #box(stroke: 2pt + red,{ canvas({ @@ -47,7 +61,6 @@ ) plot.plot( size: (16, 6), - axis-style: "scientific", x-format: none, x-label: none, x-mode: "log", x-min: 0.01, x-max: 100, x-tick-step: 1, x-minor-tick-step: 1, @@ -68,7 +81,6 @@ ) plot.plot( size: (16, 6), - axis-style: "scientific", x-mode: "log", x-min: 0.01, x-max: 100, x-tick-step: 1, x-minor-tick-step: 1, x-label: [Frequency ($upright(r a d)\/s$)], @@ -83,27 +95,6 @@ }) }) -// Column chart test -#box(stroke: 2pt + red, canvas({ - import draw: * - - plot.plot( - size: (9, 6), - axis-style: "scientific", - y-mode: "log", y-base: 10, - y-format: "sci", - x-min: -0.5, x-max: 4.5, x-tick-step: 1, - y-min: 0.1, y-max: 10000, y-tick-step: 1, y-minor-tick-step: 1, - x-grid: "both", - y-grid: "both", - { - plot.add-bar( - (1, 10, 100, 1000, 10000).enumerate().map(((x,y))=>{(x,y)}), - bar-width: 0.8, - ) - } - ) -})) // Scatter plot test #box(stroke: 2pt + red, canvas({ @@ -111,7 +102,6 @@ plot.plot( size: (9, 6), - axis-style: "scientific", y-mode: "log", y-base: 100, y-format: "sci", x-min: -0.5, x-max: 4.5, x-tick-step: 1, @@ -135,21 +125,21 @@ } ) })) +*/ // Box plot test -#box(stroke: 2pt + red, canvas({ +#test-case({ import draw: * plot.plot( size: (9, 6), - axis-style: "scientific", - y-mode: "log", y-base: 10, y-format: "sci", x-min: -0.5, x-max: 2.5, x-tick-step: 1, y-min: 0.1, y-max: 15000, y-tick-step: 1, y-minor-tick-step: 1, x-grid: "both", y-grid: "both", { + plot.log-axis("y") plot.add-boxwhisker( ( (x: 0, min: 1, q1: 10, q2: 100, q3: 1000, max: 10000), @@ -159,4 +149,4 @@ ) } ) -})) +}) diff --git a/tests/plot/equal-axis/test.typ b/tests/plot/equal-axis/test.typ index a9b0a8f..b81b05e 100644 --- a/tests/plot/equal-axis/test.typ +++ b/tests/plot/equal-axis/test.typ @@ -12,8 +12,8 @@ x-equal: "y", b-equal: "a", { - plot.add-cartesian-axis("a", (0,0), (6,0)) - plot.add-cartesian-axis("b", (0,0), (0,3)) + plot.lin-axis("a") + plot.lin-axis("b") plot.add(domain: (0, 2 * calc.pi), t => (calc.cos(t), calc.sin(t))) plot.add(domain: (0, 2 * calc.pi), t => (calc.cos(t), calc.sin(t)), axes: ("a", "b")) @@ -29,8 +29,8 @@ x-equal: "y", b-equal: "a", { - plot.add-cartesian-axis("a", (0,0), (3,0)) - plot.add-cartesian-axis("b", (0,0), (0,6)) + plot.lin-axis("a") + plot.lin-axis("b") plot.add(domain: (0, 2 * calc.pi), t => (calc.cos(t), calc.sin(t))) plot.add(domain: (0, 2 * calc.pi), t => (calc.cos(t), calc.sin(t)), axes: ("a", "b")) diff --git a/tests/plot/marks/test.typ b/tests/plot/marks/test.typ index aa584f0..ed144de 100644 --- a/tests/plot/marks/test.typ +++ b/tests/plot/marks/test.typ @@ -7,7 +7,7 @@ #test-case({ import cetz-plot: plot - let axis-options = (("x", "y"), ("x2", "y"), ("x", "y2"), ("x2", "y2")) + let axis-options = (("x", "y"), ("u", "y"), ("x", "v"), ("u", "v")) plot.plot( size: (5,5), diff --git a/tests/plot/test.typ b/tests/plot/test.typ index 4cf0944..a3f3d2b 100644 --- a/tests/plot/test.typ +++ b/tests/plot/test.typ @@ -28,26 +28,26 @@ x-min: -360, x-max: 360, y-tick-step: 1, - x2-label: none, - x2-min: -90, - x2-max: 90, - x2-tick-step: 45, - x2-minor-tick-step: 15, - y2-label: none, - y2-min: -1.5, - y2-max: 1.5, - y2-tick-step: .5, - y2-minor-tick-step: .1, + u-label: none, + u-min: -90, + u-max: 90, + u-tick-step: 45, + u-minor-tick-step: 15, + v-label: none, + v-min: -1.5, + v-max: 1.5, + v-tick-step: .5, + v-minor-tick-step: .1, { plot.add(data) - plot.add(data, style: (stroke: blue), axes: ("x2", "y2")) + plot.add(data, style: (stroke: blue), axes: ("u", "v")) }) }) /* School-Book Style */ #test-case({ plot.plot(size: (5, 4), - axis-style: "school-book", + template: "school-book", x-tick-step: 180, y-tick-step: 1, { @@ -58,7 +58,7 @@ /* Clipping */ #test-case({ plot.plot(size: (5, 4), - axis-style: "school-book", + template: "school-book", x-min: auto, x-max: 350, x-tick-step: 180, @@ -75,7 +75,6 @@ plot.plot(size: (5, 4), x-label: [Rainbow], x-tick-step: none, - axis-style: "scientific", y-label: [Color], y-max: 8, y-tick-step: none, @@ -92,34 +91,33 @@ /* Tick Step Calculation */ #test-case({ plot.plot(size: (12, 4), - y2-decimals: 4, + v-format: plot.formats.decimal.with(digits: 4), { plot.add(((0,0), (1,10)), axes: ("x", "y")) - plot.add(((0,0), (.1,.01)), axes: ("x2", "y2")) + plot.add(((0,0), (.1,.01)), axes: ("u", "v")) }) }) #test-case({ plot.plot(size: (12, 4), - y2-decimals: 9, - x2-decimals: 9, - y2-format: "sci", + v-format: plot.formats.sci, + u-format: plot.formats.sci, { plot.add(((0,0), (30,2500)), axes: ("x", "y")) - plot.add(((0,0), (.001,.0001)), axes: ("x2", "y2")) + plot.add(((0,0), (.001,.0001)), axes: ("u", "v")) }) }) -/* Axis Styles */ - - +/* Templates */ #test-case(args => { plot.plot(size: (4,4), x-tick-step: 90, y-tick-step: 1, - axis-style: args, { + template: args, { plot.add(domain: (0, 360), x => calc.sin(x * 1deg)) }) }, args: ( - "scientific", "scientific-auto", "left", "school-book", none + // TODO + //"scientific", "scientific-auto", "left", "school-book", none + "scientific", "school-book" )) /* Manual Axis Bounds */ @@ -136,10 +134,10 @@ yb-min: -1.5, yb-max: .5, yt-min: -.5, yt-max: 1.5, { - plot.add-cartesian-axis("xl", (0, 0), (4, 0)) - plot.add-cartesian-axis("xr", (0, 4), (4, 4)) - plot.add-cartesian-axis("yt", (0, 0), (0, 4)) - plot.add-cartesian-axis("yb", (4, 0), (4, 4)) + plot.lin-axis("xl") + plot.lin-axis("xr") + plot.lin-axis("yt") + plot.lin-axis("yb") plot.add(circle-data) plot.add(circle-data, axes: ("xl", "y"), style: (stroke: green)) plot.add(circle-data, axes: ("xr", "y"), style: (stroke: red)) @@ -159,10 +157,10 @@ yb-min: -1.75, yb-max: .25, yt-min: -.25, yt-max: 1.75, { - plot.add-cartesian-axis("xl", (0, 0), (4, 0)) - plot.add-cartesian-axis("xr", (0, 4), (4, 4)) - plot.add-cartesian-axis("yt", (0, 0), (0, 4)) - plot.add-cartesian-axis("yb", (4, 0), (4, 4)) + plot.lin-axis("xl") + plot.lin-axis("xr") + plot.lin-axis("yt") + plot.lin-axis("yb") plot.add(circle-data) plot.add(circle-data, axes: ("xl", "y"), style: (stroke: green)) plot.add(circle-data, axes: ("xr", "y"), style: (stroke: red)) @@ -198,16 +196,6 @@ mark: (start: ">", end: ">"), name: "amplitude") }) -/* Custom sample points */ -#test-case({ - plot.plot(size: (6, 4), y-min: -2, y-max: 2, - samples: 10, - { - plot.add(samples: 2, sample-at: (.99, 1.001, 1.99, 2.001, 2.99), domain: (0, 3), - x => calc.pow(-1, int(x))) - }) -}) - /* Format tick values */ #test-case({ plot.plot(size: (6, 4), @@ -217,19 +205,20 @@ y-tick-step: none, y-ticks: (-1, 0, 1), y-format: x => $y_(#x)$, - x2-tick-step: none, - x2-ticks: (-1, 0, 1), - x2-format: x => $x_(2,#x)$, - y2-tick-step: none, - y2-ticks: (-1, 0, 1), - y2-format: x => $y_(2,#x)$, + u-tick-step: none, + u-ticks: (-1, 0, 1), + u-format: x => $x_(2,#x)$, + v-tick-step: none, + v-ticks: (-1, 0, 1), + v-format: x => $y_(2,#x)$, { plot.add(samples: 2, domain: (-1, 1), x => -x, axes: ("x", "y")) - plot.add(samples: 2, domain: (-1, 1), x => x, axes: ("x2", "y2")) + plot.add(samples: 2, domain: (-1, 1), x => x, axes: ("u", "v")) }) }) // Test plot with anchors only +/* #test-case({ import draw: * @@ -256,13 +245,13 @@ tick: ( length: -.1, ), - left: ( + y: ( stroke: (paint: red), tick: ( stroke: auto, ) ), - bottom: ( + x: ( stroke: (paint: blue, thickness: 2pt), tick: ( stroke: auto,