Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
164c4b5
cli scafolding
bartman Jul 26, 2025
90248fc
cli will use spdlog and clipp
bartman Jul 26, 2025
673e526
move glib dependency to library
bartman Jul 26, 2025
f939066
cleanup build options
bartman Jul 26, 2025
79e06a6
working out the cli
bartman Jul 27, 2025
1f5eff9
refactor cli interface
bartman Jul 27, 2025
e8c7423
split command line parsing into config
bartman Jul 27, 2025
1d56681
handle verbose, output type, interval
bartman Jul 27, 2025
31271fb
simple file reader
bartman Jul 27, 2025
120921e
--debug
bartman Jul 27, 2025
a82b1b7
following a pipe or chardev does not make sense
bartman Jul 27, 2025
492b324
copy-n-paste fix
bartman Jul 27, 2025
ca5b18a
read from command
bartman Jul 27, 2025
042f6d8
refactor interval, parsing, add poll
bartman Jul 28, 2025
24917d7
improve --help rendering
bartman Jul 28, 2025
94c38e6
comments on two missing readers
bartman Jul 28, 2025
3e9dc7a
missing initializer for Input members
bartman Jul 28, 2025
3dd927b
FilePoller
bartman Jul 28, 2025
027ae33
ExecWatcher
bartman Jul 28, 2025
37dc36f
simple plotting
bartman Jul 28, 2025
74c33ab
fix y legend
bartman Jul 28, 2025
07195b8
work with intervals
bartman Jul 28, 2025
1453f5c
class, getters, auto color selection
bartman Jul 28, 2025
3b9863b
if not set, inherit interval from previous plot
bartman Jul 28, 2025
b3706a1
fix translation from layer to canvas
bartman Jul 28, 2025
431b4c5
fix SIGINT while sleeping
bartman Jul 28, 2025
ad1e053
add constructor to keep clang 14 happy
bartman Jul 28, 2025
5e33e5f
<format> is not available with clang-14, use <fmt> instead
bartman Jul 28, 2025
bf4222e
add --position and --regex to select numbers out of lines of text
bartman Jul 28, 2025
7849b72
some hacky things for now
bartman Jul 29, 2025
40bea72
timing for plot
bartman Jul 29, 2025
d76d8d2
display timing interval
bartman Jul 29, 2025
6f5a83a
make Blot::Exception work with std::exception mechanisms
bartman Jul 29, 2025
71b39e6
do not try to plot w/o any data
bartman Jul 29, 2025
40d3948
use COLUMNS/LINES from environment for sizing
bartman Jul 29, 2025
b0ec274
do not generate color escapes if they are not needed
bartman Jul 29, 2025
0bb0663
fix stall in release build mode
bartman Jul 29, 2025
02550ce
missing extract header
bartman Jul 29, 2025
c67b6d4
throw exception with namespace
bartman Jul 29, 2025
d42a9e7
fix margin calculation
bartman Jul 29, 2025
3cd7ddc
rewrite how we extract numbers from a line
bartman Jul 29, 2025
9e061ff
missing CR
bartman Jul 29, 2025
726d6b0
throw exception with namespace, in Debug
bartman Jul 29, 2025
0e59124
cast line to double to keep gcc happy
bartman Jul 29, 2025
8911ebc
parse 1 or 2 positions from pos parameter
bartman Jul 29, 2025
d3400dc
fix EXAMPLES
bartman Jul 29, 2025
99efb85
update README
bartman Jul 29, 2025
d0b4df6
update README
bartman Jul 29, 2025
b79c8f0
non <ranges> fallback implementation of --position parsing
bartman Jul 29, 2025
f96d926
fix a signed/unsigned comparison build issue with gcc
bartman Jul 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 11 additions & 24 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,25 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

PROJECT(blot VERSION 0.0.7 LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS YES)
#set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_BUILD_TYPE Debug)
#set(CMAKE_BUILD_TYPE RelWithDebInfo)

SET(CMAKE_INCLUDE_CURRENT_DIR ON)

SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
#FIND_PACKAGE(libev REQUIRED)
FIND_PACKAGE(PkgConfig)

PKG_CHECK_MODULES(GLIB2 REQUIRED glib-2.0>=2.44)
#PKG_CHECK_MODULES(GIO REQUIRED gio-2.0>=2.44)
#PKG_CHECK_MODULES(GIOUNIX REQUIRED gio-unix-2.0>=2.44)

LINK_DIRECTORIES(
${GLIB2_LIBRARY_DIRS}
${GIO_LIBRARY_DIRS}
${GIOUNIX_LIBRARY_DIRS}
)

