{"id":13423666,"url":"https://github.com/coolbutuseless/devout","last_synced_at":"2026-03-10T14:34:34.085Z","repository":{"id":95359498,"uuid":"184396125","full_name":"coolbutuseless/devout","owner":"coolbutuseless","description":"Write R graphics output devices in plain R","archived":false,"fork":false,"pushed_at":"2022-01-23T06:30:36.000Z","size":848,"stargazers_count":98,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-02T00:41:34.715Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://coolbutuseless.github.io/package/devout/","language":"C++","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/coolbutuseless.png","metadata":{"files":{"readme":"README.Rmd","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}},"created_at":"2019-05-01T09:41:18.000Z","updated_at":"2024-09-04T14:33:38.000Z","dependencies_parsed_at":"2023-07-04T20:16:57.990Z","dependency_job_id":null,"html_url":"https://github.com/coolbutuseless/devout","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/coolbutuseless/devout","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coolbutuseless%2Fdevout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coolbutuseless%2Fdevout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coolbutuseless%2Fdevout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coolbutuseless%2Fdevout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coolbutuseless","download_url":"https://codeload.github.com/coolbutuseless/devout/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coolbutuseless%2Fdevout/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30337305,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T12:41:07.687Z","status":"ssl_error","status_checked_at":"2026-03-10T12:41:06.728Z","response_time":106,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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-07-31T00:00:40.193Z","updated_at":"2026-03-10T14:34:34.068Z","avatar_url":"https://github.com/coolbutuseless.png","language":"C++","funding_links":[],"categories":["C++","ggplot"],"sub_categories":["Devices"],"readme":"---\noutput: github_document\n---\n\n\u003c!-- README.md is generated from README.Rmd. Please edit that file --\u003e\n\n```{r, include = FALSE}\nsuppressPackageStartupMessages({\n  library(devout)\n  library(ggplot2)\n  library(dplyr)\n  library(grid)\n})\nknitr::opts_chunk$set(\n  collapse = FALSE,\n  comment = \"\",\n  fig.path = \"man/figures/README-\",\n  out.width = \"100%\"\n)\n\n\nset.seed(1)\nmtcars \u003c- mtcars %\u003e% \n  mutate(cyl = as.factor(cyl))\n```\n\n\n\n\n```{r echo = FALSE, eval = FALSE}\n# Quick logo generation. Borrowed heavily from Nick Tierney's Syn logo process\nlibrary(magick)\nlibrary(showtext)\nfont_add_google(\"Abril Fatface\", \"gf\")\n\n\n# pkgdown::build_site(override = list(destination = \"../coolbutuseless.github.io/package/devout\"))\n```\n\n\n```{r echo = FALSE, eval = FALSE}\nimg \u003c- image_read(\"man/figures/white.png\")\n\n\nhexSticker::sticker(subplot  = img,\n                    s_x      = 0.92,\n                    s_y      = 1.2,\n                    s_width  = 1.5,\n                    s_height = 0.95,\n                    package  = \"/dev/out/\",\n                    p_x      = 1,\n                    p_y      = 1,\n                    p_color  = \"#223344\",\n                    p_family = \"gf\",\n                    p_size   = 9,\n                    h_size   = 1.2,\n                    h_fill   = \"#ffffff\",\n                    h_color  = \"#223344\",\n                    filename = \"man/figures/logo.png\")\n\nimage_read(\"man/figures/logo.png\")\n```\n\n\n\n# devout   \u003cimg src=\"man/figures/logo.png\" align=\"right\" height=230/\u003e\n\n\u003c!-- badges: start --\u003e\n![](https://img.shields.io/badge/cool-useless-green.svg)\n![](http://img.shields.io/badge/dev-out-blue.svg)\n[![R-CMD-check](https://github.com/coolbutuseless/devout/workflows/R-CMD-check/badge.svg)](https://github.com/coolbutuseless/devout/actions)\n\u003c!-- badges: end --\u003e\n\n\n`devout` is a package that enables R graphics devices to be written in plain R.\n\n`devout` uses a pseudo-graphics-device which translates graphics\ncalls into a call to an R function of your design.  \n\nThis means we can create alternative output devices (like `pdf()` or `png()`) using\nonly plain R.\n\n\n\n## How normal (C/C++) graphics devices work\n\n\n```{r echo=FALSE, eval=FALSE}\n# /Users/mike/projects/devout/man/figures/graffle/graphics-device/Canvas 4.png\nfile.copy(\"man/figures/graffle/graphics-device/Canvas 4.png\", \"man/figures/graffle/graphics-device/Canvas 4a.png\", overwrite = TRUE)\nsystem(\"convert -delay 300 man/figures/graffle/graphics-device/*.png man/figures/graffle/graphics-device/anim.gif\")\nsystem(\"convert man/figures/graffle/graphics-device/anim.gif \\\\( +clone -set delay 500 \\\\) +swap +delete  man/figures/graffle/graphics-device/animpause.gif\")\nsystem(\"gifsicle man/figures/graffle/graphics-device/animpause.gif --colors 8 \u003e man/figures/graffle/graphics-device/animo.gif\")\n\nunlink(\"man/figures/graffle/graphics-device/anim.gif\")\nunlink('man/figures/graffle/graphics-device/animpause.gif')\n```\n\n\u003cdetails open\u003e\n\u003csummary\u003e \u003cspan title='animation'\u003e animation (click to close) \u003c/summary\u003e\n\u003cimg src=\"man/figures/graffle/graphics-device/animo.gif\" /\u003e\n\u003c/details\u003e\n\n## How the devout device enables plain R graphics devices\n\n\n\n```{r echo=FALSE, eval=FALSE}\nsystem(\"convert -delay 300 man/figures/graffle/devout-device/*.png man/figures/graffle/devout-device/anim.gif\")\nsystem(\"convert man/figures/graffle/devout-device/anim.gif \\\\( +clone -set delay 500 \\\\) +swap +delete  man/figures/graffle/devout-device/animpause.gif\")\nsystem(\"gifsicle man/figures/graffle/devout-device/animpause.gif --colors 8 \u003e man/figures/graffle/devout-device/animo.gif\")\n\nunlink(\"man/figures/graffle/devout-device/anim.gif\")\nunlink('man/figures/graffle/devout-device/animpause.gif')\n```\n\n\u003cdetails open\u003e\n\u003csummary\u003e \u003cspan title='animation'\u003e animation (click to close) \u003c/summary\u003e\n\u003cimg src=\"man/figures/graffle/devout-device/animo.gif\" /\u003e\n\u003c/details\u003e\n\n## What's in the box\n\n* `rdevice()` - a generic device wrapper which will call the given R function to \n  handle the graphics drawing.\n* Two example devices written in plain R (but using the underlying `rdevice()`)\n    * `descriptive()` - an output device which dumps information about the device calls.\n    * `ascii()` - a graphics device which outputs an ascii representation of the plot \n       to a file, or to the console/terminal\n\n## How do I write my own graphics device?\n\nIf you want to write your own graphics device in plain R using `devout` you can:\n\n1. Read the `R/ascii-callback.R` included in the package\n2. Read the vignettes.\n\nA series of 4 vignettes are included in this package. They walk through the process\nof writing a naive SVG graphics device.\n\n* [Creating an SVG device - part 1 - The Callback Function](https://coolbutuseless.github.io/package/devout/articles/creating-an-svg-device-01.html)\n* [Creating an SVG device - part 2 - The Drawing Canvas](https://coolbutuseless.github.io/package/devout/articles/creating-an-svg-device-02.html)\n* [Creating an SVG device - part 3 - Rendering Graphics](https://coolbutuseless.github.io/package/devout/articles/creating-an-svg-device-03.html)\n* [Creating an SVG device - part 4 - %#$^ you I won't do what you tell me](https://coolbutuseless.github.io/package/devout/articles/creating-an-svg-device-04.html)\n\n## Installation\n\nYou can install from [GitHub](https://github.com/coolbutuseless/devout) with:\n\n``` r\n# install.packages(\"remotes\")\nremotes::install_github(\"coolbutuseless/devout\")\n```\n\n\n\n# Simple device written in plain R: `debug` device\n\nThe following 5 lines of code are about the simplest device you can write with \n`devout` in plain R.\n\nThis devices prints each of the device calls that were generated when the \nplot was \"drawn\".\n\n```{r}\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n# Write a function which takes 3 arguments\n# @param device_call name of device function call\n# @param args the arguments to that device call\n# @param state current state of the graphics device\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\ndebug_function \u003c- function(device_call, args, state) {\n  if (device_call %in% c('mode', 'strWidthUTF8', 'metricInfo')) return()\n  cat(\"[\", device_call, \"]: \")\n  cat(paste(names(args), args, sep = \" = \", collapse = \",  \"), \"\\n\", sep = \"\")\n}\n```\n\n\n```{r eval=FALSE}\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n# Call the 'rdevice' and tell it that all calls should be passed to \n# the above 'debug_function'\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nrdevice(debug_function) \nplot(1:10)\ninvisible(dev.off())\n```\n\n\n```{r echo=FALSE}\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n# Open the 'rdevice' and tell it that all graphics calls should \n# be passed to the above 'debug_function()'\n#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\nrdevice(debug_function) \nplot(1:10)\ninvisible(dev.off())\n```\n\n\n# `ascii()` device\n\nThis is an example of a more complete graphics device.  It is written in plain R and\nrelies on `devout` for all the interfacing with C++ and the internals of R.\n\nThe `ascii()` device draws an approximation of the graphics using ASCII characters\non the console (by default) or saved to a text file (if specified).\n\n\nLimitations\n\n* No support for: filled polygons, plotmath, alpha blending, angled text, rasters\n* You should probably always add `theme(legend.position = 'none')` because legends look awful in ascii.\n\n\n\n### `ggplot2`: Basic scatterplot\n\n```{r}\nlibrary(ggplot2)\nlibrary(devout)\n\np \u003c- ggplot(mtcars) + \n  geom_point(aes(mpg, wt)) +\n  labs(\n    y     = \"Car Weight\",\n    title    = \"Basic scatter plot\",\n    subtitle = \"Rendered with devout::ascii()\"\n  )  + \n  theme_bw()\n\nascii(width = 100)\np\ninvisible(dev.off())\n\n```\n\n\n\n\n### `pie` plot in base R\n\n```{r}\nascii(width = 100) \npie(c(cool = 4, but = 2, use = 1, less = 8))\ninvisible(dev.off())\n```\n\n\n\n### `geom_sf()` map of the Gulf of Mexico\n\n\u003cdetails closed\u003e\n\u003csummary\u003e \u003cspan title='Simple features example'\u003e geom_sf() example (click to open) \u003c/summary\u003e\n\n\n* Example taken from the [r-spatial website](https://www.r-spatial.org/r/2018/10/25/ggplot2-sf.html)\n* This would probably look better with filled polygons, but they are not supported yet.\n\n```{r warning = FALSE, eval = FALSE}\nlibrary(ggplot2)\nlibrary(sf)\nlibrary(rnaturalearth)\nworld \u003c- ne_countries(scale = \"medium\", returnclass = \"sf\")\n\nworld_points \u003c- st_centroid(world)\nworld_points \u003c- cbind(world, st_coordinates(st_centroid(world$geometry)))\n\nascii(width = 200)\nggplot(data = world) +\n  geom_sf() +\n  geom_text(data= world_points,aes(x=X, y=Y, label=name),\n            color = \"darkblue\", fontface = \"bold\", check_overlap = FALSE) +\n  annotate(geom = \"text\", x = -90, y = 26, label = \"Gulf of Mexico\", fontface = \"italic\", color = \"grey22\", size = 6) +\n  coord_sf(xlim = c(-102.15, -74.12), ylim = c(7.65, 33.97), expand = FALSE) + \n  theme_bw()\ninvisible(dev.off())\n```\n\n\u003cimg src = \"man/figures/gulfofmexico.png\"\u003e\n\n\u003c/details\u003e\u003cbr /\u003e\n\n\n\n\n## Ideas for other Output Devices\n\n* Colour ASCII/ANSI output\n* Audio output\n* HPGL plotting output\n* [CNC](https://en.wikipedia.org/wiki/Numerical_control) machine tooling instructions\n* Directly drive motors to power an etch-a-sketch\n\n\n\n\n## News:\n\n* v0.2.0 - Major refactor. `devout` is now a way of writing graphics devices in \n           plain R with the `ascii()` device as an example. \n* v0.1.2 - Added support for multiple page output\n* v0.1.1 - Added support for path objects, so more map plots now work.\n* v0.1.0 - initial release\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoolbutuseless%2Fdevout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoolbutuseless%2Fdevout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoolbutuseless%2Fdevout/lists"}