Mercurial > hg > aim92
diff model/spiral.c @ 0:5242703e91d3 tip
Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author | tomwalters |
---|---|
date | Fri, 20 May 2011 15:19:45 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/model/spiral.c Fri May 20 15:19:45 2011 +0100 @@ -0,0 +1,460 @@ +/* + Copyright (c) Applied Psychology Unit, Medical Research Council. 1988, 1989 + =========================================================================== + + Permission to use, copy, modify, and distribute this software without fee + is hereby granted for research purposes, provided that this copyright + notice appears in all copies and in all supporting documentation, and that + the software is not redistributed for any fee (except for a nominal shipping + charge). Anyone wanting to incorporate all or part of this software in a + commercial product must obtain a license from the Medical Research Council. + + The MRC makes no representations about the suitability of this + software for any purpose. It is provided "as is" without express or implied + warranty. + + THE MRC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE + A.P.U. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* + spiral.c + ======== + + Tap SAI frames and draw each one as a spiral in the given window. +*/ + +/* + Modifications made to the routines draw_spiral, map_ribbon, map_spiral + so that the nwidth section of the SAI image is taken into account while + doing the calculations, (in image.c, doColumn()) and while plotting, + only the positive section of the time axis is drawn. + Added a new field "nwid" in the _draw_state object. + 17 March, 1995. A Jay Datta +*/ + +#include <math.h> +#include <stdio.h> + +#include "windows.h" +#include "stitch.h" +#include "source.h" +#include "draw.h" +#include "ops.h" +#include "spiral.h" + +#ifndef lint +static char *sccs_id = "@(#)spiral.c 1.9 Mike Allerhand (MRC-APU) 6/1/91" ; +#endif + +/**************************************************************************** +* Misc definitions +****************************************************************************/ +#define TWOPI 6.28318530717 +#define log2(x) (log((double)x)/log(2.0)) + +/**************************************************************************** +* Model parameters. +* Defaults: +* form_spl = 'arch' +* dotsize_spl = 2 +* axis_spl = off +* zero_spl = 4.072 +* dotthresh_spl = 50 +****************************************************************************/ +int frameheight, framewidth, nwid; /* Dimensions of each sai */ + +char form_spl = 'A' ; /* Form of spiral, 'A' or 'L' */ +int dotsize_spl = 2 ; /* Dotsize in pixels */ +int axis_spl = 0 ; /* Flag for spiral axis */ +double zero_spl = 4.072 ; /* For rotation and circuit removal */ +int dotthresh_spl = 25 ; /* Min peak height */ + +/*=================== COORDINATE SYSTEM OF THE DATA =======================*/ + +/**************************************************************************** +* Size and location, (program arguments), of calibrated box, in +* coord system of the data. +* range = range or size of both axes, (ie. "magnification" of plot). +* xorg,yorg = real plot coordinates at centre of screen. +****************************************************************************/ +static float range; +static float xorg; +static float yorg; + +/**************************************************************************** +* Limits of calibrated box in coord system of the data. +****************************************************************************/ +static float Xd0, Yd0; /* Bottom-left corner */ +static float Xd1, Yd1; /* Top-right corner */ + + +/*===================== PIXEL COORDINATE SYSTEM ==========================*/ + +/**************************************************************************** +* Dimensions of window (box plus border) in pixel coord system. +****************************************************************************/ +static int width,height; +static int border; /* border = min(width,height) / 6; */ + +/**************************************************************************** +* Limits of on-screen calibrated box in pixel coord system. +****************************************************************************/ +static int Xp0, Yp0; /* Bottom-left corner (border, height-border) */ +static int Xp1, Yp1; /* Top-right corner (width-border, border) */ + +/**************************************************************************** +* Dimensions of calibrated box in pixel coord system +****************************************************************************/ +static int Wb; /* Box width Wb = Xp1-Xp0 */ +static int Hb; /* Box height Hb = Yp0-Yp1 */ + +/**************************************************************************** +* The parameters for the calibrated box in the pixel coord system are +* initialized for a given window size (width, height, in pixels), +* as follows: +* +* border = min(width,height) / 6; +* Xp0 = border; +* Xp1 = width - border; +* Yp0 = border; +* Yp1 = height - border; +* Wb = Xp1-Xp0; +* Hb = Yp1-Yp0; +****************************************************************************/ + +/*================= COORDINATE SYSTEM TRANSFORMATIONS =====================*/ + +/**************************************************************************** +* Transformations from coord system of the data to pixel coord system. +* For example, if the origin in the data coord system is (0,0), then +* the origin in the pixel coord system is (px(0),py(0)). +****************************************************************************/ +#define px(x) ((int)(Xp0+Wb/2+(x-xorg)*(Wb/range))) +#define py(y) ((int)(Yp0+Hb/2+(y-yorg)*(Hb/range))) + +/**************************************************************************** +* Screen Position (in pixel coord system): test position is within box. +****************************************************************************/ +#define Xinbox(x) ((Xp0<=x && x<=Xp1) ? 1 : 0) +#define Yinbox(y) ((Yp0<=y && y<=Yp1) ? 1 : 0) +#define inbox(x,y) ((Xinbox(x) && Yinbox(y)) ? 1 : 0) + + + + +/**************************************************************************** +* This routine is called once per SAI frame. The SAI data is in array "frame". +* The SAI frame is formatted row-wise, starting with the lowest-frequency row. +* The state structure is: +* struct _draw_state { +* Source source; +* WindowObject window; +* int min, max; +* int framewidth, frameheight; dimensions of the SAI +* int nwid; +* long frames; +* long framenumber; +* void (*interceptor)(); function which stores window for replay +* void (*drawer)(); drawing function +* }; +* +* This struct is defined in stitch/draw.h +* It is initialized by routine "SourceDraw" in stitch/draw.c, which is called +* from gen.c. Note that this takes place after the SaiEntry routine has been +* called, so the sai dimensions set there will apply. +* +****************************************************************************/ +void draw_spiral( state, image ) +struct _draw_state *state ; +short *image ; +{ + static int first=1; /* flag for first-call of this routine */ + + /* Coordinate arrays for axis and spiral points */ + static short *Xaxis; + static short *Yaxis; + static int naxis = 0; /* number of points plotted along spiral axis */ + /* (naxis = (100 * nsamples) +1) */ + static short *Xspiral; + static short *Yspiral; + int npoints = 0; /* number of samples in pulse ribbon and spiral */ + + static short *Xdot; + static short *Ydot; + + static short *frame ; + + frameheight = state->frameheight; + framewidth= (state->framewidth)+(state->nwid); /* -nwid */ + /* fprintf(stderr, "THE NWID VALUE is %d\n", state->nwid); */ + nwid=-state->nwid; + /* fprintf(stderr, "framewidth=%d, frameheight=%d\n", frameheight, framewidth); */ + /* Initialize parameters of pixel coord system (see spiral.h) */ + width = Width( state->window ) ; + height = Height( state->window ) ; + + border = 0; + + Xp0 = border; + Xp1 = width - border; + Yp0 = border; + Yp1 = height - border; + Wb = Xp1-Xp0; + Hb = Yp1-Yp0; + + /* Initialize parameters of data coord system */ + if (form_spl == 'A') range = 2 * (TWOPI * log2((double)(framewidth+nwid)) - TWOPI * zero_spl); + if (form_spl == 'L') range = 2 * (framewidth+nwid); + xorg = 0; + yorg = 0; + + /* Allocate space (first-time call only). */ + if (first) { + first = 0; + frame = NewArray(short, frameheight*(framewidth+nwid), "frame spiral.c" ); + if (axis_spl) { + /* Allocate space and compute (X,Y) coords for spiral axis. */ + /* Resolution of points on axis is 100 times greater, so that */ + /* inner circuits look smooth. Add 1 as time starts from zero.*/ + naxis = (100*framewidth+nwid)+1; + Xaxis = NewArray(short, naxis, "Xaxis, spiral.c" ); + Yaxis = NewArray(short, naxis, "Yaxis, spiral.c" ); + naxis = gen_axis(Xaxis,Yaxis,naxis,form_spl,zero_spl); + } + if (dotsize_spl) { + /* Allocate space for coords of spiral points.*/ + Xspiral = NewArray(short, frameheight * framewidth+nwid, "Xspiral spiral.c" ); + Yspiral = NewArray(short, frameheight * framewidth+nwid, "Yspiral spiral.c" ); + Xdot = NewArray(short, frameheight * framewidth+nwid, "Xdot spiral.c" ); + Ydot = NewArray(short, frameheight * framewidth+nwid, "Ydot spiral.c" ); + } + } + + CopyArray( image, frame, (framewidth+nwid)*frameheight ) ; + + /* Map one SAI frame onto a pulse ribbon, and return the number of 1's */ + npoints = map_ribbon(frame,dotthresh_spl); + + /* Map 1's in a pulse ribbon onto points in spiral coords. */ + /* Return the number of points in the plot window. */ + npoints = map_spiral(frame,Xspiral,Yspiral,form_spl,zero_spl); + + /* Plot spiral points and axis in window. */ + if (dotsize_spl) + plotdots(state->window,Xspiral,Yspiral,Xdot,Ydot,npoints,dotsize_spl-1); + if (axis_spl) + Draw(state->window,Xaxis,Yaxis,naxis); + + return ; +} + + + +/**************************************************************************** + Map an SAI frame onto a "pulse ribbon". + +Convert a "frame" into a binary array where a `1' corresponds to a peak in +the SAI frame, and everwhere else is `0'. This binary array is called a +pulse ribbon. + +Each SAI is a "frame". A frame is a matrix of "frameheight" rows by +"framewidth" columns. Each row corresponds to a frequency channel, and +frameheight corresponds to "chans" in image.c. The columns correspond to +the number of time samples in the "image_duration", and framewidth +corresponds to framewidth (or imagewidth/chans) in image.c. + +The format of data in the input file, (short *frame), is "unmultiplexed". +This is an SAI frame in row-wise format, with the row corresponding +to the lowest-frequency channel comming first. + +Every point in the sai frame is a coordinate pair: +(f,t); f=0,1,...,frameheight-1, t=0,1,...,framewidth-1. +(The lowest frequency channel has the lowest channel number f). +(The time-origin is the rightmost point, framewidth-1). + +Peaks in the SAI frame are maxima along the time axis. Peaks are independent +of adjacent frequency channels. +A peak is defined as the highest point between zeroes, which also exceeds a +threshold. (A zero-crossing between peaks is guaranteed, since the cochleagram +signal has been half-wave rectified). + +The number of peaks found is returned. +****************************************************************************/ +map_ribbon(frame,threshold) +short *frame; +int threshold; +{ + short p, pmax; + int i, j, k, jk, npoints=0; + + /* for each SAI row (each frequency channel) in turn */ + for (i=0, k=0 ; i<frameheight ; i++, k+=framewidth+nwid) /* SSS */ + /* for each time sample, working from right to left */ + for (j=framewidth ; j>=0 ; j--) { /* SSS */ + /* while zero, advance to next peak */ + while (j>=(0-1) && frame[j+k]<=0) { /* SSS */ + frame[j+k]=0; + j--; + } + /* while greater than zero, search for max peak */ + for (pmax=0 ; j>=(0-1) && (p=frame[j+k])>0 ; j--) { /* SSS */ + frame[j+k]=0; + if (p>pmax) { + pmax=p; jk=j+k; + } + } + /* record max peak if it exceeds the threshold */ + if (pmax > threshold) { + frame[jk]=1; + npoints++; + } + } + return npoints; +} + + +/**************************************************************************** +* Map 1's in pulse ribbon onto (x,y) spiral coords, and store in the +* given arrays if the points will appear in the plot window. +* Return the number of mapped points within the plot window. +****************************************************************************/ +map_spiral(frame,Xarray,Yarray,form_spl,zero_spl) +short *frame; +short *Xarray, *Yarray; +char form_spl; +float zero_spl; +{ + float t, f, x, y; + float r0, r, theta; + int i, j, k, X, Y; + int points=0; + + /* for each SAI row (each frequency channel) in turn */ + for (i=0, k=0 ; i<frameheight ; i++, k+=framewidth+nwid) /* SSS */ + /* for each time sample, working from right to left */ + for (j=framewidth ; j>=0 ; j--) /* SSS */ + if (frame[j+k] == 1) { /* for each `1' in pulse ribbon */ + f = i; + t = (framewidth-1) - j ; /* time origin is on the right */ + + /* calculate polar coords of point, (r,theta): */ + /* radius r at outer edge of spiral cycle, (ie at min f) */ + /* radius r0, at same theta, at min f on previous cycle */ + if (t > 1) theta = TWOPI * (log2(t) - zero_spl); + else theta = 0; /* special case of 1st cycle */ + if (form_spl == 'A') { /* spiral == ARCHEMEDIAN */ + r = theta; + if (r < 0) r = 0; + if (r > 0) r0 = r - TWOPI; + else r0 = 0; /* special case of 1st cycle */ + } + if (form_spl == 'L') { /* spiral == LOGARITHMIC */ + r = t; + if (t > 1) r0 = r/2; + else r0 = 0; /* special case of 1st cycle */ + } + /* scale radius r for current frequency f (>= min f) */ + /* scale such that spiral axis occupies its own channel */ + r = r - ((f+1)/(frameheight+1)) * (r-r0); + /* convert polar to cartesian coords */ + x = r*cos(theta); + y = r*sin(theta); + /* Transform into pixel coord system and store point */ + X = px(x); + Y = py(y); + if (inbox(X,Y)) { + Xarray[points] = X; + Yarray[points++] = Y; + } + } + + return points; +} + + +/**************************************************************************** +* Generate points (X,Y coords) for spiral axis and store in given arrays. +* Return the number of points stored. +****************************************************************************/ +int gen_axis(Xarray,Yarray,naxis,form_spl,zero_spl) +short *Xarray, *Yarray; +int naxis; +char form_spl; +float zero_spl; +{ + int t; + float r, theta; + int X, Y; + float x, y; + int points=0; + + /* Calculate axis points in data coord system */ + for (t=100 ; t < naxis ; t++) { + theta = TWOPI * (log2((float)t/100) - zero_spl); + if (theta < 0) theta = 0; + if (form_spl == 'A') /* spiral == Achemedian */ + r = theta; + else /* spiral == Logarithmic */ + r = (float)t/100; + x = r*cos(theta); + y = r*sin(theta); + /* Transform into pixel coord system and store */ + X = px(x); + Y = py(y); + if (inbox(X,Y)) { + Xarray[points] = X; + Yarray[points++] = Y; + } + + } + return points; +} + + + +/**************************************************************************** +* Plot dots of size d. +* +* Plotting in a given window uses the glib routines Draw and Plot. +* These routines, and associated structures, are declared in windows.h. +* The WindowObject structure has an "entries" pointer to a WindowEntries +* structure, and this holds pointers to a set of functions which operate on +* the window. The window functions, and the routine newDisplayWindow which +* allocates a new WindowObject, are defined in X.c. +* +* void Draw(win,X,Y,points) +* Takes two arrays of shorts, one with x-coords the other with y-coords. +* The number of x's = the number of y's = "points". +* The user must ensure that the data coords are scaled into the pixel +* coord system. This is as follows: (1,1) is bottom-left corner, and +* (width,height) is top-right corner. +* void Plot(win,X,Y,points) +* Same as Draw, but plots points. +****************************************************************************/ +plotdots(win,Xspiral,Yspiral,X,Y,npoints,d) +WindowObject win; +short *Xspiral, *Yspiral; +short *X, *Y; +int npoints, d; +{ + int x, y, i; + + if (d==0) /* dotsize = 1 */ + Plot(win,Xspiral,Yspiral,npoints); + else { /* dotsize > 1 */ + for (x=(-d) ; x<=d ; x++) + for (y=(-d) ; y<=d ; y++) { + for (i=0 ; i<npoints ; i++) { + X[i] = Xspiral[i] + x; + Y[i] = Yspiral[i] + y; + } + Plot(win,X,Y,npoints); + } + } +} +