Find a continuous bijective function with the given form and its inverse

77 Views Asked by At

I am looking to find a function $g:\mathbb{R}^2\rightarrow\mathbb{R}^2$ and its inverse $g^{-1}$, such that $g$ and $g^{-1}$ are continuous, smooth, and bijective, such that:

$$ g(x,0)=\begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x \end{pmatrix} $$

So the problem is to find an expression for a function $g(x, y)$ and its inverse $g^{-1}(x, y)$, which satisfies the criteria above in general when $y\ne0$. I realise there are probably infinitely many solutions to this problem, so any specific solution would be appreciated.

To give some background to the problem, I'm trying to find a general method for finding a function which can transform one quadratic Bezier curve to another one using an invertible transformation of 2D space. I intuitively feel that it should be possible to solve this problem, and I think this problem can be reduced to finding an invertible transformation from one specific Bezier curve to another, in which both Bezier curves start at $(0,0)$ and end at $(1,0)$, the "input" Bezier curve to the function $g$ has a central control point at $(0.5,0)$, and the output Bezier curve has a control point at $(a,b)$. I believe this more general problem can be solved using the answer to this question and simple affine transformations which are easily found.

(Sorry if this problem is overly open-ended or under-constrained. I initially tried a solution of the form $g(x,0)=\begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x+y \end{pmatrix}$, but this in general is not bijective: see the image and code shown below for lines of $g(x,y)$ in the case $a=1,b=1$ with $x\in[-1,2],y\in[-1,1]$ with $y$ constant along each line. A specific function that satisfies the criteria stated in the problem would be great, but the problem is I'm not entirely sure how to go about finding a valid solution to this problem, so even a suggestion of how to get started would be useful. I'm also not 100% sure that there is a solution to this problem, so if this is the case then a proof that there is no solution would obviously be valuable instead).

enter image description here

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-1, 2)
for y in np.arange(-1, 1.1, 0.2):
    plt.plot(2 * (1-x) * x + x*x, 2 * (1-x) * x + y, "g--")

Background

A linear Bezier curve interpolates between two points $P_0$ and $P_1$, parameterised by a continuous variable $t \in [0, 1]$:

$$ B(t) = (1 - t) P_0 + t P_1 $$

A quadratic Bezier curve defined by three points $P_0$, $P_1$ and $P_2$ interpolates between two linear Bezier curves, one from $P_0$ to $P_1$, and one from $P_1$ to $P_2$:

$$ \begin{align} B(t) &= (1 - t) \Bigl( (1 - t) P_0 + t P_1 \Bigr) + t \Bigl( (1 - t) P_1 + t P_2 \Bigr) \\ &= (1 - t)^2 P_0 + 2t(1 - t)P_1 + t^2 P_2 \end{align} $$

If we set $P_0 = \begin{pmatrix} 0 & 0 \end{pmatrix}^T, P_1 = \begin{pmatrix} a & b \end{pmatrix}^T, P_0 = \begin{pmatrix} 1 & 0 \end{pmatrix}^T $, we get the following parametric curve:

$$ \begin{align} B(t) &= (1 - t)^2 \begin{pmatrix} 0 \\ 0 \end{pmatrix} + 2t(1 - t) \begin{pmatrix} a \\ b \end{pmatrix} + t^2 \begin{pmatrix} 1 \\ 0 \end{pmatrix} \\ &= \begin{pmatrix} 2a(1-t)t+t^2 \\ 2b(1-t)t \end{pmatrix} \end{align} $$

We can view this curve as a transformation of 2D space, denoted by $g(x, y)$, along a line of constant $y = 0$:

$$ \begin{align} g(x,0) &= B(x) \\ &= \begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x \end{pmatrix} \end{align} $$

The question now is how to define $g(x, y)$ for $y \ne 0$ such that $g$ is bijective.

1

There are 1 best solutions below

0
On

Here is $\frac{3}{4}$ of a solution:

