/*
class for displaying XYZ coordinates from hwrCoord and allowing
to smooth and add noise.

Copyright, Lambert Schomaker/NICI, March 1996.

Please, refer to the author when using this code by:

Schomaker, L.R.B. (1993).
Using Stroke- or Character-based Self-organiZg Maps
in the Recognition of On-line, Connected Cursive Script.
{\em Pattern Recognition}, 26(3), 443-450.

Schomaker, L.R.B., & Teulings, H.-L. (1990).
A Handwriting Recognition System based on the Properties and Architectures 
of the Human Motor System.
{\em Proceedings of the International Workshop on Frontiers in Handwriting
Recognition (IWFHR)}. (pp. 195-211). Montreal: CENPARMI Concordia.
 */

import java.awt.*;
import java.applet.*;

    

class hwrFilterCanvas extends Canvas {

    /* begin object attributes */

    public Event ev_changed = new Event(this, Event.ACTION_EVENT,"FilterEvent");

    double      Boxcarwidth = 0.;
    boolean     RevealBoxcarwidth = false;
    boolean     EnableBoxcarwidth = false;
    double      Noise = 0.;
    boolean     EnableNoise = false;

    Font	Font;
    double      Xmin,Ymin,Xmax,Ymax,Xoff,Yoff,Xscale,Yscale;

    double      Box_xmargin = 0.05; /* LR symmetrical of graphbox on canvas */
    double      Box_ymargin = 0.15; /* bottom margin of graphbox on canvas */
    double      Hwrmargin = 0.02;   /* all sides within graphics box */
    double      Xpix, Ypix, Wpix, Hpix;

    int         Cursor = -1;

    hwrCoord    Hin;
    hwrCoord	Hout = new hwrCoord(-1);

    TextField Txt_Boxcarwidth;
    TextField Txt_noise;

    /* end object attributes */


    public hwrFilterCanvas(boolean enableNoise
                          ,boolean enableBoxcarwidth 
                          ,boolean revealBoxcarwidth) {
        EnableNoise = enableNoise;
        EnableBoxcarwidth = enableBoxcarwidth;
        RevealBoxcarwidth = revealBoxcarwidth;
    }

    public void calc() {
        int i, k;
        double xtmp[] = new double [Hin.Npoints];
        double ytmp[] = new double [Hin.Npoints];

        if(Noise != 0.0) {
            double nfact = Noise/100. * (Hin.Ymax_in - Hin.Ymin_in);
            for(i = 0; i < Hin.Npoints; ++i) {
                xtmp[i] = Hin.X[i] + nfact * 2. * (Math.random() - 0.5);
                ytmp[i] = Hin.Y[i] + nfact * 2. * (Math.random() - 0.5);
            }
        } else {
            for(i = 0; i < Hin.Npoints; ++i) {
                xtmp[i] = Hin.X[i];
                ytmp[i] = Hin.Y[i];
            }
        }

        if(Boxcarwidth >= 2.) {	
            double hfir[] = new double [(int) Boxcarwidth];

            for(i = 0; i < Boxcarwidth; ++i) {
               hfir[i] = 1.0 / Boxcarwidth;
            }

            Hin.fir_filter(xtmp, Hout.X, Hin.Npoints, hfir, (int) Boxcarwidth);
            Hin.fir_filter(ytmp, Hout.Y, Hin.Npoints, hfir, (int) Boxcarwidth);
            Hout.Npoints = Hin.Npoints;
            for(i = 0; i < Hout.Npoints; ++i) {
               Hout.Z[i] = Hin.Z[i];
            }

        } else {
            Hout.Npoints = Hin.Npoints;
            for(i = 0; i < Hout.Npoints; ++i) {
               Hout.X[i] = xtmp[i];
               Hout.Y[i] = ytmp[i];
               Hout.Z[i] = Hin.Z[i];
            }
        }
/* Other fields */
        Hout.Inkfilenumber = Hin.Inkfilenumber;
        Hout.Inkfilename = Hin.Inkfilename;
        
    }

 
    void scale() {
         int kk;

         Xmin = Xmax = Hout.X[0];
         Ymin = Ymax = Hout.Y[0];

         for(kk = 1; kk < Hout.Npoints; ++kk) {
                if( Hout.X[kk] < Xmin) {
                    Xmin = Hout.X[kk];
                }
                if( Hout.Y[kk] < Ymin) {
                    Ymin = Hout.Y[kk];
                }
                if( Hout.X[kk] > Xmax) {
                    Xmax = Hout.X[kk];
                }
                if( Hout.Y[kk] > Ymax) {
                    Ymax = Hout.Y[kk];
                }
                ++kk;
         }

         double xscale = Xmax - Xmin;
         double yscale = Ymax - Ymin;

         if(Wpix/xscale > Hpix/yscale) {
              Yscale = yscale
                             / (1.- Box_ymargin - 2. * Hwrmargin);
              Xscale = Wpix/Hpix * Yscale;
         } else {
              Xscale = xscale
                              / (1.- 2. * (Box_xmargin + Hwrmargin));
              Yscale = Hpix/Wpix * Xscale;
         }

         Xoff = (Xmax + Xmin)/ 2.0;
         Yoff = (Ymax + Ymin)/ 2.0;

    } 

