Chris@108: module yetilab.plot.plot; Chris@108: Chris@108: import org.jzy3d.plot3d.builder: Mapper; Chris@128: import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap; Chris@135: import org.jzy3d.plot3d.text.renderers.jogl: JOGLTextRenderer; Chris@119: import org.jzy3d.maths: Range, Coord3d; Chris@126: import org.jzy3d.plot3d.primitives: Shape, HistogramBar, FlatLine2d, Polygon, Quad, Point; Chris@119: import org.jzy3d.plot3d.primitives.axes.layout.providers: StaticTickProvider; Chris@133: import org.jzy3d.plot3d.primitives.axes.layout.renderers: ITickRenderer, TickLabelMap; Chris@108: import org.jzy3d.chart: Chart, ChartLauncher; Chris@108: import org.jzy3d.plot3d.builder: Builder; Chris@108: import org.jzy3d.plot3d.builder.concrete: OrthonormalGrid; Chris@108: import org.jzy3d.colors.colormaps: ColorMapRainbow; Chris@119: import org.jzy3d.colors: ColorMapper, Color; Chris@108: import org.jzy3d.plot3d.rendering.canvas: Quality; Chris@119: import org.jzy3d.plot3d.rendering.view.modes: ViewPositionMode; Chris@108: Chris@132: chartColours = array [ Chris@132: { r = 82, g = 126, b = 154 }, // dark steel blue Chris@132: { r = 161, g = 54, b = 2 }, // red Chris@132: { r = 207, g = 228, b = 148 }, // grey-green Chris@132: { r = 21, g = 183, b = 197 }, // light blue Chris@132: { r = 251, g = 116, b = 43 }, // light red Chris@132: { r = 200, g = 125, b = 234 }, // light purple Chris@132: { r = 126, g = 33, b = 28 }, // dried blood! Chris@132: { r = 188, g = 13, b = 207 }, // mid purple Chris@131: ]; Chris@131: Chris@132: chartColour n = Chris@132: if n < 0 Chris@132: then chartColour (-n) Chris@132: else Chris@132: rgb = chartColours[n % (length chartColours)]; Chris@132: new Color(rgb.r / 255.0, rgb.g / 255.0, rgb.b / 255.0); Chris@132: fi; Chris@132: Chris@108: newMatrixMapper matrix = Chris@108: (class MMapper extends Mapper Chris@108: double f(double x, double y) Chris@117: result = matrix.getAt y x; Chris@117: println "f(\(x),\(y)) -> \(result)"; Chris@117: result Chris@108: end; Chris@108: new MMapper()); Chris@108: Chris@108: newMatrixLogMapper matrix = Chris@108: (class MMapper extends Mapper Chris@108: double f(double x, double y) Chris@108: ln (matrix.getAt y x) Chris@108: end; Chris@108: new MMapper()); Chris@108: Chris@108: newMapper mapFunction = Chris@108: (class FMapper extends Mapper Chris@108: double f(double x, double y) Chris@108: mapFunction x y Chris@108: end; Chris@108: new FMapper()); Chris@108: Chris@133: newPercentTickRenderer () = Chris@133: (f v = " \(int (v * 100))%"; Chris@133: class PercentageTickRenderer extends ITickRenderer Chris@133: String format(double value) f value, Chris@133: String format(float value) f value Chris@133: end; Chris@133: new PercentageTickRenderer()); Chris@133: Chris@132: parseOptions options defaultKeys defaultXKeys = Chris@132: (parsed = { Chris@133: var keys = array (sort defaultKeys), Chris@132: var labels = [:], Chris@132: var animated = false, Chris@132: var normalised = false, Chris@132: var unit = "", Chris@135: var xkeys = array (sort defaultXKeys), Chris@135: var horizontal = false, Chris@132: }; Chris@132: for options Chris@132: \case of Chris@133: Keys kk: parsed.keys := array kk; Chris@133: XKeys xk: parsed.xkeys := array xk; Chris@132: Animated a: parsed.animated := a; Chris@132: Normalised n: parsed.normalised := n; Chris@132: Unit u: parsed.unit := u; Chris@132: Labels ll: parsed.labels := ll; Chris@135: Horizontal h: parsed.horizontal := h; Chris@132: esac; Chris@132: if empty? parsed.labels then Chris@132: parsed.labels := mapIntoHash id id parsed.keys Chris@132: fi; Chris@132: parsed); Chris@132: Chris@115: plotMatrix matrix = Chris@117: (mapper = newMatrixMapper matrix; Chris@108: size = matrix.size; Chris@119: xrange = new Range(0, size.columns - 1); Chris@119: yrange = new Range(0, size.rows - 1); Chris@108: grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows); Chris@112: println "Matrix size: \(size)"; Chris@117: surface = Builder#buildOrthonormal(grid, mapper); //??? big? Chris@117: println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())"; Chris@121: surface#setFaceDisplayed(true); Chris@108: surface#setWireframeDisplayed(true); Chris@108: surface#setWireframeColor(Color#BLACK); Chris@112: // chart = new Chart(Quality#Fastest, "swing"); Chris@112: chart = new Chart(Quality#Nicest); Chris@108: chart#getScene()#getGraph()#add(surface); Chris@115: ChartLauncher#openChart(chart); Chris@115: ()); Chris@108: Chris@135: newRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a = Chris@135: (poly = new Quad(); Chris@135: poly#add(new Point(new Coord3d(x + 0.5, z, y0))); Chris@135: poly#add(new Point(new Coord3d(x + 0.5, z, y1))); Chris@135: poly#add(new Point(new Coord3d(x - 0.5, z, y1))); Chris@135: poly#add(new Point(new Coord3d(x - 0.5, z, y0))); Chris@135: poly#setWireframeDisplayed(true); Chris@135: poly#setWireframeColor(colour); Chris@135: poly#setFaceDisplayed(true); Chris@135: poly#setColor(colour); Chris@135: poly); Chris@135: Chris@135: newProjectedRect x y0 y1 z colour is number -> number -> number -> number -> ~Color -> 'a = Chris@135: (poly = new Quad(); Chris@135: poly#add(new Point(new Coord3d(x + 0.5, y0, z))); Chris@135: poly#add(new Point(new Coord3d(x + 0.5, y1, z))); Chris@135: poly#add(new Point(new Coord3d(x - 0.5, y1, z))); Chris@135: poly#add(new Point(new Coord3d(x - 0.5, y0, z))); Chris@135: poly#setWireframeDisplayed(true); Chris@135: poly#setWireframeColor(colour); Chris@135: poly#setFaceDisplayed(true); Chris@135: poly#setColor(colour); Chris@135: poly); Chris@135: Chris@132: plotBarChart options values = Chris@132: (opts = parseOptions options (keys values) []; Chris@132: quality = Quality#Fastest; Chris@132: quality#setAnimated(opts.animated); Chris@131: chart = new Chart(quality); Chris@132: var n = length opts.keys; Chris@119: scene = chart#getScene(); Chris@132: ticks = new float[n+1]; Chris@119: tickLabels = new TickLabelMap(); Chris@132: var i = 0; Chris@132: var x = n - i - 1; Chris@134: total = sum (map do k: if k in values then values[k] else 0 fi done opts.keys); Chris@132: for opts.keys do k: Chris@134: v = if k in values then values[k] else 0 fi; Chris@133: v = if opts.normalised and total > 0 then v / total else v fi; Chris@135: c = chartColour i; Chris@135: scene#add(newRect x 0 v 1 c); Chris@135: scene#add(newProjectedRect x 0 v 1 c); Chris@132: ticks[i] := i; Chris@132: tickLabels#register(x, opts.labels[k]); Chris@132: i := i + 1; Chris@132: x := x - 1; Chris@119: done; Chris@135: if opts.horizontal then Chris@135: chart#getView()#setViewPoint(new Coord3d(0, pi/2, 0)); Chris@135: else Chris@135: chart#getView()#setViewPoint(new Coord3d(pi/2, 0, 0)); Chris@135: fi; Chris@135: Chris@119: axes = chart#getAxeLayout(); Chris@135: if opts.horizontal then Chris@135: axes#setZAxeLabelDisplayed(false); Chris@135: axes#setZTickLabelDisplayed(false); Chris@135: axes#setYAxeLabelDisplayed(true); Chris@135: if opts.normalised then Chris@135: axes#setYAxeLabel(""); Chris@135: axes#setYTickRenderer(newPercentTickRenderer ()); Chris@135: else Chris@135: axes#setYAxeLabel(opts.unit); Chris@135: fi; Chris@135: else Chris@135: axes#setYAxeLabelDisplayed(false); Chris@135: axes#setYTickLabelDisplayed(false); Chris@135: axes#setZAxeLabelDisplayed(true); Chris@135: if opts.normalised then Chris@135: axes#setZAxeLabel(""); Chris@135: axes#setZTickRenderer(newPercentTickRenderer ()); Chris@135: else Chris@135: axes#setZAxeLabel(opts.unit); Chris@135: fi; Chris@135: fi; Chris@135: Chris@119: axes#setXAxeLabelDisplayed(false); Chris@119: axes#setXTickProvider(new StaticTickProvider(ticks)); Chris@119: axes#setXTickRenderer(tickLabels); Chris@135: Chris@119: ChartLauncher#openChart(chart); Chris@119: ()); Chris@119: Chris@132: plotLines options values = Chris@132: (opts = parseOptions options (keys values) (keys values[head (keys values)]); Chris@132: quality = Quality#Fastest; Chris@132: quality#setAnimated(opts.animated); Chris@131: chart = new Chart(quality); Chris@124: scene = chart#getScene(); Chris@132: n = length opts.xkeys; Chris@124: var z = 0; Chris@132: for opts.keys do k: Chris@124: v = values[k]; Chris@124: x = new float[n]; Chris@124: y = new float[n]; Chris@124: var i = 0; Chris@132: for opts.xkeys do xk: Chris@124: x[i] := i; Chris@124: y[i] := if xk in v then v[xk] else 0 fi; Chris@124: i := i + 1; Chris@124: done; Chris@124: line = new FlatLine2d(x, y, z); Chris@124: line#setWireframeDisplayed(true); Chris@132: line#setWireframeColor(chartColour z); Chris@124: line#setWireframeWidth(2); Chris@124: line#setFaceDisplayed(false); Chris@124: scene#add(line); Chris@124: z := z + 1; Chris@124: done; Chris@124: chart#getView()#setViewPoint(new Coord3d(0, 0, 0)); Chris@124: axes = chart#getAxeLayout(); Chris@124: axes#setXAxeLabelDisplayed(false); Chris@124: axes#setYAxeLabelDisplayed(false); Chris@124: axes#setZAxeLabelDisplayed(true); Chris@132: axes#setZAxeLabel(opts.unit); Chris@124: axes#setYTickLabelDisplayed(false); Chris@124: ChartLauncher#openChart(chart); Chris@124: ()); Chris@124: Chris@129: stack keys xkeys values normalised = Chris@127: (stacked = mapIntoHash id \(mapIntoHash id \{ y0 = 0, y1 = 0 } xkeys) keys; Chris@125: prev = mapIntoHash id \0 xkeys; Chris@129: valueOf k xk = if xk in values[k] then values[k][xk] else 0 fi; Chris@125: for xkeys do xk: Chris@129: total = sum (map do k: valueOf k xk done keys); Chris@125: for keys do k: Chris@129: value = Chris@129: if normalised and total > 0 Chris@129: then (valueOf k xk) / total Chris@129: else (valueOf k xk) Chris@129: fi; Chris@125: height = prev[xk] + value; Chris@127: stacked[k][xk] := { y0 = prev[xk], y1 = height }; Chris@125: prev[xk] := height; Chris@125: done; Chris@125: done; Chris@125: stacked); Chris@125: Chris@132: plotStacked options values = Chris@132: (opts = parseOptions options (keys values) (keys values[head (keys values)]); Chris@132: quality = Quality#Fastest; Chris@132: quality#setAnimated(opts.animated); Chris@128: chart = new Chart(quality); Chris@125: scene = chart#getScene(); Chris@132: stacked = stack opts.keys opts.xkeys values opts.normalised; Chris@125: var z = 0; Chris@130: var ty = 0; Chris@133: xtickLabels = new TickLabelMap(); Chris@133: for [0..length opts.xkeys - 1] do x: Chris@133: xtickLabels#register(x, opts.xkeys[x]); Chris@133: done; Chris@132: for opts.keys do k: Chris@127: ranges = stacked[k]; Chris@132: c = chartColour z; Chris@133: for [0..length opts.xkeys - 1] do x: Chris@133: xk = opts.xkeys[x]; Chris@127: rect = newRect x ranges[xk].y0 ranges[xk].y1 z c; Chris@127: scene#add(rect); Chris@125: done; Chris@132: text = new DrawableTextBitmap(opts.labels[k], new Coord3d(-5, z, ty), c); Chris@128: scene#add(text); Chris@126: z := z - 1; Chris@130: ty := ty + 0.1; Chris@125: done; Chris@129: chart#getView()#setViewPoint(new Coord3d(-pi/2, 0, 0)); Chris@125: axes = chart#getAxeLayout(); Chris@125: axes#setXAxeLabelDisplayed(false); Chris@133: axes#setXTickRenderer(xtickLabels); Chris@125: axes#setYAxeLabelDisplayed(false); Chris@125: axes#setZAxeLabelDisplayed(true); Chris@133: if opts.normalised then Chris@133: axes#setZAxeLabel(""); Chris@133: axes#setZTickRenderer(newPercentTickRenderer ()); Chris@133: else Chris@133: axes#setZAxeLabel(opts.unit); Chris@133: fi; Chris@125: axes#setYTickLabelDisplayed(false); Chris@125: ChartLauncher#openChart(chart); Chris@125: ()); Chris@124: Chris@110: plotStructure structure = Chris@110: case structure of Chris@110: Grid matrix: Chris@115: plotMatrix matrix; Chris@110: //!!! Chris@110: _: failWith "Cannot plot this structure (only grids implemented so far)"; Chris@110: esac; Chris@108: Chris@115: { Chris@119: newMatrixMapper, Chris@119: newMatrixLogMapper, Chris@119: newMapper, Chris@115: plotMatrix, Chris@124: plotBarChart, Chris@124: plotLines, Chris@125: stack, Chris@125: plotStacked, Chris@119: plotStructure, Chris@115: } Chris@110: