Browse Source

adding untracked files

master
Michael Winter 1 year ago
parent
commit
8678b2e4ec
13 changed files with 727 additions and 0 deletions
  1. +171
    -0
      arduino/multistepper/multistepper.ino
  2. BIN
      latex/documentation/a_history_of_the_domino_problem_score.pdf
  3. BIN
      latex/documentation/selects/discos.png
  4. BIN
      latex/documentation/selects/jaendel.jpg
  5. BIN
      latex/documentation/selects/jaendel.xcf
  6. BIN
      latex/documentation/selects/maquina.png
  7. BIN
      latex/documentation/selects/maquinalit.jpg
  8. BIN
      latex/documentation/selects/maquinalit.png
  9. BIN
      latex/documentation/selects/maquinalit.xcf
  10. BIN
      latex/documentation/selects/oraclesannotated.jpg
  11. BIN
      latex/documentation/selects/oraclesannotated.xcf
  12. +99
    -0
      python/vernier_tracker.py
  13. +457
    -0
      supercollider/installation_control.scd

+ 171
- 0
arduino/multistepper/multistepper.ino View File

@@ -0,0 +1,171 @@
// Include the AccelStepper library:
#include <AccelStepper.h>

// Define stepper motor connections and motor interface type. Motor interface type must be set to 1 when using a driver:

// Set stepper 1 pins
#define m1LimitNegPin 2
#define m1LimitPosPin 3
#define m1DirPin 4
#define m1StepPin 5
#define m1PowerPin 6

// Set stepper 2 pins
#define m2LimitNegPin 9
#define m2LimitPosPin 10
#define m2DirPin 11
#define m2StepPin 12
#define m2PowerPin 13

#define motorInterfaceType 1

// Create a new instance of the AccelStepper class:
AccelStepper m1Stepper = AccelStepper(motorInterfaceType, m1StepPin, m1DirPin);
AccelStepper m2Stepper = AccelStepper(motorInterfaceType, m2StepPin, m2DirPin);

unsigned long previousMillis = 0;
unsigned long currentMillis = 0;

void setup() {

pinMode(m1PowerPin, OUTPUT);
pinMode(m1LimitNegPin, INPUT);
pinMode(m1LimitPosPin, INPUT);

pinMode(m2PowerPin, OUTPUT);
pinMode(m2LimitNegPin, INPUT);
pinMode(m2LimitPosPin, INPUT);

Serial.begin(115200);

// Set the maximum speed in steps per second:
m1Stepper.setMaxSpeed(200);
m1Stepper.setAcceleration(100);
m1Stepper.setCurrentPosition(0);
m2Stepper.setMaxSpeed(200);
m2Stepper.setAcceleration(100);
m2Stepper.setCurrentPosition(0);
}


