Code – Ben At Work https://benatwork.cc “I have no special talent. I am only passionately curious.” — Albert Einstein Sun, 10 Feb 2013 06:11:30 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 https://benatwork.cc/wp-content/uploads/2013/06/cropped-ucubeCool-2-32x32.png Code – Ben At Work https://benatwork.cc 32 32 How to use Processing in Android mode with the IOIO board https://benatwork.cc/how-to-use-processing-in-android-mode-with-the-ioio-board/ https://benatwork.cc/how-to-use-processing-in-android-mode-with-the-ioio-board/#comments Sun, 04 Mar 2012 18:11:06 +0000 http://benatwork.cc/?p=545 A little while ago I was asked whether it would be possible to control hardware from a phone with the IOIO board from code written in Processing’s Android mode.  Turns out you can – making it quite easy to develop Android apps that can control hardware in the real world – which is awesome.  I didn’t find documentation on how to do this elsewhere online, so I thought I should post the gist of it here for posterity.

This is the IOIO board:

CC BY-NC-SA from Sparkfun.com

 

1.  Download and install the Android SDK.  This link will send you to the download page, which also has a guide worth reading.

2.  Download and install Processing 2.0a4 or later (or the latest version – alpha releases are coming pretty frequently) – support for Android in Processing 1.5.1 no longer works.

2.5. Before trying anything with the IOIO board, you may want to make sure that Processing in Android mode works – plenty can go wrong just getting this set up. I highly recommend reading the Processing in Android wiki page (if you haven’t already) before going too far.

3.  Download this version (ioio.zip) of Ytai’s IOIO library (equivalent to 3.11), exported in .jar format for use in Processing.

4.  Unzip ioio.zip and install it as you would a Processing library (inside a folder called ‘libraries’ in your Processing sketch folder).

5.  Open processing – You should see ‘ioio’ as an option under the ‘sketch’ -> ‘import library’ option

6. The import statements should like this:

import ioio.lib.util.*;
import ioio.lib.impl.*;
import ioio.lib.api.*;
import ioio.lib.api.exception.*;

7.  Go here: http://code.google.com/p/apwidgets/downloads/list  and download the latest (and awesome!) APWidgets library, and put it in the same libraries folder that you put the ioio library.  (this is optional, but if you want any GUI elements like the buttons or checkboxes used in the examples below, you’ll want it).

8. There are a few basic things to remember when doing Android development in Processing for the IOIO board.

  • EVERY sketch you write for the IOIO board needs to have ‘internet’ permissions.  To enable this, go to the ‘Android’ menu in Processing, select ‘Sketch Permissions’, and scroll down until you see the ‘INTERNET’ checkbox, check it, and save.
  • Theory wise, the IOIO functions need to be put in their own thread – if you’re not familiar with threads in Processing, this post from Dan Shiffman is a great place to start.
  • Usage of basic functions from the IOIO library is exactly as it appears in most of Ytai’s examples, but examples (like his HelloIOIO example) that use the AbstractIOIOActivity don’t work.  That’s because we can’t make or use a separate Android activity straight from Processing.
9.  Example 1: Turning on and off the onboard LED.  For this you’ll want a Processing sketch with two tabs (optional, but cleaner) – one for your Processing stuff, and one for our IOIO thread class.
Here’s the first tab:
/* IOIO Test 2 -
 * Toggling the onboard LED on the IOIO board and changing rectangle size through Processing in Android mode
 * by Ben Leduc-Mills
 * This code is Beerware - feel free to reuse and credit me, and if it helped you out and we meet someday, buy me a beer.
 */
 
//import apwdidgets
import apwidgets.*;
 
//import ioio
import ioio.lib.util.*;
import ioio.lib.impl.*;
import ioio.lib.api.*;
import ioio.lib.api.exception.*;
 
//make a widget container and a button
APWidgetContainer widgetContainer;
APButton button1;
 
//our rectangle size
int rectSize = 100;
 
//boolean to turn the light on or off
boolean lightOn = false;
 
//create a IOIO instance
IOIO ioio = IOIOFactory.create();
 
//create a thread for our IOIO code
myIOIOThread thread1; 
 
void setup() {
 
  //instantiate our thread
  thread1 = new myIOIOThread("thread1", 100);
  //start our thread
  thread1.start();
 
  size(480, 800);
  smooth();
  noStroke();
  fill(255);
  rectMode(CENTER);     //This sets all rectangles to draw from the center point
 
  //create new container for widgets
  widgetContainer = new APWidgetContainer(this); 
 
  //create new button from x- and y-pos. and label. size determined by text content
  button1 = new APButton(10, 10, "Toggle LED"); 
 
  //place button in container
  widgetContainer.addWidget(button1);
}
 
