-
Notifications
You must be signed in to change notification settings - Fork 450
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
282 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# v0.1.0 | ||
features: | ||
|
||
- e, pi and tau work now | ||
- sin, cos, etc. works now | ||
- fixed a bug where top part of exponent couldn't be a sequence | ||
- cleaned up some code | ||
- dynamic table naming | ||
- multiple variable support | ||
- made the docs readable | ||
- added math-to-data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2025 Tijme | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Eqalc | ||
|
||
Convert your [Typst](https://typst.app/home) math equations to functions in order to graph or plot them. | ||
So you only have to write your equation out ONCE! | ||
|
||
[![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/7ijme/eqalc/blob/main/LICENSE) | ||
|
||
Install eqalc by cloning it and then importing like this: | ||
|
||
```typ | ||
#import "@preview/eqalc:0.1.0": math-to-func, math-to-code, math-to-table | ||
#let f = $g(t)=2t dot sqrt(e^t)+ln(t)+2pi$ | ||
#f\ | ||
#math-to-code(f) | ||
#math-to-table(f, min: 1, max: 5, step: 1) | ||
// `math-to-func` will return a function that can be used to map over values | ||
``` | ||
![image](https://github.com/user-attachments/assets/b151af4e-a0d5-4320-8bf3-3642dd5e6e33) | ||
|
||
|
||
Available functions at the moment: | ||
|
||
- `math-to-func` | ||
- `math-to-code` | ||
- `math-to-table` | ||
- `math-to-data` |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
#import "util.typ": math-to-str, get-variable | ||
|
||
/// Creates a function from a math expression. | ||
/// | ||
/// | ||
/// Example: | ||
/// `#math-to-func($x^2$)` will output `#(x => calc.pow(x,2))`. | ||
/// -> function | ||
#let math-to-func( | ||
/// The math expression. | ||
/// -> content | ||
math, | ||
) = { | ||
let string = math-to-str(math) | ||
let var = get-variable(string) | ||
x => eval("let " + var + "= " + str(x) + "; " + string) | ||
} | ||
|
||
/// Creates a table of function values. | ||
/// | ||
/// Example: | ||
/// `#math-to-table($x^2$, min: 1, max: 5, step: 1)` will output: | ||
/// ```table | ||
/// x | 1 | 2 | 3 | 4 | 5 | | ||
/// -------------------------- | ||
/// f(x) | 1 | 4 | 9 | 16| 25| | ||
/// ``` | ||
/// But in an actual table. | ||
/// -> content | ||
#let math-to-table( | ||
/// The function to evaluate. | ||
/// -> content | ||
math, | ||
/// The minimum value of the domain. | ||
/// -> integer | ||
min: 0, | ||
/// The maximum value of the domain. | ||
/// -> integer | ||
max: 5, | ||
/// The step size. | ||
/// -> integer | ||
step: 1, | ||
/// The integer of decimal places to round to. | ||
/// -> integer | ||
round: 2, | ||
/// The name of the function. | ||
/// -> content | ||
name: none, | ||
) = { | ||
assert(min < max, message: "min must be less than max") | ||
assert(step > 0, message: "step must be greater than 0") | ||
let var = get-variable(math-to-str(math)) | ||
let f = math-to-func(math) | ||
let name = if name != none { name } else { | ||
eval(math-to-str(math, get-first-part: true), mode: "math") | ||
} | ||
table( | ||
columns: calc.ceil((max - min) / step) + 2, | ||
[$#var$], ..range(min, max + step, step: step).map(x => [$#x$]), | ||
name, ..range( | ||
min, | ||
max + step, | ||
step: step, | ||
).map(x => [#calc.round(f(x), digits: round)]), | ||
) | ||
} | ||
|
||
/// Converts a math expression to code. | ||
/// | ||
/// Example: | ||
/// `#math-to-code($x^2$)` will output `calc.pow(x,2)`. | ||
/// -> content | ||
#let math-to-code( | ||
/// The math expression. | ||
/// -> content | ||
math, | ||
) = { | ||
let f = math-to-str(math) | ||
raw(lang: "typst", f) | ||
} | ||
|
||
/// Math to any data you might need. | ||
/// | ||
/// Example: | ||
/// `#math-to-data($f(x)=x^2$)` will output: | ||
/// ```typ | ||
/// #( | ||
/// func: (x => calc.pow(x,2)), | ||
/// str: "calc.pow(x,2)", | ||
/// x: "x", | ||
/// x-math: $x$, | ||
/// fx: "f(x)", | ||
/// fx-math: $f(x)$, | ||
///)``` | ||
/// -> (func: function, str: string, x: string, x-math: content, fx: string, fx-math: content) | ||
#let math-to-data( | ||
/// The math expression. | ||
/// -> content | ||
math, | ||
) = { | ||
let f = math-to-func(math) | ||
let str = math-to-str(math) | ||
let var = get-variable(str) | ||
let fx = math-to-str(math, get-first-part: true) | ||
( | ||
func: f, | ||
str: str, | ||
x: var, | ||
x-math: eval(var, mode: "math"), | ||
fx: fx, | ||
fx-math: eval(fx, mode: "math"), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "eqalc" | ||
version = "0.1.0" | ||
entrypoint = "lib.typ" | ||
authors = ["Tijme"] | ||
license = "MIT" | ||
description = "Convert math equations to functions." | ||
repository = "https://github.com/7ijme/eqalc" | ||
categories = ["utility", "visualization"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/// Converts math equations to strings. | ||
/// -> string | ||
#let math-to-str( | ||
/// The math expression. | ||
/// -> content | ||
eq, | ||
/// Get the part before the equals sign. This is used to get the function name. | ||
/// -> boolean | ||
get-first-part: false, | ||
/// The depth of the recursion. This is used for debugging. | ||
/// -> integer | ||
depth: 0, | ||
) = { | ||
let map-math(n) = { | ||
// Operators like sin, cos, etc. | ||
if n.func() == math.op { | ||
"calc." + n.fields().text.text | ||
// Parentheses | ||
} else if n.func() == math.lr { | ||
math-to-str(n.body, depth: depth + 1) | ||
// Powers | ||
} else if n.has("base") and n.has("t") { | ||
"calc.pow(" + math-to-str(n.base) + ", " + math-to-str(n.t) + ")" | ||
// Roots | ||
} else if n.func() == math.root { | ||
( | ||
"calc.root(" | ||
+ math-to-str(n.radicand, depth: depth + 1) | ||
+ ", " | ||
+ n.at("index", default: "2") | ||
+ ")" | ||
) | ||
// Fractions | ||
} else if n.func() == math.frac { | ||
( | ||
"(" | ||
+ math-to-str(n.num, depth: depth + 1) | ||
+ ")/(" | ||
+ math-to-str(n.denom, depth: depth + 1) | ||
+ ")" | ||
) | ||
// Default case | ||
} else if n == [ ] { } else if n.has("text") { | ||
if n.text == "e" { | ||
"calc.e" | ||
} else if n.text == $pi$.body.text { | ||
"calc.pi" | ||
} else if n.text == $tau$.body.text { | ||
"calc.tau" | ||
} else { | ||
n.text | ||
} | ||
// This is still a sequence. | ||
} else { | ||
math-to-str(n, depth: depth + 1) | ||
} | ||
} | ||
|
||
if not type(eq) == "string" and eq.has("body") { | ||
eq = eq.body | ||
} | ||
// Adding `[]` to make it a sequence if it isn't already. | ||
let string = (eq + []) | ||
.fields() | ||
.children | ||
.map(map-math) | ||
.join() | ||
.replace( | ||
regex("(\d)\s*([a-zA-Z]\b|calc|\()"), | ||
((captures,)) => captures.first() + "*" + captures.last(), | ||
) | ||
.replace(math.dot, "*") | ||
|
||
if depth == 0 { | ||
let reg = if get-first-part { | ||
regex("=.+") | ||
} else { | ||
regex(".+=") | ||
} | ||
string.replace(reg, "") | ||
} else { | ||
string | ||
} | ||
} | ||
|
||
/// Gets the main variable from a math expression. | ||
/// -> string | ||
#let get-variable( | ||
/// The math expression. | ||
/// -> string | ||
math-str, | ||
) = { | ||
let reg = regex("\b([A-Za-z--e])\b") | ||
let match = math-str.match(reg) | ||
if match != none { | ||
match.text | ||
} else { | ||
"x" | ||
} | ||
} |