NetAddress touchDesignerAddress;
String inputText = ""; // Stores the user input
String placeholderText = "Input Text"; // Placeholder text for input
int[] outputValues = new int[4]; // Array to store the randomized outputs
String[] labels = {"Angel: ", "Hero: ", "Feminist: ", "Rebel: "}; // Labels for outputs
int inputBoxX = 50, inputBoxY = 100, inputBoxWidth = 400, inputBoxHeight = 150; // Increased height of input box
int outputBoxY = 280; // Y position of the output boxes
boolean showCursor = true;
int cursorPosition = 0; // Position of cursor within inputText
int textOffset = 10; // X offset for text start inside input box
int maxChars = 280; // Maximum characters allowed
String maxCharsMessage = "Max Ch: " + maxChars; // Message to display below input field
boolean ctrlPressed = false;
boolean shiftPressed = false;
boolean textSelected = false; // Flag to track if text is selected
oscP5 = new OscP5(this, 12000);
touchDesignerAddress = new NetAddress("127.0.0.1", 9000);
textFont(createFont("Helvetica", 18));
background(245, 245, 245);
text("Florence Nightingale - Living Portrait", 20, 50);
rect(inputBoxX, inputBoxY, inputBoxWidth, inputBoxHeight, 10);
// Text rendering in input box
if (inputText.equals("") && !focused) {
text(placeholderText, inputBoxX + textOffset, inputBoxY + inputBoxHeight / 2);
String[] lines = inputText.split("\n"); // Split text into lines
int y = inputBoxY + 20; // Start drawing text from this y position
for (String line : lines) {
text(line, inputBoxX + textOffset, y);
y += textAscent() + textDescent(); // Increment y by text height
// Display max characters message
text(maxCharsMessage, inputBoxX + textOffset, inputBoxY + inputBoxHeight + 20);
// Output boxes and labels
for (int i = 0; i < 4; i++) {
int boxY = outputBoxY + i * 60;
rect(inputBoxX, boxY, inputBoxWidth, 40, 10);
text(labels[i] + outputValues[i], inputBoxX + 10, boxY + 25);
// Cursor blink management
showCursor = !showCursor;
if (focused && showCursor) {
float cursorX = inputBoxX + textOffset + textWidth(inputText.substring(0, cursorPosition));
float cursorY = inputBoxY + inputBoxHeight / 2;
line(cursorX, cursorY - 10, cursorX, cursorY + 10);
if (mouseX > inputBoxX && mouseX < inputBoxX + inputBoxWidth &&
mouseY > inputBoxY && mouseY < inputBoxY + inputBoxHeight) {
// User clicked inside the text box, set focus
// Calculate cursor position based on the click
float clickX = mouseX - (inputBoxX + textOffset); // X position within the text box
float textWidthUpToCursor = 0;
int newPosition = 0; // Default to start of the text
// Iterate through each character to find where the click occurred
for (int i = 0; i <= inputText.length(); i++) {
// Update the text width up to the cursor after each character
textWidthUpToCursor += textWidth(inputText.charAt(i - 1));
// Check if the accumulated width surpasses the click position
if (textWidthUpToCursor > clickX) {
// If the click is beyond the last character, set the cursor at the end
if (i == inputText.length()) {
newPosition = inputText.length();
cursorPosition = newPosition; // Update the cursor position
// Reset text selection if clicked inside the text box but not on the selected text
// Click outside the text box, remove focus
if (!focused) return; // Only process key presses when focused on the input box
if (keyCode == CONTROL) ctrlPressed = true;
if (keyCode == SHIFT) shiftPressed = true;
if (ctrlPressed && key == 'a') {
// Ctrl + A: Select all text
cursorPosition = inputText.length();
} else if (shiftPressed && (key == CODED && keyCode == LEFT || keyCode == RIGHT) && !textSelected) {
// Shift + Left/Right Arrow: Extend selection one character to the left or right
if (keyCode == LEFT && cursorPosition > 0) cursorPosition--;
if (keyCode == RIGHT && cursorPosition < inputText.length()) cursorPosition++;
} else if (keyCode == ENTER || keyCode == RETURN) {
// Process Enter key to generate random outputs and send OSC message
inputText = ""; // Clear the input text after sending
cursorPosition = 0; // Reset cursor position
} else if (keyCode == BACKSPACE) {
// Process Backspace key to delete character before the cursor
if (cursorPosition > 0 && inputText.length() > 0) {
inputText = inputText.substring(0, cursorPosition - 1) + inputText.substring(cursorPosition);
} else if (keyCode == DELETE) {
// Process Delete key to delete character at the cursor
if (cursorPosition < inputText.length()) {
inputText = inputText.substring(0, cursorPosition) + inputText.substring(cursorPosition + 1);
} else if (keyCode == LEFT) {
// Process Left arrow key to move cursor left
if (cursorPosition > 0) cursorPosition--;
} else if (keyCode == RIGHT) {
// Process Right arrow key to move cursor right
if (cursorPosition < inputText.length()) cursorPosition++;
} else if (keyCode >= 32 && keyCode <= 126 && inputText.length() < maxChars) {
// Process printable characters if not exceeding maxChars
inputText = inputText.substring(0, cursorPosition) + key + inputText.substring(cursorPosition);
if (keyCode == CONTROL) ctrlPressed = false;
if (keyCode == SHIFT) shiftPressed = false;
void generateRandomOutputs() {
int baseSeed = inputText.length() + inputText.chars().sum();
randomSeed(baseSeed); // Resetting random seed with the base seed
random(0, 101); // Discard the first result
for (int i = 0; i < outputValues.length; i++) {
outputValues[i] = int(random(0, 101)); // Generate random numbers between 0 and 100
if (outputValues.length == 4) {
OscMessage msg = new OscMessage("/randomValues");
for (int value : outputValues) {
oscP5.send(msg, touchDesignerAddress);
println("Sent OSC message with random values: " + outputValues[0] + ", " + outputValues[1] + ", " + outputValues[2] + ", " + outputValues[3]);
println("Error: Incorrect number of output values. Expected 4 values.");