Arduino:Примеры/EsploraRemote

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Удаленное управление и тестирование Esplora[1]

Этот пример создан, чтобы работать вместе с приложением на языке Processing (это язык программирования с открытым кодом). Вам нужно будет установить это приложение на компьютер, чтобы с его помощью, во-первых, считывать данные с датчиков Esplora, а во-вторых, управлять RGB-светодиодом и зуммером Esplora. Кроме того, графический интерфейс в скетче Processing будет представлять собой виртуальную копию вашей Esplora.

Если Processing на вашем компьютере нет, этот пример можно протестировать и при помощи окна Serial Monitor, взаимодействуя с Esplora при помощи специальных интерактивных команд, отсылаемых через последовательный порт.

С этим примером может работать два скетча Processing (ссылки на них можно найти ниже). Первый (попроще, для начинающих Processing-программистов) выводит данные от датчиков в текстовом виде, тогда как второй (посложнее, для продвинутых) демонстрирует графическую репрезентацию Esplora.

В этом ZIP-файле содержится тот скетч, что попроще, а в этом – что посложнее(также с кодом можно ознакомиться ниже).

Вам лишь нужно распаковать файл внутри папки скетчей Processing, а затем запустить саму Processing и открыть в ней файл с расширением *.pde.

Необходимое оборудование

  • Плата Arduino Esplora;

Необходимое ПО

Цепь

Для этого примера нужна лишь Arduino Esplora.

Расположение элементов Arduino Esplora

Код для Arduino

Этот пример создает интерфейс последовательного соединения между компьютером и Esplora. То есть, отвечая на команды, которые вы будете отправлять через последовательный порт, Esplora будет считывать данные со своих датчиков, а также управлять своими зуммером и RGB-светодиодом.

Ваши команды будут начинаться с символа, обозначающего ту или иную операцию, которую вы хотите проделать с Esplora. В довесок к этому символу будет идти значение, которое нужно будет передать тому или иному элементу Esplora (в данном случае, RGB-светодиоду или зуммеру), или количество «проб» данных, которые нужно собрать.

Используя этот скетч в комбинации с приложением на Processing, вы сможете управлять Esplora, не просто печатая те или иные команды, а пользуясь специальным графическим интерфейсом. Впрочем, вы по-прежнему можете управлять платой и при помощи текстовых команд, вводимых в окне Serial Monitor или какой-либо программе, выполняющей его функции.

Данные ото всех датчиков будут отображаться и на «рисунке» Esplora, который вы будете видеть на экране компьютера, а RGB-светодиодом и зуммером можно будет управлять при помощи слайдеров, простой кликая на нужный компонент.

ПРИМЕЧАНИЕ: Одновременно доступ к последовательному порту компьютера может иметь только одна программа. То есть, если у вас открыт Serial Monitor, то скетч Processing «пробиться» к последовательному порту Esplora не сможет. И наоборот, если у вас будет запущен скетч Processing, то вы не сможете открыть Serial Monitor и перепрограммировать свою Esplora.

/*

Удаленное управление Esplora

Этот скетч позволяет вам тестировать все периферийные элементы Esplora. 
Кроме того, им можно пользоваться вместе со скетчем ProcessingStart (для Processing).

Загрузив его, вы можете открыть Serial Monitor 
и вписать там одну из нижеследующих команд (без кавычек):

«D» – выводит данные ото всех датчиков, разделяя их запятой.
О том, что это за данные, можно узнать в функции dumpInputs().

«Rxxx», «Gxxx», «Bxxx» – задают цвет RGB-светодиода.
Например, если написать «R255», мы включим на полную яркость красный канал RGB-светодиода.
Если написать «G128», то мы установим среднюю яркость у среднего канала.
Или, если написать «B0», то мы совсем отключим синий канал. 

«Txxxx» – проигрывает ноту на зуммере.
Число – это частота, т.е., например, если вписать команду «T440», зуммер сыграет ноту «ля».
А если вписать «T0», это выключит зуммер.

Создан 22 ноября 2012 Энрико Гуэли (Enrico Gueli, enrico.gueli@gmail.com),
модифицирован 23 декабря 2012 Томом Иго (Tom Igoe). 

*/

#include <Esplora.h>

void setup() {
  while (!Serial); // нужно для плат на базе Leonardo (вроде Esplora)
  Serial.begin(9600);
}

void loop() {
  if (Serial.available())
    parseCommand();
}

/*
Эта функция считывает текстовый символ от команды, 
присланной через последовательный порт, а затем решает, что делать дальше. 
Под «что делать дальше» подразумевается вызов функции,
соответствующей считанному текстовому символу.
Например, команда «D» вызывает функцию dumpInputs(), «Rxxx» – setRed() и т.д.
*/

void parseCommand() {
  char cmd = Serial.read();
  switch (cmd) {
    case 'D':
      dumpInputs();
      break;
    case 'R':
      setRed();
      break;
    case 'G':
      setGreen();
      break;
    case 'B':
      setBlue();
      break;
    case 'T':
      setTone();
      break;
  }
}

void dumpInputs() {
  Serial.print(Esplora.readButton(SWITCH_1));
  Serial.print(',');
  Serial.print(Esplora.readButton(SWITCH_2));
  Serial.print(',');
  Serial.print(Esplora.readButton(SWITCH_3));
  Serial.print(',');
  Serial.print(Esplora.readButton(SWITCH_4));
  Serial.print(',');
  Serial.print(Esplora.readSlider());
  Serial.print(',');
  Serial.print(Esplora.readLightSensor());
  Serial.print(',');
  Serial.print(Esplora.readTemperature(DEGREES_C));
  Serial.print(',');
  Serial.print(Esplora.readMicrophone());
  Serial.print(',');
  Serial.print(Esplora.readJoystickSwitch());
  Serial.print(',');
  Serial.print(Esplora.readJoystickX());
  Serial.print(',');
  Serial.print(Esplora.readJoystickY());
  Serial.print(',');
  Serial.print(Esplora.readAccelerometer(X_AXIS));
  Serial.print(',');
  Serial.print(Esplora.readAccelerometer(Y_AXIS));
  Serial.print(',');
  Serial.print(Esplora.readAccelerometer(Z_AXIS));
  Serial.println();
}

void setRed() {
  Esplora.writeRed(Serial.parseInt());
}

void setGreen() {
  Esplora.writeGreen(Serial.parseInt());
}

void setBlue() {
  Esplora.writeBlue(Serial.parseInt());
}

void setTone() {
  Esplora.tone(Serial.parseInt());
}


Код для Processing

Простой пример

ProcessingSimpleRemote.pde

/*
Скетч к Processing для удаленного управления Esplora

Этот скетч позволит вам управлять записью и считыванием с некоторых
устройств ввода и вывода, которыми оснащена Esplora.

Чтобы получить ответ, вам надо будет вписать одну из нижеследующих команд (без кавычек):

«D» – выводит данные ото всех датчиков.

«Rxxx», «Gxxx», «Bxxx» – задают цвета для RGB-светодиода. 
Например, «R255» включит красный канал светодиода на полную яркость,
«G128» включит яркость зеленого канала наполовину,
а «B0» полностью выключит синий канал.

«Txxxx» – проигрывает ноту при помощи зуммера.
Номер вместо «иксов» обозначает частоту звука, т.е. «T440» будет означать среднее «ля».
Если написать «Т0», то это отключит зуммер.

Создан 22 декабря 2012 Томом Иго (Tom Igoe).

Этот код не защищен авторским правом.
*/

import processing.serial.*;        // инициализируем библиотеку Serial

