0

i wanted to implement B-splines in an Java Swing application. And since 4. May i tried nearly everything and spend every minute of my free time, but i don't get them right - i have headach since yesterday :/

To implement the B spline i used the specification from wikipedia

And i made (relating to MCVE) an demo version which can be found here: gist.github.com/soraphis/b-spline

the code shows the problem, its made to be called in an swing JPanel drawcomponent method. But you can comment this line and uncomment 71.

Some additional informations:

  • a and b in my basis function returns values < 0 or >1 which should not be (see wikipedia)
  • i want to implement the b splines myself - i dont want to use a library
  • referencinc to the wikipedia artikel:
    • basisFunc is B(x)
    • DeBoor is S(x)

i rly need help with the basis function, and i would like to know how to build up the knot vector "correctly"

Im thankful for every kind of reply
and thx for reading this

Community
  • 1
  • 1
Soraphis
  • 706
  • 7
  • 26
  • As usual, a http://stackoverflow.com/help/mcve would be worth more than 1000 linked formula PNGs. Just out of curiosity: The intention is to *implement* this, right? If you wanted to create a spline otherwise, you could just use `Path2D`. Not sure whether someone will help you with debugging, though... – Marco13 May 08 '14 at 20:42
  • thx for the reply. yeah i want to implement the spline myself, my gist is rly standalone and i mentioned the parts i have problems with. related to MCVE it is minimal and complete aswell as veryfiable. the png formulars are just references ... – Soraphis May 08 '14 at 21:15

1 Answers1

2

I can't imagine an appropriate answer that does not consist of a piece of code that you just can compile and run (without having to insert a main method and the surrounding GUI ... * wink *), regardless of how different it is from your code.

However, the weights that you have assigned to your T-array seemed odd, and the indices of the basis function seemed to be off +/-1. I tried to fix this, but it's still not entirely correct, maybe you or someone else likes to continue debugging this.

import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.Arrays;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class BSplineTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new GridLayout(1, 1));

        BSplineTestPanel bSplineTestPanel = new BSplineTestPanel();
        frame.getContentPane().add(bSplineTestPanel);
        frame.setSize(500,500);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

class BSplineTestPanel extends JPanel
{
    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Bspline bspline = new Bspline();
        bspline.calculatePath(g);
    }
}

class Bspline
{
    double[][] P;
    double[] T;
    int _k = 3;

    public Bspline()
    {
        P = new double[4][2];
        P[0][0] = 100;
        P[0][1] = 100;
        P[1][0] = 50;
        P[1][1] = 50;
        P[2][0] = 100;
        P[2][1] = 200;
        P[3][0] = 50;
        P[3][1] = 200;
        update();
    }

    private void update()
    {
        if (P.length < 2)
            return;
        T = new double[_k + P.length + 1];
        double d = 1.0 / (T.length-1);
        for (int i = 0; i<T.length; i++)
        {
            T[i] = i * d;
        }
        System.out.println(Arrays.toString(T));

    }

    private double basisFunc(int i, int k, double t)
    {
        if (k == 0)
        {
            if (T[i] <= t && t < T[i + 1])
                return 1;
            return 0;
        }
        double a = (t - T[i]) / (T[i + k] - T[i]);
        double b = (T[i + k + 1] - t) / (T[i + k + 1] - T[i + 1]);
        return a * basisFunc(i, k - 1, t) + b * basisFunc(i + 1, k - 1, t);
    }

    private double[] DeBoor(double t)
    {
        double[] V = new double[2];
        for (int i = 0; i < P.length; i++)
        {
            double scale = basisFunc(i, _k, t);
            V[0] += P[i][0] * scale;
            V[1] += P[i][1] * scale;
        }
        return V;
    }

    public void calculatePath(Graphics g)
    {
        if (P.length < 2)
        {
            return; // zu wenige punkte um ein pfad zu zeichnen
        }
        double[] v = null;
        double delta = 1 / 32.0;
        for (double t = T[2]; t < T[5]; t += delta)
        {
            double[] p = DeBoor(t);
            if (v != null)
            {
                g.setColor(new Color((int)(t*255), 0, 0));
                g.drawLine((int) v[0], (int) v[1], (int) p[0], (int) p[1]);
            }
            v = p;
        }

        for (int i = 0; i < P.length; i++)
        {
            int x = (int)P[i][0];
            int y = (int)P[i][1];
            g.setColor(Color.RED);
            g.fillOval(x-2, y-2,  4,  4);
            g.drawString(String.valueOf(i), x, y+15);
        }
    }
}

