Project

General

Profile

Download (21.3 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / bandaged / BandagedCreatorRenderer.java @ a0d89fd1

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.bandaged;
21

    
22
import javax.microedition.khronos.egl.EGLConfig;
23
import javax.microedition.khronos.opengles.GL10;
24

    
25
import android.app.Activity;
26
import android.opengl.GLES31;
27
import android.opengl.GLSurfaceView;
28
import android.widget.Toast;
29

    
30
import org.distorted.dialogs.RubikDialogBandagedSave;
31
import org.distorted.library.effect.EffectType;
32
import org.distorted.library.effect.FragmentEffectBrightness;
33
import org.distorted.library.effect.PostprocessEffectBorder;
34
import org.distorted.library.main.DistortedEffects;
35
import org.distorted.library.main.DistortedFramebuffer;
36
import org.distorted.library.main.DistortedLibrary;
37
import org.distorted.library.main.DistortedNode;
38
import org.distorted.library.main.DistortedScreen;
39

    
40
import org.distorted.library.main.InternalOutputSurface;
41
import org.distorted.library.mesh.MeshBase;
42
import org.distorted.library.type.Static1D;
43
import org.distorted.library.type.Static3D;
44
import org.distorted.library.type.Static4D;
45
import org.distorted.objectlib.json.JsonWriter;
46
import org.distorted.objectlib.main.InitData;
47
import org.distorted.objectlib.main.ShapeHexahedron;
48
import org.distorted.objectlib.main.TwistyObject;
49
import org.distorted.objectlib.objects.TwistyBandagedGeneric;
50
import org.json.JSONException;
51

    
52
import java.io.File;
53
import java.io.FileNotFoundException;
54
import java.io.IOException;
55
import java.nio.ByteBuffer;
56
import java.nio.ByteOrder;
57

    
58
///////////////////////////////////////////////////////////////////////////////////////////////////
59

    
60
public class BandagedCreatorRenderer implements GLSurfaceView.Renderer, DistortedLibrary.ExceptionListener
61
{
62
   public static final float BRIGHTNESS = 0.333f;
63
   private static final int RESET_DURATION = 1000;
64
   private static final float MAX_SIZE_CHANGE = 1.70f;
65
   private static final float MIN_SIZE_CHANGE = 0.50f;
66
   private static final float INIT_RATIO = 0.5f;
67

    
68
   private final BandagedCreatorView mView;
69
   private final DistortedScreen mScreen;
70
   private final Static3D mScale;
71
   private final Static4D mQuatT, mQuatA;
72
   private final int[] mObjSize;
73

    
74
   private BandagedCubit[] mCubits;
75
   private boolean mInitialPhase;
76
   private long mStartTime;
77
   private float mQuatX, mQuatY, mQuatZ, mQuatW;
78
   private int mX, mY, mZ, mNumCubits;
79
   private boolean mResetQuats, mSetQuatT, mResettingObject, mConnectingCubits, mCreatingCubits, mRescaling;
80
   private int mIndex1, mIndex2;
81
   private int mSaveIcon;
82
   private DistortedFramebuffer mFramebuffer;
83
   private String mPath;
84
   private boolean mCubitsCreated;
85
   private int mWidth, mHeight;
86
   private float mScaleValue, mObjectScreenRatio;
87

    
88
///////////////////////////////////////////////////////////////////////////////////////////////////
89

    
90
   BandagedCreatorRenderer(BandagedCreatorView v)
91
     {
92
     mQuatT = new Static4D(0,0,0,1);
93
     mQuatA = new Static4D(-0.25189602f,0.3546389f,0.009657208f,0.90038127f);
94

    
95
     mView = v;
96

    
97
     mResetQuats       = false;
98
     mSetQuatT         = false;
99
     mResettingObject  = false;
100
     mConnectingCubits = false;
101
     mCubitsCreated    = false;
102
     mCreatingCubits   = false;
103
     mRescaling        = false;
104

    
105
     mObjectScreenRatio= INIT_RATIO;
106
     mSaveIcon = -1;
107
     mObjSize  = new int[3];
108

    
109
     mScreen = new DistortedScreen();
110
     mScreen.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
111
     mScale = new Static3D(1,1,1);
112
     }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
   private boolean isAdjacent(float[] pos1, float[] pos2)
117
     {
118
     int len1 = pos1.length/3;
119
     int len2 = pos2.length/3;
120

    
121
     for(int i=0; i<len1; i++)
122
       for(int j=0; j<len2; j++)
123
         {
124
         float d0 = pos1[3*i  ] - pos2[3*j  ];
125
         float d1 = pos1[3*i+1] - pos2[3*j+1];
126
         float d2 = pos1[3*i+2] - pos2[3*j+2];
127

    
128
         if( d0*d0 + d1*d1 + d2*d2 == 1 ) return true;
129
         }
130

    
131
     return false;
132
     }
133

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

    
136
   private int computeNumCubits(int x, int y, int z)
137
     {
138
     return ( x<=1 || y<=1 || z<=1 ) ? x*y*z : x*y*z-(x-2)*(y-2)*(z-2);
139
     }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
   private void createCubits()
144
     {
145
     mCubits = new BandagedCubit[mNumCubits];
146
     int c=0;
147

    
148
     float begX = 0.5f*(1-mX);
149
     float begY = 0.5f*(1-mY);
150
     float begZ = 0.5f*(1-mZ);
151

    
152
     for(int x=0; x<mX; x++)
153
       for(int y=0; y<mY; y++)
154
          for(int z=0; z<mZ; z++)
155
            if( x==0 || x==mX-1 || y==0 || y==mY-1 || z==0 || z==mZ-1 )
156
              {
157
              float[] pos = new float[] { begX+x,begY+y,begZ+z };
158
              mCubits[c] = new BandagedCubit(pos,mX,mY,mZ,mQuatT,mQuatA,mScale,false);
159
              c++;
160
              }
161

    
162
     mView.setCubits(mCubits,mX,mY,mZ);
163
     }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
   private void resetObject()
168
     {
169
     mView.resetCubits();
170

    
171
     for(int c=0; c<mNumCubits; c++)
172
       {
173
       if( !mCubits[c].isAttached() )
174
         {
175
         mCubits[c].attach();
176
         mCubits[c].setUnmarked();
177
         mScreen.attach(mCubits[c].getNode());
178
         }
179
       if( mCubits[c].getPosition().length>3 )
180
         {
181
         mCubits[c].reset(mScaleValue);
182
         }
183
       }
184
     }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187

    
188
   @Override
189
   public void onDrawFrame(GL10 glUnused)
190
     {
191
     long time = System.currentTimeMillis();
192
     mScreen.render(time);
193

    
194
     if( mSetQuatT )
195
       {
196
       mSetQuatT = false;
197
       mQuatT.set(mQuatX,mQuatY,mQuatZ,mQuatW);
198
       }
199

    
200
     if( mResetQuats )
201
       {
202
       mResetQuats = false;
203

    
204
       float qx = mQuatT.get0();
205
       float qy = mQuatT.get1();
206
       float qz = mQuatT.get2();
207
       float qw = mQuatT.get3();
208

    
209
       float rx = mQuatA.get0();
210
       float ry = mQuatA.get1();
211
       float rz = mQuatA.get2();
212
       float rw = mQuatA.get3();
213

    
214
       float tx = rw*qx - rz*qy + ry*qz + rx*qw;
215
       float ty = rw*qy + rz*qx + ry*qw - rx*qz;
216
       float tz = rw*qz + rz*qw - ry*qx + rx*qy;
217
       float tw = rw*qw - rz*qz - ry*qy - rx*qx;
218

    
219
       mQuatT.set(0f, 0f, 0f, 1f);
220
       mQuatA.set(tx, ty, tz, tw);
221
       }
222

    
223
     if( mResettingObject )
224
       {
225
       boolean done = continueResetting(time);
226
       if( done ) mResettingObject = false;
227
       }
228

    
229
     if( mSaveIcon>=0 )
230
       {
231
       renderIcon(time); // for some reason we need to call render() twice here, otherwise the
232
       mSaveIcon++;      // icon turns out black. Probably some problem with binding the texture.
233
       }
234
     if( mSaveIcon>=2 )
235
       {
236
       saveIcon();
237
       mSaveIcon = -1;
238
       }
239

    
240
     if( mConnectingCubits )
241
       {
242
       mConnectingCubits = false;
243
       tryConnectingCubits(mIndex1,mIndex2);
244
       }
245

    
246
     if( mCreatingCubits )
247
       {
248
       mCreatingCubits = false;
249

    
250
       rescaleObject();
251

    
252
       if( mCubitsCreated )
253
         {
254
         createCubits();
255

    
256
         mScreen.detachAll();
257
         mView.resetCubits();
258

    
259
         for(int i=0; i<mNumCubits; i++)
260
           {
261
           mCubits[i].scaleMove(mScaleValue);
262
           DistortedNode node = mCubits[i].getNode();
263
           mScreen.attach(node);
264
           }
265
         }
266
       }
267

    
268
     if( mRescaling )
269
       {
270
       mRescaling = false;
271
       rescaleObject();
272
       for(int i=0; i<mNumCubits; i++) mCubits[i].scaleMove(mScaleValue);
273
       BandagedCreatorTouchControl control = mView.getTouchControl();
274
       control.setObjectRatio(mObjectScreenRatio);
275
       }
276
     }
277

    
278
///////////////////////////////////////////////////////////////////////////////////////////////////
279

    
280
   @Override
281
   public void onSurfaceChanged(GL10 glUnused, int width, int height)
282
      {
283
      if( width!=mWidth || height!=mHeight )
284
        {
285
        mWidth = width;
286
        mHeight= height;
287
        rescaleObject();
288

    
289
        mScreen.detachAll();
290
        int touched = mView.getTouched();
291

    
292
        for(int i=0; i<mNumCubits; i++)
293
          if( mCubits[i].isAttached() )
294
            {
295
            mCubits[i].scaleMove(mScaleValue);
296
            if( touched==i ) mCubits[i].setMarked();
297
            else             mCubits[i].setUnmarked();
298
            DistortedNode node = mCubits[i].getNode();
299
            mScreen.attach(node);
300
            }
301

    
302
        mView.setScreenSize(width,height);
303
        mScreen.resize(width,height);
304
        }
305
      }
306

    
307
///////////////////////////////////////////////////////////////////////////////////////////////////
308

    
309
   @Override
310
   public void onSurfaceCreated(GL10 glUnused, EGLConfig config)
311
      {
312
      DistortedLibrary.setMax(EffectType.VERTEX,0);
313
      MeshBase.setMaxEffComponents(120);
314

    
315
      FragmentEffectBrightness.enable();
316

    
317
      DistortedLibrary.onSurfaceCreated(mView.getContext(),this,1);
318
      DistortedLibrary.setCull(true);
319

    
320
      if( mCubits==null )
321
        {
322
        createCubits();
323
        }
324
      else
325
        {
326
        for(int i=0; i<mNumCubits; i++) mCubits[i].recreateBitmap();
327
        }
328

    
329
      mCubitsCreated = true;
330
      mWidth = 0;
331
      mHeight= 0;
332
      }
333

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

    
336
   public void distortedException(Exception ex)
337
     {
338
     android.util.Log.e("CREATOR", "unexpected exception: "+ex.getMessage() );
339
     }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342

    
343
   public BandagedCubit[] getCubits()
344
     {
345
     return mCubits;
346
     }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
   public DistortedScreen getScreen()
351
     {
352
     return mScreen;
353
     }
354

    
355
///////////////////////////////////////////////////////////////////////////////////////////////////
356

    
357
   void setConnecting(int index1, int index2)
358
     {
359
     mIndex1 = index1;
360
     mIndex2 = index2;
361
     mConnectingCubits = true;
362
     }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365

    
366
   private void tryConnectingCubits(int index1, int index2)
367
     {
368
     if( index1!=index2 )
369
       {
370
       float[] pos1 = mCubits[index1].getPosition();
371
       float[] pos2 = mCubits[index2].getPosition();
372

    
373
       if( isAdjacent(pos1,pos2) )
374
         {
375
         mCubits[index2].join(pos1,mScaleValue);
376
         mCubits[index1].detach();
377
         mScreen.detach(mCubits[index1].getNode());
378
         }
379
       }
380
     }
381

    
382
///////////////////////////////////////////////////////////////////////////////////////////////////
383

    
384
   public Static4D getQuatAccu()
385
     {
386
     return mQuatA;
387
     }
388

    
389
///////////////////////////////////////////////////////////////////////////////////////////////////
390

    
391
   public void setQuatTemp(float x, float y, float z, float w)
392
     {
393
     mQuatX = x;
394
     mQuatY = y;
395
     mQuatZ = z;
396
     mQuatW = w;
397

    
398
     mSetQuatT = true;
399
     }
400

    
401
///////////////////////////////////////////////////////////////////////////////////////////////////
402

    
403
   public void resetQuats()
404
     {
405
     mResetQuats = true;
406
     }
407

    
408
///////////////////////////////////////////////////////////////////////////////////////////////////
409

    
410
   public boolean isBusy()
411
     {
412
     return mResettingObject;
413
     }
414

    
415
///////////////////////////////////////////////////////////////////////////////////////////////////
416

    
417
   public void saveObject()
418
     {
419
     int numAttached=0;
420

    
421
     for(int i=0; i<mNumCubits; i++)
422
       if( mCubits[i].isAttached() ) numAttached++;
423

    
424
     float[][] pos = new float[numAttached][];
425
     int attached=0;
426

    
427
     for(int i=0; i<mNumCubits; i++)
428
       if( mCubits[i].isAttached() )
429
         {
430
         pos[attached++] = mCubits[i].getPosition();
431
         }
432

    
433
     InitData data = new InitData(mObjSize,pos);
434
     TwistyObject obj = new TwistyBandagedGeneric( data, TwistyObject.MESH_NICE, TwistyObject.MODE_NORM,
435
                                                   new Static4D(0,0,0,1), new Static3D(0,0,0), 1.0f, null );
436
     String name = obj.getShortName();
437
     BandagedCreatorActivity act = (BandagedCreatorActivity) mView.getContext();
438

    
439
     if( act.objectDoesntExist(name) && createObjectJson(obj,act) )
440
       {
441
       setupIconCreation(act,data);
442
       act.addObject(obj.getShortName());
443
       }
444
     }
445

    
446
///////////////////////////////////////////////////////////////////////////////////////////////////
447

    
448
   private boolean createObjectJson(TwistyObject object, Activity act)
449
     {
450
     final String name = object.getShortName()+"_object.json";
451
     File file = new File(act.getFilesDir(), name);
452
     String filename = file.getAbsolutePath();
453

    
454
     try
455
       {
456
       JsonWriter writer = JsonWriter.getInstance();
457
       String json = writer.createObjectString(object,24);
458
       writer.write(filename,json);
459
       return true;
460
       }
461
     catch(JSONException ex)
462
       {
463
       act.runOnUiThread(new Runnable()
464
         {
465
         public void run()
466
           {
467
           String message = "JSON Exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
468
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
469
           }
470
         });
471

    
472
       return false;
473
       }
474
     catch(FileNotFoundException ex)
475
       {
476
       act.runOnUiThread(new Runnable()
477
         {
478
         public void run()
479
           {
480
           String message = "FileNotFound exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
481
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
482
           }
483
         });
484

    
485
       return false;
486
       }
487
     catch(IOException ex)
488
       {
489
       act.runOnUiThread(new Runnable()
490
         {
491
         public void run()
492
           {
493
           String message = "IO exception saving to \n\n"+filename+"\n\n failed:\n\n"+ex.getMessage();
494
           Toast.makeText(act,message,Toast.LENGTH_LONG).show();
495
           }
496
         });
497

    
498
       return false;
499
       }
500
     }
501

    
502
///////////////////////////////////////////////////////////////////////////////////////////////////
503

    
504
   private int computeProjectionAngle()
505
     {
506
     float quot1 = mObjSize[2]/ (float)mObjSize[0];
507
     float quot2 = mObjSize[2]/ (float)mObjSize[1];
508
     float quot3 = mObjSize[0]/ (float)mObjSize[2];
509
     float quot4 = mObjSize[0]/ (float)mObjSize[1];
510

    
511
     float quot5 = Math.max(quot1,quot2);
512
     float quot6 = Math.max(quot3,quot4);
513
     float quot7 = Math.max(quot5,quot6);
514

    
515
          if( quot7<=1.0f ) return 120;
516
     else if( quot7<=1.5f ) return 90;
517
     else if( quot7<=2.0f ) return 60;
518
     else                   return 30;
519
     }
520

    
521
///////////////////////////////////////////////////////////////////////////////////////////////////
522

    
523
   private void setupIconCreation(Activity act, InitData data)
524
     {
525
     final float R=1.0f;
526
     final int FBO_WIDTH  = (int)(R*720);
527
     final int FBO_HEIGHT = (int)(R*1280);
528
     final float OBJECT_SIZE = R*0.35f;
529

    
530
     TwistyObject obj = new TwistyBandagedGeneric( data, TwistyObject.MESH_NICE, TwistyObject.MODE_ICON,
531
                                                   ShapeHexahedron.DEFAULT_ROT, new Static3D(0,0,0), OBJECT_SIZE, null );
532

    
533
     DistortedEffects effects = obj.getObjectEffects();
534
     DistortedNode node = obj.getNode();
535

    
536
     if( mFramebuffer==null )
537
       {
538
       mFramebuffer = new DistortedFramebuffer(FBO_WIDTH,FBO_HEIGHT,1, InternalOutputSurface.DEPTH_NO_STENCIL);
539
       mFramebuffer.glClearColor(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS, 1.0f);
540
       }
541

    
542
     mFramebuffer.setProjection( computeProjectionAngle() ,0.1f);
543
     mFramebuffer.detachAll();
544
     mFramebuffer.attach(node);
545

    
546
     Static1D halo = new Static1D(5);
547
     Static4D color = new Static4D(0,0,0,1);
548
     PostprocessEffectBorder border = new PostprocessEffectBorder(halo,color);
549
     border.setHaloDepth(false);
550
     effects.apply(border);
551

    
552
     final String name = obj.getShortName()+".png";
553
     File file = new File(act.getFilesDir(), name);
554
     String filename = file.getAbsolutePath();
555

    
556
     mSaveIcon = 0;
557
     mPath = filename;
558
     }
559

    
560
///////////////////////////////////////////////////////////////////////////////////////////////////
561

    
562
   private void renderIcon(long time)
563
     {
564
     mFramebuffer.render(time);
565
     }
566

    
567
///////////////////////////////////////////////////////////////////////////////////////////////////
568

    
569
   private void saveIcon()
570
     {
571
     int fW = mFramebuffer.getWidth();
572
     int fH = mFramebuffer.getHeight();
573

    
574
     ByteBuffer buf = ByteBuffer.allocateDirect(fW*fH*4);
575
     buf.order(ByteOrder.LITTLE_ENDIAN);
576

    
577
     mFramebuffer.setAsReadFramebuffer(0);
578
     GLES31.glReadBuffer(GLES31.GL_COLOR_ATTACHMENT0);
579
     GLES31.glReadPixels( 0, 0, fW, fH, GLES31.GL_RGBA, GLES31.GL_UNSIGNED_BYTE, buf);
580
     BandagedCreatorWorkerThread.newBuffer(buf,fW,fH,6,mPath);
581
     GLES31.glBindFramebuffer(GLES31.GL_READ_FRAMEBUFFER, 0);
582

    
583
     mSaveIcon = -1;
584
     }
585

    
586
///////////////////////////////////////////////////////////////////////////////////////////////////
587

    
588
   void mulObjectRatio(float ratio)
589
     {
590
     mObjectScreenRatio *= ratio;
591

    
592
     if( mObjectScreenRatio>MAX_SIZE_CHANGE*INIT_RATIO) mObjectScreenRatio = MAX_SIZE_CHANGE*INIT_RATIO;
593
     if( mObjectScreenRatio<MIN_SIZE_CHANGE*INIT_RATIO) mObjectScreenRatio = MIN_SIZE_CHANGE*INIT_RATIO;
594

    
595
     mRescaling = true;
596
     }
597

    
598
///////////////////////////////////////////////////////////////////////////////////////////////////
599

    
600
   float getObjectRatio()
601
     {
602
     return mObjectScreenRatio;
603
     }
604

    
605
///////////////////////////////////////////////////////////////////////////////////////////////////
606

    
607
   private void rescaleObject()
608
     {
609
     final int size = mX>mY ? Math.max(mX, mZ) : Math.max(mY, mZ);
610
     final float Q = mObjectScreenRatio/size;
611
     mScaleValue = mWidth<mHeight ? Q*mWidth : Q*mHeight;
612
     mScale.set( mScaleValue,mScaleValue,mScaleValue );
613
     }
614

    
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616

    
617
   public void changeObject(int x, int y, int z)
618
     {
619
     if( mX!=x || mY!=y || mZ!=z )
620
       {
621
       mX = x;
622
       mY = y;
623
       mZ = z;
624

    
625
       mObjSize[0] = mX;
626
       mObjSize[1] = mY;
627
       mObjSize[2] = mZ;
628

    
629
       mNumCubits = computeNumCubits(mX,mY,mZ);
630

    
631
       mCreatingCubits = true;
632
       }
633
     }
634

    
635
///////////////////////////////////////////////////////////////////////////////////////////////////
636

    
637
   public void displaySavingDialog()
638
     {
639
     BandagedCreatorActivity act = (BandagedCreatorActivity)mView.getContext();
640
     RubikDialogBandagedSave saveDiag = new RubikDialogBandagedSave();
641
     saveDiag.show(act.getSupportFragmentManager(), null);
642
     }
643

    
644
///////////////////////////////////////////////////////////////////////////////////////////////////
645

    
646
   public void setupReset()
647
     {
648
     mResettingObject = true;
649
     mInitialPhase    = true;
650
     mStartTime       = System.currentTimeMillis();
651
     }
652

    
653
///////////////////////////////////////////////////////////////////////////////////////////////////
654

    
655
   public boolean continueResetting(long time)
656
     {
657
     long diff = time-mStartTime;
658
     float quotient = ((float)diff)/RESET_DURATION;
659

    
660
     if( mInitialPhase && quotient>0.5f )
661
       {
662
       mInitialPhase=false;
663
       resetObject();
664
       }
665

    
666
     double angle = 2*Math.PI*quotient*quotient*(3-2*quotient);
667

    
668
     float sinA = (float)Math.sin(angle);
669
     float cosA = (float)Math.cos(angle);
670

    
671
     mQuatT.set(0, -sinA, 0, cosA);
672

    
673
     return quotient>1.0f;
674
     }
675

    
676
///////////////////////////////////////////////////////////////////////////////////////////////////
677

    
678
  public void touchCubit(int index)
679
    {
680
    if( index>=0 && index<mNumCubits && mCubits[index]!=null ) mCubits[index].setMarked();
681
    }
682

    
683
///////////////////////////////////////////////////////////////////////////////////////////////////
684

    
685
  public void untouchCubit(int index)
686
    {
687
    if( index>=0 && index<mNumCubits && mCubits[index]!=null ) mCubits[index].setUnmarked();
688
    }
689
}
(3-3/13)