void draw() {
  background(#FF9900);
  rect(width/2, height/2, rectSize, rectSize);
}
 
//onClickWidget is called when a widget is clicked/touched
void onClickWidget(APWidget widget) {
 
  if (widget == button1) { //if it was button1 that was clicked
    //rectSize = 100; //set the smaller size
 
    if (lightOn == true) {
      lightOn = false;
      rectSize = 50;
    }
    else if (lightOn == false) {
      lightOn = true;
      rectSize = 100;
    }
  }
}
And here’s the IOIO thread class:
/* This is our thread class, it's a subclass of the standard thread class that comes with Processing
 * we're not really doing anything dramatic, just using the start and run methods to control our interactions with the IOIO board
 */
 
class myIOIOThread extends Thread {
 
  boolean running;  //is our thread running?
  String id; //in case we want to name our thread
  int wait; //how often our thread should run
  DigitalOutput led;  //DigitalOutput type for the onboard led
  int count; //if we wanted our thread to timeout, we could put a counter on it, I don't use it in this sketch
 
  //our constructor
  myIOIOThread(String s, int w) {
 
    id = s;
    wait = w;
    running = false;
    count = 0;
  }
 
  //override the start method
  void start() {
    running = true;
 
    //try connecting to the IOIO board, handle the case where we cannot or the connection is lost
    try {
      IOIOConnect();  //this function is down below and not part of the IOIO library
    }
    catch (ConnectionLostException e) {
    }
 
    //try setting our led pin to the onboard led, which has a constant 'LED_PIN' associated with it
    try {
      led = ioio.openDigitalOutput(IOIO.LED_PIN);
    }
    catch (ConnectionLostException e) {
    }
 
    //don't forget this
    super.start();
  }
 
  //start automatically calls run for you
  void run() {
 
    //while our sketch is running, keep track of the lightOn boolean, and turn on or off the led accordingly
    while (running) {
      //count++;
 
      //again, we have to catch a bad connection exception
      try {
        led.write(lightOn);
      }
      catch (ConnectionLostException e) {
      }
 
      try {
        sleep((long)(wait));
      }
      catch (Exception e) {
      }
    }
  }
 
 //often we may want to quit or stop or thread, so I include this here but I'm not using it in this sketch
  void quit() {
    running = false;
    ioio.disconnect();
    interrupt();
  }
 
//a simple little method to try connecting to the IOIO board
  void IOIOConnect() throws ConnectionLostException {
 
    try {
      ioio.waitForConnect();
    }
    catch (IncompatibilityException e) {
    }
  }
}

10.  The code should compile both with an emulator and on the phone, though obviously it won’t work through the emulator.  To send to the phone, plug your phone into the computer and select ‘sketch’ -> ‘run on device’.  Processing should detect, install, and launch the sketch on your device.  Yay!

11.  After the sketch is installed on your phone, you’ll have to unplug it from your computer, and plug it into your IOIO board.  Make SURE you have USB debugging activated.  Your phone should give some indication that it has a USB connection when you plug it in.

12.  You should see your sketch in along with the other apps – go ahead and launch it.  The button should toggle on and off the yellow LED on the IOIO board. Success! (Note: If it doesn’t work, try relaunching the app a few times – this fixed things for me a few times, though your milage may vary.)

13. Example 2: Toggling a real LED, and changing the color of a rectangle based on analog values from a potentiometer.

For this to work, you’ll need an LED properly (e.g., with a resistor of appropriate value) hooked up to pin 3 of your IOIO board, and a trim pot (or other analog sensor) hooked up to pin 37.

Main Tab:

/* IOIO Test 4 -
 * Toggling a real LED on pin 3, and reading values from a potentiometer on pin 37 through Processing in Android mode
 * by Ben Leduc-Mills
 * This code is Beerware - feel free to reuse and credit me, and if it helped you out and we meet someday, buy me a beer.
 */
 
import apwidgets.*;
 
import ioio.lib.util.*;
import ioio.lib.impl.*;
import ioio.lib.api.*;
import ioio.lib.api.exception.*;
 
APWidgetContainer widgetContainer;
APButton button1;
int rectSize = 100;
boolean lightOn = false;
 
IOIO ioio = IOIOFactory.create();
myIOIOThread thread1; 
 
void setup() {
  thread1 = new myIOIOThread("thread1", 100);
  thread1.start();
 
  size(480, 800);
  smooth();
  noStroke();
  fill(255);
  rectMode(CENTER);     //This sets all rectangles to draw from the center point
 
  widgetContainer = new APWidgetContainer(this);
  button1 = new APButton(10, 10, "Toggle LED");
  widgetContainer.addWidget(button1); //place button in container
}
 
void draw() {
  background(#FF9900);
  //change the fill value based on the analog read of our potentiometer
  fill(thread1.value * 100); //it's * 100 because we only get values from 0-1, so really this should be * 255, or use the map function
  rect(width/2, height/2, rectSize, rectSize);
}
 
//onClickWidget is called when a widget is clicked/touched
void onClickWidget(APWidget widget) {
 
  if (widget == button1) { //if it was button1 that was clicked
 
    if (lightOn == true) {
      lightOn = false;
      rectSize = 50;
    }
    else if (lightOn == false) {
      lightOn = true;
      rectSize = 100;
    }
  }
}

 

And our IOIO thread, with some slight changes:

 

class myIOIOThread extends Thread {
 
  boolean running;
  String id;
  int wait;
  DigitalOutput led;
  AnalogInput in;
  int count;
  int ledpin = 3; //pin for our led
  int potpin = 37; // pin for our potentiometer
  float value; //our analog values range from 0 to 1
 
  myIOIOThread(String s, int w) {
 
    id = s;
    wait = w;
    running = false;
    count = 0;
 
  }
 
  void start() {
    running = true;
 
    try {
      IOIOConnect();
    }
    catch (ConnectionLostException e) {
    }
 
    try {
 
      led = ioio.openDigitalOutput(ledpin);
      in = ioio.openAnalogInput(potpin);
    }
    catch (ConnectionLostException e) {
    }
 
    super.start();
  }
 
  void run() {
 
    while (running) {
      //count++;
      try {
        led.write(lightOn);
        value = in.read();
      }
      catch (ConnectionLostException e) {
      }
      catch (InterruptedException e) {
      }
 
      try {
        sleep((long)(wait));
      }
      catch (Exception e) {
      }
    }
  }
 
  void quit() {
    running = false;
    //led.close();
    ioio.disconnect();
    interrupt();
  }
 
  void IOIOConnect() throws ConnectionLostException {
 
    try {
      ioio.waitForConnect();
    }
    catch (IncompatibilityException e) {
    }
  }
}

 

14.  Upload to your phone as before, hook up your phone to the IOIO board, and launch the app. The button should turn on and off the LED, and the trim pot should change the color of the rectangle in the middle of the screen.  Huzzah!

Here are some pics: for the visual learners out there:

The circuit for the second example, without the phone plugged in

With the phone connected, and the led toggle on

 

With the led toggle off, and the potentiometer turned to make the square grey

 

]]>
https://benatwork.cc/how-to-use-processing-in-android-mode-with-the-ioio-board/feed/ 17
Dangershield VJDJ Code https://benatwork.cc/dangershield-vjdj-code/ https://benatwork.cc/dangershield-vjdj-code/#comments Fri, 30 Dec 2011 00:48:50 +0000 http://benatwork.cc/?p=518 NOTE – this code is for Processing, but was written in Eclipse with the help of the wonderful Proclipsing plugin, so you may need to make some adjustments if you’re running it straight from the Processing IDE.

