Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
johannes-wolf committed Nov 16, 2024
1 parent d9bf4f2 commit 5c99ee6
Show file tree
Hide file tree
Showing 3 changed files with 277 additions and 0 deletions.
35 changes: 35 additions & 0 deletions src/axis.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

/// Transform linear axis value to linear space (low, high)
#let _transform-lin(ax, value, low, high) = {
let range = high - low

return (value - ax.low) * (range / (ax.high - ax.low))
}

/// Transform log axis value to linear space (low, high)
#let _transform-log(ax, value, low, high) = {
let range = high - low

let f(x) = {
calc.log(calc.max(x, util.float-epsilon), base: ax.base)
}

return (value - f(ax.low)) * (range / (f(ax.high) - f(ax.low)))
}

#let linear(low, high) = (
low: low, high: high, transform: _transform-lin,
)

#let logarithmic(low, high, base) = (
low: low, high: high, base: base, transform: _transform-log,
)

/// Transform an axis value to a linear value between low and high
/// - ax (axis): Axis
/// - value (number): Value to transform from axis space to linear space
/// - low (number): Linear minimum
/// - high (number): Linear maximum
#let transform(ax, value, low, high) = {
return (ax.transform)(ax, value, low, high)
}
34 changes: 34 additions & 0 deletions src/projection.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

/// Create a new cartesian projection between two vectors, low and high
///
/// - low (vector): Low vector
/// - high (vector): High vector
/// - x (axis): X axis
/// - y (axis): Y axis
/// - z (axis): Z axis
/// -> function Transformation for one or more vectors
#let cartesian(low, high, x, y, z) = {
let axes = (x, y, z)

return (..v) = {
return v.pos().map(v => {
for i range(0, v.len()) {
v.at(i) = (axes.at(i).transform)(axes.at(i), v.at(i), low.at(i), high.at(i))
}
})
}
}

/// - center (vector): Center vector
/// - start (angle): Start angle (0deg for full circle)
/// - stop (angle): Stop angle (360deg for full circle)
/// - theta (axis): Theta axis
/// - r (axis): R axis
/// -> function Transformation for one or more vectors
#let polar(center, radius, start, stop, theta, r) = {
return (..v) => {
let v = v.pos()
// TODO
return v
}
}
208 changes: 208 additions & 0 deletions src/ticks.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Compute list of linear ticks for axis
//
// - axis (axis): Axis
#let compute-linear-ticks(axis, style, add-zero: true) = {
let (min, max) = (axis.min, axis.max)
let dt = max - min; if (dt == 0) { dt = 1 }
let ticks = axis.ticks
let ferr = util.float-epsilon
let tick-limit = style.tick-limit
let minor-tick-limit = style.minor-tick-limit

let l = ()
if ticks != none {
let major-tick-values = ()
if "step" in ticks and ticks.step != none {
assert(ticks.step >= 0,
message: "Axis tick step must be positive and non 0.")
if axis.min > axis.max { ticks.step *= -1 }

let s = 1 / ticks.step

let num-ticks = int(max * s + 1.5) - int(min * s)
assert(num-ticks <= tick-limit,
message: "Number of major ticks exceeds limit " + str(tick-limit))

let n = range(int(min * s), int(max * s + 1.5))
for t in n {
let v = (t / s - min) / dt
if t / s == 0 and not add-zero { continue }

if v >= 0 - ferr and v <= 1 + ferr {
l.push((v, format-tick-value(t / s, ticks), true))
major-tick-values.push(v)
}
}
}

if "minor-step" in ticks and ticks.minor-step != none {
assert(ticks.minor-step >= 0,
message: "Axis minor tick step must be positive")
if axis.min > axis.max { ticks.minor-step *= -1 }

let s = 1 / ticks.minor-step

let num-ticks = int(max * s + 1.5) - int(min * s)
assert(num-ticks <= minor-tick-limit,
message: "Number of minor ticks exceeds limit " + str(minor-tick-limit))

let n = range(int(min * s), int(max * s + 1.5))
for t in n {
let v = (t / s - min) / dt
if v in major-tick-values {
// Prefer major ticks over minor ticks
continue
}

if v != none and v >= 0 and v <= 1 + ferr {
l.push((v, none, false))
}
}
}

}

