buffer stores 2 channels of double; colour using both channels; default supersample...
[maximus:sft.git] / SuperFractalThing / source / SftComponent.java
1 //      SFTComponent
2 //
3 //
4 //    Copyright 2013 Kevin Martin
5 //    Copyright 2013 Claude Heiland-Allen
6 //
7 //    This file is part of SuperFractalThing.
8 //
9 //    SuperFractalThing is free software: you can redistribute it and/or modify
10 //    it under the terms of the GNU General Public License as published by
11 //    the Free Software Foundation, either version 3 of the License, or
12 //    any later version.
13 //
14 //    SuperFractalThing is distributed in the hope that it will be useful,
15 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 //    GNU General Public License for more details.
18 //
19 //    You should have received a copy of the GNU General Public License
20 //    along with SuperFractalThing.  If not, see <http://www.gnu.org/licenses/>.
21 //
22
23 import java.awt.event.ActionEvent;
24 import java.awt.event.ActionListener;
25 import java.awt.event.MouseEvent;
26 import java.awt.image.BufferedImage;
27 import java.awt.*;
28 import java.math.BigDecimal;
29 import java.math.MathContext;
30
31 import javax.swing.Timer;
32 import javax.swing.event.MouseInputListener;
33
34 interface SFTGui
35 {
36         void SetCoords( BigDecimal aSize, BigDecimal x, BigDecimal y, int iterations);
37         BigDecimal GetTheSize();
38         int GetIterations();
39         void SetIterations(int aValue);
40         BigDecimal[] GetCoords();
41         void StartProcessing();
42         void EndProcessing();
43         void SetHoverIndex(double index);
44         void SetProgress(int progress, int pMax);
45         void ExportImage(BufferedImage aImage);
46         void SetCalculationTime(long aTime_ms);
47     void AddToUndoBuffer();
48     void OutOfMemory();
49 }
50
51
52 public class SftComponent extends Component implements MouseInputListener, Runnable, ActionListener, IPaletteChangeNotify
53 {
54         private static final long serialVersionUID = 0;//get rid of warning
55         BufferedImage mImage;
56         BigDecimal mPos,mPosi;
57         BigDecimal mSize;
58         int mMax_iterations;
59         int mResolution_x;
60         int mResolution_y;
61         SFTGui mGui;
62         Timer mTimer;
63         boolean mProcessing;
64         IndexBuffer2D mBuffer;
65         IndexBuffer2D mExport_buffer;
66         IPalette mPalette;
67         CalculationManager mCalculation;
68         SuperSampleType mSuper_sample_type;
69         int mNum_threads;
70         long mStart_time;
71         boolean mPressed;
72         int mPressed_x;
73         int mPressed_y;
74         int mSelected_x;
75         int mSelected_y;
76         int mDragged_size;
77         
78         public SftComponent(SFTGui aGui)
79         {
80                 mGui = aGui;
81         }
82         public void CreateImage()
83         {
84                 mResolution_x = 1024;
85                 mResolution_y = 768;
86                 mSize = new BigDecimal(3.0);
87                 mPos = new BigDecimal(-0.75,MathContext.DECIMAL128);
88                 mPosi= new BigDecimal(0,MathContext.DECIMAL128);
89                 mMax_iterations = 1024;
90                 
91                 mGui.SetCoords(mSize,mPos,mPosi,mMax_iterations);
92                 
93         mImage = new BufferedImage(mResolution_x, mResolution_y, BufferedImage.TYPE_INT_ARGB);
94         
95         UpdateImage();
96         }
97         
98         void SetPalette( IPalette aPalette)
99         {
100                 mPalette = aPalette;
101         }
102         
103         void SetSuperSampleType(SuperSampleType aType)
104         {
105                 mSuper_sample_type = aType;
106         }
107         void SetNumThreads(int aNumber)
108         {
109                 mNum_threads = aNumber;
110                 if (mNum_threads<1)
111                         mNum_threads=1;
112                 if (mNum_threads>1024)
113                         mNum_threads=1024;
114         }       
115         
116         @Override
117         public void PaletteChanged()
118         {
119                 double aPixelSpacing = mSize.doubleValue() / mBuffer.GetWidth();
120                 mImage = mBuffer.MakeTexture(mPalette, mSuper_sample_type, aPixelSpacing);
121                 repaint();
122         }
123         
124         public BufferedImage GetImage()
125         {
126                 return mImage;
127         }
128         
129         public void run()
130         {
131                 UpdateImage();
132                 mProcessing=false;
133         }
134         
135         void Refresh()
136         {
137                 SetMaxIterations();
138                 DoCalculation();
139         }
140         
141         void DoCalculation()
142         {
143                 mBuffer = DoCalculation(mResolution_x, mResolution_y, mSuper_sample_type);
144         }
145
146         void ExportCalculation(int aResolution_x, int aResolution_y, SuperSampleType aSuper_sample)
147         {
148                 mExport_buffer = DoCalculation(aResolution_x, aResolution_y, aSuper_sample);
149         }
150         
151         IndexBuffer2D DoCalculation(int aResolution_x, int aResolution_y, SuperSampleType aSuper_sample)
152         {
153                 BigDecimal coords[] = new BigDecimal[2];
154                 
155                 mGui.StartProcessing();
156                 mStart_time = System.currentTimeMillis();
157                 mGui.SetCalculationTime( -1);
158                 coords = mGui.GetCoords();
159                 mMax_iterations = mGui.GetIterations();
160                 mSize = mGui.GetTheSize();
161                 mPos = coords[0];
162                 mPosi = coords[1];      
163                 int scale = mGui.GetTheSize().scale();
164                 int precision = mGui.GetTheSize().precision();
165                 int expo=0;
166                 precision = scale -precision + 8;
167                 
168                 IndexBuffer2D buffer=null;
169                 
170                 switch (aSuper_sample)
171                 {
172                 case SUPER_SAMPLE_NONE:
173                         buffer = new IndexBuffer2D(aResolution_x,aResolution_y);
174                         break;
175                 case SUPER_SAMPLE_2X:
176                         buffer = new IndexBuffer2D(aResolution_x+1,aResolution_y*2+1);
177                         break;
178                 case SUPER_SAMPLE_4X:
179                         buffer = new IndexBuffer2D(aResolution_x*2,aResolution_y*2);
180                         break;
181                 case SUPER_SAMPLE_4X_9:
182                         buffer = new IndexBuffer2D(aResolution_x*2+1,aResolution_y*2+1);
183                         break;
184                 case SUPER_SAMPLE_9X:
185                         buffer = new IndexBuffer2D(aResolution_x*3,aResolution_y*3);
186                         break;
187                 
188                 }
189         
190                 CalculationManager calc = new CalculationManager();
191                 mCalculation = calc;
192                 
193                 double size;
194                 BigDecimal bd280 = new BigDecimal(1e-280);
195                 if (mSize.compareTo( bd280)<0)
196                 {
197                         BigDecimal mod_size = mSize;
198                         while (mod_size.compareTo( bd280)<0)
199                         {
200                                 mod_size=mod_size.movePointRight(1);
201                                 expo+=1;
202                         }
203                         size = mod_size.doubleValue();
204                                 
205                 }
206                 else
207                 {
208                         size = mSize.doubleValue();
209                 }
210                 
211                 calc.SetCoordinates(mPos,mPosi,(size/2*mResolution_x)/mResolution_y,expo, new MathContext(precision));
212                 calc.SetBuffer(buffer, aSuper_sample);
213                 calc.SetIterationLimit(mMax_iterations);
214                 calc.SetAccuracy(1);
215                 calc.ThreadedCalculation(mNum_threads);
216                 
217                 if (mTimer==null)
218                 {
219                         mTimer = new Timer(100, this);
220                         mTimer.setInitialDelay(100);
221                 }
222                 mTimer.start(); 
223                 return buffer;
224         }
225         
226         void UpdateImage()
227         {               
228                 BigDecimal coords[] = new BigDecimal[2];
229                 
230                 coords = mGui.GetCoords();
231                 mMax_iterations = mGui.GetIterations();
232                 mSize = mGui.GetTheSize();
233                 mPos = coords[0];
234                 mPosi = coords[1];
235                 
236                 BigDecimal bigx  = mPos;
237                 BigDecimal bigy  = mPosi;
238                 
239                 int scale = mGui.GetTheSize().scale();
240                 int precision = mGui.GetTheSize().precision();
241                 int expo=0;
242                 
243                 precision = scale -precision + 8;
244                 
245                 mSuper_sample_type = SuperSampleType.SUPER_SAMPLE_NONE;
246                 
247                 switch (mSuper_sample_type)
248                 {
249                 case SUPER_SAMPLE_NONE:
250                         mBuffer = new IndexBuffer2D(mResolution_x,mResolution_y);
251                         break;
252                 case SUPER_SAMPLE_2X:
253                         mBuffer = new IndexBuffer2D(mResolution_x+1,mResolution_y*2+1);
254                         break;
255                 case SUPER_SAMPLE_4X:
256                         mBuffer = new IndexBuffer2D(mResolution_x*2,mResolution_y*2);
257                         break;
258                 case SUPER_SAMPLE_4X_9:
259                         mBuffer = new IndexBuffer2D(mResolution_x*2+1,mResolution_y*2+1);
260                         break;
261                 case SUPER_SAMPLE_9X:
262                         mBuffer = new IndexBuffer2D(mResolution_x*3,mResolution_y*3);
263                         break;
264                 
265                 }
266
267                 double size;
268                 BigDecimal bd280 = new BigDecimal(1e-280);
269                 if (mSize.compareTo( bd280)<0)
270                 {
271                         BigDecimal mod_size = mSize;
272                         while (mod_size.compareTo( bd280)<0)
273                         {
274                                 mod_size=mod_size.movePointRight(1);
275                                 expo+=1;
276                         }
277                         size = mod_size.doubleValue();
278                                 
279                 }
280                 else
281                 {
282                         size = mSize.doubleValue();
283                 }
284                 
285                 CalculationManager calc = new CalculationManager();
286                 calc.SetCoordinates(bigx,bigy,(size/2*mResolution_x)/mResolution_y,expo, new MathContext(precision));
287                 calc.SetBuffer(mBuffer, mSuper_sample_type);
288                 calc.SetIterationLimit(mMax_iterations);
289                 calc.SetAccuracy(1);
290                 calc.InitialiseCalculation();
291                 calc.CalculateSector(0,1,1);
292                 mGui.AddToUndoBuffer();
293                 
294                 double aPixelSpacing = mSize.doubleValue() / mBuffer.GetWidth();
295                 mImage = mBuffer.MakeTexture(mPalette, mSuper_sample_type, aPixelSpacing);
296         }
297         
298     public void paint(Graphics g) {
299         Graphics2D g2 = (Graphics2D) g;
300         
301         g2.drawImage(mImage,0,0,null);
302         g2.setColor (Color.gray);
303         
304         if (mDragged_size>0)
305         {
306                 int width = mDragged_size;
307                 int height = (mDragged_size * 768)/1024;
308                 g2.draw3DRect(mPressed_x-width/2, mPressed_y - height/2, width, height,true);
309             
310         }
311     }
312     
313     public Dimension getPreferredSize(){
314         return new Dimension(1024, 768);
315     }
316     
317         void SetMaxIterations()
318         {
319                 mMax_iterations = mGui.GetIterations();
320                 if (mCalculation!=null)
321                 {
322                         if (mMax_iterations != mGui.GetIterations())
323                                 mMax_iterations = mGui.GetIterations();
324                         else
325                         {
326                                 mMax_iterations= Math.max(mMax_iterations, mCalculation.GetNewLimit());
327                                 mGui.SetIterations(mMax_iterations);
328                         }
329                 }
330         }
331         
332         @Override
333         public void mouseClicked(MouseEvent arg0) {
334                 // TODO Auto-generated method stub
335
336                 if (mCalculation!=null && mCalculation.GetIsProcessing())
337                         return;
338                 if (mTimer!=null && mTimer.isRunning())
339                         return;
340                 
341                 int x = arg0.getX()-getX();
342                 int y = arg0.getY()-getY();
343                 
344                 if (arg0.getClickCount()==2)
345                 {
346                         SetMaxIterations();
347                         
348                         double x_mul = x*1.0/mResolution_y - mResolution_x * 0.5/mResolution_y;
349                         double y_mul = (0.5*mResolution_y - y)/mResolution_y;
350                         
351                         BigDecimal x_offset = mSize.multiply(new BigDecimal(x_mul));
352                         BigDecimal y_offset = mSize.multiply(new BigDecimal(y_mul));
353                         
354                         int size_scale = mSize.scale();
355                         if (x_offset.scale() > size_scale+4)
356                                 x_offset = x_offset.setScale( size_scale+4, BigDecimal.ROUND_HALF_DOWN);
357                         if (y_offset.scale() > size_scale+4)
358                                 y_offset = y_offset.setScale( size_scale+4, BigDecimal.ROUND_HALF_DOWN);
359                         
360                         mPos = mPos.add( x_offset );
361                         mPosi = mPosi.add( y_offset );
362                         mSize = mSize.multiply(new BigDecimal(0.2));
363                         
364                         mPos = mPos.stripTrailingZeros();
365                         mPosi = mPosi.stripTrailingZeros();
366                         mSize = mSize.stripTrailingZeros();
367                         
368                         //mPos = mPos.add( new BigDecimal((x) * mSize/mResolution_y - mSize*mResolution_x/mResolution_y/2) );
369                         //mPosi = mPosi.add( new BigDecimal((mResolution_y/2-y) * mSize/mResolution_y));
370                         //mSize *= 0.2;
371                         
372                         //mSize_box.setText(Double.toString(mSize));
373                         mGui.SetCoords(mSize,mPos,mPosi, mMax_iterations);
374                         
375                         mGui.AddToUndoBuffer();
376                         DoCalculation();
377                         repaint();
378                 }
379                 else
380                 {
381                         if (mDragged_size>0)
382                         {
383                                 int s = mDragged_size/2;
384                                 if (x - mSelected_x <= s && mSelected_x -x <= s)
385                                 {
386                                         s = (mDragged_size*768)/1024/2;
387                                         if (y - mSelected_y < s && mSelected_y-y < s)
388                                         {
389                                                 SetMaxIterations();
390
391                                                 double x_mul = mSelected_x*1.0/mResolution_y - mResolution_x * 0.5/mResolution_y;
392                                                 double y_mul = (0.5*mResolution_y - mSelected_y)/mResolution_y;
393                                                 
394                                                 BigDecimal x_offset = mSize.multiply(new BigDecimal(x_mul));
395                                                 BigDecimal y_offset = mSize.multiply(new BigDecimal(y_mul));
396                                                 
397                                                 int size_scale = mSize.scale();
398                                                 if (x_offset.scale() > size_scale+4)
399                                                         x_offset = x_offset.setScale( size_scale+4, BigDecimal.ROUND_HALF_DOWN);
400                                                 if (y_offset.scale() > size_scale+4)
401                                                         y_offset = y_offset.setScale( size_scale+4, BigDecimal.ROUND_HALF_DOWN);
402
403                                                 mPos = mPos.add( x_offset);
404                                                 mPosi = mPosi.add( y_offset);
405                                                 mSize = mSize.multiply(new BigDecimal(mDragged_size/1024.0));
406
407                                                 mPos = mPos.stripTrailingZeros();
408                                                 mPosi = mPosi.stripTrailingZeros();
409                                                 mSize = mSize.stripTrailingZeros();
410                                                 
411                                                 //mPos = mPos.add( new BigDecimal((mSelected_x) * mSize/mResolution_y - mSize*mResolution_x/mResolution_y/2) );
412                                                 //mPosi = mPosi.add( new BigDecimal((mResolution_y/2-mSelected_y) * mSize/mResolution_y));
413                                                 //mSize *= mDragged_size/1024.0;
414                                                 mGui.SetCoords(mSize,mPos,mPosi, mMax_iterations);
415                                                 mGui.AddToUndoBuffer();
416                                                 DoCalculation();
417                                                 repaint();
418                                         }
419                                 }
420                         }
421                 }
422                 if (mDragged_size!=0)
423                 {
424                         mDragged_size = 0;
425                         repaint();
426                 }
427         }
428         @Override
429         public void mouseEntered(MouseEvent arg0) {
430                 // TODO Auto-generated method stub
431                 
432         }
433         @Override
434         public void mouseExited(MouseEvent arg0) {
435                 // TODO Auto-generated method stub
436                 
437         }
438         @Override
439         public void mousePressed(MouseEvent arg0)
440         {
441                 mPressed = true;
442                 mPressed_x = arg0.getX()-getX();
443                 mPressed_y = arg0.getY()-getY();
444                 
445         }
446         @Override
447         public void mouseReleased(MouseEvent arg0)
448         {
449                 mPressed = false;               
450         }
451         @Override
452         public void mouseDragged(MouseEvent arg0)
453         {
454                 int x = arg0.getX()-getX();
455                 int y = arg0.getY()-getY();
456                 
457                 mDragged_size =2*Math.abs(x-mPressed_x);
458                 int ds2 = (Math.abs(y-mPressed_y)*2*1024)/768;
459                 mDragged_size = Math.max(mDragged_size, ds2);
460                 repaint();
461                 mSelected_x = mPressed_x;
462                 mSelected_y = mPressed_y;
463         }
464         @Override
465         public void mouseMoved(MouseEvent arg0)
466         {
467                 if (mCalculation!=null && mCalculation.GetIsProcessing())
468                         return;
469                 
470                 int x = arg0.getX()-getX();
471                 int y = mResolution_y - arg0.getY()-getY();
472                 
473                 if (x<0 || x>=mResolution_x || y<0 || y>=mResolution_y)
474                         return;
475                 
476                 if (mSuper_sample_type == SuperSampleType.SUPER_SAMPLE_4X)
477                 {
478                         x*=2;
479                         y*=2;
480                 }
481                 else if (mSuper_sample_type == SuperSampleType.SUPER_SAMPLE_4X_9)
482                 {
483                         x=x*2+1;
484                         y=y*2+1;
485                 }
486                 else if (mSuper_sample_type == SuperSampleType.SUPER_SAMPLE_9X)
487                 {
488                         x=x*3+1;
489                         y=y*3+1;
490                 }
491                 
492                 if (mBuffer!=null && y<mBuffer.GetHeight() && x<mBuffer.GetWidth())
493                 {
494                         double index = mBuffer.GetValue(x,y,0);
495                         mGui.SetHoverIndex(index);
496                 }               
497         }
498         @Override
499         public void actionPerformed(ActionEvent arg0)
500         {
501                 if (!mCalculation.GetIsProcessing())
502                 {
503                         if (mExport_buffer!=null)
504                         {
505                                 double aPixelSpacing = mSize.doubleValue() / mBuffer.GetWidth();
506                         BufferedImage image = mExport_buffer.MakeTexture(mPalette, mCalculation.GetSuperSampleType(), aPixelSpacing);
507                         if (image==null)
508                         {
509                                 mExport_buffer = null;
510                                 mGui.OutOfMemory();
511                         }
512                         else
513                         {
514                                 mExport_buffer = null;
515                                         mGui.ExportImage(image);
516                         }
517                         }
518                         else
519                         {
520                                 double aPixelSpacing = mSize.doubleValue() / mBuffer.GetWidth();
521                         mImage = mBuffer.MakeTexture(mPalette, mSuper_sample_type, aPixelSpacing);
522                                 repaint();
523                                 //mMax_iterations = mCalculation.GetNewLimit();
524                         }
525                         mTimer.stop();
526                         mGui.EndProcessing();
527                         mGui.SetCalculationTime( System.currentTimeMillis() - mStart_time);
528                 }
529                 else
530                 {
531                         if (mExport_buffer!=null)
532                                 mGui.SetProgress(mCalculation.GetProgress(), mExport_buffer.GetWidth()* mExport_buffer.GetHeight());
533                         else
534                                 mGui.SetProgress(mCalculation.GetProgress(), mBuffer.GetWidth()* mBuffer.GetHeight());
535                 }
536         }
537         
538         void Cancel()
539         {
540                 if (mCalculation!=null)
541                 {
542                         mCalculation.Cancel();
543                 }
544         }
545 }