$$ \eqalign{ g(x,y)&=g(x,0)+\alpha y\begin{pmatrix}0&-1\\1&0\end{pmatrix}\frac{\partial g(x,0)}{\partial x} \cr g(x,0)&=\begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x \end{pmatrix} \cr \Rightarrow \frac{\partial g(x,0)}{\partial x}&=\begin{pmatrix}2a(1-2x)+2x \\ 2b(1-2x) \end{pmatrix} \cr \Rightarrow g(x,y)&=\begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x \end{pmatrix}+\alpha y\begin{pmatrix}0&-1\\1&0\end{pmatrix}\begin{pmatrix}2a(1-2x)+2x \\ 2b(1-2x) \end{pmatrix} \cr &=\begin{pmatrix}2a(1-x)x+x^2 \\ 2b(1-x)x \end{pmatrix}+\alpha y\begin{pmatrix}-2b(1-2x) \\ 2a(1-2x)+2x \end{pmatrix} \cr } $$

The scaling constant $\alpha$ can be chosen arbitrarily, EG $\alpha=\frac{1}{\left|\frac{\partial g(x,0)}{\partial x}\right|}$ to preserve certain distances, or $\alpha=1$ for simplicity. This function takes an $x$ coordinate, transforms it as if $y=0$, and then adds a vector which is perpendicular to the curve at $g(x,0)$, and is proportional to $y$.

Taking the example stated at the end of the question above with $a=b=1$, this function is $\frac{3}{4}$ of a solution in the sense that $g:\mathbb{R}^2\rightarrow\mathbb{R}^2$ is surjective, and the map of the upper half of the plane $g:(-\infty,\infty)\times[0, \infty)\rightarrow\mathbb{R}^2$ is injective, but the map $g:\mathbb{R}^2\rightarrow\mathbb{R}^2$ is not injective, see below for a visual demonstration:

With $\alpha=\frac{1}{\left|\frac{\partial g(x,0)}{\partial x}\right|}$:

enter image description here

With $\alpha=1$:

enter image description here

Here is the code for plotting the above graphics (change the default argument normalise to the function g from True to False to alternate between the cases above):

import numpy as np
import matplotlib.pyplot as plt
import os
import shutil
import PIL

def g(x, y, a=1, b=1, alpha=1, normalise=True):
    g11 = 2*a*(1-x)*x + x*x
    g21 = 2*b*(1-x)*x
    g12 = -2*b*(1-2*x)
    g22 = (2*a*(1-2*x) + 2*x)
    if normalise:
        scale = 1 / np.sqrt(g12*g12 + g22*g22)
    else:
        scale = 1
    g1 = g11 + alpha*y*scale*g12
    g2 = g21 + alpha*y*scale*g22
    return g1, g2

def plot_g(a, b, filename, x_min=-5, x_max=7, y_min=-6, y_max=5, step=0.2):
    plt.figure(figsize=[8, 6])
    x = np.linspace(x_min, x_max, 500)
    plt.plot(*g(x, 0, a, b), "r", lw=2, zorder=10)
    for y in np.arange(y_min, y_max + step/2, step):
        plt.plot(*g(x, y, a, b), "k", alpha=0.2)
    y = np.linspace(y_min, y_max, 500)
    for x in np.arange(x_min, x_max + step/2, step):
        plt.plot(*g(x, y, a, b), "k", alpha=0.2)
    plt.xlim(-3, 3)
    plt.ylim(-3, 3)
    plt.savefig(filename, dpi=50)
    plt.close()

def make_gif(output_filename, input_filename_list):
    first_frame = PIL.Image.open(input_filename_list[0])
    first_frame.save(
        output_filename,
        format="gif",
        save_all=True,
        append_images=[PIL.Image.open(f) for f in input_filename_list[1:]],
        duration=100,
        optimise=True,
        loop=0,
    )

t = np.linspace(0, 2*np.pi, 100)
alpha_list = 0.5 * (1 - np.cos(t))
filename_list = []
current_dir = os.path.dirname(os.path.abspath(__file__))
output_dir = os.path.join(current_dir, "temp_dir")
if os.path.isdir(output_dir):
    shutil.rmtree(output_dir)
os.makedirs(output_dir)
for i, alpha in enumerate(alpha_list):
    a = 0.5 + alpha*1
    b = 0   + alpha*1
    filename = os.path.join(output_dir, "frame %i.png" % i)
    plot_g(a, b, filename)
    print(".", end="", flush=True)
    filename_list.append(filename)

output_filename = os.path.join(current_dir, "Bezier transformation.gif")
make_gif(output_filename, filename_list)

Here is another cool looking graphic with a fixed at 0.5 (and not varying between frames):

enter image description here