int integerValue=0;
bool negativeNumber = false; // track if number is negative
char incomingByte;
void loop() {
currentMillis = millis();
int m1EorNeg = digitalRead(m1LimitNegPin);
int m1EorPos = digitalRead(m1LimitPosPin);

int m2EorNeg = digitalRead(m2LimitNegPin);
int m2EorPos = digitalRead(m2LimitPosPin);

if (currentMillis - previousMillis >= 1000 == true ) {
Serial.println("------Stepper 1------");
Serial.print("m1EorPos:");
Serial.println(m1EorNeg);
Serial.print("m1EorNeg: ");
Serial.println(m1EorPos);
Serial.print("m1CurPos: ");
Serial.println(m1Stepper.currentPosition() * -1);
Serial.print("m1TarPos: ");
Serial.println(m1Stepper.targetPosition() * -1);
Serial.println("");

Serial.println("------Stepper 2------");
Serial.print("m2EorPos: ");
Serial.println(m2EorNeg);
Serial.print("m2EorNeg: ");
Serial.println(m2EorPos);
Serial.print("m2CurPos: ");
Serial.println(m2Stepper.currentPosition() * -1);
Serial.print("m2TarPos: ");
Serial.println(m2Stepper.targetPosition() * -1);
Serial.println("");

previousMillis = currentMillis;
}

// limit switch logic for stepper 1
if ((m1EorNeg < m1EorPos) && (m1Stepper.targetPosition() > m1Stepper.currentPosition())) {
m1Stepper.setSpeed(0);
m1Stepper.moveTo(m1Stepper.currentPosition());
digitalWrite(m1PowerPin, HIGH);
} else if ((m1EorNeg > m1EorPos) && (m1Stepper.targetPosition() < m1Stepper.currentPosition())) {
m1Stepper.setSpeed(0);
m1Stepper.moveTo(m1Stepper.currentPosition());
digitalWrite(m1PowerPin, HIGH);
} else if (m1Stepper.targetPosition() == m1Stepper.currentPosition()) {
digitalWrite(m1PowerPin, HIGH);
} else {
digitalWrite(m1PowerPin, LOW);
m1Stepper.run();
}

// limit switch logic for stepper 2
if ((m2EorNeg < m2EorPos) && (m2Stepper.targetPosition() > m2Stepper.currentPosition())) {
m2Stepper.setSpeed(0);
m2Stepper.moveTo(m2Stepper.currentPosition());
digitalWrite(m2PowerPin, HIGH);
} else if ((m2EorNeg > m2EorPos) && (m2Stepper.targetPosition() < m2Stepper.currentPosition())) {
m2Stepper.setSpeed(0);
m2Stepper.moveTo(m1Stepper.currentPosition());
digitalWrite(m2PowerPin, HIGH);
} else if (m2Stepper.targetPosition() == m2Stepper.currentPosition()) {
digitalWrite(m2PowerPin, HIGH);
} else {
digitalWrite(m2PowerPin, LOW);
m2Stepper.run();
}
if (Serial.available() > 0) { // something came across serial
integerValue = 0; // throw away previous integerValue
negativeNumber = false; // reset for negative
while(1) { // force into a loop until 'n' is received
incomingByte = Serial.read();
if (incomingByte == ' ') break; // exit the while(1), we're done receiving
if (incomingByte == -1) continue; // if no characters are in the buffer read() returns -1
if (incomingByte == '-') {
negativeNumber = true;
continue;
}
integerValue *= 10; // shift left 1 decimal place
integerValue = ((incomingByte - 48) + integerValue); // convert ASCII to integer, add, and shift left 1 decimal place
}
if (negativeNumber)
integerValue = -integerValue;

integerValue = -integerValue; // this makes up for the fact that things are backwards
m1Stepper.moveTo(integerValue);


integerValue = 0; // throw away previous integerValue
negativeNumber = false; // reset for negative
while(1) { // force into a loop until 'n' is received
incomingByte = Serial.read();
if (incomingByte == '\n') break; // exit the while(1), we're done receiving
if (incomingByte == -1) continue; // if no characters are in the buffer read() returns -1
if (incomingByte == '-') {
negativeNumber = true;
continue;
}
integerValue *= 10; // shift left 1 decimal place
integerValue = ((incomingByte - 48) + integerValue); // convert ASCII to integer, add, and shift left 1 decimal place
}
if (negativeNumber)
integerValue = -integerValue;

integerValue = -integerValue; // this makes up for the fact that things are backwards
m2Stepper.moveTo(integerValue);

}
//delay(100);
}

BIN
latex/documentation/a_history_of_the_domino_problem_score.pdf View File


BIN
latex/documentation/selects/discos.png View File

Before After
Width: 4032  |  Height: 3024  |  Size: 12MB

BIN
latex/documentation/selects/jaendel.jpg View File

Before After
Width: 1898  |  Height: 1897  |  Size: 2.6MB

BIN
latex/documentation/selects/jaendel.xcf View File


BIN
latex/documentation/selects/maquina.png View File

Before After
Width: 3800  |  Height: 2850  |  Size: 31MB

BIN
latex/documentation/selects/maquinalit.jpg View File