Serial Esplora;                    // подключаем Esplora через последовательный порт
String numberString = "";          // строка для порта, который будет инициализирован следующим
String[] valueNames = {            // названия, связанные со значениями, которые будет отправлять Esplora
  "switch 1", "switch 2", "switch 3", "switch 4", "slider", 
  "light sensor", "temperature (Celsius)", "microphone", "joystick switch", 
  "joystick X", "joystick Y", "accelerometer X axis", "accelerometer Y axis", 
  "accelerometer Z axis"
};
String displayString = "";         // строка для отображения информации на экране
char command;                      // однобуквенная команда от пользователя

void setup() {
  size(640, 480);  // размер экрана программы
}

void draw() {
  // очищаем экран:
  background(0);
  fill(255);
  int linePosition = 20;        // задаем позицию для строки с начальным текстом:

  // Если Esplora не инициализирована:
  if (Esplora == null) {
    // Получаем список последовательных портов:
    String[] portList = Serial.list();
    // Показываем на экране инструкции:
    text("Type P and the port number of your Esplora:", linePosition, 20);  //  "Напишите «P» и номер порта вашей Esplora:"
    // Показываем на экране список портов:
    for (int i = 0; i < portList.length; i++) {
      linePosition = (i+4) *20;
      text("port " + i + ":  " + portList[i], 20, linePosition);
    }
  } 

  // Если все необходимые порты имеются:
  else {
    // Показываем самые последние новости:
    text(displayString, 20, linePosition);

    // Опускаемся чуть ниже, где будем показывать обновления:
    linePosition = 240;    
    text("type Rxxx (xxx = a number 0-255) and return to set the red value", 20, linePosition);  //  "Впишите «Rxxx» (где «ххх» – это номер от 0 до 255) и нажмите Enter, чтобы задать значение для красного канала RGB-светодиода"
    linePosition += 20;
    text("type Gxxx (xxx = a number 0-255) and return to set the green value", 20, linePosition);  "Впишите «Gxxx» (где «ххх» – это номер от 0 до 255) и нажмите Enter, чтобы задать значение для зеленого канала RGB-светодиода"
    linePosition += 20;
    text("type Bxxx (xxx = a number 0-255) and return to set the blue value", 20, linePosition);  "Впишите «Bxxx» (где «ххх» – это номер от 0 до 255) и нажмите Enter, чтобы задать значение для синего канала RGB-светодиода"
    linePosition += 20;
    text("type Txxx (xxx = a number 0 - 20000) and return to set the tone", 20, linePosition);  "Впишите «Txxx» (где «ххх» – это номер от 0 до 2000) и нажмите Enter, чтобы задать нужную ноту"
    linePosition += 20;
    text("type D to get the values of all sensors", 20, linePosition);  //  "Впишите D, чтобы получить значения ото всех датчиков"

  }
}

void serialEvent(Serial Esplora) {
  // считываем строку, пришедшую через последовательный порт:
  displayString = Esplora.readStringUntil('\n');
  // анализируем ответ и добавляем надписи для отображения на экране:
  displayString = parseResponse(displayString);
}

void keyReleased() {
  // буквы A-Z и a-z могут быть командами:
  if (key >= 'A' && key <= 'z') {   
    command = key;
  }
  // если нажат Enter, останавливаем выбор номера порта:
  if (key == ENTER ) {
    String outputString = "";
    switch (command) {
    case 'D':     // нужно показать данные ото всех датчиков Esplora
    case 'd': 
      outputString = "D" + numberString;
      break;
    case 'R':     // задаем значение для красного канала (0 - 255)
    case 'r':
      outputString = "R" + numberString;
      break;
    case 'G':     // задаем значение для зеленого канала (0 - 255)
    case 'g': 
      outputString = "G" + numberString;
      break;
    case 'B':     // задаем значение для синего канала (0 - 255)
    case 'b': 
      outputString = "B" + numberString;
      break;
    case 'T':     // задаем звук (0 - 20000)
    case 't': 
      outputString = "T" + numberString;  
      break;
    case 'P':     // открываем последовательный порт
    case 'p':
      if (Esplora == null) {
        choosePort(numberString);
        outputString = "Port " + numberString + " opened.";
      } 
      else {
        outputString = "Port already open";
      }
      break;
    }
    // Обновляем строку на дисплее последними командами от пользователя:
    displayString = outputString;
    // Если есть какие-либо данные для записи на Esplora, отправляем их:
    if (outputString != "") {
      Esplora.write(outputString);
      numberString = "";        // очищаем строку с числами
    }
    command = 0;                // очищаем переменную с командой
  }
  // Если пользователь нажимает от 0 до 9, используем это для выбора номера порта:
  if (key >= '0' && key <= '9') {
    numberString += key;
  }
}