return l
}

// Compute list of linear ticks for axis
//
// - axis (axis): Axis
#let compute-logarithmic-ticks(axis, style, add-zero: true) = {
let ferr = util.float-epsilon
let (min, max) = (
calc.log(calc.max(axis.min, ferr), base: axis.base),
calc.log(calc.max(axis.max, ferr), base: axis.base)
)
let dt = max - min; if (dt == 0) { dt = 1 }
let ticks = axis.ticks

let tick-limit = style.tick-limit
let minor-tick-limit = style.minor-tick-limit
let l = ()

if ticks != none {
let major-tick-values = ()
if "step" in ticks and ticks.step != none {
assert(ticks.step >= 0,
message: "Axis tick step must be positive and non 0.")
if axis.min > axis.max { ticks.step *= -1 }

let s = 1 / ticks.step

let num-ticks = int(max * s + 1.5) - int(min * s)
assert(num-ticks <= tick-limit,
message: "Number of major ticks exceeds limit " + str(tick-limit))

let n = range(
int(min * s),
int(max * s + 1.5)
)

for t in n {
let v = (t / s - min) / dt
if t / s == 0 and not add-zero { continue }

if v >= 0 - ferr and v <= 1 + ferr {
l.push((v, format-tick-value( calc.pow(axis.base, t / s), ticks), true))
major-tick-values.push(v)
}
}
}

if "minor-step" in ticks and ticks.minor-step != none {
assert(ticks.minor-step >= 0,
message: "Axis minor tick step must be positive")
if axis.min > axis.max { ticks.minor-step *= -1 }

let s = 1 / ticks.step
let n = range(int(min * s)-1, int(max * s + 1.5)+1)

for t in n {
for vv in range(1, int(axis.base / ticks.minor-step)) {

let v = ( (calc.log(vv * ticks.minor-step, base: axis.base) + t)/ s - min) / dt
if v in major-tick-values {continue}

if v != none and v >= 0 and v <= 1 + ferr {
l.push((v, none, false))
}

}

}
}
}

return l
}

// Get list of fixed axis ticks
//
// - axis (axis): Axis object
#let fixed-ticks(axis) = {
let l = ()
if "list" in axis.ticks {
for t in axis.ticks.list {
let (v, label) = (none, none)
if type(t) in (float, int) {
v = t
label = format-tick-value(t, axis.ticks)
} else {
(v, label) = t
}

v = value-on-axis(axis, v)
if v != none and v >= 0 and v <= 1 {
l.push((v, label, true))
}
}
}
return l
}

// Compute list of axis ticks
//
// A tick triple has the format:
// (rel-value: float, label: content, major: bool)
//
// - axis (axis): Axis object
#let compute-ticks(axis, style, add-zero: true) = {
let find-max-n-ticks(axis, n: 11) = {
let dt = calc.abs(axis.max - axis.min)
let scale = calc.floor(calc.log(dt, base: 10) - 1)
if scale > 5 or scale < -5 {return none}

let (step, best) = (none, 0)
for s in style.auto-tick-factors {
s = s * calc.pow(10, scale)

let divs = calc.abs(dt / s)
if divs >= best and divs <= n {
step = s
best = divs
}
}
return step
}

if axis == none or axis.ticks == none { return () }
if axis.ticks.step == auto {
axis.ticks.step = find-max-n-ticks(axis, n: style.auto-tick-count)
}
if axis.ticks.minor-step == auto {
axis.ticks.minor-step = if axis.ticks.step != none {
axis.ticks.step / 5
} else {
none
}
}

let ticks = if axis.mode == "log" {
compute-logarithmic-ticks(axis, style, add-zero: add-zero)
} else {
compute-linear-ticks(axis, style, add-zero: add-zero)
}
ticks += fixed-ticks(axis)
return ticks
}

0 comments on commit 5c99ee6

Please sign in to comment.