/* essai de Mandelbrot en Java */

import java.awt.*;
import java.awt.image.*;
import java.applet.*;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;

public class Mandel3 extends Applet implements Runnable {
  Image mimg=null;
  Image jimg=null;
  public JuliaProducer jprod;
  public MandProducer mprod;
  int x1,y1;

  public void init() {
    setLayout(new BorderLayout());
    resize(300,410);
    mprod=new MandProducer(this);
    mimg=createImage(mprod);
    jprod=new JuliaProducer(-0.777,0.136,this);
    jimg=createImage(jprod); 
    mprod.start();
    jprod.start();
  }

  public boolean handleEvent(Event e) {
    int x2,y2;
    double fx,fy;
  
    switch(e.id) {
      case Event.MOUSE_DOWN:
        x1=-1; y1=-1;
        if ((e.modifiers & Event.SHIFT_MASK)==0)
          if ((e.modifiers & Event.CTRL_MASK)==0) {
            if (e.y<200) {
              mprod.drawcursor();
              jprod.reinit(mprod.zx0+mprod.zinc*e.x,mprod.zy0-mprod.zinc*e.y);
              mprod.drawcursor();
              mprod.produce(mprod.ic);
              getGraphics().drawImage(mimg,0,0,(ImageObserver)this);
            }
          } 
          else {
            if (e.y>=210)
              jprod.rezoom(jprod.zx0+(e.x-150)*jprod.zinc,
                           jprod.zy0-(e.y-310)*jprod.zinc,jprod.zinc);
            if (e.y<200)
              mprod.rezoom(mprod.zx0+(e.x-150)*mprod.zinc,
                           mprod.zy0-(e.y-100)*mprod.zinc,mprod.zinc);
          } 
        else
          if ((e.modifiers & Event.CTRL_MASK)==0) {
            x1=e.x; y1=e.y;
          }
          else {
            if (e.y>=210) jprod.rezoom(-1.5,1.0,0.01);
            if (e.y<200) mprod.rezoom(-2.0,1.0,0.01);
          }
        return true;
      case Event.MOUSE_UP:
        if ((y1>=210)&&(e.y>=210)) {
          x2=x1+e.x; 
          y2=y1+e.y;
          fx=((double)(x1-e.x))/300.0;
          if (fx<0) fx=-fx;
          fy=((double)(y1-e.y))/200.0;
          if (fy<0) fy=-fy;
          if (fy>fx) fx=fy;
          if (fx>0.02) jprod.rezoom(
            jprod.zx0+x2*jprod.zinc/2.0-150.0*fx*jprod.zinc,
            jprod.zy0-(y2-420)*jprod.zinc/2.0+100.0*fx*jprod.zinc,
            jprod.zinc*fx);
        }
        if ((y1<200)&&(e.y<200)&&(y1>=0)) {
          x2=x1+e.x; 
          y2=y1+e.y;
          fx=((double)(x1-e.x))/300.0;
          if (fx<0) fx=-fx;
          fy=((double)(y1-e.y))/200.0;
          if (fy<0) fy=-fy;
          if (fy>fx) fx=fy;
          if (fx>0.02) mprod.rezoom(
            mprod.zx0+x2*mprod.zinc/2.0-150.0*fx*mprod.zinc,
            mprod.zy0-y2*mprod.zinc/2.0+100.0*fx*mprod.zinc,
            mprod.zinc*fx);
        }
        return true;
      case Event.WINDOW_DESTROY:
        System.exit(0);
        return true;
      default:
        return false;
    }
  }
  
  public void run() {
    prepareImage(mimg,this);
    prepareImage(jimg,this);
  }

  public void paint(Graphics g) {
   g.drawImage(mimg,0,0,(ImageObserver)this);
   g.drawImage(jimg,0,210,(ImageObserver)this);
  }
  
  public boolean imageUpdate(Image img,int flags,int x,int y,int w,int h) {
    /* repaint(); */
    return true;
  }
}

class MandProducer extends Thread implements ImageProducer {
  int mandbmp[];
  boolean uptodate;
  public double zx0,zy0,zinc;
  Vector ic;
  ColorModel DCModel;
  Enumeration e;
  ImageConsumer cli;
  Hashtable props;
  Mandel3 boss;

  public void drawcursor() {
    int a,b,j;
    
    a=(int)((boss.jprod.cx-zx0)/zinc);
    b=(int)((zy0-boss.jprod.cy)/zinc);
    if ((a>=0)&&(a<=299))
      for (j=b-5;j<=b+5;j++)
        if ((j>=0)&&(j<=199))
          mandbmp[300*j+a]^=0xffff;
    if ((b>=0)&&(b<=199))
      for (j=a-5;j<=a+5;j++)
        if ((j>=0)&&(j<=299))
          mandbmp[300*b+j]^=0xffff;
  }  