void choosePort(String thisPort) {
  int whichPort = int(thisPort);    // конвертируем строку в целое число
  // Если считанный номер является подходящим значением, открываем порт:
  if (whichPort >= 0 && whichPort < Serial.list().length) {
    // Берем название порта из списка последовательных портов:
    String portName = Serial.list()[whichPort];
    // Инициализируем следующую Esplora:
    Esplora = new Serial(this, portName, 9600);
    // Считываем байты в буфер, пока не наткнемся на символ перевода строки (номер 10 в таблице ASCII):
    Esplora.bufferUntil('\n');
    // Очищаем строку с номером порта:
    numberString = "";
  }
}

String parseResponse(String inputString) {
  String response = "";                           // строка с данными, возвращенными от функции
  int[] values = int(split(inputString, ","));    // разбиваем входящую строку по запятым

  // прочесываем входящий массив, добавляя к значениям названия из массива valueNames:
  for (int i = 0; i < values.length; i++) {
    response += (valueNames[i] + ": " + values[i] + "\n");
  }
  // Возвращаем итоговую строку:
  return response;
}

Сложный пример

ProcessingEsploraRemote.pde

import processing.serial.*;

final int WIDTH = 1162;
final int HEIGHT = 652;

final int margin = 20;
final int boardRatio = 2;
final int b_width = WIDTH - boardRatio*margin;
final int b_height = b_width/3;

final int middleY = margin+b_height/2;

final int jsCenterX = 255;
final int jsCenterY = 289;
final int jsOuterRadius = 80;
final int jsInnerRadius = 60;
final int jsSwing   = 60;

final int switchLeft      = 787;
final int switchesCenterX = 849;
final int switchRight     = 910;
final int switchTop       = 231;
final int switchesCenterY = 290;
final int switchBottom    = 351;
final int switchRadius    = 20;

final int accelCenterX = 549;
final int accelCenterY = 289;
final int accelBarHeight = 20;
final int accelBarWidth  = 50;

final int micCenterX = 377;
final int micCenterY = 419;
final int micRadiusOffset = 10;
final float micRadiusScale = 50;

final int sliderLeft = 448;
final int sliderRight = 645;
final int sliderY = 419;
final int sliderCursorHeight = 12;
final int sliderCursorWidth  = 35;

final int lightCenterX = 765;
final int lightCenterY = 167;
final int lightRadius = 10;
final float lightScale = 70;

final int ledCenterX = 720;
final int ledCenterY = 419;
final int ledRadius = 10;

final int buzzCenterX = 336;
final int buzzCenterY = 167;
final int buzzRadius = 30;

final int tempCenterX = 549;
final int tempCenterY = 351;

PImage esploraBkg;

void setup() {
  size(WIDTH, HEIGHT);

  esploraBkg = loadImage("esplora.png");

  Esplora.begin(this);
  
  for (SlideControl rc : rgbControls) {
    rc.send();
  }
}