if (NOT DEFINED CMAKE_C_STANDARD)
SET(CMAKE_C_STANDARD 99) # use C99 if one is not set by user
SET(CMAKE_C_STANDARD 99)
endif()
if (NOT DEFINED CMAKE_CXX_STANDARD)
SET(CMAKE_CXX_STANDARD 20) # used only in test harness
SET(CMAKE_CXX_STANDARD 20)
endif()

add_definitions(-D_GNU_SOURCE)

if(CMAKE_COMPILER_IS_GNUCC)
add_definitions(-Wall -Werror -Wno-variadic-macros -Wno-dangling-pointer)
endif(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options(-Wall -Werror -Wno-variadic-macros -Wno-dangling-pointer)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_compile_options(-Wall -Werror -Wno-variadic-macros)
endif()

if(CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "ASAN enabled for debug build")
Expand All @@ -49,14 +36,14 @@ include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
git_describe(GIT_REVISION)

add_definitions(-D"BLOT_GIT_REFSPEC=${GIT_REFSPEC}")
add_definitions(-D"BLOT_GIT_REVISION=${GIT_REVISION}")
add_definitions(-D"BLOT_GIT_SHA1=${GIT_SHA1}")
add_definitions(-DBLOT_GIT_REFSPEC="${GIT_REFSPEC}")
add_definitions(-DBLOT_GIT_REVISION="${GIT_REVISION}")
add_definitions(-DBLOT_GIT_SHA1="${GIT_SHA1}")

INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)

add_subdirectory(lib)
#add_subdirectory(cli)
add_subdirectory(cli)
add_subdirectory(examples/c)
add_subdirectory(examples/cpp)

Expand Down
171 changes: 163 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@
# blot

Blot is a plotting library written in C (with a C++ wrapper), that plots data onto a string buffer.
Blot is a plotting library written in C, that plots data onto a string buffer.

That's right, there are no images, just text -- see examples below.

There is a C++ wrapper provided, as well as a CLI tool.