    public void paint(Graphics g) {
        init(g);
        calc();
        scale();
        draw(g);
        annot(g);
    }

    void init(Graphics g) /* init and draw graphics box */
    {
        int x1,y1,x2,y2;

	Rectangle box = bounds();

	g.setColor(Color.black);
	g.setFont(Font);



/* Filter canvas */

        x1 = 0; /* is left */
        y1 = 0; /* is top */

        x2 = box.width - 1;
        y2 = box.height - 1;

	g.drawLine(x1, y1, x1, y2 );
	g.drawLine(x1, y2, x2, y2);
	g.drawLine(x2, y2, x2, y1);
	g.drawLine(x2, y1, x1, y1);

/* AFilter graphics area */

        x1 = (int) (Box_xmargin * box.width);
        y1 = (int) (0.04 * box.height); /* is top */

        x2 = (int) ((1. - Box_xmargin) * box.width);
        y2 = (int) ((1. - Box_ymargin) * box.height);

	g.drawLine(x1, y1, x1, y2 );
	g.drawLine(x1, y2, x2, y2);
	g.drawLine(x2, y2, x2, y1);
	g.drawLine(x2, y1, x1, y1);

/* Remember that this is the graphics box */

        Xpix = x1;
        Ypix = y1;
        Wpix = x2 - x1 + 1;
        Hpix = y2 - y1 + 1;

	g.setColor(Color.blue);

    }

    void draw(Graphics g) 
    {
        int k;

        for (int i =  1; i < (Hout.Npoints - 1); i++) {

            if(Hout.Z[i+1] > 0 && Hout.Z[i] > 0) {
                    g.drawLine(scalex(Hout.X[i]), scaley(Hout.Y[i])
                             , scalex(Hout.X[i+1]), scaley(Hout.Y[i+1]));
            } else {
                g.drawLine(scalex(Hout.X[i+1]), scaley(Hout.Y[i+1]),
                           scalex(Hout.X[i+1]), scaley(Hout.Y[i+1]));
            }
            if(RevealBoxcarwidth) {
                if(15 < i && i <= 15 + (int) Boxcarwidth) {
                   for(k = -4; k <= 4; ++k) {
                    g.drawLine(scalex(Hout.X[i]), scaley(Hout.Y[i]) + k
                             , scalex(Hout.X[i + 1]), scaley(Hout.Y[i+1]) + k);
                   }
                }
            } 
        }

	if(Cursor >= 0 && Cursor < Hout.Npoints) {
             Color now = g.getColor();
             g.setColor(Color.red);
             g.fillArc(scalex(Hout.X[Cursor])-4
                      ,scaley(Hout.Y[Cursor])-4, 8, 8, 0, 360);
             g.setColor(now);
        }

        if(Hout.Vbs.Nsegm > 0) {
            Color now = g.getColor();
            g.setColor(Color.green);  
            for (int i =  0; i < Hout.Vbs.Nsegm; i++) {
             g.fillArc(scalex(Hout.X[Hout.Vbs.t[i]])-3
                      ,scaley(Hout.Y[Hout.Vbs.t[i]])-3, 6, 6, 0, 360);
            } 
            g.setColor(now);
        }
    }

    public void annot(Graphics g)
    {
        g.drawString(Hin.Inkfilename, (int) Xpix, (int) (Ypix + Hpix + 18.));

        int sy;
        if(EnableNoise) {
           sy = bounds().height - Txt_noise.location().y - 4;
           g.drawString("Noise:", Txt_noise.location().x,sy);
        }

        if(EnableBoxcarwidth) {
           sy = bounds().height - Txt_noise.location().y - 4;
           g.drawString("FilterWidth:", Txt_Boxcarwidth.location().x,sy);
        }
    }

    int scalex(double r)
    {
        return((int) (Xpix + 0.5 * Wpix + Wpix * (r - Xoff)/Xscale));
    }

