1

I'm currently developing something that requires objects to orbit around another. Though, I am limited. I cannot use Radians to achieve this. I have access to sin and cos, and the degrees. But I cannot use radians (it breaks everything). Note that this is within Minecraft and the values there cannot hold floats or doubles. So, an answer like 0.017 will be 17. For this reason, I cannot use radians.

The function to calculate sin and cos is limited between -180 to 180. This means I cannot simply turn 0.787 radians into 787 radians, as that's out of the limit, and the answer returned would be completely wrong.

Right now the code would be something like this:

var distance = 100; // from the centre of orbit
var degrees = 45; // around a 360 degree orbit
var radians = degrees * (Math.PI / 180);

var x = Math.cos(radians) * distance;
var y = Math.sin(radians) * distance;

But that code completely relies on degrees being converted into radians. I cannot do this, because of Minecraft's integer limits, and how the functions calculate sin and cos. It just is simply not possible.

So the main questions is: How can I find future position of an object with just the degrees, sin and cos? (Perhaps base the answer as if the degrees were 45 for instance)

Here is a great example picture: An image showing what I need to do

Spektre
  • 49,595
  • 11
  • 110
  • 380
James
  • 699
  • 2
  • 6
  • 23
  • 1
    Maybe on here you get some better math answers: https://math.stackexchange.com/ And maybe you show some code or provide a bit more information, it is a bit vague at the moment – G43beli May 07 '18 at 07:04
  • I'll post one there too, thanks for the suggestion =) – James May 07 '18 at 07:05
  • The conversion between degrees and radians is quite simple: you just multiply the degrees value by `pi` to get the radians value. If you can't use decimal fractions, you can work around it by multiplication and division accordingly – GalAbra May 07 '18 at 07:11
  • @GalAbra Actually I cannot do that. The function to calculate sin and cos is limited between -180 and 180 degrees (for reasons of not having access to decimals.) If I had to use, for example, 0.787 radians, I'd have to enter 787 into the function, and that would break it and return a completely wrong answer. – James May 07 '18 at 07:18
  • Please don't use JPG for non-photographic images! – Andreas Rejbrand May 07 '18 at 07:21
  • @AndreasRejbrand Just found that image online =) Not my doing. – James May 07 '18 at 07:22
  • @MSpace-Dev I added better approx constants for both ranges `<-100,+100>` and `<-127,+127>` which is even more effective as it get rid of the `/fp` division – Spektre May 07 '18 at 09:23

1 Answers1

2

why not make your own LUT in fixed point ? something like this in C++:

const int fp=1000; // fixed point precision
const int mycos[360]={ 1000, 999, 999, 998, 997, 996, 994, 992, 990, ... }

float x,y,x0=0,y0=0,r=50,ang=45;
x = x0 + ( (r*mycos[ ang    %360]) / fp );
y = y0 + ( (r*mycos[(ang+90)%360]) / fp );

Also you can write a script that will create the LUT for you. Each value in the LUT is computed like this:

LUT[i] = fp*cos(i*M_PI/180); // i = 0,1,2,...359

Now to normalize angle before use:

ang %= 360;
if (ang<0) ang+=360;

There are also ways to compute sin,cos tables with integer variables only out there. We used it in 8-bit era asm on Z80 for our stuff and later on x86 demos ... so it is possible to write code that would create it directly in minecraft script without the need of another compiler use.

You can even change the angular units to power of 2 instead of 360 so you can get rid of the modulo and also set the fp to mower of 2 -1 so you do not need even to divide. After some digging in my source archives I found my ancient TASM MS-DOS demo which uses this technique. After porting it to C++ and tweaking the constants here C++ result:

int mysinLUT[256];
void mysin_init100() // <-100,+100>
    {
    int bx,si=620,cx=0,dx;  // si ~amplitude
    for (bx=0;bx<256;bx++)
        {
        mysinLUT[bx]=(cx>>8);
        cx+=si;
        dx=41*cx;
        if (dx<0) dx=-((-dx)>>16); else dx>>=16;
        si-=dx;
        }
    }
void mysin_init127() // <-127,+127>
    {
    int bx,si=793,cx=0,dx;  // si ~amplitude
    for (bx=0;bx<256;bx++)
        {
        mysinLUT[bx]=(cx>>8)+1;
        cx+=si;
        dx=41*cx;
        if (dx<0) dx=-((-dx)>>16); else dx>>=16;
        si-=dx;
        }
    }
