
/* 
 * This is a texture plugin for blender 1.60 or above.
 *
 * LED-drawing plugin, based on a sample plugin from NaN
 * Copyright (c) 1999, Bernd Breitenbach
 *
 * $Id: led.c,v 1.3 1999/08/08 23:57:45 bernd Exp bernd $
 * 
 */

/* ************************************************************
	NOTE: This software comes with absolute no warranty.
	You may distribute, modify and use it free of charge.
	
	If you have any enhancements or bug fixes, please let me now:

	              breitenb@iml.fhg.de
   ************************************************************ */

#include <stdio.h>
#include "math.h"
#include "plugin.h"

/* ******************** GLOBAL VARIABLES ***************** */

char name[]= "LED";
char *release="$Revision: 1.3 $";

/* Subtype names must be less than 15 characters */

#define NR_TYPES	3
char stnames[NR_TYPES][16]= {"Counter","HH:MM:SS","HH:MM"};

#define COUNTER (0)
#define HHMMSS (1)
#define HHMM   (2)

VarStruct varstr[]= {
  {LABEL|FLO,"led VX.X   bb   ",0,      0,     0, ""},
  {NUM|INT,  "Digits",         4.0,  1.,       8, "No. of digits"},
  {NUM|INT,  "Thickness",      1.0,  0.,     2.0, "Thickness of the segments"}, 
  {NUM|FLO,  "Skew",           0.1,  0.,     0.5, "Skew of the digits"},
  {NUM|FLO,  "Size",           1.0,  0.01,   100, "Size of the digits"},
  {NUM|FLO,  "Light",          1.0,  0.,      1., "Value for active segments"},
  {NUM|FLO,  "Dark",           0.0,  0.,      1., "Value for inactive segments"},
  {NUM|INT,  "Value",          8888, 0,  99999999., "Value to display"},
  {NUM|INT,  "LZeros",         8.0,  0.,     8., "Display leading zeros"},
  {TOG|INT,  "12/24",          0.0,  0.0,    1.0, "Change mode from 12h to 24h"},
  {NUM|FLO,  "Delta",          0.0, -10000.,10000., "Change per Frame"},
  {TOG|INT,  "Invert",         0.0,  0.0,    1.0, "Change mode from delta*frame to frames/delta"},
  {TOG|INT,  "No AAlias",      0.0,  0.0,    1.0, "Turn antialias off (independent from render settings)"},
};

/* The cast struct is for input in the main doit function
   Varstr and Cast must have the same variables in the same order */ 

typedef struct Cast {
  float dummy;
  int   digits;
  int   thickness;
  float skew;
  float size;
  float light;
  float dark;
  int   value;
  int   zeros;
  int   m_12_24; 
  float delta;
  int   invert;
  int  noaalias;
} Cast;

/* result: 
   Intensity, R, G, B, Alpha, nor.x, nor.y, nor.z
 */

float result[8];

/* cfra: the current frame */

float cfra;

int plugin_tex_doit(int, Cast *, float *, float *, float *);


/* ******************** Fixed functions ***************** */

int plugin_tex_getversion(void) 
{	
	return B_PLUGIN_VERSION;
}

void plugin_but_changed(int but) 
{

}

void plugin_init(void)
{
}

/* this function should not be changed: */

void plugin_getinfo(PluginInfo *info)
{
  
  info->name= name;
  info->stypes= NR_TYPES;
  info->nvars= sizeof(varstr)/sizeof(VarStruct);
	
  info->snames= stnames[0];
  info->result= result;
  info->cfra= &cfra;
  info->varstr= varstr;

  info->init= plugin_init;
  info->tex_doit=  (TexDoit) plugin_tex_doit;
  info->callback= plugin_but_changed;
}

/* ************************************************************
	LED
	

	Anti-aliasing is performed by calculating the mean value 
	from four texture values at the vertices of an pixel. 
	This can be turned off by toggle the 'no aalias'-button
   ************************************************************ */


/*   
     The segments:

        1
       ---
     6|   |2
      | 7 |
       ---
     5|   |3
      |   |
       --- o 8 
        4  



 */

#define S1 (0x01)
#define S2 (0x02)
#define S3 (0x04)
#define S4 (0x08)
#define S5 (0x10)
#define S6 (0x20)
#define S7 (0x40)


#define PATTERN (12)


#define EMPTY  (10)
#define HYPHEN (11)

static int seg_active[PATTERN]={
  S1|S2|S3|S4|S5|S6,   /* 0 */
  S2|S3,               /* 1 */
  S1|S2|S7|S5|S4,      /* 2 */
  S1|S2|S7|S3|S4,      /* 3 */
  S2|S3|S6|S7,         /* 4 */
  S1|S6|S7|S3|S4,      /* 5 */
  S1|S6|S5|S4|S3|S7,   /* 6 */
  S1|S2|S3,            /* 7 */
  S1|S2|S3|S4|S5|S6|S7,/* 8 */
  S1|S6|S7|S2|S3|S4,   /* 9 */
  0,                   /*   */
  S7                   /* - */
};



#define SPACE (0.04)
#define ABOVE(a,b) (y>(a*x+b+SPACE))
#define BELOW(a,b) (y<(a*x+b-SPACE))
#define INCIRCLE(u,v) ((u-x)*(u-x)+(v-y)*(v-y)<width*width)
#define SETVAL(v) result[0] = (v) ? cast->light : cast->dark; 

static float cor_arr[3]={0.06,0.08,0.12};
static float width_arr[3]={0.15,0.20,0.25};