    int scaley(double r)
    {
        return((int) (Ypix + Hpix * (0.5 - (r - Yoff)/Yscale)));
    }

    public void redraw() {

        if(Boxcarwidth < 0.) {
            Boxcarwidth  = 0.;
        }
        if(Boxcarwidth > 51.) {
            Boxcarwidth = 51.;
        }

        if(EnableBoxcarwidth) {
            Txt_Boxcarwidth.setText(Double.toString(Boxcarwidth));
        }

        if(Noise < 0.) {
            Noise  = 0.;
        }
        if(Noise > 100.) {
            Noise = 100.;
        }

        if(EnableNoise) {
            Txt_noise.setText(Double.toString(Noise));
        }

        repaint();
        deliverEvent(ev_changed);
    }

}

class hwrFilterControls extends Panel {
    hwrFilterCanvas canvas;
    Scrollbar Boxcarwidth_slider;

    protected void makebutton(String name,
                              GridBagLayout gridbag,
                              GridBagConstraints c) {
        Button button = new Button(name);
        gridbag.setConstraints(button, c);
        add(button);
    }


    public hwrFilterControls(hwrFilterCanvas canvas) {
	this.canvas = canvas;

        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints rules = new GridBagConstraints();
        setLayout(gridbag);

        rules.fill = GridBagConstraints.BOTH;
        rules.weightx = 1.0;

        if(this.canvas.EnableBoxcarwidth) {
           Boxcarwidth_slider = new Scrollbar(Scrollbar.HORIZONTAL, 0, 1, 0, 51);
           gridbag.setConstraints(Boxcarwidth_slider, rules);
           add(Boxcarwidth_slider);

           rules.weightx = 0.0;
	   this.canvas.Txt_Boxcarwidth = 
              new TextField(Double.toString(this.canvas.Boxcarwidth), 6);
           gridbag.setConstraints(this.canvas.Txt_Boxcarwidth, rules);
           add(this.canvas.Txt_Boxcarwidth);
        }

	makebutton("Apply", gridbag, rules);
	makebutton("Previous", gridbag, rules);
	makebutton("Next", gridbag, rules);

        if(this.canvas.EnableNoise) {
	   this.canvas.Txt_noise = 
              new TextField(Double.toString(this.canvas.Noise), 6);
           gridbag.setConstraints(this.canvas.Txt_noise, rules);
           add(this.canvas.Txt_noise);
        }
    }

    public boolean handleEvent(Event ev) {
        Double tmp_filter, tmp_noise;

        if (ev.target instanceof Scrollbar) {
            this.canvas.Boxcarwidth = (double) Boxcarwidth_slider.getValue();

            tmp_noise = Double.valueOf(this.canvas.Txt_noise.getText().trim());
            this.canvas.Noise = tmp_noise.doubleValue();

            this.canvas.Txt_Boxcarwidth.setText(Double.toString(this.canvas.Boxcarwidth));
            this.canvas.Txt_noise.setText(Double.toString(this.canvas.Noise));

	    canvas.redraw();

	    return true;
	}

	if (ev.target instanceof Button) {
            
            if ( "Next".equals(ev.arg) ) {
                   this.canvas.Hin.next();
                   this.canvas.Hout.Vbs.Nsegm = 0;
            }

            if ( "Previous".equals(ev.arg) ) {
                   this.canvas.Hin.previous();
                   this.canvas.Hout.Vbs.Nsegm = 0;
            }

            if(this.canvas.EnableBoxcarwidth) {
                tmp_filter = Double.valueOf(this.canvas.Txt_Boxcarwidth.getText().trim());
                this.canvas.Boxcarwidth = tmp_filter.doubleValue();
            }

	    if(this.canvas.EnableNoise) {
                tmp_noise = Double.valueOf(this.canvas.Txt_noise.getText().trim());
                this.canvas.Noise = tmp_noise.doubleValue();
            } 

	    canvas.redraw();

	    return true;

	}

	return false;
    }
}


class hwrFilterPanel extends Panel {
     hwrFilterControls controls;
     hwrFilterCanvas canvas;
 
     public hwrFilterPanel(boolean enableNoise
                          ,boolean enableBoxcarwidth
                          ,boolean revealBoxcarwidth) {
        canvas = new hwrFilterCanvas(enableNoise, enableBoxcarwidth, revealBoxcarwidth);
        controls = new hwrFilterControls(canvas);

        this.setLayout(new BorderLayout());
        add("Center",canvas);
        add("South", controls);
    }

    public boolean handleEvent(Event ev) {
	return false;
    }

}

/* end hwrFilterCanvas.java */

