package hu.taxi.sensors; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PointF; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup.LayoutParams; @Copyright("2011, Ferenc Takács, Budapest, Hungary") @Author("Ferenc Takács (takacs.ferenc.bp@freemail.hu)") public class SensorsActivity extends Activity implements SensorEventListener { SensorManager sensorManager; GLSurfaceView surfaceView; MyRenderer renderer; MyView myView; boolean calibrateStand = false; boolean showBall=true; boolean sign = false; float gravityEearth = SensorManager.GRAVITY_EARTH; float light=-1,proximity=-1; Vect3d accelData = new Vect3d(); Vect3d accelDataSlow = new Vect3d(); Vect3d magnetData = new Vect3d(); Vect3d magnetDataSlow = new Vect3d(); Vect3d orientEarth = new Vect3d(); Vect3d orientMagnet = new Vect3d(); Vect3d magnet = new Vect3d(); Vect3d magnetXearth = new Vect3d(); Vect3d acceleration = new Vect3d(); float magnetSize,accAbs,accPeek; int resetPeek = 30; int counter = 0; int depth = 3; void decelerate(Vect3d prev, Vect3d curr) { final float weight = 14f; final float fullweight = 1f / (weight + 1f); prev.x = (prev.x * weight + curr.x) * fullweight; prev.y = (prev.y * weight + curr.y) * fullweight; prev.z = (prev.z * weight + curr.z) * fullweight; } void GetValue(SensorEvent event, Vect3d prev, Vect3d curr) { curr.x = event.values[0]; curr.y = event.values[1]; curr.z = event.values[2]; decelerate(prev, curr); } @Override public void onSensorChanged(SensorEvent event) { synchronized (this) { if(surfaceView==null) return; switch (event.sensor.getType()) { case Sensor.TYPE_PROXIMITY: proximity = event.values[0]; break; case Sensor.TYPE_LIGHT: light = event.values[0]; break; case Sensor.TYPE_ACCELEROMETER: GetValue(event, accelDataSlow, accelData); if( calibrateStand ) { gravityEearth = accelDataSlow.abs(); accPeek = 0; calibrateStand = false; } orientEarth = accelDataSlow.Norm(); acceleration = accelData.sub(orientEarth.mul(gravityEearth)); accAbs = acceleration.abs(); if( resetPeek>0 ) { resetPeek--; accPeek=0; } else if( accPeek 7 ){ counter=0; myView.invalidate(); // only one-seventh slowly refresh the normal view }; break; } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } final int delay = SensorManager.SENSOR_DELAY_FASTEST; void Reg(int s) { Sensor sensor = sensorManager.getDefaultSensor(s); if (!sensorManager.registerListener(this, sensor, delay)) { ///final String txt[]={"","Accelerometer","Magnetic field","Orientation", /// "Gyroscope","Ligth","Pressure","Temperature","Proximity",""}; ///log.i("Not supported sensor: " + txt[s]); } } @Override protected void onResume() { ///log.v(); super.onResume(); Reg(Sensor.TYPE_LIGHT); Reg(Sensor.TYPE_PROXIMITY); Reg(Sensor.TYPE_ACCELEROMETER); Reg(Sensor.TYPE_MAGNETIC_FIELD); //Reg(Sensor.TYPE_ORIENTATION); // Calculated values (suspicious), not a sensor //Reg(Sensor.TYPE_GYROSCOPE); // Not supported on Galaxy S //Reg(Sensor.TYPE_PRESSURE); // Not supported on Galaxy S //Reg(Sensor.TYPE_TEMPERATURE); // Not supported on Galaxy S surfaceView.onResume(); } @Override protected void onPause() { ///log.v(); super.onPause(); surfaceView.onPause(); sensorManager.unregisterListener(this); } private static final int DIALOG_INFO = 1; @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(0, 1, 0, "info"); menu.add(0, 2, 0, (showBall?"hide":"show")+" ball"); if(showBall){ menu.add(0, 3, 0, "fix center"); menu.add(0, 4, 0, "ball shape"); } return true; } @Override public boolean onPrepareOptionsMenu(Menu menu){ boolean ret = super.onPrepareOptionsMenu(menu); int i = menu.size(); if(showBall){ if(i==2) { menu.add(0, 3, 0, "fix center"); menu.add(0, 4, 0, "ball shape"); } } else if(i==4) { menu.removeItem(4); menu.removeItem(3); } return ret; } @Override public boolean onOptionsItemSelected(MenuItem item) { boolean ret = super.onOptionsItemSelected(item); switch (item.getItemId()) { case 1: showDialog(DIALOG_INFO); break; case 2: showBall = !showBall; item.setTitle((showBall?"hide":"show")+" ball"); break; case 3: calibrateStand = true; break; case 4: depth = (depth+1)%5; renderer.spere = new Body(this,Shape.SPHERE); break; } return ret; } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_INFO: return new AlertDialog.Builder(this) .setTitle("3D Sensors") .setMessage( "Display of the accelerometer and the magnetic field sensors\n\n" + "The orientation of device is calculated from the mean value in time of acceleration due to gravity. " + "The middle vertical needle allways shows up. " + "The small orange needle shows direction of the real magnetic field. " + "The field strength is described numerically also, the natural values between 30 - 60 μT, " + "higher values ​​indicate an artificial field. " + "The red-blue needle is horizontal projection of the magnetic field, red shows north. " + "The small ball shows the device acceleration of gravity beyond. " + "The largest difference from the center is described numerically, and reset by click. " + "Until very moving in the other directions do not reliably show. This tunable off." + "The three scales is 10-degree-level. " + "The inner scale fixed to red-blue neddle. " + "The middle scale show the slope of phone. " + "The outer scale show the arrange of phone. " + "If on your location the gravity different from global mean, then the ball there is not in the center. " + "You able fix that, when device is immobile." + "\n\nNotices. The strong magnetic fields desensitize the magnetic sensor. " + "That restores only later with device shaking. Elsewhere it is called a calibration." + "\n\nWarning! The inside of device contained magnetic parts, that influence the magnetic field. " + "Therefore magnetic sensor only limited directions precise. " + "For example, by turning the device seems north in different directions." + "\n\nAuthor: Ferenc Takács (takacs.ferenc.bp@freemail.hu)" ).create(); } return null; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ///log.v(); GetIni(); sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); surfaceView = new GLSurfaceView(this); surfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); renderer = new MyRenderer(this); surfaceView.setRenderer(renderer); surfaceView.getHolder().setFormat(PixelFormat.RGBA_8888); surfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); setContentView(surfaceView); myView = new MyView(this); addContentView(myView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); surfaceView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { resetPeek=30; } }); } @Override protected void onDestroy(){ SaveIni(); super.onDestroy(); } void GetIni(){ try { FileInputStream f = this.openFileInput("ini"); showBall = f.read()==1?true:false; depth = f.read(); byte b[]=new byte[30]; f.read(b); float a = Float.parseFloat(new String(b,"UTF-8")); if(a!=0) gravityEearth = a; f.close(); } catch (FileNotFoundException e) { } catch (IOException e) { } catch (NumberFormatException e){} } void SaveIni(){ try { FileOutputStream f = this.openFileOutput("ini", 0); f.write(showBall?1:0); f.write(depth); String s = ""+gravityEearth; f.write(s.getBytes("UTF-8")); f.close(); } catch (FileNotFoundException e) { } catch (IOException e) {} } } class MyView extends View { private PointF center = null; private Paint p; private SensorsActivity act; private float xpos = 0; private float ypos = 0; private float radius = 0; private boolean cc; public MyView(SensorsActivity context) { super(context); act = context; ///log.v(); } @Override protected void onSizeChanged(int w, int h, int ow, int oh) { super.onSizeChanged(w, h, ow, oh); ///log.v("w: " + w + " h: " + h + " oldw: " + ow + " oldh: " + oh); radius = Math.min(w, h)/2; center = new PointF(radius,radius); radius -= 40; p = new Paint(); p.setTextSize(radius/10); radius += 15; invalidate(); } private void print(Canvas c,String s,float xpos,float ypos){ p.setStyle(Paint.Style.STROKE); p.setStrokeWidth(3); p.setColor(0xff404000); c.drawText(s, xpos+2, ypos+2, p); // background p.setStyle(Paint.Style.FILL); p.setColor(cc?0xffff9090:0xffffffff); c.drawText(s, xpos, ypos, p); // letters } private void println(Canvas c,String s){ print(c,s,xpos,ypos); ypos += radius/5; } float Angle(float f){ return (float)(Math.floor(Math.asin(f)*1800.0/Math.PI+0.5)/10.0); } Vect3d Angles(Vect3d v) { Vect3d r = new Vect3d(Angle(v.x),Angle(v.y),Angle(v.z)); return r; } final float o_90 = 1f/90f; float iron(float x){ x*=o_90; x = x>0?1-x:-1-x; x = x*x*x*x*x*x*x; x = x>0?1-x:-1-x; return x*radius;} @Override protected void onDraw(Canvas c) { Vect3d e = Angles(act.orientEarth); p.setStyle(Paint.Style.FILL); p.setColor(0x8000ffff); p.setStrokeWidth(4); c.drawLine(center.x-radius, center.y-radius, center.x-radius, center.y+radius, p); c.drawLine(center.x-radius, center.y+radius, center.x+radius, center.y+radius, p); p.setStrokeWidth(1); for(int i=-20;i<=20;i++){ int l = (i%10==0)?10:(i%5==0)?8:4; c.drawLine(center.x-radius+l, center.y-iron(i), center.x-radius-l, center.y-iron(i), p); c.drawLine(center.x+iron(i), center.y+radius-l, center.x+iron(i), center.y+radius+l, p); } p.setColor(act.sign?0xa0ff7070:0xa070ffff); c.drawCircle(center.x-radius, center.y-iron(e.y), 7, p); c.drawCircle(center.x+iron(e.x), center.y+radius, 7, p); act.sign=false; cc = false; print(c,e.y+"°",center.x-radius+20,center.y-radius+20); print(c,e.x+"°",center.x+radius-60,center.y+radius-20); print(c,e.z+"°",center.x+radius-60,center.y-radius+20); xpos = 30; ypos = 2*center.y + 20; Float s = act.magnetDataSlow.abs(10); if( s > SensorManager.MAGNETIC_FIELD_EARTH_MAX || s < SensorManager.MAGNETIC_FIELD_EARTH_MIN ) cc = true; println(c,"Magnetic field strength: " + s +" μT"); cc = false; println(c,"Acceleration: " + Vect3d.Round(act.accAbs,1000f)+" m/s²"); if(act.showBall) println(c,"Acceleration peek: " + Vect3d.Round(act.accPeek,1000f)+" m/s²"); if(act.light!=-1) println(c,"Light: " + act.light+" lux"); if(act.proximity!=-1) println(c,"Proximity: " + act.proximity); //println(c,"Earth: " + act.orientEarth); //println(c,"Magnet: " + act.orientMagnet); super.onDraw(c); } } enum Shape { NEEDLE_UP, NEEDLE_BICUSPID, SPHERE, NEEDLE_HALF, RING_M, SCALE_M, RING_O, SCALE_O, RING_I, SCALE_I } class MyRenderer implements GLSurfaceView.Renderer { SensorsActivity act; public Body spere; private Body needleUp; private Body needleBicuspid; private Body needleHalf; private Body ring_m; private Body scale_m; private Body ring_o; private Body scale_o; private Body ring_i; private Body scale_i; public MyRenderer(SensorsActivity act) { this.act = act; spere = new Body(act,Shape.SPHERE); needleUp = new Body(act,Shape.NEEDLE_UP); needleBicuspid = new Body(act,Shape.NEEDLE_BICUSPID); needleHalf = new Body(act,Shape.NEEDLE_HALF); ring_m = new Body(act,Shape.RING_M); ring_o = new Body(act,Shape.RING_O); ring_i = new Body(act,Shape.RING_I); scale_m = new Body(act,Shape.SCALE_M); scale_o = new Body(act,Shape.SCALE_O); scale_i = new Body(act,Shape.SCALE_I); //log.v(); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glDisable(GL10.GL_DITHER); gl.glEnable(GL10.GL_BLEND); gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST); gl.glClearColor(0, 0, 0, 0); gl.glEnable(GL10.GL_CULL_FACE); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST); } public void onSurfaceChanged(GL10 gl, int width, int height) { //log.d("w: " + width + " h: " + height); int size = Math.min(width, height); gl.glViewport(width - size, height - size, size, size); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); gl.glFrustumf(-0.5f, 0.5f, -0.5f, 0.5f, 1f, 5); } Vect3d axisSphere = new Vect3d(1,0,0); Vect3d axisSphereD = new Vect3d(0.002317f,0.009151f,0.001323f); float angleSphere = 0; float angleSphereD = 9.2327f; float angleSphereDD = 0.2329f; final float o_toggle_limit = 0.5f; final float o_hide_limit = 0.1f; boolean o_up = true; public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glTranslatef(0, 0, -3f); // shift viewpoint gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); gl.glFrontFace(GL10.GL_CW); gl.glPushMatrix(); rotate_m(gl, act.orientMagnet); needleHalf.modifyVertex(14, Math.atan(act.magnetSize / 45d) * 2.6 / Math.PI); needleHalf.draw(gl); gl.glPopMatrix(); if(act.showBall) { gl.glPushMatrix(); gl.glTranslatef(act.acceleration.x * 0.3f, act.acceleration.y * 0.3f, act.acceleration.z * 0.3f); angleSphereD+=angleSphereDD; if(angleSphereD>18||angleSphereD<2)angleSphereDD*=-1; angleSphere+=angleSphereD; if(angleSphere>360)angleSphere-=360; axisSphere = axisSphere.add(axisSphereD); if(axisSphere.x>1||axisSphere.x<-1)axisSphereD.x*=-1; if(axisSphere.y>1||axisSphere.y<-1)axisSphereD.y*=-1; if(axisSphere.z>1||axisSphere.z<-1)axisSphereD.z*=-1; gl.glRotatef(angleSphere, axisSphere.x,axisSphere.y,axisSphere.z); spere.draw(gl); gl.glPopMatrix(); } gl.glPushMatrix(); rotate_m(gl, act.orientEarth); scale_m.draw(gl); ring_m.draw(gl); gl.glPopMatrix(); float z = act.orientEarth.z; boolean o_hide = false; if( o_up ) { if( z<-o_toggle_limit ) o_up = false; else if( z<-o_hide_limit ) o_hide = true; } else { if( z>o_toggle_limit ) o_up = true; else if( z>o_hide_limit ) o_hide = true; } gl.glPushMatrix(); rotate_o(gl,o_up); if( !o_hide ) scale_o.draw(gl); ring_o.draw(gl); gl.glPopMatrix(); gl.glPushMatrix(); rotate_i(gl); needleUp.draw(gl); needleBicuspid.draw(gl); scale_i.draw(gl); ring_i.draw(gl); gl.glPopMatrix(); gl.glFlush(); } // outer scale (PDA lie) private void rotate_o(GL10 gl,boolean up) { Vect3d v = up ? act.orientEarth : act.orientEarth.mul(-1); float axy = (float) (Math.atan2(v.x, v.y) * 180 / Math.PI); float xz = (float) (Math.acos(v.z) * 180 / Math.PI); gl.glRotatef( axy, 0, 0, -1); gl.glRotatef( xz, -1, 0, 0); gl.glRotatef( axy, 0, 0, 1); } // magnet field direction, middle scale (with ring) private void rotate_m(GL10 gl, Vect3d v) { float axy = (float) (Math.atan2(v.x, v.y) * 180 / Math.PI); float xz = (float) (Math.acos(v.z) * 180 / Math.PI); gl.glRotatef( axy, 0, 0, -1); gl.glRotatef( xz, -1, 0, 0); } // compass, and it inner scale, and up direction private void rotate_i(GL10 gl) { Vect3d v = act.orientEarth; Vect3d ez = new Vect3d(0,0,1); Vect3d ax = v.add(ez).Norm(); boolean n = ax.abs()==0; if( n ){ // TODO not tested yet gl.glRotatef( 180, 1, 1, 0); gl.glRotatef( 180, ez.x, ez.y, ez.z); act.sign = true; } else { gl.glRotatef( 180, ax.x, ax.y, ax.z); gl.glRotatef( 180, ez.x, ez.y, ez.z); } Vect3d m = act.magnetXearth; Vect3d h = ax.mul(ax.mulS(m)); m = h.sub(m.sub(h)); float axy = (float) (Math.atan2(m.x, m.y) * -180 / Math.PI); gl.glRotatef( axy+180, 0, 0, 1); } } class Body { SensorsActivity act; private int indicescount; private IntBuffer mVertexBuffer; private IntBuffer mColorBuffer; private ShortBuffer mIndexBuffer; private boolean rot1,rot2; final int one = 0x10000; final int o25_26 = 25 * one / 26; final int o7_8 = 7 * one / 8; final int o5_6 = 5 * one / 6; final int o3_4 = 3 * one / 4; final int o2_3 = 2 * one / 3; final int o5_8 = 5 * one / 8; final int o_2 = one / 2; final int o3_8 = 3 * one / 8; final int o_3 = one / 3; final int o_4 = one / 4; final int o_6 = one / 6; final int o_8 = one / 8; final int o_10 = one / 10; final int o_16 = one / 16; final int o_20 = one / 20; final int o_32 = one / 32; final int o_40 = one / 40; final int o_64 = one / 64; final int o_88 = one / 88; int vnum; Vect3d sph_v[] = null; int shp_bw[][] = null; final int vertices_needleH[] = { -o_16, -o_16, o_16, o_16, -o_16, o_16, o_16, o_16, o_16, -o_16, o_16, o_16, 0, 0, o3_4 }; final int colors_needleH[] = { one, o_2, 0, o3_4, one, o_2, 0, o3_4, one, o_2, 0, o3_4, one, o_2, 0, o3_4, o_2, o_2, o_2, o3_4 }; final short indices_needleH[] = { 0, 4, 1, 1, 4, 2, 2, 4, 3, 3, 4, 0, 0, 1, 2, 2, 3, 0 }; final int vertices_needle2[] = { 0, 0, o_20-one, -o_8, -o_32, 0, o_8, -o_32, 0, o_8, o_32, 0, -o_8, o_32, 0, 0, 0, one-o_20 }; final int colors_needle2[] = { one, 0, 0, o3_4, o_2, 0, o_2, o3_4, o_2, 0, o_2, o3_4, o_2, 0, o_2, o3_4, o_2, 0, o_2, o3_4, 0, o_2, one, o3_4, }; final short indices_needle2[] = { 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 1, 1, 5, 2, 2, 5, 3, 3, 5, 4, 4, 5, 1 }; public Body(SensorsActivity act, Shape sh) { int vertices[] = null; int colors[] = null; short indices[] = null; this.act = act; int edges, c = 0, hh=o_16; double mul = 0.94; rot1 = sh==Shape.NEEDLE_BICUSPID; rot2 = sh==Shape.SCALE_I||sh==Shape.RING_I; switch (sh) { case NEEDLE_BICUSPID: vertices = vertices_needle2; colors = colors_needle2; indices = indices_needle2; break; case NEEDLE_HALF: vertices = vertices_needleH; colors = colors_needleH; indices = indices_needleH; break; case NEEDLE_UP: edges = 12; mVertexBuffer = intBuffer((2 + edges * 7)*3); vx( 0, 0, one); // hegyes csúcs vx( 0, 0, -one); // bunkós vég for (int i = 0; i < edges; i++) { double a = 2 * i * Math.PI / edges; int x = (int) (o_6 * Math.sin(a)); int y = (int) (o_6 * Math.cos(a)); vx( x/2, y/2, -o25_26); vx( 8*x/9, 8*y/9, -o7_8); vx( x, y, -o5_6); // legszélesebb rész vx( 4*x/9, 4*y/9, -o_3); vx( x/3, y/3, 0); // közepe vx( 2*x/9, 2*y/9, o_3); vx( x/9, y/9, o2_3); } mColorBuffer = intBuffer((2 + edges * 7)*4); int al = o3_4; col( one, one, one, one); col( o7_8, o7_8, o7_8, al); for (int i = 0; i < edges; i++) { int j=i%3; int c0=j==0?o5_8:o3_8; int c1=j==1?o5_8:o3_8; int c2=j==2?o5_8:o3_8; col( c2, c0, c1, al); col( c0, c1, c2, al); col( c1, c2, c0, al); col( c2, c0, c1, al); col( c0, c1, c2, al); col( c1, c2, c0, al); col( c2, c0, c1, al); } indicescount = edges * 2 * 7 * 3; mIndexBuffer = shortBuffer(indicescount); for (int i = 0; i < edges; i++) { int j = i * 7 + 2; int k = ((i == 0 ? edges : i) - 1) * 7 + 2; idx( 1, j, k); idx( j, j+1, k); idx( k, j+1, k+1); idx( j+1, j+2, k+1); idx( k+1, j+2, k+2); idx( j+2, j+3, k+2); idx( k+2, j+3, k+3); idx( j+3, j+4, k+3); idx( k+3, j+4, k+4); idx( j+4, j+5, k+4); idx( k+4, j+5, k+5); idx( j+5, j+6, k+5); idx( k+5, j+6, k+6); idx( j+6, 0, k+6); } return; case RING_O: mul += 0.08; hh = hh/2; case RING_M: mul += 0.08; hh = 4*hh/6; case RING_I: edges = 64; mVertexBuffer = intBuffer(edges * 4 * 3); mColorBuffer = intBuffer(edges * 4 * 4); indicescount = edges * 8 * 3; mIndexBuffer = shortBuffer(indicescount); for (int i = 0; i < edges; i++) { double a = 2 * i * Math.PI / edges; int x = (int) (one * Math.sin(a)*mul), x1 = (int) (x * 1.06); int y = (int) (one * Math.cos(a)*mul), y1 = (int) (y * 1.06); vx( x, y, + hh); vx( x, y, - hh); vx( x1, y1, + hh); vx( x1, y1, - hh); col( o_2, c, o_2, o_2); col( o_2, c, o_2, o_2); col( o_2, o_2, c, o_2); col( o_2, o_2, c, o_2); c = o_2 - c; } for (int i = 0; i < edges; i++) { int j = i * 4; int k = ((i == 0 ? edges : i) - 1) * 4; idx( j, j + 1, k); idx( j + 1, k + 1, k); idx( j + 2, j, k + 2); idx( j, k, k + 2); idx( j + 3, j + 2, k + 3); idx( j + 2, k + 2, k + 3); idx( j + 1, j + 3, k + 1); idx( j + 3, k + 3, k + 1); } return; case SCALE_O: mul += 0.08; hh = hh/2; case SCALE_M: mul += 0.08; hh = hh/2; case SCALE_I: edges = 36; hh += 8; mVertexBuffer = intBuffer(edges * 8 * 3); mColorBuffer = intBuffer(edges * 8 * 4); indicescount = edges * 8 * 3; mIndexBuffer = shortBuffer(indicescount); for (int i = 0; i < edges; i++) { double a = 2 * i * Math.PI / edges; double b = (i % 9) == 0 ? 0.025 : 0.015; double d = (i % 9) == 0 ? 1.08 : 1.06; int x0 = (int) (one * Math.sin(a - b)*mul), x1 = (int) (x0 * d); int y0 = (int) (one * Math.cos(a - b)*mul), y1 = (int) (y0 * d); int x2 = (int) (one * Math.sin(a + b)*mul), x3 = (int) (x2 * d); int y2 = (int) (one * Math.cos(a + b)*mul), y3 = (int) (y2 * d); vx( x0, y0, + hh); vx( x1, y1, + hh); vx( x2, y2, + hh); vx( x3, y3, + hh); vx( x0, y0, - hh); vx( x1, y1, - hh); vx( x2, y2, - hh); vx( x3, y3, - hh); } final int col[][] = { { o_2, o_2, one }, { o3_4, 0, 0 }, { one, one, 0 }, { o_2, o_2, 0 }, { 0, o_2, 0 } }; for (int i = 0; i < edges; i++) { c = i == 18 ? 0 : (i == 9?4:(((i % 18) == 0) ? 1 : (((i % 3) == 0) ? 2 : 3))); for (int k = 0; k < 8; k++) { col( col[c][0], col[c][1], col[c][2], o3_4); } } for (int i = 0; i < edges; i++) { int j = i * 8; idx( j + 0, j + 1, j + 2); idx( j + 2, j + 1, j + 3); idx( j + 4, j + 6, j + 5); idx( j + 5, j + 6, j + 7); idx( j + 3, j + 1, j + 7); idx( j + 7, j + 1, j + 5); idx( j + 0, j + 2, j + 6); idx( j + 0, j + 6, j + 4); } return; case SPHERE: { vnum = 12; // vertices of icosahedron edges = 30; // edges of icosahedron indicescount = 20;// faces of icosahedron for(int i=0; ij){int t=i;i=j;j=t;}// sort for(int k=12;k