Before After
Width: 5184  |  Height: 3456  |  Size: 7.3MB

BIN
latex/documentation/selects/maquinalit.png View File

Before After
Width: 5184  |  Height: 3456  |  Size: 17MB

BIN
latex/documentation/selects/maquinalit.xcf View File


BIN
latex/documentation/selects/oraclesannotated.jpg View File

Before After
Width: 5184  |  Height: 3456  |  Size: 3.2MB

BIN
latex/documentation/selects/oraclesannotated.xcf View File


+ 99
- 0
python/vernier_tracker.py View File

@@ -0,0 +1,99 @@
#This is a proof of concept for motion tracking of the vernier in very early stages
# TODO: stabilize the tracker and connect the plumbing via OSC to the SuperCollider app
# and get the stream to feed to the Open Stage Control GUI for calibration

import cv2
import sys

# Read video (eventually will be the live capture from the camera)
video = cv2.VideoCapture("/home/mwinter/Sketches/a_history_of_the_domino_problem/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")

# Exit if video not opened.
if not video.isOpened():
print("Could not open video")
sys.exit()

# Read first frame.
video.set(cv2.CAP_PROP_POS_FRAMES, 5000)
ok, frame = video.read()
if not ok:
print('Cannot read video file')
sys.exit()

# Define an initial bounding box
#bbox = (287, 23, 86, 320)

frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
#frame = cv2.GaussianBlur(frame,(5,5),cv2.BORDER_DEFAULT)
r1 = cv2.selectROI('Tracking', frame)
r2 = cv2.selectROI('Tracking', frame)
#r = (606, 448, 35, 177);
#cv2.destroyWindow('select')
#print(r)
crop1 = frame[int(r1[1]):int(r1[1]+r1[3]), int(r1[0]):int(r1[0]+r1[2])]
crop2 = frame[int(r2[1]):int(r2[1]+r2[3]), int(r2[0]):int(r2[0]+r2[2])]



while True:
# Read a new frame
ok, frame = video.read()
if not ok:
break

