Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: matlab

on:
push:
paths:
- "**.m"
- ".github/workflows/ci.yml"
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow runs only on push. If the intent is to validate pull requests, add a pull_request trigger as well so CI executes for PRs before merge.

Suggested change
- ".github/workflows/ci.yml"
- ".github/workflows/ci.yml"
pull_request:
paths:
- "**.m"
- ".github/workflows/ci.yml"

Copilot uses AI. Check for mistakes.

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true


jobs:

tests:
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest]
matlab: [R2023b, R2025b]
include:
- os: macos-latest
matlab: R2025b
- os: windows-latest
matlab: R2025b

runs-on: ${{ matrix.os }}

timeout-minutes: 15

steps:

- name: Install MATLAB
timeout-minutes: 10
uses: matlab-actions/setup-matlab@v2
with:
release: ${{ matrix.matlab }}
cache: true

- uses: actions/checkout@v6

- name: Check Matlab code
uses: matlab-actions/run-build@v2
with:
tasks: check

- name: Install font
uses: matlab-actions/run-build@v2
with:
tasks: install

- name: Run Matlab tests
uses: matlab-actions/run-build@v2
with:
tasks: test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.swp
*.asv
57 changes: 46 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,57 @@

DARK converts a standard light-themed plot to a dark color scheme. DARK is easy to use, simply run the command "dark" on your current plot to render it in a dark theme. Once converted you may continue to manipulate the plot as desired including zooming, panning, and modifying properties.

DARK uses only native functions common to MATLAB and GNU Octave without any dependencies on toolboxes or packages. Because of this it will likely run on most any version. It's been tested with MATLAB versions R2019b, R2020b, R2022b, and R2023b as well as GNU Octave versions 3.8.2, 4.4.0, 5.2.0, 6.4.0, 8.3.0, and 9.1.0. DARK has been tested on Windows 10 and Linux distros running Centos7, RHEL 8, and Ubuntu 22.04.
DARK uses only native functions common to MATLAB and GNU Octave without any dependencies on toolboxes or packages.
Because of this it will likely run on most any version.
It's been tested with MATLAB versions R2019b, R2020b, R2022b, R2023b, and R2026a
as well as GNU Octave versions 3.8.2, 4.4.0, 5.2.0, 6.4.0, 8.3.0, 9.1.0, and 11.1.0.
DARK has been tested on Windows 10, macOS,and Linux distros running Centos7, RHEL 8, and Ubuntu 22.04.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space after the comma in "macOS,and".

Suggested change
DARK has been tested on Windows 10, macOS,and Linux distros running Centos7, RHEL 8, and Ubuntu 22.04.
DARK has been tested on Windows 10, macOS, and Linux distros running Centos7, RHEL 8, and Ubuntu 22.04.

Copilot uses AI. Check for mistakes.

UNDARK restores a dark-themed plot back to its standard light theme. Running the command "undark" will restore the current plot to its standard light theme. In short UNDARK undoes DARK.
UNDARK restores a dark-themed plot back to its standard light theme.
Running the command "undark" will restore the current plot to its standard light theme.
In short UNDARK undoes DARK.

HAND renders a plot in a theme that emulates a hand-drawn plot in an engineering notebook. It works the same way as DARK, simply run the command "hand" on your current plot to render it in a hand-drawn thme. Once converted you may continue to modify properties, zoom, and pan as desired.
HAND renders a plot in a theme that emulates a hand-drawn plot in an engineering notebook.
It works the same way as DARK, simply run the command "hand" on your current plot to render it in a hand-drawn thme.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "thme" should be "theme".

Suggested change
It works the same way as DARK, simply run the command "hand" on your current plot to render it in a hand-drawn thme.
It works the same way as DARK, simply run the command "hand" on your current plot to render it in a hand-drawn theme.

Copilot uses AI. Check for mistakes.
Once converted you may continue to modify properties, zoom, and pan as desired.
If the "xkcd Script" font is not yet installed, hand() will prompt to run `install_font()` to install the font.
This only needs to be done once per system.

