Join us on Discord!
You can help CodeWalrus stay online by donating here.

Graphics and other system routines

Started by DarkestEx, October 19, 2015, 10:31:05 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

DarkestEx

Quote from: semiprocoder on October 20, 2015, 07:57:25 PM
Just wanted to point it out, I don't know any graphics so I won't be able to help you there.

Anyways, the trig functions, namely tan don't even give near the correct value, so here are the actual functions. I also updated sin to allow negative numbers, forgot to do that. Here is the new code, without any of the testing stuff that I had before that I was going to use:

unsigned char sinArray[90] = {0, 4, 8, 13, 17, 22, 26, 31, 35, 40, 44, 48, 53, 57, 61, 66, 70, 74, 79, 83, 87, 91, 95, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 154, 157, 161, 164, 167, 171, 174, 177, 181, 184, 187, 190, 193, 196, 198, 201, 204, 207, 209, 212, 214, 217, 219, 221, 223, 226, 228, 230, 232, 233, 235, 237, 238, 240, 242, 243, 244, 246, 247, 248, 249, 250, 251, 252, 252, 253, 254, 254, 255, 255, 255, 255, 255};
void fastSinMult(int *val, int degree) {
if (degree == 90) {
return;
}
else if (degree < 90 && degree>=0) {
*val = (*val)*sinArray[degree+1];
(*val) = (*val) >> 8;
return;
}
else if (degree > 90 && degree <= 180) {
fastSinMult(val, 180 - degree);
return;
}
else if (degree > 180 && degree <= 360) {
fastSinMult(val, degree - 180);
(*val) *= -1;
return;
}
else if (degree > 360)
fastSinMult(val, degree % 360);
else {
fastSinMult(val, -degree);
(*val) = -(*val);
}
}
void fastCosMult(int *val, int degree) {
fastSinMult(val, degree - 90);
}
void fastTanMult(int *val, int angle) {
int val1 = *val;
int val2 = *val;
fastSinMult(&val1, angle);
fastCosMult(&val2, angle);
(*val) = (*val)*val1 / val2;
}

That's great! Thanks for fixing it. I will implement it now :)
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

Snektron

Btw, you can divide the circle in 8 octands, that will reduce the static data even more :)
  • Calculators owned: TI-84+
Legends say if you spam more than DJ Omnimaga, you will become a walrus...


DarkestEx

Quote from: Cumred_Snektron on October 20, 2015, 08:00:03 PM
Btw, you can divide the circle in 8 octands, that will reduce the static data even more :)
Interesting idea. Well I could store a lot more static values. I have half a MB of flash to use up. I will be able to fit multiple fonts too in that space, so I don't think I need to compact this more (for speed reasons too).
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

semiprocoder

#18
Sorry again. So sorry. Even after testing I didn't really pay attention to the cos values or tan values. I modified my second post to work. I checked the outputs of all the functions, and they seem pretty good. Not perfect, obviously, and you have to be very safe with tan due to divide by 0 error.
  • Calculators owned: ti nspire, ti 84 plus se
My cemetech username is awesommee333.

novenary

Quote from: DarkestEx on October 20, 2015, 05:01:23 PM
Rounded rectangles would be useful too I guess. And if somebody has a lightweight raycaster or raytracer written in C or who could port one, I would absolutely love to have it in the graphics library. At 120MHz core speed, a raytracer or raycaster is feasible and would allow for more great games.
I'd see that more as a linkable library than a built in feature, it's not exactly small.

DarkestEx

Quote from: Streetwalrus on October 20, 2015, 10:19:56 PM
Quote from: DarkestEx on October 20, 2015, 05:01:23 PM
Rounded rectangles would be useful too I guess. And if somebody has a lightweight raycaster or raytracer written in C or who could port one, I would absolutely love to have it in the graphics library. At 120MHz core speed, a raytracer or raycaster is feasible and would allow for more great games.
I'd see that more as a linkable library than a built in feature, it's not exactly small.
There are really only two ways to make it happen: Not or builtin
Problems when its not builtin are either size or speed releated. So we try to include a compact raycaster.
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