/*
 * Danger Shield VJDJ
 * Using the Danger Shield from SparkFun to do live Video and Audio manipulation
 * by Ben Leduc-Mills - https://benatwork.cc
 * video code based off code by Dan Shiffman - http://shiffman.net
 * This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license)
 */
 
import processing.core.PApplet;
import processing.serial.Serial;
import processing.video.*;
import ddf.minim.*;
 
public class DangerShieldAV extends PApplet {
 
	Capture myCamera;
	MovieMaker mm;
 
	Minim minim;
	AudioSample kick;
	AudioSample snare;
 
	Serial usbPort;
	// Size of each cell in the grid
	int cellSize = 20;
	// Number of columns and rows in our system
	int cols, rows;
	//int[] sensors;
	String[] sensors;
	boolean firstContact = false;
 
	int slider1, slider2, slider3;
	int button1, button2, button3;
	int photoCell;
 
	public void setup() {
		size(800,600);
 
		minim = new Minim(this);
		kick = minim.loadSample("kick.wav", 2048);
		snare = minim.loadSample("snare.wav", 2048);
 
		myCamera = new Capture(this, width, height, 15);
		mm = new MovieMaker(this, width, height, "drawing.mov");
		cols = width / cellSize;
		rows = height / cellSize;
		colorMode(RGB, 255, 255, 255, 100);
 
		usbPort = new Serial (this, Serial.list( )[0], 9600);
		usbPort.bufferUntil ('\n');
		background(0);
	}
 
	public void draw() {
 
		doSounds();
 
		if (myCamera.available()) {
			myCamera.read();
			myCamera.loadPixels();
 
			// Begin loop for columns
			for (int i = 0; i < cols; i++) {
				// Begin loop for rows
				for (int j = 0; j < rows; j++) {
 
					int x = i*cellSize;
					int y = j*cellSize;
					int loc = (myCamera.width - x - 1) + y*myCamera.width; // Reversing x to mirror the image
 
					float r = red(myCamera.pixels[loc]);
					float g = green(myCamera.pixels[loc]);
					float b = blue(myCamera.pixels[loc]);
					// Make a new color with an alpha component
					int c = color(r, g, b, 55);
 
					// Code for drawing a single rect
					// Using translate in order for rotation to work properly
					pushMatrix();
					translate(x+cellSize/2, y+cellSize/2);
 
					// Rotation formula based on slider1 value
					rotate((float) (2 * PI * brightness(c) / slider1));
					rectMode(CENTER);
 
					fill(c);
					noStroke();
					// Width and height of rects are based on slider2 and slider3 values
					rect(0, 0, slider2/4, slider3/4);
					popMatrix();
				}
			}
		}
 
		mm.addFrame();
	}
 