int mysin(int a){ return mysinLUT[(a   )&255]; }
int mycos(int a){ return mysinLUT[(a+64)&255]; }

The constants are set so the sin[256] holds rough approximation of sinus within range <-100,+100> or <-127,+127> (depends on which init you call) and the angle period is 256 instead of 360. You need first call the mysin_init???(); once to init the LUT after that you can use mysin,mycos just do not forget to divide the final result by /100 or >>7.

When I render overlay of real and approximated circle using VCL:

void draw()
    {
    // select range
//  #define range100
    #define range127

    // init sin LUT just once
    static bool _init=true;
    if (_init)
        {
        _init=false;
        #ifdef range100
        mysin_init100();
        #endif
        #ifdef range127
        mysin_init127();
        #endif
        }

    int a,x,y,x0,y0,r;
    // clear screen
    bmp->Canvas->Brush->Color=clWhite;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    // compute circle size from window resolution xs,ys
    x0=xs>>1;
    y0=ys>>1;
    r=x0; if (r>y0) r=y0; r=(r*7)/10;
    // render real circle
    bmp->Canvas->Pen->Color=clRed;
    bmp->Canvas->Ellipse(x0-r,y0-r,x0+r,y0+r);
    // render approximated circle
    bmp->Canvas->Pen->Color=clBlack;
    for (a=0;a<=256;a++)
        {
        #ifdef range100
        x=x0+((r*mycos(a))/100);
        y=y0-((r*mysin(a))/100);
        #endif
        #ifdef range127
        // if >> is signed (copying MSB)
        x=x0+((r*mycos(a))>>7);
        y=y0-((r*mysin(a))>>7);
        // if >> is unsigned (inserting 0) and all circle points are non negative
//      x=( (x0<<7)+(r*mycos(a)) )>>7;
//      y=( (y0<<7)-(r*mysin(a)) )>>7;
        // this should work no matter what
//      x=r*mycos(a); if (x<0) x=-((-x)>>7); else x>>=7; x=x0+x;
//      y=r*mysin(a); if (y<0) y=-((-y)>>7); else y>>=7; y=y0-y;
        // this work no matter what but use signed division
//      x=x0+((r*mycos(a))/127);
//      y=y0-((r*mysin(a))/127);
        #endif
        if (!a) bmp->Canvas->MoveTo(x,y);
        else    bmp->Canvas->LineTo(x,y);
        }
    Form1->Canvas->Draw(0,0,bmp);
    //bmp->SaveToFile("out.bmp");
    }

the result looks like this:

result

Red is real circle and Black is circle using mysin,mycos. As you can see there are small deviations due to approximations accuracy but no floating point operation is used here. It is weird as the 3 methods of bitshift rsults in different numbers (it must be some optimization of mine compiler) the constants are tweaked for the first method.

Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Really awesome answer dude, thank you! Hope I can get this implemented in the functions =) – James May 07 '18 at 09:32
  • 1
    @MSpace-Dev this is what we used before **FPU** and `float` :) ... we did not have even multiplication instruction then ... and had to implement it on our own – Spektre May 07 '18 at 09:33
  • Ah, that's actually super interesting! Never knew that. Glad that you have the experience to show me how to do this =) Ty again. – James May 07 '18 at 09:53
  • 1
    @MSpace-Dev I just spotted a bug (`mysin` and `mycos` where swapped I repaired the code) also if you're interested in low level operations see [Building a logarithm function in C without using float type](https://stackoverflow.com/a/42108287/2521214) I am still tweaking the constants have even better will update when finished :) – Spektre May 07 '18 at 10:16
  • I've never used C before, "lowest" I've gone is C++, but I'll definitely drop that a read, looks interesting =) Thanks for the bug fix – James May 07 '18 at 10:25
  • 1
    @MSpace-Dev the answer is in C++ ... only the title is in C ... they are almost the same but in C you do not have clases and some libs ... – Spektre May 07 '18 at 10:26
  • 1
    @MSpace-Dev I updated the code it is weird the bitshifts have diffeent results (they should have be the same) some faull optimization of code by my compiler is most likely in the play... but +/-1 pixel is not that a big deal ... The 127 range is more accurate than the 100 – Spektre May 07 '18 at 10:50
  • Haha, thanks for all the effort bud! I see why you have so much rep on this site, haha. This is a HUGE help, you don't even know. – James May 07 '18 at 19:36