1 /*
2  * Copyright (C) 1997-2005 Kare Sjolander <kare@speech.kth.se>
3  *
4  * This file is part of the Snack Sound Toolkit.
5  * The latest version can be found at http://www.speech.kth.se/snack/
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 
22 #include "tcl.h"
23 #include "snack.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <math.h>
27 #define USE_OLD_CANVAS /* To keep Tk8.3 happy */
28 #include "tk.h"
29 #include "jkCanvItems.h"
30 #include <string.h>
31 
32 #define SNACK_DEFAULT_SECTWINTYPE      SNACK_WIN_HAMMING
33 #define SNACK_DEFAULT_SECTWINTYPE_NAME "hamming"
34 
35 #define SNACK_DEFAULT_LPC_ORDER        "20"
36 
37 /*
38  * Section item structure
39  */
40 
41 typedef struct SectionItem  {
42 
43   Tk_Item header;
44   Tk_Canvas canvas;
45   double x, y;
46   Tk_Anchor anchor;
47   int nPoints;
48   double *coords;
49   XColor *fg;
50   Pixmap fillStipple;
51   GC gc;
52   char *newSoundName;
53   char *soundName;
54   Sound *sound;
55   SnackItemInfo si;
56   float *xfft;
57   double *ffts;
58   int height;
59   int width;
60   int startSmp;
61   int endSmp;
62   int ssmp;
63   int esmp;
64   int frame;
65   int id;
66   XPoint fpts[5];
67   char *channelstr;
68   int debug;
69   double topFrequency;
70   double maxValue;
71   double minValue;
72   char *windowTypeStr;
73   char *analysisTypeStr;
74   int type;
75   int lpcOrder;
76   Tcl_Interp *interp;
77   double preemph;
78 
79 } SectionItem;
80 
81 Tk_CustomOption sectTagsOption = { (Tk_OptionParseProc *) NULL,
82 				   (Tk_OptionPrintProc *) NULL,
83 				   (ClientData) NULL };
84 
85 typedef enum {
86   OPTION_ANCHOR,
87   OPTION_TAGS,
88   OPTION_SOUND,
89   OPTION_HEIGHT,
90   OPTION_WIDTH,
91   OPTION_FFTLEN,
92   OPTION_WINLEN,
93   OPTION_PREEMP,
94   OPTION_START,
95   OPTION_END,
96   OPTION_FILL,
97   OPTION_STIPPLE,
98   OPTION_FRAME,
99   OPTION_TOPFREQUENCY,
100   OPTION_CHANNEL,
101   OPTION_MAXVAL,
102   OPTION_MINVAL,
103   OPTION_SKIP,
104   OPTION_WINTYPE,
105   OPTION_ANALYSISTYPE,
106   OPTION_LPCORDER
107 } ConfigSpec;
108 
109 static Tk_ConfigSpec configSpecs[] = {
110 
111   {TK_CONFIG_ANCHOR, "-anchor", (char *) NULL, (char *) NULL,
112    "nw", Tk_Offset(SectionItem, anchor), TK_CONFIG_DONT_SET_DEFAULT},
113 
114   {TK_CONFIG_CUSTOM, "-tags", (char *) NULL, (char *) NULL,
115    (char *) NULL, 0, TK_CONFIG_NULL_OK, &sectTagsOption},
116 
117   {TK_CONFIG_STRING, "-sound", (char *) NULL, (char *) NULL,
118    "", Tk_Offset(SectionItem, newSoundName), TK_CONFIG_NULL_OK},
119 
120   {TK_CONFIG_INT, "-height", (char *) NULL, (char *) NULL,
121    "256", Tk_Offset(SectionItem, height), 0},
122 
123   {TK_CONFIG_PIXELS, "-width", (char *) NULL, (char *) NULL,
124    "256", Tk_Offset(SectionItem, width), 0},
125 
126   {TK_CONFIG_INT, "-fftlength", (char *) NULL, (char *) NULL,
127    "512", Tk_Offset(SectionItem, si.fftlen), 0},
128 
129   {TK_CONFIG_INT, "-winlength", (char *) NULL, (char *) NULL,
130    "256", Tk_Offset(SectionItem, si.winlen), 0},
131 
132   {TK_CONFIG_DOUBLE, "-preemphasisfactor", (char *) NULL, (char *) NULL,
133    "0.0", Tk_Offset(SectionItem, preemph), 0},
134 
135   {TK_CONFIG_INT, "-start", (char *) NULL, (char *) NULL,
136    "0", Tk_Offset(SectionItem, startSmp), 0},
137 
138   {TK_CONFIG_INT, "-end", (char *) NULL, (char *) NULL,
139    "-1", Tk_Offset(SectionItem, endSmp), 0},
140 
141   {TK_CONFIG_COLOR, "-fill", (char *) NULL, (char *) NULL,
142    "black", Tk_Offset(SectionItem, fg), TK_CONFIG_NULL_OK},
143 
144   {TK_CONFIG_BITMAP, "-stipple", (char *) NULL, (char *) NULL,
145    (char *) NULL, Tk_Offset(SectionItem, fillStipple), TK_CONFIG_NULL_OK},
146 
147   {TK_CONFIG_BOOLEAN, "-frame", (char *) NULL, (char *) NULL,
148    "no", Tk_Offset(SectionItem, frame), TK_CONFIG_NULL_OK},
149 
150   {TK_CONFIG_DOUBLE, "-topfrequency", (char *) NULL, (char *) NULL,
151    "0.0", Tk_Offset(SectionItem, topFrequency), 0},
152 
153   {TK_CONFIG_STRING, "-channel", (char *) NULL, (char *) NULL,
154    "-1", Tk_Offset(SectionItem, channelstr), TK_CONFIG_NULL_OK},
155 
156   {TK_CONFIG_DOUBLE, "-maxvalue", (char *) NULL, (char *) NULL,
157    "0.0", Tk_Offset(SectionItem, maxValue), 0},
158 
159   {TK_CONFIG_DOUBLE, "-minvalue", (char *) NULL, (char *) NULL,
160    "-80.0", Tk_Offset(SectionItem, minValue), 0},
161 
162   {TK_CONFIG_INT, "-skip", (char *) NULL, (char *) NULL,
163    "-1", Tk_Offset(SectionItem, si.skip), 0},
164 
165   {TK_CONFIG_STRING, "-windowtype", (char *) NULL, (char *) NULL,
166    SNACK_DEFAULT_SECTWINTYPE_NAME, Tk_Offset(SectionItem, windowTypeStr), 0},
167 
168   {TK_CONFIG_STRING, "-analysistype", (char *) NULL, (char *) NULL,
169    "fft", Tk_Offset(SectionItem, analysisTypeStr), 0},
170 
171   {TK_CONFIG_INT, "-lpcorder", (char *) NULL, (char *) NULL,
172    SNACK_DEFAULT_LPC_ORDER, Tk_Offset(SectionItem, lpcOrder), 0},
173 
174   {TK_CONFIG_INT, "-debug", (char *) NULL, (char *) NULL,
175    "0", Tk_Offset(SectionItem, debug), 0},
176 
177   {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
178    (char *) NULL, 0, 0}
179 
180 };
181 
182 /*
183  * Protos
184  */
185 
186 static void   ComputeSectionBbox(Tk_Canvas canvas, SectionItem *sectPtr);
187 
188 static int    ComputeSectionCoords(Tk_Item *itemPtr);
189 
190 static int    ConfigureSection(Tcl_Interp *interp, Tk_Canvas canvas,
191 			       Tk_Item *itemPtr, int argc,
192 			       char **argv, int flags);
193 
194 static int    CreateSection(Tcl_Interp *interp,	Tk_Canvas canvas,
195 			    struct Tk_Item *itemPtr, int argc, char **argv);
196 
197 static void   DeleteSection(Tk_Canvas canvas, Tk_Item *itemPtr,
198 			    Display *display);
199 
200 static void   DisplaySection(Tk_Canvas canvas, Tk_Item *itemPtr,
201 			     Display *display, Drawable dst,
202 			     int x, int y, int width, int height);
203 
204 static void   ScaleSection(Tk_Canvas canvas, Tk_Item *itemPtr,
205 			   double originX, double originY,
206 			   double scaleX, double scaleY);
207 
208 static int    SectionCoords(Tcl_Interp *interp,	Tk_Canvas canvas,
209 			    Tk_Item *itemPtr, int argc, char **argv);
210 
211 static int    SectionToArea(Tk_Canvas canvas, Tk_Item *itemPtr,
212 			    double *rectPtr);
213 
214 static double SectionToPoint(Tk_Canvas canvas, Tk_Item *itemPtr,
215 			     double *coords);
216 
217 static int    SectionToPS(Tcl_Interp *interp, Tk_Canvas canvas,
218 			  Tk_Item *itemPtr, int prepass);
219 
220 static void   TranslateSection(Tk_Canvas canvas, Tk_Item *itemPtr,
221 			       double deltaX, double deltaY);
222 
223 /*
224  * Section item type
225  */
226 
227 Tk_ItemType snackSectionType = {
228   "section",
229   sizeof(SectionItem),
230   CreateSection,
231   configSpecs,
232   ConfigureSection,
233   SectionCoords,
234   DeleteSection,
235   DisplaySection,
236   0,
237   SectionToPoint,
238   SectionToArea,
239   SectionToPS,
240   ScaleSection,
241   TranslateSection,
242   (Tk_ItemIndexProc *) NULL,
243   (Tk_ItemCursorProc *) NULL,
244   (Tk_ItemSelectionProc *) NULL,
245   (Tk_ItemInsertProc *) NULL,
246   (Tk_ItemDCharsProc *) NULL,
247   (Tk_ItemType *) NULL
248 };
249 
250 static int
CreateSection(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv)251 CreateSection(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
252 	   int argc, char **argv)
253 {
254   SectionItem *sectPtr = (SectionItem *) itemPtr;
255 
256   if (argc < 2) {
257     Tcl_AppendResult(interp, "wrong # args: should be \"",
258 		     Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
259 		     itemPtr->typePtr->name, " x y ?opts?\"", (char *) NULL);
260     return TCL_ERROR;
261   }
262 
263   sectPtr->canvas = canvas;
264   sectPtr->anchor = TK_ANCHOR_NW;
265   sectPtr->nPoints = 0;
266   sectPtr->coords = NULL;
267   sectPtr->fg = None;
268   sectPtr->fillStipple = None;
269   sectPtr->gc = None;
270   sectPtr->newSoundName = NULL;
271   sectPtr->soundName = NULL;
272   sectPtr->sound = NULL;
273   sectPtr->si.samprate = 16000;
274   sectPtr->si.BufPos = 0;
275   sectPtr->si.fftlen = 512;
276   sectPtr->si.winlen = 256;
277   sectPtr->preemph = 0.0;
278   sectPtr->si.hamwin = (float *) ckalloc(NMAX * sizeof(float));
279   sectPtr->si.abmax = 0.0f;
280   sectPtr->xfft = (float *)  ckalloc(NMAX * sizeof(float));
281   sectPtr->ffts = (double *) ckalloc(NMAX / 2 * sizeof(double));
282   sectPtr->height = 256;
283   sectPtr->width = 256;
284   sectPtr->startSmp = 0;
285   sectPtr->endSmp = -1;
286   sectPtr->ssmp = 0;
287   sectPtr->esmp = -1;
288   sectPtr->id = 0;
289   sectPtr->frame = 0;
290   sectPtr->debug = 0;
291   sectPtr->x = 0;
292   sectPtr->y = 0;
293   sectPtr->topFrequency = 0.0;
294   sectPtr->channelstr = NULL;
295   sectPtr->si.channel = -1;
296   sectPtr->si.channelSet = -1;
297   sectPtr->si.nchannels = 1;
298   sectPtr->maxValue = 0.0;
299   sectPtr->minValue = -80.0;
300   sectPtr->si.validStart = 0;
301   sectPtr->si.skip = -1;
302   sectPtr->si.windowType = SNACK_DEFAULT_SECTWINTYPE;
303   sectPtr->si.windowTypeSet = SNACK_DEFAULT_SECTWINTYPE;
304   sectPtr->windowTypeStr = NULL;
305   sectPtr->analysisTypeStr = NULL;
306   sectPtr->type = 0;
307   sectPtr->lpcOrder = atoi(SNACK_DEFAULT_LPC_ORDER);
308   sectPtr->interp = interp;
309 
310   if (sectPtr->si.hamwin == NULL) {
311     Tcl_AppendResult(interp, "Couldn't allocate analysis window buffer!",NULL);
312     return TCL_ERROR;
313   }
314 
315   if (sectPtr->xfft == NULL) {
316     Tcl_AppendResult(interp, "Couldn't allocate fft buffer!", NULL);
317     ckfree((char *)sectPtr->si.hamwin);
318     return TCL_ERROR;
319   }
320 
321   if (sectPtr->ffts == NULL) {
322     Tcl_AppendResult(interp, "Couldn't allocate fft buffer!", NULL);
323     ckfree((char *)sectPtr->si.hamwin);
324     ckfree((char *)sectPtr->xfft);
325     return TCL_ERROR;
326   }
327 
328 
329   if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &sectPtr->x) != TCL_OK) ||
330       (Tk_CanvasGetCoord(interp, canvas, argv[1], &sectPtr->y) != TCL_OK))
331     return TCL_ERROR;
332 
333   if (ConfigureSection(interp, canvas, itemPtr, argc-2, argv+2, 0) == TCL_OK)
334     return TCL_OK;
335 
336   DeleteSection(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
337   return TCL_ERROR;
338 }
339 
340 static int
SectionCoords(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv)341 SectionCoords(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
342 	      int argc, char **argv)
343 {
344   SectionItem *wPtr = (SectionItem *) itemPtr;
345   char xc[TCL_DOUBLE_SPACE], yc[TCL_DOUBLE_SPACE];
346 
347   if (argc == 0) {
348     Tcl_PrintDouble(interp, wPtr->x, xc);
349     Tcl_PrintDouble(interp, wPtr->y, yc);
350     Tcl_AppendResult(interp, xc, " ", yc, (char *) NULL);
351   } else if (argc == 2) {
352     if ((Tk_CanvasGetCoord(interp, canvas, argv[0], &wPtr->x) != TCL_OK) ||
353 	(Tk_CanvasGetCoord(interp, canvas, argv[1], &wPtr->y) != TCL_OK)) {
354       return TCL_ERROR;
355     }
356     ComputeSectionBbox(canvas, wPtr);
357   } else {
358     char buf[80];
359 
360     sprintf(buf, "wrong # coordinates: expected 0 or 2, got %d", argc);
361     Tcl_SetResult(interp, buf, TCL_VOLATILE);
362 
363     return TCL_ERROR;
364   }
365   return TCL_OK;
366 }
367 
368 static int
ComputeSectionCoords(Tk_Item * itemPtr)369 ComputeSectionCoords(Tk_Item *itemPtr)
370 {
371   SectionItem *sectPtr = (SectionItem *) itemPtr;
372   int i;
373   int nPoints = sectPtr->nPoints;
374   float xscale = (float) (sectPtr->width) / nPoints;
375   float yscale = (float) ((float) (sectPtr->height - 1) /
376     (sectPtr->minValue - sectPtr->maxValue));
377   float fscale = (float) (sectPtr->si.topfrequency / (sectPtr->si.samprate / 2.0));
378 
379   if (sectPtr->debug > 1) {
380     Snack_WriteLogInt("  Enter ComputeSectionCoords", nPoints);
381   }
382 
383   if (sectPtr->coords != NULL) ckfree((char *) sectPtr->coords);
384   sectPtr->coords = (double *) ckalloc((unsigned)
385 			       (sizeof(double) * (2 * nPoints)));
386 
387   for (i = 0; i < nPoints; i++) {
388     double t = (double) (sectPtr->ffts[(int)((float)i*fscale)] -
389 			 sectPtr->maxValue) * yscale;
390     if (t > sectPtr->height-1) t = (double) (sectPtr->height-1);
391     if (t < 0.0) t = 0.0;
392     sectPtr->coords[i*2]   = (double) i * xscale;
393     sectPtr->coords[i*2+1] = t;
394   }
395 
396   ComputeSectionBbox(sectPtr->canvas, sectPtr);
397 
398   if (sectPtr->debug) Snack_WriteLog("Exit ComputeSectionCoords\n");
399 
400   return TCL_OK;
401 }
402 
403 void
GetFloatMonoSigSect(SnackItemInfo * siPtr,SnackLinkedFileInfo * info,float * sig,int beg,int len)404 GetFloatMonoSigSect(SnackItemInfo *siPtr,SnackLinkedFileInfo *info,
405 		    float *sig,int beg, int len) {
406   /* sig buffer must be allocated, file must be open! */
407 
408   int i;
409 
410   if (siPtr->storeType == SOUND_IN_MEMORY) {
411     if (siPtr->nchannels == 1 || siPtr->channel != -1) {
412       int p = beg * siPtr->nchannels + siPtr->channel;
413 
414       for (i = 0; i < len; i++) {
415 	sig[i] = (float) (FSAMPLE(siPtr, p));
416 	p += siPtr->nchannels;
417       }
418     } else {
419       int c;
420 
421       for (i = 0; i < len; i++) {
422 	sig[i] = 0.0;
423       }
424       for (c = 0; c < siPtr->nchannels; c++) {
425 	int p = beg * siPtr->nchannels + c;
426 
427 	for (i = 0; i < len; i++) {
428 	  sig[i] += (float) (FSAMPLE(siPtr, p));
429 	  p += siPtr->nchannels;
430 	}
431       }
432       for (i = 0; i < len; i++) {
433 	sig[i] /= siPtr->nchannels;
434       }
435     }
436   } else { /* storeType != SOUND_IN_MEMORY */
437     if (siPtr->nchannels == 1 || siPtr->channel != -1) {
438       int p = beg * siPtr->nchannels + siPtr->channel;
439 
440       for (i = 0; i < len; i++) {
441 	sig[i] = (float) (GetSample(info, p));
442 	p += siPtr->nchannels;
443       }
444     } else {
445       int c;
446 
447       for (i = 0; i < len; i++) {
448 	sig[i] = 0.0;
449       }
450       for (c = 0; c < siPtr->nchannels; c++) {
451 	int p = beg * siPtr->nchannels + c;
452 
453 	for (i = 0; i < len; i++) {
454 	  sig[i] += (float) (GetSample(info, p));
455 	  p += siPtr->nchannels;
456 	}
457       }
458       for (i = 0; i < len; i++) {
459 	sig[i] /= siPtr->nchannels;
460       }
461     }
462   }
463 }
464 
465 extern void Snack_PowerSpectrum(float *z);
466 
467 void
ComputeSection(Tk_Item * itemPtr)468 ComputeSection(Tk_Item *itemPtr)
469 {
470   SectionItem *sectPtr = (SectionItem *) itemPtr;
471   SnackItemInfo *siPtr = &sectPtr->si;
472   int i, j;
473   int fftlen     = siPtr->fftlen;
474   int winlen     = siPtr->winlen;
475   float preemph  = siPtr->preemph;
476   int RestartPos = siPtr->RestartPos - siPtr->validStart;
477   int storeType  = siPtr->storeType;
478   int n, skip = siPtr->skip;
479   SnackLinkedFileInfo info;
480   float *sig_lpc;
481   float presample = 0.0;
482   int siglen;
483   float g_lpc;
484 
485   if (sectPtr->debug) Snack_WriteLogInt("Enter ComputeSection", sectPtr->ssmp);
486 
487   if (skip < 1) {
488     skip = fftlen;
489   }
490   siglen = sectPtr->esmp - siPtr->RestartPos;
491   n = siglen / skip;
492 
493   for (i = 0; i < fftlen/2; i++) {
494     sectPtr->ffts[i] = 0.0;
495   }
496 
497   if (n == 0) return;
498 
499   Snack_InitFFT(fftlen);
500   Snack_InitWindow(siPtr->hamwin, winlen, fftlen, siPtr->windowType);
501 
502   if (storeType != SOUND_IN_MEMORY) {
503     if (OpenLinkedFile(sectPtr->sound, &info) != TCL_OK) {
504       return;
505     }
506   }
507 
508   if (sectPtr->type != 0 && n > 0) { /* LPC + FFT */
509 
510     sig_lpc = (float *) ckalloc(siglen * sizeof(float));
511 
512     GetFloatMonoSigSect(siPtr,&info,sig_lpc,RestartPos,siglen);
513     if (RestartPos > 0)
514 	GetFloatMonoSigSect(siPtr,&info,&presample,RestartPos-1,1);
515     PreEmphase(sig_lpc,presample,siglen,preemph);
516 
517     /* windowing signal to make lpc look more like the fft spectrum ??? */
518     for (i = 0; i < winlen/2; i++) {
519       sig_lpc[i] = sig_lpc[i] * siPtr->hamwin[i];
520     }
521     for (i = winlen/2; i < winlen; i++) {
522       sig_lpc[i+siglen-winlen] = sig_lpc[i+siglen-winlen] * siPtr->hamwin[i];
523     }
524 
525     g_lpc = LpcAnalysis(sig_lpc,siglen,sectPtr->xfft,sectPtr->lpcOrder);
526     ckfree((char *)sig_lpc);
527 
528     for (i=0; i<=sectPtr->lpcOrder; i++) {
529       /* the factor is a guess, try looking for analytical value */
530       sectPtr->xfft[i] = sectPtr->xfft[i] * 5000000000.0f;
531     }
532     for (i = sectPtr->lpcOrder + 1; i < fftlen; i++) {
533       sectPtr->xfft[i] = 0.0;
534     }
535 
536     Snack_DBPowerSpectrum(sectPtr->xfft);
537 
538     for (i = 0; i < fftlen/2; i++) {
539       sectPtr->ffts[i] = -sectPtr->xfft[i];
540     }
541   } else {  /* usual FFT */
542 
543     for (j = 0; j < n; j++) {
544       if (storeType == SOUND_IN_MEMORY) {
545 	if (siPtr->nchannels == 1 || siPtr->channel != -1) {
546 	  int p = (RestartPos + j * skip) * siPtr->nchannels + siPtr->channel;
547 
548 	  for (i = 0; i < fftlen; i++) {
549 	    sectPtr->xfft[i] = (float) ((FSAMPLE(siPtr, p + siPtr->nchannels)
550 					 - preemph * FSAMPLE(siPtr, p))
551 					* siPtr->hamwin[i]);
552 	    p += siPtr->nchannels;
553 	  }
554 	} else {
555 	  int c;
556 
557 	  for (i = 0; i < fftlen; i++) {
558 	    sectPtr->xfft[i] = 0.0;
559 	  }
560 	  for (c = 0; c < siPtr->nchannels; c++) {
561 	    int p = (RestartPos + j * skip) * siPtr->nchannels + c;
562 
563 	    for (i = 0; i < fftlen; i++) {
564 	      sectPtr->xfft[i] += (float)((FSAMPLE(siPtr, p + siPtr->nchannels)
565 					   - preemph * FSAMPLE(siPtr, p))
566 					  * siPtr->hamwin[i]);
567 	      p += siPtr->nchannels;
568 	    }
569 	  }
570 	  for (i = 0; i < fftlen; i++) {
571 	    sectPtr->xfft[i] /= siPtr->nchannels;
572 	  }
573 	}
574       } else { /* storeType != SOUND_IN_MEMORY */
575 	if (siPtr->nchannels == 1 || siPtr->channel != -1) {
576 	  int p = (RestartPos + j * skip) * siPtr->nchannels + siPtr->channel;
577 
578 	  for (i = 0; i < fftlen; i++) {
579 	    sectPtr->xfft[i] = (float) ((GetSample(&info, p + siPtr->nchannels)
580 					 - preemph * GetSample(&info, p))
581 					* siPtr->hamwin[i]);
582 	    p += siPtr->nchannels;
583 	  }
584 	} else {
585 	  int c;
586 
587 	  for (i = 0; i < fftlen; i++) {
588 	    sectPtr->xfft[i] = 0.0;
589 	  }
590 	  for (c = 0; c < siPtr->nchannels; c++) {
591 	    int p = (RestartPos + j * skip) * siPtr->nchannels + c;
592 
593 	    for (i = 0; i < fftlen; i++) {
594 	      sectPtr->xfft[i] += (float)((GetSample(&info, p+siPtr->nchannels)
595 					   - preemph * GetSample(&info, p))
596 					  * siPtr->hamwin[i]);
597 	      p += siPtr->nchannels;
598 	    }
599 	  }
600 	  for (i = 0; i < fftlen; i++) {
601 	    sectPtr->xfft[i] /= siPtr->nchannels;
602 	  }
603 	}
604       }
605 
606       Snack_PowerSpectrum(sectPtr->xfft);
607 
608       for (i = 0; i < fftlen/2; i++) {
609 	sectPtr->ffts[i] += sectPtr->xfft[i];
610       }
611     }
612 
613     for (i = 0; i < fftlen/2; i++) {
614       sectPtr->ffts[i] = sectPtr->ffts[i] / (float) n;
615     }
616 
617     for (i = 1; i < fftlen/2; i++) {
618       if (sectPtr->ffts[i] < SNACK_INTLOGARGMIN)
619 	sectPtr->ffts[i] = SNACK_INTLOGARGMIN;
620       sectPtr->ffts[i] = (float)(SNACK_DB*log(sectPtr->ffts[i]) - SNACK_CORRN);
621     }
622     if (sectPtr->ffts[0] < SNACK_INTLOGARGMIN)
623       sectPtr->ffts[0] = SNACK_INTLOGARGMIN;
624     sectPtr->ffts[0] = (float)(SNACK_DB*log(sectPtr->ffts[0]) - SNACK_CORR0);
625   }
626   if (storeType != SOUND_IN_MEMORY) {
627     CloseLinkedFile(&info);
628   }
629 
630   if (sectPtr->debug) Snack_WriteLog("Exit ComputeSection");
631 }
632 
633 static void
UpdateSection(ClientData clientData,int flag)634 UpdateSection(ClientData clientData, int flag)
635 {
636   SectionItem *sectPtr = (SectionItem *) clientData;
637   Sound *s = sectPtr->sound;
638 
639   if (sectPtr->debug) Snack_WriteLogInt("Enter UpdateSection", flag);
640 
641   if (sectPtr->canvas == NULL) return;
642 
643   if (flag == SNACK_DESTROY_SOUND) {
644     sectPtr->sound = NULL;
645     if (sectPtr->id) Snack_RemoveCallback(s, sectPtr->id);
646     sectPtr->id = 0;
647     return;
648   }
649 
650   Tk_CanvasEventuallyRedraw(sectPtr->canvas,
651 			    sectPtr->header.x1, sectPtr->header.y1,
652 			    sectPtr->header.x2, sectPtr->header.y2);
653 
654   sectPtr->si.blocks = s->blocks;
655   sectPtr->si.BufPos = s->length;
656   sectPtr->si.storeType = s->storeType;
657   sectPtr->si.samprate = s->samprate;
658   sectPtr->si.encoding = s->encoding;
659   sectPtr->si.nchannels = s->nchannels;
660 
661   if (flag == SNACK_MORE_SOUND) {
662     sectPtr->esmp = sectPtr->si.BufPos - 1;
663     sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
664 
665     if (sectPtr->ssmp < 0) {
666       sectPtr->ssmp = 0;
667     }
668 
669     sectPtr->si.RestartPos = sectPtr->ssmp;
670   }
671 
672   if (flag == SNACK_NEW_SOUND) {
673     sectPtr->esmp = sectPtr->endSmp;
674 
675     if (sectPtr->endSmp < 0)
676       sectPtr->esmp = sectPtr->si.BufPos - 1;
677 
678     if (sectPtr->endSmp > sectPtr->si.BufPos - 1)
679       sectPtr->esmp = sectPtr->si.BufPos - 1;
680 
681     if (sectPtr->startSmp > sectPtr->endSmp && sectPtr->endSmp >= 0)
682       sectPtr->startSmp = sectPtr->endSmp;
683 
684     if (sectPtr->startSmp < 0)
685       sectPtr->startSmp = 0;
686 
687     sectPtr->ssmp = sectPtr->startSmp;
688 
689     if (sectPtr->ssmp > sectPtr->esmp)
690       sectPtr->ssmp = sectPtr->esmp;
691 
692     if (sectPtr->ssmp > sectPtr->esmp - sectPtr->si.fftlen) {
693       sectPtr->esmp = sectPtr->ssmp + sectPtr->si.fftlen;
694       if (sectPtr->esmp > sectPtr->si.BufPos - 1) {
695 	sectPtr->esmp = sectPtr->si.BufPos - 1;
696 	sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
697 	if (sectPtr->ssmp < 0) {
698 	  sectPtr->ssmp = 0;
699 	}
700       }
701     }
702 
703     if (sectPtr->topFrequency <= 0.0) {
704       sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
705     } else if (sectPtr->topFrequency > sectPtr->si.samprate / 2.0) {
706       sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
707     } else {
708       sectPtr->si.topfrequency = sectPtr->topFrequency;
709     }
710   }
711   sectPtr->si.channel = sectPtr->si.channelSet;
712   if (sectPtr->si.nchannels == 1) {
713     sectPtr->si.channel = 0;
714   }
715 
716   sectPtr->si.validStart = s->validStart;
717 
718   ComputeSection((Tk_Item *)sectPtr);
719 
720   if (ComputeSectionCoords((Tk_Item *)sectPtr) != TCL_OK) {
721     return;
722   }
723 
724   Tk_CanvasEventuallyRedraw(sectPtr->canvas,
725 			    sectPtr->header.x1, sectPtr->header.y1,
726 			    sectPtr->header.x2, sectPtr->header.y2);
727 
728   if (sectPtr->debug) Snack_WriteLog("Exit UpdateSection\n");
729 }
730 
731 static int
ConfigureSection(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int argc,char ** argv,int flags)732 ConfigureSection(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr,
733 	      int argc, char **argv, int flags)
734 {
735   SectionItem *sectPtr = (SectionItem *) itemPtr;
736   Sound *s = sectPtr->sound;
737   Tk_Window tkwin = Tk_CanvasTkwin(canvas);
738   XGCValues gcValues;
739   GC newGC;
740   unsigned long mask;
741   int doCompute = 0;
742   int i, j;
743 
744   if (argc == 0) return TCL_OK;
745 
746   if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc,
747 			 (CONST84 char **)argv,
748 			 (char *) sectPtr, flags) != TCL_OK) return TCL_ERROR;
749 
750   if (sectPtr->debug) Snack_WriteLog("Enter ConfigureSection\n");
751 
752   for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
753     for (j = 0; j < argc; j += 2) {
754       if (strncmp(argv[j], configSpecs[i].argvName, strlen(argv[j])) == 0) {
755 	configSpecs[i].specFlags |= TK_CONFIG_OPTION_SPECIFIED;
756 	break;
757       }
758     }
759   }
760 
761 #if defined(MAC) || defined(MAC_OSX_TCL)
762   for (i = 0; i < argc; i++) {
763     if (strncmp(argv[i], "-anchor", strlen(argv[i])) == 0) {
764       i++;
765       if (strcmp(argv[i], "ne") == 0) {
766 	sectPtr->anchor = 1;
767       } else if (strcmp(argv[i], "nw") == 0) {
768 	sectPtr->anchor = 7;
769       } else if (strcmp(argv[i], "n") == 0) {
770 	sectPtr->anchor = 0;
771       } else if (strcmp(argv[i], "e") == 0) {
772 	sectPtr->anchor = 2;
773       } else if (strcmp(argv[i], "se") == 0) {
774 	sectPtr->anchor = 3;
775       } else if (strcmp(argv[i], "sw") == 0) {
776 	sectPtr->anchor = 5;
777       } else if (strcmp(argv[i], "s") == 0) {
778 	sectPtr->anchor = 4;
779       } else if (strcmp(argv[i], "w") == 0) {
780 	sectPtr->anchor = 6;
781       } else if (strncmp(argv[i], "center", strlen(argv[i])) == 0) {
782 	sectPtr->anchor = 8;
783       }
784       break;
785     }
786   }
787 #endif
788 
789   if (CheckFFTlen(interp, sectPtr->si.fftlen) != TCL_OK) return TCL_ERROR;
790 
791   if (CheckWinlen(interp, sectPtr->si.winlen, sectPtr->si.fftlen) != TCL_OK)
792     return TCL_ERROR;
793 
794   if (CheckLPCorder(interp, sectPtr->lpcOrder) != TCL_OK) return TCL_ERROR;
795 
796   if (OptSpecified(OPTION_SOUND)) {
797     if (sectPtr->newSoundName == NULL) {
798       sectPtr->sound = NULL;
799       if (sectPtr->id) Snack_RemoveCallback(s, sectPtr->id);
800       sectPtr->id = 0;
801       sectPtr->si.BufPos = 0;
802       doCompute = 1;
803     } else {
804       if ((s = Snack_GetSound(interp, sectPtr->newSoundName)) == NULL) {
805 	return TCL_ERROR;
806       }
807       if (s->storeType == SOUND_IN_CHANNEL) {
808 	Tcl_AppendResult(interp, sectPtr->newSoundName,
809 			 " can not be linked to a channel", (char *) NULL);
810 	return TCL_ERROR;
811       }
812       if (s->storeType == SOUND_IN_FILE) {
813 	s->itemRefCnt++;
814       }
815       sectPtr->sound = s;
816       if (sectPtr->soundName == NULL) {
817 	sectPtr->soundName = ckalloc(strlen(sectPtr->newSoundName)+1);
818 	strcpy(sectPtr->soundName, sectPtr->newSoundName);
819       }
820       if (strcmp(sectPtr->soundName, sectPtr->newSoundName) != 0) {
821 	Sound *t = Snack_GetSound(interp, sectPtr->soundName);
822 	ckfree(sectPtr->soundName);
823 	sectPtr->soundName = ckalloc(strlen(sectPtr->newSoundName)+1);
824 	strcpy(sectPtr->soundName, sectPtr->newSoundName);
825 	sectPtr->nPoints = 0;
826 	sectPtr->ssmp    = 0;
827 	sectPtr->esmp    = -1;
828 	Snack_RemoveCallback(t, sectPtr->id);
829 	sectPtr->id = 0;
830       }
831       if (!sectPtr->id)
832 	sectPtr->id = Snack_AddCallback(s, UpdateSection, (int *)sectPtr);
833 
834       sectPtr->si.blocks = s->blocks;
835       sectPtr->si.BufPos = s->length;
836       sectPtr->si.samprate = s->samprate;
837       sectPtr->si.encoding = s->encoding;
838       sectPtr->si.nchannels = s->nchannels;
839       sectPtr->si.storeType = s->storeType;
840       doCompute = 1;
841     }
842   }
843   sectPtr->esmp = sectPtr->endSmp;
844 
845   if (sectPtr->endSmp < 0)
846     sectPtr->esmp = sectPtr->si.BufPos - 1;
847 
848   if (sectPtr->endSmp > sectPtr->si.BufPos - 1)
849     sectPtr->esmp = sectPtr->si.BufPos - 1;
850 
851   if (sectPtr->startSmp > sectPtr->endSmp && sectPtr->endSmp >= 0)
852     sectPtr->startSmp = sectPtr->endSmp;
853 
854   if (sectPtr->startSmp < 0)
855     sectPtr->startSmp = 0;
856 
857   sectPtr->ssmp = sectPtr->startSmp;
858 
859   if (sectPtr->ssmp > sectPtr->esmp)
860     sectPtr->ssmp = sectPtr->esmp;
861 
862   if (sectPtr->ssmp > sectPtr->esmp - sectPtr->si.fftlen) {
863     sectPtr->esmp = sectPtr->ssmp + sectPtr->si.fftlen;
864     if (sectPtr->esmp > sectPtr->si.BufPos - 1) {
865       sectPtr->esmp = sectPtr->si.BufPos - 1;
866       sectPtr->ssmp = sectPtr->esmp - sectPtr->si.fftlen;
867       if (sectPtr->ssmp < 0) {
868 	sectPtr->ssmp = 0;
869       }
870     }
871   }
872 
873   if (OptSpecified(OPTION_WINLEN))
874     doCompute = 1;
875 
876   if (OptSpecified(OPTION_FFTLEN)) {
877     doCompute = 1;
878   }
879 
880   sectPtr->si.preemph = (float) sectPtr->preemph;
881 
882   if (OptSpecified(OPTION_SKIP)) {
883     doCompute = 1;
884   }
885 
886   if (OptSpecified(OPTION_START)) {
887     doCompute = 1;
888   }
889 
890   if (OptSpecified(OPTION_END)) {
891     doCompute = 1;
892   }
893 
894   if (sectPtr->topFrequency <= 0.0) {
895     sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
896   } else if (sectPtr->topFrequency > sectPtr->si.samprate / 2.0) {
897     sectPtr->si.topfrequency = sectPtr->si.samprate / 2.0;
898   } else {
899     sectPtr->si.topfrequency = sectPtr->topFrequency;
900   }
901 
902   if (OptSpecified(OPTION_CHANNEL)) {
903     if (GetChannel(interp, sectPtr->channelstr, sectPtr->si.nchannels,
904 		   &sectPtr->si.channelSet) != TCL_OK) {
905       return TCL_ERROR;
906     }
907     doCompute = 1;
908   }
909   sectPtr->si.channel = sectPtr->si.channelSet;
910   if (sectPtr->si.nchannels == 1) {
911     sectPtr->si.channel = 0;
912   }
913 
914   if (OptSpecified(OPTION_ANALYSISTYPE)) {
915     int len = strlen(sectPtr->analysisTypeStr);
916 
917     if (strncasecmp(sectPtr->analysisTypeStr, "lpc", len) == 0) {
918       sectPtr->type = 1;
919     } else if (strncasecmp(sectPtr->analysisTypeStr, "fft", len) == 0) {
920       sectPtr->type = 0;
921     } else {
922       Tcl_AppendResult(interp, "-type should be FFT or LPC", (char *) NULL);
923       return TCL_ERROR;
924     }
925     doCompute = 1;
926   }
927   if (OptSpecified(OPTION_LPCORDER)) {
928     doCompute = 1;
929   }
930   if (OptSpecified(OPTION_WINTYPE)) {
931     if (GetWindowType(interp, sectPtr->windowTypeStr,
932 		      &sectPtr->si.windowTypeSet)
933 	!= TCL_OK) {
934       return TCL_ERROR;
935     }
936     doCompute = 1;
937   }
938   sectPtr->si.windowType = sectPtr->si.windowTypeSet;
939 
940   if (doCompute) {
941     sectPtr->nPoints = sectPtr->si.fftlen / 2;
942     sectPtr->si.RestartPos = sectPtr->ssmp;
943     ComputeSection((Tk_Item *)sectPtr);
944   }
945 
946   if (sectPtr->height <= 2) sectPtr->height = 0;
947 
948   if (sectPtr->fg == NULL) {
949     newGC = None;
950   } else {
951     gcValues.foreground = sectPtr->fg->pixel;
952     gcValues.line_width = 1;
953     mask = GCForeground|GCLineWidth;
954     if (sectPtr->fillStipple != None) {
955       gcValues.stipple = sectPtr->fillStipple;
956       gcValues.fill_style = FillStippled;
957       mask |= GCStipple|GCFillStyle;
958     }
959     newGC = Tk_GetGC(tkwin, mask, &gcValues);
960     gcValues.line_width = 0;
961   }
962   if (sectPtr->gc != None) {
963     Tk_FreeGC(Tk_Display(tkwin), sectPtr->gc);
964   }
965   sectPtr->gc = newGC;
966 
967   ComputeSectionBbox(canvas, sectPtr);
968 
969   if (ComputeSectionCoords(itemPtr) != TCL_OK) {
970     return TCL_ERROR;
971   }
972 
973   for (i = 0; configSpecs[i].type != TK_CONFIG_END; i++) {
974     configSpecs[i].specFlags &= ~TK_CONFIG_OPTION_SPECIFIED;
975   }
976 
977   if (sectPtr->debug) Snack_WriteLog("Exit ConfigureSection\n");
978 
979   return TCL_OK;
980 }
981 
982 static void
DeleteSection(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display)983 DeleteSection(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display)
984 {
985   SectionItem *sectPtr = (SectionItem *) itemPtr;
986 
987   if ((sectPtr->id) &&
988       (Snack_GetSound(sectPtr->interp, sectPtr->soundName) != NULL)) {
989     Snack_RemoveCallback(sectPtr->sound, sectPtr->id);
990   }
991 
992   if (sectPtr->soundName != NULL) ckfree(sectPtr->soundName);
993 
994   if (sectPtr->coords != NULL) ckfree((char *) sectPtr->coords);
995 
996   if (sectPtr->si.hamwin != NULL) ckfree((char *)sectPtr->si.hamwin);
997 
998   if (sectPtr->xfft != NULL) ckfree((char *)sectPtr->xfft);
999 
1000   if (sectPtr->ffts != NULL) ckfree((char *)sectPtr->ffts);
1001 
1002   if (sectPtr->fg != NULL) Tk_FreeColor(sectPtr->fg);
1003 
1004   if (sectPtr->fillStipple != None) Tk_FreeBitmap(display, sectPtr->fillStipple);
1005 
1006   if (sectPtr->gc != None) Tk_FreeGC(display, sectPtr->gc);
1007 
1008   if (sectPtr->sound != NULL) {
1009     if (sectPtr->sound->storeType == SOUND_IN_FILE) {
1010       sectPtr->sound->itemRefCnt--;
1011     }
1012   }
1013 }
1014 
1015 static void
ComputeSectionBbox(Tk_Canvas canvas,SectionItem * sectPtr)1016 ComputeSectionBbox(Tk_Canvas canvas, SectionItem *sectPtr)
1017 {
1018   int width = sectPtr->width;
1019   int height = sectPtr->height;
1020   int x = (int) (sectPtr->x + ((sectPtr->x >= 0) ? 0.5 : - 0.5));
1021   int y = (int) (sectPtr->y + ((sectPtr->y >= 0) ? 0.5 : - 0.5));
1022 
1023   switch (sectPtr->anchor) {
1024   case TK_ANCHOR_N:
1025     x -= width/2;
1026     break;
1027   case TK_ANCHOR_NE:
1028     x -= width;
1029     break;
1030   case TK_ANCHOR_E:
1031     x -= width;
1032     y -= height/2;
1033     break;
1034   case TK_ANCHOR_SE:
1035     x -= width;
1036     y -= height;
1037     break;
1038   case TK_ANCHOR_S:
1039     x -= width/2;
1040     y -= height;
1041     break;
1042   case TK_ANCHOR_SW:
1043     y -= height;
1044     break;
1045   case TK_ANCHOR_W:
1046     y -= height/2;
1047     break;
1048   case TK_ANCHOR_NW:
1049     break;
1050   case TK_ANCHOR_CENTER:
1051     x -= width/2;
1052     y -= height/2;
1053     break;
1054   }
1055 
1056   sectPtr->header.x1 = x;
1057   sectPtr->header.y1 = y;
1058   sectPtr->header.x2 = x + width;
1059   sectPtr->header.y2 = y + height;
1060 }
1061 
1062 static void
DisplaySection(Tk_Canvas canvas,Tk_Item * itemPtr,Display * display,Drawable drawable,int x,int y,int width,int height)1063 DisplaySection(Tk_Canvas canvas, Tk_Item *itemPtr, Display *display,
1064 	    Drawable drawable, int x, int y, int width, int height)
1065 {
1066   SectionItem *sectPtr = (SectionItem *) itemPtr;
1067   double *coords = sectPtr->coords;
1068   int i, nPoints = sectPtr->nPoints;
1069   XPoint *wpts = (XPoint *) ckalloc((unsigned)(nPoints * sizeof(XPoint)));
1070   XPoint *p = wpts;
1071   int xo = sectPtr->header.x1;
1072   int yo = sectPtr->header.y1;
1073 
1074   if (sectPtr->debug) Snack_WriteLogInt("Enter DisplaySection", nPoints);
1075 
1076   if (sectPtr->gc == None) return;
1077 
1078   if (sectPtr->fillStipple != None)
1079     Tk_CanvasSetStippleOrigin(canvas, sectPtr->gc);
1080 
1081   for (i = 0; i < sectPtr->nPoints; i++) {
1082     Tk_CanvasDrawableCoords(canvas, xo + coords[0], yo + coords[1],
1083 			    &p->x, &p->y);
1084     coords += 2;
1085     p++;
1086   }
1087 
1088   XDrawLines(display, drawable, sectPtr->gc, wpts, nPoints,
1089 	     CoordModeOrigin);
1090 
1091   if (sectPtr->frame) {
1092     Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1093 			    &sectPtr->fpts[0].x, &sectPtr->fpts[0].y);
1094     Tk_CanvasDrawableCoords(canvas, (double) (xo + sectPtr->width - 1),
1095 			    (double) yo,
1096 			    &sectPtr->fpts[1].x, &sectPtr->fpts[1].y);
1097     Tk_CanvasDrawableCoords(canvas, (double) (xo + sectPtr->width - 1),
1098 			    (double) (yo + sectPtr->height - 1),
1099 			    &sectPtr->fpts[2].x, &sectPtr->fpts[2].y);
1100     Tk_CanvasDrawableCoords(canvas, (double) xo,
1101 			    (double) (yo + sectPtr->height - 1),
1102 			    &sectPtr->fpts[3].x, &sectPtr->fpts[3].y);
1103     Tk_CanvasDrawableCoords(canvas, (double) xo, (double) yo,
1104 			    &sectPtr->fpts[4].x, &sectPtr->fpts[4].y);
1105     XDrawLines(display, drawable, sectPtr->gc, sectPtr->fpts, 5, CoordModeOrigin);
1106   }
1107 
1108   ckfree((char *) wpts);
1109 
1110   if (sectPtr->debug) Snack_WriteLog("Exit DisplaySection\n");
1111 }
1112 
1113 static double
SectionToPoint(Tk_Canvas canvas,Tk_Item * itemPtr,double * coords)1114 SectionToPoint(Tk_Canvas canvas, Tk_Item *itemPtr, double *coords)
1115 {
1116   SectionItem *sectPtr = (SectionItem *) itemPtr;
1117   double dx = 0.0, dy = 0.0;
1118   double x1 = sectPtr->header.x1;
1119   double y1 = sectPtr->header.y1;
1120   double x2 = sectPtr->header.x2;
1121   double y2 = sectPtr->header.y2;
1122 
1123   if (coords[0] < x1)
1124     dx = x1 - coords[0];
1125   else if (coords[0] > x2)
1126     dx = coords[0] - x2;
1127   else
1128     dx = 0;
1129 
1130   if (coords[1] < y1)
1131     dy = y1 - coords[1];
1132   else if (coords[1] > y2)
1133     dy = coords[1] - y2;
1134   else
1135     dy = 0;
1136 
1137   return hypot(dx, dy);
1138 }
1139 
1140 static int
SectionToArea(Tk_Canvas canvas,Tk_Item * itemPtr,double * rectPtr)1141 SectionToArea(Tk_Canvas canvas, Tk_Item *itemPtr, double *rectPtr)
1142 {
1143   SectionItem *sectPtr = (SectionItem *) itemPtr;
1144 
1145   if ((rectPtr[2] <= sectPtr->header.x1) ||
1146       (rectPtr[0] >= sectPtr->header.x2) ||
1147       (rectPtr[3] <= sectPtr->header.y1) ||
1148       (rectPtr[1] >= sectPtr->header.y2))
1149     return -1;
1150 
1151   if ((rectPtr[0] <= sectPtr->header.x1) &&
1152       (rectPtr[1] <= sectPtr->header.y1) &&
1153       (rectPtr[2] >= sectPtr->header.x2) &&
1154       (rectPtr[3] >= sectPtr->header.y2))
1155     return 1;
1156 
1157   return 0;
1158 }
1159 
1160 static void
ScaleSection(Tk_Canvas canvas,Tk_Item * itemPtr,double ox,double oy,double sx,double sy)1161 ScaleSection(Tk_Canvas canvas, Tk_Item *itemPtr, double ox, double oy,
1162 	  double sx, double sy)
1163 {
1164   SectionItem *sectPtr = (SectionItem *) itemPtr;
1165   double *coords = sectPtr->coords;
1166   int i;
1167 
1168   for (i = 0; i < sectPtr->nPoints; i++) {
1169     coords[0] = ox + sx * (coords[0] - ox);
1170     coords[1] = oy + sy * (coords[1] - oy);
1171     coords += 2;
1172   }
1173   sectPtr->width  = (int) (sx * sectPtr->width);
1174   sectPtr->height = (int) (sy * sectPtr->height);
1175 
1176   ComputeSectionBbox(canvas, sectPtr);
1177 }
1178 
1179 static void
TranslateSection(Tk_Canvas canvas,Tk_Item * itemPtr,double dx,double dy)1180 TranslateSection(Tk_Canvas canvas, Tk_Item *itemPtr, double dx, double dy)
1181 {
1182   SectionItem *sectPtr = (SectionItem *) itemPtr;
1183 
1184   sectPtr->x += dx;
1185   sectPtr->y += dy;
1186   ComputeSectionBbox(canvas, sectPtr);
1187 }
1188 
1189 static int
SectionToPS(Tcl_Interp * interp,Tk_Canvas canvas,Tk_Item * itemPtr,int prepass)1190 SectionToPS(Tcl_Interp *interp, Tk_Canvas canvas, Tk_Item *itemPtr, int prepass)
1191 {
1192   SectionItem *sectPtr = (SectionItem *) itemPtr;
1193   double  *coords = sectPtr->coords;
1194   int     nPoints = sectPtr->nPoints;
1195   char buffer[100];
1196   int xo = sectPtr->header.x1;
1197   int yo = sectPtr->header.y1;
1198 
1199   if (sectPtr->fg == NULL) {
1200     return TCL_OK;
1201   }
1202 
1203   Tcl_AppendResult(interp, "%% SECT BEGIN\n", (char *) NULL);
1204 
1205   sprintf(buffer, "%.15g %.15g moveto\n", coords[0] + xo,
1206 	  Tk_CanvasPsY(canvas, (double) (coords[1] + yo)));
1207   Tcl_AppendResult(interp, buffer, (char *) NULL);
1208   coords += 2;
1209   for (nPoints--; nPoints > 0; nPoints--) {
1210     sprintf(buffer, "%.15g %.15g lineto\n", coords[0] + xo,
1211 	    Tk_CanvasPsY(canvas, (double) (coords[1] + yo)));
1212     Tcl_AppendResult(interp, buffer, (char *) NULL);
1213     coords += 2;
1214   }
1215 
1216   if (sectPtr->frame) {
1217     sprintf(buffer, "%.15g %.15g moveto\n", (double) xo, Tk_CanvasPsY(canvas, (double) yo));
1218     Tcl_AppendResult(interp, buffer, (char *) NULL);
1219 
1220     sprintf(buffer, "%.15g %.15g lineto\n", (double) xo + sectPtr->width - 1,
1221 	    Tk_CanvasPsY(canvas, (double) yo));
1222     Tcl_AppendResult(interp, buffer, (char *) NULL);
1223 
1224     sprintf(buffer, "%.15g %.15g lineto\n", (double) xo + sectPtr->width - 1,
1225 	    Tk_CanvasPsY(canvas, (double) (yo + sectPtr->height - 1)));
1226     Tcl_AppendResult(interp, buffer, (char *) NULL);
1227 
1228     sprintf(buffer, "%.15g %.15g lineto\n", (double) xo,
1229 	    Tk_CanvasPsY(canvas, (double) (yo + sectPtr->height - 1)));
1230     Tcl_AppendResult(interp, buffer, (char *) NULL);
1231 
1232     sprintf(buffer, "%.15g %.15g lineto\n", (double) xo,
1233 	    Tk_CanvasPsY(canvas, (double) yo));
1234     Tcl_AppendResult(interp, buffer, (char *) NULL);
1235   }
1236 
1237   Tcl_AppendResult(interp, "1 setlinewidth\n", (char *) NULL);
1238   Tcl_AppendResult(interp, "0 setlinecap\n0 setlinejoin\n", (char *) NULL);
1239   if (Tk_CanvasPsColor(interp, canvas, sectPtr->fg) != TCL_OK) {
1240     return TCL_ERROR;
1241   };
1242   if (sectPtr->fillStipple != None) {
1243     Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
1244     if (Tk_CanvasPsStipple(interp, canvas, sectPtr->fillStipple) != TCL_OK) {
1245       return TCL_ERROR;
1246     }
1247   } else {
1248     Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
1249   }
1250 
1251   Tcl_AppendResult(interp, "%% SECT END\n", (char *) NULL);
1252 
1253   return TCL_OK;
1254 }
1255