	public void doSounds() {
 
		//if button1 is pressed, play kick sound
		if(button1 == 0) {
 
			kick.trigger();
 
		}
 
		//if button2 is pressed, play snare sound
		if(button2 == 0) {
 
			snare.trigger();
		}
	}
 
	public void keyPressed() {
		  if (key == ' ') {
		    // Finish the movie if space bar is pressed
		    mm.finish();
		    // Quit running the sketch once the file is written
		    exit();
		  }
		}
 
	public void stop()
	{
	  // always close Minim audio classes when you are done with them
	  kick.close();
	  snare.close();
	  minim.stop();
 
	  super.stop();
	}
 
	public void serialEvent ( Serial usbPort ) {
		String usbString = usbPort.readStringUntil ('\n');
		if (usbString != null) {
			usbString = trim(usbString);
 
			//serial handshake
			if (firstContact == false) {
				if (usbString.equals("Hello")) {
					usbPort.clear();
					firstContact = true;
					usbPort.write('A');
					println("contact");
				}
			}
			else {
 
				//we got something, put the sensor values in their own variables
				sensors = split(usbString, ',');
				for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) {
				}
 
				slider1 = Integer.parseInt(sensors[0]);
				button1 = Integer.parseInt(sensors[1]);
				slider2 = Integer.parseInt(sensors[2]);
				button2 = Integer.parseInt(sensors[3]);
				slider3 = Integer.parseInt(sensors[4]);
				button3 = Integer.parseInt(sensors[5]);
				photoCell = Integer.parseInt(sensors[7]);
 
				//request more data!
				usbPort.write("A");
			}
		}
	}
 
}

 

And here is the Arduino sketch that goes along with it:

/* Arduino code for DangerShield VJDJ
 * by Ben Leduc-Mills
 * Released under the beerware licence (if you use it, and we meet, you can buy me a beer)
 */
 
// Pin definitions
#define SLIDER3  0
#define SLIDER2  1
#define SLIDER1  2
 
//#define KNOCK    5
//
//#define CAP1  2
//#define CAP1  9
 
#define BUTTON1  10
#define BUTTON2  11
#define BUTTON3  12
 
#define LED1  5
#define LED2  6
 
#define BUZZER   3
 
#define TEMP  4
 
#define LIGHT  3
 
#define LATCH 7
#define CLOCK 8
#define DATA 4
 
//char START_BYTE = '*';
char DELIMITER = ',';
//char END_BYTE = '#';
int val;
 
// Shift register bit values to display 0-9 on the seven-segment display
const byte ledCharSet[10] = {
 
  B00111111,B00000110,B01011011,B01001111,B01100110,B01101101,B01111101,B00000111,B01111111,B01101111
};
 
void setup()
{
 
  pinMode(BUZZER, OUTPUT);
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  digitalWrite(LED1,HIGH);
  digitalWrite(LED2,HIGH);
  pinMode(LATCH, OUTPUT);
  pinMode(CLOCK, OUTPUT);
  pinMode(DATA,OUTPUT);
  digitalWrite(10, HIGH);
  digitalWrite(11, HIGH);
  digitalWrite(12, HIGH);
  Serial.begin(9600); 
  establishContact();
}
 
void loop(){
 
  //Serial.print(START_BYTE, BYTE);
 
  val = analogRead(SLIDER1); //read slider1
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = digitalRead(BUTTON1);//read button1
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = analogRead(SLIDER2); //read slider2
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = digitalRead(BUTTON2); //read button2
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = analogRead(SLIDER3); //read slider3
 
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = digitalRead(BUTTON3);//read button3
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  val = analogRead(TEMP);
  Serial.print(val, DEC);
  Serial.print(DELIMITER);  
 
  val = analogRead(LIGHT);
 
  Serial.print(val, DEC);
  Serial.print(DELIMITER);
 
  int sevenSeg = (int)map(val, 400, 900, 1, 9);
  Serial.println(sevenSeg);
 
  digitalWrite(LATCH, LOW);
  shiftOut(DATA, CLOCK, MSBFIRST, ~(ledCharSet[sevenSeg]));
  digitalWrite(LATCH, HIGH);
 
  //Serial.println(END_BYTE, BYTE);
}
 
