major cleanup and development of tracker
parent
e1a2324b0c
commit
5ffb61d4f4
@ -1,171 +1,216 @@
|
||||
|
||||
// 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
|
||||
#define xLimitSwitchPosPin 2
|
||||
#define xLimitSwitchNegPin 3
|
||||
#define xDirPin 4
|
||||
#define xStepPin 5
|
||||
#define xPowerPin 6
|
||||
|
||||
// Set stepper 2 pins
|
||||
#define m2LimitNegPin 9
|
||||
#define m2LimitPosPin 10
|
||||
#define m2DirPin 11
|
||||
#define m2StepPin 12
|
||||
#define m2PowerPin 13
|
||||
#define yLimitSwitchPosPin 9
|
||||
#define yLimitSwitchNegPin 10
|
||||
#define yDirPin 11
|
||||
#define yStepPin 12
|
||||
#define yPowerPin 13
|
||||
|
||||
#define motorInterfaceType 1
|
||||
|
||||
// Create a new instance of the AccelStepper class:
|
||||
AccelStepper m1Stepper = AccelStepper(motorInterfaceType, m1StepPin, m1DirPin);
|
||||
AccelStepper m2Stepper = AccelStepper(motorInterfaceType, m2StepPin, m2DirPin);
|
||||
// Create new instances of the AccelStepper class:
|
||||
AccelStepper xStepper = AccelStepper(motorInterfaceType, xStepPin, xDirPin);
|
||||
AccelStepper yStepper = AccelStepper(motorInterfaceType, yStepPin, yDirPin);
|
||||
|
||||
unsigned long previousMillis = 0;
|
||||
unsigned long currentMillis = 0;
|
||||
unsigned long previousMillis = 0;
|
||||
unsigned long currentMillis = 0;
|
||||
bool printMode = false;
|
||||
|
||||
void setup() {
|
||||
// safeMode means that the limit has been hit
|
||||
bool xSafeMode = false;
|
||||
long xLimitPos = 0;
|
||||
|
||||
pinMode(m1PowerPin, OUTPUT);
|
||||
pinMode(m1LimitNegPin, INPUT);
|
||||
pinMode(m1LimitPosPin, INPUT);
|
||||
bool ySafeMode = false;
|
||||
long yLimitPos = 0;
|
||||
|
||||
pinMode(m2PowerPin, OUTPUT);
|
||||
pinMode(m2LimitNegPin, INPUT);
|
||||
pinMode(m2LimitPosPin, INPUT);
|
||||
int microsteps = 1;
|
||||
float maxSpeedConstant = 350 * microsteps;
|
||||
float accelerationConstant = 100 * microsteps;
|
||||
|
||||
Serial.begin(115200);
|
||||
void setup() {
|
||||
|
||||
// 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);
|
||||
}
|
||||
pinMode(xPowerPin, OUTPUT);
|
||||
pinMode(xLimitSwitchNegPin, INPUT);
|
||||
pinMode(xLimitSwitchPosPin, INPUT);
|
||||
|
||||
pinMode(yPowerPin, OUTPUT);
|
||||
pinMode(yLimitSwitchNegPin, INPUT);
|
||||
pinMode(yLimitSwitchPosPin, INPUT);
|
||||
|
||||
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.begin(115200);
|
||||
|
||||
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("");
|
||||
xStepper.setMaxSpeed(maxSpeedConstant);
|
||||
xStepper.setAcceleration(accelerationConstant);
|
||||
//xStepper.setCurrentPosition(0);
|
||||
|
||||
previousMillis = currentMillis;
|
||||
}
|
||||
yStepper.setMaxSpeed(maxSpeedConstant);
|
||||
yStepper.setAcceleration(accelerationConstant);
|
||||
//yStepper.setCurrentPosition(0);
|
||||
}
|
||||
|
||||
// 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);
|
||||
void stepperLogic(int stepperIndex, bool printMode){
|
||||
|
||||
// init vars
|
||||
AccelStepper *stepper;
|
||||
int powerPin;
|
||||
int limitSwitchNeg;
|
||||
int limitSwitchPos;
|
||||
bool *safeMode;
|
||||
long *limitPos;
|
||||
|
||||
// set vars based on stepper index
|
||||
if(stepperIndex == 1){
|
||||
stepper = &xStepper;
|
||||
powerPin = xPowerPin;
|
||||
limitSwitchNeg = digitalRead(xLimitSwitchNegPin);
|
||||
limitSwitchPos = digitalRead(xLimitSwitchPosPin);
|
||||
safeMode = &xSafeMode;
|
||||
limitPos = &xLimitPos;
|
||||
} else {
|
||||
digitalWrite(m1PowerPin, LOW);
|
||||
m1Stepper.run();
|
||||
stepper = &yStepper;
|
||||
powerPin = yPowerPin;
|
||||
limitSwitchNeg = digitalRead(yLimitSwitchNegPin);
|
||||
limitSwitchPos = digitalRead(yLimitSwitchPosPin);
|
||||
safeMode = &ySafeMode;
|
||||
limitPos = &yLimitPos;
|
||||
}
|
||||
|
||||
// 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);
|
||||
// stepper logic
|
||||
if (!*safeMode && (limitSwitchNeg == limitSwitchPos)){
|
||||
// just keep swimming
|
||||
digitalWrite(powerPin, LOW);
|
||||
stepper->run();
|
||||
//runLogic(stepperIndex, stepperFlip, stepper, last, distance, dir);
|
||||
|
||||
} else if (!*safeMode && (limitSwitchNeg != limitSwitchPos)) {
|
||||
// limit hit; go into safeMode; power down
|
||||
*safeMode = true;
|
||||
*limitPos = stepper->currentPosition();
|
||||
stepper->setSpeed(0);
|
||||
stepper->moveTo(stepper->currentPosition());
|
||||
digitalWrite(powerPin, HIGH);
|
||||
Serial.println("!! limit hit, move the other direction");
|
||||
|
||||
} else if (*safeMode && (abs(*limitPos - stepper->currentPosition()) < (1000 * microsteps))) {
|
||||
// just keep swimming for a while (only in the other direction), hoping to get out of safeMode within 1000 steps
|
||||
if((limitSwitchNeg == 0 && (stepper->currentPosition() > stepper->targetPosition())) ||
|
||||
(limitSwitchPos == 0 && (stepper->currentPosition() < stepper->targetPosition()))) {
|
||||
*safeMode = (limitSwitchNeg != limitSwitchPos);
|
||||
digitalWrite(powerPin, LOW);
|
||||
stepper->run();
|
||||
}
|
||||
|
||||
} else if (stepper->distanceToGo() == 0) {
|
||||
// destination reached; power down
|
||||
digitalWrite(powerPin, 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
|
||||
// houston we have a problem; safeMode is still on after 10 steps from limitPos
|
||||
stepper->setSpeed(0);
|
||||
stepper->moveTo(stepper->currentPosition());
|
||||
digitalWrite(powerPin, HIGH);
|
||||
Serial.println("!!! limit switch still on after 1000 steps, this should NEVER happen");
|
||||
};
|
||||
|
||||
if (printMode) {
|
||||
//Serial.println("[" + String(stepperIndex) + ", " + String(stepper->currentPosition()) + ", " + String(stepper->targetPosition()) + ", " +
|
||||
//String(limitSwitchNeg) + ", " + String(limitSwitchPos) + ", " + String(*safeMode) + ", " + String(*limitPos) + "]");
|
||||
|
||||
Serial.println("[" + String(xStepper.currentPosition()) + ", " + String(yStepper.currentPosition()) + ", " +
|
||||
String(limitSwitchNeg) + ", " + String(limitSwitchPos) + "]");
|
||||
|
||||
/*
|
||||
if (stepperIndex == 1){
|
||||
Serial.println("------Stepper 1------");
|
||||
} else {
|
||||
Serial.println("------Stepper 2------");
|
||||
};
|
||||
Serial.print("curPos: ");
|
||||
Serial.println(stepper->currentPosition());
|
||||
Serial.print("tarPos: ");
|
||||
Serial.println(stepper->targetPosition());
|
||||
Serial.print("limitSwitchNeg: ");
|
||||
Serial.println(limitSwitchNeg);
|
||||
Serial.print("limitSwitchPos: ");
|
||||
Serial.println(limitSwitchPos);
|
||||
Serial.print("safeMode: ");
|
||||
Serial.println(*safeMode);
|
||||
Serial.print("limitPos: ");
|
||||
Serial.println(*limitPos);
|
||||
Serial.print("speed: ");
|
||||
Serial.println(stepper->speed());
|
||||
Serial.println("");
|
||||
*/
|
||||
|
||||
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
|
||||
long parseDest(char delimiter){
|
||||
long integerValue = 0; // throw away previous integerValue
|
||||
bool negativeNumber = false; // reset for negative
|
||||
char incomingByte;
|
||||
|
||||
while(1) { // force into a loop until delimiter is received
|
||||
incomingByte = Serial.read();
|
||||
/*
|
||||
if (incomingByte == 'c') {
|
||||
xStepper.setCurrentPosition(0);
|
||||
yStepper.setCurrentPosition(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (negativeNumber)
|
||||
integerValue = -integerValue;
|
||||
*/
|
||||
if (incomingByte == delimiter) 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
|
||||
return integerValue;
|
||||
}
|
||||
|
||||
integerValue = -integerValue; // this makes up for the fact that things are backwards
|
||||
m2Stepper.moveTo(integerValue);
|
||||
|
||||
void loop() {
|
||||
|
||||
currentMillis = millis();
|
||||
printMode = false;
|
||||
|
||||
if (currentMillis - previousMillis >= 100 == true ) {
|
||||
printMode = true;
|
||||
previousMillis = currentMillis;
|
||||
}
|
||||
|
||||
|
||||
stepperLogic(1, printMode);
|
||||
stepperLogic(2, printMode);
|
||||
|
||||
if (Serial.available() > 0) { // something came across serial
|
||||
|
||||
xStepper.moveTo(parseDest(' '));
|
||||
yStepper.moveTo(parseDest('\n'));
|
||||
|
||||
};
|
||||
|
||||
//delay(100);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,6 @@
|
||||
\relax
|
||||
\catcode 95\active
|
||||
\citation{*}
|
||||
\bibstyle{unsrt}
|
||||
\bibdata{hdp}
|
||||
\gdef \@abspage@last{240}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,286 @@
|
||||
\documentclass[10pt]{letter}
|
||||
|
||||
\usepackage[a4paper, top=0.7in, bottom=0.7in, left=0.7in, right=0.7in]{geometry}
|
||||
\usepackage{mathtools}
|
||||
\usepackage{wasysym}
|
||||
\usepackage{multicol}
|
||||
\usepackage{dirtree}
|
||||
\usepackage{underscore}
|
||||
\usepackage{pdfpages}
|
||||
\usepackage[framed,numbered]{sclang-prettifier}
|
||||
\usepackage{listings}
|
||||
\usepackage[obeyspaces]{url}
|
||||
\usepackage{datetime2}
|
||||
%\usepackage{draftwatermark}
|
||||
\renewcommand{\arraystretch}{1.3}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{enumitem}
|
||||
|
||||
\DTMsetdatestyle{default}
|
||||
\DTMsetup{datesep={.}}
|
||||
|
||||
%\SetWatermarkColor[rgb]{1, 0.6, 0.6}
|
||||
%\SetWatermarkScale{2}
|
||||
%\SetWatermarkHorCenter{1.25in}
|
||||
%\SetWatermarkVerCenter{1.25in}
|
||||
|
||||
% Define Language
|
||||
\lstdefinelanguage{Lilypond}
|
||||
{
|
||||
% list of keywords
|
||||
morekeywords={
|
||||
}
|
||||
}
|
||||
|
||||
% Set Language
|
||||
\lstset{
|
||||
numbers=left,
|
||||
numberstyle=\small,
|
||||
numberstyle = \color{black!33},
|
||||
numbersep=8pt,
|
||||
frame = single,
|
||||
language={Lilypond},
|
||||
}
|
||||
|
||||
\newenvironment{note}{
|
||||
\vspace{-3mm}
|
||||
\small
|
||||
\par
|
||||
\leftskip=4em\rightskip=5em
|
||||
\noindent\ignorespaces}{\par\smallskip}
|
||||
|
||||
\makeatletter
|
||||
\newenvironment{thebibliography}[1]
|
||||
{\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||
\leftmargin\labelwidth
|
||||
\advance\leftmargin\labelsep
|
||||
\usecounter{enumiv}%
|
||||
\let\p@enumiv\@empty
|
||||
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||
\sloppy
|
||||
\clubpenalty4000
|
||||
\@clubpenalty \clubpenalty
|
||||
\widowpenalty4000%
|
||||
\sfcode`\.\@m}
|
||||
{\def\@noitemerr
|
||||
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||
\endlist}
|
||||
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||
\makeatother
|
||||
|
||||
\begin{document}
|
||||
|
||||
\textit{\textbf{a history of the domino problem}} \\
|
||||
a performance-installation
|
||||
|
||||
\begin{flushright}
|
||||
michael winter \\ schloss solitude and cdmx; 2018 - 2019 \\
|
||||
\end{flushright}
|
||||
|
||||
\bigskip
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{cc}
|
||||
|
||||
\centering
|
||||
\includegraphics[width=0.49\linewidth]{selects/maquina.jpg}
|
||||
|
||||
\centering
|
||||
\includegraphics[width=0.49\linewidth]{selects/discos.jpg}
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Description / note}
|
||||
|
||||
\textit{a history of the domino problem} is a performance-installation that traces the history of an epistemological problem in mathematics about how things that one could never imagine fitting together, actually come together and unify in unexpected ways. The work comprises a set of musical compositions and a kinetic sculpture that sonify and visualize rare tilings (or mosaics) constructed from dominoes. The dominoes in these tilings are similar yet slightly different than those used in the popular game of the same name. As opposed to rectangles, they are squares with various color combinations along the edges (which can alternatively also be represented by numbers or patterns).\footnote{\url{https://en.wikipedia.org/wiki/Wang_tile}} Like in the game, the rule is that edges of adjacent dominoes in a tiling must match.
|
||||
|
||||
The tilings sonified and visualized in \textit{a history of the domino problem} are rare because there is no systematic way to find them. This is due to the fact that they are \textit{aperiodic}.\footnote{\url{https://en.wikipedia.org/wiki/Aperiodic_tiling}} One can think of an aperiodic tiling like an infinite puzzle with a peculiar characteristic. Given unlimited copies of dominoes with a finite set of color/pattern combinations for the edges, there is a solution that will result in a tiling that expands infinitely. However, in that solution, any periodic/repeating structure in the tiling will eventually be interrupted. This phenomenon is one of the most intriguing aspects of the work. As the music and the visuals are derived from the tilings, the resulting textures are always shifting ever so slightly.
|
||||
|
||||
The Domino Problem and its corresponding history are somewhat vexing and difficult to describe. The original problem asked if there exists an algorithm/computer program that, when given as input a finite set of dominoes with varying color combinations for the edges, can output a binary answer, `yes' or `no', whether or not copies of that set can form an infinite tiling. The reason why the Domino Problem is inextricably linked to whether or not aperiodic tilings exist is the following. The existence of aperiodic tilings would mean that such an algorithm \textit{does not} exist. The problem was first posed by Hao Wang in 1961. He actually conjectured that aperiodic tilings do not exist. However, in 1966, his student, Robert Berger, proved him wrong by discovering an infinite, aperiodic tiling constructed with copies of a set of 20,426 dominoes. With the original problem solved, mathematicians then took on the challenge of finding the smallest set of dominoes that would construct an infinite aperiodic tiling. Over the past 60 years, this number has been continually reduced until the most recent discovery of a set of 11 dominoes along with a proof that no smaller sets exist. It is a remarkable narrative/history of a particular epistemological problem that challenged a group of people not only to solve it, but to understand it to the extent possible.
|
||||
|
||||
The music was composed by writing computer programs that generate and scan the tilings such that musical material and information is correlated with the dominoes. Shifting structures in the tilings are reflected by similarly shifting textures in the music. The musical compositions can act as an accompaniment to the visual component by being performed as interventions within the installation or as singular pieces in concert. The visual component of the piece consists of a kinetic sculpture that displays the tilings using visual cryptography.\footnote{\url{https://en.wikipedia.org/wiki/Visual_cryptography}} In visual cryptography, a message is encrypted by dividing the information of the message into two `shadow' images, each which look completely random independently. The message is decrypted and revealed when the shadow images are combined/overlayed in a precise orientation. The use of visual cryptography to reveal the tilings is yet another reflection of the general motivation of the piece: exploring `how things fit together in unexpected ways'. In \textit{a history of the domino problem}, the message \textit{is} the tilings. The shadow images are printed on photomasks, which are essentially high-resolution transparencies: quartz wafers with a chrome coating etched at a pixel size ranging from nano- to micrometers. I used photomasks for two reasons. One: the Domino Problem is about the limits of computation. What computers can and cannot do. Displaying the tilings using photomasks, which are typically implemented to manufacture computer chips, reflects the concept of the piece through its medium. Two: though the wafers are actually small in size, because they are printed at such high resolution, large portions of the tilings can be displayed. In other words: to go big, sometimes you have to go small. The kinetic sculpture uses a high-precision, motorized multiaxis stage to align the finely printed shadow images and reveal the tilings (along with 3 other images of poetic texts inspired by the history of the Domino Problem). The whole apparatus rests on a light source that illuminates the photomasks, which are then magnified and projected.
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Installation and performance setting}
|
||||
|
||||
As an installation, the apparatus that aligns the image should be centered in a dark room such that observers can view the photomasks up close. Ideally, this should be set up with a teleprompter mirror at a 45 degree angle above the apparatus so that the viewer can see the photomasks without having to bend over. On the other side of the mirror, a video camera is placed such that the resulting projected image aligns with what the viewer sees in the teleprompter mirror. The camera side of the mirror must be darkened out with a cover in order to allow the viewer to only see the reflection of the photomasks. The projection should be as large as possible and at as high a resolution as possible. Ideally, the camera should be able to zoom into the images of the tilings to show more detail.
|
||||
|
||||
In the installation, recordings of the musical pieces are played back; sometimes randomly and sometimes in sync with the respective tilings from which they were generated. The installation can be augmented (e.g. for an exhibition opening) by live performances of the musical pieces instead of the recordings. If so, direct access to the apparatus should be avoided in order for a situation where the observers can view the projected images and listen to the musical accompaniment in a tranquil and focused environment.
|
||||
|
||||
A demo of the apparatus is available at: \url{https://vimeo.com/375784136}.
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Photomask alignment}
|
||||
|
||||
Between the two photomasks, there are nine embedded images which can be seen at nine precise orientation organized in a 3 x 3 grid. The image area is surrounded by Moire pattern and Vernier markings to aid in the alignment. Provided below are a description of each of these markings.
|
||||
|
||||
\begin{center}
|
||||
\includegraphics[width=0.7\linewidth]{selects/oraclesannotated\string_cp.jpg}
|
||||
\end{center}
|
||||
|
||||
\begin{description}[labelindent=0.5cm]
|
||||
\item [unidimensional Verniers:] These are the most useful markings for image alignment. For each axis, there are a set of coarse Verniers (bordering the image) and a set of fine Verniers (further from the image) which move 5 times the speed of the coarse Veniers. Every time an image is aligned the white blob will be centered in both the coarse and fine Verniers. These markings essentially amplify and scale the distance between the image (640 microns) and can be used by a motion tracker in a closed-loop alignment system.
|
||||
\item [multidimenional Verniers:] These are Verniers that are centered in a two-dimenional space everytime an image is focused.
|
||||
\item [linear Moire grating:] These gratings can be used to make sure that the plates are aligned rotationally (resulting in a completely monochrome bar without any patterns).
|
||||
\item [circular Moire grating:] These gratings best represent the grid of the images. When an image is aligned the respective grating on the grid will be dark. The number of fringes denotes the accuracy of the alignment (none means perfectly aligned).
|
||||
\end{description}
|
||||
|
||||
|
||||
High precision optical stages are used for the alignment of the wafers. Ideally, the wafers are not touching (separated by a few microns). If the wafers touch, they will degrade over time as they move across each other. However, it is very difficult to achieve perfect alignment without the photomasks touching as the they need to be aligned in all 6 degrees of freedom ($x$, $y$, $z$, $\theta x$, $\theta y$, and $\theta z$) in order for the resulting image to be properly produced. Only the $x$ and $y$ axis need to be moved to find the images once all the other degrees of freedom are set accurately.
|
||||
|
||||
The original setup is as follows. Each of the photomasks are mounted onto tilt stages to be able to align the masks together rotationally (note that a more ideal setup would use goniometer stages). One of the tilt / goniometer stages is then affixed to a stage with 3 degrees of freedom: $x$, $y$, and $z$. The other is fixed directly to an optical breadboard.
|
||||
|
||||
To automate the alignment, high precision motors are used to move one of the photomasks on the $x$ and $y$ axes. In the original setup, the high precision motors are stepper motors. If the photomasks are not touching, an open-loop system can be used to automate alignment. That is, the accuracy of the step count of the motors should be sufficient for alignment. However, if the photomasks need to touch in order to produce the resulting images (as is the often the case with the original setup), the friction between the two photomasks will cause inaccuracies in an open-loop system. To compensate for this, the Vernier markings can be tracked optically (using motion-tracking software or opto-interrupts) in order to create a closed-loop system. The software used to automate the system and control the motors is detailed in the following section.
|
||||
|
||||
\begin{center}
|
||||
\includegraphics[width=0.7\linewidth]{selects/maquinalit\string_cp.jpg}
|
||||
\end{center}
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Computer code repository and documentation}
|
||||
|
||||
As the code is subject to change / improvements, the current state of the is available through a git repository at the following address: \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem}. The repository contains the computer code needed to run the installation along with all the code that generated the musical pieces and all the code / schematics / files to rebuild the installation. Further, this document along with each of the scores is marked with the date it was generated in order to verify it is the most recent version.
|
||||
|
||||
%\textbf{Overview of computer code}
|
||||
|
||||
%As the code is subject to change / improvements, the current state of the code is available through a git repository at the following address: \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem}. The repository contains the computer code needed to run the installation along with all the code that generated the musical pieces and all the code / schematics / files to rebuild the installation. Further, this document along with each of the scores is marked with the date it was generated in order to verify it is the most recent version.
|
||||
|
||||
%The repository is organized by the various languages / formats used to generate the musical pieces and visualizations as well as control the installation. To start, I will focus on the software needed to control the installation which includes four basic components:
|
||||
|
||||
%\begin{itemize}[labelindent=0.5cm]
|
||||
%\item A SuperCollider program with the filename \url{installation_control.scd}.
|
||||
|
||||
%\item A GUI interface written using Open Stage Control with the filename %\url{installation_control_gui.json}
|
||||
|
||||
%\item A motion tracker written in Python using OpenCV to track the Verniers for a closed-loop system with the filename \url{vernier_tracker.py}
|
||||
|
||||
%\item Arduino code to communicate between Supercollider and the stepper-motor drivers with the filename\\ \url{multistepper.ino}
|
||||
%\end{itemize}
|
||||
|
||||
%In the original setup, the Arduino code is loaded onto the Arduino and the three other programs are loaded and launched on a computer (e.g., a Raspberry Pi) connected to the Arduino to control the system. The SuperCollider program is the main control center. Both the GUI and the motion tracker (which takes in video input from the video camera) communicate to the SuperCollider program through OSC messages, which, in turn, sends serial messages to the Arduino to control the stepper motors via the stepper motor drivers. The Arduino code not only sends messages to the stepper motor drivers, but also takes input signals from the limit switches of the motors to ensure that the motors are not destroyed by running into a hard stop.
|
||||
|
||||
%All the other code (primarily written in SuperCollider) was written to generate the musical pieces as well as the visualizations and is maintained in the repository for reference. For the musical pieces, the SuperCollider code generates electronic realizations of the compositions and Lilypond files for generation of the musical scores.
|
||||
|
||||
%The photomasks were printed from the GDSII file format (\url{*.gds}). The workflow consisted of \url{*.png} generated by SuperCollider and then formatted into \url{*.gds} files using the Python KLayout API. However, all that is needed to reprint the photomasks are the files \url{wafer_1.gds} and \url{wafer_2.gds}. Note that the the \url{*.gds} files along with all larger files are archived in a code releases available at \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem/releases}. Again, all the other files in the repository are maintained for reference.
|
||||
|
||||
\bigskip
|
||||
\newpage
|
||||
|
||||
\textbf{Appendix: partial historical timeline of the domino problem, selected bibliography, acknowledgments, and tiling images}
|
||||
|
||||
pre-history:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
17th century & Leibniz & pioneer of binary arithmetic and the idea of computing machines \\
|
||||
ca 1928 & Hilbert & posed the original ``Entsheidungsproblem'' \\
|
||||
ca 1931 & Goedel & first showed that their exists truths that are undecidable with a finite set of axioms \\
|
||||
ca 1936 & Turing & invented the concept of the modern day computer and showed its limits yet was unfortunately persecuted for his sexuality despite being a key figure in the triumph of the allied nations against the nazi regime
|
||||
\end{tabular}
|
||||
|
||||
conjecture and first proof:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1961 & Wang & conjectured that aperiodic tilings of the plane did not exist \\
|
||||
ca 1966 & Berger & showed that an aperiodic set of 20000+ tiles exist (using his method this was quickly reduced to 104 then 92 by Berger and Knuth, respectively)
|
||||
\end{tabular}
|
||||
|
||||
first wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1971 & Robinson \& Lauchli & 56 and 40 tiles, respectively; using a similar technique of tiling arbitrarily large squares discovered independently
|
||||
\end{tabular}
|
||||
|
||||
second wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1986 & Penrose \& Amman & 32 and 16 tiles, respectively; using a method that translates different, non-squared aperiodic tiles into Wang tiles
|
||||
\end{tabular}
|
||||
|
||||
third wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1996 & Kari \& Culik & 13 tiles using an new construction with aperiodic integer sequences
|
||||
\end{tabular}
|
||||
|
||||
final reduction\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 2015 & Jaendel \& Rao & 11 tiles with an incredible computer-assisted proof that no smaller aperiodic sets exist
|
||||
\end{tabular}
|
||||
|
||||
\bigskip
|
||||
|
||||
\nocite{*}
|
||||
\bibliographystyle{unsrt}
|
||||
\bibliography{hdp}
|
||||
|
||||
\bigskip
|
||||
|
||||
A special thanks to: Alice Koegel, Ana Filipovic\textsuperscript{$*$}, Angela Butterstein\textsuperscript{†}, Anita Carey-Yard\textsuperscript{†}, Aykan Safoğlu\textsuperscript{$*$}, Bjoern Gottstein, Charis von Ristok\textsuperscript{†}, Christof Pruss, Daniela Kern-Michler\textsuperscript{§}, David Frühauf\textsuperscript{$*$}, David Mathews\textsuperscript{$*$}, Deborah Walker, Denise Helene Sumi\textsuperscript{$*$}, Didier Aschour, Edith Lázár\textsuperscript{$*$}, Elena Morena Weber\textsuperscript{$*$}, Elke aus dem Moore\textsuperscript{†}, Elmar Mellert, Florian Hoelscher, Gaetan Borot, Helmut Dietz\textsuperscript{†}, Irasema Fernandez, Johanna Markert\textsuperscript{$*$}, Julian Hartbaum\textsuperscript{‡}, Konstantin Lom\textsuperscript{†}, Leon Müllner\textsuperscript{$*$}, Luise Boege\textsuperscript{$*$}, Lukas Ludwig\textsuperscript{$*$}, Luke Wilkins\textsuperscript{$*$}, Marieanne Roth\textsuperscript{†}, Mathias Irmsher\textsuperscript{‡}, Mitra Wakil\textsuperscript{$*$}, Patrizia Bach\textsuperscript{$*$}, Philip Mecke\textsuperscript{$*$}, Robert Blatt\textsuperscript{$*$}, Sander Wickersheim\textsuperscript{†}, Savyon\textsuperscript{$*$}, Silke Pflüger\textsuperscript{†}, Sílvia das Fadas\textsuperscript{$*$}, Simar Preet Kaur\textsuperscript{$*$}, Sonja Flury\textsuperscript{†}, Sophia Guggenberger\textsuperscript{$*$}, Sophie-Charlotte Thieroff\textsuperscript{†}, Stephan Martens\textsuperscript{‡}, Susanna Flock\textsuperscript{$*$}, Tom Rosenberg\textsuperscript{$*$}, Vincenzo Talluto\textsuperscript{§}, and Yuval Shenhar\textsuperscript{$*$}.
|
||||
|
||||
* denotes contemporary resident at Akademie Schloss Solitude\\
|
||||
† denotes Akademie Schloss Solitude staff\\
|
||||
‡ denotes Institut für Mikroelektronik Stuttgart staff\\
|
||||
§ denotes Newport Optics staff\\
|
||||
|
||||
|
||||
|
||||
\vspace{\fill}
|
||||
|
||||
\begin{flushright}
|
||||
version generated: \today
|
||||
\end{flushright}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/berger\string_cp.jpg}
|
||||
Berger
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/robinson\string_cp.jpg}
|
||||
Robinson
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/penrose\string_cp.jpg}
|
||||
Penrose
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/ammann\string_cp.jpg}
|
||||
Ammann
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/kari\string_cp.jpg}
|
||||
Kari
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/jaendel\string_cp.jpg}
|
||||
Jaendel-Rao
|
||||
|
||||
\vspace*{\fill}
|
||||
|
||||
\includepdf[pages={-}]{../berger/berger\string_score.pdf}
|
||||
\includepdf[pages={-}]{../robinson/robinson\string_score.pdf}
|
||||
\includepdf[pages={-}]{../penrose/penrose\string_score.pdf}
|
||||
\includepdf[pages={-}]{../ammann/ammann\string_score.pdf}
|
||||
\includepdf[pages={-}]{../kari/kari\string_score.pdf}
|
||||
\includepdf[pages={-}]{../jaendel/jaendel\string_rao\string_score.pdf}
|
||||
|
||||
|
||||
|
||||
\end{document}
|
@ -0,0 +1,6 @@
|
||||
\relax
|
||||
\catcode 95\active
|
||||
\citation{*}
|
||||
\bibstyle{unsrt}
|
||||
\bibdata{hdp}
|
||||
\gdef \@abspage@last{78}
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -0,0 +1,295 @@
|
||||
\documentclass[10pt]{letter}
|
||||
|
||||
\usepackage[a4paper, top=0.7in, bottom=0.7in, left=0.7in, right=0.7in]{geometry}
|
||||
\usepackage{mathtools}
|
||||
\usepackage{wasysym}
|
||||
\usepackage{multicol}
|
||||
\usepackage{dirtree}
|
||||
\usepackage{underscore}
|
||||
\usepackage{pdfpages}
|
||||
\usepackage[framed,numbered]{sclang-prettifier}
|
||||
\usepackage{listings}
|
||||
\usepackage[obeyspaces]{url}
|
||||
\usepackage{datetime2}
|
||||
%\usepackage{draftwatermark}
|
||||
\renewcommand{\arraystretch}{1.3}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{enumitem}
|
||||
|
||||
\DTMsetdatestyle{default}
|
||||
\DTMsetup{datesep={.}}
|
||||
|
||||
%\SetWatermarkColor[rgb]{1, 0.6, 0.6}
|
||||
%\SetWatermarkScale{2}
|
||||
%\SetWatermarkHorCenter{1.25in}
|
||||
%\SetWatermarkVerCenter{1.25in}
|
||||
|
||||
% Define Language
|
||||
\lstdefinelanguage{Lilypond}
|
||||
{
|
||||
% list of keywords
|
||||
morekeywords={
|
||||
}
|
||||
}
|
||||
|
||||
% Set Language
|
||||
\lstset{
|
||||
numbers=left,
|
||||
numberstyle=\small,
|
||||
numberstyle = \color{black!33},
|
||||
numbersep=8pt,
|
||||
frame = single,
|
||||
language={Lilypond},
|
||||
}
|
||||
|
||||
\newenvironment{note}{
|
||||
\vspace{-3mm}
|
||||
\small
|
||||
\par
|
||||
\leftskip=4em\rightskip=5em
|
||||
\noindent\ignorespaces}{\par\smallskip}
|
||||
|
||||
\makeatletter
|
||||
\newenvironment{thebibliography}[1]
|
||||
{\list{\@biblabel{\@arabic\c@enumiv}}%
|
||||
{\settowidth\labelwidth{\@biblabel{#1}}%
|
||||
\leftmargin\labelwidth
|
||||
\advance\leftmargin\labelsep
|
||||
\usecounter{enumiv}%
|
||||
\let\p@enumiv\@empty
|
||||
\renewcommand\theenumiv{\@arabic\c@enumiv}}%
|
||||
\sloppy
|
||||
\clubpenalty4000
|
||||
\@clubpenalty \clubpenalty
|
||||
\widowpenalty4000%
|
||||
\sfcode`\.\@m}
|
||||
{\def\@noitemerr
|
||||
{\@latex@warning{Empty `thebibliography' environment}}%
|
||||
\endlist}
|
||||
\newcommand\newblock{\hskip .11em\@plus.33em\@minus.07em}
|
||||
\makeatother
|
||||
|
||||
\begin{document}
|
||||
|
||||
\textit{\textbf{a history of the domino problem}} \\
|
||||
a performance-installation
|
||||
|
||||
\begin{flushright}
|
||||
michael winter \\ schloss solitude and cdmx; 2018 - 2019 \\
|
||||
\end{flushright}
|
||||
|
||||
\bigskip
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{cc}
|
||||
|
||||
\centering
|
||||
\includegraphics[width=0.49\linewidth]{selects/maquina.jpg}
|
||||
|
||||
\centering
|
||||
\includegraphics[width=0.49\linewidth]{selects/discos.jpg}
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Description / note}
|
||||
|
||||
\textit{a history of the domino problem} is a performance-installation that traces the history of an epistemological problem in mathematics about how things that one could never imagine fitting together, actually come together and unify in unexpected ways. The work comprises a set of musical compositions and a kinetic sculpture that sonify and visualize rare tilings (or mosaics) constructed from dominoes. The dominoes in these tilings are similar yet slightly different than those used in the popular game of the same name. As opposed to rectangles, they are squares with various color combinations along the edges (which can alternatively also be represented by numbers or patterns).\footnote{\url{https://en.wikipedia.org/wiki/Wang_tile}} Like in the game, the rule is that edges of adjacent dominoes in a tiling must match.
|
||||
|
||||
The tilings sonified and visualized in \textit{a history of the domino problem} are rare because there is no systematic way to find them. This is due to the fact that they are \textit{aperiodic}.\footnote{\url{https://en.wikipedia.org/wiki/Aperiodic_tiling}} One can think of an aperiodic tiling like an infinite puzzle with a peculiar characteristic. Given unlimited copies of dominoes with a finite set of color/pattern combinations for the edges, there is a solution that will result in a tiling that expands infinitely. However, in that solution, any periodic/repeating structure in the tiling will eventually be interrupted. This phenomenon is one of the most intriguing aspects of the work. As the music and the visuals are derived from the tilings, the resulting textures are always shifting ever so slightly.
|
||||
|
||||
The Domino Problem and its corresponding history are somewhat vexing and difficult to describe. The original problem asked if there exists an algorithm/computer program that, when given as input a finite set of dominoes with varying color combinations for the edges, can output a binary answer, `yes' or `no', whether or not copies of that set can form an infinite tiling. The reason why the Domino Problem is inextricably linked to whether or not aperiodic tilings exist is the following. The existence of aperiodic tilings would mean that such an algorithm \textit{does not} exist. The problem was first posed by Hao Wang in 1961. He actually conjectured that aperiodic tilings do not exist. However, in 1966, his student, Robert Berger, proved him wrong by discovering an infinite, aperiodic tiling constructed with copies of a set of 20,426 dominoes. With the original problem solved, mathematicians then took on the challenge of finding the smallest set of dominoes that would construct an infinite aperiodic tiling. Over the past 60 years, this number has been continually reduced until the most recent discovery of a set of 11 dominoes along with a proof that no smaller sets exist. It is a remarkable narrative/history of a particular epistemological problem that challenged a group of people not only to solve it, but to understand it to the extent possible.
|
||||
|
||||
The music was composed by writing computer programs that generate and scan the tilings such that musical material and information is correlated with the dominoes. Shifting structures in the tilings are reflected by similarly shifting textures in the music. The musical compositions can act as an accompaniment to the visual component by being performed as interventions within the installation or as singular pieces in concert. The visual component of the piece consists of a kinetic sculpture that displays the tilings using visual cryptography.\footnote{\url{https://en.wikipedia.org/wiki/Visual_cryptography}} In visual cryptography, a message is encrypted by dividing the information of the message into two `shadow' images, each which look completely random independently. The message is decrypted and revealed when the shadow images are combined/overlayed in a precise orientation. The use of visual cryptography to reveal the tilings is yet another reflection of the general motivation of the piece: exploring `how things fit together in unexpected ways'. In \textit{a history of the domino problem}, the message \textit{is} the tilings. The shadow images are printed on photomasks, which are essentially high-resolution transparencies: quartz wafers with a chrome coating etched at a pixel size ranging from nano- to micrometers. I used photomasks for two reasons. One: the Domino Problem is about the limits of computation. What computers can and cannot do. Displaying the tilings using photomasks, which are typically implemented to manufacture computer chips, reflects the concept of the piece through its medium. Two: though the wafers are actually small in size, because they are printed at such high resolution, large portions of the tilings can be displayed. In other words: to go big, sometimes you have to go small. The kinetic sculpture uses a high-precision, motorized multiaxis stage to align the finely printed shadow images and reveal the tilings (along with 3 other images of poetic texts inspired by the history of the Domino Problem). The whole apparatus rests on a light source that illuminates the photomasks, which are then magnified and projected.
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Installation and performance setting}
|
||||
|
||||
As an installation, the apparatus that aligns the image should be centered in a dark room such that observers can view the photomasks up close. Ideally, this should be set up with a teleprompter mirror at a 45 degree angle above the apparatus so that the viewer can see the photomasks without having to bend over. On the other side of the mirror, a video camera is placed such that the resulting projected image aligns with what the viewer sees in the teleprompter mirror. The camera side of the mirror must be darkened out with a cover in order to allow the viewer to only see the reflection of the photomasks. The projection should be as large as possible and at as high a resolution as possible. Ideally, the camera should be able to zoom into the images of the tilings to show more detail.
|
||||
|
||||
In the installation, recordings of the musical pieces are played back; sometimes randomly and sometimes in sync with the respective tilings from which they were generated. The installation can be augmented (e.g. for an exhibition opening) by live performances of the musical pieces instead of the recordings. If so, direct access to the apparatus should be avoided in order for a situation where the observers can view the projected images and listen to the musical accompaniment in a tranquil and focused environment.
|
||||
|
||||
A demo of the apparatus is available at: \url{https://vimeo.com/375784136}.
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Photomask alignment}
|
||||
|
||||
Between the two photomasks, there are nine embedded images which can be seen at nine precise orientation organized in a 3 x 3 grid. The image area is surrounded by Moire pattern and Vernier markings to aid in the alignment. Provided below are a description of each of these markings.
|
||||
|
||||
\begin{center}
|
||||
\includegraphics[width=0.7\linewidth]{selects/oraclesannotated\string_cp.jpg}
|
||||
\end{center}
|
||||
|
||||
\begin{description}[labelindent=0.5cm]
|
||||
\item [unidimensional Verniers:] These are the most useful markings for image alignment. For each axis, there are a set of coarse Verniers (bordering the image) and a set of fine Verniers (further from the image) which move 5 times the speed of the coarse Veniers. Every time an image is aligned the white blob will be centered in both the coarse and fine Verniers. These markings essentially amplify and scale the distance between the image (640 microns) and can be used by a motion tracker in a closed-loop alignment system.
|
||||
\item [multidimenional Verniers:] These are Verniers that are centered in a two-dimenional space everytime an image is focused.
|
||||
\item [linear Moire grating:] These gratings can be used to make sure that the plates are aligned rotationally (resulting in a completely monochrome bar without any patterns).
|
||||
\item [circular Moire grating:] These gratings best represent the grid of the images. When an image is aligned the respective grating on the grid will be dark. The number of fringes denotes the accuracy of the alignment (none means perfectly aligned).
|
||||
\end{description}
|
||||
|
||||
|
||||
High precision optical stages are used for the alignment of the wafers. Ideally, the wafers are not touching (separated by a few microns). If the wafers touch, they will degrade over time as they move across each other. However, it is very difficult to achieve perfect alignment without the photomasks touching as the they need to be aligned in all 6 degrees of freedom ($x$, $y$, $z$, $\theta x$, $\theta y$, and $\theta z$) in order for the resulting image to be properly produced. Only the $x$ and $y$ axis need to be moved to find the images once all the other degrees of freedom are set accurately.
|
||||
|
||||
The original setup is as follows. Each of the photomasks are mounted onto tilt stages to be able to align the masks together rotationally (note that a more ideal setup would use goniometer stages). One of the tilt / goniometer stages is then affixed to a stage with 3 degrees of freedom: $x$, $y$, and $z$. The other is fixed directly to an optical breadboard.
|
||||
|
||||
To automate the alignment, high precision motors are used to move one of the photomasks on the $x$ and $y$ axes. In the original setup, the high precision motors are stepper motors. If the photomasks are not touching, an open-loop system can be used to automate alignment. That is, the accuracy of the step count of the motors should be sufficient for alignment. However, if the photomasks need to touch in order to produce the resulting images (as is the often the case with the original setup), the friction between the two photomasks will cause inaccuracies in an open-loop system. To compensate for this, the Vernier markings can be tracked optically (using motion-tracking software or opto-interrupts) in order to create a closed-loop system. The software used to automate the system and control the motors is detailed in the following section.
|
||||
|
||||
\begin{center}
|
||||
\includegraphics[width=0.7\linewidth]{selects/maquinalit\string_cp.jpg}
|
||||
\end{center}
|
||||
|
||||
\bigskip
|
||||
|
||||
\textbf{Computer code repository and documentation}
|
||||
|
||||
As the code is subject to change / improvements, the current state of the is available through a git repository at the following address: \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem}. The repository contains the computer code needed to run the installation along with all the code that generated the musical pieces and all the code / schematics / files to rebuild the installation. Further, this document along with each of the scores is marked with the date it was generated in order to verify it is the most recent version.
|
||||
|
||||
%\textbf{Overview of computer code}
|
||||
|
||||
%As the code is subject to change / improvements, the current state of the code is available through a git repository at the following address: \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem}. The repository contains the computer code needed to run the installation along with all the code that generated the musical pieces and all the code / schematics / files to rebuild the installation. Further, this document along with each of the scores is marked with the date it was generated in order to verify it is the most recent version.
|
||||
|
||||
%The repository is organized by the various languages / formats used to generate the musical pieces and visualizations as well as control the installation. To start, I will focus on the software needed to control the installation which includes four basic components:
|
||||
|
||||
%\begin{itemize}[labelindent=0.5cm]
|
||||
%\item A SuperCollider program with the filename \url{installation_control.scd}.
|
||||
|
||||
%\item A GUI interface written using Open Stage Control with the filename %\url{installation_control_gui.json}
|
||||
|
||||
%\item A motion tracker written in Python using OpenCV to track the Verniers for a closed-loop system with the filename \url{vernier_tracker.py}
|
||||
|
||||
%\item Arduino code to communicate between Supercollider and the stepper-motor drivers with the filename\\ \url{multistepper.ino}
|
||||
%\end{itemize}
|
||||
|
||||
%In the original setup, the Arduino code is loaded onto the Arduino and the three other programs are loaded and launched on a computer (e.g., a Raspberry Pi) connected to the Arduino to control the system. The SuperCollider program is the main control center. Both the GUI and the motion tracker (which takes in video input from the video camera) communicate to the SuperCollider program through OSC messages, which, in turn, sends serial messages to the Arduino to control the stepper motors via the stepper motor drivers. The Arduino code not only sends messages to the stepper motor drivers, but also takes input signals from the limit switches of the motors to ensure that the motors are not destroyed by running into a hard stop.
|
||||
|
||||
%All the other code (primarily written in SuperCollider) was written to generate the musical pieces as well as the visualizations and is maintained in the repository for reference. For the musical pieces, the SuperCollider code generates electronic realizations of the compositions and Lilypond files for generation of the musical scores.
|
||||
|
||||
%The photomasks were printed from the GDSII file format (\url{*.gds}). The workflow consisted of \url{*.png} generated by SuperCollider and then formatted into \url{*.gds} files using the Python KLayout API. However, all that is needed to reprint the photomasks are the files \url{wafer_1.gds} and \url{wafer_2.gds}. Note that the the \url{*.gds} files along with all larger files are archived in a code releases available at \url{https://unboundedpress.org/code/mwinter/a_history_of_the_domino_problem/releases}. Again, all the other files in the repository are maintained for reference.
|
||||
|
||||
\bigskip
|
||||
\newpage
|
||||
|
||||
\textbf{Appendix: partial historical timeline of the domino problem, selected bibliography, acknowledgments, and tiling images}
|
||||
|
||||
pre-history:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
17th century & Leibniz & pioneer of binary arithmetic and the idea of computing machines \\
|
||||
ca 1928 & Hilbert & posed the original ``Entsheidungsproblem'' \\
|
||||
ca 1931 & Goedel & first showed that their exists truths that are undecidable with a finite set of axioms \\
|
||||
ca 1936 & Turing & invented the concept of the modern day computer and showed its limits yet was unfortunately persecuted for his sexuality despite being a key figure in the triumph of the allied nations against the nazi regime
|
||||
\end{tabular}
|
||||
|
||||
conjecture and first proof:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1961 & Wang & conjectured that aperiodic tilings of the plane did not exist \\
|
||||
ca 1966 & Berger & showed that an aperiodic set of 20000+ tiles exist (using his method this was quickly reduced to 104 then 92 by Berger and Knuth, respectively)
|
||||
\end{tabular}
|
||||
|
||||
first wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1971 & Robinson \& Lauchli & 56 and 40 tiles, respectively; using a similar technique of tiling arbitrarily large squares discovered independently
|
||||
\end{tabular}
|
||||
|
||||
second wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1986 & Penrose \& Amman & 32 and 16 tiles, respectively; using a method that translates different, non-squared aperiodic tiles into Wang tiles
|
||||
\end{tabular}
|
||||
|
||||
third wave of reduction:\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 1996 & Kari \& Culik & 13 tiles using an new construction with aperiodic integer sequences
|
||||
\end{tabular}
|
||||
|
||||
final reduction\\
|
||||
\begin{tabular}{p{0.9in}p{1.3in}p{4.0in}}
|
||||
ca 2015 & Jaendel \& Rao & 11 tiles with an incredible computer-assisted proof that no smaller aperiodic sets exist
|
||||
\end{tabular}
|
||||
|
||||
\bigskip
|
||||
|
||||
\nocite{*}
|
||||
\bibliographystyle{unsrt}
|
||||
\bibliography{hdp}
|
||||
|
||||
\bigskip
|
||||
|
||||
A special thanks to: Alice Koegel, Ana Filipovic\textsuperscript{$*$}, Angela Butterstein\textsuperscript{†}, Anita Carey-Yard\textsuperscript{†}, Aykan Safoğlu\textsuperscript{$*$}, Bjoern Gottstein, Charis von Ristok\textsuperscript{†}, Christof Pruss, Daniela Kern-Michler\textsuperscript{§}, David Frühauf\textsuperscript{$*$}, David Mathews\textsuperscript{$*$}, Deborah Walker, Denise Helene Sumi\textsuperscript{$*$}, Didier Aschour, Edith Lázár\textsuperscript{$*$}, Elena Morena Weber\textsuperscript{$*$}, Elke aus dem Moore\textsuperscript{†}, Elmar Mellert, Florian Hoelscher, Gaetan Borot, Helmut Dietz\textsuperscript{†}, Irasema Fernandez, Johanna Markert\textsuperscript{$*$}, Julian Hartbaum\textsuperscript{‡}, Konstantin Lom\textsuperscript{†}, Leon Müllner\textsuperscript{$*$}, Luise Boege\textsuperscript{$*$}, Lukas Ludwig\textsuperscript{$*$}, Luke Wilkins\textsuperscript{$*$}, Marieanne Roth\textsuperscript{†}, Mathias Irmsher\textsuperscript{‡}, Mitra Wakil\textsuperscript{$*$}, Patrizia Bach\textsuperscript{$*$}, Philip Mecke\textsuperscript{$*$}, Robert Blatt\textsuperscript{$*$}, Sander Wickersheim\textsuperscript{†}, Savyon\textsuperscript{$*$}, Silke Pflüger\textsuperscript{†}, Sílvia das Fadas\textsuperscript{$*$}, Simar Preet Kaur\textsuperscript{$*$}, Sonja Flury\textsuperscript{†}, Sophia Guggenberger\textsuperscript{$*$}, Sophie-Charlotte Thieroff\textsuperscript{†}, Stephan Martens\textsuperscript{‡}, Susanna Flock\textsuperscript{$*$}, Tom Rosenberg\textsuperscript{$*$}, Vincenzo Talluto\textsuperscript{§}, and Yuval Shenhar\textsuperscript{$*$}.
|
||||
|
||||
* denotes contemporary resident at Akademie Schloss Solitude\\
|
||||
† denotes Akademie Schloss Solitude staff\\
|
||||
‡ denotes Institut für Mikroelektronik Stuttgart staff\\
|
||||
§ denotes Newport Optics staff\\
|
||||
|
||||
|
||||
|
||||
\vspace{\fill}
|
||||
|
||||
\begin{flushright}
|
||||
version generated: \today
|
||||
\end{flushright}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/berger\string_cp.jpg}
|
||||
Berger
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/robinson\string_cp.jpg}
|
||||
Robinson
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/penrose\string_cp.jpg}
|
||||
Penrose
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/ammann\string_cp.jpg}
|
||||
Ammann
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/kari\string_cp.jpg}
|
||||
Kari
|
||||
\vspace*{\fill}
|
||||
|
||||
\newpage
|
||||
\vspace*{\fill}
|
||||
\centering
|
||||
\includegraphics[width=1\linewidth]{selects/jaendel\string_cp.jpg}
|
||||
Jaendel-Rao
|
||||
|
||||
\vspace*{\fill}
|
||||
|
||||
\includepdf[pages={1-1}]{../berger/berger\string_score.pdf}
|
||||
\includepdf[pages={-7}]{../../score/lilypond\string_v2.24\string_update/berger\string_knuth/berger\string_knuth\string_merge\string_for\string_kali.pdf}
|
||||
|
||||
\includepdf[pages={1-1}]{../robinson/robinson\string_score.pdf}
|
||||
\includepdf[pages={-15}]{../../score/lilypond\string_v2.24\string_update/robinson/robinson\string_no\string_alto.pdf}
|
||||
|
||||
\includepdf[pages={1-1}]{../penrose/penrose\string_score.pdf}
|
||||
\includepdf[pages={-19}]{../../score/lilypond\string_v2.24\string_update/penrose/penrose\string_no\string_alto.pdf}
|
||||
|
||||
\includepdf[pages={1-1}]{../ammann/ammann\string_score.pdf}
|
||||
\includepdf[pages={-6}]{../../score/lilypond\string_v2.24\string_update/ammann\string_distributed/ammann\string_distributed.pdf}
|
||||
|
||||
\includepdf[pages={-}]{../kari/kari\string_score.pdf}
|
||||
|
||||
\includepdf[pages={1-1}]{../jaendel/jaendel\string_rao\string_score.pdf}
|
||||
|
||||
|
||||
|
||||
\end{document}
|
@ -1,2 +1,3 @@
|
||||
\relax
|
||||
\catcode 95\active
|
||||
\gdef \@abspage@last{36}
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -1,2 +1,3 @@
|
||||
\relax
|
||||
\catcode 95\active
|
||||
\gdef \@abspage@last{16}
|
||||
|
Binary file not shown.
@ -1,2 +1,3 @@
|
||||
\relax
|
||||
\catcode 95\active
|
||||
\gdef \@abspage@last{31}
|
||||
|
Binary file not shown.
@ -1,149 +0,0 @@
|
||||
# importing libraries
|
||||
import cv2
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
|
||||
|
||||
drawing = False
|
||||
point1 = ()
|
||||
point2 = ()
|
||||
|
||||
def mouse_drawing(event, x, y, flags, params):
|
||||
global point1, point2, drawing
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if drawing is False:
|
||||
drawing = True
|
||||
point1 = (x, y)
|
||||
else:
|
||||
drawing = False
|
||||
|
||||
elif event == cv2.EVENT_MOUSEMOVE:
|
||||
if drawing is True:
|
||||
point2 = (x, y)
|
||||
|
||||
# Our ROI, defined by two points
|
||||
p1, p2 = None, None
|
||||
state = 0
|
||||
|
||||
# Called every time a mouse event happen
|
||||
def on_mouse(event, x, y, flags, userdata):
|
||||
global state, point1, point2
|
||||
|
||||
# Left click
|
||||
if event == cv2.EVENT_LBUTTONUP:
|
||||
# Select first point
|
||||
if state == 0:
|
||||
point1 = (x,y)
|
||||
state += 1
|
||||
# Select second point
|
||||
elif state == 1:
|
||||
point2 = (x,y)
|
||||
state += 1
|
||||
|
||||
|
||||
#xFine = (848, 187, 225, 21.0)
|
||||
#yFine = (604, 402, 20.5, 276)
|
||||
|
||||
xFine = (848, 187, 848 + 225, 187 + 21.0)
|
||||
yFine = (604, 402, 604 + 20.5, 402 + 276)
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
def track(frame, ROI, centroid, update):
|
||||
if(update):
|
||||
crop = frame[int(ROI[1]):int(ROI[3]), int(ROI[0]):int(ROI[2])]
|
||||
crop = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
|
||||
crop = cv2.GaussianBlur(crop,(7,7),cv2.BORDER_DEFAULT)
|
||||
|
||||
#ret, thresh = cv2.threshold(crop, 100, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
|
||||
ret,thresh = cv2.threshold(crop, 50, 255, 0)
|
||||
|
||||
M = cv2.moments(thresh)
|
||||
|
||||
# calculate x,y coordinate of center
|
||||
if M["m00"] != 0:
|
||||
centroid[0] = int(M["m10"] / M["m00"])
|
||||
centroid[1] = int(M["m01"] / M["m00"])
|
||||
#else:
|
||||
# cX, cY = 0, 0
|
||||
#print(cY)
|
||||
cv2.circle(frame, (int(ROI[0]) + centroid[0], int(ROI[1]) + centroid[1]), 3, (0, 255, 0), -1)
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", mouse_drawing)
|
||||
cv2.namedWindow("Process")
|
||||
|
||||
# Create a VideoCapture object and read from input file
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 10000)
|
||||
|
||||
# Check if camera opened successfully
|
||||
if (cap.isOpened()== False):
|
||||
print("Error opening video file")
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
# Read until video is completed
|
||||
while(cap.isOpened()):
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = cap.read()
|
||||
if ret == True:
|
||||
# Display the resulting frame
|
||||
|
||||
if point1 and point2:
|
||||
px = sorted([point1[0], point2[0]])
|
||||
py = sorted([point1[1], point2[1]])
|
||||
#cv2.rectangle(frame, point1, point2, (0, 255, 0))
|
||||
xFine = (px[0], py[0], px[1], py[1])
|
||||
|
||||
crop = frame[int(xFine[1]):int(xFine[3]), int(xFine[0]):int(xFine[2])]
|
||||
gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
|
||||
fm = cv2.Laplacian(gray, cv2.CV_64F).var()
|
||||
|
||||
kernel = np.ones((30, 50), np.uint8)
|
||||
|
||||
text = "Not Blurry"
|
||||
blur = cv2.GaussianBlur(cv2.bitwise_not(crop),(1001,3), 3, 1) * 3
|
||||
dilation = cv2.dilate(gray, kernel, iterations=1)
|
||||
ret,dilation = cv2.threshold(dilation,20,255,cv2.THRESH_BINARY_INV)
|
||||
|
||||
mean = pow(dilation.mean(), 3)
|
||||
# if the focus measure is less than the supplied threshold,
|
||||
# then the image should be considered "blurry"
|
||||
if fm < 100.0:
|
||||
text = "Blurry"
|
||||
# show the image
|
||||
cv2.rectangle(frame, (int(xFine[0]), int(xFine[1])), (int(xFine[2]),int(xFine[3])), (0, 255, 0))
|
||||
cv2.rectangle(frame, (int(xFine[0]), int(xFine[1])), (int(xFine[2]),int(xFine[3])), (0, 255, 0))
|
||||
cv2.putText(frame, "{}: {:.2f}".format(text, fm), (10, 30),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
cv2.putText(frame, "{}: {:.2f}".format("Brightness", mean), (10, 100),
|
||||
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
cv2.imshow("Process", crop)
|
||||
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
key = cv2.waitKey(100)
|
||||
|
||||
if key == 32:
|
||||
cv2.waitKey()
|
||||
elif key == ord('q'):
|
||||
break
|
||||
|
||||
# Break the loop
|
||||
else:
|
||||
break
|
||||
|
||||
# When everything done, release
|
||||
# the video capture object
|
||||
cap.release()
|
||||
|
||||
# Closes all the frames
|
||||
cv2.destroyAllWindows()
|
@ -1,148 +0,0 @@
|
||||
# importing libraries
|
||||
import cv2
|
||||
import numpy as np
|
||||
from scipy import stats
|
||||
|
||||
|
||||
rectToSet = 'x'
|
||||
moving = False
|
||||
roiXCenter = (960, 195)
|
||||
roiYCenter = (615, 530)
|
||||
w = 10
|
||||
l1 = 50
|
||||
l2 = 150
|
||||
l3 = 20
|
||||
roiXInner = (roiXCenter[0] - l1, roiXCenter[1] - w, roiXCenter[0] + l1, roiXCenter[1] + w)
|
||||
roiXOuter = (roiXCenter[0] - l2, roiXCenter[1] - w, roiXCenter[0] + l2, roiXCenter[1] + w)
|
||||
roiXCourse = (roiXCenter[0] - l3, roiXCenter[1] + (w * 1), roiXCenter[0] + l3, roiXCenter[1] + (w * 3))
|
||||
|
||||
roiYInner = (roiYCenter[0] - w, roiYCenter[1] - l1, roiYCenter[0] + w, roiYCenter[1] + l1)
|
||||
roiYOuter = (roiYCenter[0] - w, roiYCenter[1] - l2, roiYCenter[0] + w, roiYCenter[1] + l2)
|
||||
roiYCourse = (roiYCenter[0] + (w * 1), roiYCenter[1] - l3, roiYCenter[0] + (w * 3), roiYCenter[1] + l3)
|
||||
|
||||
dilationVal = 75
|
||||
|
||||
def moveROI(event, x, y, flags, params):
|
||||
global roiXCenter, roiYCenter, roiXInner, roiXOuter, roiXCourse, roiYInner, roiYOuter, roiYCourse, moving
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
moving = True
|
||||
|
||||
elif event==cv2.EVENT_MOUSEMOVE:
|
||||
if moving==True:
|
||||
if rectToSet=='x':
|
||||
roiXCenter = (x, y)
|
||||
roiXInner = (roiXCenter[0] - l1, roiXCenter[1] - w, roiXCenter[0] + l1, roiXCenter[1] + w)
|
||||
roiXOuter = (roiXCenter[0] - l2, roiXCenter[1] - w, roiXCenter[0] + l2, roiXCenter[1] + w)
|
||||
roiXCourse = (roiXCenter[0] - l3, roiXCenter[1] + (w * 1), roiXCenter[0] + l3, roiXCenter[1] + (w * 3))
|
||||
|
||||
elif rectToSet=='y':
|
||||
roiYCenter = (x, y)
|
||||
roiYInner = (roiYCenter[0] - w, roiYCenter[1] - l1, roiYCenter[0] + w, roiYCenter[1] + l1)
|
||||
roiYOuter = (roiYCenter[0] - w, roiYCenter[1] - l2, roiYCenter[0] + w, roiYCenter[1] + l2)
|
||||
roiYCourse = (roiYCenter[0] + (w * 1), roiYCenter[1] - l3, roiYCenter[0] + (w * 3), roiYCenter[1] + l3)
|
||||
|
||||
elif event == cv2.EVENT_LBUTTONUP:
|
||||
moving = False
|
||||
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", moveROI)
|
||||
#cv2.namedWindow("Process")
|
||||
|
||||
# Create a VideoCapture object and read from input file
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 10000)
|
||||
|
||||
# Check if camera opened successfully
|
||||
if (cap.isOpened()== False):
|
||||
print("Error opening video file")
|
||||
|
||||
|
||||
def track(frame, roiInner, roiOuter, roiCourse):
|
||||
w = 30
|
||||
l1 = 30
|
||||
l2 = 100
|
||||
cropFine = frame[roiOuter[1]:roiOuter[3], roiOuter[0]:roiOuter[2]]
|
||||
cropCourse = frame[roiCourse[1]:roiCourse[3], roiCourse[0]:roiCourse[2]]
|
||||
#gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
#may not need any of this
|
||||
kernel = np.ones((dilationVal, dilationVal), np.uint8)
|
||||
dilation = cv2.dilate(cropFine, kernel, iterations=1)
|
||||
ret,tresh = cv2.threshold(dilation,20,255,cv2.THRESH_BINARY)
|
||||
|
||||
#mean = pow(frame[roiInner[1]:roiInner[3], roiInner[0]:roiInner[2]].mean(), 3)
|
||||
frame[roiOuter[1]:roiOuter[3], roiOuter[0]:roiOuter[2]] = tresh
|
||||
meanFine = pow(frame[roiInner[1]:roiInner[3], roiInner[0]:roiInner[2]].mean(), 2)
|
||||
meanCourse = frame[roiCourse[1]:roiCourse[3], roiCourse[0]:roiCourse[2]].mean()
|
||||
|
||||
mean = 0
|
||||
if(meanCourse > 10):
|
||||
mean = meanFine
|
||||
|
||||
distance = pow(255, 2) - mean
|
||||
|
||||
return distance
|
||||
|
||||
def drawRects(frame):
|
||||
cv2.rectangle(frame, (roiXOuter[0], roiXOuter[1]), (roiXOuter[2], roiXOuter[3]), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiXCenter[0] - l1, roiXInner[1]), (roiXCenter[0], roiXInner[3]), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiXCenter[0], roiXInner[1]), (roiXCenter[0] + l1, roiXInner[3]), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiXCourse[0], roiXCourse[1]), (roiXCourse[2], roiXCourse[3]), (0, 255, 0))
|
||||
|
||||
cv2.rectangle(frame, (roiYOuter[0], roiYOuter[1]), (roiYOuter[2], roiYOuter[3]), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiYInner[0], roiYCenter[1] - l1), (roiYInner[2], roiYCenter[1]), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiYInner[0], roiYCenter[1]), (roiYInner[2], roiYCenter[1] + l1), (0, 255, 0))
|
||||
cv2.rectangle(frame, (roiYCourse[0], roiYCourse[1]), (roiYCourse[2], roiYCourse[3]), (0, 255, 0))
|
||||
|
||||
# Read until video is completed
|
||||
while(cap.isOpened()):
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = cap.read()
|
||||
if ret == True:
|
||||
# Display the resulting frame
|
||||
|
||||
distanceX = track(frame, roiXInner, roiXOuter, roiXCourse)
|
||||
distanceY = track(frame, roiYInner, roiYOuter, roiYCourse)
|
||||
|
||||
drawRects(frame)
|
||||
|
||||
cv2.putText(frame, "{}: {:.2f}".format("distance x", distanceX), (10, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
cv2.putText(frame, "{}: {:.2f}".format("distance y", distanceY), (10, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
|
||||
#fm = cv2.Laplacian(gray, cv2.CV_64F).var()
|
||||
#cv2.putText(frame, "{}: {:.2f}".format("blur", fm), (10, 30),
|
||||
#cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
#cv2.imshow("Process", tresh)
|
||||
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
key = cv2.waitKey(1)
|
||||
|
||||
if key == 32:
|
||||
cv2.waitKey()
|
||||
elif key == ord('+'):
|
||||
dilationVal = dilationVal + 1
|
||||
elif key == ord('-'):
|
||||
if dilationVal > 0:
|
||||
dilationVal = dilationVal - 1
|
||||
elif key == ord('x'):
|
||||
rectToSet = 'x'
|
||||
elif key == ord('y'):
|
||||
rectToSet = 'y'
|
||||
elif key == ord('q'):
|
||||
break
|
||||
|
||||
# Break the loop
|
||||
else:
|
||||
break
|
||||
|
||||
# When everything done, release
|
||||
# the video capture object
|
||||
cap.release()
|
||||
|
||||
# Closes all the frames
|
||||
cv2.destroyAllWindows()
|
@ -1,53 +0,0 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
drawing = False
|
||||
point1 = ()
|
||||
point2 = ()
|
||||
|
||||
def mouse_drawing(event, x, y, flags, params):
|
||||
global point1, point2, drawing
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if drawing is False:
|
||||
drawing = True
|
||||
point1 = (x, y)
|
||||
else:
|
||||
drawing = False
|
||||
|
||||
elif event == cv2.EVENT_MOUSEMOVE:
|
||||
if drawing is True:
|
||||
point2 = (x, y)
|
||||
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
|
||||
# Exit if video not opened.
|
||||
if not cap.isOpened():
|
||||
print("Could not open video")
|
||||
sys.exit()
|
||||
|
||||
# Read first frame.
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 5000)
|
||||
ok, initFrame = cap.read()
|
||||
if not ok:
|
||||
print('Cannot read video file')
|
||||
sys.exit()
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
#cv2.setMouseCallback("Frame", mouse_drawing)
|
||||
|
||||
while cap.isOpened():
|
||||
ok, frame = cap.read()
|
||||
|
||||
if ok == True:
|
||||
|
||||
#if point1 and point2:
|
||||
# cv2.rectangle(frame, point1, point2, (0, 255, 0))
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
cap.release()
|
||||
cv2.destroyAllWindows()
|
@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Streaming Demonstration</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Video Streaming Demonstration</h1>
|
||||
<img src="{{ url_for('video_feed') }}">
|
||||
</body>
|
||||
</html>
|
@ -1,162 +1,221 @@
|
||||
#This is a proof of concept for motion tracking of the vernier in very early stages
|
||||
|
||||
#!/usr/bin/python3
|
||||
from PyQt5.QtWidgets import QApplication, QWidget, QShortcut
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from PyQt5.QtCore import Qt, QThread, QPoint
|
||||
import time
|
||||
import cv2
|
||||
import sys
|
||||
from pythonosc.udp_client import SimpleUDPClient
|
||||
from flask import Flask, render_template, Response
|
||||
import threading
|
||||
import argparse
|
||||
|
||||
outputFrame = None
|
||||
lock = threading.Lock()
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
ip = "127.0.0.1"
|
||||
port = 57120
|
||||
|
||||
client = SimpleUDPClient(ip, port) # Create client
|
||||
|
||||
# Read video (eventually will be the live capture from the camera)
|
||||
video = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/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, initFrame = video.read()
|
||||
if not ok:
|
||||
print('Cannot read video file')
|
||||
sys.exit()
|
||||
|
||||
#frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
|
||||
#frame = cv2.GaussianBlur(frame,(5,5),cv2.BORDER_DEFAULT)
|
||||
|
||||
# all this for selecting ROI
|
||||
#xROI = cv2.selectROI('Tracking', initFrame)
|
||||
#yROI = cv2.selectROI('Tracking', initFrame)
|
||||
#print(xROI)
|
||||
#print(yROI)
|
||||
#xFine = (xROI[0], xROI[1], xROI[2], xROI[3] / 2)
|
||||
#xCourse = (xROI[0], xROI[1] + (xROI[3] / 2), xROI[2], xROI[3] / 2)
|
||||
#yFine = (yROI[0], yROI[1], yROI[2] / 2, yROI[3])
|
||||
#yCourse = (yROI[0] + (yROI[2] / 2), yROI[1], yROI[2] / 2, yROI[3])
|
||||
#print(xFine)
|
||||
#print(yFine)
|
||||
|
||||
xFine = (848, 187, 225, 21.0)
|
||||
yFine = (604, 402, 20.5, 276)
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
def track(frame, ROI, centroid, update):
|
||||
if(update):
|
||||
crop = frame[int(ROI[1]):int(ROI[1]+ROI[3]), int(ROI[0]):int(ROI[0]+ROI[2])]
|
||||
crop = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
|
||||
crop = cv2.GaussianBlur(crop,(7,7),cv2.BORDER_DEFAULT)
|
||||
|
||||
#ret, thresh = cv2.threshold(crop, 100, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
|
||||
ret,thresh = cv2.threshold(crop, 50, 255, 0)
|
||||
M = cv2.moments(thresh)
|
||||
|
||||
# calculate x,y coordinate of center
|
||||
if M["m00"] != 0:
|
||||
centroid[0] = int(M["m10"] / M["m00"])
|
||||
centroid[1] = int(M["m01"] / M["m00"])
|
||||
#else:
|
||||
# cX, cY = 0, 0
|
||||
#print(cY)
|
||||
cv2.circle(frame, (int(ROI[0]) + centroid[0], int(ROI[1]) + centroid[1]), 5, (255, 255, 255), -1)
|
||||
|
||||
def detect_motion():
|
||||
# grab global references to the video stream, output frame, and
|
||||
# lock variables
|
||||
global vs, outputFrame, lock
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
"""Video streaming generator function."""
|
||||
while True:
|
||||
# Read a new frame
|
||||
ok, frame = video.read()
|
||||
if not ok:
|
||||
break
|
||||
|
||||
if(frameCountMod == 0):
|
||||
track(frame, xFine, centroidX, True)
|
||||
track(frame, yFine, centroidY, True)
|
||||
xPos = (centroidX[0] / xFine[2]) * 2 - 1
|
||||
yPos = (centroidY[1] / yFine[3]) * 2 - 1
|
||||
client.send_message("/trackerpos", [xPos, yPos])
|
||||
else:
|
||||
track(frame, xFine, centroidX, False)
|
||||
track(frame, yFine, centroidY, False)
|
||||
|
||||
|
||||
frameCountMod = (frameCountMod + 1) % 10
|
||||
|
||||
cv2.rectangle(frame, (int(xFine[0]), int(xFine[1])), (int(xFine[0]+int(xFine[2])),int(xFine[1]+xFine[3])), (255, 255, 255), 5)
|
||||
cv2.rectangle(frame, (int(yFine[0]), int(yFine[1])), (int(yFine[0]+int(yFine[2])),int(yFine[1]+yFine[3])), (255, 255, 255), 5)
|
||||
|
||||
# Display result
|
||||
#cv2.imshow("Tracking", frame)
|
||||
#cv2.imshow("Crop", crop)
|
||||
|
||||
with lock:
|
||||
outputFrame = frame.copy()
|
||||
|
||||
# Exit if ESC pressed
|
||||
#k = cv2.waitKey(1) & 0xff
|
||||
#if k == 27 :
|
||||
# cv2.destroyWindow('Tracking')
|
||||
# break
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""Video streaming home page."""
|
||||
return render_template('index.html')
|
||||
|
||||
|
||||
def generate():
|
||||
# grab global references to the output frame and lock variables
|
||||
global outputFrame, lock
|
||||
|
||||
# loop over frames from the output stream
|
||||
while True:
|
||||
# wait until the lock is acquired
|
||||
with lock:
|
||||
# check if the output frame is available, otherwise skip
|
||||
# the iteration of the loop
|
||||
if outputFrame is None:
|
||||
continue
|
||||
|
||||
# encode the frame in JPEG format
|
||||
(flag, encodedImage) = cv2.imencode(".jpg", outputFrame)
|
||||
|
||||
# ensure the frame was successfully encoded
|
||||
if not flag:
|
||||
continue
|
||||
|
||||
# yield the output frame in the byte format
|
||||
yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' +
|
||||
bytearray(encodedImage) + b'\r\n')
|
||||
|
||||
|
||||
@app.route('/video_feed')
|
||||
def video_feed():
|
||||
"""Video streaming route. Put this in the src attribute of an img tag."""
|
||||
return Response(generate(),mimetype='multipart/x-mixed-replace; boundary=frame')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
t = threading.Thread(target=detect_motion)
|
||||
t.daemon = True
|
||||
t.start()
|
||||
app.run(host='127.0.0.1', port=5000, threaded=True)
|
||||
from picamera2 import MappedArray, Picamera2, Preview
|
||||
from picamera2.previews.qt import QGlPicamera2
|
||||
import numpy as np
|
||||
|
||||
|
||||
def rectFromPoint(center, len, width, axis):
|
||||
rect = ((0, 0), (0, 0))
|
||||
l = int(len/2)
|
||||
w = int(width/2)
|
||||
if(axis == 'x'):
|
||||
rect = ((center[0] - l, center[1] - w), (center[0] + l, center[1] + w))
|
||||
elif(axis == 'y'):
|
||||
rect = ((center[0] - w, center[1] - l), (center[0] + w, center[1] + l))
|
||||
return rect
|
||||
|
||||
|
||||
def rectsFromPoint(center, l1, l2, l3, w, axis):
|
||||
centerFine = center
|
||||
fineInner = rectFromPoint(centerFine, l1, w, axis)
|
||||
fineOuter = rectFromPoint(centerFine, l2, w, axis)
|
||||
centerCoarse = center
|
||||
if(axis == 'x'):
|
||||
centerCoarse = (center[0], center[1] + w)
|
||||
elif(axis == 'y'):
|
||||
centerCoarse = (center[0] + w, center[1])
|
||||
coarse = rectFromPoint(centerCoarse, l3, w, axis)
|
||||
return [fineInner, fineOuter, coarse, center]
|
||||
|
||||
|
||||
def moveROI(event, x, y, flags, params):
|
||||
global roiX, roiY, moving, l1, l2, l3, w, selectedAxis
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
moving = True
|
||||
|
||||
elif event==cv2.EVENT_MOUSEMOVE:
|
||||
if moving==True:
|
||||
if(selectedAxis == 'x'):
|
||||
roiX = rectsFromPoint((x, y), l1, l2, l3, w, selectedAxis)
|
||||
elif(selectedAxis == 'y'):
|
||||
roiY = rectsFromPoint((x, y), l1, l2, l3, w, selectedAxis)
|
||||
|
||||
elif event == cv2.EVENT_LBUTTONUP:
|
||||
moving = False
|
||||
|
||||
|
||||
def crop(frame, rect):
|
||||
return frame[rect[0][1]:rect[1][1], rect[0][0]:rect[1][0]]
|
||||
|
||||
|
||||
def replaceCrop(frame, rect, crop):
|
||||
frame[rect[0][1]:rect[1][1], rect[0][0]:rect[1][0]] = crop
|
||||
|
||||
|
||||
def genDKernel(dVal):
|
||||
return np.ones((dVal, dVal), np.uint8)
|
||||
|
||||
|
||||
def track(frame, roi, dKernel):
|
||||
exp = 2
|
||||
roiFineInner, roiFineOuter, roiCourse, roiCenter = roi
|
||||
|
||||
cropFineOuter = crop(frame, roiFineOuter)
|
||||
cropCoarse = crop(frame, roiCourse)
|
||||
#gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
dilation = cv2.dilate(cropFineOuter, dKernel, iterations=1)
|
||||
ret,thresh = cv2.threshold(dilation,100,255,cv2.THRESH_BINARY)
|
||||
|
||||
replaceCrop(frame, roiFineOuter, thresh)
|
||||
|
||||
# this could potentially be made more efficient by cropping from cropFineOuter
|
||||
cropFineInner = crop(frame, roiFineInner)
|
||||
|
||||
meanFine = pow(cropFineInner.mean(), exp)
|
||||
meanCourse = pow(cropCoarse.mean(), 1)
|
||||
mean = 0
|
||||
if(meanCourse > 10):
|
||||
mean = meanFine
|
||||
distance = pow(255, exp) - mean
|
||||
|
||||
return distance
|
||||
|
||||
|
||||
def drawRect(frame, points):
|
||||
cv2.rectangle(frame, points[0], points[1], (0, 255, 0))
|
||||
|
||||
|
||||
def drawRoi(frame, roi):
|
||||
for rect in roi[:3]:
|
||||
drawRect(frame, rect)
|
||||
center = roi[3]
|
||||
cv2.line(frame, (center[0] - 5, center[1]), (center[0] + 5, center[1]), (0, 255, 0), 1)
|
||||
cv2.line(frame, (center[0], center[1] - 5), (center[0], center[1] + 5), (0, 255, 0), 1)
|
||||
|
||||
def picameraToCVTrack():
|
||||
global roiX, roiY, moving, l1, l2, l3, w, selectedAxis, dilationKernel, calibrate
|
||||
|
||||
while True:
|
||||
frame = picam2.capture_buffer("lores")
|
||||
frame = frame[:s1 * h1].reshape((h1, s1))
|
||||
#frame = picam2.capture_array("lores")
|
||||
#frame = cv2.cvtColor(frame, cv2.COLOR_GRAY2RGB)
|
||||
|
||||
distanceX = track(frame, roiX, dilationKernel)
|
||||
distanceY = track(frame, roiY, dilationKernel)
|
||||
|
||||
drawRoi(frame, roiX)
|
||||
drawRoi(frame, roiY)
|
||||
|
||||
cv2.putText(frame, "{}: {:.2f}".format("distance x", distanceX), (10, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
cv2.putText(frame, "{}: {:.2f}".format("distance y", distanceY), (10, 200), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 3)
|
||||
|
||||
if calibrate:
|
||||
cv2.imshow("Frame", frame)
|
||||
#cv2.imshow("Process", tresh)
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
key = cv2.waitKey(20)
|
||||
#if key == 32:
|
||||
# cv2.waitKey()
|
||||
if key == ord('+'):
|
||||
dilationVal = dilationVal + 1
|
||||
dilationKernel = genDKernel(dilationVal)
|
||||
elif key == ord('-'):
|
||||
if dilationVal > 0:
|
||||
dilationVal = dilationVal - 1
|
||||
dilationKernel = genDKernel(dilationVal)
|
||||
elif key == ord('x'):
|
||||
selectedAxis = 'x'
|
||||
elif key == ord('y'):
|
||||
selectedAxis = 'y'
|
||||
elif key == ord('c'):
|
||||
if calibrate:
|
||||
calibrate = False
|
||||
cv2.destroyAllWindows()
|
||||
else:
|
||||
print("hello")
|
||||
calibrate = True
|
||||
cv2.startWindowThread()
|
||||
#elif key == ord('q'):
|
||||
# break
|
||||
|
||||
|
||||
class TrackerThread(QThread):
|
||||
def __init__(self, target=None):
|
||||
super().__init__()
|
||||
self.target = target
|
||||
|
||||
def run(self):
|
||||
if self.target:
|
||||
self.target()
|
||||
|
||||
|
||||
class MainWindow(QGlPicamera2):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MainWindow, self).__init__(*args, **kwargs)
|
||||
trackerThread = TrackerThread(target=picameraToCVTrack)
|
||||
trackerThread.start()
|
||||
self.shortcut_close_window = QShortcut(QKeySequence('f'), self)
|
||||
self.shortcut_close_window.activated.connect(self.goFullscreen)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.move(0, 0)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
self.oldPos = event.globalPos()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
delta = QPoint (event.globalPos() - self.oldPos)
|
||||
self.move(self.x() + delta.x(), self.y() + delta.y())
|
||||
self.oldPos = event.globalPos()
|
||||
|
||||
def goFullscreen(self):
|
||||
if self.isFullScreen():
|
||||
#self.setWindowFlags(self._flags)
|
||||
self.showNormal()
|
||||
else:
|
||||
#self._flags = self.windowFlags()
|
||||
#self.setWindowFlags(Qt.WindowCloseButtonHint | Qt.WindowType_Mask)
|
||||
self.showFullScreen()
|
||||
|
||||
picam2 = Picamera2()
|
||||
#picam2.start_preview(Preview.QTGL)
|
||||
#max resolution is (4056, 3040) which is more like 10 fps
|
||||
config = picam2.create_preview_configuration(main={"size": (2028, 1520)}, lores={"size": (1920, 1440), "format": "YUV420"})
|
||||
picam2.configure(config)
|
||||
|
||||
app = QApplication([])
|
||||
qpicamera2 = MainWindow(picam2, width=1920, height=1440, keep_ar=False)
|
||||
qpicamera2.setWindowTitle("Qt Picamera2 App")
|
||||
|
||||
selectedAxis = 'x'
|
||||
moving = False
|
||||
l1 = 100
|
||||
l2 = 300
|
||||
l3 = 40
|
||||
w = 20
|
||||
roiXCenter = (960, 195)
|
||||
roiYCenter = (615, 530)
|
||||
roiX = rectsFromPoint(roiXCenter, l1, l2, l3, w, 'x')
|
||||
roiY = rectsFromPoint(roiYCenter, l1, l2, l3, w, 'y')
|
||||
dilationVal = 75
|
||||
dilationKernel = genDKernel(dilationVal)
|
||||
calibrate = True
|
||||
|
||||
cv2.startWindowThread()
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", moveROI)
|
||||
|
||||
(w0, h0) = picam2.stream_configuration("main")["size"]
|
||||
(w1, h1) = picam2.stream_configuration("lores")["size"]
|
||||
s1 = picam2.stream_configuration("lores")["stride"]
|
||||
|
||||
picam2.start()
|
||||
qpicamera2.show()
|
||||
app.exec()
|
||||
|
@ -1,113 +0,0 @@
|
||||
# importing libraries
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
drawing = False
|
||||
point1 = ()
|
||||
point2 = ()
|
||||
|
||||
def mouse_drawing(event, x, y, flags, params):
|
||||
global point1, point2, drawing
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if drawing is False:
|
||||
drawing = True
|
||||
point1 = (x, y)
|
||||
else:
|
||||
drawing = False
|
||||
|
||||
elif event == cv2.EVENT_MOUSEMOVE:
|
||||
if drawing is True:
|
||||
point2 = (x, y)
|
||||
|
||||
#xFine = (848, 187, 225, 21.0)
|
||||
#yFine = (604, 402, 20.5, 276)
|
||||
|
||||
xFine = (848, 187, 848 + 225, 187 + 21.0)
|
||||
yFine = (604, 402, 604 + 20.5, 402 + 276)
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
def track(frame, ROI, centroid, update):
|
||||
if(update):
|
||||
crop = frame[int(ROI[1]):int(ROI[3]), int(ROI[0]):int(ROI[2])]
|
||||
crop = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
|
||||
crop = cv2.GaussianBlur(crop,(7,7),cv2.BORDER_DEFAULT)
|
||||
|
||||
ret,thresh = cv2.threshold(crop, 50, 255, 0)
|
||||
|
||||
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
|
||||
|
||||
for c in contours:
|
||||
# calculate moments for each contour
|
||||
M = cv2.moments(c)
|
||||
|
||||
# calculate x,y coordinate of center
|
||||
if M["m00"] != 0:
|
||||
centroid[0] = int(M["m10"] / M["m00"])
|
||||
centroid[1] = int(M["m01"] / M["m00"])
|
||||
else:
|
||||
centroid[0], centroid[1] = 0, 0
|
||||
#cv2.circle(img, (cX, cY), 5, (255, 255, 255), -1)
|
||||
cv2.circle(frame, (int(ROI[0]) + centroid[0], int(ROI[1]) + centroid[1]), 3, (0, 255, 0), -1)
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", mouse_drawing)
|
||||
|
||||
# Create a VideoCapture object and read from input file
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 10000)
|
||||
|
||||
# Check if camera opened successfully
|
||||
if (cap.isOpened()== False):
|
||||
print("Error opening video file")
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
# Read until video is completed
|
||||
while(cap.isOpened()):
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = cap.read()
|
||||
if ret == True:
|
||||
# Display the resulting frame
|
||||
|
||||
if(frameCountMod == 0):
|
||||
track(frame, xFine, centroidX, True)
|
||||
track(frame, yFine, centroidY, True)
|
||||
xPos = (centroidX[0] / xFine[2]) * 2 - 1
|
||||
yPos = (centroidY[1] / yFine[3]) * 2 - 1
|
||||
else:
|
||||
track(frame, xFine, centroidX, False)
|
||||
track(frame, yFine, centroidY, False)
|
||||
|
||||
frameCountMod = (frameCountMod + 1) % 10
|
||||
|
||||
cv2.rectangle(frame, (int(xFine[0]), int(xFine[1])), (int(xFine[2]),int(xFine[3])), (0, 255, 0))
|
||||
cv2.rectangle(frame, (int(yFine[0]), int(yFine[1])), (int(yFine[2]),int(yFine[3])), (0, 255, 0))
|
||||
|
||||
if point1 and point2:
|
||||
#cv2.rectangle(frame, point1, point2, (0, 255, 0))
|
||||
xFine = (point1[0], point1[1], point2[0], point2[1])
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
if cv2.waitKey(25) & 0xFF == ord('q'):
|
||||
break
|
||||
|
||||
# Break the loop
|
||||
else:
|
||||
break
|
||||
|
||||
# When everything done, release
|
||||
# the video capture object
|
||||
cap.release()
|
||||
|
||||
# Closes all the frames
|
||||
cv2.destroyAllWindows()
|
@ -1,110 +0,0 @@
|
||||
# importing libraries
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
drawing = False
|
||||
point1 = ()
|
||||
point2 = ()
|
||||
|
||||
def mouse_drawing(event, x, y, flags, params):
|
||||
global point1, point2, drawing
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if drawing is False:
|
||||
drawing = True
|
||||
point1 = (x, y)
|
||||
else:
|
||||
drawing = False
|
||||
|
||||
elif event == cv2.EVENT_MOUSEMOVE:
|
||||
if drawing is True:
|
||||
point2 = (x, y)
|
||||
|
||||
#xFine = (848, 187, 225, 21.0)
|
||||
#yFine = (604, 402, 20.5, 276)
|
||||
|
||||
xFine = (848, 187, 848 + 225, 187 + 21.0)
|
||||
yFine = (604, 402, 604 + 20.5, 402 + 276)
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
def track(frame, ROI, centroid, update):
|
||||
if(update):
|
||||
crop = frame[int(ROI[1]):int(ROI[3]), int(ROI[0]):int(ROI[2])]
|
||||
crop = cv2.cvtColor(crop, cv2.COLOR_RGB2GRAY)
|
||||
crop = cv2.GaussianBlur(crop,(7,7),cv2.BORDER_DEFAULT)
|
||||
|
||||
#ret, thresh = cv2.threshold(crop, 100, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
|
||||
ret,thresh = cv2.threshold(crop, 50, 255, 0)
|
||||
|
||||
M = cv2.moments(thresh)
|
||||
|
||||
# calculate x,y coordinate of center
|
||||
if M["m00"] != 0:
|
||||
centroid[0] = int(M["m10"] / M["m00"])
|
||||
centroid[1] = int(M["m01"] / M["m00"])
|
||||
#else:
|
||||
# cX, cY = 0, 0
|
||||
#print(cY)
|
||||
cv2.circle(frame, (int(ROI[0]) + centroid[0], int(ROI[1]) + centroid[1]), 3, (0, 255, 0), -1)
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", mouse_drawing)
|
||||
|
||||
# Create a VideoCapture object and read from input file
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 10000)
|
||||
|
||||
# Check if camera opened successfully
|
||||
if (cap.isOpened()== False):
|
||||
print("Error opening video file")
|
||||
|
||||
frameCountMod = 0
|
||||
centroidX = [0, 0]
|
||||
centroidY = [0, 0]
|
||||
|
||||
# Read until video is completed
|
||||
while(cap.isOpened()):
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = cap.read()
|
||||
if ret == True:
|
||||
# Display the resulting frame
|
||||
|
||||
if(frameCountMod == 0):
|
||||
track(frame, xFine, centroidX, True)
|
||||
track(frame, yFine, centroidY, True)
|
||||
xPos = (centroidX[0] / xFine[2]) * 2 - 1
|
||||
yPos = (centroidY[1] / yFine[3]) * 2 - 1
|
||||
else:
|
||||
track(frame, xFine, centroidX, False)
|
||||
track(frame, yFine, centroidY, False)
|
||||
|
||||
frameCountMod = (frameCountMod + 1) % 3
|
||||
|
||||
cv2.rectangle(frame, (int(xFine[0]), int(xFine[1])), (int(xFine[2]),int(xFine[3])), (0, 255, 0))
|
||||
cv2.rectangle(frame, (int(yFine[0]), int(yFine[1])), (int(yFine[2]),int(yFine[3])), (0, 255, 0))
|
||||
|
||||
if point1 and point2:
|
||||
#cv2.rectangle(frame, point1, point2, (0, 255, 0))
|
||||
xFine = (point1[0], point1[1], point2[0], point2[1])
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
if cv2.waitKey(25) & 0xFF == ord('q'):
|
||||
break
|
||||
|
||||
# Break the loop
|
||||
else:
|
||||
break
|
||||
|
||||
# When everything done, release
|
||||
# the video capture object
|
||||
cap.release()
|
||||
|
||||
# Closes all the frames
|
||||
cv2.destroyAllWindows()
|
@ -1,60 +0,0 @@
|
||||
# importing libraries
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
drawing = False
|
||||
point1 = ()
|
||||
point2 = ()
|
||||
|
||||
def mouse_drawing(event, x, y, flags, params):
|
||||
global point1, point2, drawing
|
||||
if event == cv2.EVENT_LBUTTONDOWN:
|
||||
if drawing is False:
|
||||
drawing = True
|
||||
point1 = (x, y)
|
||||
else:
|
||||
drawing = False
|
||||
|
||||
elif event == cv2.EVENT_MOUSEMOVE:
|
||||
if drawing is True:
|
||||
point2 = (x, y)
|
||||
|
||||
cv2.namedWindow("Frame")
|
||||
cv2.setMouseCallback("Frame", mouse_drawing)
|
||||
|
||||
# Create a VideoCapture object and read from input file
|
||||
cap = cv2.VideoCapture("/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/recs/a_history_of_the_domino_problem_final_documentation_hq.mp4")
|
||||
|
||||
# Check if camera opened successfully
|
||||
if (cap.isOpened()== False):
|
||||
print("Error opening video file")
|
||||
|
||||
# Read until video is completed
|
||||
while(cap.isOpened()):
|
||||
|
||||
# Capture frame-by-frame
|
||||
ret, frame = cap.read()
|
||||
if ret == True:
|
||||
# Display the resulting frame
|
||||
|
||||
if point1 and point2:
|
||||
cv2.rectangle(frame, point1, point2, (0, 255, 0))
|
||||
|
||||
cv2.imshow("Frame", frame)
|
||||
|
||||
|
||||
# Press Q on keyboard to exit
|
||||
if cv2.waitKey(25) & 0xFF == ord('q'):
|
||||
break
|
||||
|
||||
# Break the loop
|
||||
else:
|
||||
break
|
||||
|
||||
# When everything done, release
|
||||
# the video capture object
|
||||
cap.release()
|
||||
|
||||
# Closes all the frames
|
||||
cv2.destroyAllWindows()
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,175 @@
|
||||
\version "2.24.1"
|
||||
|
||||
genStaff =
|
||||
#(define-music-function (parser location part)
|
||||
(string?)
|
||||
(let * ((file (string-append "includes/ammann_part_" part ".ly")))
|
||||
#{
|
||||
\new Staff \with {
|
||||
instrumentName = #part
|
||||
shortInstrumentName = #part
|
||||
}
|
||||
<<
|
||||
\include #file
|
||||
>>
|
||||
#}))
|
||||
|
||||
\paper {
|
||||
#(set-paper-size "a4" 'portrait)
|
||||
top-margin = 1 \cm
|
||||
bottom-margin = 1 \cm
|
||||
left-margin = 1.75 \cm
|
||||
|
||||
top-system-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
last-bottom-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
systems-per-page = 4
|
||||
|
||||
print-page-number = ##t
|
||||
oddHeaderMarkup = \markup { \unless \on-first-page {"(ammann)"}}
|
||||
evenHeaderMarkup = \markup { \unless \on-first-page {"(ammann)"}}
|
||||
oddFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
evenFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
}
|
||||
|
||||
\header {
|
||||
title = \markup { \italic {ammann}}
|
||||
subtitle = \markup { \normal-text { from \italic{a history of the domino the problem}}}
|
||||
composer = \markup \right-column {"michael winter" "(schloss solitude, stuttgart and calle monclova 62, mexico city; 2018-19)"}
|
||||
tagline = ""
|
||||
}
|
||||
|
||||
#(set-global-staff-size 11)
|
||||
|
||||
\layout {
|
||||
indent = 0.0\cm
|
||||
line-width = 17\cm
|
||||
%ragged-last = ##t
|
||||
|
||||
\context {
|
||||
\Score
|
||||
\override BarNumber.extra-offset = #'(0 . 4)
|
||||
\override BarNumber.stencil = #(make-stencil-circler 0.1 0.25 ly:text-interface::print)
|
||||
\override RehearsalMark.direction = #DOWN
|
||||
rehearsalMarkFormatter = #format-mark-box-numbers
|
||||
}
|
||||
\context {
|
||||
\Staff
|
||||
\override VerticalAxisGroup.staff-staff-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
\override TimeSignature.font-size = #2
|
||||
\override TimeSignature.break-align-symbol = #'clef
|
||||
\override TimeSignature.X-offset =
|
||||
#ly:self-alignment-interface::x-aligned-on-self
|
||||
\override TimeSignature.self-alignment-X = #LEFT
|
||||
%\override TimeSignature.after-line-breaking =
|
||||
% #shift-right-at-line-begin
|
||||
\override TimeSignature.Y-offset = #11
|
||||
\override TimeSignature.extra-offset = #'(2 . 0)
|
||||
|
||||
}
|
||||
|
||||
\context {
|
||||
\StaffGroup
|
||||
\name "SemiStaffGroup"
|
||||
\consists "Span_bar_engraver"
|
||||
\override SpanBar.stencil =
|
||||
#(lambda (grob)
|
||||
(if (string=? (ly:grob-property grob 'glyph-name) "|")
|
||||
(set! (ly:grob-property grob 'glyph-name) ""))
|
||||
(ly:span-bar::print grob))
|
||||
}
|
||||
\context {
|
||||
\Score
|
||||
\accepts SemiStaffGroup
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
\score {
|
||||
%showLastLength = R1*128
|
||||
\new Score
|
||||
%\with{ proportionalNotationDuration = #(ly:make-moment 1 16) }
|
||||
<<
|
||||
\new SemiStaffGroup {
|
||||
<<
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"4"
|
||||
shortInstrumentName = #"4"
|
||||
midiInstrument = #"clarinet"
|
||||
}
|
||||
<<
|
||||
\transpose c c'
|
||||
\include "includes/ammann_part_4.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"3"
|
||||
shortInstrumentName = #"3"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\clef bass
|
||||
\transpose c c,
|
||||
\include "includes/ammann_part_5.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"2"
|
||||
shortInstrumentName = #"2"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/ammann_part_6.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"1"
|
||||
shortInstrumentName = #"1"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\clef bass
|
||||
\transpose c c,,
|
||||
\include "includes/ammann_part_7.ly"
|
||||
>>
|
||||
|
||||
%\genStaff #"0"
|
||||
%\genStaff #"1"
|
||||
%\genStaff #"2"
|
||||
%\genStaff #"3"
|
||||
%\genStaff #"4"
|
||||
%\genStaff #"5"
|
||||
>>
|
||||
}
|
||||
>>
|
||||
\layout { }
|
||||
%\midi { }
|
||||
}
|
Binary file not shown.
@ -0,0 +1,193 @@
|
||||
\version "2.24.1"
|
||||
|
||||
genStaff =
|
||||
#(define-music-function (parser location part)
|
||||
(string?)
|
||||
(let * ((file (string-append "includes/ammann_part_" part ".ly")))
|
||||
#{
|
||||
\new Staff \with {
|
||||
instrumentName = #part
|
||||
shortInstrumentName = #part
|
||||
}
|
||||
<<
|
||||
\include #file
|
||||
>>
|
||||
#}))
|
||||
|
||||
\paper {
|
||||
#(set-paper-size "a4" 'portrait)
|
||||
top-margin = 1 \cm
|
||||
bottom-margin = 1 \cm
|
||||
left-margin = 1.75 \cm
|
||||
|
||||
top-system-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
last-bottom-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
systems-per-page = 3
|
||||
|
||||
print-page-number = ##t
|
||||
oddHeaderMarkup = \markup { \unless \on-first-page {"(ammann)"}}
|
||||
evenHeaderMarkup = \markup { \unless \on-first-page {"(ammann)"}}
|
||||
oddFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
evenFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
}
|
||||
|
||||
\header {
|
||||
title = \markup { \italic {ammann}}
|
||||
subtitle = \markup { \normal-text { from \italic{a history of the domino the problem}}}
|
||||
composer = \markup \right-column {"michael winter" "(schloss solitude, stuttgart and calle monclova 62, mexico city; 2018-19)"}
|
||||
tagline = ""
|
||||
}
|
||||
|
||||
#(set-global-staff-size 11)
|
||||
|
||||
\layout {
|
||||
indent = 0.0\cm
|
||||
line-width = 17\cm
|
||||
%ragged-last = ##t
|
||||
|
||||
\context {
|
||||
\Score
|
||||
\override BarNumber.extra-offset = #'(0 . 4)
|
||||
\override BarNumber.stencil = #(make-stencil-circler 0.1 0.25 ly:text-interface::print)
|
||||
\override RehearsalMark.direction = #DOWN
|
||||
rehearsalMarkFormatter = #format-mark-box-numbers
|
||||
}
|
||||
\context {
|
||||
\Staff
|
||||
\override VerticalAxisGroup.staff-staff-spacing =
|
||||
#'((basic-distance . 17 )
|
||||
(minimum-distance . 17 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
\override TimeSignature.font-size = #2
|
||||
\override TimeSignature.break-align-symbol = #'clef
|
||||
\override TimeSignature.X-offset =
|
||||
#ly:self-alignment-interface::x-aligned-on-self
|
||||
\override TimeSignature.self-alignment-X = #LEFT
|
||||
%\override TimeSignature.after-line-breaking =
|
||||
% #shift-right-at-line-begin
|
||||
\override TimeSignature.Y-offset = #9
|
||||
\override TimeSignature.extra-offset = #'(2 . 0)
|
||||
|
||||
}
|
||||
|
||||
\context {
|
||||
\StaffGroup
|
||||
\name "SemiStaffGroup"
|
||||
\consists "Span_bar_engraver"
|
||||
\override SpanBar.stencil =
|
||||
#(lambda (grob)
|
||||
(if (string=? (ly:grob-property grob 'glyph-name) "|")
|
||||
(set! (ly:grob-property grob 'glyph-name) ""))
|
||||
(ly:span-bar::print grob))
|
||||
}
|
||||
\context {
|
||||
\Score
|
||||
\accepts SemiStaffGroup
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
\score {
|
||||
%showLastLength = R1*128
|
||||
\new Score
|
||||
%\with{ proportionalNotationDuration = #(ly:make-moment 1 16) }
|
||||
<<
|
||||
\new SemiStaffGroup {
|
||||
<<
|
||||
\new Staff \with {
|
||||
instrumentName = #"3+4↑"
|
||||
shortInstrumentName = #"3+4↑"
|
||||
midiInstrument = #"clarinet"
|
||||
\consists Merge_rests_engraver
|
||||
}
|
||||
<<
|
||||
\transpose c c
|
||||
\include "includes/ammann_part_4_up.ly"
|
||||
\\
|
||||
\transpose c c,
|
||||
\include "includes/ammann_part_5_up.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"3+4↓"
|
||||
shortInstrumentName = #"3+4↓"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
\consists Merge_rests_engraver
|
||||
}
|
||||
<<
|
||||
\clef bass
|
||||
\transpose c c
|
||||
\include "includes/ammann_part_4_down.ly"
|
||||
\\
|
||||
\clef bass
|
||||
\transpose c c,
|
||||
\include "includes/ammann_part_5_down.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"1+2↑"
|
||||
shortInstrumentName = #"1+2↑"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
\consists Merge_rests_engraver
|
||||
}
|
||||
<<
|
||||
\transpose c c
|
||||
\include "includes/ammann_part_6_up.ly"
|
||||
\\
|
||||
\transpose c c,
|
||||
\include "includes/ammann_part_7_up.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"1+2↓"
|
||||
shortInstrumentName = #"1+2↓"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
\consists Merge_rests_engraver
|
||||
}
|
||||
<<
|
||||
\clef bass
|
||||
\transpose c c
|
||||
\include "includes/ammann_part_6_down.ly"
|
||||
\\
|
||||
\clef bass
|
||||
\transpose c c,
|
||||
\include "includes/ammann_part_7_down.ly"
|
||||
>>
|
||||
|
||||
%\genStaff #"0"
|
||||
%\genStaff #"1"
|
||||
%\genStaff #"2"
|
||||
%\genStaff #"3"
|
||||
%\genStaff #"4"
|
||||
%\genStaff #"5"
|
||||
>>
|
||||
}
|
||||
>>
|
||||
\layout { }
|
||||
%\midi { }
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,208 @@
|
||||
\version "2.24.1"
|
||||
|
||||
genStaff =
|
||||
#(define-music-function (parser location part)
|
||||
(string?)
|
||||
(let * ((file (string-append "includes/berger_part_" part ".ly")))
|
||||
#{
|
||||
\new Staff \with {
|
||||
instrumentName = #part
|
||||
shortInstrumentName = #part
|
||||
}
|
||||
<<
|
||||
\include #file
|
||||
>>
|
||||
#}))
|
||||
|
||||
\paper {
|
||||
#(set-paper-size "a4" 'portrait)
|
||||
top-margin = 1 \cm
|
||||
bottom-margin = 1 \cm
|
||||
left-margin = 1.75 \cm
|
||||
|
||||
top-system-spacing =
|
||||
#'((basic-distance . 25 )
|
||||
(minimum-distance . 25 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
last-bottom-spacing =
|
||||
#'((basic-distance . 15 )
|
||||
(minimum-distance . 15 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
systems-per-page = 3
|
||||
|
||||
print-page-number = ##t
|
||||
oddHeaderMarkup = \markup { \unless \on-first-page {"(berger-knuth)"}}
|
||||
evenHeaderMarkup = \markup { \unless \on-first-page {"(berger-knuth)"}}
|
||||
oddFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
evenFooterMarkup = \markup { \fill-line {
|
||||
\concat {
|
||||
"-"
|
||||
\fontsize #1.5
|
||||
\fromproperty #'page:page-number-string
|
||||
"-"}}}
|
||||
}
|
||||
|
||||
\header {
|
||||
title = \markup { \italic {berger-knuth}}
|
||||
subtitle = \markup { \normal-text { from \italic{a history of the domino the problem}}}
|
||||
composer = \markup \right-column {"michael winter" "(schloss solitude, stuttgart and calle monclova 62, mexico city; 2018-19)"}
|
||||
tagline = ""
|
||||
}
|
||||
|
||||
#(set-global-staff-size 11)
|
||||
|
||||
\layout {
|
||||
indent = 0.0\cm
|
||||
line-width = 17\cm
|
||||
%ragged-last = ##t
|
||||
|
||||
\context {
|
||||
\Score
|
||||
\override BarNumber.extra-offset = #'(0 . 4)
|
||||
\override BarNumber.stencil = #(make-stencil-circler 0.1 0.25 ly:text-interface::print)
|
||||
\override RehearsalMark.direction = #DOWN
|
||||
rehearsalMarkFormatter = #format-mark-box-numbers
|
||||
}
|
||||
\context {
|
||||
\Staff
|
||||
\override VerticalAxisGroup.staff-staff-spacing =
|
||||
#'((basic-distance . 16 )
|
||||
(minimum-distance . 16 )
|
||||
(padding . 0 )
|
||||
(stretchability . 0))
|
||||
|
||||
\override TimeSignature.font-size = #2
|
||||
\override TimeSignature.break-align-symbol = #'clef
|
||||
\override TimeSignature.X-offset =
|
||||
#ly:self-alignment-interface::x-aligned-on-self
|
||||
\override TimeSignature.self-alignment-X = #LEFT
|
||||
%\override TimeSignature.after-line-breaking =
|
||||
% #shift-right-at-line-begin
|
||||
\override TimeSignature.Y-offset = #9
|
||||
\override TimeSignature.extra-offset = #'(2 . 0)
|
||||
|
||||
}
|
||||
|
||||
\context {
|
||||
\StaffGroup
|
||||
\name "SemiStaffGroup"
|
||||
\consists "Span_bar_engraver"
|
||||
\override SpanBar.stencil =
|
||||
#(lambda (grob)
|
||||
(if (string=? (ly:grob-property grob 'glyph-name) "|")
|
||||
(set! (ly:grob-property grob 'glyph-name) ""))
|
||||
(ly:span-bar::print grob))
|
||||
}
|
||||
\context {
|
||||
\Score
|
||||
\accepts SemiStaffGroup
|
||||
}
|
||||
}
|
||||
|
||||
\score{
|
||||
%showLastLength = R1*128
|
||||
\new Score
|
||||
%\with{ proportionalNotationDuration = #(ly:make-moment 1 16) }
|
||||
<<
|
||||
\new SemiStaffGroup {
|
||||
<<
|
||||
\new Staff \with {
|
||||
instrumentName = #"1a"
|
||||
shortInstrumentName = #"1a"
|
||||
midiInstrument = #"flute"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_3.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"2a"
|
||||
shortInstrumentName = #"2a"
|
||||
midiInstrument = #"clarinet"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_2.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"3a"
|
||||
shortInstrumentName = #"3a"
|
||||
midiInstrument = #"viola"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_1.ly"
|
||||
>>
|
||||
>>
|
||||
}
|
||||
\new SemiStaffGroup {
|
||||
<<
|
||||
%{
|
||||
\new Staff \with {
|
||||
instrumentName = #"1b"
|
||||
shortInstrumentName = #"1b"
|
||||
midiInstrument = #"cello"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_7.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"2b"
|
||||
shortInstrumentName = #"2b"
|
||||
midiInstrument = #"saxophone"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_6.ly"
|
||||
>>
|
||||
%}
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"1b + 2b"
|
||||
shortInstrumentName = #"1b + 2b"
|
||||
midiInstrument = #"cello"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\override DynamicLineSpanner.direction = #UP
|
||||
\include "includes/berger_part_7.ly"
|
||||
\\
|
||||
\include "includes/berger_part_6.ly"
|
||||
>>
|
||||
|
||||
\new Staff \with {
|
||||
instrumentName = #"3b"
|
||||
shortInstrumentName = #"3b"
|
||||
midiInstrument = #"bassoon"
|
||||
\remove "Time_signature_engraver"
|
||||
}
|
||||
<<
|
||||
\include "includes/berger_part_5.ly"
|
||||
>>
|
||||
|
||||
|
||||
|
||||
%\genStaff #"0"
|
||||
%\genStaff #"1"
|
||||
%\genStaff #"2"
|
||||
%\genStaff #"3"
|
||||
%\genStaff #"4"
|
||||
%\genStaff #"5"
|
||||
>>
|
||||
}
|
||||
>>
|
||||
%\midi{}
|
||||
\layout{}
|
||||
}
|
Binary file not shown.
@ -0,0 +1 @@
|
||||
jaendel-rao is the only score that remains pegged to lilypond 2.19.81, please use the that version.
|
@ -0,0 +1,300 @@
|
||||
(
|
||||
~ammannTranscribe = {arg seqs;
|
||||
var dir, basePath, subTiling, tileMap, tiles, genTileMap, odds, harms, amps;
|
||||
|
||||
//thisThread.randSeed = 100;
|
||||
|
||||
//dir = thisProcess.nowExecutingPath.dirname;
|
||||
//basePath = dir +/+ ".." +/+ "score" +/+ "lilypond_v2.24_update" +/+ "ammann_distributed";
|
||||
basePath = "/home/mwinter/Portfolio/a_history_of_the_domino_problem/a_history_of_the_domino_problem_source/score/lilypond_v2.24_update/ammann_distributed";
|
||||
|
||||
seqs.do({arg part, p;
|
||||
|
||||
var lilyFile, lilyString, lastNote, lastTimeSig, lastClef, lastDynamic, beatCountTotal;
|
||||
|
||||
//create file
|
||||
lilyFile = File(basePath +/+ "includes" +/+ "ammann_" ++ "part_" ++ p.asString ++ ".ly".standardizePath,"w");
|
||||
|
||||
//start lilypond directives
|
||||
lilyString = "";
|
||||
lilyString = lilyString ++ "{\n\\set tupletFullLength = ##t\n"; //"\\set Score.markFormatter = #format-mark-box-numbers\n";
|
||||
//lilyString = lilyString ++ "{\n";
|
||||
//lilyString = lilyString + "\\tempo 4 = 60 \n";
|
||||
lilyString = lilyString ++ "\n\\tempo \\markup {
|
||||
\\concat {
|
||||
\\smaller \\general-align #Y #DOWN
|
||||
\\note #\"4\" #1 \\normal-text \" ≈ 60\"
|
||||
}}\n";
|
||||
|
||||
lastNote = "r";
|
||||
lastTimeSig = "";
|
||||
lastClef = "\\clef treble ";
|
||||
lastDynamic = "";
|
||||
beatCountTotal = 0;
|
||||
part.size.do({arg nIndex;
|
||||
var dirs, del, dur, sustain, harm, amp, quant, note, beatCount, durUnit, measureDur, timeSig, clef, delayGroup, tuplet, dynamic, octFlag = false;
|
||||
dirs = part[nIndex];
|
||||
del = dirs[0][0];
|
||||
dur = dirs[0][1];
|
||||
sustain = dirs[1, 1]; //not used
|
||||
harm = dirs[2][1];
|
||||
amp = dirs[3][1];
|
||||
quant = dirs[4];
|
||||
|
||||
/*
|
||||
if(amp != 0, {
|
||||
note = ["c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b"][((55 * harm).cpsmidi % 12).trunc.asInteger];
|
||||
note = note ++ [",,", ",", "", "'", "''"][((55 * if(harm >= 8, {harm / 2}, {harm})).cpsmidi / 12).trunc.asInteger - 2];
|
||||
}, {note = "r"});
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if(amp != 0, {
|
||||
note = ["c", "cih", "cis", "deh", "d", "dih", "dis", "eeh", "e", "eih", "f", "fih", "fis", "geh", "g", "gih", "gis", "aeh", "a", "aih", "ais", "beh", "b", "bih"][((55 * harm).cpsmidi % 12).round(0.5) * 2];
|
||||
note = note ++ [",,", ",", "", "'", "''", "'''", "''''"][((55 * if(harm >= 8, {harm /* / 2 */}, {harm})).cpsmidi / 12).trunc.asInteger - 2];
|
||||
}, {note = "r"});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
if((amp == 0) || (harm < 8) , {note = "r"}, {
|
||||
note = ["c", "cih", "cis", "deh", "d", "dih", "dis", "eeh", "e", "eih", "f", "fih", "fis", "geh", "g", "gih", "gis", "aeh", "a", "aih", "ais", "beh", "b", "bih"][((55 * harm).cpsmidi % 12).round(0.5) * 2];
|
||||
//if(p == 4, {(harm + " sdfsdfsd").postln});
|
||||
note = note ++ [",,", ",", "", "'", "''", "'''", "''''"][((55 * if(harm >= 8, {harm /* / 2 */}, {harm})).cpsmidi / 12).trunc.asInteger - 2];
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
//tmp note vals
|
||||
//note = ["c'", "d'", "e'", "f'", "g'", "a'", "b'", "c''", "d''", "e''", "f''", "g''", "a''", "b''", "c'''", "d'''"][harm];
|
||||
|
||||
beatCount = 0;
|
||||
durUnit = 0.25;
|
||||
measureDur = del + dur;
|
||||
|
||||
//put in a tie if there is a delay
|
||||
if((del > 0) && (nIndex > 0) && (lastNote != "r"), {lilyString = lilyString ++ "~ "});
|
||||
if(lilyString.keep(-4) == "} ~ ", {lilyString = lilyString.drop(-4) ++ "~ } "});
|
||||
|
||||
//double bar
|
||||
if(((nIndex % 32) == 0) && (nIndex != 0), {lilyString = lilyString ++ "\\bar \"||\" "});
|
||||
//bar check
|
||||
if(nIndex != 0, {lilyString = lilyString ++ " | " ++ "%{ noteIndex: " ++ nIndex.asString ++ " beat: " ++ beatCountTotal ++ " %} "});
|
||||
//rehearsal mark
|
||||
if((nIndex % 32) == 0, {lilyString = lilyString ++ "\\mark \\default "});
|
||||
|
||||
//set time signature for each measure
|
||||
timeSig = "\\numericTimeSignature \\time " ++ if((measureDur / 0.5 % 2) == 0, {measureDur.trunc.asInteger.asString ++ "/4 "},
|
||||
{(measureDur / 0.5).trunc.asInteger.asString ++ "/8 "});
|
||||
if(lastTimeSig != timeSig, {lilyString = lilyString ++ timeSig}, {lilyString = lilyString ++ "%{" ++ timeSig ++ "%} "});
|
||||
lastTimeSig = timeSig;
|
||||
|
||||
|
||||
tuplet = case
|
||||
{quant == 0.25} {[1/4, " "]}
|
||||
{quant == (1/3)} {[1/6, "3\/2 "]}
|
||||
{quant == (1/5)} {[1/5, "5\/4 "]};
|
||||
|
||||
//put any quarter notes held from the last measure
|
||||
if(del.trunc.asInteger >= 1, {
|
||||
beatCount = beatCount + del.trunc.asInteger;
|
||||
lilyString = lilyString ++ del.trunc.asInteger.collect({arg index; lastNote ++ "4 " ++
|
||||
if(((del.frac / quant) >= 1) && (lastNote != "r"), {"~ "}, {""})}).join
|
||||
});
|
||||
|
||||
//transition from last note to new note
|
||||
if((del.frac / quant) >= 1, {var lastNoteFrac, noteFrac;
|
||||
|
||||
beatCount = beatCount + 1;
|
||||
//only start a tuplet if necessary
|
||||
if(quant != 0.25, {lilyString = lilyString ++ "\\tuplet " ++ tuplet[1] + " { "});
|
||||
//end of last note
|
||||
lastNoteFrac = switch((del.frac / tuplet[0]).round.asInteger, 1, {"16 "}, 2, {"8 "}, 3, {"8. "}, 4, {"4 "});
|
||||
lilyString = lilyString ++ lastNote ++ lastNoteFrac;
|
||||
|
||||
lilyString = lilyString ++ "%{notechange%} ";
|
||||
//put clef if necessary
|
||||
if(amp != 0, {
|
||||
clef = if((55 * harm).cpsmidi >= 57, {"\\clef treble "}, {"\\clef bass "});
|
||||
if(lastClef != clef, {lilyString = lilyString + clef});
|
||||
lastClef = clef;
|
||||
});
|
||||
|
||||
//beginning of new note
|
||||
noteFrac = switch(((if(beatCount > measureDur, {measureDur.frac}, {1}) - del.frac) / tuplet[0]).round.asInteger, 1, {"16 "}, 2, {"8 "}, 3, {"8. "}, 4, {"4 "});
|
||||
|
||||
dynamic = if(amp == 0.06, {"\\mf "}, {"\\p "});
|
||||
|
||||
lilyString = lilyString ++ note ++ noteFrac ++
|
||||
if((note != "r"), {if(harm >= 8, {"upp "}, {"down "})}, {""}) ++
|
||||
if((lastDynamic != dynamic) && (note != "r"), {lastDynamic = dynamic; dynamic}, {""}) ++
|
||||
if((((measureDur - beatCount) / 0.5).trunc.asInteger > 0) && (note != "r"), {"~ "}, {""});
|
||||
|
||||
if(quant != 0.25, {lilyString = lilyString ++ " } "});
|
||||
octFlag = false;
|
||||
}, {
|
||||
|
||||
lilyString = lilyString ++ "%{notechange%} ";
|
||||
|
||||
//put clef if necessary
|
||||
if(amp != 0, {
|
||||
clef = if((55 * harm).cpsmidi >= 57, {"\\clef treble "}, {"\\clef bass "});
|
||||
if(lastClef != clef, {lilyString = lilyString + clef});
|
||||
lastClef = clef;
|
||||
});
|
||||
octFlag = true;
|
||||
|
||||
});
|
||||
|
||||
dynamic = if(amp == 0.06, {"\\mf "}, {"\\p "});
|
||||
lilyString = lilyString ++ (measureDur - beatCount).trunc.asInteger.collect({arg b; beatCount = beatCount + 1; note ++ "4 " ++
|
||||
if(octFlag && (b == 0) && (note != "r"), {if(harm >= 8, {"upp "}, {"down "})}, {""}) ++
|
||||
if((lastDynamic != dynamic) && (note != "r"), {lastDynamic = dynamic; dynamic}, {""}) ++
|
||||
if(((measureDur - beatCount) > 0) && (note != "r"), {"~ "}, {""});
|
||||
}).join;
|
||||
if((measureDur - beatCount) > 0, {
|
||||
lilyString = lilyString ++ ((measureDur - beatCount).frac / 0.5).trunc.asInteger.collect({note ++ "8 "}).join});
|
||||
|
||||
|
||||
beatCountTotal = beatCountTotal + measureDur;
|
||||
lastNote = note
|
||||
|
||||
});
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4").clump(16).do({arg match;
|
||||
if(
|
||||
[
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1],
|
||||
match[10][1] ++ match[11][1],
|
||||
match[14][1] ++ match[15][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "1" ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4").clump(12).do({arg match;
|
||||
if(
|
||||
[
|
||||
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1],
|
||||
match[10][1] ++ match[11][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "2." ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4").clump(8).do({arg match;
|
||||
if(
|
||||
[
|
||||
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "2" ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)8 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)8").clump(8).do({arg match;
|
||||
if(
|
||||
[
|
||||
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "4" ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)8 ").clump(8).do({arg match;
|
||||
if(
|
||||
[
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "4. " ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
lilyString.findRegexp(
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)8 (upp |down |)(\\\\mf |\\\\p |)(~ )*" ++
|
||||
"([a-g]|[a-g]+is|[a-g]+ih|[a-g]+eh|r)(,,|,|'|''|)4 ").clump(8).do({arg match;
|
||||
if(
|
||||
[
|
||||
|
||||
match[1][1] ++ match[2][1],
|
||||
match[6][1] ++ match[7][1]
|
||||
].asSet.size == 1, {lilyString = lilyString.replace(match[0][1], match[1][1] ++ match[2][1] ++ "4. " ++ match[3][1] ++ match[4][1])
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
//rest consolidation for half parts
|
||||
lilyString.findRegexp("\\\\tuplet\\h*\\d/\\d\\h*{\\h*r\\d.?\\h*%{notechange%}\\h*r\\d.?\\h*}").do({arg match;
|
||||
lilyString = lilyString.replace(match[1], "r4")
|
||||
});
|
||||
|
||||
lilyString.findRegexp("\\\\time\\h*\\d/\\d\\h*%?}?\\h*((%{notechange%})?\\h*r\\d.?\\h*)*\\|").do({arg match;
|
||||
if(match[1].contains("time"), {
|
||||
var repString = "", drop = 10, sig = "";
|
||||
case
|
||||
{match[1].contains("time 1/4")} {repString = "r4 | "}
|
||||
{match[1].contains("time 2/4")} {repString = "r2 | "}
|
||||
{match[1].contains("time 3/4")} {repString = "r2. | "}
|
||||
{match[1].contains("time 4/4")} {repString = "r1 | "}
|
||||
{match[1].contains("time 5/4")} {repString = "r2 r2. | "}
|
||||
{match[1].contains("time 6/4")} {repString = "r1. | "}
|
||||
{match[1].contains("time 7/4")} {repString = "r1 r2. | "}
|
||||
{match[1].contains("time 8/4")} {repString = "r1 r1 | "}
|
||||
{match[1].contains("time 9/4")} {repString = "r2. r2. r2. | "}
|
||||
{match[1].contains("time 10/4")} {repString = "r1 r1 r2 | "}
|
||||
//{match[1].contains("time 11/4")} {repString = "r1 r1 r2. | "}
|
||||
{match[1].contains("time 3/8")} {repString = "r4. | "}
|
||||
{match[1].contains("time 5/8")} {repString = "r4. r4 | "}
|
||||
{match[1].contains("time 7/8")} {repString = "r2 r4. | "}
|
||||
{match[1].contains("time 9/8")} {repString = "r4. r4. r4.| "};
|
||||
|
||||
if(match[1].contains("10/4") || match[1].contains("11/4"), {drop = 11});
|
||||
if(match[1].findRegexp("\\d/\\d %}").size > 0, {drop = drop + 3});
|
||||
if(match[1].findRegexp("\\d\\d/\\d %}").size > 0, {drop = drop + 1});
|
||||
|
||||
//if(match[1].contains("11"), {
|
||||
// [match[1], match[1].drop(drop), repString].postln;
|
||||
//});
|
||||
|
||||
lilyString = lilyString.replace(match[1].drop(drop), " \\hideNotes " ++ repString ++ " \\unHideNotes ")
|
||||
|
||||
//sig = match[1].findRegexp("\\d/\\d")[0][1];
|
||||
//sig.postln;
|
||||
//lilyString = lilyString.replace(match[1].drop(drop), (" R1*" ++ sig ++ " | ").postln)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
//lilyString = lilyString.replace("upp ", "^\\markup{\\bold{\\larger{↑}}}").replace("down", "^\\markup{\\bold{\\larger{↓}}}");
|
||||
lilyString = lilyString.replace("upp ", "").replace("down", "");
|
||||
|
||||
lilyString = lilyString.replace("\\note #\"4\" #1 \\normal-text \" ≈ 60\"", "\\note {4} #1 \\normal-text \" ≈ 60\"");
|
||||
|
||||
|
||||
//write file
|
||||
lilyString = lilyString ++ "\\bar \"|.\"\n}";
|
||||
lilyFile.write(lilyString);
|
||||
lilyFile.close;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
~ammannTranscribe.value(~ammannMusic)
|
||||
)
|
Loading…
Reference in New Issue