Dream of Omnimaga

Would the raycaster allow people to use textures?  Otherwise if it's large then maybe it wouldn't be worth adding. On the other hand, if it supports textures and is even larger then that won't be worth it either, so I would say only add raycasting if it won't take too much space.
  • Calculators owned: TI-82 Advanced Edition Python TI-84+ TI-84+CSE TI-84+CE TI-84+CEP TI-86 TI-89T cfx-9940GT fx-7400G+ fx 1.0+ fx-9750G+ fx-9860G fx-CG10 HP 49g+ HP 39g+ HP 39gs (bricked) HP 39gII HP Prime G1 HP Prime G2 Sharp EL-9600C
  • Consoles, mobile devices and vintage computers owned: Huawei P30 Lite, Moto G 5G, Nintendo 64 (broken), Playstation, Wii U

semiprocoder

#22
Also I think that some useful graphics type things you could have would be translating and rotating the display screen.
It would go something along the lines of this:

//some global vars private to the file with them, forget what modifier specifies that so just leaving it be
int degRot;//degree rotated
int transX;//amount translated
int transY;
void setPixelTR(int x, int y){//T is translated, R is rotated. This requires floats, and I am not sure how to do it without, but maybe just mult everything by 256 like I did with fast trig, and then bit shift >>8?
    int mag=sqrt(x^2+y^2);
    int angle=acos(x/mag);
    angle+=degRot;
    setPixel(mag*cos(angle)+transX, mag*sin(angle)+transY);
}