void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("Hello");   // send a starting message
    delay(300);
  }
}
]]>
https://benatwork.cc/dangershield-vjdj-code/feed/ 2
UCube v08 – now with OpenGL and Toxiclibs! https://benatwork.cc/ucube-v08-now-with-opengl-and-toxiclibs/ https://benatwork.cc/ucube-v08-now-with-opengl-and-toxiclibs/#comments Thu, 17 Feb 2011 01:14:00 +0000 http://benatwork.cc/?p=391 Significant code upgrade in UCube v08 – I switched graphics libraries from P3D to OpenGL, and — more importantly — stabilized the .stl file export by switching to toxiclibs – now pretty much any .stl file will be able to print straightaway, no sliceform errors, no adjustments necessary!  Plus the graphics just look a lot smoother.

Evidence:

UCUbe v08   
/* UCube v.08
 * 3d modeling input device and stl export
 * now using toxiclibs and opengl
 * Manual rotation, shape mode toggle, and export button
 * by Ben Leduc-Mills - 2.10.11
 */
 
import processing.opengl.*;
import newhull.*;
import java.awt.event.*;
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.processing.*;
import controlP5.*;
import processing.serial.*;
 
import javax.media.opengl.GL;
 
ControlP5 controlP5;
//Nav3D nav; // camera controller
 
QuickHull3D hull = new QuickHull3D(); //init quickhull
Point3d[] points; //init Point3d array
Point3d[] savedPoints;
 
ToxiclibsSupport gfx;
Mesh3D mesh = new TriangleMesh();
Vec3D[] vectors;
 
Serial myPort; // the serial port
 
float rotX, rotY; //for manual roatation
int gridSize = 4; //size of grid (assumes all dimensions are equal)
int spacing = 50; //distance between points
int counter = 0; //wireframe toggle
String inString; //string of coordinates from arduino
String oldString;
boolean reDraw = true;
PFont myFont; //init font for text
 
PGraphicsOpenGL pgl;
GL gl;
 
void setup() {
 
  size(1400,850,OPENGL);
  frameRate(13);
  gfx=new ToxiclibsSupport(this); // initialize ToxiclibsSupport
  //background(255); 
 
  pgl = (PGraphicsOpenGL) g; //processing graphics object
  gl = pgl.beginGL(); //begin opengl
  gl.setSwapInterval(1); //set vertical sync on
  pgl.endGL(); //end opengl
 
  controlP5 = new ControlP5(this);
  controlP5.addButton("Mode", 0,100,120,80,19);
  controlP5.addButton("Export", 0,100,140,80,19);
 
  myFont = createFont("FFScala", 32);
  textFont(myFont);
 
  println(Serial.list()); // list available serial ports
  myPort = new Serial(this, Serial.list()[0], 19200);
  myPort.bufferUntil('\n');
 
} 
 
void draw() {
 
  //}
 
  //void serialEvent(Serial myPort) {
 
  if (myPort.available() > 0) {
  background(255);
  smooth();
  // because we want controlP5 to be drawn on top of everything
  // else we need to disable OpenGL's depth testing at the end
  // of draw(). that means we need to turn it on again here.
  hint(ENABLE_DEPTH_TEST);
 
  pushMatrix();
  //lights();
  translations();
  drawGrid();
  drawAxes();
 
  String inString = myPort.readStringUntil('\n');
  //String m1[] = match(inString, "[0-3]");
 
  //TODO: compare inString to oldString to see if coords changed
 
  //if a coordinate string is coming in from arduino
  if (inString != null) {
    //make acive points more visible
    strokeWeight(8);
    stroke(255, 0, 0); 
 
    if (inString != oldString) {
      reDraw = true;
      oldString = inString;
    }
 
    inString = trim(inString);
    //split string of mutliple coordinates into coordinate-sized chuncks
    String coord[] = split(inString, ';');
 
    //init point3d array equal to number of activated points
    points = new Point3d[coord.length-1]; 
 
    //put the xyz coordinates into the point3d array and draw them
    for(int p3d = 0; p3d < coord.length-1; p3d++) {
 
      int subCoord[] = int(split(coord[p3d], ','));
      points[p3d] = new Point3d(subCoord[0] * spacing, subCoord[1] * spacing, subCoord[2] * spacing);
      point(subCoord[0] * spacing, subCoord[1] * spacing, subCoord[2] * spacing );
    }
 
    if (counter%2 != 0) {
      drawHull();
    }
  } //end if inString!=null
  popMatrix();
  // turn off depth test so the controlP5 GUI draws correctly
  hint(DISABLE_DEPTH_TEST);
  }
}
 
public void controlEvent(ControlEvent theEvent) {
  println(theEvent.controller().name());
}
 
void mouseDragged() {
  float x1 = mouseX-pmouseX;
  float y1 = mouseY-pmouseY;
  rotX = (mouseY * -0.01);
  rotY = (mouseX * 0.01);
}
 
void translations() {
  translate(width/2, height/2);
  rotateX(rotX);
  rotateY(rotY);
}
 
