From 5ff4b67278f841365bc2935eedba198283aa71ef Mon Sep 17 00:00:00 2001 From: Douglas Hanley Date: Sat, 26 Oct 2024 08:01:58 -0500 Subject: [PATCH] add individual examples (start) --- docs/examples/a_stack.js | 5 ++ docs/examples/axis_diagram.js | 21 +++++ docs/examples/axis_ticks.js | 6 ++ docs/examples/bar_plot.js | 3 + docs/examples/copy_icon.js | 8 ++ docs/examples/distribution_plot.js | 21 +++++ docs/examples/download_arrow.js | 11 +++ docs/examples/economist_plot.js | 25 ++++++ docs/examples/equil_plot.js | 16 ++++ docs/examples/ex_boxes.js | 8 ++ docs/examples/ex_plot.js | 5 ++ docs/examples/exponential_plot.js | 18 ++++ docs/examples/flow_diagram.js | 14 +++ docs/examples/gelu_plot.js | 12 +++ docs/examples/gum_blocks.js | 6 ++ docs/examples/homotopy.js | 11 +++ docs/examples/inverted_plot.js | 18 ++++ docs/examples/inward_spiral.js | 10 +++ docs/examples/legend_plot.js | 9 ++ docs/examples/malthus.js | 19 ++++ docs/examples/mushroom_planet.js | 4 + docs/examples/placement.js | 5 ++ docs/examples/point_diagram.js | 33 +++++++ docs/examples/rotate.js | 2 + docs/examples/shaded_plot.js | 22 +++++ docs/examples/sine_wave.js | 9 ++ docs/examples/stack_boxes.js | 4 + docs/examples/starburst.js | 7 ++ docs/examples/whale.js | 2 + docs/examples/word_counts.js | 22 +++++ docs/gen/extras.js | 137 +++++++++++++++++++++++++++++ 31 files changed, 493 insertions(+) create mode 100644 docs/examples/a_stack.js create mode 100644 docs/examples/axis_diagram.js create mode 100644 docs/examples/axis_ticks.js create mode 100644 docs/examples/bar_plot.js create mode 100644 docs/examples/copy_icon.js create mode 100644 docs/examples/distribution_plot.js create mode 100644 docs/examples/download_arrow.js create mode 100644 docs/examples/economist_plot.js create mode 100644 docs/examples/equil_plot.js create mode 100644 docs/examples/ex_boxes.js create mode 100644 docs/examples/ex_plot.js create mode 100644 docs/examples/exponential_plot.js create mode 100644 docs/examples/flow_diagram.js create mode 100644 docs/examples/gelu_plot.js create mode 100644 docs/examples/gum_blocks.js create mode 100644 docs/examples/homotopy.js create mode 100644 docs/examples/inverted_plot.js create mode 100644 docs/examples/inward_spiral.js create mode 100644 docs/examples/legend_plot.js create mode 100644 docs/examples/malthus.js create mode 100644 docs/examples/mushroom_planet.js create mode 100644 docs/examples/placement.js create mode 100644 docs/examples/point_diagram.js create mode 100644 docs/examples/rotate.js create mode 100644 docs/examples/shaded_plot.js create mode 100644 docs/examples/sine_wave.js create mode 100644 docs/examples/stack_boxes.js create mode 100644 docs/examples/starburst.js create mode 100644 docs/examples/whale.js create mode 100644 docs/examples/word_counts.js create mode 100644 docs/gen/extras.js diff --git a/docs/examples/a_stack.js b/docs/examples/a_stack.js new file mode 100644 index 0000000..075e3ad --- /dev/null +++ b/docs/examples/a_stack.js @@ -0,0 +1,5 @@ +// Three square boxes containing the letter "A", with two boxes stacked vertically on the left side and one box on the right side. +let a = Node('A', {aspect: 1}); +let n1 = VStack([a, a]); +let n2 = HStack([n1, a]); +return Frame(n2, {margin: 0.1}); diff --git a/docs/examples/axis_diagram.js b/docs/examples/axis_diagram.js new file mode 100644 index 0000000..32e3f08 --- /dev/null +++ b/docs/examples/axis_diagram.js @@ -0,0 +1,21 @@ +// A horizontal axis with ticks at "q/\gamma", "Bq/\gamma", "q", "Bq", and "\gamma^{\prime}q$ labeled at the bottom. On the top, "q/\gamma" is labeled with "Prev", "q" is labeled with "Current", and "\gamma^{\prime}q" is labeled with "Next". There is a semi-transparent box ranging from "Bq/\gamma" to "Bq" labeled with "Lagging" on the top left, "Leading" on the top right, and "Patent Coverage" across the bottom. + +// ticks (top and bottom) +let ticks = [ + [0.1, Tex('q/\\gamma')], [0.3, Tex('B q/\\gamma')], [0.5, Tex('q')], + [0.7, Tex('B q')], [0.9, Tex('\\gamma^{\\prime} q')] +]; +let main = HAxis(ticks, {tick_pos: 'both', label_pos: 'top', tick_size: 0.07}); +let tops = [[0.1, 'Prev'], [0.3, ''], [0.5, 'Current'], [0.7, ''], [0.9, 'Next']]; +let top = HAxis(tops, {line_stroke: 0, tick_size: 0.05}); + +// shaded box with albels +let vline = VLine(0.5, {opacity: 0.25, stroke_dasharray: 4}); +let shade = Rect({rect: [0.3, 0, 0.7, 1], stroke: 'black', fill: '#BBB', opacity: 0.15}); +let lag = Place(Text('Lagging', {stroke: '#444'}), {pos: [0.4, 0.15], rad: 0.08}); +let led = Place(Text('Leading', {stroke: '#444'}), {pos: [0.6, 0.15], rad: 0.08}); +let pat = Place(Text('Patent Coverage', {stroke: '#444'}), {pos: [0.5, 0.9], rad: 0.18}); + +// group and frame +let group = Group([vline, shade, main, top, lag, led, pat], {aspect: 3}); +return Frame(group, {margin: 0.05}); diff --git a/docs/examples/axis_ticks.js b/docs/examples/axis_ticks.js new file mode 100644 index 0000000..4b03ea0 --- /dev/null +++ b/docs/examples/axis_ticks.js @@ -0,0 +1,6 @@ +// A horizontal axis with 4 ticks. On the top side, the ticks are labeled "0", "i", "j", and "1". On the bottom side, the middle two ticks are labeled in Latex with "q\_{i,1}" and "q\_{i,2}". +let ticks1 = zip([0, 0.35, 0.7, 1], [Tex('0'), Tex('i'), Tex('j'), Tex('1')]); +let ticks2 = zip([0.35, 0.7], [Tex('q_{i,1}'), Tex('q_{j,2}')]); +let axis1 = HAxis(ticks1, {tick_size: 0.5, tick_pos: 'both'}); +let axis2 = HAxis(ticks2, {tick_size: 0.5, label_pos: 'out', label_offset: 0}); +return Frame(Group([axis1, axis2]), {aspect: 6, margin: [0.05, 2]}); diff --git a/docs/examples/bar_plot.js b/docs/examples/bar_plot.js new file mode 100644 index 0000000..eff8090 --- /dev/null +++ b/docs/examples/bar_plot.js @@ -0,0 +1,3 @@ +// Bar plot with 5 bars whose labels range from "A" to "E" and whose heights have values between 0 and 10. +let b = BarPlot([['A', 5], ['B', 8], ['C', 10], ['D', 6], ['E', 3]]); +return Frame(b, {margin: 0.2}); diff --git a/docs/examples/copy_icon.js b/docs/examples/copy_icon.js new file mode 100644 index 0000000..49660ff --- /dev/null +++ b/docs/examples/copy_icon.js @@ -0,0 +1,8 @@ +// A 20x25 pixel icon of two rectangles shaped like pieces of paper partially overlaid on one another. +let x = 0.35; +let rects = Points( + [[x, x], [1-x, 1-x]], + {shape: Rect(), size: x} +); +let frame = Frame(rects, {margin: 0.05}); +return SVG(frame, {size: [20, 25]}); diff --git a/docs/examples/distribution_plot.js b/docs/examples/distribution_plot.js new file mode 100644 index 0000000..50adecb --- /dev/null +++ b/docs/examples/distribution_plot.js @@ -0,0 +1,21 @@ +// A plot with an exponentially decaying line. It is divided horizontally at a point "R" with a dashed line. To the left of "R" it is shaded dark gray, and to the right of "R" it is shaded light gray. There are also x-axis ticks a "0" and "1" and y-axis ticks at "0". The x-axis is labeled "Worker Index" and the y-axis is labeled "Research Aptitude". The graph is framed with the title "Research Aptitude Distribution". + +// define params +let [β, R] = [0.5, 0.2]; +let xlim = [xlo, xhi] = [0.0001, 1]; +let ylim = [ylo, yhi] = [0, 5]; +let f = x => min(yhi, (1-β)*x**(-β)); + +// create lines +let sr = SymFill({fy1: 0, fy2: f, xlim: [xlo, R], fill: '#555', opacity: 0.4, N: 250}); +let sp = SymFill({fy1: 0, fy2: f, xlim: [R, xhi], fill: '#BBB', opacity: 0.4, N: 250}); +let b = SymPath({fy: f, xlim, stroke: '#333', N: 250}); +let bline = VLine(R, {lim: ylim, stroke_dasharray: 3}); + +// plot and frame +let plot = Plot([sr, sp, b, bline], { + aspect: 1.5, ylim, xlabel_offset: 0.1, ylabel_offset: 0.05, + xticks: [[0, '0'], [1, '1'], [R, 'R'], [2, '']], yticks: [0], + xlabel: 'Worker Index', ylabel: 'Research Aptitude' +}); +return TitleFrame(plot, 'Research Aptitude Distribution', {margin: 0.2}); diff --git a/docs/examples/download_arrow.js b/docs/examples/download_arrow.js new file mode 100644 index 0000000..17f09e8 --- /dev/null +++ b/docs/examples/download_arrow.js @@ -0,0 +1,11 @@ +// A 25x25 pixel icon in which an arrow is pointing downward into a curved tray. +let [mid, rad] = [0.25, 0.06]; +let arrow = Arrow(-90, { + pos: [0.5, 0.85], head: 0.25, tail: 0.75 +}); +let base = Bezier2Path([0, 1-mid], [ + [0, 1-rad], [[rad, 1], [0, 1]], [1-rad, 1], [[1, 1-rad], [1, 1]], [1, 1-mid] +]); +let shape = Group([base, arrow]); +let frame = Frame(shape, {margin: 0.1}); +return SVG(frame, {size: 25, prec: 2}); diff --git a/docs/examples/economist_plot.js b/docs/examples/economist_plot.js new file mode 100644 index 0000000..b05c513 --- /dev/null +++ b/docs/examples/economist_plot.js @@ -0,0 +1,25 @@ +// A bar plot in the style of the Economist magazine. There are roughly 40 densely packed blue bars with random heights between 0 and 70. The y-axis has horizontal grid lines at 0, 25, 50, and 75, while the x-axis ranges between 0 and 40. + +// plot parameters +let n = 41; +let ymax = 70; +let xticks = range(0, n+10, 10); +let yticks = linspace(0, 100, 5); + +// generate random data +let vals = range(n).map(x => ymax*random(x)); + +// make bars and y-labels +let bars = VBars(vals, { + integer: true, bar_fill: '#1e5c99', bar_stroke: 'none' +}); +let labs = Points( + yticks.map(x => [Anchor(Text(x)), [n+3, x+4]]), {size: 1} +); +let plot = Plot([bars, labs], { + aspect: phi, xlim: [-1, n+4], ylim: [0, ymax+5], xticks, yticks, + xaxis_tick_pos: 'down', yaxis: false, ygrid: yticks +}); + +// return framed plot +return Frame(plot, {margin: 0.2}); diff --git a/docs/examples/equil_plot.js b/docs/examples/equil_plot.js new file mode 100644 index 0000000..52c393a --- /dev/null +++ b/docs/examples/equil_plot.js @@ -0,0 +1,16 @@ +// Plot of two lines that both originate at zero and intersect at a single point, one that is convext and one that is concave. The intersection point is labeled as k1* and k2*, both on the axes and with grid lines. The x-axis label is "Capital Stock 1 (k1)" and the y-axis label is "Capital Stock 2 (k2)". +let klim = [0.0, 1.5]; let [a, b] = [1.0, 0.5]; +let kzero1 = k1 => a*k1**b; let kzero2 = k2 => a*k2**b; +let kstar = a**(1/(1-b)); +let path1 = SymPath({fy: kzero1, xlim: klim}); +let path2 = SymPath({fx: kzero2, ylim: klim}); +let note1 = Note('\\dot{k}_1=0', {pos: [1.62, 1.24], rad: 0.09, latex: true}); +let note2 = Note('\\dot{k}_2=0', {pos: [1.21, 1.6], rad: 0.09, latex: true}); +let xlabel = HStack([Text('Capital Stock 1 '), Tex('(k_1)')]); +let ylabel = HStack([Text('Capital Stock 2 '), Tex('(k_2)')]); +let plot = Plot([path1, path2, note1, note2], { + aspect: phi, xlim: klim, ylim: klim, xaxis_pos: 0, xaxis_tick_pos: 'both', + xticks: [[kstar, Tex('k_1^{\\ast}')]], yticks: [[kstar, Tex('k_2^{\\ast}')]], + xlabel, ylabel, grid: true, label_offset: 0.12 +}); +return Frame(plot, {margin: 0.25}); diff --git a/docs/examples/ex_boxes.js b/docs/examples/ex_boxes.js new file mode 100644 index 0000000..456390c --- /dev/null +++ b/docs/examples/ex_boxes.js @@ -0,0 +1,8 @@ +// An array of partially overlapping squares depicting the letter "X". The squares for one direction are in red, while the squares in the other direction are in blue. +let [shape, size, num] = [Rect(), 0.1, 16]; +let pos1 = linspace(0, 1, num).map(x => [x, x]); +let pos2 = linspace(0, 1, num).map(x => [1 - x, x]); +let p1 = Points(pos1, {shape, size, stroke: 'red'}); +let p2 = Points(pos2, {shape, size, stroke: 'blue'}); +let gg = Group([p1, p2], {opacity: 0.75}); +return Frame(gg, {margin: 0.15}); diff --git a/docs/examples/ex_plot.js b/docs/examples/ex_plot.js new file mode 100644 index 0000000..a1cc2c0 --- /dev/null +++ b/docs/examples/ex_plot.js @@ -0,0 +1,5 @@ +// Plot showing three points, each one marked with an "x" and the label "hello". The x-axis ranges from -1 to 1, while the y-axis ranges from 0 to 1. +let shape = HStack(['✗', 'hello'].map(Text), {spacing: 0.1}); +let points = Points([[-0.3, 0.3], [0.4, 0.6], [-0.5, 0.8]], {shape, size: 0.1}); +let plot = Plot(points, {xlim: [-1, 1], ylim: [0, 1]}); +return Frame(plot, {margin: 0.2}); diff --git a/docs/examples/exponential_plot.js b/docs/examples/exponential_plot.js new file mode 100644 index 0000000..408b6e1 --- /dev/null +++ b/docs/examples/exponential_plot.js @@ -0,0 +1,18 @@ +// Plot of the path of a line that starts at a high level y0*, then at time t0 begins to decay exponentially down to a lower level y1*. The x-axis has a label for time t0, and the y-axis has labels for y0* and y1*. Each of these are also denoted with dashed lines in the plot. The x-axis label is "Time (t)" and the y-axis label is "Output (y)". +let [t0, ymax, yss0, yss1] = [0.2, 1.4, 1.0, 0.7]; +let path0 = HLine(1, {x1: 0, x2: t0, stroke_width: 2}); +let path1 = SymPath({ + fy: x => min(yss0, yss1 + (yss0-yss1)*exp(-6*(x-t0))), + xlim: [0, 1], stroke_width: 2 +}); +let start = VLine(t0, {lim: [0, ymax], stroke_dasharray: [4, 4]}); +let steady1 = HLine(1, {lim: [0, 1], stroke_dasharray: [4, 4]}); +let steady2 = HLine(yss1, {lim: [0, 1], stroke_dasharray: [4, 4]}); +let plot = Plot([path0, path1, start, steady1, steady2], { + xlim: [0, 1], ylim: [0, ymax], aspect: phi, + xlabel: 'Time (t)', ylabel: 'Output (y)', + xticks: [[t0, Tex('t_0')]], label_offset: 0.1, + yticks: [[yss1, Tex('y^{\\ast}_1')], [1, Tex('y^{\\ast}_0')]] +}); +let frame = Frame(plot, {padding: 0.2}); +return frame; diff --git a/docs/examples/flow_diagram.js b/docs/examples/flow_diagram.js new file mode 100644 index 0000000..2ed3709 --- /dev/null +++ b/docs/examples/flow_diagram.js @@ -0,0 +1,14 @@ +// A flow diagram with three nodes arranged horizontally: "Input", "Causal Self Attention", and "Perceptron". There are unidirectional arrows going from left to right, connecting the first and second nodes and the second and third nodes, each with the label "Layer Norm". Each node is a rectangle with slightly rounded corners. +let net = Network([ + ['input', 'Input', [0.15, 0.5], [0.1, 0.2]], + ['attn', ['Causal Self', 'Attention'], [0.5, 0.5], [0.1, 0.4]], + ['percep', 'Perceptron', [0.85, 0.5], [0.1, 0.2]], +], [ + ['input', 'attn'], ['attn', 'percep'] +], { + aspect: 8, directed: true, node_border_radius: 0.05, arrow_size: 0.03 +}); +let notes = Points([[0.32, 0.4], [0.67, 0.4]], { + shape: Text('Layer Norm'), size: 0.08 +}); +return Group([net, notes]); diff --git a/docs/examples/gelu_plot.js b/docs/examples/gelu_plot.js new file mode 100644 index 0000000..8b14297 --- /dev/null +++ b/docs/examples/gelu_plot.js @@ -0,0 +1,12 @@ +// Plot of three lines, one in red which is max(0,x), another in blue which is the same but with a slight dip at 0, and a final dashed horizontal line at 0. There is a legend labeling these lines as "ReLU" for the red line and "GELU" for the blue line. The x-axis ranges from -4 to 3, making sure there is a tick at 0. +let xlim = [-4, 3]; let ylim = [-1, 3]; +let rargs = {stroke: red, stroke_width: 2}; +let bargs = {stroke: blue, stroke_width: 2}; +let relu = SymPath({fy: x => max(0, x), xlim, ...rargs}); +let gelu = SymPath({fy: x => x*sigmoid(x), xlim, ...bargs}); +let zero = HLine(0, {lim: xlim, stroke_dasharray: 4}); +let leg = Legend([[rargs, 'ReLU'], [bargs, 'GELU']], { + pos: [-2, 2], rad: 0.55, hspacing: 0.1, vspacing: 0.2 +}); +let plot = Plot([zero, relu, gelu, leg], {aspect: phi, ylim, xticks: 8}); +return Frame(plot, {margin: 0.2}); diff --git a/docs/examples/gum_blocks.js b/docs/examples/gum_blocks.js new file mode 100644 index 0000000..0ae6e1a --- /dev/null +++ b/docs/examples/gum_blocks.js @@ -0,0 +1,6 @@ +// Letters stacked like blocks spelling out the word "GUM". +let [g, u, m] = 'GUM'.split('').map(t => + Node(t, {aspect: 1, fill: '#EEE', border: 3}) +); +let s = VStack([g, HStack([u, m])], {expand: false}); +return Frame(s, {margin: 0.02}); diff --git a/docs/examples/homotopy.js b/docs/examples/homotopy.js new file mode 100644 index 0000000..dc3b6db --- /dev/null +++ b/docs/examples/homotopy.js @@ -0,0 +1,11 @@ +// Wide-form plot depicting various curves traversing in the vertical dimension from 0 to 1. Some of the curves span the whole distance, others loop back to the origin, while others form independent loops. The x-axis is labeled "Solutions (x)" and the y-axis is labeled "Deformation path (t)". +let path1 = SymPath({fy: x => 6*(x-0.1)*(1.2-3*x), xlim: [0.1, 0.4]}); +let path2 = SymPath({fx: y => 0.6 + 0.25*cos(3*y), ylim: [0, 1]}); +let ellipse1 = Ellipse({pos: [0.5, 0.3], rad: [0.05, 0.1]}); +let ellipse2 = Ellipse({pos: [0.8, 0.7], rad: [0.05, 0.15]}); +let plot = Plot([path1, path2, ellipse1, ellipse2], { + aspect: 2, xlim: [0, 1], ylim: [0, 1], xticks: [], yticks: [0, 1], + xlabel: 'Solutions (x)', ylabel: 'Deformation Path (t)', label_offset: 0.05, +}); +let frame = Frame(plot, {border: 1, margin: 0.15}); +return frame; diff --git a/docs/examples/inverted_plot.js b/docs/examples/inverted_plot.js new file mode 100644 index 0000000..169c75a --- /dev/null +++ b/docs/examples/inverted_plot.js @@ -0,0 +1,18 @@ +// A plot showing two exponentials with only an x-axis with ticks ranging from -0.5 to 1.5. There is a steep exponential decay starting at 0 in blue on the top side of the axis and a shallow exponential decay starting at 0 on the bottom side in red. + +// define params +let [αi, αr] = [4, 2]; +let xlim = [-0.5, 1.5]; +let xlim1 = [0, 1.5]; + +// make shaded lines +let [fi, fr] = [αi, αr].map(α => (x => α*exp(-α*x))); +let si = SymFill({fy1: fi, fy2: 0, xlim: xlim1, fill: blue, opacity: 0.5}); +let sr = SymFill({fy1: x => -fr(x), fy2: 0, xlim: xlim1, fill: red, opacity: 0.5}); + +// plot and frame +let plot = Plot([si, sr], { + aspect: 2, xaxis_pos: 0, xlim, yaxis: false, + xticks: [-0.5, 0, 0.5, 1, 1.5], xaxis_tick_pos: 'both' +}); +return Frame(plot, {margin: 0.1}); diff --git a/docs/examples/inward_spiral.js b/docs/examples/inward_spiral.js new file mode 100644 index 0000000..0b168ab --- /dev/null +++ b/docs/examples/inward_spiral.js @@ -0,0 +1,10 @@ +// A plot of a curve spiraling inwards counterclockwise, with axes in both directions ranging from -1 to 1. +let alpha = 0.1; +let tlim = [0, 50]; +let s = SymPath({ + fx: t => exp(-alpha*t) * cos(t), + fy: t => exp(-alpha*t) * sin(t), + tlim, N: 1000, +}); +let p = Plot(s, {xlim: [-1, 1], ylim: [-1, 1]}); +return Frame(p, {margin: 0.15}); diff --git a/docs/examples/legend_plot.js b/docs/examples/legend_plot.js new file mode 100644 index 0000000..d928041 --- /dev/null +++ b/docs/examples/legend_plot.js @@ -0,0 +1,9 @@ +// A plot of two lines that are zero at 0 and 2 but are positive inbetween. One of the lines is solid blue while the other line is dashed red. The plot has a legend in the top right naming the blue line "Hello World" and the red line "Testing Longer String". +let args1 = {stroke: blue}; +let args2 = {stroke: red, stroke_dasharray: 6}; +let info = [[args1, 'Hello World'], [args2, 'Testing Longer String']]; +let leg = Legend(info, {pos: [1.5, 1.8], rad: 0.45, vspacing: 0.3}); +let line1 = SymPath({fy: x => 1.5*x*(2-x), xlim: [0, 2], ...args1}); +let line2 = SymPath({fy: x => x*(2-x), xlim: [0, 2], ...args2}); +let plot = Plot([line1, line2, leg], {aspect: phi, ylim: [0, 2]}); +return Frame(plot, {margin: 0.2}); diff --git a/docs/examples/malthus.js b/docs/examples/malthus.js new file mode 100644 index 0000000..d725b75 --- /dev/null +++ b/docs/examples/malthus.js @@ -0,0 +1,19 @@ +// Draw a plot of a function increases from below zero to a maximum value of nbar. Indicate with vertical and horizontal lines where this line cross zero and a positive threshhold g_z / alpha. Label these intersection points on the axis, along with nbar and zero. Let the x-axis label be "Standard of Living (y)", the y-axis label be "Population Growth (gL)", and the title be "Modified Demographic Function". +let nbar = 2; let ybar = 1; let theta = 1; let gz = 0.5; let alpha = 0.5; +let gza = gz/alpha; let ystar = ybar + gza/theta; +let xlim = [xlo, xhi] = [0, 5]; let ylim = [ylo, yhi] = [-2, 3]; +let demo = y => min(nbar, theta*(y-ybar)); +let path = SymPath({fy: demo, xlim}); +let zero1 = HLine(0, {lim: xlim, opacity: 0.2}); +let zero2 = VLine(ybar, {lim: ylim, opacity: 0.2}); +let line1 = HLine(gza, {lim: xlim, stroke_dasharray: 3}); +let line2 = VLine(ystar, {lim: ylim, stroke_dasharray: 3}); +let dot = Points([[ystar, gza]], {size: 0.04}); +let xticks = [[1, Tex('\\bar{y}')], [ystar, Tex('y^{\\ast}')], [xhi, '']]; +let yticks = [[0, '0'], [gz/alpha, Tex('\\frac{g_z}{\\alpha}')], [nbar, Tex('\\bar{n}')], [yhi, '']]; +let plot = Plot([path, zero1, zero2, line1, line2, dot], { + aspect: phi, xlim, ylim, xticks, yticks, xlabel: 'Standard of living (y)', + ylabel: 'Population growth (gL)', title: 'Modified Demographic Function' +}); +let frame = Frame(plot, {margin: 0.25}); +return frame; diff --git a/docs/examples/mushroom_planet.js b/docs/examples/mushroom_planet.js new file mode 100644 index 0000000..1d1a151 --- /dev/null +++ b/docs/examples/mushroom_planet.js @@ -0,0 +1,4 @@ +// Two side-by-side rectangles, the left one being square and containing a mushroom emoji, the right one containing the world "planet". +let boxes = ['🍄', 'planet'].map(x => Node(x, {padding: 0.15})); +let stack = HStack(boxes); +return Frame(stack, {padding: 0.05}); diff --git a/docs/examples/placement.js b/docs/examples/placement.js new file mode 100644 index 0000000..24b4c90 --- /dev/null +++ b/docs/examples/placement.js @@ -0,0 +1,5 @@ +// A square in the top left and a circle in the bottom right. +return Group([ + [Square(), {pos: [0.3, 0.3], rad: 0.15}], + [Circle(), {pos: [0.7, 0.7], rad: 0.15}] +]); diff --git a/docs/examples/point_diagram.js b/docs/examples/point_diagram.js new file mode 100644 index 0000000..fe3eb79 --- /dev/null +++ b/docs/examples/point_diagram.js @@ -0,0 +1,33 @@ +// Two horizontal lines connected at the left with a vertical line. The top line contains mostly blue circles and one red circle. The bottom line contains mostly red circles that are spaced more widely. There is a thin box on the top line labeled "Low B" and a wider box on the bottom line labeled "High B". At the bottom of the figure there is the text "Productivity >>>". + +// line and point positions +let [B1, B2] = [0.05, 0.2]; +let [size, bsize] = [0.02, 0.1]; +let [y1, y2] = [0.3, 0.7]; +let [b1, b2] = [0.44, 0.65]; +let p1 = [0.08, 0.17, b1, 0.52, 0.59]; +let p2 = [0.2, 0.4, b2, 0.95]; + +// line structure +let base = VLine(0); +let prods = [y1, y2].map(y => HLine(y, {stroke: 'black'})); + +// points on lines +let steps1 = Points(p1.map(x => [x, y1]), {shape: Circle({fill: blue}), size}); +let steps1a = Points([[0.36, y1]], {shape: Circle({fill: red}), size: 0.02}); +let steps2 = Points(p2.map(x => [x, y2]), {shape: Circle({fill: red}), size}); + +// gray rectangles +let pat1 = Rect({rect: [b1, y1-bsize, b1+B1, y1+bsize], fill: '#DDD', opacity: 0.75}); +let pat2 = Rect({rect: [b2, y2-bsize, b2+B2, y2+bsize], fill: '#DDD', opacity: 0.75}); + +// labels +let label1 = Note('"Low B"', {pos: [0.47, y1-0.15], rad: 0.06}); +let label2 = Note('"High B"', {pos: [0.753, y2-0.15], rad: 0.06}); +let xlabel = Note('Productivity >>>', {pos: [0.5, 1], rad: 0.1}); + +// group together and frame +let group = Group([ + base, ...prods, pat1, pat2, steps1, steps2, steps1a, label1, label2, xlabel +], {aspect: 1.3}); +return Frame(group, {margin: 0.1}); diff --git a/docs/examples/rotate.js b/docs/examples/rotate.js new file mode 100644 index 0000000..26e2dd6 --- /dev/null +++ b/docs/examples/rotate.js @@ -0,0 +1,2 @@ +// A rectangle with aspect ratio 2 placed in the top right and rotated 20 degrees clockwise. +return Place(Rect(), {pos: [0.65, 0.3], rad: [0.2, 0.1], rotate: 20}); diff --git a/docs/examples/shaded_plot.js b/docs/examples/shaded_plot.js new file mode 100644 index 0000000..5dd6761 --- /dev/null +++ b/docs/examples/shaded_plot.js @@ -0,0 +1,22 @@ +// A plot of two lines that are shaded below. One line is a steep exponential in blue and the other line is a more shallow exponential in red. The x-axis has tick labels at "1" and "B" and the "B" tick has associated solid vertical line. The y-axis has a tick at "0". The x-axis has the lable "Innovation Step Size" and the y-axis "Density". + +// define parameters +let [αi, αr] = [8, 2]; let B = 1.3; +let xlim = [xlo, xhi] = [1, 2]; let ylim = [ylo, yhi] = [0, sqrt(αi)]; +let [fi, fr] = [αi, αr].map(α => (x => sqrt(α)*x**(-α-1))); + +// make shaded areas and lines +let si = SymFill({fy1: 0, fy2: fi, xlim, fill: blue, opacity: 0.4}); +let sr = SymFill({fy1: 0, fy2: fr, xlim, fill: red, opacity: 0.4}); +let [bi, br] = [fi, fr].map(f => SymPath({fy: f, xlim, stroke: '#333'})); +let bline = VLine(B, {lim: ylim, stroke_width: 2}); + +// plot it and frame it +let plot = Plot([si, sr, bi, br, bline], { + aspect: phi, grid: true, + xticks: [[1, '1'], [B, 'B'], [2, '']], + yticks: [[0, '0'], [sqrt(αi), '']], + xlabel: 'Innovation Size', ylabel: 'Density', + xlabel_offset: 0.13, ylabel_offset: 0.05 +}); +return Frame(plot, {margin: 0.2}); diff --git a/docs/examples/sine_wave.js b/docs/examples/sine_wave.js new file mode 100644 index 0000000..49a112e --- /dev/null +++ b/docs/examples/sine_wave.js @@ -0,0 +1,9 @@ +// A plot of a sin function from 0 to 2\*pi. The y-axis has ticks from -1 to 1. The x-axis has ticks from 0 to 2\*pi labeled as fractions of pi and is anchored at y\=0. There are light gray dashed grid lines aligned with the ticks. +let line = SymPath({fy: x => -sin(x), xlim: [0, 2*pi]}); +let xticks = linspace(0, 2, 6).slice(1).map(x => [x*pi, `${rounder(x, 1)} π`]); +let plot = Plot(line, { + aspect: phi, xaxis_pos: 0, xticks, yticks: 5, grid: true, + xlabel: 'phase', ylabel: 'amplitude', title: 'Inverted Sine Wave', + xlabel_offset: 0.1, xaxis_tick_pos: 'both', grid_stroke_dasharray: 3 +}); +return Frame(plot, {margin: 0.25}); diff --git a/docs/examples/stack_boxes.js b/docs/examples/stack_boxes.js new file mode 100644 index 0000000..1d1a151 --- /dev/null +++ b/docs/examples/stack_boxes.js @@ -0,0 +1,4 @@ +// Two side-by-side rectangles, the left one being square and containing a mushroom emoji, the right one containing the world "planet". +let boxes = ['🍄', 'planet'].map(x => Node(x, {padding: 0.15})); +let stack = HStack(boxes); +return Frame(stack, {padding: 0.05}); diff --git a/docs/examples/starburst.js b/docs/examples/starburst.js new file mode 100644 index 0000000..6e5f776 --- /dev/null +++ b/docs/examples/starburst.js @@ -0,0 +1,7 @@ +// A square grid of four cells, where each cell contains a starburst pattern with 12 lines radiating outwards from a central point. +let n = 12; +let shape = [2, 2]; +let size = prod(shape); +let star = Group(range(-90, 90, 180/n).map(Ray)); +let grid = Grid(reshape(repeat(star, size), shape)); +return Frame(grid, {border: 1, margin: 0.05}); diff --git a/docs/examples/whale.js b/docs/examples/whale.js new file mode 100644 index 0000000..fb843ae --- /dev/null +++ b/docs/examples/whale.js @@ -0,0 +1,2 @@ +// A whale emoji in a square box. +return Node('🐋', {margin: 0.1}); diff --git a/docs/examples/word_counts.js b/docs/examples/word_counts.js new file mode 100644 index 0000000..11fce92 --- /dev/null +++ b/docs/examples/word_counts.js @@ -0,0 +1,22 @@ +// A horizontal grid of boxes, each containing an integer. On top of each box is a label indicating which word it is associated with, such as "quick" or "fox". Near the end of the grid, there is an ellipsis box indicating there are many omitted words inbetween. + +// define cell data +let data = [ + ['the', '2'], ['quick', '1'], ['brown', '2'], ['fox', '1'], + ['jumped', '1'], ['over', '1'], ['dog', '1'], ['lazy', '0'], + ['plum', '0'], ['house', '0'] +]; + +// make stacked cell array +let cells = data.map(([w, c]) => VStack([ + Node(w, {aspect: 1, border: 0, padding: [0, 0.8]}), + Node(c, {padding: 0.4, aspect: 1}) +])); + +// splice in ellipsis +let dots = HStack(repeat(Dot(), 3), {spacing: 1}); +let elps = Frame(dots, {aspect: 1, padding: 1.5}); +cells.splice(8, 0, VStack([1/2, elps])); + +// returned cells with margin +return Frame(HStack(cells), {margin: 0.05}); diff --git a/docs/gen/extras.js b/docs/gen/extras.js new file mode 100644 index 0000000..c8ce032 --- /dev/null +++ b/docs/gen/extras.js @@ -0,0 +1,137 @@ +// A plot with three lines: a horizontal line at height $\frac{\eta}{\alpha\rho}$ labeled "SMB", a decresing line starting at the same point labeled "PMB\*", and an increasing line starting at "1" labeled "SMC\=PMC\*". The intersections of these lines are market with grid lines and are labeled "Equil" and "Efficient". The x-axis ranges from "0" to "1" and is labeled "Researcher Share (R)". +let [alpha, eta, rho] = [1, 0.1, 0.05]; +let [rstar, rhat] = [0.25, 0.5]; +let smb0 = eta/(alpha*rho); +let xlim = [0, 1]; let N = 500; +let mc = r => 1/(1-r); +let smb = r => smb0; +let pmb = r => (eta/alpha)/(rho+eta*r); +let line_mc = SymPath({fy: mc, xlim: [0, 0.75], N}); +let line_smb = SymPath({fy: smb, xlim: [0, 1], N}); +let line_pmb = SymPath({fy: pmb, xlim: [0, 1], N}); +let note_mc = Note('SMC=PMC*', {pos: [0.758, 4.23], rad: 0.07}); +let note_smb = Note('SMB', {pos: [1.055, 2.02], rad: 0.03}); +let note_pmb = Note('PMB*', {pos: [1.06, 0.68], rad: 0.035}); +let dots = Points([[rstar, 1.34], [rhat, 2]], {size: 0.008}); +let label_smb = Tex('\\frac{\\eta}{\\alpha\\rho}'); +let xticks = [[0, '0'], [rstar, 'Equil'], [rhat, 'Efficient'], [1, '1']]; +let yticks = [[0, '0'], [1, '1'], [smb0, label_smb], [4, '']]; +let plot = Plot([line_mc, line_smb, line_pmb, note_mc, note_smb, note_pmb, dots], { + aspect: phi, xlim: [0, 1], ylim: [0, 4], xticks, yticks, grid: true, + grid_opacity: 0.15, xlabel: 'Researcher Share (R)' +}); +return Frame(plot, {margin: [0.15, 0.1, 0.15, 0.25]}); + +// Two by two table with columns "Product and "¬ Product" and rows "Patent" and "¬ Patent". The cell values in the table are "Protected Innovation" and "Strategic Patents" in the first row and "Secrecy" and "Incation" in the second row. The column labels are in blue and the row labels are in red. +function make_node(t, c, b, s) { + let text = VStack(t.map(s => Text(s, {stroke: c})), {expand: false}); + let place = Place(text, {rad: [0.4, s*t.length]}); + return Frame(place, {border: b}); +} +let [b, c] = [['Product'], ['¬ Product']].map(s => make_node(s, blue, 0, 0.17)); +let [d, g] = [['Patent'], ['¬ Patent']].map(s => make_node(s, red, 0, 0.12)); +let [e, f, h, i] = [['Protected', 'Innovation'], ['Strategic', 'Patents'], ['Secrecy'], ['Inaction']].map( + s => make_node(s, 'black', 1, 0.12) +); +let grid1 = VStack([ + [HStack([[Spacer(), 0.25], b, c]), 0.25], + [HStack([[d, 0.25], e, f]), 0.75/2], + [HStack([[g, 0.25], h, i]), 0.75/2], +]); +return Frame(grid1, {aspect: phi, margin: 0.05}); + +// A plot of two lines: one blue one labeled "No Stretegic Patents" and one red one labeled "Strategic Patents". The x-axis is labeled "Firm Productivity" and the y-axis is labeled "Complementarity". +let xlim = [1, 2]; let ylim = [0, 1]; +let line1 = SymPath({fy: x => 0.5/x**2, xlim, stroke: '#ff0d57', stroke_width: 2}); +let line2 = SymPath({fy: x => 1 - 0.5*1/x**2, xlim, stroke: '#1e88e5', stroke_width: 2}); +let note1 = Place( + Node(['No Strategic', 'Patents'], {border: 0}), {pos: [1.3, 0.84], rad: 0.14} +); +let note2 = Place( + Node(['Strategic', 'Patents'], {border: 0}), {pos: [1.3, 0.16], rad: 0.11} +); +let dot = Place(Dot(), {pos: [1, 0.5], rad: 0.01}); +let plot = Plot([line1, line2, note1, note2, dot], { + xlim, ylim, aspect: 1.2, xticks: [], yticks: [], + xlabel: 'Firm Productivity', ylabel: 'Complementarity', + xlabel_offset: 0.07, ylabel_offset: 0.07, +}); +let frame = Frame(plot, {margin: [0.2, 0.05, 0.1, 0.15]}); +return frame; + +// Four wide nodes with the words "scented", "large", and "liquid" (in blue), and "OxiClean". There are arrows down to four more nodes with the same words but "liquid" is changed to "pods" (in red). The arrow for the changed node is filled in. Below that there is a horizontal line, then an array of numbers "0", "0", "1" (in red), and "0". Below that is a filled in arrow pointing to the text "25% novelty". +let cells1 = [['scented'], ['large'], ['liquid', {text_stroke: 'blue'}], ['OxiClean']]; +let cells2 = [['scented'], ['large'], ['pods', {text_stroke: 'red'}], ['OxiClean']]; +let aprops = [{}, {}, {arrow_fill: '#CCC', arrow_base: true}, {}] +let vals = [[0], [0], [1, {stroke: 'red'}], [0]]; +let row1 = HStack(cells1.map(([s, a]) => Node(s, {aspect: 3, padding: 0.2, ...a}))); +let row2 = HStack(cells2.map(([s, a]) => Node(s, {aspect: 3, padding: 0.2, ...a}))); +let arrow1 = HStack(aprops.map(a => + Edge([0.5, 0], [0.5, 1], {arrow: true, arrow_size: [0.1, 0.08], ...a}) +)); +let arrow2 = Edge([0.5, 0], [0.5, 1], { + arrow: true, arrow_size: [0.03, 0.15], arrow_fill: '#CCC', arrow_base: true +}); +let nums = HStack(vals.map(([s, a]) => + Node(`${s}`, {flex: true, padding: 0, border: 0, ...a}) +)); +let line = HLine(0.5); +let result = Node('25% novelty', {flex: true, border: 0}); +let rows = VStack([ + [row1, 0.1], [arrow1, 0.25], [row2, 0.1], [line, 0.15], + [nums, 0.1], [Spacer(), 0.03], [arrow2, 0.15], [Spacer(), 0.03], + [result, 0.1] +]); +let nn = (s, a) => Node(s, {flex: true, border: 0, ...a}); +return Frame(rows, {margin: 0.05}); + +// Three rows of nodes connected by lines. The first row has three nodes for product categories 1, 2, and 3. The second row has three nodes for Wikipedia text 1, 2, and 3. And the third row has five notes for patent abstract 1 through 5. The first two rows are connected by bi-directional filled in arrows. The second and third rows are partially connected by curved lines. +let locs = range(5).map(i => 0.2*i+0.1); +let cells1 = [null, ...range(1, 4).map(i => ['Product', `Category ${i}`]), null]; +let cells2 = [null, ...range(1, 4).map(i => ['Wikipedia', `Text ${i}`]), null]; +let cells3 = range(1, 6).map(i => ['Patent', `Abstract ${i}`]); +let noder = s => (s == null) ? Spacer() : Node(s, { + aspect: 3, padding: 0.2, flex: true, border_radius: 0.05 +}); +let [row1, row2, row3] = [cells1, cells2, cells3].map( + c => HStack(c.map(noder), {spacing: 0.02}) +); +let bidi = Edge([0.5, 0], [0.5, 1], { + arrow_beg: true, arrow_end: true, arrow_size: [0.1, 0.075], arrow_fill: '#DDD', arrow_base: true +}); +let edges1 = HStack([Spacer(), bidi, bidi, bidi, Spacer()]); +let elocs2 = [[0, 1], [1, 1], [2, 3], [3, 2], [4, 3]].map( + ([x1, x2]) => [[locs[x1], 1], [locs[x2], 0]] +); +let edges2 = Group(elocs2.map(([p1, p2]) => Edge(p1, p2))); +let rows = VStack([row1, edges1, row2, edges2, row3], {aspect: phi}); +return Frame(rows, {margin: 0.05}); + +// Four by three grid of boxes, each with a different emoji in it. The emojis are a random selection of fruits and candies. +let data = [ + '🍩🍦🍨🍫🍌', + '🍕🍉🍒🍇🍐', + '🥝🍎🍓🍬🍪', +] +let make_blocks = s => [...s].map(c => Node(c, {aspect: 1})); +let rows = data.map(make_blocks); +let stack = VStack(rows.map(HStack), {expand: false}); +return Frame(stack, {margin: 0.05}); + +// Plot of a sine function where the line markers vary in color from blue to red as the function value changes and the marker size increases with the amplitude. The x-axis is labeled "phase" and the y-axis is labeled "amplitude". The plot is titled "Inverted Sine Wave". +let xlim = [0, 2*pi], ylim = [-1, 1]; +let func = x => -sin(x); +let pal = x => interpolateHex('#1e88e5', '#ff0d57', x); +let xticks = linspace(0, 2, 6).slice(1).map(x => [x*pi, `${rounder(x, 1)} π`]); +let line = SymPath({fy: func, xlim}); +let points = SymPoints({ + fy: func, xlim, N: 21, size: 0.04, + fs: (x, y) => Circle({fill: pal((1+y)/2), rad: (1+abs(y))/2}) +}); +let plot = Plot([line, points], { + xlim, ylim, xanchor: 0, aspect: 1.5, xaxis_tick_pos: 'both', + xticks, yticks: 5, xgrid: true, ygrid: true, xlabel_offset: 0.1, + xlabel: 'phase', ylabel: 'amplitude', title: 'Inverted Sine Wave', + xgrid_stroke_dasharray: 3, ygrid_stroke_dasharray: 3 +}); +return Frame(plot, {margin: 0.25});