Site: [bartman.github.io/blot](https://bartman.github.io/blot/)

GitHub: [github.com/bartman/blot](https://github.com/bartman/blot/)

Copyright © 2021 Bart Trojanowski
Copyright © 2021-2025 Bart Trojanowski

Licensed under LGPL v2.1, or any later version.


## Noteworthy features

* plots to the console as text (by calling `puts()`)
* plots to the console as text (by calling `puts()`/`printf()`)
* very very fast (compared to python alternatives)
* very very memory usage friendly
* can plot multiple datasets on one canvas
* uses familiar figure based API (similar to existing python plotting frameworks)
* supports braille plotting (like [plotille](https://github.com/tammoippen/plotille))
* 256 colour support
* data arrays can be provided in various types (`int16`, `int32`, `int64`, `double`, or `float`)
* there is a C++ wrapper, for convenience
* there is a C++ wrapper, with limited features, for convenience
* there is a CLI tool that can plot from files or using output of commands

## Prerequisites

Expand All @@ -46,10 +48,160 @@ You can build debug (with `ASAN`) using

make TYPE=Debug

## Examples
Run `make help` for a full list.

## blot CLI

The easiest say to use `blot` is to use the CLI, which is able to read from files
or launch programs, then plot the numbers it finds.

See the online help for a full list of features...
<details>
<summary> ❯ blot --help </summary>

```sh
SYNOPSIS
blot [-h] [-V] [-v] [--debug] [--timing] [-i <sec>]
blot [-A|-U|-B] ((scatter|line|bar) ([-R <file>] | [-F <file>] | [-P <file>] | [-X <cmd>] | [-W <cmd>]) [-p <y-pos|x-pos,y-pos>] [-r <regex>] [-c <color>] [-i <sec>])...

OPTIONS
-h, --help This help
-V, --version Version
-v, --verbose Enable verbose output
--debug Enable debug output
--timing Show timing statitiscs
-i, --interval <sec> Display interval in seconds

Output:
-A, --ascii ASCII output
-U, --unicode Unicode output
-B, --braille Braille output

Plot type:
scatter Add a scatter plot
line Add a line/curve plot
bar Add a bar plot

Plot data source:
-R, --read <file> Read file to the end, each line is a record
-F, --follow <file> Read file waiting for more, each line is a record
-P, --poll <file> Read file at interval, each read is one record
-X, --exec <cmd> Run command, each line is a record
-W, --watch <cmd> Run command at interval, each read is one record

Data source parsing:
-p, --position <y-pos|x-pos,y-pos>
Find numbers in input line, pick 1 or 2 positions for X and Y values

-r, --regex <regex> Regex to match numbers from input line

Plot modifiers:
-c, --color <color> Set plot color (1..255)
-i, --interval <sec> Set sampling interval in seconds

EXAMPE

blot --braille \
line --color 10 --read x_y1_values -p 1,2 \
scatter --color 11 --read x_y2_values -p 1,2

blot --braille \
scatter --color 11 --read y_values \
line --color 10 --exec 'seq 1 100'

blot --braille \
line --poll /proc/loadavg --position 1 \
line --poll /proc/loadavg --position 2 \
line --poll /proc/loadavg --position 3
```
</details>

First, pick a plotting mode, there are 3 choices: `scatter`, `line`, and `bar`.
Multiple plots can be overlayed.

Each plot needs to get data from a file or process, and there are 5 options:
- `--read <file>` to read lines data from a file, stop at the end.
- `--follow <file>` to read lines data from a file, but wait for more data (`Ctrl-C` to stop).
- `--poll <file>` to poll a file (like `/sys` or `/proc` files), at some interval, and plot the results.
- `--exec <cmd>` to run a program that will give us data to plot on successive lines.
- `--watch <cmd>` to run a program that outputs one number per execution, which will be accumulate and plotted.

Let's look at some examples.

### plot numbers from a file (read file mode)

Let's say we have a file that contains some X,Y pairs...
```
0 10
1 25
2 0
3 40
4 55
```

We can plot this using...

```sh
❯ blot bar --read mydata --position 1,2
```
The values 1 and 2 are positions in each line where `blot` will find the X,Y coordinates.

`blot` is being used in other projects as a library, but it comes with some
examples.
![blot bar --read](images/blot-bar-read.png)

### plot numbers from a log file (follow file mode)

Let's say that we have a log file, with some magnitude values.
Let's just make something up with a script...
```sh
#!/usr/bin/env bash
while true ; do
echo $RANDOM
sleep 1
done > mydata
```
While the above is running, we can plot the data being generated...
```sh
❯ blot line --follow mydata
```
The file contains only one number per line, and `blot` will use the line number as the Y coordinate.
![blot line --follow](images/blot-line-follow.png)

Plot will update continuously, until `Ctrl-C` is used to stop.

### plot the system load (poll file mode)

Recall that `/proc/loadavg` has 3 load values (1,5,15 minutes). We can plot them...

```sh
❯ blot line --poll /proc/loadavg --position 1 --interval 0.5 \
line --poll /proc/loadavg --position 2 \
line --poll /proc/loadavg --position 3
```
![blot line --poll](images/blot-line-poll.png)

### read X,Y from command line (exec mode)

Here is a cool way to test your random number generator, generate two consecutive numbers and plot them against one another.

```sh
❯ blot scatter --exec 'for ((x=0;x<10000000;x++)) ; do echo $RANDOM $RANDOM ; done' --position 1,2
```
![blot scatter --exec](images/blot-scatter-exec.png)

### plot power draw of a GPU (watch mode)

The Nvidia graphics tools provide a system management interface CLI, named `nvidia-smi`.
If we wanted to plot the average power draw, then we could do this...

```sh
blot --timing line --watch 'nvidia-smi --id=0 -q | grep -m1 "Average Power Draw"' --interval 0.1
```
![blot scatter --watch](images/plot-line-watch.png)

## Source code examples

`blot` is being used in other projects as a library, and it comes with many
examples for the C and C++ usage.

Generated from [simple.c](examples/c/c-simple.c) (see also [simple.cpp](examples/cpp/cpp-simple.cpp) for C++ wrapper usage)

Expand All @@ -63,12 +215,15 @@ Generated from [trig.c](examples/c/c-trig.c) (see also [trig.cpp](examples/cpp/c

![trig example](images/trig.png)



## Missing features

* different plotting modes like histograms (currently only plots line/scatter/bar)
* improve axis line and numbering (currently not very accurate)
* add axis labels and minor ticks (configurable)
* draw origin lines and minor tick lines (configurable)
* better 256 colour support

### Ideas:

Expand Down
38 changes: 38 additions & 0 deletions cli/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
INCLUDE(FetchContent)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.14.1
)
FetchContent_Declare(
clipp
GIT_REPOSITORY https://github.com/muellan/clipp.git
GIT_TAG v1.2.3
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 11.2.0
)
FetchContent_MakeAvailable(spdlog clipp fmt)

ADD_EXECUTABLE(blot
main.cpp
config.cpp
reader.cpp
)

TARGET_COMPILE_DEFINITIONS(blot PRIVATE
FMT_HEADER_ONLY
)

TARGET_INCLUDE_DIRECTORIES(blot PRIVATE
${clipp_SOURCE_DIR}/include
)

TARGET_LINK_LIBRARIES(blot PRIVATE
blot_a
-lm
spdlog::spdlog
fmt::fmt
)
Loading