void drawGrid() {
 
  //draw rest of grid
  //(spacing * (gridSize -1) * -1) /2 = center around 0
  int xpos = 0;
  int ypos = 0;
  int zpos = 0;
 
  for (int i = 0; i < gridSize; i++) {
    for (int j = 0; j < gridSize; j++) {
      for( int k = 0; k < gridSize; k++) {
        stroke(100);
        strokeWeight(2);
        point(xpos, ypos, zpos);
        //println(xpos + "," + ypos + "," + zpos);
        xpos += spacing;
      }
      xpos = 0;
      ypos += spacing;
    }
    xpos = 0;
    ypos = 0;
    zpos += spacing;
  }
}
 
void drawAxes() {
 
  stroke(255,0,255);
  line(0,0,0, 100,0,0);
  fill(255,0,255);
  text("X", 200, 0);
  stroke(0,255,0);
  line(0,0,0, 0,-100,0);
  fill(0,255,0);
  text("Y", 0, -200);
  stroke(0,0,255);
  line(0,0,0, 0,0,100);
  fill(0,0, 255);
  text("Z", 0, 0, 200);
  fill(0,0,0);
}
 
public void Mode(int theValue) {
  counter++;
  println(counter);
  drawHull();
}
 
public void Export(int theValue) {
  outputSTL();
}
 
void outputSTL() {
 
  TriangleMesh mySTL = new TriangleMesh();
 
  for(int i = 0; i < vectors.length; i+=3) {
 
    mesh.addFace(vectors[i], vectors[i+1], vectors[i+2]);
   // println(vectors[i] + " " + vectors[i+1] + " " + vectors[i+2]);
  }
 
  mySTL.addMesh(mesh);
  mySTL.saveAsSTL(selectOutput());
}
 
void drawHull() {
 
  int numPoints = points.length;
  //check that our hull is valid
 
  if(hull.myCheck(points, numPoints) == false) {
 
    //brute force inefficiency
    beginShape(TRIANGLE_STRIP);
    strokeWeight(1);
    fill(0);
 
    for (int j = 0; j < numPoints; j++) {
 
      float x = (float)points[j].x;
      float y = (float)points[j].y;
      float z = (float)points[j].z;
      vertex(x,y,z);
    }
 
    endShape(CLOSE);
  }
 
  else if (hull.myCheck(points, numPoints) == true) {  
 
    if(reDraw == true) {
      //print("redraw = true");
      hull.build(points);
      hull.triangulate();
      //get an array of the vertices so we can get the faces
      Point3d[] vertices = hull.getVertices();
      savedPoints = new Point3d[0];
      vectors = new Vec3D[0];
 
      beginShape(TRIANGLE_STRIP);
      strokeWeight(1);
      //noFill();
      int[][] faceIndices = hull.getFaces();
      for (int i = 0; i < faceIndices.length; i++) {
        for (int k = 0; k < faceIndices[i].length; k++) {
 
          //get points that correspond to each face
          Point3d pnt2 = vertices[faceIndices[i][k]];
          float x = (float)pnt2.x;
          float y = (float)pnt2.y;
          float z = (float)pnt2.z;
          vertex(x,y,z);
          Vec3D tempVect = new Vec3D(x,y,z);
          //println(x + "," + y + "," + z + " " + k);
          savedPoints = (Point3d[])append(savedPoints, pnt2);
          vectors = (Vec3D[])append(vectors, tempVect);
          //savedPoints[k] = new Point3d(pnt2);
          //println(savedPoints[k]);
 
          //println(x + "," + y + "," + z);
        }
      }
      endShape(CLOSE);
      reDraw = false;
    }
 
    else if(reDraw == false) {
      // print("redraw = false");
      beginShape(TRIANGLE_STRIP);
      strokeWeight(1);
      //noFill();
      for(int i = 0; i < savedPoints.length; i++) {
 
        float x = (float)savedPoints[i].x;
        float y = (float)savedPoints[i].y;
        float z = (float)savedPoints[i].z;
        vertex(x,y,z);
      }
      endShape(CLOSE);
    }
  }
}
]]>
https://benatwork.cc/ucube-v08-now-with-opengl-and-toxiclibs/feed/ 2
Newhull Library v1.0 https://benatwork.cc/newhull-library-v1-0/ https://benatwork.cc/newhull-library-v1-0/#respond Wed, 02 Feb 2011 22:58:50 +0000 http://benatwork.cc/?p=354 In case there is anyone interested in using a robust convex hull algorithm in Processing, here’s a link to a library I made, a slightly modified version of the QuickHull3D library by John Lloyd (check his documentation here).

Library: newhull

Just un-zip and stick it in your libraries folder in Processing.