int calc_colon_value(Cast *cast,float x,float y,int active)
{
  int pnt_found=0;
  float x1,y1,x2,y2;
  float width;

  y1=0.66;
  y2=1.33;
  width=0.6*width_arr[cast->thickness];

  if (x>-0.5 && x<0.5 &&
      y>y1-width && y<y2+width) {
    x1=0.5*y1*cast->skew;
    x2=0.5*y2*cast->skew;
    if (INCIRCLE(x1,y1)) {
      SETVAL(active);
      pnt_found=1;
    } else if (INCIRCLE(x2,y2)) {
      SETVAL(active);
      pnt_found=1;
    }
  }
  return pnt_found;
}


int calc_led_value(Cast *cast,float x,float y,int digit)
{ 
  int active;
  int pnt_found=0;
  float width;
  float cor;

  active=seg_active[digit%PATTERN];
  cor=cor_arr[cast->thickness];
  width=width_arr[cast->thickness];

  x=x-(0.5*y)*cast->skew;
  if (x>0. && x<1. && y>0. && y<2.) {
    pnt_found=1;
    if (x<width) { /* left vertical segments */
      if (BELOW(-1.,2.) && ABOVE(1.,1.-cor) &&
	  (y<2.-width || INCIRCLE(width,2.-width)) &&
	  (y>1.+width || INCIRCLE(width,1.+width))) {
	SETVAL(active&S6);
      } else if(ABOVE(1.,0.) && BELOW(-1.,1.+cor) &&
	  (y<1.-width || INCIRCLE(width,1.-width)) &&
	  (y>width || INCIRCLE(width,width))) {
	SETVAL(active&S5);
      }
    } else if (x>1.-width) { /* right vertical segments */
      if (BELOW(1.,1.) && ABOVE(-1,2.-cor) &&
	  (y<2.-width || INCIRCLE(1.-width,2.-width)) &&
	  (y>1.+width || INCIRCLE(1.-width,1.+width))){
	SETVAL(active&S2);
      } else if(ABOVE(-1.,1.) && BELOW(1,cor) &&
	  (y<1.-width || INCIRCLE(1.-width,1.-width)) &&
	  (y>width || INCIRCLE(1.-width,width))){
	SETVAL(active&S3);
      }
    }

    if (y<width) { /* lower horizontal segment */
      if (BELOW(1.,0.) && BELOW(-1.,1.) &&
	  (x>width || INCIRCLE(width,width)) &&
	  (x<1.-width || INCIRCLE(1.-width,width))){
	SETVAL(active&S4);
      }
    } else if (y>2.-width) { /* upper horizontal segment */
      if (ABOVE(-1.,2) && ABOVE(1.,1.) &&
	  (x>width || INCIRCLE(width,2.-width)) &&
	  (x<1.-width || INCIRCLE(1.-width,2.-width))){
	SETVAL(active&S1);
      }
    } else if (y>1.-0.5*width && y< 1.+0.5*width) { /* middle horizontal segment */
      if (BELOW(1.,1.-cor) && BELOW(-1.,2.-cor) &&
	  ABOVE(-1.,1.+cor) && ABOVE(1.,cor)) {
	SETVAL(active&S7);
      }
    }
  }
  return pnt_found;
}


#define ROUND12_24(h,val)\
 if (cast->m_12_24)\
   h=val%24;\
 else {\
   h=val%12;\
   if (!h) h=12;\
 }
#define EPS (1e-8)

void calc_value(int stype,Cast *cast,float x,float y)
{
  int val=cast->value,i,delta;
  int digit;
  int zeros=cast->zeros;
  float space=1.3;

  if (cast->invert && fabs(cast->delta)>EPS)
    delta=(int)(cfra/cast->delta);
  else
    delta=(int)(cfra*cast->delta);

  if (stype==COUNTER) {
    val+=delta;
  } else {
    int h,m,s;

    if (stype==HHMM) val*=100;
    s=(val%100)%60;
    val/=100;
    m=(val%100)%60;
    val/=100;
    ROUND12_24(h,val)
    if (fabs(cast->delta)>EPS) {
      val=h*3600+m*60+s+delta;
      s=val%60;
      val/=60;
      m=val%60;
      val/=60;
      ROUND12_24(h,val)
    }
    val=h*10000+m*100+s;
    if (stype==HHMM) val/=100;
  } 


  x=x*5./cast->size;
  y=y*5./cast->size+1.;

  x-=0.5*space*cast->digits;

  digit=val%10;
  for(i=0;i<cast->digits;i++) {
    if (stype!=COUNTER && i && (i%2==0)) {
      x+=space/4;
      calc_colon_value(cast,x,y,1);
    }
    x+=space;
    if (calc_led_value(cast,x,y,digit)) { 
      break;
    }

    if (digit!=EMPTY) {
      val/=10;
      if (val)
	digit=val%10;
      else if(zeros-->0)
	digit=0;
      else
	digit=EMPTY;
    }
  }
}


int plugin_tex_doit(int stype, Cast *cast, float *texvec, float *dxt, float *dyt)
{
  float x,y;

  x=texvec[0];
  y=texvec[1];

  result[0]=0.;
  calc_value(stype,cast,x,y);

  if (!cast->noaalias && dxt && dyt) {
    float offx=fabs(dxt[0])+fabs(dxt[1]);
    float offy=fabs(dyt[0])+fabs(dyt[1]);
    calc_value(stype,cast,x+offx,y);
    calc_value(stype,cast,x,y+offy);
    calc_value(stype,cast,x+offx,y+offy);
    result[0]/=4.0;
  }

  return 0;
}





