diff --git a/arduino/multistepper_prototype/multistepper_prototype.ino b/arduino/multistepper_prototype/multistepper_prototype.ino new file mode 100644 index 0000000..4c76300 --- /dev/null +++ b/arduino/multistepper_prototype/multistepper_prototype.ino @@ -0,0 +1,171 @@ +// Include the AccelStepper library: +#include + +// 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); +} diff --git a/klayout/separate_wafers.py b/klayout/separate_wafers.py index 55ea95d..6bf690c 100644 --- a/klayout/separate_wafers.py +++ b/klayout/separate_wafers.py @@ -7,7 +7,9 @@ layout = pya.Layout() layout.read(os.path.join(base_dir, "..", "gds", "image_with_alignment_marks_overlapped.gds")) layout.delete_layer(1) -layout.top_cell().shapes(0).insert(pya.Box(-9000000, 6850000 - 1850000 + 30000, 9000000, 6850000 - 1850000 + 30000 + 5000)) +layer3_index = layout.insert_layer(pya.LayerInfo.new(3, 0)) +layout.top_cell().shapes(0).insert(pya.Box(-7500000, 6850000 - 1850000 + 30000, 7500000, 6850000 - 1850000 + 30000 + 5000)) +layout.top_cell().shapes(layer3_index).insert(pya.Box(-7500000, -7500000, 7500000, 7500000)) #layout.transform(pya.Trans(2, False, 0, 0)) layout.write(os.path.join(base_dir, "..", "gds", "wafer_1.gds")) @@ -17,9 +19,12 @@ layout = pya.Layout() layout.read(os.path.join(base_dir, "..", "gds", "image_with_alignment_marks_overlapped.gds")) layout.delete_layer(0) +layer3_index = layout.insert_layer(pya.LayerInfo.new(3, 0)) #layout.transform(pya.Trans(2, True, 0, 0)) layout.transform(pya.Trans(0, True, 0, 0)) -layout.top_cell().shapes(1).insert(pya.Box(-9000000, 6850000 - 1850000 + 30000, 9000000, 6850000 - 1850000 + 30000 + 5000)) +layout.top_cell().shapes(1).insert(pya.Box(-7500000, 6850000 - 1850000 + 30000, 7500000, 6850000 - 1850000 + 30000 + 5000)) +layout.top_cell().shapes(layer3_index).insert(pya.Box(-7500000, -7500000, 7500000, 7500000)) + layout.write(os.path.join(base_dir, "..", "gds", "wafer_2.gds")) diff --git a/klayout/shift_tester_inverted.py b/klayout/shift_tester_inverted.py index 028cdad..73da09e 100644 --- a/klayout/shift_tester_inverted.py +++ b/klayout/shift_tester_inverted.py @@ -1,8 +1,8 @@ import pya #amount to shift in units of distance between images -shift_x = -1 -shift_y = -1 +shift_x = 0 +shift_y = 0.1 #vars on current sizes shift_mult = 5 diff --git a/score/lilypond/berger_knuth/berger_knuth.pdf b/score/lilypond/berger_knuth/berger_knuth.pdf index 4c60667..c9f7f8d 100644 Binary files a/score/lilypond/berger_knuth/berger_knuth.pdf and b/score/lilypond/berger_knuth/berger_knuth.pdf differ diff --git a/supercollider/main.scd b/supercollider/main.scd index fca87db..a5734c0 100644 --- a/supercollider/main.scd +++ b/supercollider/main.scd @@ -1,7 +1,7 @@ ~seed = 11735; ~dir = thisProcess.nowExecutingPath.dirname; -PathName.new(~dir).files.do({arg path; if(path.fileName != "main.scd", {path.fileName.loadRelative})}); +PathName.new(~dir).files.do({arg path; if((path.fileName != "main.scd") && (path.fileName != "stepper_control.scd"), {path.fileName.loadRelative})}); ~bergerTiling = ~berger.value(500, 500, true); @@ -12,7 +12,7 @@ PathName.new(~dir).files.do({arg path; if(path.fileName != "main.scd", {path.fil ~bergerSound.play; ~bergerTranscribe.value(~bergerMusic); ~visualize.value(~berger.value(100, 100), 0, 0, scale: 1, name: "berger") -s.record(~dir +/+ ".." +/+ "recs" +/+ "berger_knuth.wav", duration: 300); +s.record(~dir +/+ ".." +/+ "recs" +/+ "berger_knuth.wav", duration: (30 * 60)); ~robinsonTiling = ~robinson.value(8); @@ -22,7 +22,7 @@ s.record(~dir +/+ ".." +/+ "recs" +/+ "berger_knuth.wav", duration: 300); ~robinsonSound.play; ~robinsonTranscribe.value(~robinsonMusic); ~visualize.value(~robinsonTiling, 0, 0, 200, 200, scale: 1, name: "robinson"); -s.record(~dir +/+ ".." +/+ "recs" +/+ "robinson.wav", duration: 300); +s.record(~dir +/+ ".." +/+ "recs" +/+ "robinson.wav", duration: (30 * 60)); //Potential TODO: add (de)crescendo markings and update synthdef to have the fades @@ -33,7 +33,7 @@ s.record(~dir +/+ ".." +/+ "recs" +/+ "robinson.wav", duration: 300); ~penroseSound.play; ~penroseTranscribe.value(~penroseMusic); ~visualize.value(~penroseTiling, 0, 0, name: "penrose"); -s.record(~dir +/+ ".." +/+ "recs" +/+ "penrose.wav", duration: 300); +s.record(~dir +/+ ".." +/+ "recs" +/+ "penrose.wav", duration: (30 * 60)); ~ammannTiling = ~ammann.value(645, 105); diff --git a/supercollider/stepper_control.scd b/supercollider/stepper_control.scd new file mode 100644 index 0000000..c68414c --- /dev/null +++ b/supercollider/stepper_control.scd @@ -0,0 +1,441 @@ +( +var imageDist, micronsPerStep, automation, imgPositions, curPos, tarPos, +netAddress, serialPort, serialListener, +moveTo, jogControl, jogHorizontal, jogVertical, +imgSelect, imgCalibrate, automate; + +// init global vars +imageDist = 100; // in steps +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, { + if((curPos.x - tarPos.x).abs < 100, {tarPos.x = imageDist.rand2}); + if((curPos.y - tarPos.y).abs < 100, {tarPos.y = imageDist.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; + }, { + 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); +) + + +( +// 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); +) +