void draw() {
  Esplora.update();
  
  background(255);
  ellipseMode(RADIUS);

  /*
   * Рисунок Esplora
   */
  image(esploraBkg, 0, 0);

  pushMatrix(); 
  {
    translate(jsCenterX, jsCenterY);

    /*
     * Джойстик, статичная часть
     */
    fill(64);
    ellipse(0, 0, jsOuterRadius, jsOuterRadius);
 
    /*
     * Джойстик, двигающаяся часть
     */
    if (Esplora.joystickSwitch)
      fill(255, 0, 0);
    else
      fill(0, 0, 0);    
    ellipse(Esplora.joystickX * jsSwing, 
    Esplora.joystickY * jsSwing, 
    jsInnerRadius, jsInnerRadius);
  } 
  popMatrix();

  /*
   * Кнопки
   */
  int[][] switchMap = new int[][] {
    { switchesCenterX, switchBottom }, 
    { switchLeft     , switchesCenterY }, 
    { switchesCenterX, switchTop }, 
    { switchRight    , switchesCenterY }, 
  };
  pushMatrix(); 
  {
    for (int i=0; i<switchMap.length; i++) {
      pushMatrix(); 
      {
        translate(switchMap[i][0], switchMap[i][1]);
        if (Esplora.switches(i))
          fill(255, 0, 0);
        else
          fill(0, 0, 0);
        ellipse(0, 0, switchRadius, switchRadius);
      } 
      popMatrix();
    }
  } 
  popMatrix();

  /*
   * Оси акселерометра
   */
  pushMatrix(); 
  {
    translate(accelCenterX, accelCenterY);

    fill(192);
    drawAccelBar(Esplora.accelX);
    rotate(-PI/2);
    drawAccelBar(Esplora.accelY);
    rotate(-PI/2-PI/4);
    drawAccelBar(Esplora.accelZ);
    
    fill(0);
  } 
  popMatrix();
  
  
  /*
   * Микрофон
   */
  pushMatrix();
  {
    translate(micCenterX, micCenterY);
    float radius = micRadiusOffset + Esplora.mic*micRadiusScale;
    ellipse(0, 0, radius, radius);
  }
  popMatrix();
  
  
  pushMatrix(); pushStyle();
  {
    /*
     * Линейный потенциометр, статичная часть
     */
    final int sliderWidth = sliderRight - sliderLeft;
    //line(sliderLeft, sliderY, sliderRight, sliderY);
    
    /*
     * Линейный потенциометр, двигающаяся часть
     */
    fill(143, 121, 99);
    translate(sliderLeft + (1-Esplora.slider) * sliderWidth, sliderY);
    rect(
      -sliderCursorWidth/2, -sliderCursorHeight/2, 
       sliderCursorWidth,    sliderCursorHeight);
    
  }
  popStyle(); popMatrix();
  
  /*
   * Световой датчик
   */
  pushMatrix(); pushStyle();
  {
    translate(lightCenterX, lightCenterY);
    noStroke();
    
    float l = Esplora.light; 
    scale(l);
    for (float r = 1; r > 0; r -= .05f) {
      fill(l*(1-r)*255, l*(1-r)*255, l*(1-r)*255, (1-r)*255);
      ellipse(0, 0, r*lightScale, r*lightScale);
    } 
    
  }
  popStyle(); popMatrix();
  
  /*
   * Температурный датчик
   */
  pushMatrix(); pushStyle();
  {
    translate(tempCenterX, tempCenterY);
    scale(2);
    fill(255);
    textAlign(CENTER, CENTER);
    text((int)Esplora.temperatureC, 0, 0);
    textAlign(LEFT, CENTER);
    text("  °C", 0, 0);
  }
  popStyle(); popMatrix();
  
  
  handleInputControls();
}

void drawAccelBar(float value) {
  pushMatrix(); 
  {
    translate(accelBarHeight/2,0);
    rect(0, -accelBarHeight/2, accelBarWidth*value, accelBarHeight);
  }
  popMatrix();
}

Esplora.pde

float[] ACCEL_SCALES = new float[] {
  160,  // X
  162,  // Y
  140,  // Z
};

