 /**
 * $Id: trellis.c,v 1.3 2006/11/22 15:53:45 hos Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: Those lines identical to tiles.c in the
 * blender distribution 
 *
 * Contributor(s): Karl R. Wilcox (kwilcox@iee.org)
 *                  Some code and ideas were inspired by Brend Breitenbach's
 *                  rfilings plug-in. With thanks.  
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */  
	
/* ***** INSTRUCTIONS FOR USE ******
 *
 * 2D Patterns
 *  
 * This sub-type produces trellis like 2D patterns of squares, circles
 * and other more complex shapes. The patterns are created in the XY
 * plane, the Z value is ignored. The "thickness" value controls the
 * width of the "bars" in the trellis. In most cases it is a proportion
 * of the unit width, so a value of 0.1 produces bars 0.1 units wide.
 * For the more complex shapes you will have to adjust to suit. The
 * pattern is intensity only (0.0 in the "bars", 1.0 in the "spaces"
 * between). If the "multi-col" toggle is activated then the values
 * become 0.25 in the "bars" and 0.75 in the "spaces, for use with
 * colourband colours.
 * It is expected that these patterns will typically be used as a mask.
 * 
 * 3D Cube
 * 
 * The square pattern is also available as a true 3D pattern, in which
 * we also use the Z coordinate to produce a 3D "lattice". As before, 
 * the "thickness" controls the width of the bars; however the "multi-col"
 * toggle works slightly differently - every cubic "space" will be given
 * a different (random) intensity value. This can be used with the colour
 * band to produce some interesting effects.
 * Note that if "thickness" is set to 0.0, this is equivalent to the
 * POV-RAY texture "cells".            
 *
 * ***** END INSTRUCTIONS FOR USE *****       
 */ 
	
#include "math.h"
#include "plugin.h"

#ifdef DEBUG
	#include "stdio.h"
#endif

#include "stdlib.h"
	
/* Given a value v, known to be between min and max, return a value 
 * that is 0 if v = min, 1 if v = max and an appropriate proportion
 * if in between. */ 
#define NORM(v,min,max) (((v)-(min))/((max)-(min))) 

#define SIN45 0.7071067
#define OCTEDGE 0.2928932   /* 0.5 - tan(22.5) */
	
/* ******************** GLOBAL VARIABLES ***************** */ 
char name[] = "trellis";

#define VER "trellis 0.4 KRW"
/* Subtype names must be less than 15 characters */ 
	
#define NR_TYPES   2
#define TWO_D      0
#define THREE_D    1
#define NUM_VARS   9

char stnames[NR_TYPES][16] = { "2D Pattern", "3D Cube" };

VarStruct varstr[] = {	/*        Default  Min   Max Tooltip */
	{ NUM | INT, "Pattern", 1.0, 1.0, (float) NUM_VARS, 
		"Choose different patterns"},  
	{ NUMSLI | FLO, "Thickness", 0.1, 0.0, 1.0, 
		"The width of the trellis"},
	{ TOG | INT, "Multi-Colour", 0.0, 0.0, 1.0, 
		"Use colourband for colours"},
	{ LABEL | FLO, VER, 0.0, 0.0, 0.0, ""} 
};


/* 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 {
	int pattern;
	float thick;
	int multi;
	float label;
} 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 *);
void plugin_instance_init(Cast *);

/* We use globals to avoid passing lots of float args around every call */ 
float x, y, z;			/* Normalised coordinates */
float t;			/* thickness */
float t2;			/* 1/2 thickness */
int multi;			/* Use multi-colours? */

#ifdef DEBUG
	FILE * debug;
#endif
	
/* ******************** Fixed functions ***************** */ 
int plugin_tex_getversion(void) {
	return B_PLUGIN_VERSION;
}
void plugin_but_changed(int but) {
} 

void plugin_init(void) {
	
#ifdef DEBUG
		debug = fopen("debug.txt", "w");
#endif
} 

/*
 * initialize any data for a particular instance of
 * the plugin here
 */ 
void plugin_instance_init(Cast * cast) {
} 