crop1 = frame[int(r1[1]):int(r1[1]+r1[3]), int(r1[0]):int(r1[0]+r1[2])]
crop1 = cv2.cvtColor(crop1, cv2.COLOR_RGB2GRAY)
crop1 = cv2.GaussianBlur(crop1,(5,5),cv2.BORDER_DEFAULT)
crop2 = frame[int(r2[1]):int(r2[1]+r2[3]), int(r2[0]):int(r2[0]+r2[2])]
crop2 = cv2.cvtColor(crop2, cv2.COLOR_RGB2GRAY)
crop2 = cv2.GaussianBlur(crop2,(5,5),cv2.BORDER_DEFAULT)
ret1, thresh1 = cv2.threshold(crop1, 230, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
cnts1 = cv2.findContours(thresh1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts1 = cnts1[1]
ret2, thresh2 = cv2.threshold(crop2, 230, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
cnts2 = cv2.findContours(thresh2.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts2 = cnts2[1]
center = None
for c in cnts1[0:2]:
# calculate moments for each contour
M = cv2.moments(c)
# calculate x,y coordinate of center
if M["m00"] != 0:
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
#else:
# cX, cY = 0, 0
#print(cY)
cv2.circle(frame, (int(r1[0]) + cX, int(r1[1]) + cY), 5, (255, 255, 255), -1)
# only proceed if at least one contour was found
if len(cnts2) > 0:
# find the largest contour in the mask, then use
# it to compute the minimum enclosing circle and
# centroid
c = max(cnts2, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

# only proceed if the radius meets a minimum size
if radius > 5:
# draw the circle and centroid on the frame,
# then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius), (0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
# Display result
cv2.imshow("Tracking", frame)
#cv2.imshow("Crop", crop)

# Exit if ESC pressed
k = cv2.waitKey(1) & 0xff
if k == 27 :
cv2.destroyWindow('Tracking')
break

+ 457
- 0
supercollider/installation_control.scd View File

@@ -0,0 +1,457 @@
// main controller for the installation
// TODO: playback of the recordings, automation, switch from open-loop to closed-loop with the openCV tracker
(
var imageDist, micronsPerStep, automation, imgPositions, curPos, tarPos,
netAddress, serialPort, serialListener,
moveTo, jogControl, jogHorizontal, jogVertical,
imgSelect, imgCalibrate, automate, lastSelect;

// init global vars
imageDist = 300; // in microns
micronsPerStep = 0.0977;
automation = false;
imgPositions = 9.collect({nil});
curPos = Point.new(0, 0);
tarPos = Point.new(0, 0);
netAddress = NetAddr.new("127.0.0.1", 7777);
~serialPort = SerialPort("/dev/ttyACM0", baudrate: 115200, crtscts: true);

// recieve motor feedback
~serialListener = Routine({
var byte, str, res, valArray,
stepper, limitSwitchNeg, limitSwitchPos, safeMode, limitPos;

safeMode = false;

loop{
byte = ~serialPort.read;
if(byte==13, {
if(str[1].asString == "[", {
valArray = str.asString.interpret.postln;
curPos = Point.new(valArray[0], valArray[1]);
limitSwitchNeg = valArray[2];
limitSwitchPos = valArray[3];
if(safeMode && (limitSwitchNeg == limitSwitchPos), {
safeMode = false;
fork {
netAddress.sendMsg("/STATE/SET", "{message: \"all clear\"}");
2.wait;
netAddress.sendMsg("/STATE/SET", "{message: \"\"}");
}
});
if(automation, {
var centerPos = nil, dist = 0;
if(lastSelect != 0, {
centerPos = imgPositions[lastSelect].deepCopy;
dist = 300;
}, {
centerPos = imgPositions[4].deepCopy;
dist = imageDist / micronsPerStep;
});
if((curPos.x - tarPos.x).abs < 100, {tarPos.x = centerPos.x + dist.rand2});
if((curPos.y - tarPos.y).abs < 100, {tarPos.y = centerPos.y + dist.rand2});
moveTo.value(tarPos);
});
}, {
if(str[1..3].asString == "!!!", {
netAddress.sendMsg("/STATE/SET", "{message: \"!!! limit switch still on after 1000 steps, this should NEVER happen\"}");
}, {
automation = false;
safeMode = true;
netAddress.sendMsg("/STATE/SET", "{message: \"!! limit hit, move the other direction\"}");
});
});
str = "";
}, {str = str++byte.asAscii});
};
}).play(AppClock);

// send new coordinates to the arduino / motors
moveTo = {arg point;
~serialPort.putAll(point.x.asInteger.asString ++ " " ++ point.y.asInteger.asString);
~serialPort.put(10);
};

jogControl = {arg axis;
var jog, count = 0, jogRate= 0, jogDirection = 1;
jog = Task({
loop{
count = (count + 0.01).clip(0, 1);
jogRate = pow(count, 2) * 500;
if(axis == '/jog_horizontal', {
tarPos.x = curPos.x + (jogRate * jogDirection);
}, {
tarPos.y = curPos.y + (jogRate * jogDirection);
});
moveTo.value(tarPos);
0.1.wait
};
});
OSCFunc({arg msg;
//tarPos.x = curPos.x + (1000 * msg[1]);
//moveTo.value(tarPos);
if(msg[1] == 0, {count = 0; jogRate = 0; jog.pause()}, {jogDirection = msg[1]; jog.play(AppClock)});
automation = false;
netAddress.sendMsg("/STATE/SET", "{automate: 0}");
}, axis, netAddress)
};

jogHorizontal = jogControl.value('/jog_horizontal');
jogVertical = jogControl.value('/jog_vertical');

imgSelect = {
//var lastSelect = nil;
OSCFunc({arg msg;
var imgIndex;
if(msg[1] > 0, {
imgIndex = msg[1] - 1;
if(imgPositions[imgIndex] != nil, {tarPos = imgPositions[imgIndex].deepCopy; moveTo.value(tarPos)});
9.do({arg i; if(imgIndex != i, {
netAddress.sendMsg("/STATE/SET", "{img_" ++ (i + 1).asString ++ "_select: " ++ (i + 1).neg ++ "}")})});
automation = false;
netAddress.sendMsg("/STATE/SET", "{automate: 0}");
lastSelect = imgIndex;
}, {
lastSelect = 0;
/*
imgIndex = msg[1].neg - 1;
if(imgIndex == lastSelect, {
if(imgPositions[imgIndex] != nil, {tarPos = imgPositions[imgIndex].deepCopy; moveTo.value(tarPos)});
netAddress.sendMsg("/STATE/SET", "{img_" ++ (imgIndex + 1).asInteger.asString ++ "_select: " ++ (imgIndex + 1) ++ "}")});
*/
});
}, '/img_select', netAddress)
}.value;

imgCalibrate = {
var calibrateHold, imgIndex, setPos;
calibrateHold = Routine({
20.do({0.1.wait});
imgPositions[imgIndex] = setPos.deepCopy;
netAddress.sendMsg("/STATE/SET", "{message: \"image calibrated\"}");
});

OSCFunc({ arg msg;
imgIndex = msg[1] - 1;
if(imgIndex >= 0, {
setPos = curPos.deepCopy;
calibrateHold.play(AppClock);
}, {
calibrateHold.stop; calibrateHold.reset; netAddress.sendMsg("/STATE/SET", "{message: \"\"}");
});
}, '/img_calibrate', netAddress);
}.value;

automate = OSCFunc({arg msg;
if(msg[1] == 1, {
automation = true;
}, {
automation = false;
tarPos = curPos.deepCopy;
moveTo.value(tarPos);
});
9.do({arg i; netAddress.sendMsg("/STATE/SET", "{img_" ++ (i + 1).asString ++ "_select: " ++ (i + 1).neg ++ "}")});
}, '/automate', netAddress);
)
~serialPort.close
~serialPort = SerialPort.new("/dev/ttyACM0", baudrate: 115200, crtscts: true);
~serialListener.reset
~serialListener.play(AppClock);

(
// TODO:
// set position to 0
// limit switch warnings
// More clean up and testing
var imageDist, rotation, micronsPerStep, curPos, tarPos, automate, imagePositions,
serialPort, serialListener, moveTo,
window, xOffset, yOffset,
userView, imageButtonRects,
dirKeyBlockTasks, jogTasks, jogRates,
moveButtons, curPosFields, tarPosFields,
calibrationSteps, wizardButtons, wizMoveBlock, curWizardStep, curWizardText;

// init global vars
imageDist = 25; // in microns
rotation = 0; // in degrees
micronsPerStep = 0.0977;
curPos = Point.new(0, 0);
tarPos = Point.new(0, 0);
automate = false;
imagePositions = 3.collect({arg r; 3.collect({arg c; Point(imageDist * (c - 1), imageDist * (r - 1))})}).reverse.flat;

// connect to arduino
serialPort = SerialPort(
"/dev/ttyACM0", //edit to match the port (SerialPort.listDevice)
baudrate: 115200, //check that baudrate is the same as in arduino sketch
crtscts: true);

// recieve motor feedback
serialListener = Routine({
var byte, str, res, valArray,
stepper, limitSwitchPos, limitSwitchNeg, safeMode, limitPos;
loop{
byte = serialPort.read;
if(byte==13, {
if(str[1].asString == "[", {

valArray = str.asString.interpret;
stepper = valArray[0];
if(stepper == 1, {curPos.x = valArray[1]}, {curPos.y = valArray[1]});
//tarPos = valArray[2];
limitSwitchPos = valArray[3];
limitSwitchNeg = valArray[4];
safeMode = valArray[5];
limitPos = valArray[6];

// update all the curPos fields
if(stepper == 2, {
//curPos = curPos.rotate(rotation.neg * (pi / 180.0)) * micronsPerStep;
curPos = curPos * micronsPerStep;
curPosFields[0].string = (curPos.x).round(0.1).asString;
curPosFields[1].string = (curPos.y).round(0.1).asString;
curPosFields[2].string = (curPos.rho).round(0.1).asString;
curPosFields[3].string = (if(curPos.theta >= 0, {0}, {360}) + (curPos.theta * (180 / pi))).round(0.1).asString;
userView.refresh;

// automate mode: select new point before the motor comes to a stop
if(automate, {
if((curPos.x - tarPos.x).abs < 5.0, {tarPos.x = imageDist.rand2.round(0.1)});
if((curPos.y - tarPos.y).abs < 5.0, {tarPos.y = imageDist.rand2.round(0.1)});
moveTo.value(tarPos);
});
});
}, {
(str).postln;
});
str = "";
}, {str = str++byte.asAscii});
};
});

// send new coordinates to the arduino / motors
moveTo = {arg point;
var rotatedPoint, xMove, yMove;
tarPosFields[0].string = tarPos.x.round(0.1).asString;
tarPosFields[1].string = tarPos.y.round(0.1).asString;
tarPosFields[2].string = tarPos.rho.round(0.1).asString;
tarPosFields[3].string = (if(tarPos.theta >= 0, {0}, {360}) + (tarPos.theta * (180 / pi))).round(0.1).asString;
//rotatedPoint = point.rotate(rotation * (pi / 180.0));
rotatedPoint = point;
xMove = (rotatedPoint.x / micronsPerStep).round(1).asInteger;
yMove = (rotatedPoint.y / micronsPerStep).round(1).asInteger;
serialPort.putAll(xMove.asString ++ " " ++ yMove.asString);
serialPort.put(10);
};


// generate the gui
window = Window.new("", Rect(400, 400, 480, 650)).front;

xOffset = 240;
yOffset = 220;


// drawing and window key commands
userView = UserView(window, Rect(0, 0, 800, 600));
imageButtonRects = (({arg r; ({arg c; Rect.aboutPoint(Point(xOffset + (120 * (r - 1)), yOffset + (120 * (c - 1))), 5, 5)}) ! 3}) ! 3).flat;

userView.drawFunc = ({
imageButtonRects.do({ arg rect, i;
Pen.addOval(rect);
Pen.color = Color.blue;
Pen.draw;
});

Pen.addOval(Rect.aboutPoint(Point(xOffset + (curPos.x * (120 / imageDist)), yOffset + (curPos.y.neg * (120 / imageDist))), 5, 5));
Pen.color = Color.black;
Pen.draw;

Pen.line(Point(xOffset, yOffset + 150), Point(xOffset, yOffset + 250));
Pen.stroke;
});

userView.keyDownAction = ({arg view, char, mod, unicode, keycode, key;
switch(key,
16r1000012, {moveButtons[0].focus; dirKeyBlockTasks[0].stop; jogTasks[0].pause; jogTasks[0].play(AppClock)},
16r1000013, {moveButtons[1].focus; dirKeyBlockTasks[1].stop; jogTasks[1].pause; jogTasks[1].play(AppClock)},
16r1000014, {moveButtons[2].focus; dirKeyBlockTasks[2].stop; jogTasks[2].pause; jogTasks[2].play(AppClock)},
16r1000015, {moveButtons[3].focus; dirKeyBlockTasks[3].stop; jogTasks[3].pause; jogTasks[3].play(AppClock)})
});


// create all the jog buttons and logic
dirKeyBlockTasks = [];
jogTasks = [];
jogRates = [0, 0, 0, 0, 0, 0, 0, 0];
moveButtons = ([[-1, 0], [0, -1], [1, 0], [0, 1], [-1, 0], [0, -1], [1, 0], [0, 1]].collect({arg m, i;
var icons = ["◄", "▲", "►", "▼", "↻", "+", "↺", "-"], button;

// speeds up the jog based on how long the button was pressed
jogTasks = jogTasks.add(
Task({
dirKeyBlockTasks[i].stop;
loop{
jogRates[i] = (jogRates[i] + 0.1).clip(0, 10);
if(i < 4, {
// cartesian horizontal movement
if(m[0].abs == 1, {tarPos.x = tarPos.x + (jogRates[i] * m[0])});
// cartesian vertical movement
if(m[1].abs == 1, {tarPos.y = tarPos.y + (jogRates[i] * m[1].neg);});
}, {// polar change theta (rotate)
if(m[0].abs == 1, {tarPos.theta = ((tarPos.theta * (180 / pi)) + (jogRates[i] * m[0])) * (pi / 180.0)});
// polar change magnitude
if(m[1].abs == 1, {tarPos.rho = tarPos.rho + (jogRates[i] * m[1].neg)});
});
moveTo.value(tarPos);
0.2.wait
};
})
);

// hack to acount for a key held down
dirKeyBlockTasks = dirKeyBlockTasks.add(Task({0.1.wait; jogRates[i] = 0;jogTasks[i].stop}));

// create buttons
button = Button(window, Rect(xOffset - 12.5 + (25 * m[0]) + if(i < 4, {-175}, {175}), yOffset + 187.5 + (25 * m[1]), 25, 25))
.states_([[icons[i]]])
.mouseDownAction_({jogRates[i] = 0; jogTasks[i].play(AppClock)})
.action_({jogTasks[i].stop(AppClock)})
.enabled_(false)
.keyDownAction_({arg butt, char, mod, unicode, keycode, key;
switch(key,
16r1000012, {moveButtons[0].focus; dirKeyBlockTasks[0].stop; jogTasks[0].pause; jogTasks[0].play(AppClock); true},
16r1000013, {moveButtons[1].focus; dirKeyBlockTasks[1].stop; jogTasks[1].pause; jogTasks[1].play(AppClock); true},
16r1000014, {moveButtons[2].focus; dirKeyBlockTasks[2].stop; jogTasks[2].pause; jogTasks[2].play(AppClock); true},
16r1000015, {moveButtons[3].focus; dirKeyBlockTasks[3].stop; jogTasks[3].pause; jogTasks[3].play(AppClock); true},
{false})})
.keyUpAction_({arg butt, char, mod, unicode, keycode, key;
switch(key,
16r1000012, {dirKeyBlockTasks[0].start(AppClock); true},
16r1000013, {dirKeyBlockTasks[1].start(AppClock); true},
16r1000014, {dirKeyBlockTasks[2].start(AppClock); true},
16r1000015, {dirKeyBlockTasks[3].start(AppClock); true},
{false})})
}));


// position text fields
StaticText(window, Rect(xOffset - 82, yOffset + 150, 300, 20)).string_("cartesian");
StaticText(window, Rect(xOffset + 39, yOffset + 150, 300, 20)).string_("polar");
curPosFields = [];
tarPosFields = ["x", "y", "ρ", "θ"].collect({arg v, i;
StaticText(window, Rect(xOffset + 22.5 + (55 * (i - 2)), yOffset + 170, 50, 20)).string_(v);
curPosFields = curPosFields.add(StaticText(window, Rect(xOffset + 5 + (55 * (i - 2)), yOffset + 220, 50, 20)).string_("0.0"));
TextField(window, Rect(xOffset + 2.5 + (55 * (i - 2)), yOffset + 190, 50, 20))
.string_("0.0")
.enabled_(false)
.action_({arg field;
if(i < 2, {
tarPos.x = tarPosFields[0].string.asFloat;
tarPos.y = tarPosFields[1].string.asFloat;
tarPosFields[2].string = tarPos.rho.round(0.1).asString;
tarPosFields[3].string = (if(tarPos.theta >= 0, {0}, {360}) + (tarPos.theta * (180 / pi))).round(0.1).asString;
}, {
tarPos.rho = tarPosFields[2].string.asFloat;
tarPos.theta = tarPosFields[3].string.asFloat * (pi / 180);
tarPosFields[0].string = tarPos.x.round(0.1).asString;
tarPosFields[1].string = tarPos.y.round(0.1).asString;
});
moveTo.value(tarPos)})
});


// calibration wizard
calibrationSteps = [
"1) find center image",
"2) find northwest image \ntry first by using only the ↻ ↺ buttons to change θ",
"3) compute all other points \nthis will erase previously saved points unless skipped",
"4) find north image",
"5) find northeast image",
"6) find east image",
"7) find southeast image",
"8) find south image",
"9) find southwest image",
"10) find west image"
];

// disables everything till the point is reached between each step in the wizard
wizMoveBlock = Task({
while({curPos.dist(tarPos) > 1}, {
moveButtons.do({arg button; button.enabled = false});
wizardButtons.do({arg button; button.enabled = false});
tarPosFields.do({arg field; field.enabled = false});
0.1.wait;
});
wizardButtons.do({arg button; button.enabled = true});
wizardButtons[2].focus;
moveButtons.do({arg button; button.enabled = true});
tarPosFields.do({arg field; field.enabled = true});
});

// automate / calibrate button
Button.new(window, Rect.aboutPoint(Point(xOffset, yOffset + 270), 75, 12.5))
.states_([["calibrate"], ["automate"]])
.action_({arg button;
if(button.value == 0, {
automate = true;
curWizardText.string = "";
wizardButtons.do({arg button; button.visible = false});
}, {
automate = false;
curWizardText.string = calibrationSteps[0];
tarPos = imagePositions[4].deepCopy;
moveTo.value(tarPos);
wizMoveBlock.start(AppClock);
curWizardStep = 0;
wizardButtons.do({arg button; button.visible = true});
});
moveButtons.do({arg button; button.enabled = automate.not});
tarPosFields.do({arg field; field.enabled = automate.not});
});

// wizard button logic
curWizardStep = 0;
curWizardText = StaticText(window, Rect.aboutPoint(Point(xOffset, yOffset + 310), 200, 20)).string_("").align_(\center);
wizardButtons = ["back", "skip", "next"].collect({arg t, i;
var pointSeq, button;
pointSeq = [4, 0, 0, 1, 2, 5, 8, 7, 6, 3, 4];
button = Button(window, Rect.aboutPoint(Point(xOffset - 60 + (60 * i), yOffset + 350), 25, 12.5))
.states_([[t]])
.action_({arg button;

// code to automate populate all the points based on relation between two of the points
if((curWizardStep == 2) && (i == 2), {
if(imagePositions[0].rho == imageDist, {

}, {

});
rotation = imagePositions[0].theta - (0.75 * pi);
imagePositions[1].theta = (0.5 * pi) + rotation;
imagePositions[2].theta = (0.25 * pi) + rotation;
imagePositions[3].theta = pi + rotation;
imagePositions[5].theta = rotation;
imagePositions[6].theta = (1.25 * pi) + rotation;
imagePositions[7].theta = (1.5 * pi) + rotation;
imagePositions[8].theta = (1.75 * pi) + rotation;
});

if((curWizardStep == 0) && (i == 2), {serialPort.putAll("c")});
if(i == 2, {imagePositions[pointSeq[curWizardStep]] = if(curWizardStep == 0, {Point(0, 0)}, {curPos.deepCopy})});
curWizardStep = (curWizardStep + if(i == 0, {-1}, {1})) % 10;
tarPos = imagePositions[pointSeq[curWizardStep]].deepCopy;
moveTo.value(tarPos);
wizMoveBlock.start(AppClock);
//wizardButtons.do({arg button; button.enabled = true});
//moveButtons.do({arg button; button.enabled = true});
//tarPosFields.do({arg field; field.enabled = true});
curWizardText.string = calibrationSteps[curWizardStep];
//wizardButtons[1].visible = if(curWizardStep == 2, {true}, {false});
})
.visible_(false)
});

serialListener.play(AppClock);
)


Loading…
Cancel
Save