Also I am working on arc trig functions. You input a val from 0 to 256 and get the angle correspoding to that value, similar to a converse of my fast trig. It is taking a while cause I also have to make a fast sqrt function for atan(so far I have made an extremely inaccurate one(it got sqrt 7500=82. 82*82=6724, so innacurate. It should be 86 or 87) using bit shifting, which I will use as a guess for the average one that I don't remember the name of. If the number passed to be square rooted is 16 bits or less, I use an index of 256 numbers square rooted, so it uses them. Otherwise the array would be too large.), as atan=fastACos(math.sqrt(1/(1+math.pow(val,2)))), which I calculated for my learn to fly idle game, that really didn't improve performance, but I just wanted to make it for excercise.
  • Calculators owned: ti nspire, ti 84 plus se
My cemetech username is awesommee333.

Snektron

Translation is defenitely cooll, though im not so sure about rotation. Since it requires a sqrt and an acos for every pixel it will be horribly slow...
  • Calculators owned: TI-84+
Legends say if you spam more than DJ Omnimaga, you will become a walrus...


DarkestEx

#24
Quote from: DJ Omnimaga
Would the raycaster allow people to use textures?  Otherwise if it's large then maybe it wouldn't be worth adding. On the other hand, if it supports textures and is even larger then that won't be worth it either, so I would say only add raycasting if it won't take too much space.
I think that it will support textures.

Quote from: semiprocoder
Also I think that some useful graphics type things you could have would be translating and rotating the display screen.
It would go something along the lines of this:

--- Code: ---//some global vars private to the file with them, forget what modifier specifies that so just leaving it be
int degRot;//degree rotated
int transX;//amount translated
int transY;
void setPixelTR(int x, int y){//T is translated, R is rotated. This requires floats, and I am not sure how to do it without, but maybe just mult everything by 256 like I did with fast trig, and then bit shift >>8?
    int mag=sqrt(x^2+y^2);
    int angle=acos(x/mag);
    angle+=degRot;
    setPixel(mag*cos(angle)+transX, mag*sin(angle)+transY);
}

--- End code ---

Also I am working on arc trig functions. You input a val from 0 to 256 and get the angle correspoding to that value, similar to a converse of my fast trig. It is taking a while cause I also have to make a fast sqrt function for atan(so far I have made an extremely inaccurate one(it got sqrt 7500=82. 82*82=6724, so innacurate. It should be 86 or 87) using bit shifting, which I will use as a guess for the average one that I don't remember the name of. If the number passed to be square rooted is 16 bits or less, I use an index of 256 numbers square rooted, so it uses them. Otherwise the array would be too large.), as atan=fastACos(math.sqrt(1/(1+math.pow(val,2)))), which I calculated for my learn to fly idle game, that really didn't improve performance, but I just wanted to make it for excercise.
Thanks, sounds good :)

Quote from: Cumred_Snektron
Translation is defenitely cooll, though im not so sure about rotation. Since it requires a sqrt and an acos for every pixel it will be horribly slow...
I think 90° steps are enough.
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

adekto

#25
curently looking into triangles and stumbled upon u8glibs convex polygon drawing.
i cant realy figure out how it works but it might me nice to have
this is in c++ and we need it in c. im not the best translater and not fully understanding whats going on makes it even harder
/*

  u8g_polygon.c

  Implementation of a polygon draw algorithm for "convex" polygons.

  Universal 8bit Graphics Library
 
  Copyright (c) 2013, [email protected]
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification,
  are permitted provided that the following conditions are met:

  * Redistributions of source code must retain the above copyright notice, this list
    of conditions and the following disclaimer.
   
  * Redistributions in binary form must reproduce the above copyright notice, this
    list of conditions and the following disclaimer in the documentation and/or other
    materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

  See also:
  http://www.angelfire.com/linux/myp/ConvexPolRas/ConvexPolRas.html
  Computer Graphics, Principles and Practice, Foley, van Dam, Feiner, Hughes (pp 92)
  Michael Abrash's Graphics Programming Black Book, Special Edition (Chapter 38 and 39)

  Optimized for embedded systems
  - static memory usage only
  - consistent data types
  - low flash ROM consumption
 
*/


#include "u8g.h"




/*===========================================*/
/* procedures, which should not be inlined (save as much flash ROM as possible */

static uint8_t pge_Next(struct pg_edge_struct *pge) PG_NOINLINE;
static uint8_t pg_inc(pg_struct *pg, uint8_t i) PG_NOINLINE;
static uint8_t pg_dec(pg_struct *pg, uint8_t i) PG_NOINLINE;
static void pg_expand_min_y(pg_struct *pg, pg_word_t min_y, uint8_t pge_idx) PG_NOINLINE;
static void pg_line_init(pg_struct * const pg, uint8_t pge_index) PG_NOINLINE;

/*===========================================*/
/* line draw algorithm */

static uint8_t pge_Next(struct pg_edge_struct *pge)
{
  if ( pge->current_y >= pge->max_y )
    return 0;
 
  pge->current_x += pge->current_x_offset;
  pge->error += pge->error_offset;
  if ( pge->error > 0 )
  {
    pge->current_x += pge->x_direction;
    pge->error -= pge->height;
  } 
 
  pge->current_y++;
  return 1;
}

/* assumes y2 > y1 */
static void pge_Init(struct pg_edge_struct *pge, pg_word_t x1, pg_word_t y1, pg_word_t x2, pg_word_t y2)
{
  pg_word_t dx = x2 - x1;
  pg_word_t width;

  pge->height = y2 - y1;
  pge->max_y = y2;
  pge->current_y = y1;
  pge->current_x = x1;

  if ( dx >= 0 )
  {
    pge->x_direction = 1;
    width = dx;
    pge->error = 0;
  }
  else
  {
    pge->x_direction = -1;
    width = -dx;
    pge->error = 1 - pge->height;
  }
 
  pge->current_x_offset = dx / pge->height;
  pge->error_offset = width % pge->height;
}

/*===========================================*/
/* convex polygon algorithm */

static uint8_t pg_inc(pg_struct *pg, uint8_t i)
{
    i++;
    if ( i >= pg->cnt )
      i = 0;
    return i;
}

static uint8_t pg_dec(pg_struct *pg, uint8_t i)
{
    i--;
    if ( i >= pg->cnt )
      i = pg->cnt-1;
    return i;
}

static void pg_expand_min_y(pg_struct *pg, pg_word_t min_y, uint8_t pge_idx)
{
  uint8_t i = pg->pge[pge_idx].curr_idx;
  for(;;)
  {
    i = pg->pge[pge_idx].next_idx_fn(pg, i);
    if ( pg->list[i].y != min_y )
      break;
    pg->pge[pge_idx].curr_idx = i;
  }
}

static uint8_t pg_prepare(pg_struct *pg)
{
  pg_word_t max_y;
  pg_word_t min_y;
  uint8_t i;

  /* setup the next index procedures */
  pg->pge[PG_RIGHT].next_idx_fn = pg_inc;
  pg->pge[PG_LEFT].next_idx_fn = pg_dec;
 
  /* search for highest and lowest point */
  max_y = pg->list[0].y;
  min_y = pg->list[0].y;
  pg->pge[PG_LEFT].curr_idx = 0;
  for( i = 1; i < pg->cnt; i++ )
  {
    if ( max_y < pg->list[i].y )
    {
      max_y = pg->list[i].y;
    }
    if ( min_y > pg->list[i].y )
    {
      pg->pge[PG_LEFT].curr_idx = i;
      min_y = pg->list[i].y;
    }
  }

  /* calculate total number of scan lines */
  pg->total_scan_line_cnt = max_y;
  pg->total_scan_line_cnt -= min_y;
 
  /* exit if polygon height is zero */
  if ( pg->total_scan_line_cnt == 0 )
    return 0;
 
  /* if the minimum y side is flat, try to find the lowest and highest x points */
  pg->pge[PG_RIGHT].curr_idx = pg->pge[PG_LEFT].curr_idx; 
  pg_expand_min_y(pg, min_y, PG_RIGHT);
  pg_expand_min_y(pg, min_y, PG_LEFT);
 
  /* check if the min side is really flat (depends on the x values) */
  pg->is_min_y_not_flat = 1;
  if ( pg->list[pg->pge[PG_LEFT].curr_idx].x != pg->list[pg->pge[PG_RIGHT].curr_idx].x )
  {
    pg->is_min_y_not_flat = 0;
  }
  else
  {
    pg->total_scan_line_cnt--;
    if ( pg->total_scan_line_cnt == 0 )
      return 0;
  }

  return 1;
}

static void pg_hline(pg_struct *pg, u8g_t *u8g)
{
  pg_word_t x1, x2, y;
  x1 = pg->pge[PG_LEFT].current_x;
  x2 = pg->pge[PG_RIGHT].current_x;
  y = pg->pge[PG_RIGHT].current_y;
 
  if ( y < 0 )
    return;
  if ( y >= u8g_GetHeight(u8g) )
    return;
  if ( x1 < x2 )
  {
    if ( x2 < 0 )
      return;
    if ( x1 >= u8g_GetWidth(u8g) )
      return;
    if ( x1 < 0 )
      x1 = 0;
    if ( x2 >= u8g_GetWidth(u8g) )
      x2 = u8g_GetWidth(u8g);
    u8g_DrawHLine(u8g, x1, y, x2 - x1);
  }
  else
  {
    if ( x1 < 0 )
      return;
    if ( x2 >= u8g_GetWidth(u8g) )
      return;
    if ( x2 < 0 )
      x1 = 0;
    if ( x1 >= u8g_GetWidth(u8g) )
      x1 = u8g_GetWidth(u8g);
    u8g_DrawHLine(u8g, x2, y, x1 - x2);
  }
}

static void pg_line_init(pg_struct * pg, uint8_t pge_index)
{
  struct pg_edge_struct  *pge = pg->pge+pge_index;
  uint8_t idx; 
  pg_word_t x1;
  pg_word_t y1;
  pg_word_t x2;
  pg_word_t y2;

  idx = pge->curr_idx; 
  y1 = pg->list[idx].y;
  x1 = pg->list[idx].x;
  idx = pge->next_idx_fn(pg, idx);
  y2 = pg->list[idx].y;
  x2 = pg->list[idx].x;
  pge->curr_idx = idx;
 
  pge_Init(pge, x1, y1, x2, y2);
}

static void pg_exec(pg_struct *pg, u8g_t *u8g)
{
  pg_word_t i = pg->total_scan_line_cnt;

  /* first line is skipped if the min y line is not flat */
  pg_line_init(pg, PG_LEFT);
  pg_line_init(pg, PG_RIGHT);
 
  if ( pg->is_min_y_not_flat != 0 )
  {
    pge_Next(&(pg->pge[PG_LEFT]));
    pge_Next(&(pg->pge[PG_RIGHT]));
  }

  do
  {
    pg_hline(pg, u8g);
    while ( pge_Next(&(pg->pge[PG_LEFT])) == 0 )
    {
      pg_line_init(pg, PG_LEFT);
    }
    while ( pge_Next(&(pg->pge[PG_RIGHT])) == 0 )
    {
      pg_line_init(pg, PG_RIGHT);
    }
    i--;
  } while( i > 0 );
}

/*===========================================*/
/* API procedures */

void pg_ClearPolygonXY(pg_struct *pg)
{
  pg->cnt = 0;
}

void pg_AddPolygonXY(pg_struct *pg, u8g_t *u8g, int16_t x, int16_t y)
{
  if ( pg->cnt < PG_MAX_POINTS )
  {
    pg->list[pg->cnt].x = x;
    pg->list[pg->cnt].y = y;
    pg->cnt++;
  }
}

void pg_DrawPolygon(pg_struct *pg, u8g_t *u8g)
{
  if ( pg_prepare(pg) == 0 )
    return;
  pg_exec(pg, u8g);
}

pg_struct u8g_pg;

void u8g_ClearPolygonXY(void)
{
  pg_ClearPolygonXY(&u8g_pg);
}

void u8g_AddPolygonXY(u8g_t *u8g, int16_t x, int16_t y)
{
  pg_AddPolygonXY(&u8g_pg, u8g, x, y);
}

void u8g_DrawPolygon(u8g_t *u8g)
{
  pg_DrawPolygon(&u8g_pg, u8g);
}

void u8g_DrawTriangle(u8g_t *u8g, int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2)
{
  u8g_ClearPolygonXY();
  u8g_AddPolygonXY(u8g, x0, y0);
  u8g_AddPolygonXY(u8g, x1, y1);
  u8g_AddPolygonXY(u8g, x2, y2);
  u8g_DrawPolygon(u8g);
}



-----------

oh and we got circles filled circles and lines linetriangle in the code and working :)