/* 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;
}


float calc_trellis(int type) {
	float lineval;		/* value to return if point is on a "line" of trellis */
	float areaval;		/* value to return if point is not on "line" */
	float d;		/* width of circle */
	float cx, cy;	/* centre of circle to test for */
	float tx;	/* temp x */
	float ts;	/* temp for swap */

	if (multi) {		/* multi-colour */
		areaval = 0.75;
		lineval = 0.25;
	} else {
		areaval = 1.0;
		lineval = 0.0;
	}

	switch (type) {
		case 1:		/* Square */
			if ((x < t2) || (x > (1.0 - t2)) || (y < t2) || (y > (1.0 - t2)))
				return lineval;
			else return areaval;
			break;
		case 2:		/* Rhombus */
			if ((ABS(x - y) < (t2 / SIN45)) || (ABS(1.0 - y - x) < (t2 / SIN45)))
				return lineval;
			else return areaval;
			break;
		case 3:		/* Circles in a square arrangement */
			d = (float) sqrt(((x - 0.5) * (x - 0.5)) + ((y - 0.5) * (y - 0.5)));
			if ((d < (0.5 + t2)) && (d > (0.5 - t2))) return lineval;
			else return areaval;
			break;
		case 4:		/* Double Tear Drops */
		case 5:		/* Heart shapes */
			if (type == 5) {    /* extra arc to make heart */
				tx = x;

				if (y > 0.5) {
					if (x > 0.5) x -= 0.5;

					d = (float) sqrt(((x - 0.25) * (x - 0.25)) +
						     ((y - 0.5) * (y - 0.5)));
					if ((d < (0.25 + t2)) && (d > (0.25 - t2))) {
						return lineval;
						break;
					}
				} else {
					if (x > 0.5) x -= 0.5;
					d = sqrt(((x - 0.25) * (x - 0.25)) + ((y) * (y)));
					if ((d < (0.25 + t2)) && (d > (0.25 - t2))) {
						return lineval;
						break;
					}
				}
				x = tx;
			}
			if (x < 0.25) {
				if (y > 0.5) y = 1.0 - y;
				cx = 0.25;
				cy = 0.0;
			} else if (x < 0.75) {
				if (x > 0.5) x -= 0.25;
				else x += 0.25;
				cx = 0.5;
				cy = 0.5;
			} else	{ /* x > 0.75 */
				if (y > 0.5) y = 1.0 - y;
				cx = 0.75;
				cy = 0.0;
			}
			
			/* test for point on circle */ 
			d = sqrt(((x - cx) * (x - cx)) + ((y - cy) * (y - cy)));
			if ((d < (0.25 + t2)) && (d > (0.25 - t2))) return lineval;
			else return areaval;
			break;
		case 6:		/* Octogon */
			/* Fold coordinates into bottom left corner */ 
			if (x > 0.5) x = 1.0 - x;
			if (y > 0.5) y = 1.0 - y;
			
			/* draw vertical edge */ 
			if ((x < t2) && (y > OCTEDGE)) return lineval;
			
			/* draw horizontal edge */ 
			else if ((x > OCTEDGE) && (y < t2)) return lineval;
			
			/* draw diagonal line */ 
			else if (ABS(OCTEDGE - y - x) < (t2 / SIN45)) return lineval;
			
			/* otherwise, blank */ 
			else return areaval;
			break;
		case 7:		/* Double Headed Axe */
			/* move top right quarter into lower left */ 
			if (x > 0.5 && y > 0.5) {
				x -= 0.5;
				y -= 0.5;
		 	} else if (y > 0.5) {
				/* Rotate top left quarter & move into lower left */
				ts = x;
				x = y - 0.5;
				y = ts;
			} else if (x > 0.5) {
				/* Rotate lower right quarter and move into lower left */
				ts = y;
				y = x - 0.5;
				x = ts;
			}
			
			/* now swap left and right halves of lower left quarter */ 
			/* and expand x to double width */ 
			if (x > 0.25) {
				x -= 0.25;
				x = 0.25 - ((0.25 - x) * 2);
			} else {
				x += 0.25;
				x = 0.25 + ((x - 0.25) * 2);
			}
			
			/* Finally, check for a circle */ 
			d = sqrt(((x - 0.25) * (x - 0.25)) + ((y - 0.25) * (y - 0.25)));
			if ((d < (0.25 + t2)) && (d > (0.25 - t2))) return lineval;
			else return areaval;
			break;
		case 8:		/* '+' Shapes */
			/* The '+' pattern has two-way rotational symmetry, so rotate
			   other three quarters into bottom left */ 
			if (x < 0.5 && y > 0.5)	{ /* top left */
				ts = x;
				x = 1.0 - y;
				y = ts;
			} else if (x > 0.5 && y < 0.5) { /* bottom right */
				ts = x;
				x = y;
				y = 1.0 - ts;
			} else if (x > 0.5 && y > 0.5) { /* top right */
				x = 1.0 - x;
				y = 1.0 - y;
			}
			
			/* Now check for presence on cross shape */ 
			/* Vertical lines first */ 
			if (((x < t2) || (x >= 0.2 - t2 && x < 0.2 + t2))  &&
				(y <= 0.2 + t2 || y > 0.4 - t2)) {
				return lineval;
				break;
			} else if ((x >= 0.4 - t2 && x < 0.4 + t2) && (y <= 0.4 + t2)) {
				return lineval;
				break;
			}
			
			/* Then horizontal lines */ 
			if ((y <= t2) && (x < 0.2 + t2 || x >= 0.4 - t2)) {
				return lineval;
				break;
			} else if ((y > 0.2 - t2 && y <= 0.2 + t2)
				 && (x >= 0.2 - t2)) {
				return lineval;
				break;
			} else if ((y > 0.4 - t2 && y <= 0.4 + t2) 
				 &&(x >= 0.2 - t2 && x < 0.4 + t2)) {
				return lineval;
				break;
			}
			return areaval;
			break;
		case 9:		/* 'T' Shapes */
			
			/* Horizontal lines first */ 
			if (((y > (1.0 - t)) || (y <= 0.5 && y > (0.5 - t)))  &&
				(x < 0.25 || x >= 0.5)) {
				return lineval;
				break;
			} else if (((y <= 0.75 && y > (0.75 - t)) || 
				(y <= 0.25 && y > (0.25 - t)))  &&(x < 0.75)) {
				return lineval;
				break;
			}
			
			/* If none found, check for vertical lines */ 
			if (((x < t) || (x >= 0.75 && x < (0.75 + t)))
			    && ((y <= 0.25) || (y > 0.5 && y <= 0.75))) {
				return lineval;
				break;
			}
			if (((x >= 0.25 && x < (0.25 + t))
				|| (x >= 0.5 & x < (0.5 + t)))  && 
				((y > 0.25 && y <= 0.5) || y > 0.75)) {
				return lineval;
				break;
			}
			return areaval;
			break;
		default:
			return areaval;
	}
	return areaval;
}

