# TitrationCurve and PhScale

> Titration is an acid titrated with sodium hydroxide: the acid's concentration and volume, the base's concentration, and a pka for a weak acid…

Page: https://fframes-kit.pages.dev/docs/chemistry/titration-curve-and-ph-scale/
Cargo feature: `chemistry` (`fframes-kit = { version = "..", features = ["chemistry"] }`)

`Titration` is an acid titrated with sodium hydroxide: the acid's concentration and volume,
the base's concentration, and a `pka` for a weak acid (`Titration::weak`) or none for a strong
one (`Titration::strong`). `ph(added)` solves the charge balance exactly, so the start, the
buffer region and the equivalence point all come out right: 25 mL of 0.1 M acetic acid starts
at pH 2.88, is at 4.76 at half equivalence and at 8.72 at equivalence. `equivalence()` gives
the base needed and `curve(until, steps)` the points.

`TitrationCurve` draws the curve on the `axes` of an `Axes` you show yourself (mL across, pH
up). From `delay` the base pours in at a constant rate up to `until` mL over `run_time`
seconds, with a dot at the current pH. As the pour passes them, the equivalence point is marked
with guides to both axes and its pH. For a weak acid, the half-equivalence point is marked too,
where the pH equals the pKa. `ph_at(seconds)` gives the pH at a time, for a readout or a
`PhScale` pointer.

`PhScale` is the scale as 15 cells in the colours of universal indicator (`indicator_color`
blends them), with the values and "acidic", "neutral" and "basic" under it. Each of `markers`,
a name and a pH, points at its place from above. They appear `stagger` seconds apart after
`delay`, in two rows. A `pointer` marks a pH on the bar. Gallery scenes: `TitrationCurve`
(acetic acid, with a scale following the pH) and `PhScale` (everyday substances).

Gallery clip of `TitrationCurve`: https://fframes-kit.pages.dev/media/scenes/TitrationCurve-5a351a50.mp4

Gallery clip of `PhScale`: https://fframes-kit.pages.dev/media/scenes/PhScale-0ffbee3f.mp4

```rust
use fframes_kit::prelude::*;

let acetic = Titration::weak(0.1, 25.0, 0.1, 4.76);
assert_eq!(acetic.equivalence(), 25.0);
assert!((acetic.ph(12.5) - 4.76).abs() < 0.01);

fn figure<'a>(ui: &mut Ui<'a, '_, '_>) -> Svgr<'a> {
    let axes = Axes {
        x_range: Range::new(0.0, 50.0, 10.0),
        y_range: Range::new(0.0, 14.0, 2.0),
        x_length: 8.0,
        y_length: 4.2,
        y_label: Some("pH".into()),
        ..ui.defaults()
    };
    let curve = TitrationCurve {
        titration: Titration::strong(0.1, 25.0, 0.1),
        axes: Some(axes.map(ui)),
        delay: 0.5,
        ..ui.defaults()
    };
    let ph = curve.ph_at(ui.seconds());
    let curve = ui.show(curve);
    let axes = ui.show(axes);
    let scale = ui.show(PhScale {
        center: (0.0, -3.0),
        width: 900.0,
        height: 36.0,
        pointer: Some(ph),
        ..ui.defaults()
    });
    svgr!(<g>{axes}{curve}{scale}</g>)
}
```

## API

Components are plain structs. Set the fields you need and fill the rest with `..ui.defaults()`: the defaults below come from the current theme (`theme.` values are fields of `Theme`).

### TitrationCurve