(Compare to http://www.ibiblio.org/e-notes/Splines/basis.html, "Quadratic B-spline (n = 3, k = 3)")

Apart from that I wonder how such an overly complicated **** like deBoors algorithm could become so "famous". Use De-Casteljau and you'll be done in a few minutes, without a single debugging run.


EDIT A port of the code from http://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/

// From http://chi3x10.wordpress.com/2009/10/18/de-boor-algorithm-in-c/
double[] deBoor(int k,int degree, int i, double x, double knots[], double ctrlPoints[][])
{
    if( k == 0)
    {
        i = Math.max(0, Math.min(ctrlPoints.length-1, i));
        return ctrlPoints[i];
    }
    else
    {  
        double alpha = (x-knots[i])/(knots[i+degree+1-k]-knots[i]);
        double p0[] = deBoor(k-1,degree, i-1, x, knots, ctrlPoints);
        double p1[] = deBoor(k-1,degree, i, x, knots, ctrlPoints);
        double p[] = new double[2];
        p[0] = p0[0] *(1-alpha ) + p1[0]*alpha;
        p[1] = p0[1] *(1-alpha ) + p1[1]*alpha;
        return p;
    }
}    
int WhichInterval(double x, double knot[], int ti)
{
    int index = -1;

    for(int i = 1; i <= ti - 1; i++)
    {
        if(x < knot[i]) {
            index = i - 1;
            break;
        }
    }
    if(x == knot[ti - 1]) {
        index = ti - 1;
    }
    return index;
}

private double[] DeBoor(double t)
{
    int i = WhichInterval(t, T, T.length);
    return deBoor(_k, 3, i, t, T, P);
}
Marco13
  • 53,703
  • 9
  • 80
  • 159
  • wow thx, looks good. my knot values looked like this because i want to count the first and the last knot multiple times. i dont have time atm to check more but later that day. thanks rly! – Soraphis May 10 '14 at 08:45
  • ok, tested it and it looks good with 1 exception: i want an open uniform knot vector like `[0,0,0.33,0.66,1,1,1]` but with that i get a line starting at (0,0) (due to 0 divisions) which was my problem earlier – Soraphis May 11 '14 at 09:17
  • @Soraphis Yes, I mentioned that it is still not entirely correct. However I can hardly imagine that someone is willing to go through your code and check where you made some indexing error or so. I inserted a working version from another site. Of course, this is not satisfactory (and don't hesitate to remove the upvote - I really did not help you so much with your particular problem), but for me (personally) it's on the one hand unsatisfactory to debug other people's code, and on the other hand, not worth the effort to create something as useless as an own deBoor implementation... – Marco13 May 11 '14 at 10:58
  • ... however, the original problem may be related to the fact that you are not inserting new control points, as indicated by this well-known "pyramid scheme" that you'll find on all websited that pretend to explain the deBoor algorithm. – Marco13 May 11 '14 at 11:00
  • Im pretty sure its no problem with indexing, because it works as thought the problem is the 0 division which is ... intended. In the german wikipedia it says "it is possible, that the denominator in the recursion formular may be 0, but then function is the null function" but ... i dont know how to implement that ... everything i try doesnt look right) i tried the C algorithm earlier, but no "wichintervall" worked for me and again: i dont see how the knot vector is build up - which is my problem. To the upvote: your answere is an working algorithm, so no reason to remove the upvote :D – Soraphis May 11 '14 at 12:46
  • to the "pyramid scheme" it just explains the recursion, doesnt it? just saw that your code has the same problem as mine see [here](https://drive.google.com/file/d/0Bzv4AkTSDuf1aHAzZ3djdy1Cdms/edit?usp=sharing) it tends to (0,0) :/ (if you rearrange the points you'll see it better) – Soraphis May 11 '14 at 13:27
  • @Soraphis The code that I posted originally was mainly an attempt to "clean up" your code, hoping that I might stumble over the error while doing that. But I think that your code is conceptually wrong: You are *not* computing new points to interpolate between. The "pyramid" does not only show the recursion, but also that the interpolation at each recursion level happens between new **points**, and not between values of the basis function. I think http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node18.html explains it pretty well, and this is similar to the code from the "EDIT" part. – Marco13 May 11 '14 at 13:39