float calc_cube(float aval) {
	float lineval;		/* value to return if point is on a "line" of trellis */
	float areaval;		/* value to return if point is not on "line" */

	if (multi) {		/* multi-colour */
		areaval = aval;
		lineval = 0.0;
	} else {
		areaval = 1.0;
		lineval = 0.0;
	}
	
	/* It is easier to search for point NOT in a line */ 
	if (((x > t2 && x < (1.0 - t2)) && (y > t2 && y < (1.0 - t2)))
		|| ((z > t2 && z < (1.0 - t2)) && (y > t2 && y < (1.0 - t2))) 
		|| ((x > t2 && x < (1.0 - t2)) && (z > t2 && z < (1.0 - t2))))
		return areaval;
	else return lineval;
}

int plugin_tex_doit(int stype, Cast * cast, float *texvec, float *dxt,
		      float *dyt) {
	t = cast->thick;
	t2 = t / 2.0;
	multi = cast->multi;
	
	/* We only calculate the shape inside the unit square */ 
	x = texvec[0] - floor(texvec[0]);
	y = texvec[1] - floor(texvec[1]);
	z = texvec[2] - floor(texvec[2]);
	if (stype == TWO_D) {
		result[0] = calc_trellis(cast->pattern);
	} else {		/* stype == THREE_D */
		int xn, yn, zn;	/* integer parts of coordinate */

		xn = ((int) floor(texvec[0]) * 4 - 23) & 0x3FF;
		yn = ((int) floor(texvec[1]) * 7 - 9) & 0x3FF;
		zn = ((int) floor(texvec[2]) * 3 + 1) & 0x3FF;
		srand((unsigned int) (yn * xn * zn));
		result[0] = calc_cube((float) rand() / (float) RAND_MAX);
	} 
	return TEX_INT;
}