The pH of a `Titration` as the base is added on `axes` (mL across, pH up): from `delay` over `run_time` seconds the base pours in at a constant rate up to `until` mL and the curve draws along with a dot at the current pH. When it gets there the equivalence point is marked with guides to both axes; for a weak acid the half-equivalence point too, where the pH equals the pKa. Show the axes yourself. Everything fades with `parts_enter`.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `titration` | `Titration` | `Titration::default()` | The acid and base being titrated, whose pH the curve follows. |
| `axes` | `Option<AxesMap>` | `None` | The axes to draw on, mL across and pH up (`c2p` maps data to the canvas); `None` draws nothing. |
| `until` | `f64` | `50.0` | Millilitres of base added when the pour ends, the right end of the curve. |
| `delay` | `f32` | `0.0` | Seconds after the container's start before the base begins to pour. |
| `run_time` | `f32` | `4.0` | Seconds the pour takes, at a constant rate, from none to `until` mL. |
| `labels` | `bool` | `true` | Names the equivalence point (and the half-equivalence point of a weak acid) when the curve reaches it; `false` for none. |
| `color` | `Color` | `theme.colors.accent` | Colour of the curve and its moving dot. |
| `marker_color` | `Color` | `theme.colors.highlight` | Colour of the dots on the marked points. |
| `guide_color` | `Color` | `theme.colors.muted` | Colour of the dashed guides from a marked point to both axes. |
| `text_color` | `Color` | `theme.colors.text` | Colour of the labels. |
| `stroke_width` | `f32` | `theme.stroke.regular` | Width in pixels of the curve and the guides. |
| `parts_enter` | `Option<FadeIn>` | `None` | Fade in of the curve and its marks; `None` for none. |
| `parts_exit` | `Option<FadeOut>` | `None` | Fade out of the curve and its marks before the end; `None` for none. |

Methods:

- `fn added_at(&self, seconds: f32) -> f64`: The base added by `seconds`, in mL.
- `fn ph_at(&self, seconds: f32) -> f64`: The pH at `seconds`, for a `PhScale` pointer or a readout.

Feature `chemistry` · [Source](https://github.com/khanhthanhdev/fframes-kit/blob/main/fframes-kit/src/kits/chemistry/titration.rs#L110)

### PhScale

The pH scale as a bar of 15 cells in the colours of universal indicator, `width` by `height` pixels around `center` (units of `ui.space`). With `labels` the values sit under the cells and "acidic", "neutral" and "basic" under them. Each of `markers` (a name and its pH) points at its place above the bar; after `delay` they appear `stagger` seconds apart, in two rows so neighbours do not collide. A `pointer` marks a pH on the bar, for a value that changes. The cells draw with `enter`, the rest fades with `parts_enter`.

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `center` | `(f64, f64)` | `(0.0, 0.0)` | The middle of the bar, in units of `ui.space`. |
| `width` | `f32` | `1350.0` | Width of the bar in pixels, shared equally between the 15 cells. |
| `height` | `f32` | `70.0` | Height of the bar in pixels. |
| `markers` | `Vec<(Cow<str>, f64)>` | `Vec::new()` | Names and their pH, each pointing at its place above the bar; empty for none. |
| `pointer` | `Option<f64>` | `None` | A pH to point at on the bar, for a value that changes; `None` for no pointer. |
| `delay` | `f32` | `0.0` | Seconds after the container's start before the first marker appears. |
| `stagger` | `f32` | `0.4` | Seconds between one marker appearing and the next. |
| `labels` | `bool` | `true` | Writes the pH values under the cells and "acidic", "neutral" and "basic" under them; `false` for none. |
| `text_color` | `Color` | `theme.colors.text` | Colour of the labels and marker names. |
| `pointer_color` | `Color` | `theme.colors.title` | Colour of the pointer. |
| `enter` | `Option<EnterAnim>` | `None` | How the cells draw; `None` shows them from the start. |
| `exit` | `Option<ExitAnim>` | `None` | How the cells leave at the end of the container; `None` stays. |
| `parts_enter` | `Option<FadeIn>` | `None` | Fade in of the labels, markers and pointer; `None` for none. |
| `parts_exit` | `Option<FadeOut>` | `None` | Fade out of the labels, markers and pointer before the end; `None` for none. |

Methods:

- `fn point(&self, ui: &Ui, ph: f64) -> Point`: The pixel on the middle line of the bar at `ph`, the whole values at the cells' middles.

Feature `chemistry` · [Source](https://github.com/khanhthanhdev/fframes-kit/blob/main/fframes-kit/src/kits/chemistry/titration.rs#L313)
