0

The gist of it is, how do you animate between two points on a hyperbolic plane?

So we have basic linear interpolation (lerp) through tweening libraries such as GSAP's TweenMax, where you can specify a start and end position and a duration, and it will call your lerp function with the percentage (from 0 to 1) of completion of the duration (i.e. the percent of time passed), along with the start and end values:

const lerp = (v0, v1, t) => v0 * (1 - t) + v1 * t

You can customize the animation flow with easing functions in this situation, like:

x = lerp(startX, endX, easeInOutSine(t))

function easeInOutSine(x) {
  return -(cos(PI * x) - 1) / 2
}

In the end, you can compose a dynamic animation like this, by just specifying a few key states, and then tweening between them:

But how can you do this for the hyperbolic plane? I am thinking like the game HyperRogue but where you click on an outer tile, and it tweens you around to that tile. That is, I think it moves along the hyperbolic geodesic (the visibly curved path from start to end state).

I have these functions I'm working with from the MagicTile project, which I ported to TypeScript:

// The pure translation (i.e. moves the origin straight in some direction) that takes p1 to p2.
// I borrowed this from Don's hyperbolic applet.

PureTranslation(g: Geometry, p1: Complex, p2: Complex) {
  let A: Complex = p2.Subtract(p1)
  let B: Complex = p2.Multiply(p1)

  let denom: number =
    1 - (B.Real * B.Real + B.Imaginary * B.Imaginary)

  let P: Complex = new Complex(
    (A.Real * (1 + B.Real) + A.Imaginary * B.Imaginary) / denom,
    (A.Imaginary * (1 - B.Real) + A.Real * B.Imaginary) / denom,
  )

  this.Isometry(g, 0, P)
  this.Normalize()
}

// Move from a point p1 -> p2 along a geodesic.
// Also somewhat from Don.

Geodesic(g: Geometry, p1: Complex, p2: Complex) {
  let t: Mobius = Mobius.construct()
  t.Isometry(g, 0, p1.Negate())

  let p2t: Complex = t.ApplyComplex(p2)
  let m2: Mobius = Mobius.construct()
  let m1: Mobius = Mobius.construct()
  m1.Isometry(g, 0, p1.Negate())
  m2.Isometry(g, 0, p2t)

  let m3: Mobius = m1.Inverse()
  this.Merge(m3.Multiply(m2.Multiply(m1)))
}

This is defined on a Mobius class which calculates a Mobius transform. You don't need to know about Mobius transforms (I don't know much), but they have 4 complex number fields. They are then attached to an "Isometry", which is used to compute the transform for each polygon in a hyperbolic tessellation, like this:

Polygon p = tile.Polygon.Clone();
p.Transform( isometry );
Color color = getColor()
GLUtils.DrawConcavePolygon( p, color, GrabModelTransform() );

Given that, I would think about potentially calling the Geodesic function like this:

Geodesic(Geometry.Hyperbolic, currentPosition, desiredPosition)

That would, I think, compute the final transformation on the Mobius object on the Isometry. So that gives me the final position (I think so far).

My question is, given we have the final position, how do we do a lerp sort of function to animate to it? At a high level, how might you accomplish that?

I would think that one approach is to generate a hundred points between here and there, like path segment points (breaking down the curved path into small segments), and then tweening from one point to the next. That wouldn't be ideal though, so maybe there is an equation like the lerp function that would work. Either way, I don't see how I could accomplish it with either of these approaches. How do you take the difference between two points on the hyperbolic plane, get the arc'd path between them, and then animate along those arcs?

Lance
  • 75,200
  • 93
  • 289
  • 503

0 Answers0