From a210d0bc4feccc4684356f2e67d96869ce075649 Mon Sep 17 00:00:00 2001 From: jules Date: Fri, 22 Jun 2018 14:01:06 +0100 Subject: [PATCH] BLOCKS: Added topological position and rotation information to the Block class --- .../juce_blocks_basics/blocks/juce_Block.h | 11 ++ .../topology/juce_PhysicalTopologySource.cpp | 144 ++++++++++++++++++ 2 files changed, 155 insertions(+) diff --git a/modules/juce_blocks_basics/blocks/juce_Block.h b/modules/juce_blocks_basics/blocks/juce_Block.h index f0c7bc6b31..4bc0c3cb2e 100644 --- a/modules/juce_blocks_basics/blocks/juce_Block.h +++ b/modules/juce_blocks_basics/blocks/juce_Block.h @@ -108,6 +108,9 @@ public: */ virtual bool isMasterBlock() const = 0; + /** Returns the UID of the master block this block is connected to. */ + virtual UID getConnectedMasterUID() const = 0; + //============================================================================== /** Returns the width of the device in logical device units. */ virtual int getWidth() const = 0; @@ -121,6 +124,14 @@ public: /** Returns the length of one logical device unit as physical millimeters. */ virtual float getMillimetersPerUnit() const = 0; + /** Returns the area that this block covers within the layout of the group as a whole. + The coordinates are in logical block units, and are relative to the origin, which is the master block's top-left corner. + */ + virtual Rectangle getBlockAreaWithinLayout() const = 0; + + /** Returns the rotation of this block relative to the master block in 90 degree steps clockwise. */ + virtual int getRotation() const = 0; + //============================================================================== /** If this block has a grid of LEDs, this will return an object to control it. Note that the pointer that is returned belongs to this object, and the caller must diff --git a/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp b/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp index 520fb00ff3..6fd31b79be 100644 --- a/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp +++ b/modules/juce_blocks_basics/topology/juce_PhysicalTopologySource.cpp @@ -1275,6 +1275,9 @@ struct PhysicalTopologySource::Internal { lastTopology = detector->currentTopology; + BlocksTraverser traverser; + traverser.traverseBlockArray (detector->currentTopology); + for (auto* d : detector->activeTopologySources) d->listeners.call ([] (TopologySource::Listener& l) { l.topologyChanged(); }); @@ -1295,6 +1298,131 @@ struct PhysicalTopologySource::Internal JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Detector) }; + //============================================================================== + /** This is a friend of the BlocksImplementation that will scan and set the + physical positions of the blocks */ + struct BlocksTraverser + { + void traverseBlockArray (const BlockTopology& topology) + { + juce::Array visited; + + for (auto& block : topology.blocks) + { + if (block->isMasterBlock() && ! visited.contains (block->uid)) + { + if (auto* bi = dynamic_cast (block)) + { + bi->masterUID = {}; + bi->position = {}; + bi->rotation = 0; + } + + layoutNeighbours (block, topology, block->uid, visited); + } + } + } + + Block::Ptr findBlockWithUid (const BlockTopology& topology, Block::UID uid) + { + for (auto& block : topology.blocks) + if (block->uid == uid) + return block; + + return {}; + } + + // returns the distance from corner clockwise + int getUnitForIndex (Block::Ptr block, Block::ConnectionPort::DeviceEdge edge, int index) + { + if (block->getType() == Block::seaboardBlock) + { + if (edge == Block::ConnectionPort::DeviceEdge::north) + { + if (index == 0) return 1; + if (index == 1) return 4; + } + else if (edge == Block::ConnectionPort::DeviceEdge::east + || edge == Block::ConnectionPort::DeviceEdge::west) + { + return 1; + } + } + + if (edge == Block::ConnectionPort::DeviceEdge::south) + return block->getWidth() - (index + 1); + + if (edge == Block::ConnectionPort::DeviceEdge::west) + return block->getHeight() - (index + 1); + + return index; + } + + // returns how often north needs to rotate by 90 degrees + int getRotationForEdge (Block::ConnectionPort::DeviceEdge edge) + { + switch (edge) + { + case Block::ConnectionPort::DeviceEdge::north: return 0; + case Block::ConnectionPort::DeviceEdge::east: return 1; + case Block::ConnectionPort::DeviceEdge::south: return 2; + case Block::ConnectionPort::DeviceEdge::west: return 3; + } + } + + void layoutNeighbours (Block::Ptr block, const BlockTopology& topology, + Block::UID masterUid, juce::Array& visited) + { + visited.add (block->uid); + + for (auto& connection : topology.connections) + { + if ((connection.device1 == block->uid && ! visited.contains (connection.device2)) + || (connection.device2 == block->uid && ! visited.contains (connection.device1))) + { + const auto theirUid = connection.device1 == block->uid ? connection.device2 : connection.device1; + const auto neighbourPtr = findBlockWithUid (topology, theirUid); + + if (auto* neighbour = dynamic_cast (neighbourPtr.get())) + { + const auto myBounds = block->getBlockAreaWithinLayout(); + const auto& myPort = connection.device1 == block->uid ? connection.connectionPortOnDevice1 : connection.connectionPortOnDevice2; + const auto& theirPort = connection.device1 == block->uid ? connection.connectionPortOnDevice2 : connection.connectionPortOnDevice1; + const auto myOffset = getUnitForIndex (block, myPort.edge, myPort.index); + const auto theirOffset = getUnitForIndex (neighbourPtr, theirPort.edge, theirPort.index); + + neighbour->rotation = (2 + block->getRotation() + + getRotationForEdge (myPort.edge) + - getRotationForEdge (theirPort.edge)) % 4; + + Point delta; + const auto theirBounds = neighbour->getBlockAreaWithinLayout(); + + switch ((block->getRotation() + getRotationForEdge (myPort.edge)) % 4) + { + case 0: // over me + delta = { myOffset - (theirBounds.getWidth() - (theirOffset + 1)), -theirBounds.getHeight() }; + break; + case 1: // right of me + delta = { myBounds.getWidth(), myOffset - (theirBounds.getHeight() - (theirOffset + 1)) }; + break; + case 2: // under me + delta = { (myBounds.getWidth() - (myOffset + 1)) - theirOffset, myBounds.getHeight() }; + break; + case 3: // left of me + delta = { -theirBounds.getWidth(), (myBounds.getHeight() - (myOffset + 1)) - theirOffset }; + break; + } + + neighbour->position = myBounds.getPosition() + delta; + } + + layoutNeighbours (neighbourPtr, topology, masterUid, visited); + } + } + } + }; + //============================================================================== struct BlockImplementation : public Block, private MIDIDeviceConnection::Listener, @@ -1315,6 +1443,7 @@ struct PhysicalTopologySource::Internal touchSurface.reset (new TouchSurfaceImplementation (*this)); int i = 0; + for (auto&& b : modelData.buttons) controlButtons.add (new ControlButtonImplementation (*this, i++, b)); @@ -1371,6 +1500,16 @@ struct PhysicalTopologySource::Internal juce::Array getPorts() const override { return modelData.ports; } bool isConnected() const override { return isStillConnected && detector.isConnected (uid); } bool isMasterBlock() const override { return isMaster; } + Block::UID getConnectedMasterUID() const override { return masterUID; } + int getRotation() const override { return rotation; } + + Rectangle getBlockAreaWithinLayout() const override + { + if (rotation % 2 == 0) + return { position.getX(), position.getY(), modelData.widthUnits, modelData.heightUnits }; + + return { position.getX(), position.getY(), modelData.heightUnits, modelData.widthUnits }; + } TouchSurface* getTouchSurface() const override { return touchSurface.get(); } LEDGrid* getLEDGrid() const override { return ledGrid.get(); } @@ -1876,6 +2015,11 @@ struct PhysicalTopologySource::Internal uint32 resetMessagesSent = 0; bool isStillConnected = true; bool isMaster = false; + Block::UID masterUID = {}; + + Point position; + int rotation = 0; + friend BlocksTraverser; void initialiseDeviceIndexAndConnection() {