Edit (Streetwalrus): merged double post.

DarkestEx

I really don't like the licence of the code. It requires us to include a copyright. I think its better to use the functions provided earlier in this topic.

Also I was rewriting the image converter in Java as we had way better results there.
It will have a GUI and a command line so that it's easy for beginners and powerful for people who like to include it into an automated build system like make.
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

novenary

Quote from: DarkestEx on October 21, 2015, 01:30:03 PM
I really don't like the licence of the code. It requires us to include a copyright. I think its better to use the functions provided earlier in this topic.
That's just the BSD license, the "copyright notice" is only a request for credits.

DarkestEx

Quote from: Streetwalrus
--- Quote from: DarkestEx on Today at 03:30:03 pm ---I really don't like the licence of the code. It requires us to include a copyright. I think its better to use the functions provided earlier in this topic.

--- End quote ---
That's just the BSD license, the "copyright notice" is only a request for credits.
Sure, but i don't like and can't include the licence into the binary image. I want to have as much of the code written by us or people interested in the project as possible. I don't like reusing others code when not required. In this case, as we have most functions that we will use provided by either the great people here, written ourselves or adapted from MIT code, I think we will just rewirite the external code.
  • Calculators owned: TI-84+, Casio 101-S, RPN-Calc, Hewlett-Packard 100LX, Hewlett-Packard 95LX
  • Consoles, mobile devices and vintage computers owned: Original Commodore 64C, C64 DTV, Nintendo GameBoy Color, Nintendo GameCube, Xbox 360, PlayStation 2

novenary

The copyright notice must be distributed with the binaries. That means you can put a credits screen, have it in a readme or even on a flier that comes with the console.

Powered by EzPortal