# HG changeset patch
# User hh
# Date 1574412016 -3600
# Node ID 16509f98f3012bc07aec1c2841e48f7ddc02274c
--
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/build.gradle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/build.gradle Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,17 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0-alpha09'
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ google()
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/lib031-release/build.gradle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/lib031-release/build.gradle Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,2 @@
+configurations.maybeCreate("default")
+artifacts.add("default", file('lib031-release.aar'))
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/local.properties
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/local.properties Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/home/L/_HH/obsah/TECHNO/Android/sdk
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/build.gradle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/build.gradle Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,45 @@
+apply plugin: 'com.android.application'
+
+android {
+ signingConfigs {
+ hh {
+ keyAlias 'androiddebugkey'
+ keyPassword 'android'
+ storeFile file('/home/hh/.android/debug.keystore')
+ storePassword 'android'
+ }
+ }
+
+ compileSdkVersion 28
+
+ defaultConfig {
+ applicationId 'hh.michelson'
+ minSdkVersion 22
+ targetSdkVersion 26
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ signingConfig signingConfigs.hh
+ }
+ buildTypes {
+ release {
+ signingConfig signingConfigs.hh
+ }
+ debug {
+ debuggable false
+ }
+ }
+ productFlavors {
+ }
+}
+
+dependencies {
+ implementation project(':lib031-release')
+ implementation fileTree(include: ['*.jar'], dir: 'libs')
+ implementation 'com.android.support:support-v4:28.0.0'
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.android.support:preference-v7:28.0.0'
+ implementation 'com.android.support:coordinatorlayout:28.0.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+ implementation 'com.android.support:design:28.0.0'
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/AndroidManifest.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/AndroidManifest.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/A.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/A.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,462 @@
+package hh.michelson;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ListView;
+import android.widget.SeekBar;
+
+import hh.lib.D;
+import hh.lib.DA;
+import hh.ui.FlatButton;
+
+public class A extends DA implements WavesFragment.OnFragmentInteractionListener, Handler.Callback {
+ /**
+ * ----------------------------------------------------------
+ * main activity template with uninstall in menu
+ * ----------------------------------------------------------
+ */
+
+ WavesFragment waveList;
+ boolean playing = C.playingDefault;
+ Wave wave = null;
+ double phase = 0;
+ boolean node = true;
+
+ volatile double freq = C.freqDef.Default;
+ volatile double freqProgr = C.freqProgrDef.Default;
+ volatile double targetFreq, deltaFreq, dt;
+ final static int UPDFREQ = 0;
+
+ double amplitude;
+ volatile double amplKoef = 0;
+ double maxY = 0;
+ boolean mute = false;
+
+ double pulseSecs = C.pulseDef.Default;
+ final double pulseStep = (Math.log10(C.pulseDef.Max) - Math.log10(C.pulseDef.Min)) / C.maxBarProgress; /* in secs */
+ int pulseCycles; /* pulse in cycles */
+ boolean isInPulse = true;
+
+ double pauseSecs = C.pauseDef.Default;
+ final double pauseStep = (Math.log10(C.pauseDef.Max) - Math.log10(C.pauseDef.Min)) / C.maxBarProgress; /* in secs */
+ int pauseCycles; /* pause in cycles */
+ volatile double pulsePausePhase = 0;
+
+ double slope = C.slopeDef.Default;
+ final double slopeStep = (Math.log10(C.slopeDef.Max) - Math.log10(C.slopeDef.Min)) / C.maxBarProgress;
+ double slopeKoef = 1d;
+ final static int SETSLOPE = 1;
+
+ Handle amplBar;
+ Handle pulseBar;
+ Handle pauseBar;
+ Handle slopeBar;
+ Handle freqBar;
+ Handle freqProgressBar;
+ Handle[] handles;
+
+ AudioTrack audioTrack;
+ int buffsize;
+ class Samples {
+ short[] samples;
+ boolean ready;
+
+ Samples(int n) {
+ samples = new short[n];
+ }
+ }
+ Samples[] s;
+ Thread audioThread = null;
+ boolean audioIsActive = true;
+
+ Handler h = new Handler(this);
+
+ class AmplHandle extends Handle {
+
+ AmplHandle(D d, double actual) { super(d, C.amplitudeDef, actual); }
+
+ @Override
+ public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
+ super.onProgressChanged(bar, progress, fromUser);
+ getAmpl();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ final boolean r = super.onLongClick(v);
+ getAmpl();
+ return r;
+ }
+ }
+
+ class PulseHandle extends Handle {
+
+ PulseHandle(D d, double actual) { super(d, C.pulseDef, actual); }
+
+ @Override
+ double progress2value(int progress) { return progress == 0 ? 0d : Math.pow(10, progress * pulseStep - 1); }
+
+ @Override
+ int value2progress(double value) { return value == 0 ? 0 : (int)((Math.log10(value) + 1) / pulseStep); }
+ }
+
+ class PauseHandle extends Handle {
+
+ PauseHandle(D d, double actual) { super(d, C.pauseDef, actual); }
+
+ PauseHandle(D d, String label, double min, double max, double def, double actual, String key) {
+ super(d, label, min, max, def, actual, key);
+ }
+
+ @Override
+ double progress2value(int progress) { return progress == 0 ? 0d : Math.pow(10, progress * pulseStep - 1); }
+
+ @Override
+ int value2progress(double value) { return value == 0 ? 0 : (int)((Math.log10(value) + 1) / pauseStep); }
+ }
+
+ class SlopeHandle extends Handle {
+
+ SlopeHandle(D d, double actual) { super(d, C.slopeDef, actual); }
+
+ SlopeHandle(D d, String label, double min, double max, double def, double actual, String key) {
+ super(d, label, min, max, def, actual, key);
+ }
+
+ @Override
+ double progress2value(int progress) { return progress == 0 ? 0d : Math.pow(10, (progress + 1) * slopeStep); }
+
+ @Override
+ int value2progress(double value) { return value == 0 ? 0 : (int)(Math.log10(value)/slopeStep - 1); }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.main);
+
+ Point size = new Point();
+ ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(size);
+ C.windowWidth = size.x;
+
+ getValues();
+ setMenu();
+ findViewById(R.id.dispWave).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ showListUI();
+ }
+ });
+ setControlHandles();
+ findViewById(R.id.mute).setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ flipMute();
+ }
+ });
+ setMute();
+ allocAudio();
+
+ if(savedInstanceState != null) playing = savedInstanceState.getBoolean(C.playingKey, C.playingDefault);
+ if(playing)startAudio();
+ else showListUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) { outState.putBoolean(C.playingKey, playing); }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopAudio();
+ audioTrack.release();
+ }
+
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch(msg.what) {
+ case UPDFREQ:
+ updateFreq();
+ return true;
+ case SETSLOPE:
+ setSlopeBar();
+ return true;
+ default:
+ return d.handleMessage(msg);
+ }
+ }
+
+ @Override
+ public void setNewWave(int position) {
+ if(fm.getBackStackEntryCount() > 0) {
+ fm.popBackStackImmediate();
+ fm.beginTransaction().detach(waveList).commit();
+ }
+ if(position >= 0) {
+ wave = Enum.valueOf(Wave.class, Wave.names[position]);
+ sp.edit().putString(C.waveKey, wave.name()).apply();
+ }
+ playing = true;
+ startAudio();
+ }
+
+ void startAudio() {
+ wave.init(this);
+ if(audioThread != null) return;
+ audioThread = new Thread() {
+ public void run() {
+ setPriority(Thread.MAX_PRIORITY);
+ play();
+ }
+ };
+ s[0].ready = false;
+ s[1].ready = false;
+ audioTrack.play();
+ audioThread.start();
+ audioIsActive = true;
+
+ new Thread() { public void run() { synthesize(); } }.start();
+ }
+
+ void stopAudio() {
+ if(audioThread == null) return;
+ audioIsActive = false;
+ try { audioThread.join(); } catch (InterruptedException e) { e.printStackTrace(); }
+ audioThread = null;
+ audioTrack.stop();
+ }
+
+ void getValues() {
+ wave = Enum.valueOf(Wave.class, sp.getString(C.waveKey, C.waveDefault.name()));
+ amplitude = sp.getFloat(C.amplitudeDef.Key, Double.valueOf(C.amplitudeDef.Default).floatValue());
+ mute = sp.getBoolean(C.muteKey, C.muteDefault);
+ pulseSecs = sp.getFloat(C.pulseDef.Key, Double.valueOf(C.pulseDef.Default).floatValue());
+ pauseSecs = sp.getFloat(C.pauseDef.Key, Double.valueOf(C.pauseDef.Default).floatValue());
+ slope = sp.getFloat(C.slopeDef.Key, Double.valueOf(C.slopeDef.Default).floatValue());
+ freq = sp.getFloat(C.freqDef.Key, Double.valueOf(C.freqDef.Default).floatValue());
+ if(freq < C.freqDef.Min) freq = C.freqDef.Min;
+ freqProgr = sp.getFloat(C.freqProgrDef.Key, Double.valueOf(C.freqProgrDef.Default).floatValue());
+ }
+
+ void setControlHandles() {
+ amplBar = new AmplHandle(d, amplitude);
+ pulseBar = new PulseHandle(d, pulseSecs);
+ pauseBar = new PauseHandle(d, pauseSecs);
+ slopeBar = new SlopeHandle(d, slope);
+ freqBar = new Handle(d, C.freqDef, freq);
+ freqProgressBar = new Handle(d, C.freqProgrDef, freqProgr);
+
+ handles = new Handle[] { amplBar, pulseBar, pauseBar, slopeBar, freqBar, freqProgressBar };
+ ((ListView)findViewById(R.id.handles)).setAdapter(new Windrover(d, "Controls", this, R.layout.handles_row, handles));
+ }
+
+ void showListUI() {
+ stopAudio();
+ playing = false;
+ waveList = new WavesFragment();
+ fm.beginTransaction()
+ .add(R.id.main, waveList)
+ .addToBackStack(null)
+ .commit();
+ }
+
+ void getAmpl() {
+ synchronized(this) {
+ amplitude = amplBar.getActual();
+ setAmplKoef();
+ }
+ }
+
+ void setAmplKoef() {
+ amplKoef = amplitude;
+ if(maxY > 1d) amplKoef = amplKoef / maxY;
+ }
+
+ void setMaxY(double maxY) {
+ this.maxY = maxY;
+ setAmplKoef();
+ }
+
+ void updateFreq() {
+ /* zatím se volá z nitě syntetizátoru (netřeba synchronizovat), který zajišťuje pravidelný rytmus */
+ double nextTargFreq = targetFreq;
+
+ final double barFreq = freqBar.getActual();
+ final double fProgress = freqProgressBar.getActual();
+
+ if(targetFreq != barFreq) nextTargFreq = barFreq; // změna freqBar má přednost
+ else if(fProgress != 0d) {
+ nextTargFreq = targetFreq + fProgress;
+ if(nextTargFreq > C.freqDef.Max) nextTargFreq = C.freqDef.Max;
+ if(nextTargFreq < C.freqDef.Min) nextTargFreq = C.freqDef.Min;
+ freqBar.setActual(nextTargFreq);
+ }
+
+ if(nextTargFreq != targetFreq) {
+ synchronized(this) {
+ targetFreq = nextTargFreq;
+ deltaFreq = (targetFreq - freq) / 10;
+ }
+ if(ll(5)) l(String.format(lo, "freq=%f, targetFreq=%f, deltaFreq=%f",
+ freq, targetFreq, deltaFreq));
+ }
+ }
+
+ void getPulse() {
+ pulseCycles = dur2len(pulseBar.getActual());
+ pauseCycles = dur2len(pauseBar.getActual());
+ Message.obtain(h, SETSLOPE).sendToTarget();
+ }
+
+ void setSlopeBar() {
+ /* slope can't exceed pulse/2 */
+ int p = (int)Math.floor(pulseCycles /2);
+ if(pulseCycles > 0 && slopeBar.getActual() > p) slopeBar.setActual(p);
+ }
+
+ int dur2len(double duration) { return (int)Math.round(duration * freq); }
+
+ void flipMute() {
+ mute = !mute;
+ setMute();
+ }
+
+ void setMute() {
+ ((FlatButton)findViewById(R.id.mute)).setText(mute ? "unmute" : "mute");
+ sp.edit().putBoolean(C.muteKey, mute).apply();
+ }
+
+ void allocAudio() {
+ final int audioSessionId = ((AudioManager)getSystemService(Context.AUDIO_SERVICE)).generateAudioSessionId();
+ buffsize = AudioTrack.getMinBufferSize(
+ C.SR,
+ AudioFormat.CHANNEL_OUT_MONO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ l(4, String.format(lo, "buf size=%d", buffsize));
+ audioTrack = new AudioTrack(
+ new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_UNKNOWN)
+ .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
+ .build(),
+ new AudioFormat.Builder()
+ .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .build(),
+ buffsize,
+ AudioTrack.MODE_STREAM,
+ audioSessionId);
+ s = new Samples[] {new Samples(buffsize), new Samples(buffsize)};
+ }
+
+ void play() {
+ while(audioIsActive) {
+ try {
+ playSamples(0);
+ if(audioIsActive) playSamples(1);
+ } catch(InterruptedException e) {}
+ }
+ l(4,"exiting playing thread");
+ }
+
+ void playSamples(int y) throws InterruptedException {
+ synchronized(s[y]) {
+ while(!s[y].ready && audioIsActive) {
+ if(ll(7)) l("waiting for samples " + y);
+ s[y].wait(555);
+ }
+ }
+ if(!audioIsActive) return;
+ if(ll(7)) l(String.format(lo, "playing samples %d", y));
+ int offset = 0, size = buffsize, put;
+ while((put = audioTrack.write(s[y].samples, offset, size)) < size) {
+ offset += put;
+ size -= put;
+ }
+ synchronized(s[y]) {
+ s[y].ready = false;
+ s[y].notify();
+ }
+ }
+
+ void synthesize() {
+ phase = 0;
+ freq = targetFreq = freqBar.getActual();
+ dt = C.twoPI * freq / C.SR;
+ while(audioIsActive) {
+ try {
+ compute(0);
+ if(audioIsActive) compute(1);
+ } catch(InterruptedException e) {}
+ // pravidelný interval pro snímání změny frekvence; samotná změna frekvence s provede postupně, když je průběh vlny v uzlu
+ Message.obtain(h, UPDFREQ).sendToTarget();
+ }
+ }
+
+ void compute(int i) throws InterruptedException {
+ synchronized(s[i]) {
+ while(s[i].ready && audioIsActive) s[i].wait();
+ }
+ if(!audioIsActive) return;
+ computeSamples(i);
+ synchronized(s[i]) {
+ s[i].ready = true;
+ s[i].notify();
+ }
+ }
+
+ void computeSamples(int i) {
+ for(int j = 0; j < buffsize; j++) {
+ synchronized(this) { // každý vzorek se synchronizuje zvlášť
+ if(node) adjustInNode();
+ final double k = (mute ? 0 : 1) * slopeKoef * amplKoef;
+
+ s[i].samples[j] = (k != 0 ? (short)(k * wave.value(phase)) : 0);
+
+ phase += dt;
+ if(phase > C.twoPI) {
+ phase -= C.twoPI;
+ node = true;
+ }
+ else node = false;
+
+ if(node) {
+ ++pulsePausePhase;
+ if(pulsePausePhase > (pulseCycles + pauseCycles)) pulsePausePhase = 0;
+ }
+ }
+ }
+ }
+
+ void adjustInNode() {
+ getPulse();
+ if(pauseCycles == 0) isInPulse = true;
+ else isInPulse = ((pulseCycles != 0) && (pulsePausePhase <= pulseCycles));
+ if(isInPulse) {
+ slopeKoef = 1d;
+ setSlopeBar();
+ final double slope = slopeBar.getActual();
+ if(slope > 0 && pauseCycles > 0) {
+ if(pulsePausePhase < slope)
+ slopeKoef = (pulsePausePhase + 1d) / (slope + 1d);
+ if(pulsePausePhase > (pulseCycles - 1 - slope))
+ slopeKoef = (pulseCycles - pulsePausePhase) / (slope + 1d);
+ }
+ }
+ else slopeKoef = 0;
+
+ if(freq != targetFreq) {
+ if(Math.abs(freq - targetFreq) < Math.abs(deltaFreq)) freq = targetFreq;
+ else freq += deltaFreq;
+ dt = C.twoPI * freq / C.SR;
+ getPulse();
+ }
+ }
+}
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/C.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/C.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,46 @@
+package hh.michelson;
+
+public class C {
+ /**----------------------------------------------------------
+ * constant vector
+ * ----------------------------------------------------------*/
+
+ static final int SR = 44100;
+ /*static final double PI = 4. * Math.atan(1.);*/
+ static final double PI = Math.PI;
+ static final double twoPI = 2. * Math.PI;
+ static final double quadPI = Math.pow(Math.PI, 2);
+ static final int maxBarProgress = 999;
+ static int windowWidth;
+
+ static final String playingKey = "playing";
+ static final boolean playingDefault = false;
+
+ static final String waveKey = "WAVE";
+ static final Wave waveDefault = Wave.F1;
+
+ static final HandleDef pulseDef = new HandleDef("PULSE", "pulse", 0.1, 3, 0);
+ static final HandleDef pauseDef = new HandleDef("PAUSE", "pause", 0.1, 3, 0);
+ static final HandleDef slopeDef = new HandleDef("SLOPE", "slope", 1, 30, 0);
+ static final HandleDef freqDef = new HandleDef("FREQ", "frequence", 220, 880, 220);
+ static final HandleDef freqProgrDef = new HandleDef("FREQPROGR", "freq progress", -33, 33, 0);
+ static final HandleDef amplitudeDef = new HandleDef("AMPLITUDE", "amplitude", 0, 10000, 5000);
+
+ static final String muteKey = "MUTE";
+ static final boolean muteDefault = false;
+}
+
+class HandleDef {
+ String Key;
+ String Label;
+ double Min;
+ double Max;
+ double Default;
+ HandleDef(String Key, String Label, double Min, double Max, double Default) {
+ this.Key = Key;
+ this.Label = Label;
+ this.Min = Min;
+ this.Max = Max;
+ this.Default = Default;
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/GraphView.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/GraphView.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,93 @@
+package hh.michelson;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import hh.lib.D;
+
+public class GraphView extends View {
+ D d;
+ Context context;
+ Paint mPaint;
+ float[] graphValues = new float[] {0f};
+ float[] graphLines = null;
+ int W = 0, H, Y0;
+ final int maxH = 600;
+ double maxY = 0d;
+ double dx;
+ double kY;
+
+ /*GraphView(Context context) {
+ super(context);
+ init(context); }*/
+
+ public GraphView(Context context, AttributeSet a) {
+ super(context, a);
+ init(context); }
+
+ /*GraphView(Context context, AttributeSet a, int defStyleAttr) {
+ super(context, a, defStyleAttr);
+ init(context); }
+
+ GraphView(Context context, AttributeSet a, int defStyleAttr, int defStyleRes) {
+ super(context, a, defStyleAttr, defStyleRes);
+ init(context); }*/
+
+ void init(Context context) {
+ this.d = ((A)context).d.klon(this);
+ this.context = context;
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.RED);}
+
+ public void onDraw(Canvas canvas) {
+ d.l(4, "graph.onDraw");
+ canvas.drawLine(0, Y0, W, Y0, mPaint);
+ fillGraphArray();
+ if(graphLines != null) canvas.drawLines(graphLines, mPaint);
+ }
+
+ void setGraphValues(float[] graphValues) { this.graphValues = graphValues; }
+
+ void setMaxY(double maxY) { this.maxY = maxY; }
+
+ protected void onMeasure(int w, int h) {
+ W = MeasureSpec.getSize(w);
+ H = MeasureSpec.getSize(h) > maxH ? maxH : MeasureSpec.getSize(h);
+ super.onMeasure(w, h);
+ setMeasuredDimension(W, H);
+ Y0 = H / 2;
+ if(d.ll(4)) d.l(String.format(A.lo, "graph, onMeasure, w=%d, W=%d, h=%d, H=%d, Y0=%d",
+ MeasureSpec.getSize(w), W, MeasureSpec.getSize(h), H, Y0));
+ }
+
+ void fillGraphArray() {
+ if(d.ll(4)) d.l(String.format(A.lo, "fillGraphArray, W=%d, graphValues.length=%d", W, graphValues.length));
+ graphLines = null;
+ if(W > 0 && graphValues != null) {
+ dx = (double)W / graphValues.length;
+ kY = 4.0 * Y0 / (5.0 * maxY);
+ graphLines = new float[4 * graphValues.length];
+ graphLines[0] = 0;
+ graphLines[1] = Y0;
+ if(d.ll(4)) d.l(String.format(A.lo, "W=%d, H=%d, Y0=%d, graphLines.length=%d", W, H, Y0, graphLines.length));
+ int i = 0;
+ for(double v: graphValues) {
+ if(i > 0) {
+ graphLines[i] = graphLines[i-2];
+ graphLines[i+1] = graphLines[i-1];
+ }
+ graphLines[i+2] = graphLines[i] + Double.valueOf(dx).floatValue();
+ graphLines[i+3] = Double.valueOf(Y0 - kY * v).floatValue();
+ i += 4;
+ }
+ graphLines[0] = graphLines[2];
+ graphLines[1] = graphLines[3];
+ }
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/Handle.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/Handle.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,113 @@
+package hh.michelson;
+
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import hh.lib.D;
+import hh.lib.DA;
+
+class Handle implements SeekBar.OnSeekBarChangeListener, View.OnLongClickListener {
+ static final String labelDefault = "rowViewRes";
+ static final double minDefault = 0f;
+ static final double maxDefault = 100f;
+
+ String key;
+ String label = labelDefault;
+ double min = minDefault;
+ double max = maxDefault;
+ double def = min;
+ volatile double actual = def;
+
+ View handleView;
+ SeekBar bar;
+ TextView actualDisp;
+ TextView labelView;
+ int listPosition = -1;
+
+ D d;
+
+ Handle(D d, HandleDef def, double actual) { this(d, def.Label, def.Min, def.Max, def.Default, actual, def.Key); }
+
+ Handle(D d, String label, double min, double max, double def, double actual, String key) {
+ this.d = d.klon(this);
+ this.label = label;
+ this.min = min;
+ this.max = max;
+ this.def = def;
+ this.actual = actual;
+ this.key = key;
+ }
+
+ /* listener implementation */
+ @Override
+ public void onStopTrackingTouch(SeekBar bar) { saveActual(); }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar bar) { }
+
+ @Override
+ public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
+ if(fromUser) { actual = progress2value(bar.getProgress()); }
+ dispActual();
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ reset();
+ return true;
+ }
+
+ void dispActual() {
+ actualDisp.setText(String.format(A.lo, "%.2f", actual));
+ actualDisp.invalidate();
+ }
+
+ double getActual() { return actual; }
+
+ int getActualInt() { return (int)Math.round(actual); }
+
+ void adjustBar() {
+// d.l(String.format("+++ adjustBar, key=%s, actual=%f, bar=%d", key, actual, bar.hashCode()));
+ bar.setProgress(value2progress(actual));
+ bar.invalidate();
+ }
+
+ void setActual(double actual) {
+ this.actual = actual;
+ if(isInflated()) adjustBar();
+ saveActual();
+ }
+
+ void reset() { setActual(def); }
+
+ void saveActual() {
+ DA.sp.edit().putFloat(key, Double.valueOf(actual).floatValue()).apply(); }
+
+ boolean isInflated() {
+ return (handleView != null && handleView.getId() == listPosition); }
+
+ double progress2value(int progress) { return min + (max - min) * progress / C.maxBarProgress; }
+
+ int value2progress(double value) { return (int)Math.round(C.maxBarProgress * (value - min) / (max - min)); }
+
+ void infillView(View handleView, int listPosition) {
+ this.listPosition = listPosition;
+ this.handleView = handleView;
+ handleView.setId(listPosition);
+ labelView = ((TextView)handleView.findViewById(R.id.label));
+ labelView.setText(label);
+ labelView.setOnLongClickListener(this);
+ final TextView tmin = (TextView)handleView.findViewById(R.id.min);
+ if(tmin != null) tmin.setText(String.format(A.lo, "%.1f", min));
+ final TextView tmax = (TextView)handleView.findViewById(R.id.max);
+ if(tmax != null) tmax.setText(String.format(A.lo, "%.1f", max));
+ bar = (SeekBar)handleView.findViewById(R.id.bar);
+ bar.setMax(C.maxBarProgress);
+ adjustBar();
+ actualDisp = handleView.findViewById(R.id.actual);
+ dispActual();
+ bar.setOnSeekBarChangeListener(this);
+ actualDisp.setOnLongClickListener(this);
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/RotatedLv.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/RotatedLv.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,31 @@
+package hh.michelson;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+import java.util.Locale;
+
+import hh.lib.D;
+
+public class RotatedLv extends ListView {
+ static String tag;
+ D d;
+
+ public RotatedLv(Context c) { this(c, null); }
+
+ public RotatedLv(Context c, AttributeSet a) {
+ super(c, a);
+ d = new D(c, getClass().getSimpleName() + "." + getId());
+ }
+
+ protected void onMeasure(int w, int h) {
+ super.onMeasure(w, h);
+ Resources r = d.c.getResources();
+ int W = r.getDimensionPixelSize(R.dimen.graph_width);
+ int H = C.windowWidth - r.getDimensionPixelSize(R.dimen.graph_heigth);
+ setMeasuredDimension(W, H);
+ d.l(4, String.format(Locale.getDefault(), "onMeasure, adjusted to width=%d, heigth=%d", W, H));
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/Wave.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/Wave.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,296 @@
+package hh.michelson;
+
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import hh.lib.D;
+import hh.lib.DA;
+
+enum Wave {
+ SINE {
+ @Override
+ double value(double x) {
+ return Math.sin(x);
+ }
+ },
+ SAW {
+ @Override
+ double value(double x) {
+ return x / C.PI - 1;
+ }
+ },
+ TRIAN {
+ @Override
+ double value(double x) {
+ return (x < C.PI ? 2*x/ C.PI - 1 : 2*(1 - x/ C.PI) + 1);
+ }
+ },
+ SQUARE {
+ @Override
+ double value(double x) {
+ return x < C.PI ? 1 : -1;
+ }
+ },
+ F1 {
+ final HandleDef[] F1Def = new HandleDef[] {
+ new HandleDef(name() + "K00", "N", 1, 30, 3),
+ new HandleDef(name() + "K01", "K", 0, 2, 1)
+ };
+
+ @Override
+ public String toString() {
+ return "SUM(N) sin(N * x) / pow(N, K)";
+ }
+
+ @Override
+ void setHandleDef() { paramsDef = F1Def; }
+
+ @Override
+ void adjustParams() {
+ n = (int)Math.round(params[0]);
+ k1 = new double[n];
+ for(int i = 1; i < n; i += 2) k1[i] = Math.pow(i, params[1]);
+ }
+
+ @Override
+ double value(double x) {
+ double y = 0;
+ for(int i = 1; i < n; i += 2) y += Math.sin(i * x) / k1[i];
+ return y;
+ }
+ },
+ F2 {
+ @Override public String toString () { return "sin(x)+cos(x) + (sin(2x)+cos(2x))/2 + (sin(4x)+cos(4x))/4"; }
+
+ @Override
+ double value(double x) {
+ return (Math.sin(x) + Math.cos(x) + 0.5 * Math.sin(2*x) + 0.5 * Math.cos(2*x) +
+ 0.25 * Math.sin(4*x) + 0.25 * Math.cos(4*x));
+ }
+ },
+ F3 {
+ @Override
+ public String toString () { return "cos(ln(x))"; }
+
+ @Override
+ double value(double x) { return Math.cos(Math.log(x)); }
+ },
+ F4 {
+ @Override public String toString () { return "cos(x^3))"; }
+
+ @Override
+ double value(double x) { return Math.cos(Math.pow(x, 3)); }
+ },
+ F5 {
+ @Override public String toString () { return "cos(sin(x)))"; }
+
+ @Override
+ double value(double x) { return Math.cos(Math.sin(x)); }
+ },
+ F6 {
+ @Override public String toString () { return "sin(cos(x)))"; }
+
+ @Override
+ double value(double x) { return Math.sin(Math.cos(x)); }
+ },
+ F7 {
+ @Override public String toString () { return "sin(sin(x)))"; }
+
+ @Override
+ double value(double x) { return Math.sin(Math.sin(x)); }
+ },
+ F8 {
+ @Override public String toString () { return "cos(cos(x)))"; }
+
+ @Override
+ double value(double x) { return Math.cos(Math.cos(x)); }
+ },
+ F9 {
+ @Override public String toString () { return "ln(sin(x))"; }
+
+ @Override
+ double value(double x) { return Math.log(Math.sin(x)); }
+ },
+ F10 {
+ @Override public String toString () { return "tan(sin(x)+cos(x))"; }
+
+ @Override
+ double value(double x) { return Math.tan(Math.sin(x) + Math.cos(x)); }
+ },
+ MICHELSON {
+ final int numOfWaves = 20;
+
+ @Override public String toString () { return "SUMn (Kn * sin(n*x))"; }
+
+ @Override
+ void setHandleDef() {
+ paramsDef = new HandleDef[numOfWaves];
+ for(int i = 0; i t = new ArrayList<>();
+ final List n = new ArrayList<>();
+ for (Wave w : Wave.values()) { t.add(w.toString()); n.add(w.name()); }
+ titles = new String[t.size()];
+ t.toArray(titles);
+ names = new String[n.size()];
+ n.toArray(names);
+ }
+
+ class ParamHandle extends Handle implements SeekBar.OnSeekBarChangeListener {
+
+ ParamHandle(D d, int i) { super(d, paramsDef[i], params[i]); }
+
+ @Override
+ public void onProgressChanged(SeekBar bar, int progress, boolean fromUser) {
+ super.onProgressChanged(bar, progress, fromUser);
+ if(fromUser) {
+ synchronized(context) {
+ setParam(listPosition, actual);
+ adjust();
+ }
+ graphView.invalidate();
+ }
+ }
+
+ @Override
+ public boolean onLongClick(View v) {
+ final boolean r = super.onLongClick(v);
+ synchronized(context) {
+ setParam(listPosition, actual);
+ adjust();
+ }
+ graphView.invalidate();
+ return r;
+ }
+
+ @Override
+ void infillView(View handleView, int listPosition) {
+ super.infillView(handleView, listPosition);
+ labelView.setOnLongClickListener(this);
+ bar.setOnSeekBarChangeListener(this);
+ }
+ }
+
+ String tag = getClass().getName();
+
+ D d;
+ A context;
+ int n;
+ double k1[];
+ double[] params = null;
+ HandleDef[] paramsDef = null;
+ ParamHandle[] paramHandles;
+ RotatedLv paramView;
+
+ GraphView graphView;
+ float[] graphValues;
+ double maxY = 0d;
+
+ double value(double x) {
+ return 0.0d;
+ }
+
+ boolean hasParams() { return params != null; }
+
+ void init(A context) {
+ this.context = context;
+ this.d = context.d.klon(this);
+ ((TextView)context.findViewById(R.id.dispWave)).setText(String.format("%s", this));
+ paramView = context.findViewById(R.id.params);
+ graphView = context.findViewById(R.id.graph);
+ graphValues = new float[(int)Math.ceil(A.rs.getDimension(R.dimen.graph_width))];
+ /*graphValues = new double[1 + (int)Math.ceil(2 * D.SR / freq)];*/
+ graphView.setGraphValues(graphValues);
+ initParams();
+ adjust();
+ setParamsView();
+ graphView.invalidate();
+ }
+
+ void adjust() {
+ adjustParams();
+ computeWaveGraph();
+ }
+
+ void setHandleDef() {}
+
+ void initParams() {
+ if(params == null) {
+ setHandleDef();
+ if(paramsDef != null) {
+ params = new double[paramsDef.length];
+ for(int i = 0; i < params.length; i++)
+ params[i] = DA.sp.getFloat(paramsDef[i].Key, Double.valueOf(paramsDef[i].Default).floatValue());
+ }
+ }
+ }
+
+ void adjustParams() {} // předzpracování parametrů vlny po změně nastavitelného parametru
+
+ void setParam(int i, double value) { params[i] = value; }
+
+ void resetParams() {
+ if(hasParams()) {
+ for(int i=0; i < params.length; i++) paramHandles[i].reset();
+ synchronized(context) {
+ for(int i = 0; i < params.length; i++) setParam(i, paramsDef[i].Default);
+ adjust();
+ }
+ graphView.invalidate();
+ }
+ }
+
+ void setParamsView() {
+ final View reset = context.findViewById(R.id.reset);
+ if(hasParams()) {
+ reset.setVisibility(View.VISIBLE);
+ reset.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ resetParams();
+ }
+ });
+ setParamHandles();
+ paramView.setAdapter(new Windrover(d, "Params", context, R.layout.wave_params_row, paramHandles));
+ }
+ else {
+ reset.setVisibility(View.GONE);
+ paramView.setAdapter(null);
+ }
+ }
+
+ void setParamHandles() {
+ paramHandles = new ParamHandle[params.length];
+ for(int i = 0; i < params.length; i++) { paramHandles[i] = new ParamHandle(d, i); }
+ }
+
+ void computeWaveGraph() {
+ final double dt = 2 * C.twoPI / graphValues.length; /*dt = D.twoPI * freq / D.SR,*/
+ double t = 0d;
+ maxY = 0d;
+ for (int i=0; i maxY) maxY = Math.abs(v);
+ graphValues[i] = Double.valueOf(v).floatValue();
+ t += dt;
+ }
+ graphView.setMaxY(maxY);
+ context.setMaxY(maxY);
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/WavesFragment.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/WavesFragment.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,45 @@
+package hh.michelson;
+
+import android.app.Activity;
+import android.app.ListFragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+public class WavesFragment extends ListFragment {
+ OnFragmentInteractionListener mListener;
+ boolean selected = false;
+
+ @Override
+ public void onAttach(Activity context) {
+ super.onAttach(context);
+ setListAdapter(new ArrayAdapter<>(getActivity(), R.layout.wave_list_row, Wave.titles));
+ mListener = (OnFragmentInteractionListener)context;
+ setRetainInstance(true); // zachovat fragment při otáčení obrazovky
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ return inflater.inflate(R.layout.list_view, container, false);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ mListener.setNewWave(position);
+ selected = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if(!selected) mListener.setNewWave(-1);
+ mListener = null;
+ }
+
+ public static interface OnFragmentInteractionListener {
+ public void setNewWave(int position);
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/java/hh/michelson/Windrover.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/java/hh/michelson/Windrover.java Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,45 @@
+package hh.michelson;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+
+import hh.lib.D;
+
+class Windrover extends BaseAdapter implements ListAdapter {
+ D d;
+ Context c;
+ int rowViewRes;
+ Handle[] handles;
+
+ Windrover(D d, String label, Context c, int rowViewRes, Handle[] handles) {
+ this.d = d.klon(getClass().getSimpleName() + "." + label);
+ this.c = c;
+ this.rowViewRes = rowViewRes;
+ this.handles = handles;
+ }
+
+ public Object getItem(int pos) {
+ if(d.ll(4)) d.l(String.format("getItem=%s", handles[pos]));
+ return handles[pos];
+ }
+
+ public long getItemId(int pos) {
+ if(d.ll(4)) d.l(String.format(A.lo, "getItemId=%d", pos));
+ return pos;
+ }
+
+ public int getCount() {
+ if(d.ll(4)) d.l(String.format(A.lo, "getCount=%d", handles.length));
+ return handles.length;
+ }
+
+ public View getView(int pos, View handleView, ViewGroup parent) {
+ View v = ((LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(rowViewRes, null);
+ handles[pos].infillView(v, pos);
+ return v;
+ }
+}
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/handles_row.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/handles_row.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/header.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/header.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/list_view.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/list_view.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/main.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/main.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/wave.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/wave.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/wave_list_row.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/wave_list_row.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/layout/wave_params_row.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/layout/wave_params_row.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/values/dimens.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/values/dimens.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,8 @@
+
+
+ 320px
+ 320px
+ 60px
+ 15px
+ 20px
+
\ No newline at end of file
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/michelson/src/main/res/values/strings.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/michelson/src/main/res/values/strings.xml Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,4 @@
+
+
+ .michelson.17
+
diff -r 000000000000 -r 16509f98f301 android/michelson.studio/settings.gradle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/android/michelson.studio/settings.gradle Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,1 @@
+include ':michelson', ':lib031-release'
diff -r 000000000000 -r 16509f98f301 python/michelson.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/python/michelson.py Fri Nov 22 09:40:16 2019 +0100
@@ -0,0 +1,103 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
+import sys, math
+from PyQt5.QtWidgets import QWidget, QApplication, QSlider, QVBoxLayout, QPushButton
+from PyQt5.QtGui import QPainter, QPen, QStaticText
+from PyQt5.QtCore import Qt
+
+pos = (300, 300)
+size = 300
+margin = 10
+
+class Michelson(QWidget):
+
+ def __init__(self):
+ super().__init__()
+
+ self.setGeometry(pos[0], pos[1], size + 20 * 40 + 0, size)
+ self.setWindowTitle('Michelson')
+
+ self.s = []
+ for i in range(0, 20):
+ self.s.append(SliderK(self, i))
+ self.s[i].setGeometry(size + i*40, margin, 40, size - 2*margin)
+
+ self.show()
+
+ def paintEvent(self, e):
+ qp = QPainter()
+ qp.begin(self)
+ self.drawGraph(qp)
+ qp.end()
+
+ def drawGraph(self, qp):
+ vmax, v = self.values()
+ y0 = (size - 2*margin) / 2 + margin
+ yk = (y0 - margin) / vmax if vmax > 0 else 0
+ qp.setPen(QPen(Qt.black, 1, Qt.SolidLine))
+ qp.drawLine(0, y0, size, y0)
+ qp.setPen(QPen(Qt.green, 2, Qt.SolidLine))
+ for i in range(0, len(v) - 1):
+ qp.drawLine(margin + i, y0 - yk * v[i], margin + i + 1, y0 - yk * v[i+1])
+
+ def values(self):
+ vmax = 0
+ v = []
+ x = 0
+ dx = 2 * math.pi / (size - 2 * margin)
+ while x <= 2 * math.pi:
+ y = 0
+ for i in range(0, 20):
+ y = y + self.s[i].v * math.sin((i + 1) * x)
+# y = math.sqrt(math.pi * math.pi - (x-math.pi) * (x-math.pi))
+ if math.fabs(y) > vmax: vmax = math.fabs(y)
+ v.append(y)
+ x = x + dx
+ return (vmax, v)
+
+
+class SliderK(QWidget):
+
+ def __init__(self, w, i):
+ super().__init__(w)
+
+ self.w = w
+ self.i = i
+ self.init = 0
+ self.v = 0
+
+ label = QPushButton(str(i+1))
+
+ self.val = QPushButton(str(self.init))
+ self.val.clicked.connect(self.resetK)
+
+ self.sld = QSlider(Qt.Vertical, self.w)
+ self.sld.setFocusPolicy(Qt.NoFocus)
+ self.sld.setMaximum(20)
+ self.sld.setMinimum(-20)
+ self.sld.setValue(self.init)
+ self.sld.valueChanged[int].connect(self.changeK)
+
+ vbox = QVBoxLayout()
+
+ vbox.addWidget(label)
+ vbox.addWidget(self.val)
+ vbox.addWidget(self.sld)
+
+ self.setLayout(vbox)
+
+ def changeK(self, v):
+# print("changeK({})={}".format(self.i, v))
+ self.v = v
+ self.val.setText(str(v))
+ self.w.update()
+
+ def resetK(self):
+ self.sld.setValue(self.init)
+
+
+if __name__ == '__main__':
+ app = QApplication(sys.argv)
+ widget = Michelson()
+ sys.exit(app.exec_())