From 9474e6e25e7da48259a26f7a94fe017b8bc5996a Mon Sep 17 00:00:00 2001 From: aNNiMON Date: Tue, 13 Feb 2024 23:58:29 +0200 Subject: [PATCH] Initial commit --- .classpath | 8 + .project | 33 + AndroidManifest.xml | 34 + proguard-project.txt | 5 + project.properties | 14 + res/drawable-hdpi/ic_launcher.png | Bin 0 -> 12859 bytes res/drawable-ldpi/ic_launcher.png | Bin 0 -> 3391 bytes res/drawable-mdpi/ic_launcher.png | Bin 0 -> 4779 bytes res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 21926 bytes res/values-ru/strings.xml | 30 + res/values/strings.xml | 30 + res/xml/visualme.xml | 54 + src/com/annimon/visualme/FastMath.java | 1046 +++++++++++++++++ src/com/annimon/visualme/Fps.java | 35 + src/com/annimon/visualme/GameSurfaceView.java | 146 +++ src/com/annimon/visualme/MainActivity.java | 23 + .../annimon/visualme/SettingsActivity.java | 14 + src/com/annimon/visualme/visuals/Visual.java | 24 + .../annimon/visualme/visuals/VisualME.java | 403 +++++++ 19 files changed, 1899 insertions(+) create mode 100644 .classpath create mode 100644 .project create mode 100644 AndroidManifest.xml create mode 100644 proguard-project.txt create mode 100644 project.properties create mode 100644 res/drawable-hdpi/ic_launcher.png create mode 100644 res/drawable-ldpi/ic_launcher.png create mode 100644 res/drawable-mdpi/ic_launcher.png create mode 100644 res/drawable-xhdpi/ic_launcher.png create mode 100644 res/values-ru/strings.xml create mode 100644 res/values/strings.xml create mode 100644 res/xml/visualme.xml create mode 100644 src/com/annimon/visualme/FastMath.java create mode 100644 src/com/annimon/visualme/Fps.java create mode 100644 src/com/annimon/visualme/GameSurfaceView.java create mode 100644 src/com/annimon/visualme/MainActivity.java create mode 100644 src/com/annimon/visualme/SettingsActivity.java create mode 100644 src/com/annimon/visualme/visuals/Visual.java create mode 100644 src/com/annimon/visualme/visuals/VisualME.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..4160c21 --- /dev/null +++ b/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..a2c0547 --- /dev/null +++ b/.project @@ -0,0 +1,33 @@ + + + VisualME + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..f9e4d0e --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/proguard-project.txt b/proguard-project.txt new file mode 100644 index 0000000..31ac955 --- /dev/null +++ b/proguard-project.txt @@ -0,0 +1,5 @@ +@E:\\SETUPS\\Disk\\Programming\\Java\\android.pro +-obfuscationdictionary E:\\SETUPS\\Disk\\Programming\\Java\\compact.txt +-optimizationpasses 9 +-allowaccessmodification +-overloadaggressively \ No newline at end of file diff --git a/project.properties b/project.properties new file mode 100644 index 0000000..e22da9e --- /dev/null +++ b/project.properties @@ -0,0 +1,14 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-16 diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..7a0af234498afc43ba279b6fa3c4a11c7ef558f5 GIT binary patch literal 12859 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 V2?ak>!3+QZ002ovPDHLkV1m!GYySWM literal 0 HcmV?d00001 diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b6432023b59786ab3d20b17796aa95c82b462dc8 GIT binary patch literal 4779 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 literal 0 HcmV?d00001 diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml new file mode 100644 index 0000000..55bc485 --- /dev/null +++ b/res/values-ru/strings.xml @@ -0,0 +1,30 @@ + + + + VisualME + + Запустить VisualME + Показывать fps + Количество ветвей + Гибкость + Количество звеньев в линии + Пропорции экрана + Скорость + Автосмена визуализаций? + Автоматически изменять режим визуализации? + Автосмена фигур? + Автоматически изменять тип рисуемых фигур? + Тип фигуры + + + Линия + Эллипс + Квадрат + + + 0 + 1 + 2 + + + \ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..11a4e7c --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,30 @@ + + + + VisualME + + Run VisualME + Show fps + Number of branches + Flexibility + Number of elements in line + Screen proportions + Speed + Change visualization? + Automatically change visualization mode? + Change figures? + Automatically change drawing figures? + Shape type + + + Line + Ellipse + Square + + + 0 + 1 + 2 + + + \ No newline at end of file diff --git a/res/xml/visualme.xml b/res/xml/visualme.xml new file mode 100644 index 0000000..eed7449 --- /dev/null +++ b/res/xml/visualme.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/com/annimon/visualme/FastMath.java b/src/com/annimon/visualme/FastMath.java new file mode 100644 index 0000000..b713135 --- /dev/null +++ b/src/com/annimon/visualme/FastMath.java @@ -0,0 +1,1046 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.annimon.visualme; + + +/** + * Faster, more accurate, portable alternative to {@link Math} and + * {@link StrictMath} for large scale computation. + *

+ * FastMath is a drop-in replacement for both Math and StrictMath. This + * means that for any method in Math (say {@code Math.sin(x)} or + * {@code Math.cbrt(y)}), user can directly change the class and use the + * methods as is (using {@code FastMath.sin(x)} or {@code FastMath.cbrt(y)} + * in the previous example). + *

+ *

+ * FastMath speed is achieved by relying heavily on optimizing compilers + * to native code present in many JVMs today and use of large tables. + * The larger tables are lazily initialised on first use, so that the setup + * time does not penalise methods that don't need them. + *

+ *

+ * Note that FastMath is + * extensively used inside Apache Commons Math, so by calling some algorithms, + * the overhead when the the tables need to be intialised will occur + * regardless of the end-user calling FastMath methods directly or not. + * Performance figures for a specific JVM and hardware can be evaluated by + * running the FastMathTestPerformance tests in the test directory of the source + * distribution. + *

+ *

+ * FastMath accuracy should be mostly independent of the JVM as it relies only + * on IEEE-754 basic operations and on embedded tables. Almost all operations + * are accurate to about 0.5 ulp throughout the domain range. This statement, + * of course is only a rough global observed behavior, it is not a + * guarantee for every double numbers input (see William Kahan's Table + * Maker's Dilemma). + *

+ *

+ * FastMath additionally implements the following methods not found in Math/StrictMath: + *

    + *
  • {@link #asinh(double)}
  • + *
  • {@link #acosh(double)}
  • + *
  • {@link #atanh(double)}
  • + *
+ * The following methods are found in Math/StrictMath since 1.6 only, they are provided + * by FastMath even in 1.5 Java virtual machines + *
    + *
  • {@link #copySign(double, double)}
  • + *
  • {@link #getExponent(double)}
  • + *
  • {@link #nextAfter(double,double)}
  • + *
  • {@link #nextUp(double)}
  • + *
  • {@link #scalb(double, int)}
  • + *
  • {@link #copySign(float, float)}
  • + *
  • {@link #getExponent(float)}
  • + *
  • {@link #nextAfter(float,double)}
  • + *
  • {@link #nextUp(float)}
  • + *
  • {@link #scalb(float, int)}
  • + *
+ *

+ * @version $Id: FastMath.java 1422313 2012-12-15 18:53:41Z psteitz $ + * @since 2.2 + */ +public class FastMath { + /** Archimede's constant PI, ratio of circle circumference to diameter. */ + public static final double PI = 105414357.0 / 33554432.0 + 1.984187159361080883e-9; + + /** Napier's constant e, base of the natural logarithm. */ + public static final double E = 2850325.0 / 1048576.0 + 8.254840070411028747e-8; + + /** Index of exp(0) in the array of integer exponentials. */ + static final int EXP_INT_TABLE_MAX_INDEX = 750; + /** Length of the array of integer exponentials. */ + static final int EXP_INT_TABLE_LEN = EXP_INT_TABLE_MAX_INDEX * 2; + /** Logarithm table length. */ + static final int LN_MANT_LEN = 1024; + /** Exponential fractions table length. */ + static final int EXP_FRAC_TABLE_LEN = 1025; // 0, 1/1024, ... 1024/1024 + + + /** Sine table (high bits). */ + private static final double SINE_TABLE_A[] = + { + +0.0d, + +0.1246747374534607d, + +0.24740394949913025d, + +0.366272509098053d, + +0.4794255495071411d, + +0.5850973129272461d, + +0.6816387176513672d, + +0.7675435543060303d, + +0.8414709568023682d, + +0.902267575263977d, + +0.9489846229553223d, + +0.9808930158615112d, + +0.9974949359893799d, + +0.9985313415527344d, + }; + + /** Sine table (low bits). */ + private static final double SINE_TABLE_B[] = + { + +0.0d, + -4.068233003401932E-9d, + +9.755392680573412E-9d, + +1.9987994582857286E-8d, + -1.0902938113007961E-8d, + -3.9986783938944604E-8d, + +4.23719669792332E-8d, + -5.207000323380292E-8d, + +2.800552834259E-8d, + +1.883511811213715E-8d, + -3.5997360512765566E-9d, + +4.116164446561962E-8d, + +5.0614674548127384E-8d, + -1.0129027912496858E-9d, + }; + + /** Cosine table (high bits). */ + private static final double COSINE_TABLE_A[] = + { + +1.0d, + +0.9921976327896118d, + +0.9689123630523682d, + +0.9305076599121094d, + +0.8775825500488281d, + +0.8109631538391113d, + +0.7316888570785522d, + +0.6409968137741089d, + +0.5403022766113281d, + +0.4311765432357788d, + +0.3153223395347595d, + +0.19454771280288696d, + +0.07073719799518585d, + -0.05417713522911072d, + }; + + /** Cosine table (low bits). */ + private static final double COSINE_TABLE_B[] = + { + +0.0d, + +3.4439717236742845E-8d, + +5.865827662008209E-8d, + -3.7999795083850525E-8d, + +1.184154459111628E-8d, + -3.43338934259355E-8d, + +1.1795268640216787E-8d, + +4.438921624363781E-8d, + +2.925681159240093E-8d, + -2.6437112632041807E-8d, + +2.2860509143963117E-8d, + -4.813899778443457E-9d, + +3.6725170580355583E-9d, + +2.0217439756338078E-10d, + }; + + + /** Bits of 1/(2*pi), need for reducePayneHanek(). */ + private static final long RECIP_2PI[] = new long[] { + (0x28be60dbL << 32) | 0x9391054aL, + (0x7f09d5f4L << 32) | 0x7d4d3770L, + (0x36d8a566L << 32) | 0x4f10e410L, + (0x7f9458eaL << 32) | 0xf7aef158L, + (0x6dc91b8eL << 32) | 0x909374b8L, + (0x01924bbaL << 32) | 0x82746487L, + (0x3f877ac7L << 32) | 0x2c4a69cfL, + (0xba208d7dL << 32) | 0x4baed121L, + (0x3a671c09L << 32) | 0xad17df90L, + (0x4e64758eL << 32) | 0x60d4ce7dL, + (0x272117e2L << 32) | 0xef7e4a0eL, + (0xc7fe25ffL << 32) | 0xf7816603L, + (0xfbcbc462L << 32) | 0xd6829b47L, + (0xdb4d9fb3L << 32) | 0xc9f2c26dL, + (0xd3d18fd9L << 32) | 0xa797fa8bL, + (0x5d49eeb1L << 32) | 0xfaf97c5eL, + (0xcf41ce7dL << 32) | 0xe294a4baL, + 0x9afed7ecL << 32 }; + + /** Bits of pi/4, need for reducePayneHanek(). */ + private static final long PI_O_4_BITS[] = new long[] { + (0xc90fdaa2L << 32) | 0x2168c234L, + (0xc4c6628bL << 32) | 0x80dc1cd1L }; + + /** Eighth's. + * This is used by sinQ, because its faster to do a table lookup than + * a multiply in this time-critical routine + */ + private static final double EIGHTHS[] = {0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625}; + + /* + * There are 52 bits in the mantissa of a double. + * For additional precision, the code splits double numbers into two parts, + * by clearing the low order 30 bits if possible, and then performs the arithmetic + * on each half separately. + */ + + /** + * 0x40000000 - used to split a double into two parts, both with the low order bits cleared. + * Equivalent to 2^30. + */ + private static final long HEX_40000000 = 0x40000000L; // 1073741824L + + /** 2^52 - double numbers this large must be integral (no fraction) or NaN or Infinite */ + private static final double TWO_POWER_52 = 4503599627370496.0; + + /** + * Private Constructor + */ + private FastMath() {} + + // Generic helper methods + + + /** + * Computes sin(x) - x, where |x| < 1/16. + * Use a Remez polynomial approximation. + * @param x a number smaller than 1/16 + * @return sin(x) - x + */ + private static double polySine(final double x) + { + double x2 = x*x; + + double p = 2.7553817452272217E-6; + p = p * x2 + -1.9841269659586505E-4; + p = p * x2 + 0.008333333333329196; + p = p * x2 + -0.16666666666666666; + //p *= x2; + //p *= x; + p = p * x2 * x; + + return p; + } + + /** + * Computes cos(x) - 1, where |x| < 1/16. + * Use a Remez polynomial approximation. + * @param x a number smaller than 1/16 + * @return cos(x) - 1 + */ + private static double polyCosine(double x) { + double x2 = x*x; + + double p = 2.479773539153719E-5; + p = p * x2 + -0.0013888888689039883; + p = p * x2 + 0.041666666666621166; + p = p * x2 + -0.49999999999999994; + p *= x2; + + return p; + } + + /** + * Compute sine over the first quadrant (0 < x < pi/2). + * Use combination of table lookup and rational polynomial expansion. + * @param xa number from which sine is requested + * @param xb extra bits for x (may be 0.0) + * @return sin(xa + xb) + */ + private static double sinQ(double xa, double xb) { + int idx = (int) ((xa * 8.0) + 0.5); + final double epsilon = xa - EIGHTHS[idx]; //idx*0.125; + + // Table lookups + final double sintA = SINE_TABLE_A[idx]; + final double sintB = SINE_TABLE_B[idx]; + final double costA = COSINE_TABLE_A[idx]; + final double costB = COSINE_TABLE_B[idx]; + + // Polynomial eval of sin(epsilon), cos(epsilon) + double sinEpsA = epsilon; + double sinEpsB = polySine(epsilon); + final double cosEpsA = 1.0; + final double cosEpsB = polyCosine(epsilon); + + // Split epsilon xa + xb = x + final double temp = sinEpsA * HEX_40000000; + double temp2 = (sinEpsA + temp) - temp; + sinEpsB += sinEpsA - temp2; + sinEpsA = temp2; + + /* Compute sin(x) by angle addition formula */ + double result; + + /* Compute the following sum: + * + * result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB + + * sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB; + * + * Ranges of elements + * + * xxxtA 0 PI/2 + * xxxtB -1.5e-9 1.5e-9 + * sinEpsA -0.0625 0.0625 + * sinEpsB -6e-11 6e-11 + * cosEpsA 1.0 + * cosEpsB 0 -0.0625 + * + */ + + //result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB + + // sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB; + + //result = sintA + sintA*cosEpsB + sintB + sintB * cosEpsB; + //result += costA*sinEpsA + costA*sinEpsB + costB*sinEpsA + costB * sinEpsB; + double a = 0; + double b = 0; + + double t = sintA; + double c = a + t; + double d = -(c - a - t); + a = c; + b = b + d; + + t = costA * sinEpsA; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + b = b + sintA * cosEpsB + costA * sinEpsB; + /* + t = sintA*cosEpsB; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + t = costA*sinEpsB; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + */ + + b = b + sintB + costB * sinEpsA + sintB * cosEpsB + costB * sinEpsB; + /* + t = sintB; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + t = costB*sinEpsA; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + t = sintB*cosEpsB; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + t = costB*sinEpsB; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + */ + + if (xb != 0.0) { + t = ((costA + costB) * (cosEpsA + cosEpsB) - + (sintA + sintB) * (sinEpsA + sinEpsB)) * xb; // approximate cosine*xb + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + } + + result = a + b; + + return result; + } + + /** + * Compute cosine in the first quadrant by subtracting input from PI/2 and + * then calling sinQ. This is more accurate as the input approaches PI/2. + * @param xa number from which cosine is requested + * @param xb extra bits for x (may be 0.0) + * @return cos(xa + xb) + */ + private static double cosQ(double xa, double xb) { + final double pi2a = 1.5707963267948966; + final double pi2b = 6.123233995736766E-17; + + final double a = pi2a - xa; + double b = -(a - pi2a + xa); + b += pi2b - xb; + + return sinQ(a, b); + } + + /** + * Compute tangent (or cotangent) over the first quadrant. 0 < x < pi/2 + * Use combination of table lookup and rational polynomial expansion. + * @param xa number from which sine is requested + * @param xb extra bits for x (may be 0.0) + * @param cotanFlag if true, compute the cotangent instead of the tangent + * @return tan(xa+xb) (or cotangent, depending on cotanFlag) + */ + private static double tanQ(double xa, double xb, boolean cotanFlag) { + + int idx = (int) ((xa * 8.0) + 0.5); + final double epsilon = xa - EIGHTHS[idx]; //idx*0.125; + + // Table lookups + final double sintA = SINE_TABLE_A[idx]; + final double sintB = SINE_TABLE_B[idx]; + final double costA = COSINE_TABLE_A[idx]; + final double costB = COSINE_TABLE_B[idx]; + + // Polynomial eval of sin(epsilon), cos(epsilon) + double sinEpsA = epsilon; + double sinEpsB = polySine(epsilon); + final double cosEpsA = 1.0; + final double cosEpsB = polyCosine(epsilon); + + // Split epsilon xa + xb = x + double temp = sinEpsA * HEX_40000000; + double temp2 = (sinEpsA + temp) - temp; + sinEpsB += sinEpsA - temp2; + sinEpsA = temp2; + + /* Compute sin(x) by angle addition formula */ + + /* Compute the following sum: + * + * result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB + + * sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB; + * + * Ranges of elements + * + * xxxtA 0 PI/2 + * xxxtB -1.5e-9 1.5e-9 + * sinEpsA -0.0625 0.0625 + * sinEpsB -6e-11 6e-11 + * cosEpsA 1.0 + * cosEpsB 0 -0.0625 + * + */ + + //result = sintA + costA*sinEpsA + sintA*cosEpsB + costA*sinEpsB + + // sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB; + + //result = sintA + sintA*cosEpsB + sintB + sintB * cosEpsB; + //result += costA*sinEpsA + costA*sinEpsB + costB*sinEpsA + costB * sinEpsB; + double a = 0; + double b = 0; + + // Compute sine + double t = sintA; + double c = a + t; + double d = -(c - a - t); + a = c; + b = b + d; + + t = costA*sinEpsA; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + b = b + sintA*cosEpsB + costA*sinEpsB; + b = b + sintB + costB*sinEpsA + sintB*cosEpsB + costB*sinEpsB; + + double sina = a + b; + double sinb = -(sina - a - b); + + // Compute cosine + + a = b = c = d = 0.0; + + t = costA*cosEpsA; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + t = -sintA*sinEpsA; + c = a + t; + d = -(c - a - t); + a = c; + b = b + d; + + b = b + costB*cosEpsA + costA*cosEpsB + costB*cosEpsB; + b = b - (sintB*sinEpsA + sintA*sinEpsB + sintB*sinEpsB); + + double cosa = a + b; + double cosb = -(cosa - a - b); + + if (cotanFlag) { + double tmp; + tmp = cosa; cosa = sina; sina = tmp; + tmp = cosb; cosb = sinb; sinb = tmp; + } + + + /* estimate and correct, compute 1.0/(cosa+cosb) */ + /* + double est = (sina+sinb)/(cosa+cosb); + double err = (sina - cosa*est) + (sinb - cosb*est); + est += err/(cosa+cosb); + err = (sina - cosa*est) + (sinb - cosb*est); + */ + + // f(x) = 1/x, f'(x) = -1/x^2 + + double est = sina/cosa; + + /* Split the estimate to get more accurate read on division rounding */ + temp = est * HEX_40000000; + double esta = (est + temp) - temp; + double estb = est - esta; + + temp = cosa * HEX_40000000; + double cosaa = (cosa + temp) - temp; + double cosab = cosa - cosaa; + + //double err = (sina - est*cosa)/cosa; // Correction for division rounding + double err = (sina - esta*cosaa - esta*cosab - estb*cosaa - estb*cosab)/cosa; // Correction for division rounding + err += sinb/cosa; // Change in est due to sinb + err += -sina * cosb / cosa / cosa; // Change in est due to cosb + + if (xb != 0.0) { + // tan' = 1 + tan^2 cot' = -(1 + cot^2) + // Approximate impact of xb + double xbadj = xb + est*est*xb; + if (cotanFlag) { + xbadj = -xbadj; + } + + err += xbadj; + } + + return est+err; + } + + /** Reduce the input argument using the Payne and Hanek method. + * This is good for all inputs 0.0 < x < inf + * Output is remainder after dividing by PI/2 + * The result array should contain 3 numbers. + * result[0] is the integer portion, so mod 4 this gives the quadrant. + * result[1] is the upper bits of the remainder + * result[2] is the lower bits of the remainder + * + * @param x number to reduce + * @param result placeholder where to put the result + */ + private static void reducePayneHanek(double x, double result[]) + { + /* Convert input double to bits */ + long inbits = Double.doubleToLongBits(x); + int exponent = (int) ((inbits >> 52) & 0x7ff) - 1023; + + /* Convert to fixed point representation */ + inbits &= 0x000fffffffffffffL; + inbits |= 0x0010000000000000L; + + /* Normalize input to be between 0.5 and 1.0 */ + exponent++; + inbits <<= 11; + + /* Based on the exponent, get a shifted copy of recip2pi */ + long shpi0; + long shpiA; + long shpiB; + int idx = exponent >> 6; + int shift = exponent - (idx << 6); + + if (shift != 0) { + shpi0 = (idx == 0) ? 0 : (RECIP_2PI[idx-1] << shift); + shpi0 |= RECIP_2PI[idx] >>> (64-shift); + shpiA = (RECIP_2PI[idx] << shift) | (RECIP_2PI[idx+1] >>> (64-shift)); + shpiB = (RECIP_2PI[idx+1] << shift) | (RECIP_2PI[idx+2] >>> (64-shift)); + } else { + shpi0 = (idx == 0) ? 0 : RECIP_2PI[idx-1]; + shpiA = RECIP_2PI[idx]; + shpiB = RECIP_2PI[idx+1]; + } + + /* Multiply input by shpiA */ + long a = inbits >>> 32; + long b = inbits & 0xffffffffL; + + long c = shpiA >>> 32; + long d = shpiA & 0xffffffffL; + + long ac = a * c; + long bd = b * d; + long bc = b * c; + long ad = a * d; + + long prodB = bd + (ad << 32); + long prodA = ac + (ad >>> 32); + + boolean bita = (bd & 0x8000000000000000L) != 0; + boolean bitb = (ad & 0x80000000L ) != 0; + boolean bitsum = (prodB & 0x8000000000000000L) != 0; + + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prodA++; + } + + bita = (prodB & 0x8000000000000000L) != 0; + bitb = (bc & 0x80000000L ) != 0; + + prodB = prodB + (bc << 32); + prodA = prodA + (bc >>> 32); + + bitsum = (prodB & 0x8000000000000000L) != 0; + + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prodA++; + } + + /* Multiply input by shpiB */ + c = shpiB >>> 32; + d = shpiB & 0xffffffffL; + ac = a * c; + bc = b * c; + ad = a * d; + + /* Collect terms */ + ac = ac + ((bc + ad) >>> 32); + + bita = (prodB & 0x8000000000000000L) != 0; + bitb = (ac & 0x8000000000000000L ) != 0; + prodB += ac; + bitsum = (prodB & 0x8000000000000000L) != 0; + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prodA++; + } + + /* Multiply by shpi0 */ + c = shpi0 >>> 32; + d = shpi0 & 0xffffffffL; + + bd = b * d; + bc = b * c; + ad = a * d; + + prodA += bd + ((bc + ad) << 32); + + /* + * prodA, prodB now contain the remainder as a fraction of PI. We want this as a fraction of + * PI/2, so use the following steps: + * 1.) multiply by 4. + * 2.) do a fixed point muliply by PI/4. + * 3.) Convert to floating point. + * 4.) Multiply by 2 + */ + + /* This identifies the quadrant */ + int intPart = (int)(prodA >>> 62); + + /* Multiply by 4 */ + prodA <<= 2; + prodA |= prodB >>> 62; + prodB <<= 2; + + /* Multiply by PI/4 */ + a = prodA >>> 32; + b = prodA & 0xffffffffL; + + c = PI_O_4_BITS[0] >>> 32; + d = PI_O_4_BITS[0] & 0xffffffffL; + + ac = a * c; + bd = b * d; + bc = b * c; + ad = a * d; + + long prod2B = bd + (ad << 32); + long prod2A = ac + (ad >>> 32); + + bita = (bd & 0x8000000000000000L) != 0; + bitb = (ad & 0x80000000L ) != 0; + bitsum = (prod2B & 0x8000000000000000L) != 0; + + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prod2A++; + } + + bita = (prod2B & 0x8000000000000000L) != 0; + bitb = (bc & 0x80000000L ) != 0; + + prod2B = prod2B + (bc << 32); + prod2A = prod2A + (bc >>> 32); + + bitsum = (prod2B & 0x8000000000000000L) != 0; + + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prod2A++; + } + + /* Multiply input by pio4bits[1] */ + c = PI_O_4_BITS[1] >>> 32; + d = PI_O_4_BITS[1] & 0xffffffffL; + ac = a * c; + bc = b * c; + ad = a * d; + + /* Collect terms */ + ac = ac + ((bc + ad) >>> 32); + + bita = (prod2B & 0x8000000000000000L) != 0; + bitb = (ac & 0x8000000000000000L ) != 0; + prod2B += ac; + bitsum = (prod2B & 0x8000000000000000L) != 0; + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prod2A++; + } + + /* Multiply inputB by pio4bits[0] */ + a = prodB >>> 32; + b = prodB & 0xffffffffL; + c = PI_O_4_BITS[0] >>> 32; + d = PI_O_4_BITS[0] & 0xffffffffL; + ac = a * c; + bc = b * c; + ad = a * d; + + /* Collect terms */ + ac = ac + ((bc + ad) >>> 32); + + bita = (prod2B & 0x8000000000000000L) != 0; + bitb = (ac & 0x8000000000000000L ) != 0; + prod2B += ac; + bitsum = (prod2B & 0x8000000000000000L) != 0; + /* Carry */ + if ( (bita && bitb) || + ((bita || bitb) && !bitsum) ) { + prod2A++; + } + + /* Convert to double */ + double tmpA = (prod2A >>> 12) / TWO_POWER_52; // High order 52 bits + double tmpB = (((prod2A & 0xfffL) << 40) + (prod2B >>> 24)) / TWO_POWER_52 / TWO_POWER_52; // Low bits + + double sumA = tmpA + tmpB; + double sumB = -(sumA - tmpA - tmpB); + + /* Multiply by PI/2 and return */ + result[0] = intPart; + result[1] = sumA * 2.0; + result[2] = sumB * 2.0; + } + + /** + * Sine function. + * + * @param x Argument. + * @return sin(x) + */ + public static double sin(double x) { + boolean negative = false; + int quadrant = 0; + double xa; + double xb = 0.0; + + /* Take absolute value of the input */ + xa = x; + if (x < 0) { + negative = true; + xa = -xa; + } + + /* Check for zero and negative zero */ + if (xa == 0.0) { + long bits = Double.doubleToLongBits(x); + if (bits < 0) { + return -0.0; + } + return 0.0; + } + + if (xa != xa || xa == Double.POSITIVE_INFINITY) { + return Double.NaN; + } + + /* Perform any argument reduction */ + if (xa > 3294198.0) { + // PI * (2**20) + // Argument too big for CodyWaite reduction. Must use + // PayneHanek. + double reduceResults[] = new double[3]; + reducePayneHanek(xa, reduceResults); + quadrant = ((int) reduceResults[0]) & 3; + xa = reduceResults[1]; + xb = reduceResults[2]; + } else if (xa > 1.5707963267948966) { + final CodyWaite cw = new CodyWaite(xa); + quadrant = cw.getK() & 3; + xa = cw.getRemA(); + xb = cw.getRemB(); + } + + if (negative) { + quadrant ^= 2; // Flip bit 1 + } + + switch (quadrant) { + case 0: + return sinQ(xa, xb); + case 1: + return cosQ(xa, xb); + case 2: + return -sinQ(xa, xb); + case 3: + return -cosQ(xa, xb); + default: + return Double.NaN; + } + } + + /** + * Cosine function. + * + * @param x Argument. + * @return cos(x) + */ + public static double cos(double x) { + int quadrant = 0; + + /* Take absolute value of the input */ + double xa = x; + if (x < 0) { + xa = -xa; + } + + if (xa != xa || xa == Double.POSITIVE_INFINITY) { + return Double.NaN; + } + + /* Perform any argument reduction */ + double xb = 0; + if (xa > 3294198.0) { + // PI * (2**20) + // Argument too big for CodyWaite reduction. Must use + // PayneHanek. + double reduceResults[] = new double[3]; + reducePayneHanek(xa, reduceResults); + quadrant = ((int) reduceResults[0]) & 3; + xa = reduceResults[1]; + xb = reduceResults[2]; + } else if (xa > 1.5707963267948966) { + final CodyWaite cw = new CodyWaite(xa); + quadrant = cw.getK() & 3; + xa = cw.getRemA(); + xb = cw.getRemB(); + } + + //if (negative) + // quadrant = (quadrant + 2) % 4; + + switch (quadrant) { + case 0: + return cosQ(xa, xb); + case 1: + return -sinQ(xa, xb); + case 2: + return -cosQ(xa, xb); + case 3: + return sinQ(xa, xb); + default: + return Double.NaN; + } + } + + /** + * Tangent function. + * + * @param x Argument. + * @return tan(x) + */ + public static double tan(double x) { + boolean negative = false; + int quadrant = 0; + + /* Take absolute value of the input */ + double xa = x; + if (x < 0) { + negative = true; + xa = -xa; + } + + /* Check for zero and negative zero */ + if (xa == 0.0) { + long bits = Double.doubleToLongBits(x); + if (bits < 0) { + return -0.0; + } + return 0.0; + } + + if (xa != xa || xa == Double.POSITIVE_INFINITY) { + return Double.NaN; + } + + /* Perform any argument reduction */ + double xb = 0; + if (xa > 3294198.0) { + // PI * (2**20) + // Argument too big for CodyWaite reduction. Must use + // PayneHanek. + double reduceResults[] = new double[3]; + reducePayneHanek(xa, reduceResults); + quadrant = ((int) reduceResults[0]) & 3; + xa = reduceResults[1]; + xb = reduceResults[2]; + } else if (xa > 1.5707963267948966) { + final CodyWaite cw = new CodyWaite(xa); + quadrant = cw.getK() & 3; + xa = cw.getRemA(); + xb = cw.getRemB(); + } + + if (xa > 1.5) { + // Accuracy suffers between 1.5 and PI/2 + final double pi2a = 1.5707963267948966; + final double pi2b = 6.123233995736766E-17; + + final double a = pi2a - xa; + double b = -(a - pi2a + xa); + b += pi2b - xb; + + xa = a + b; + xb = -(xa - a - b); + quadrant ^= 1; + negative ^= true; + } + + double result; + if ((quadrant & 1) == 0) { + result = tanQ(xa, xb, false); + } else { + result = -tanQ(xa, xb, true); + } + + if (negative) { + result = -result; + } + + return result; + } + + + /** Enclose the Cody/Waite reduction (used in "sin", "cos" and "tan"). */ + private static class CodyWaite { + /** k */ + private final int finalK; + /** remA */ + private final double finalRemA; + /** remB */ + private final double finalRemB; + + /** + * @param xa Argument. + */ + CodyWaite(double xa) { + // Estimate k. + //k = (int)(xa / 1.5707963267948966); + int k = (int)(xa * 0.6366197723675814); + + // Compute remainder. + double remA; + double remB; + while (true) { + double a = -k * 1.570796251296997; + remA = xa + a; + remB = -(remA - xa - a); + + a = -k * 7.549789948768648E-8; + double b = remA; + remA = a + b; + remB += -(remA - b - a); + + a = -k * 6.123233995736766E-17; + b = remA; + remA = a + b; + remB += -(remA - b - a); + + if (remA > 0) { + break; + } + + // Remainder is negative, so decrement k and try again. + // This should only happen if the input is very close + // to an even multiple of pi/2. + --k; + } + + this.finalK = k; + this.finalRemA = remA; + this.finalRemB = remB; + } + + /** + * @return k + */ + int getK() { + return finalK; + } + /** + * @return remA + */ + double getRemA() { + return finalRemA; + } + /** + * @return remB + */ + double getRemB() { + return finalRemB; + } + } +} diff --git a/src/com/annimon/visualme/Fps.java b/src/com/annimon/visualme/Fps.java new file mode 100644 index 0000000..b386621 --- /dev/null +++ b/src/com/annimon/visualme/Fps.java @@ -0,0 +1,35 @@ +/* By aNNiMON from GipGameActivity */ + +package com.annimon.visualme; + +public class Fps { + + private static final int MAX_FPS = 30; + private static final int MAX_DELAY = 1000 / MAX_FPS; + + private static long currentFps; + private static long counter = 0, startTime = 0; + private static long startTimeForMeasureDelay = 0; + + public static String getFpsAsString() { + counter++; + if (startTime == 0) { + startTime = System.currentTimeMillis(); + } + if ((System.currentTimeMillis() - startTime) >= 1000) { + currentFps = counter; + counter = 0; + startTime = System.currentTimeMillis(); + } + return Long.toString(currentFps); + } + + public static void startMeasuringDelay() { + startTimeForMeasureDelay = System.currentTimeMillis(); + } + + public static long getDelay() { + long delay = System.currentTimeMillis() - startTimeForMeasureDelay; + return (delay > MAX_DELAY ? 0 : MAX_DELAY - delay); + } +} diff --git a/src/com/annimon/visualme/GameSurfaceView.java b/src/com/annimon/visualme/GameSurfaceView.java new file mode 100644 index 0000000..bf26bb9 --- /dev/null +++ b/src/com/annimon/visualme/GameSurfaceView.java @@ -0,0 +1,146 @@ +package com.annimon.visualme; + +import com.annimon.visualme.visuals.Visual; +import com.annimon.visualme.visuals.VisualME; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.preference.PreferenceManager; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class GameSurfaceView extends SurfaceView implements SurfaceHolder.Callback { + + private static final int BACKGROUND_COLOR = 0xff000000; + + private boolean showFps; + private Paint paint; + private SurfaceHolder surfaceHolder; + private DrawingThread thread; + + private Visual visual; + + + public GameSurfaceView(Context context) { + super(context); + + surfaceHolder = getHolder(); + surfaceHolder.addCallback(this); + + visual = new VisualME(); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); + showFps = pref.getBoolean("show_fps", false); + if (visual instanceof VisualME) { + VisualME vis = (VisualME) visual; + vis.setNumberOfBranches( Integer.parseInt(pref.getString("branches", "15")) ); + vis.setFlexibility( Integer.parseInt(pref.getString("flexibility", "20")) ); + vis.setScreenProportions( Integer.parseInt(pref.getString("proportions", "4")) ); + vis.setSpeed( Integer.parseInt(pref.getString("speed", "1")) ); + + vis.setShapeType( Integer.parseInt(pref.getString("shape_type", "0")) ); + + vis.setVisualizationAutochange( pref.getBoolean("autochangevis", true) ); + vis.setShapeAutochange( pref.getBoolean("autochangefig", false) ); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Paint.Style.FILL); + + + + thread = new DrawingThread(); + thread.keepRunning = true; + thread.start(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int newW, int newH) { + visual.sizeChanged(newW, newH); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + thread.keepRunning = false; + boolean retry = true; + while (retry) { + try { + thread.join(); + retry = false; + + paint = null; + thread = null; + } catch (InterruptedException e) { + } + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + visual.onTouch((int) event.getX(), (int)event.getY()); + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onDraw(Canvas canvas) { + // Fill background + paint.setColor(BACKGROUND_COLOR); + canvas.drawPaint(paint); + + visual.paint(canvas, paint); + + // Draw FPS + if (showFps) { + paint.setColor(Color.WHITE); + canvas.drawText(Fps.getFpsAsString(), 10, 10, paint); + } + } + + private void update() { + visual.update(); + } + + private class DrawingThread extends Thread { + public boolean keepRunning = true; + + @Override + public void run() { + Canvas c; + + while (keepRunning) { + Fps.startMeasuringDelay(); + update(); + c = null; + + try { + c = surfaceHolder.lockCanvas(); + if (c != null) { + synchronized (surfaceHolder) { + onDraw(c); + } + } + } finally { + if (c != null) { + surfaceHolder.unlockCanvasAndPost(c); + } + } + + try { + Thread.sleep(Fps.getDelay()); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + } + } +} \ No newline at end of file diff --git a/src/com/annimon/visualme/MainActivity.java b/src/com/annimon/visualme/MainActivity.java new file mode 100644 index 0000000..09765b7 --- /dev/null +++ b/src/com/annimon/visualme/MainActivity.java @@ -0,0 +1,23 @@ +package com.annimon.visualme; + +import android.app.Activity; +import android.os.Bundle; +import android.view.WindowManager; + +public class MainActivity extends Activity { + + private GameSurfaceView gameView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Turn on the backlight + getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + gameView = new GameSurfaceView(this); + gameView.setFocusable(true); + setContentView(gameView); + } +} \ No newline at end of file diff --git a/src/com/annimon/visualme/SettingsActivity.java b/src/com/annimon/visualme/SettingsActivity.java new file mode 100644 index 0000000..bcf9e26 --- /dev/null +++ b/src/com/annimon/visualme/SettingsActivity.java @@ -0,0 +1,14 @@ +package com.annimon.visualme; + +import android.os.Bundle; +import android.preference.PreferenceActivity; + +public class SettingsActivity extends PreferenceActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.visualme); + } + +} diff --git a/src/com/annimon/visualme/visuals/Visual.java b/src/com/annimon/visualme/visuals/Visual.java new file mode 100644 index 0000000..23b08d2 --- /dev/null +++ b/src/com/annimon/visualme/visuals/Visual.java @@ -0,0 +1,24 @@ +package com.annimon.visualme.visuals; + +import android.graphics.Canvas; +import android.graphics.Paint; + +public abstract class Visual { + + protected int width, height; + + public void sizeChanged(int width, int height) { + this.width = width; + this.height = height; + init(); + } + + public abstract void init(); + + public abstract void paint(Canvas canvas, Paint paint); + + public abstract void onTouch(int x, int y); + + public abstract void update(); + +} diff --git a/src/com/annimon/visualme/visuals/VisualME.java b/src/com/annimon/visualme/visuals/VisualME.java new file mode 100644 index 0000000..1d7d200 --- /dev/null +++ b/src/com/annimon/visualme/visuals/VisualME.java @@ -0,0 +1,403 @@ +package com.annimon.visualme.visuals; + +import java.util.Random; + +import com.annimon.visualme.FastMath; + +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; + +public class VisualME extends Visual { + + private static final int MAX_DELTA_COLOR = 20; + + protected static final Random random = new Random(); + + // + private int numberOfBranches; + private int flexibility; + private float screenProportions; + private int shapeType; + private int speed; + + // + private boolean shapeAutochange; + private boolean visualizationAutochange; + + // + private boolean redChange, greenChange, blueChange; + + private int rc, gc, bc; + private int rgbChangeMode, visualMode, colorMode; + private int startAngleInt; + private float angleDelta;// + private float len; // + private float[] angles; + + private Thread thr; + + public void init() { + // () + rc = 0; + gc = 0; + bc = 0; + // () + rgbChangeMode = 1; + // + visualMode = 1; + // + colorMode = 1; + + // + startAngleInt = random.nextInt(360); + + // + if (width > height) { + len = height / screenProportions / flexibility; + } else { + len = width / screenProportions / flexibility; + } + + thr = new Thread(new Runnable() { + + @Override + public void run() { + while (thr == Thread.currentThread()) { + threadUpdate(); + } + } + }); + thr.start(); + } + + /** + * . + * @param flexibility . + */ + public void setFlexibility(int flexibility) { + this.flexibility = flexibility; + angles = new float[flexibility]; + } + + /** + * . + * @param numberOfBranches + */ + public void setNumberOfBranches(int numberOfBranches) { + this.numberOfBranches = numberOfBranches; + // + angleDelta = (float) (2 * FastMath.PI / numberOfBranches); + } + + /** + * . + * @param screenProportions + */ + public void setScreenProportions(int screenProportions) { + this.screenProportions = 4f / screenProportions; + } + + /** + * (, , ..). + * @param shapeType + */ + public void setShapeType(int shapeType) { + this.shapeType = shapeType; + } + + /** + * . + * @param speed + */ + public void setSpeed(int speed) { + this.speed = 10 - speed; + } + + /** + * . + * @param shapeAutochange + */ + public void setShapeAutochange(boolean shapeAutochange) { + this.shapeAutochange = shapeAutochange; + } + + /** + * . + * @param visualizationAutochange + */ + public void setVisualizationAutochange(boolean visualizationAutochange) { + this.visualizationAutochange = visualizationAutochange; + } + + @Override + public void paint(Canvas canvas, Paint paint) { + if (random.nextInt(20) == 5) startAngleInt = random.nextInt(360); + + // + float startAngle = (float) (startAngleInt * FastMath.PI / 180); + + // + angles[0] = (float) (angles[0] + FastMath.sin(startAngle) / speed); + for (int i = 1; i < flexibility; i++) { + angles[i] = (float) (angles[i] + (angles[i - 1] - angles[i]) * 0.1); + } + + //String currentTime = ""; + //if (shapeType == 3) currentTime = time2fileName(); + for (int j = 0; j < numberOfBranches; j++) { + float x = 0.5f * width; + float y = 0.5f * height; + float tx = 0, ty = 0; + final float temp1 = j * angleDelta + angles[1]; + for (int i = 1; i < flexibility; i++) { + final float temp2 = j * angleDelta + angles[i]; + if (visualMode == 1) { + tx = (float) (x + FastMath.cos(temp1) * len); + ty = (float) (y + FastMath.sin(temp2) * len); + x = x + (float) (FastMath.cos(temp2) * len); + } + else if (visualMode == 2) { + tx = (float) (x + FastMath.sin(temp2) * len); + ty = (float) (y + FastMath.cos(temp2) * len); + } + else if (visualMode == 3) { + tx = (float) (x + FastMath.tan(temp1) * len); + ty = (float) (y + FastMath.cos(temp2) * len); + y = y - (float) (FastMath.sin(temp2) * len); + } + else if (visualMode == 4) { + tx = (float) (x + FastMath.tan(temp1) * len); + ty = (float) (y + FastMath.cos(temp2) * len); + x = y - (float) (FastMath.sin(temp2) * len); + } + else if (visualMode == 5) { + tx = (float) (x + FastMath.tan(temp1) * len); + ty = (float) (y + FastMath.cos(temp2) * len); + x = x * ty * angleDelta + angles[i] - (float) (FastMath.sin(temp2) * len); + } + else if (visualMode == 6) { + x = x + (float) (FastMath.sin(temp2) * len); + y = y + (float) (FastMath.cos(temp2) * len); + tx = (float) (x + FastMath.cos(temp1) * len); + ty = (float) (y + FastMath.sin(temp1) * len); + x = x + (float) (FastMath.sin(temp2) * len); + y = y + (float) (FastMath.cos(temp2) * len); + } + + if (colorMode == 1) setColor(paint, rc, gc, bc); + else { + final int deltaColor = 255 - 255 * i / flexibility; + if (colorMode == 2) setColor(paint, deltaColor, 0, 255); + else if (colorMode == 3) setColor(paint, deltaColor, 255, 0); + else if (colorMode == 4) setColor(paint, deltaColor, 255, 255); + + else if (colorMode == 5) setColor(paint, 0, deltaColor, 255); + else if (colorMode == 6) setColor(paint, 255, deltaColor, 0); + else if (colorMode == 7) setColor(paint, 255, deltaColor, 255); + + else if (colorMode == 8) setColor(paint, 0, 255, deltaColor); + else if (colorMode == 9) setColor(paint, 255, 0, deltaColor); + else if (colorMode == 10) setColor(paint, 255, 255, deltaColor); + + else if (colorMode == 11) setColor(paint, deltaColor, deltaColor, 0); + else if (colorMode == 12) setColor(paint, deltaColor, deltaColor, 255); + + else if (colorMode == 13) setColor(paint, deltaColor, 0, deltaColor); + else if (colorMode == 14) setColor(paint, deltaColor, 255, deltaColor); + + else if (colorMode == 15) setColor(paint, 0, deltaColor, deltaColor); + else if (colorMode == 16) setColor(paint, 255, deltaColor, deltaColor); + + else if (colorMode == 17) setColor(paint, rc, deltaColor, 255); + else if (colorMode == 18) setColor(paint, 255, gc, deltaColor); + else if (colorMode == 19) setColor(paint, 255, deltaColor, bc); + + else if (colorMode == 20) setColor(paint, bc, rc, deltaColor); + } + + if (shapeType == 0) canvas.drawLine(x, y, tx, ty, paint); + else if (shapeType == 1) { + RectF rf = new RectF(tx, ty, x, y); + canvas.drawArc(rf, 0, 360, false, paint); + } + else if (shapeType == 2) canvas.drawRect(tx, ty, x, y, paint); + // else if (shapeType == 3) grf.drawString(currentTime,(int) x, (int) y, 20); + + x = tx; + y = ty; + } + } + // Motion-Blur + //grf.drawRGB(backgroundPixels, 0, width, 0, 0, width, height/2, true); + //grf.drawRGB(backgroundPixels, 0, width, 0, height/2, width, height/2, true); + + } + + @Override + public void onTouch(int x, int y) { + if (y >= (height / 2)) { + colorMode++; + if (colorMode > MAX_DELTA_COLOR) { + colorMode = 1; + } + } else { + visualMode++; + if (visualMode > 6) { + visualMode = 1; + } + } + } + + @Override + public void update() { + rgb(); + } + + public void threadUpdate() { + int i = random.nextInt(40); + if (visualizationAutochange) { + if (i == 16) colorMode = random.nextInt(MAX_DELTA_COLOR) + 1; + else if (i == 19) visualMode = random.nextInt(6) + 1; + } + if (shapeAutochange) if (i == 36) shapeType = random.nextInt(4); + + try { + Thread.sleep(800); + } catch (InterruptedException ex) {} + } + + private void setColor(Paint paint, int red, int green, int blue) { + paint.setColor(Color.argb(255, red, green, blue)); + } + + private void rgb() { + if (rgbChangeMode == 1) { + rc++; + if (rc > 253) { + rc--; + redChange = true; + } + if (redChange == true) { + rc = rc - 2; + } + if (rc < 18 && redChange == true) { + rc = 0; + redChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 2) { + gc++; + if (gc > 253) { + gc--; + greenChange = true; + } + if (greenChange == true) { + gc = gc - 2; + } + if (gc < 18 && greenChange == true) { + gc = 0; + greenChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 3) { + bc++; + if (bc > 253) { + bc--; + blueChange = true; + } + if (blueChange == true) { + bc = bc - 2; + } + if (bc < 18 && blueChange == true) { + bc = 0; + blueChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 4) { + rc++; + gc++; + if (rc > 253) { + rc--; + gc--; + redChange = true; + } + if (redChange == true) { + rc = rc - 2; + gc = gc - 2; + } + if (rc < 18 && redChange == true) { + rc = 0; + gc = 0; + redChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 5) { + rc++; + bc++; + if (rc > 253) { + rc--; + bc--; + redChange = true; + } + if (redChange == true) { + rc = rc - 2; + bc = bc - 2; + } + if (rc < 18 && redChange == true) { + rc = 0; + bc = 0; + redChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 6) { + gc++; + bc++; + if (gc > 253) { + gc--; + bc--; + greenChange = true; + } + if (greenChange == true) { + gc = gc - 2; + bc = bc - 2; + } + if (gc < 18 && greenChange == true) { + gc = 0; + bc = 0; + greenChange = false; + rgbChangeMode++; + } + } + else if (rgbChangeMode == 7) { + rc++; + gc++; + bc++; + if (gc > 253) { + rc--; + gc--; + bc--; + greenChange = true; + } + if (greenChange == true) { + rc = rc - 2; + gc = gc - 2; + bc = bc - 2; + } + if (gc < 18 && greenChange == true) { + rc = 0; + gc = 0; + bc = 0; + greenChange = false; + rgbChangeMode = 1; + } + } + } + +}