The most significant functional difference is the addition of a boolean myCheck() function that can check the validity of your hull BEFORE you attempt to draw it to the screen, thus saving you from QuickHull3D’s exception errors that crash Processing.
Usage below, the first parameter being the Point3D array of points, the second being the number of points in your array. Returns true if your hull contains no colinear, coplanar, or coincident points.

  if (hull.myCheck(points, numPoints) == true) {

Simple complete example:

For some more detailed usage examples, check out the code section of this website under the UCube examples.

]]>
https://benatwork.cc/newhull-library-v1-0/feed/ 0
Laser Art Code https://benatwork.cc/laser-art-code/ https://benatwork.cc/laser-art-code/#respond Wed, 02 Feb 2011 22:06:43 +0000 http://benatwork.cc/?p=350 Below is some code I wrote up to make fun shapes to laser etch. It uses an new version of the convex hull library I’ve been writing called newhull (which I will release shortly), as well as the pdf export library that comes with processing. It provides an object class that sets a convex hull with inputs for number of points, minimum and maximum on each axis (x, y, and z) and stroke weight. It produces something like this (which you can export to .pdf, then open with Inkscape or Illustrator, save to .svg and send to the laser cutter).

 

 

Code Here:

]]>
https://benatwork.cc/laser-art-code/feed/ 0
uCube code – no serial https://benatwork.cc/ucube-code-no-serial/ https://benatwork.cc/ucube-code-no-serial/#respond Wed, 15 Dec 2010 04:42:20 +0000 http://benatwork.cc/?p=315 This is some code for an early version of the UCube that doesn’t rely on Serial input. Useful for testing.

]]>
https://benatwork.cc/ucube-code-no-serial/feed/ 0
uCube v.01 https://benatwork.cc/ucube-v-01/ https://benatwork.cc/ucube-v-01/#respond Fri, 08 Oct 2010 00:01:13 +0000 http://benatwork.cc/?p=242 Here’s the first version of the uCube code, which integrates the convexhull + stl code with taking input from the physical 4x4x4 cube.

You will need the libraries I mentioned in the convex hull example, plus the serial library. The Processing sketch is parsing a string of xyz coordinates being send from Arduino, of the form (x,y,z;x,y,z,;etc.).

]]>
https://benatwork.cc/ucube-v-01/feed/ 0
Convex Hull + STL Export https://benatwork.cc/convex-hull-stl-export/ https://benatwork.cc/convex-hull-stl-export/#respond Wed, 06 Oct 2010 00:22:05 +0000 http://benatwork.cc/?p=234 Big breakthrough today on the way toward realizing the UCube. I finally got code working in Processing that takes an arbitrary number of 3d coordinates (in x,y,z format), finds the convex hull, and is able to export a file of the shape that can be read accurately by 3d printers.

First, what a relief. I had been working on getting a convex hull working for a few weeks now. Second, I’m standing on the shoulders of giants, as always. The code below makes use of two libraries: the quickhull3d package by John Lloyd, which I turned into a processing library, and the awesome unlekker library from Marius Watz, which allows the nice export to .stl capabilities.

Here’s the Processing version of the quickhull3d library I used/adapted. Just unpack it and drop it in your Processing->libraries folder. quickhull3d

The example below just takes a bunch of random points and displays something like this:

You could, of course, populate the array with the coordinates you wanted.

 

]]>
https://benatwork.cc/convex-hull-stl-export/feed/ 0
3d Geocube code – upgrade https://benatwork.cc/3d-geocube-code-upgrade/ https://benatwork.cc/3d-geocube-code-upgrade/#respond Sun, 05 Sep 2010 00:23:49 +0000 http://benatwork.cc/?p=126 Cleaned up and improved the code significantly for the 3d geocube (working title). Thought I’d post it, mostly for my own records. Note that the newer Arduino code uses the String library to format the xyz coordinate strings. I hear it’s going to be in core in Arduino v019.

Arduino:

#include < WString.h >

/* 3D grid object placement
 * V.05 - two buttons sending xyz coordinates w/ debouncing
 * Ben Leduc-Mills
*/

//number of buttons/nodes in our physical cube
const int numOfButtons = 2;

// pin assignments won't change
// each point has a switch, an led, and an xyz coordinate
const int button1 = 2;
const int button2 = 3;
int buttons[] = {button1, button2};

//array of xyz coordinates
String coord1 = "10,30,40;";
String coord2 = "20,20,20;";
String coord[] = {coord1, coord2};

void setup() {

 //start serial communitcation
 Serial.begin(9600);

 //init button pins as input
 pinMode(button1, INPUT);
 pinMode(button2, INPUT);

}

void loop() {

 for (int i = 0; i < numOfButtons; i++) {

  int reading1 = digitalRead(buttons[i]);

  delay(50);

  int reading2 = digitalRead(buttons[i]);

  if (reading1 == reading2 && reading2 == HIGH ) {

      Serial.print(coord[i]);
  } 
 }

 Serial.print('\n');

}

Processing:

/* grid 3D mapper
 * v.04 - multiple switch inputs, and cleaner code
 * by Ben Leduc-Mills
*/

import processing.serial.*;

Serial myPort; // the serial port
float buttonId; // the button id we get from arduino if a button is switched

int gridSize = 10; //size of grid
int spacing = 10; //distance between pixels

void setup() {

 size(800, 600, P3D);
 //size(400, 300);

 println(Serial.list()); // list available serial ports

 myPort = new Serial(this, Serial.list()[0], 9600);

 myPort.bufferUntil('\n');

 background(255); //set initial background color

}

void draw() {

  translate(width/2, height/2 -50, 200);
  rotateY(frameCount * 0.01);

}

void serialEvent (Serial myPort) {

 background(255);

 int xpos = 0;
 int ypos = 0;
 int zpos = 0;

 //get the button ID
 String inString = myPort.readStringUntil('\n');

 if (inString != null) {

  //trim whitespace
  inString = trim(inString);

  String coord[] = split(inString, ';');

 pushMatrix();

 //draw active points on grid

  for (int i = 0; i < coord.length -1; i++ ) {

    int subCoord[] = int(split(coord[i], ','));
    strokeWeight(3);
    point(subCoord[0], subCoord[1], subCoord[2] );

  }

  //draw rest of grid

     for (int i = 0; i < gridSize; i++) {
        for (int j = 0; j < gridSize; j++) {
          for( int k = 0; k < gridSize; k++) {
                strokeWeight(1);
                point(xpos, ypos, zpos);
                xpos += spacing;
          }
          xpos = 0;
          ypos += spacing;   
        }
        xpos = 0;
        ypos = 0;
        zpos += spacing; 
     }

  popMatrix();

  } 

}

]]>
https://benatwork.cc/3d-geocube-code-upgrade/feed/ 0
Early 3D grid code https://benatwork.cc/early-3d-grid-code/ https://benatwork.cc/early-3d-grid-code/#respond Fri, 27 Aug 2010 04:49:00 +0000 http://benatwork.cc/?p=123 So, as part of my research at the Craft Tech Lab, I have been assigned to (attempt) to build an input device. This device would be in the form of a physical grid, in three dimensions, whereby the user would activate various vertices/nodes to intuitively model an object, in 3d, in real time. My first attempt will be to rig up a switch and led at each node (controlled via Arduino), and display the activated nodes in real time in Processing.

Here’s the code I’ve got so far (working with 2 switches):

Processing:


/* grid 3D mapper
 * v.02 - multiple input
 * by Ben Leduc-Mills
*/

import processing.serial.*;

Serial myPort; // the serial port
float buttonId; // the button id we get from arduino if a button is switched

void setup() {

 size(400, 300, P3D);
 //size(400, 300);

 println(Serial.list()); // list available serial ports

 myPort = new Serial(this, Serial.list()[0], 9600);

 myPort.bufferUntil('\n');

 background(255); //set initial background color

}

void draw() {

}

void serialEvent (Serial myPort) {

 //get the button ID
 String inString = myPort.readStringUntil('\n');

 if (inString != null) {

  //trim whitespace
  inString = trim(inString);

  //convert to int so we can use it as a number
  buttonId = float(inString);

  //draw the point
  if ( buttonId == 2 ) {
     stroke(0);
     point (30, 20, -50);
     //ellipse(20, 30, 20 ,20);
    }

  //or cover it up
  else if (buttonId == -2 ) {
    stroke(255);
    point (30, 20, -50);
    //ellipse(20, 30, 20, 20);
  }  

  if (buttonId == 3 ) {
     stroke(0);
     point(50, 20, -50);
  }

  else if (buttonId == -3 ) {
     stroke(255);
     point(50, 20, -50);
  }    

  }

 }

Arduino:


/* 3D grid object placement
 * V.02 - two buttons 
 * Ben Leduc-Mills
*/

// pin assignments won't change
// each point has a switch, an led, and an xyz coordinate
const int button1 = 2;
const int button2 = 3;
//const int led1 = 3;

//var for reading button status
int button1State = 0;
int button2State = 0;

void setup() {

  //start serial communitcation
  Serial.begin(9600);

 //init LED pins as output
 //pinMode(led1, OUTPUT);

 //init button pins as input
 pinMode(button1, INPUT);
 pinMode(button2, INPUT);

}

void loop() {

 // read button states
 button1State = digitalRead(button1);

 if (button1State == HIGH) {
   //digitalWrite(led1, HIGH);
   //Serial.print("button 1:");
   Serial.println(button1);
 }

 else if (button1State == LOW) {
    Serial.println(button1 * -1);
 }

 button2State = digitalRead(button2);

 if (button2State == HIGH) {
  Serial.println(button2);
 }

 else if (button2State == LOW) {
  Serial.println(button2 * -1);
 }

}

]]>
https://benatwork.cc/early-3d-grid-code/feed/ 0