HAND has been tested with MATLAB R2023b and GNU Octave versions 5.2.0, 6.4.0, 8.3.0, and 9.1.0 running on Windows 10 as well as Linux RHEL 8 and Ubuntu 22.04. For running on Linux, HAND bundles the excellent "xkcd Script" font from [xkcd-font](https://github.com/ipython/xkcd-font). When running on Windows, HAND uses the "Segoe Print" font included with all modern Windows distributions. Similar to DARK, HAND is coded using funtions native to MATLAB and GNU Octave without any dependencies on toolboxes or packages, making it highly portable.
HAND has been tested with MATLAB R2023b and R2026a and
GNU Octave versions 7.1.0, 8.3.0, 9.1.0, and 11.1.0 running on Windows 10, macOS, Linux RHEL 8 and Ubuntu 22.04.
HAND bundles the excellent "xkcd Script" font from [xkcd-font](https://github.com/ipython/xkcd-font).
Similar to DARK, HAND is coded using funtions native to MATLAB and GNU Octave without any dependencies on toolboxes or packages, making it highly portable.
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in README: "funtions" should be "functions".

Suggested change
Similar to DARK, HAND is coded using funtions native to MATLAB and GNU Octave without any dependencies on toolboxes or packages, making it highly portable.
Similar to DARK, HAND is coded using functions native to MATLAB and GNU Octave without any dependencies on toolboxes or packages, making it highly portable.

Copilot uses AI. Check for mistakes.

# Files
* hand.m - NEW - Convert plot to a hand-drawn theme
* test_hand.m - NEW - Test for hand.m
To run self-tests on Matlab:

```matlab
buildtool check
buildtool test
```

To install fonts with Matlab buildtool

```matlab
buildtool install
```

## Files

* hand.m - Convert plot to a hand-drawn theme
* dark.m - Convert plot to a dark color theme
* undark.m - Convert dark theme plot back to the standard light theme
* test_dark.m - Test dark.m with several plot types
* test_undark.m - Test undark.m with several plot types
* xkcd-script.ttf - NEW - xkcd Script font from [xkcd-font](https://github.com/ipython/xkcd-font)
* install_font.m - Install the "xkcd Script" font for use with hand.m. This only needs to be run once per system.
* xkcd-script.ttf - xkcd Script font from [xkcd-font](https://github.com/ipython/xkcd-font)

Test files:

* buildfile.m - Matlab build system
* test_hand.m - Script test for hand.m (Octave or Matlab)
* TestHand.m - Matlab unit tests for hand.m
* test_dark.m - Script test for dark.m with several plot types (Octave or Matlab)
* TestDark.m - Matlab unit tests for dark.m
* test_undark.m - Script test for undark.m with several plot types (Octave or Matlab)
* TestUndark.m - Matlab unit tests for undark.m

![Sample plot 1](./images/example1.PNG "Sample plot 1")

Expand Down Expand Up @@ -61,6 +96,6 @@ hand
~~~~

# Citation
1. **[xkcd-font](https://github.com/ipython/xkcd-font)**
1. **[xkcd-font](https://github.com/ipython/xkcd-font)**

> Written with [StackEdit](https://stackedit.io/).
24 changes: 24 additions & 0 deletions buildfile.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
function plan = buildfile

plan = buildplan(localfunctions);

plan("test") = matlab.buildtool.tasks.TestTask("test/");
end

function checkTask(context)
root = context.Plan.RootFolder;

c = codeIssues(root, IncludeSubfolders=true);

if isempty(c.Issues)
fprintf('%d files checked OK with %s under %s\n', numel(c.Files), c.Release, root)
else
disp(c.Issues)
error("Errors found in " + join(c.Issues.Location, newline))
end

end

function installTask(~)
assert(install_font(), "install font failed")
end
112 changes: 57 additions & 55 deletions dark.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function dark(colors_in)
% >> dark('bp');
%

global colors
global colors %#ok<*GVMIS>

if nargin == 0
colors = fetch_colortab();
Expand All @@ -70,12 +70,12 @@ function dark(colors_in)
function draw_canvas()

% draw background
set(gca,'Color','black');
set(gcf,'Color','black');
set(gca,'zcolor','white');
set(gca,'Color','black')
set(gcf,'Color','black')
set(gca,'zcolor','white')
if isoctave()
set(gca,'xcolor','white');
set(gca,'ycolor','white');
set(gca,'xcolor','white')
set(gca,'ycolor','white')
end

end % function
Expand All @@ -100,25 +100,26 @@ function ml_graph_data()
for kk = numel(h):-1:1
cc = cc + 1;
index = rem(cc-1,size(colors,1)) + 1;
if isa(h(kk), 'matlab.graphics.chart.primitive.Line')
set(h(kk),'Color',colors(index,:));
set(h(kk),'MarkerFaceColor',colors(index,:));
set(h(kk),'MarkerEdgeColor',colors(index,:));
elseif isa(h(kk), 'matlab.graphics.chart.primitive.Bar')
set(h(kk),'FaceColor',colors(index,:));
set(h(kk),'EdgeColor','white');
set(h(kk),'EdgeAlpha',0.5);
elseif isa(h(kk), 'matlab.graphics.chart.primitive.Stem')
set(h(kk),'Color',colors(index,:));
set(h(kk),'MarkerFaceColor',colors(index,:));
set(h(kk),'MarkerEdgeColor',colors(index,:));
elseif isa(h(kk), 'matlab.graphics.primitive.Patch')
switch class(h(kk))
case 'matlab.graphics.chart.primitive.Line'
set(h(kk),'Color',colors(index,:))
set(h(kk),'MarkerFaceColor',colors(index,:))
set(h(kk),'MarkerEdgeColor',colors(index,:))
Comment on lines +103 to +107
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacing isa(...) with switch class(...) means subclasses of these graphics primitives will no longer be styled. If you still want to cover derived chart objects, use isa checks instead of exact class matches.

Copilot uses AI. Check for mistakes.
case 'matlab.graphics.chart.primitive.Bar'
set(h(kk),'FaceColor',colors(index,:))
set(h(kk),'EdgeColor','white')
set(h(kk),'EdgeAlpha',0.5)
case 'matlab.graphics.chart.primitive.Stem'
set(h(kk),'Color',colors(index,:))
set(h(kk),'MarkerFaceColor',colors(index,:))
set(h(kk),'MarkerEdgeColor',colors(index,:))
case 'matlab.graphics.primitive.Patch'
celight = get(h(kk),'EdgeColor');
cflight = get(h(kk),'FaceColor');
set(h(kk),'EdgeColor',[1 1 1] - celight);
set(h(kk),'FaceColor',[1 1 1] - cflight);
elseif isa(h(kk), 'matlab.graphics.primitive.Text')
set(h(kk),'Color','w');
set(h(kk),'EdgeColor',[1 1 1] - celight)
set(h(kk),'FaceColor',[1 1 1] - cflight)
case 'matlab.graphics.primitive.Text'
set(h(kk),'Color','w')
end
end

Expand All @@ -136,28 +137,29 @@ function go_graph_data()
index = rem(cc-1,size(colors,1)) + 1;
if isa(h, 'double')
% octave
if strcmpi(get(h(kk),'type'),'line')
set(h(kk),'Color',colors(index,:));
elseif strcmpi(get(h(kk),'type'),'hggroup')
switch lower(get(h(kk),'type'))
case 'line'
set(h(kk),'Color',colors(index,:))
case 'hggroup'
if isfield(get(h(kk)),'bargroup')
% bar plot
set(h(kk),'FaceColor',colors(index,:));
set(h(kk),'EdgeColor','white');
set(h(kk),'FaceColor',colors(index,:))
set(h(kk),'EdgeColor','white')
else
% stem plot
set(h(kk),'Color',colors(index,:));
set(h(kk),'MarkerEdgeColor',colors(index,:));
set(h(kk),'Color',colors(index,:))
set(h(kk),'MarkerEdgeColor',colors(index,:))
if isnumeric(get(h(kk)).markerfacecolor)
set(h(kk),'MarkerFaceColor',colors(index,:));
set(h(kk),'MarkerFaceColor',colors(index,:))
end
end
elseif strcmpi(get(h(kk),'type'),'patch')
case 'patch'
celight = get(h(kk),'EdgeColor');
cflight = get(h(kk),'FaceColor');
set(h(kk),'EdgeColor',[1 1 1] - celight);
set(h(kk),'FaceColor',[1 1 1] - cflight);
elseif strcmpi(get(h(kk),'type'),'text')
set(h(kk),'Color','w');
set(h(kk),'EdgeColor',[1 1 1] - celight)
set(h(kk),'FaceColor',[1 1 1] - cflight)
case 'text'
set(h(kk),'Color','w')
end % hggroup
end % double
end % kk
Expand All @@ -179,9 +181,9 @@ function ml_label_axes()
tt = get(gca,'title');
xx = get(gca,'xaxis');
yy = get(gca,'yaxis');
set(tt,'Color','white');
set(xx,'Color','white');
set(yy,'Color','white');
set(tt,'Color','white')
set(xx,'Color','white')
set(yy,'Color','white')

end % function

Expand All @@ -192,10 +194,10 @@ function go_label_axes()
xx = get(gca,'xlabel');
yy = get(gca,'ylabel');
zz = get(gca,'zlabel');
set(tt,'Color','white');
set(xx,'Color','white');
set(yy,'Color','white');
set(zz,'Color','white');
set(tt,'Color','white')
set(xx,'Color','white')
set(yy,'Color','white')
set(zz,'Color','white')

end % function

Expand Down Expand Up @@ -241,9 +243,9 @@ function go_handle_legend()
props = get(h(end));
if isfield(props,'displayname') && ~isempty(props.displayname)
lgd = legend;
set(lgd,'Color','black');
set(lgd,'EdgeColor','white');
set(lgd,'TextColor','white');
set(lgd,'Color','black')
set(lgd,'EdgeColor','white')
set(lgd,'TextColor','white')
end

end % function
Expand All @@ -262,7 +264,7 @@ function ml_handle_colorbar()

cb = get(gca,'Colorbar');
if ~isempty(cb)
set(cb,'Color','white');
set(cb,'Color','white')
end

end % function
Expand All @@ -273,8 +275,8 @@ function go_handle_colorbar()
if isfield(props,'__colorbar_handle__')
cb = get(gca,'__colorbar_handle__');
if ~isempty(cb)
set(cb,'ycolor','white');
set(cb,'fontsize',12);
set(cb,'ycolor','white')
set(cb,'fontsize',12)
end
end

Expand All @@ -292,22 +294,22 @@ function finish_up()

function ml_finish_up()

set(gca,'GridColor','white');
set(gca,'GridAlpha',0.3);
set(gca,'GridColor','white')
set(gca,'GridAlpha',0.3)

end % function

function go_finish_up()

props = get(gca);
if isfield(props,'gridcolor')
set(gca,'GridColor','white');
set(gca,'GridColor','white')
end
if isfield(props,'gridalpha')
set(gca,'GridAlpha',0.3);
set(gca,'GridAlpha',0.3)
end
if isfield(props,'fontsize')
set(gca,'FontSize',14);
set(gca,'FontSize',14)
end

end % function
Expand All @@ -318,7 +320,7 @@ function go_finish_up()
fc = fieldnames(colortab_base());
c = '';
for kk = 1:numel(fc)
c = [c fc{kk}];
c = [c fc{kk}]; %#ok<*AGROW>
end
else
c = colors_in;
Expand Down
Loading
Loading