class _Esplora {
  /* 
   * Главный поток Processing считывает входящие данные, а Serial-поток записывает их. 
   * Поскольку все переменные независимы друг от друга
   * (т.е. не находятся в противоречивом состоянии),
   * я не обращаю внимания на проблемы с синхронизацией,
   * а переменные для входных данных объявляю изменчивыми,
   * чтобы отключить любую форму кэширования.
   */
  volatile boolean switch1;
  volatile boolean switch2;
  volatile boolean switch3;
  volatile boolean switch4;
  
  volatile float joystickX;
  volatile float joystickY;
  volatile boolean joystickSwitch;

  volatile float accelX;
  volatile float accelY;
  volatile float accelZ;

  volatile float mic;
  volatile float slider;
  volatile float light;
  volatile float temperatureC;

  volatile float ledR;
  volatile float ledG;
  volatile float ledB;
  volatile int tone;
  

  public boolean switches(int sw) {
    switch(sw) { 
    case 0: return switch1;
    case 1: return switch2;
    case 2: return switch3;
    case 3: return switch4;
    }
    return false;
  }

  /**
   * Список корректных последовательных портов. Если при запуске порт 
   * будет иметь одно из этих названий, то он и будет использоваться. 
   * Впрочем, возможно, будет лучше применить более общий подход.
   */
  final String[] SERIAL_PORT_NAMES = {
    "/dev/ttyACM0", 
    "/dev/ttyACM1", 
    "/dev/tty.usbmodemfd131", 
    "/dev/tty.usbmodemfa131", 
    "/dev/tty.usbmodemfd121", 
    "/dev/tty.usbmodemfa121",
    "COM6",
    "COM7"
  };

  Serial serial = null;
  StringBuilder serialRow;


  public void begin(PApplet applet) {
    String[] serialList = Serial.list(); 

    if (serialList.length >= 1) {
      try {
        for (String portNameA : serialList) {
          for (String portNameB : SERIAL_PORT_NAMES) {
            if (portNameB.equals(portNameA)) {
              serial = new Serial(applet, portNameA, 9600);
              println("found serial port: " + portNameA);
              break;
            }
          }
        }
      }
      catch (Exception e) {
        println("Serial port open error: " + e.toString());
      }
    }
  }

  public void update() {
    if (serial == null)
      return;
      
    String s = String.format("D\nR%d\nG%d\nB%d\nT%d\n",
      (int)(constrain(ledR*256, 0, 255)),
      (int)(constrain(ledG*256, 0, 255)),
      (int)(constrain(ledB*256, 0, 255)),
      tone
    );

    serial.write(s);
    delay(10);
    if (serial.available() == 0)
      return;

    String line = serial.readStringUntil('\n');
    if (line == null)
      return;
    
    line = line.trim();

    String[] fields = line.split(",");
    if (fields.length != 14) {
      println("nodump: " + line);
      return;
    }
    
    int[] values = new int[fields.length];
    for (int i=0; i<values.length; i++) {
      values[i] = Integer.parseInt(fields[i]);
    }
    
    switch1 = values[0] == 0;
    switch2 = values[1] == 0;
    switch3 = values[2] == 0;
    switch4 = values[3] == 0;

    joystickSwitch = values[8] == 0;
    joystickX = -((float)values[9]/1024);
    joystickY =   (float)values[10]/1024;
    
    int aX = values[11];
    int aY = values[12];
    int aZ = values[13];
    accelX = aX / ACCEL_SCALES[0];
    accelY = aY / ACCEL_SCALES[1];
    accelZ = aZ / ACCEL_SCALES[2];
    
    mic = (float)values[7] / 1024;
    slider = (float)values[4] / 1024;
    light = (float)values[5] / 1024;
    temperatureC = values[6];
  }
}

_Esplora Esplora = new _Esplora();

Input.pde