  public void drawmand() {
    double x,y,xx,yy,xt;
    int c,i,j;
    int index=0;
  
   if (!uptodate) {
    y=zy0;
    for (i=0;i<200;i++) {
      x=zx0;
      for (j=0;j<300;j++) {
        xx=x; yy=y; c=0;
        while ((c++<20)&&(xx*xx+yy*yy<4.0)) {
          xt=xx*xx-yy*yy+x;
          yy=2*xx*yy+y;
          xx=xt;
        }
        mandbmp[index++]=(255<<24)+c*0x0c0c0c;
        x+=zinc;
      }
      if (i==199) drawcursor();
      if (i%20==19) {
        produce(ic);
        boss.getGraphics().drawImage(boss.mimg,0,0,(ImageObserver)boss);
      }
      y-=zinc;
    }
    uptodate=true;
   }
   else produce(ic);
  }

  public MandProducer(Mandel3 appl) {
    props=new Hashtable();
    DCModel=ColorModel.getRGBdefault();
    ic=new Vector();
    uptodate=false;
    mandbmp=new int[200*300];
    boss=appl;
    zx0=-2.0; zy0=1.0; zinc=0.01;
  }    

  public synchronized void rezoom(double x0,double y0,double inc) {
    zx0=x0; zy0=y0; zinc=inc;
    uptodate=false;
    resume();
  }

  void produce(Vector icbis) {
    for (e=icbis.elements();e.hasMoreElements();) {
      cli=(ImageConsumer)e.nextElement();
      cli.setDimensions(300,200);
      cli.setProperties(props);
      cli.setColorModel(DCModel);
      cli.setHints(ImageConsumer.TOPDOWNLEFTRIGHT|
                   ImageConsumer.COMPLETESCANLINES|
                   ImageConsumer.SINGLEPASS);
      cli.setPixels(0,0,300,200,DCModel,(int[])mandbmp,0,300);
      cli.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
    }
  }      

  public void run() {
    while (true) {
      drawmand();
      suspend();
    }
  }

  public synchronized void addConsumer(ImageConsumer c) {
    ic.addElement(c);
  }

  public synchronized boolean isConsumer(ImageConsumer c) {
    return ic.contains(c);
  }
  
  public synchronized void removeConsumer(ImageConsumer c) {
    ic.removeElement(c);
  }
  
  public /* synchronized */ void startProduction(ImageConsumer c) {
    ic.addElement(c);
    produce(ic);
  }
  
  public void requestTopDownLeftRightResend(ImageConsumer c) {
  }
}

class JuliaProducer extends Thread implements ImageProducer {
  int juliabmp[];
  public double cx,cy;
  public double zx0,zy0,zinc;
  boolean uptodate;
  Vector ic;
  ColorModel DCModel;
  Enumeration e;
  ImageConsumer cli;
  Hashtable props;
  Mandel3 boss;
  
  public void drawjulia() {
    double x,y,xx,yy,xt;
    int c,i,j;
    int index=0;
  
   if (!uptodate) {
    y=zy0;
    for (i=0;i<200;i++) {
      x=zx0;
      for (j=0;j<300;j++) {
        xx=x; yy=y; c=0;
        while ((c++<40)&&(xx*xx+yy*yy<4.0)) {
          xt=xx*xx-yy*yy+cx;
          yy=2*xx*yy+cy;
          xx=xt;
        }
        juliabmp[index++]=(255<<24)+c*0x060606;
        x+=zinc;
      }
      if (i%20==19) {
        produce(ic);
        boss.getGraphics().drawImage(boss.jimg,0,210,(ImageObserver)boss);
      }
      y-=zinc;
    }
    uptodate=true;
   }
     else produce(ic);
  }

  public JuliaProducer(double x,double y,Mandel3 appl) {
    cx=x; cy=y;
    zx0=-1.5; zy0=1.0; zinc=0.01;
    props=new Hashtable();
    DCModel=ColorModel.getRGBdefault();
    ic=new Vector();
    uptodate=false;
    juliabmp=new int[200*300];
    boss=appl;
  }    

  public synchronized void reinit(double x,double y) {
    cx=x; cy=y;
    zx0=-1.5; zy0=1.0; zinc=0.01;
    uptodate=false;
    resume();
  }

  public synchronized void rezoom(double x0,double y0,double inc) {
    zx0=x0; zy0=y0; zinc=inc;
    uptodate=false;
    resume();
  }

  void produce(Vector icbis) {
    for (e=icbis.elements();e.hasMoreElements();) {
      cli=(ImageConsumer)e.nextElement();
      cli.setDimensions(300,200);
      cli.setProperties(props);
      cli.setColorModel(DCModel);
      cli.setHints(ImageConsumer.TOPDOWNLEFTRIGHT|
                   ImageConsumer.COMPLETESCANLINES|
                   ImageConsumer.SINGLEPASS);
      cli.setPixels(0,0,300,200,DCModel,(int[])juliabmp,0,300);
      cli.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
    }
  }      

  public void run() {
    while (true) {
      drawjulia();
      suspend();
    }
  }
  
  public synchronized void addConsumer(ImageConsumer c) {
    ic.addElement(c);
  }

  public synchronized boolean isConsumer(ImageConsumer c) {
    return ic.contains(c);
  }
  
  public synchronized void removeConsumer(ImageConsumer c) {
    ic.removeElement(c);
  }
  
  public /* synchronized */ void startProduction(ImageConsumer c) {
    ic.addElement(c);
    produce(ic);
  }
  
  public void requestTopDownLeftRightResend(ImageConsumer c) {
  }
}
