/ mb-math Public

Mathematical functions shared by mb-sound, mb-geometry, and other personal projects

# mike-bourgeous/mb-math

## Folders and files

NameName
Last commit message
Last commit date

133 Commits

# mb-math

Mathematical functions such as range-clamping, interpolation, extrapolation, etc. This is companion code to my educational video series about code and sound.

You might also be interested in mb-sound, mb-geometry, and mb-util.

This code is reasonably well-tested, but I recommend using it for non-critical tasks, not for making important decisions or for mission-critical data modeling. Most of the function implementations target 4 to 6 decimals of accuracy, which may be too low for some applications.

## Quick start

Clone the repo, follow the standalone installation instructions below, and run `bin/console`. Use Pry's `ls` command to get a list of what's available, and the `show-source -d` command to see a function's documentation).

`bin/console`
```ls
show-source -d clamp```

## Examples

For typing convenience, and to avoid conflicting with Ruby's top-level `Math` module, everything lives under `MB::M` instead of `MB::Math`.

### Smooth interpolation

The `interp` method blends between two `Numeric`s, `Array`s, `Hash`es, or `Numo::NArray`s.

#### Numbers

```MB::M.interp(1, 2, 0.5)
# => 1.5```

#### Hashes

```a = { x: 0.5, y: 1.5 }
b = { x: 1.0, y: -1.0 }

MB::M.interp(a, b, 0)
# => { x: 0.5, y: 1.5 }

MB::M.interp(a, b, 1)
# => { x: 1.0, y: -1.0 }

MB::M.interp(a, b, 0.5)
# => { x: 0.75, y: 0.25 }```

#### Smoothed interpolation

The `:func` keyword argument accepts a tweening function. Anything that responds to `:call` and returns 0.0 if given 0.0 and 1.0 if given 1.0 can be used here.

```a = [-1, -1]
b = [1, 2]
steps = [0, 0.25, 0.5, 0.75, 1]
MB::M.interp(a, b, steps, func: MB::M.method(:smootherstep))
# => [[-1, -1], [-0.79296875, -0.689453125], [0.0, 0.5], [0.79296875, 1.689453125], [1, 2]]```

#### Linear extrapolation

Note: extrapolation doesn't work as one might expect with `smoothstep` or `smootherstep`.

```a = 1
b = 2
MB::M.interp(a, b, 2)
# => 3```

### Plotting

A simple wrapper around GNUPlot (or any compatible plotter) is provided that can plot to an image file, a graphical window, or a text console.

The `MB::M::Plot#plot` method takes a `Hash` mapping dataset names to data values.

```# Standard plot
p = MB::M::Plot.terminal(width_fraction: 1, height_fraction: 1, width: 40, height: 15)
p.plot({noise: Numo::SFloat.zeros(10).rand(-0.9, 0.9)}, columns: 1, rows: 1)```
``````   1 +----------------------------+
|  +  +   +  +  +  +   +  +  |
0.5 |-+            noise *******-|
|         *               *  |
|  *     * *              ** |
0 |-* *   *  *             * +*|
| *  *  *   *            *  *|
-0.5 |*+  * *    *           *  +-|
|     *      *          *    |
|  +  +   +  ***********  +  |
-1 +----------------------------+
0  1  2   3  4  5  6   7  8  9
``````
```# Scatter plot
p = MB::M::Plot.terminal(width_fraction: 1, height_fraction: 1, width: 40, height: 20)
points = (0..(Math::PI * 2)).step(Math::PI / 8).map { |a| [ 0.9 * Math.cos(a), 0.9 * Math.sin(a) ] }
p.xrange(-1, 1)
p.plot({ circle: points })```
``````    1 +----------------------------+
|      +       +      +      |
|             circle ******* |
|                            |
0.5 |-+       *********        +-|
|        *         **        |
|       *            *       |
0 |-+    *              *    +-|
|       *             *      |
|       *            *       |
|        ***       **        |
-0.5 |-+         *******        +-|
|                            |
|                            |
|      +       +      +      |
-1 +----------------------------+
-1    -0.5      0     0.5     1
``````

```# f(x) = x^2 + 4
# => [(0.0+2.0i), (0.0-2.0i)]```

### Scaling ranges

Scales values or `Numo::NArray`s from one linear range to another, extrapolating for values beyond the end of the range.

```MB::M.scale(2, 0..4, 10..12)
# => 11

MB::M.scale(-2, 0..4, 10..12)
# => 9

# Reverse ranges work too
MB::M.scale(Numo::SFloat[0, 1, 2, 3, 4], 1..3, 6..2)
# => Numo::SFloat[8, 6, 4, 2, 0]```

## Installation and usage

This project contains some useful programs of its own, or you can use it as a Gem (with Git source) in your own projects.

### Standalone usage and development

First, install a Ruby version manager like RVM. Using the system's Ruby is not recommended -- that is only for applications that come with the system. You should follow the instructions from https://rvm.io, but here are the basics:

```gpg2 --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
\curl -sSL https://get.rvm.io | bash -s stable```

Next, install Ruby. RVM binary rubies are still broken on Ubuntu 20.04.x, so use the `--disable-binary` option if you are running Ubuntu 20.04.x.

`rvm install --disable-binary 2.7.3`

You can tell RVM to isolate all your projects and switch Ruby versions automatically by creating `.ruby-version` and `.ruby-gemset` files (already present in this project):

```cd mb-math
cat .ruby-gemset
cat .ruby-version```

Now install dependencies:

```bundle install

# Ubuntu/Debian
sudo apt-get install gnuplot-qt

# macOS
brew install gnuplot```

### Using the project as a Gem

To use mb-math in your own Ruby projects, add this Git repo to your `Gemfile`:

```# your-project/Gemfile
gem 'mb-math', git: 'https://github.com/mike-bourgeous/mb-math.git```

## Testing

Run `rspec` to run all tests.

## Contributing

Pull requests welcome, though development is focused specifically on the needs of my video series.

This project is released under a 2-clause BSD license. See the LICENSE file.

### Dependencies

Mathematical functions shared by mb-sound, mb-geometry, and other personal projects