boolean lastPressed = false;
int startDragX = 0;
int startDragY = 0;

boolean draggingLed = false;
boolean draggingTone = false;

final int slideBarHeight = 40;
final int slideBarWidth  = 250;

final int toneSliderX = 50;
final int toneSliderY = 50;

final int rgbSlidersX = 700;
final int rgbSlidersY = 500;

abstract class SlideControl {
  float value = 0;
  float valueDragging = value;
  color col;
  SlideControl(color col) {
    this.col = col;
  }
  void beginDrag() { valueDragging = value; }
  void endDrag()   { value = valueDragging; }
  
  abstract void send();
}

class ToneSlideControl extends SlideControl {
  ToneSlideControl() {
    super(color(255,255,255));
  }
  void send(){
    Esplora.tone=(int)(valueDragging*2000);
  }
  void beginDrag() {
    Esplora.tone=(int)(value*2000);
    super.beginDrag();
  }
  void endDrag() {
    Esplora.tone=0;
    super.endDrag();
  }
}

SlideControl[] rgbControls = new SlideControl[] {
  new SlideControl(color(255, 0, 0)) {void send(){Esplora.ledR=valueDragging;}},
  new SlideControl(color(0, 255, 0)) {void send(){Esplora.ledG=valueDragging;}},
  new SlideControl(color(0, 0, 255)) {void send(){Esplora.ledB=valueDragging;}},
};

SlideControl toneControl = new ToneSlideControl();

void handleInputControls() {

  if (mousePressed) {
    if (mouseX > rgbSlidersX && mouseX < rgbSlidersX + slideBarWidth) {
      for (int i=0; i<rgbControls.length; i++) {          
        SlideControl rc = rgbControls[i];
        if (mouseY > rgbSlidersY + i * slideBarHeight - slideBarHeight/2
         && mouseY < rgbSlidersY + i * slideBarHeight + slideBarHeight/2) {
           rc.valueDragging = (mouseX - rgbSlidersX) / (float)slideBarWidth;
           rc.send();
        }
      }
    }

    if (mouseY > toneSliderX && mouseX < toneSliderX + slideBarWidth) {
      if (mouseY > toneSliderY - slideBarHeight/2
       && mouseY < toneSliderY + slideBarHeight/2) {
         toneControl.valueDragging = 
           (mouseX - toneSliderX) / (float)slideBarWidth;
         toneControl.send();
      }
    }    
  }
  else
    toneControl.endDrag();

  
  /*
   * Слайдер для звука
   */
  pushStyle(); pushMatrix();
  {
    translate(toneSliderX, toneSliderY);
    drawSliders(new SlideControl[]{toneControl});
  }
  popMatrix(); popStyle();

  /*
   * Слайдеры для цвета
   */
  pushStyle(); pushMatrix();
  {
    translate(rgbSlidersX, rgbSlidersY);
    drawSliders(rgbControls);
  }
  popMatrix(); popStyle();

}

void drawSliders(SlideControl[] controls) {
  for (int i=0; i<controls.length; i++) {
    SlideControl c = controls[i];
    pushMatrix();
    {
      translate(0, i*slideBarHeight);
      //rect(0, -slideBarHeight/2, slideBarWidth, slideBarHeight);
      gradientRect(
        0, -slideBarHeight/2, 
        slideBarWidth, slideBarHeight,
        color(0,0,0), c.col);

      translate(slideBarWidth*c.valueDragging, 0);
      stroke(255);
      line(0, -slideBarHeight/2, 0, slideBarHeight/2);
    }
    popMatrix();
  }
}

void gradientRect(int l, int t, int w, int h, color c1, color c2) {
  for (int x=l; x<w; x++) {
    float inter = map(x, l, l+w, 0, 1);
    color c = lerpColor(c1, c2, inter);
    stroke(c);
    line(x, t, x, t+h);
  }
}

См.также

  1. Esplora Library

Внешние ссылки