From cea9fba46ccb0a82277f9fc7b5f652fa91ef7076 Mon Sep 17 00:00:00 2001 From: Vincenzo Sicurella Date: Mon, 21 Aug 2023 18:15:27 -0400 Subject: [PATCH] add adjust layout colour helper class --- Source/Main.cpp | 6 +- Source/Main.h | 2 +- Source/color/adjust_layout_colour.cpp | 230 +++++++++++++++++++++++ Source/color/adjust_layout_colour.h | 74 ++++++++ Source/data/lumatone_layout.cpp | 14 ++ Source/data/lumatone_layout.h | 4 + Source/hex/hex_field.cpp | 254 ++++++++++++++++++++++++++ Source/hex/hex_field.h | 95 ++++++++++ Source/hex/lumatone_hex_map.cpp | 137 ++++++++++++++ Source/hex/lumatone_hex_map.h | 72 ++++++++ TerpstraSysEx.jucer | 12 ++ 11 files changed, 897 insertions(+), 3 deletions(-) create mode 100644 Source/color/adjust_layout_colour.cpp create mode 100644 Source/color/adjust_layout_colour.h create mode 100644 Source/hex/hex_field.cpp create mode 100644 Source/hex/hex_field.h create mode 100644 Source/hex/lumatone_hex_map.cpp create mode 100644 Source/hex/lumatone_hex_map.h diff --git a/Source/Main.cpp b/Source/Main.cpp index df9de9d7..d6ec47b6 100644 --- a/Source/Main.cpp +++ b/Source/Main.cpp @@ -586,11 +586,13 @@ bool TerpstraSysExApplication::canPasteSubBoardData() const return false; } -bool TerpstraSysExApplication::performUndoableAction(UndoableAction* editAction) +bool TerpstraSysExApplication::performUndoableAction(UndoableAction* editAction, bool newTransaction) { if (editAction != nullptr) { - undoManager.beginNewTransaction(); + if (newTransaction) + undoManager.beginNewTransaction(); + if (undoManager.perform(editAction)) // UndoManager will check for nullptr and also for disposing of the object { setHasChangesToSave(true); diff --git a/Source/Main.h b/Source/Main.h index 4946a239..45e7236b 100644 --- a/Source/Main.h +++ b/Source/Main.h @@ -91,7 +91,7 @@ class TerpstraSysExApplication : public JUCEApplication void setCalibrationMode(bool calibrationStarted) { inCalibrationMode = calibrationStarted; } bool getInCalibrationMode() const { return inCalibrationMode; } - bool performUndoableAction(UndoableAction* editAction); + bool performUndoableAction(UndoableAction* editAction, bool newTransaction=true); bool undo(); bool redo(); diff --git a/Source/color/adjust_layout_colour.cpp b/Source/color/adjust_layout_colour.cpp new file mode 100644 index 00000000..50544c5e --- /dev/null +++ b/Source/color/adjust_layout_colour.cpp @@ -0,0 +1,230 @@ +#include "adjust_layout_colour.h" +#include "../Main.h" + +AdjustLayoutColour::AdjustLayoutColour() + : hexMap(*TerpstraSysExApplication::getApp().getMappingData()) +{ + endAction(); +} + +AdjustLayoutColour::~AdjustLayoutColour() +{ +} + +void AdjustLayoutColour::replaceColour(juce::Colour oldColour, juce::Colour newColour, bool sendUpdate) +{ + auto keyCoords = layoutBeforeAdjust.getKeysWithColour(oldColour); + juce::Array keyUpdates; + + for (auto coord : keyCoords) + { + auto key = ¤tLayout.getBoard(coord.boardIndex)->theKeys[coord.keyIndex]; + keyUpdates.add(MappedLumatoneKey(key->withColour(newColour), coord)); + } + + if (sendUpdate) + sendSelectionUpdate(keyUpdates); +} + +void AdjustLayoutColour::rotateHue(float change, bool sendUpdate) +{ + auto coords = currentLayout.getAllKeyCoords(); + rotateHue(change, coords, false); + if (sendUpdate) + sendMappingUpdate(currentLayout); +} + +void AdjustLayoutColour::rotateHue(float change, const juce::Array& selection, bool sendUpdate) +{ + if (currentAction != AdjustLayoutColour::Type::ROTATEHUE) + { + beginAction(AdjustLayoutColour::Type::ROTATEHUE); + currentLayout = *TerpstraSysExApplication::getApp().getMappingData(); + } + + juce::Array updateKeys; + for (auto coord : selection) + { + auto key = ¤tLayout.getBoard(coord.boardIndex)->theKeys[coord.keyIndex]; + auto colour = (&layoutBeforeAdjust.getBoard(coord.boardIndex)->theKeys[coord.keyIndex])->colour; + if (colour.isTransparent() + || (colour.getRed() == colour.getGreen() && colour.getRed() == colour.getBlue()) + ) + continue; + + auto rotated = colour.withRotatedHue(change); + key->colour = rotated; + updateKeys.add(MappedLumatoneKey(*key, coord)); + } + + if (sendUpdate) + sendSelectionUpdate(updateKeys); +} + +void AdjustLayoutColour::multiplyBrightness(float change, bool sendUpdate) +{ + auto coords = currentLayout.getAllKeyCoords(); + multiplyBrightness(change, coords, false); + if (sendUpdate) + sendMappingUpdate(currentLayout); +} + +void AdjustLayoutColour::multiplyBrightness(float change, const juce::Array& selection, bool sendUpdate) +{ + if (currentAction != AdjustLayoutColour::Type::ADJUSTBRIGHTNESS) + { + beginAction(AdjustLayoutColour::Type::ADJUSTBRIGHTNESS); + currentLayout = *TerpstraSysExApplication::getApp().getMappingData(); + } + + juce::Array updateKeys; + for (auto coord : selection) + { + auto key = ¤tLayout.getBoard(coord.boardIndex)->theKeys[coord.keyIndex]; + auto colour = (&layoutBeforeAdjust.getBoard(coord.boardIndex)->theKeys[coord.keyIndex])->colour; + if (colour.isTransparent()) + continue; + + auto adjusted = colour.withMultipliedBrightness(change); + key->colour = adjusted; + updateKeys.add(MappedLumatoneKey(*key, coord)); + } + + if (sendUpdate) + sendSelectionUpdate(updateKeys); +} + +void AdjustLayoutColour::setGradient(SetGradientOptions options) +{ + float originColumn = 0; + float originRow = 0; + + float furthestColumn = 0; + float furthestRow = 0; + + juce::Array presentColumns; + juce::Array presentRows; + + juce::Array updateHexCoords; + for (auto coord : options.selection) + { + auto hex = hexMap.keyCoordsToHex(coord); + updateHexCoords.add(hex); + + if (hex.q < originColumn) + originColumn = hex.q; + if (hex.r < originRow) + originRow = hex.r; + + if (hex.q > furthestColumn) + furthestColumn = hex.q; + if (hex.r > furthestRow) + furthestRow = hex.r; + + presentColumns.addIfNotAlreadyThere(hex.q); + presentRows.addIfNotAlreadyThere(hex.r); + } + + presentColumns.sort(); + presentRows.sort(); + + auto selectionOrigin = Hex::Point(originColumn, originRow); + auto furthestPoint = Hex::Point(furthestColumn, furthestRow); + int selectionDistance = furthestPoint.distanceTo(selectionOrigin); + + auto boardOrigin = Hex::Point(0, 0); + int maxBoardDistance = 35; + + juce::Array keyUpdates; + + float maxGradientDistance = 1.0f; + if (options.selectionOrigin) + { + if (options.fillRelative) + maxGradientDistance = presentColumns.size(); + else + maxGradientDistance = selectionDistance; + } + else + { + maxGradientDistance = maxBoardDistance; + } + + float keyGradientDistance = 0.0f; + for (int i = 0; i < options.selection.size(); i++) + { + auto mappedKey = options.selection[i]; + auto hex = updateHexCoords[i]; + + if (options.selectionOrigin) + { + if (options.fillRelative) + { + keyGradientDistance = hex.q - presentColumns[0]; + } + else + { + keyGradientDistance = hex.distanceTo(selectionOrigin); + } + } + else + { + keyGradientDistance = hex.distanceTo(boardOrigin); + } + + float t = (maxGradientDistance == 0.0f) ? 0.0f : keyGradientDistance / maxGradientDistance; + auto colour = options.gradient.getColourAtPosition(t); + auto key = ¤tLayout.getBoard(mappedKey.boardIndex)->theKeys[mappedKey.keyIndex]; + key->colour = colour; + + keyUpdates.add(MappedLumatoneKey(*key, mappedKey.boardIndex, mappedKey.keyIndex)); + } + + sendSelectionUpdate(keyUpdates); +} + +void AdjustLayoutColour::beginAction(AdjustLayoutColour::Type type) +{ + if (type == AdjustLayoutColour::Type::NONE) + return endAction(); + + if (currentAction != type) + { + if (currentAction == AdjustLayoutColour::Type::NONE) + { + layoutBeforeAdjust = *TerpstraSysExApplication::getApp().getMappingData(); + } + } + + currentAction = type; +} + +void AdjustLayoutColour::endAction() +{ + layoutBeforeAdjust = *TerpstraSysExApplication::getApp().getMappingData(); + currentLayout = layoutBeforeAdjust; + currentAction = AdjustLayoutColour::Type::NONE; +} + +void AdjustLayoutColour::commitChanges() +{ + endAction(); +} +void AdjustLayoutColour::resetChanges() +{ + TerpstraSysExApplication::getApp().getLumatoneController()->sendCompleteMapping(layoutBeforeAdjust); + endAction(); +} + +void AdjustLayoutColour::sendSelectionUpdate(const juce::Array& keyUpdates) +{ + auto updateAction = new LumatoneEditAction::MultiKeyAssignAction(TerpstraSysExApplication::getApp().getLumatoneController(), keyUpdates); + + TerpstraSysExApplication::getApp().performUndoableAction(updateAction); +} + +void AdjustLayoutColour::sendMappingUpdate(const LumatoneLayout& updatedLayout) +{ + for (int i = 0; i < TerpstraSysExApplication::getApp().getNumBoards(); i++) + TerpstraSysExApplication::getApp().performUndoableAction(new LumatoneEditAction::SectionEditAction(TerpstraSysExApplication::getApp().getLumatoneController(), i, *updatedLayout.readBoard(i)), i == 0); +} diff --git a/Source/color/adjust_layout_colour.h b/Source/color/adjust_layout_colour.h new file mode 100644 index 00000000..f0c8fc65 --- /dev/null +++ b/Source/color/adjust_layout_colour.h @@ -0,0 +1,74 @@ +#pragma once + +#include "../LumatoneController.h" +#include "../actions/edit_actions.h" +#include "../hex/lumatone_hex_map.h" + +class AdjustLayoutColour +{ + +public: + enum class Type + { + NONE, + FINDREPLACE, + ROTATEHUE, + ADJUSTBRIGHTNESS, + SETGRADIENT + }; + + struct SetGradientOptions + { + const juce::Array& selection; + juce::ColourGradient gradient; + bool selectionOrigin = true; + bool fillRelative = true; + int numColumns = 1; + int numRows = 1; + + SetGradientOptions(const juce::Array& selectionIn, juce::ColourGradient gradientIn, bool selectionOriginIn = true, bool fillRelativeIn=true, int numColumnsIn = 1, int numRowsIn = 1) + : selection(selectionIn) + , gradient(gradientIn) + , selectionOrigin(selectionOriginIn) + , fillRelative(fillRelativeIn) + , numColumns(numColumnsIn) + , numRows(numRowsIn) {} + }; + +public: + + AdjustLayoutColour(); + ~AdjustLayoutColour(); + + void replaceColour(juce::Colour oldColour, juce::Colour newColour, bool sendUpdate=true); + + void rotateHue(float change, bool sendUpdate=true); + void rotateHue(float change, const juce::Array& selection, bool sendUpdate=true); + + void multiplyBrightness(float change, bool sendUpdate=true); + void multiplyBrightness(float change, const juce::Array& selection, bool sendUpdate=true); + + void setGradient(SetGradientOptions options); + + void commitChanges(); + void resetChanges(); + +private: + + void beginAction(AdjustLayoutColour::Type type); + void endAction(); + +private: + + void sendSelectionUpdate(const juce::Array& keyUpdates); + void sendMappingUpdate(const LumatoneLayout& updatedLayout); + +private: + + LumatoneHexMap hexMap; + + LumatoneLayout layoutBeforeAdjust; + LumatoneLayout currentLayout; + + AdjustLayoutColour::Type currentAction; +}; diff --git a/Source/data/lumatone_layout.cpp b/Source/data/lumatone_layout.cpp index 26d9a02e..314f6886 100644 --- a/Source/data/lumatone_layout.cpp +++ b/Source/data/lumatone_layout.cpp @@ -552,3 +552,17 @@ juce::Array LumatoneLayout::getKeysWithColour(const juce::Colo return keyCoords; } + +juce::Array LumatoneLayout::getAllKeyCoords() const +{ + juce::Array coords; + for (int boardIndex = 0; boardIndex < getNumBoards(); boardIndex++) + { + for (int keyIndex = 0; keyIndex < getOctaveBoardSize(); keyIndex++) + { + coords.add(LumatoneKeyCoord(boardIndex, keyIndex)); + } + } + + return coords; +} diff --git a/Source/data/lumatone_layout.h b/Source/data/lumatone_layout.h index 3d20b74f..ed60c357 100644 --- a/Source/data/lumatone_layout.h +++ b/Source/data/lumatone_layout.h @@ -87,6 +87,10 @@ class LumatoneLayout const LumatoneKey* readKey(int boardIndex, int keyIndex) const; +public: + + juce::Array getAllKeyCoords() const; + public: int numBoards = 0; diff --git a/Source/hex/hex_field.cpp b/Source/hex/hex_field.cpp new file mode 100644 index 00000000..1c9fabbe --- /dev/null +++ b/Source/hex/hex_field.cpp @@ -0,0 +1,254 @@ +/* + ============================================================================== + + hex_geometry.cpp + Created: 4 Jun 2023 4:59:13pm + Author: Vincenzo + + ============================================================================== +*/ + +#include "hex_field.h" + +Hex::Cube Hex::Cube::operator-(const Cube& toSubtract) const +{ + return { q - toSubtract.q, r - toSubtract.r, s - toSubtract.s }; +} + +Hex::Cube Hex::Cube::operator+(const Cube& toAdd) const +{ + return { q + toAdd.q, r + toAdd.q, s + toAdd.s }; +} + +float Hex::Cube::distanceTo(const Cube& toPoint) const +{ + auto dif = *this - toPoint; + return (abs(dif.q) + abs(dif.r) + abs(dif.s)) / 2.0f; +} + +void Hex::Cube::round() +{ + int rQ = juce::roundToInt(q); + int rR = juce::roundToInt(r); + int rS = juce::roundToInt(s); + + float dQ = abs(rQ - q); + float dR = abs(rR - r); + float dS = abs(rS = s); + + if (dQ < dR && dQ > dS) + rQ = -rR - rS; + else if (dR > dS) + rR = -rQ - rS; + else + rS = -rQ - rR; + + q = rQ; + r = rR; + s = rS; +} + +Hex::Point::Point(int hex_q, int hex_r) + : q(hex_q), r(hex_r) { } + +Hex::Point::Point(float hex_q, float hex_r) + : q(hex_q), r(hex_r) { } + +Hex::Point::Point(Hex::Cube cube) : q(cube.q), r(cube.r) { } + +float Hex::Point::line(int a, int b, float t) +{ + return a + (b - a) * t; +} + +float Hex::Point::distance(Hex::Point a, Hex::Point b) +{ + return ( abs(a.q - b.q) + + abs(a.q + a.r - b.q - b.r) + + abs(a.r - b.r) + ) / 2.0f; +} + +Hex::Cube Hex::Point::axialToCube(int q, int r) +{ + return { (float)q, (float)r, (float)(-q - r) +}; + +}Hex::Cube Hex::Point::axialToCube(float q, float r) +{ + return { q, r, -q - r }; +} + +Hex::Cube Hex::Point::cubeLinearInterpolation(Cube a, Cube b, float t) +{ + return { + line(a.q, b.q, t), + line(a.r, b.r, t), + line(a.s, b.s, t) + }; +} + +Hex::Cube Hex::Point::cubeLineDraw(Hex::Cube a, Hex::Cube b, int i, int N) +{ + if (N == 0) + N = (int)a.distanceTo(b); + + if (N == 0) + return a; + + float t = i / N; + return cubeLinearInterpolation(a, b, t); +} + +Hex::Point Hex::Point::rounded(Hex::Point point) +{ + auto cubed = point.toCube(); + cubed.round(); + return Hex::Point(cubed.q, cubed.r); +} + +Hex::Cube Hex::Point::toCube() const +{ + return axialToCube(q, r); +} + +void Hex::Point::round() +{ + auto cubed = toCube(); + cubed.round(); + + q = cubed.q; + r = cubed.r; +} + +bool Hex::Point::operator==(const Hex::Point& compare) const +{ + return q == compare.q && r == compare.r; +} + +Hex::Point Hex::Point::operator+(const Hex::Point& toAdd) const +{ + return Hex::Point(q + toAdd.q, r + toAdd.r); +} + +void Hex::Point::operator+=(const Hex::Point & toAdd) +{ + q += toAdd.q; + r += toAdd.r; +} + +Hex::Point Hex::Point::operator-(const Hex::Point & toSubtract) const +{ + return Hex::Point(q - toSubtract.q, r - toSubtract.r); +} + +void Hex::Point::operator-=(const Hex::Point & toSubtract) +{ + q -= toSubtract.q; + r -= toSubtract.r; +} + +int Hex::Point::distanceTo(Hex::Point toPoint) const +{ + return Hex::Point::distance(*this, toPoint); +} + +void Hex::Point::transpose(int delta_q, int delta_r) +{ + q += delta_q; + r += delta_r; +} + +void Hex::Point::transposeQ(int delta_q) +{ + q += delta_q; +} + +void Hex::Point::transposeR(int delta_r) +{ + r += delta_r; +} + +Hex::Point Hex::Point::stepUpQ(int steps) +{ + return Hex::Point(q + steps, r); +} + +Hex::Point Hex::Point::stepDownQ(int steps) +{ + return Hex::Point(q - steps, r); +} + +Hex::Point Hex::Point::stepUpR(int steps) +{ + return Hex::Point(q, r + steps); +} + +Hex::Point Hex::Point::stepDownR(int steps) +{ + return Hex::Point(q, r - steps); +} + +Hex::Point Hex::Point::stepUpQUpR(int steps_q, int steps_r) +{ + return Hex::Point(q + steps_q, r + steps_r); +} + +Hex::Point Hex::Point::stepUpQDownR(int steps_q, int steps_r) +{ + return Hex::Point(q + steps_q, r - steps_r); +} + +Hex::Point Hex::Point::stepDownQUpR(int steps_q, int steps_r) +{ + return Hex::Point(q - steps_q, r + steps_r); +} + +Hex::Point Hex::Point::stepDownQDownR(int steps_q, int steps_r) +{ + return Hex::Point(q - steps_q, r - steps_r); +} + +juce::Array Hex::Point::ring(int distance) const +{ + juce::Array points; + + if (distance > 0) for (int d = 0; d <= distance; d++) + { + points.add(Hex::Point(q + distance, r - distance + d)); + points.add(Hex::Point(q - d, r + distance)); + points.add(Hex::Point(q - distance + d, r - d)); + } + + return points; +} + +juce::Array Hex::Point::neighbors(int maxDistance) const +{ + juce::Array points; + + for (int d = 1; d <= maxDistance; d++) + { + points.addArray(ring(d)); + } + + return points; +} + +juce::Array Hex::Point::lineTo(Hex::Point endPoint) +{ + juce::Array points; + + auto cubeA = toCube(); + auto cubeB = endPoint.toCube(); + + int N = (int)cubeA.distanceTo(cubeB); + for (int i = 0; i <= N; N++) + { + auto cube = cubeLineDraw(cubeA, cubeB, i, N); + points.add(Hex::Point(cube)); + } + + return points; +} + diff --git a/Source/hex/hex_field.h b/Source/hex/hex_field.h new file mode 100644 index 00000000..26a693a8 --- /dev/null +++ b/Source/hex/hex_field.h @@ -0,0 +1,95 @@ +/* + ============================================================================== + + hex_geometry.h + Created: 4 Jun 2023 4:59:13pm + Author: Vincenzo + + ============================================================================== +*/ + +#pragma once +#include + +namespace Hex +{ + struct Cube + { + float q = 0; + float r = 0; + float s = 0; + + Hex::Cube operator-(const Cube& toSubtract) const; + Hex::Cube operator+(const Cube& toAdd) const; + + float distanceTo(const Cube& toPoint) const; + + void round(); + }; + + class Point + { + public: + float q = 0; + float r = 0; + + private: + + static float line(int a, int b, float t); + + public: + + static float distance(Hex::Point a, Hex::Point b); + + static Cube axialToCube(int q, int r); + static Cube axialToCube(float q, float r); + + static Cube cubeLinearInterpolation(Cube a, Cube b, float t); + static Cube cubeLineDraw(Cube a, Cube b, int i, int N = 0); + + static Hex::Point rounded(Hex::Point point); + + public: + + Point() {} + Point(int hex_q, int hex_r); + Point(float hex_q, float hex_r); + Point(Hex::Cube cube); + + juce::String toString() const { return juce::String(q) + "," + juce::String(r); } + + bool operator==(const Hex::Point& compare) const; + + Hex::Point operator+(const Hex::Point& toAdd) const; + void operator+=(const Hex::Point& toAdd); + + Hex::Point operator-(const Hex::Point& toSubtract) const; + void operator-=(const Hex::Point& toSubtract); + + Cube toCube() const; + void round(); + + int distanceTo(Hex::Point toPoint) const; + + void transpose(int delta_q, int delta_r); + void transposeQ(int delta_q); + void transposeR(int delta_r); + + Hex::Point stepUpQ(int steps = 1); + Hex::Point stepDownQ(int steps = 1); + + Hex::Point stepUpR(int steps = 1); + Hex::Point stepDownR(int steps = 1); + + Hex::Point stepUpQUpR(int steps_q = 1, int steps_r = 1); + Hex::Point stepUpQDownR(int steps_q = 1, int steps_r = 1); + + Hex::Point stepDownQUpR(int steps_q = 1, int steps_r = 1); + Hex::Point stepDownQDownR(int steps_q = 1, int steps_r = 1); + + juce::Array ring(int distance = 1) const; + juce::Array neighbors(int maxDistance = 1) const; + + juce::Array lineTo(Hex::Point endPoint); + }; +} \ No newline at end of file diff --git a/Source/hex/lumatone_hex_map.cpp b/Source/hex/lumatone_hex_map.cpp new file mode 100644 index 00000000..2fb00bbf --- /dev/null +++ b/Source/hex/lumatone_hex_map.cpp @@ -0,0 +1,137 @@ +/* + ============================================================================== + + lumatone_hex_map.cpp + Created: 31 Jul 2023 1:39:39am + Author: Vincenzo + + ============================================================================== +*/ + +#include "lumatone_hex_map.h" + +LumatoneHexMap::LumatoneHexMap(const LumatoneLayout& layoutIn, Hex::Point originPointIn, int originBoardIndexIn, int originKeyIndexIn) + : layout(layoutIn) + , originPoint(originPointIn) + , originBoardIndex(originBoardIndexIn) + , originKeyIndex(originKeyIndexIn) +{ + map.reset(new juce::HashMap(280)); + renderMap(); +} + +LumatoneHexMap::~LumatoneHexMap() +{ + map = nullptr; +} + +LumatoneKeyCoord LumatoneHexMap::hexToKeyCoords(Hex::Point point) const +{ + auto key = point.toString(); + return (*map)[key].key; +} + +Hex::Point LumatoneHexMap::keyCoordsToHex(int boardIndex, int keyIndex) const +{ + return boards[boardIndex].keys[keyIndex].point; +} + +Hex::Point LumatoneHexMap::keyCoordsToHex(const LumatoneKeyCoord& keyCoord) const +{ + return keyCoordsToHex(keyCoord.boardIndex, keyCoord.keyIndex); +} + +Hex::Point LumatoneHexMap::addBoardIndex(Hex::Point point, int numIndexes) +{ + return Hex::Point(point.q + numIndexes * 7, point.r - numIndexes * 5); +} + +void LumatoneHexMap::renderMap() +{ + map->clear(); + //boards.clear(); + + for (int i = 0; i < layout.getNumBoards(); i++) + //boards.add(MapBoard(layout.getOctaveBoardSize())); + boards[i] = MapBoard(layout.getOctaveBoardSize()); + + // use lumatoneGeometry.getVerticalOriginLineIndexForRow to find zeroth key from origin + + int originRowIndex = 0; + int lineToZeroDiff = 0; + + for (int row = 0; row < lumatoneGeometry.horizontalLineCount(); row++) + { + if (originKeyIndex < lumatoneGeometry.getLastIndexForRow(row)) + { + originRowIndex = row; + lineToZeroDiff = originKeyIndex - lumatoneGeometry.getVerticalOriginLineIndexForRow(row); + break; + } + } + + Hex::Point zeroKeyPoint = addBoardIndex( + Hex::Point(originPoint.q + lineToZeroDiff, originPoint.r - originRowIndex), + -originBoardIndex); + + int lineIndex = 0; + int keyIndex = 0; + int rowKeyIndex = 0; + + for (int row = 0; row < lumatoneGeometry.horizontalLineCount(); row++) + { + Hex::Point rowPoint = zeroKeyPoint; + + switch (row) + { + case 0: + rowPoint = zeroKeyPoint; + break; + + case 2: + case 4: + case 6: + case 8: + rowPoint = zeroKeyPoint + Hex::Point(-row, row * 2); + break; + + case 1: + case 3: + case 5: + case 7: + rowPoint = zeroKeyPoint + Hex::Point(-row - 1, row * 2 + 1); + break; + + case 9: + rowPoint = zeroKeyPoint + Hex::Point(-1, 10); + break; + + case 10: + rowPoint = zeroKeyPoint + Hex::Point(-1, 10); + break; + } + + int rowLastKey = lumatoneGeometry.getLastIndexForRow(lineIndex); + while (keyIndex <= rowLastKey) + { + for (int boardIndex = 0; boardIndex < layout.getNumBoards(); boardIndex++) + { + auto hexCoord = addBoardIndex(rowPoint + Hex::Point(rowKeyIndex, 0), boardIndex); + + LumatoneKeyCoord keyCoord(boardIndex, keyIndex); + + MappedKey mappedKey = { keyCoord, hexCoord }; + //boards.getReference(boardIndex).keys[keyIndex] = mappedKey; + boards[boardIndex].keys[keyIndex] = mappedKey; + + auto hexHash = hexCoord.toString(); + map->set(hexHash, mappedKey); + } + keyIndex++; + rowKeyIndex++; + } + + lineIndex++; + rowKeyIndex = 0; + } +} diff --git a/Source/hex/lumatone_hex_map.h b/Source/hex/lumatone_hex_map.h new file mode 100644 index 00000000..5b1f2387 --- /dev/null +++ b/Source/hex/lumatone_hex_map.h @@ -0,0 +1,72 @@ +/* + ============================================================================== + + lumatone_hex_map.h + Created: 31 Jul 2023 1:39:39am + Author: Vincenzo + + ============================================================================== +*/ + +#pragma once +#include "./hex_field.h" +#include "../lumatone_geometry.h" +#include "../data/lumatone_layout.h" + +class LumatoneHexMap +{ +public: + + struct MappedKey + { + LumatoneKeyCoord key; + Hex::Point point { 0, 0 }; + }; + + struct MapBoard + { + int size = 0; + MappedKey keys[MAXBOARDSIZE]; + + MapBoard(int octaveBoardSize = 0) + { + size = octaveBoardSize; + for (int i = 0; i < octaveBoardSize; i++) + keys[i] = MappedKey(); + } + }; + +public: + LumatoneHexMap(const LumatoneLayout& layout, + Hex::Point originPoint = Hex::Point(0, 0), + int originBoardIndex = 0, + int originKeyIndex = 0); + + ~LumatoneHexMap(); + + LumatoneKeyCoord hexToKeyCoords(Hex::Point point) const; + + Hex::Point keyCoordsToHex(int boardIndex, int keyIndex) const; + Hex::Point keyCoordsToHex(const LumatoneKeyCoord& keyCoord) const; + +private: + + std::unique_ptr> map; + + void renderMap(); + + Hex::Point addBoardIndex(Hex::Point point, int numIndexes); + +private: + + LumatoneLayout layout; + + LumatoneGeometry lumatoneGeometry; + + //juce::Array boards; + MapBoard boards[MAXNUMBOARDS]; + + Hex::Point originPoint; + int originBoardIndex = 0; + int originKeyIndex = 0; +}; \ No newline at end of file diff --git a/TerpstraSysEx.jucer b/TerpstraSysEx.jucer index 6cab7a0c..67e27292 100644 --- a/TerpstraSysEx.jucer +++ b/TerpstraSysEx.jucer @@ -70,6 +70,10 @@ file="Source/actions/lumatone_action.h"/> + + @@ -98,6 +102,14 @@ + + + + + +