{"id":19881559,"url":"https://github.com/chifisource/gattino.jl","last_synced_at":"2025-06-23T12:37:13.303Z","repository":{"id":40629534,"uuid":"358812235","full_name":"ChifiSource/Gattino.jl","owner":"ChifiSource","description":"elegant data visualization for julia","archived":false,"fork":false,"pushed_at":"2024-05-21T06:17:32.000Z","size":461,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-22T08:59:48.384Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ChifiSource.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["emmaccode","UnformalPenguin"]}},"created_at":"2021-04-17T07:19:42.000Z","updated_at":"2024-05-30T11:26:40.000Z","dependencies_parsed_at":"2023-12-17T12:24:10.957Z","dependency_job_id":"6ef2d4f3-17e8-4d37-9960-8b5d590840ce","html_url":"https://github.com/ChifiSource/Gattino.jl","commit_stats":null,"previous_names":["chifisource/gattino.jl"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ChifiSource/Gattino.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FGattino.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FGattino.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FGattino.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FGattino.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ChifiSource","download_url":"https://codeload.github.com/ChifiSource/Gattino.jl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ChifiSource%2FGattino.jl/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261480775,"owners_count":23164984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-12T17:14:37.170Z","updated_at":"2025-06-23T12:37:08.290Z","avatar_url":"https://github.com/ChifiSource.png","language":"Julia","funding_links":["https://github.com/sponsors/emmaccode","https://github.com/sponsors/UnformalPenguin"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/gattino.png\" width = 250 /\u003e\n  \u003ch4\u003egattino\u003c/h4\u003e\n\u003c/div\u003e\n\u003cdiv align=\"left\"\u003e\n\nGattino is Toolips-based, extensible plotting for Julia. `Gattino` features ...\n- composable, **fully-featured** data-visualization\n- an *extension ecosystem*\n- [toolips](https://github.com/ChifiSource/Toolips.jl) compatibility\n- powerful **layout** syntax\n\nThere is currently a lot underway when it comes to [Chifi](https://github.com/ChifiSource/) packages, so this package is currently a **work in progress**.\n##### map\n- [getting started](#getting-started)\n   - [adding gattino](#adding-gattino)\n   - [resources](#resources)\n- [visualizations](#visualizations)\n  - [creating visualizations](#creating-visualizations)\n  - [layouts](#layouts)\n  - [working with layers](#working-with-layers)\n    - [styling layers](#styling-layers)\n    - [editing layers](#editing-layers)\n    - [setting attributes](#setting-attributes)\n    - [shape](#shape)\n  - [legends](#legends)\n  - [annotations](#annotations)\n  - [animation](#animation)\n- [context plotting](#context-plotting)\n  - [lines](#plotting-lines)\n  - [shapes](#plotting-shapes)\n  - [other](#plotting-other-stuff)\n- [dashboards](#dashboards)\n- [examples](#examples)\n  - [styled multichart](#styled-multichart)\n- [adding more](#adding-more)\n## getting started\n##### adding gattino\nBefore `Gattino` is merged to the Julia `General` `Pkg` registry, `Gattino will need to be added by URL.\n```julia\nusing Pkg; Pkg.add(url = \"https://github.com/ChifiSource/Gattino.jl\")\nusing Gattino\n```\nIf you would like to use the `Unstable` version of Gattino, which will have more features but be less stable, set the `rev` key-word argument to `Unstable`.\n```julia\nusing Pkg; Pkg.add(url = \"https://github.com/ChifiSource/Gattino.jl\", rev = \"Unstable\")\nusing Gattino\n```\n###### resources\n[chifi](https://github.com/ChifiSource) is currently working an in-ecosystem [Olive](https://github.com/ChifiSource/Olive.jl)-based documentation (and notebook) webapp which will hold the documentation for this project as well as other modules from this organization. While this new interactive documentation is still in the works, the resources for information on `Gattino` will be limited to\n- this `README`\n- [gattino notebooks](https://github.com/ChifiSource/OliveNotebooks.jl/tree/main/gattino)\n\nFortunately, we have a lot of plans for resources coming in the future and if this `README` is on the main branch it probably means that these plans are pretty well in motion; `Gattino` is meant to be coming at around the same time as these new resources.\n## visualizations\n- [notebook](https://github.com/ChifiSource/OliveNotebooks.jl/blob/main/gattino/doc/gattino_visualizations.jl)\n##### creating visualizations\nWhile `Gattino` plots are completely composable and can be made by composing [context plotting](#context-plotting) elements together, the module also comes with some high-level functions which may be used to produce standard visualizations we are likely familiar with. These examples currently include `scatter`, `line`, and `hist`.\n```julia\nscatter(x::Vector, y::Vector, divisions::Int64 = 4, title::String = \"\")\n\nline(x::Vector, y::Vector, divisions::Int64 = 4, title::String = \"\")\n\nhist(x::Vector, y::Vector, divisions::Int64 = 4, title::String = \"\")\n```\nThese methods are used to create a minimalistic visualization which can be further mutated with other `Gattino` methods. In each of these examples,the return type will be an `AbstractContext`. Let's make our first histogram with `Gattino`.\n```julia\nmyhist = hist([\"emma\", \"emmy\", \"em\"], [22, 25, 14], title = \"votes for names\")\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/firsthist.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nThe `hist` function is just a passthrough to `hist!`, which is a [context plotting](#context-plotting) function that creates a histogram. Notably, `hist` will create a new `Context` for us and `hist!` expects us to provide an `AbstractContext` as an argument. The `line` and `scatter` equivalence to this is found in `scatter_plot!` and `line_plot!`. That being said, if we want to add a visualization to a `Context` that already exists, we would use these methods, rather than the high-level method. When using `hist!` we will want to add our histogram to an old plot, when using `hist` we will be making a new plot with a new window. Here, I will use the `context` and `group!` functions to compose a scatter with the `scatter_plot!` method.\n```julia\nmyframe = context(500, 500) do con::Context\n    group!(con, \"scatter\", 250, 250) do g::Group\n        Gattino.scatter_plot!(g, [1, 2, 3, 4], [1, 2, 3, 4])\n    end\nend\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/secondplot.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nGraphics in `Gattino` are scaled using the `Context` and `Group` types. A `Context` represents a window, whereas a `Group` represents an area in that window. To create a context, we use the `context` method.\n- `context(f::Function, width::Int64 = 1280, height::Int64= 720, margin::Pair{Int64, Int64} = 0 =\u003e 0)`\n```julia\nmycontext = context(500, 500) do con::Context\n\nend\n```\nThis `Context` can now be used with [context plotting](#context-plotting) methods. There are two different types of `group` which we can use on our project,\n` `group!` is the mutating group -- this will add anything drawn to the group to the `Context`.\n- `group` is non-mutating group -- anything we draw will not be drawn onto the window.\n\nThe methods are\n\n- `group(f::Function, c::AbstractContext, w::Int64, h::Int64, margin::Pair{Int64, Int64})`\n- `group!(f::Function, c::AbstractContext, name::String, w::Int64, h::Int64, margin::Pair{Int64, Int64})\n\nThese dispatches are for the most part the same as the `context` method. The `width`, `height`, and `margin` will all default to those of the provided `AbstractContext`. Additionally, `group!` will take the name of the layer as the second positional argument.\nThese two forms of group are used in tandem to organize the layers of our `Context`. `group` is used to define new `AbstractContext` dimensions without adding a layer, whereas `group!` will add a new layer in those dimensions. For example.\n```julia\nmycon = context(500, 500) do con::Context\n    group(con, 500, 250) do gridbox::Group\n        group!(gridbox, \"grid\") do g::Group\n            Gattino.grid!(g, 4)\n        end\n    end\n    group(con, 500, 250, 0 =\u003e 250) do otherbox::Group\n        group!(otherbox, \"grid2\") do g::Group\n            Gattino.grid!(g, 4, \"stroke\" =\u003e \"pink\")\n            \n        end\n    end\n    Gattino.text!(con, 230, 250, \"hello!\")\nend\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/griddemonstration.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nIn this case, I used `group` to create an initial `AbstractContext` with a certain dimensionality so that we could draw a bunch of things onto it. Note the use of `group` in this case, as I do not want this group to be drawn as a layer it is only used to change the dimensions. Next, I used `group!` whenever I actually wanted to draw onto the grid. The advantage to using `group!` like this is that we get all of the elements on different layers. We can access these layers with the `layers` function.\n```julia\nlayers(mycon)\n\n3-element Vector{Pair{Int64, String}}:\n 1 =\u003e \"grid\"\n 2 =\u003e \"grid2\"\n 3 =\u003e \"la81WFbV\"\n```\n##### layouts\nWith the last example, we got an idea of how we might stack plots on top of one another. We have two different options which may be used to create layouts. The first of these options was demonstrated prior, this is using the `margin`, `height`, and `width` arguments with `Groups` to draw scaled frames in different portions of our window. Our `width`, `height`, and `margins` are provided to either the `context` method or one of the `group` methods (`group`/`group!`) in that order. Our margins push our frame to the right or down as they increase.\n```julia\nmyframe = context(500, 250) do con::Context\n    group(con, 250, 250) do g::Group\n        Gattino.scatter_plot!(g, [1, 2, 3, 4], [1, 2, 3, 4])\n    end\nend\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/layoutsdemonstration.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nIn this case, we have a `Context`, or window, of width **500** and height **250**. The group we created below this is of width **250** and of height **250** -- the full height and half of the width. Let's add a grid with a `margin` of **250** on the X with the same size. This will make it easier to discern the difference between these visualizations.\n```julia\ngroup(myframe, 250, 250, 250 =\u003e 0) do g::Group\n    Gattino.grid!(g, 4, \"stroke\" =\u003e \"pink\")\nend\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/layoutsdemonstration2.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nThis form of layouts is **for the inside of a `Context`**. We also have the option to form layouts by placing Contexts next to eachother. To do this, use the `compose` function. This takes a name, the name of the window we want to create, and then our `Context`(s).\n```julia\nplt = context(200, 200) do con::Context\n    group!(con, \"points\") do g::Group\n        Gattino.points!(g, [5, 10, 15], [5, 10, 15])\n    end\nend\nplt2 = context(200, 200) do con::Context\n    group!(con, \"points\") do g::Group\n        Gattino.points!(g, [5, 10, 15], [5, 10, 15], \"fill\" =\u003e \"blue\")\n    end\nend\nn = compose(\"new\", plt2, plt)\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/composedemonstration.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nWith this technique, we are also able to add different types of contexts together, and this is very useful for making full dashboard layouts with very little effort. We have three main functions for this,\n```julia\nhcat\npush!\nvcat\n```\nConcatenating horizontally will add the `Context` horizontally, vertically will add it vertically and `push!` will of course also concatenate horizontally.\n```julia\nvcat(n, plt, plt2)\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/composedemonstration2.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\n#### working with layers\n- [notebook](https://github.com/ChifiSource/OliveNotebooks.jl/blob/main/gattino/doc/gattino_layers.jl)\n\nAn important aspect to `Gattino` is the layering aspect. In `Gattino`, visualizations are premade from the [context plotting](#context-plotting) toolkit and then mutated by making changes to the layers. We are able to access the layers of an `AbstractContext` using the `layers` method.\n```julia\nlayers(con::AbstractContext)\n```\n```julia\nusing Gattino\nmyvis = Gattino.scatter([10, 4, 3, 4, 6, 4], [10, 3, 5, 3, 4, 4])\nlayers(myvis)\n\n5-element Vector{Pair{Int64, String}}: 1 =\u003e \"axes\" 2 =\u003e \"grid\" 3 =\u003e \"points\" 4 =\u003e \"labels\" 5 =\u003e \"axislabels\"\n```\nWe can also index a `Context` with a `String` to retrieve a layer directly.\n```julia\ngetindex(con::AbstractContext, str::String)\n```\n```julia\nmyvis[\"points\"]\n```\nWe can also mutate layers using the various methods `Gattino` provides to do so. These include\n- `style!(con::AbstractContext, s::String, spairs::Pair{String, String} ...)`\n- `move_layer!(con::Context, layer::String, to::Int64)`\n- `delete_layer!(con::Context, layer::String)`\n- `merge!(c::AbstractContext, c2::AbstractContext)`\n- `open_layer!(f::Function, con::AbstractContext, layer::String)`\n##### styling layers\nThe first thing we are going to want to do with our new `Gattino` visualization is probably style it, for this we use the following style dispatch:\n- `style!(con::AbstractContext, s::String, spairs::Pair{String, String} ...)`\n\nLet's get our histogram back from earlier.\n```julia\nmyhist = Gattino.hist([\"emma\", \"emmy\", \"em\"], [22, 25, 14], title = \"votes for names\")\n```\nIn order to use this dispatch, we will need to provide a layer name as the second argument. In order to check the layers currently in your `Context`, use `layers(::AbstractContext)`. Let's try this on [the histogram we created](#creating-visualizations).\n```julia\nlayers(myhist)\n7-element Vector{Pair{Int64, String}}:\n1 =\u003e \"XS3ms8yi\"\n2 =\u003e \"title\"\n3 =\u003e \"axes\"\n4 =\u003e \"grid\"\n5 =\u003e \"bars\"\n6 =\u003e \"labels\"\n7 =\u003e \"axislabels\"\n```\nWe are able to style these all individually with the `style!` dispatch we created earlier. These `style!` calls are simply CSS pairs from Toolips. Let's change the `fill` of our bars and make some other adjustmnets to the labels.\n```julia\nstyle!(myhist, \"bars\", \"fill\" =\u003e \"orange\", \"opacity\" =\u003e 70percent)\nstyle!(myhist, \"labels\", \"stroke-width\" =\u003e 0px, \"fill\" =\u003e \"white\", \"font-weight\" =\u003e \"bold\")\n```\n\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/histstyled.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\nWe may also style our window itself with\n\n```julia\nstyle!(myhist, \"border\" =\u003e \"5px solid black\")\n```\n##### editing layers\nEditing layers will primarily consist of either setting the attributes of layers using `open_layer!`, or using one of the following to mutate the state of the layers:\n- `set!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, prop::Symbol, value::Any)`\n- `set!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, prop::Symbol, vec::Vector{\u003c:Number}; max::Int64 = 10)`\n- `move_layer!(con::AbstractContext, layer::String, to::Int64)`\n- `delete_layer!(con::Context, layer::String)`\n- `merge!(con::Context, othercon::Context)`\n\n`move_layer!` and `delete_layer!` are both straightforward.\n```julia\nlayers(myhist)\n7-element Vector{Pair{Int64, String}}: 1 =\u003e \"4qGRocCq\" 2 =\u003e \"title\" 3 =\u003e \"axes\" 4 =\u003e \"grid\" 5 =\u003e \"bars\" 6 =\u003e \"labels\" 7 =\u003e \"axislabels\"\n\nmove_layer!(myhist, \"axislabels\", 2)\n\n7-element Vector{Pair{Int64, String}}: 1 =\u003e \"4qGRocCq\" 2 =\u003e \"axislabels\" 3 =\u003e \"title\" 4 =\u003e \"axes\" 5 =\u003e \"grid\" 6 =\u003e \"bars\" 7 =\u003e \"labels\"\n\ndelete_layer!(myhist, \"axislabels\")\n\n\n6-element Vector{Pair{Int64, String}}: 1 =\u003e \"4qGRocCq\" 2 =\u003e \"title\" 3 =\u003e \"axes\" 4 =\u003e \"grid\" 5 =\u003e \"bars\" 6 =\u003e \"labels\"\n```\n`merge!` is used to merge to `Contexts` together. This will essentially just copy the shapes, and does no scaling.\n```julia\nGattino.merge!(myhist, myvis)\n```\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/mergesample.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\n##### setting attributes\nThe final piece of this puzzle is the ability to mutate the `properties` of each `Component` in a layer. This is done with `set!` functions, which will take a `Pair` with our layer element and its enumeration. For many of these functions, we are also able to add a `Vector` to change these `properties` based on the values of a continuous feature.\n- `set!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, prop::Symbol, to::Any)`\n- `set!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, prop::Symbol, vec::Vector{\u003c:Number}; max::Int64 = 10)`\n- `style!(ecomp::Pair{Int64, \u003c:Toolips.AbstractComponent}, vec::Vector{\u003c:Number}, stylep::Pair{String, Int64} ...)`\n- `set_gradient!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, vec::Vector{\u003c:Number}, colors::Vector{String})`\n- `set_shape!(ecomp::Pair{Int64, \u003c:Toolips.Servable}, shape::Symbol)`\n\nTo get started, we will create a visualization and then open a layer.\n\n```julia\nmycon = context(500, 500) do con::Context\n    Gattino.scatter_plot!(con, firstfeature, secondfeature)\nend\n\nGattino.open_layer!(mycon, \"points\") do ec\n\nend\n```\nHere, I will use `style!`, `set_gradient!`, and `set!` to show three different features:\n```julia\nGattino.open_layer!(mycon, \"points\") do ec\n     Gattino.set!(ec, :r, thirdfeature, max = 60)\n     style!(ec, fourth_feature, \"stroke-width\" =\u003e 10)\n     Gattino.set_gradient!(ec, fourth_feature)\nend\n```\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/opensample.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\n##### annotations\nBecause `Gattino` plot features are composable, we are able to easily annotate `Gattino` plots \n##### animation\nAnimation in `Gattino` centers around `Toolips`' `Animation` syntax. To create an animation, we will call the `Animation` constructor, like so:\n```julia\nmyanim = Animation(\"myanim\", iterations = 1, length = 1.1)\n```\nNow we set the steps of our animation, this can be done with percentages or `:from` and `:to`:\n```julia\nmyanim[:from] = \"opacity\" =\u003e 0percent\nmyanim[:from] = \"transform\" =\u003e \"scale(0)\"\nmyanim[:to] = \"opacity\" =\u003e 100percent\nmyanim[:to] = \"transform\" =\u003e \"scale(1)\"\n```\nAnd we finish by calling `animate!` on our `Context` and selecting a layer:\n```julia\nplt = context(200, 200) do con::Context\n    group!(con, \"points\") do g::Group\n        Gattino.points!(g, [5, 10, 15], [5, 10, 15])\n    end\n    Gattino.animate!(con, \"points\", myanim)\nend\n```\nOnce an animation is registered into a `Context`, it is there until that `Context` is recreated, or can be cleared manually from `Context.window.extras`. A defined animation can be set again when using [GattinoInteractive](https://github.com/ChifiSource/GattinoInteractive.jl) or a similar interactive extension package for `Gattino`.\n## context plotting\n#### plotting lines\n#### plotting shapes\n#### plotting other stuff\n## dashboards\n`Gattino` is based on an extensible web-development framework for julia, `Toolips`. As a result, creating A `Gattino` dashboard mostly consists of composing `Gattino` visualizations and then serving them to a `Toolips` server or exporting them as HTML.\n## examples\n#### styled multichart\n- [notebook](https://github.com/ChifiSource/OliveNotebooks.jl/blob/main/gattino/examples/styled_multichart.jl)\n\u003cdiv align=\"center\"\u003e\u003cimg src=\"https://github.com/ChifiSource/image_dump/blob/main/gattino/docsc/styledmultichart.png\"\u003e\u003c/img\u003e\u003c/div\u003e\n\n\n```julia\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchifisource%2Fgattino.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchifisource%2Fgattino.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchifisource%2Fgattino.jl/lists"}