Skip to content

3.2. Blocks

This sections explains how to inspect/add/modify/remove blocks in a SyncWorld

Note

All world modifications are done through a Transaction. A transaction can do multiple insertions/modifications/deletions at once. For performance reasons, always try to group your modifications in as few transactions as possible.

Prerequisites

auto world = newWorld();

List all blocks

world.blocks() is a forward range. You can iterate over it using a range-based for loop:

// Utility function to list blocks.
auto printBlocks = [&world]() -> void {
    auto const& blocks = world.blocks();
    std::cout << "List of blocks (size = " << blocks.size() << "):\n";
    for (auto const& block : blocks) {
        std::cout << "- " << block.index() << ": mass = " << block.mass() << ", isFoundation = " << block.isFoundation() << '\n';
    }
};

auto const& block is a C++ reference to a BlockReference object. Through this object, we can get any public property of the block:

  • its mass: block.mass()
  • its maximum stress block.maxPressureStress()
  • if it's a foundation: block.isFoundation()
  • the structures it belongs to, its contacts, its current stress ratio...

Note

The iteration order is implementation defined. It will probably be different from the order of insertion, or the order of the block indices (like an unordered_map).

Insert blocks

First, let's define a few constants for all the blocks in this tutorial:

auto const maxBlockStress = G::Model::PressureStress{
    100'000.0, // compression (Pascal)
    50'000.0,  // shear (Pascal)
    20'000.0,  // tensile (Pascal)
};
auto const heavyMass = 10'000.0;  // kilogram
auto const lightMass = 1'000.0;   // kilogram

Then we can create a simple tower in the SyncWorld with a Transaction like this:

{
    // Create a new transaction
    auto tr = World::Transaction{};
    // Add a single foundation block at coordinates {0,0,0}
    tr.addBlock({ { 0,0,0 }, maxBlockStress, heavyMass, true });
    // Add 6 non-foundation blocks above the foundation
    for (int i = 1; i <= 6; ++i) {
        tr.addBlock({ { 0,i,0 }, maxBlockStress, lightMass, false });
    }
    // Run the transaction
    world.modify(tr);
}

The key method is World::Transaction::addBlock(BlockConstructionInfo const&). The BlockConstructionInfo has a constructor taking 4 arguments:

  • blockIndex: the position of the block in the world.
  • maxBlockStress: the maximum constraints this block can withstand before failing.
  • mass: the mass of this block.
  • isFoundation: whether this block is a foundation.

Possible output of printBlocks():

List of blocks (size = 7):
- { "x": 0, "y": 0, "z": 0}: mass = 10000, isFoundation = 1
- { "x": 0, "y": 1, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 3, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 2, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 4, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 5, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 6, "z": 0}: mass = 1000, isFoundation = 0

Delete blocks

Again, any world modification goes through a Transaction. To remove blocks, just use Transaction::removeBlock(BlockIndex):

{
    auto tr = World::Transaction{};
    tr.removeBlock({ 0,6,0 });
    tr.removeBlock({ 0,5,0 });
    world.modify(tr);
}

This transaction will remove the 2 blocks at the top of the tower.

Possible output of printBlocks():

List of blocks (size = 5):
- { "x": 0, "y": 0, "z": 0}: mass = 10000, isFoundation = 1
- { "x": 0, "y": 1, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 3, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 2, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 4, "z": 0}: mass = 1000, isFoundation = 0

Modify blocks

To modify a block, remove & add it in the same transaction:

{
    auto tr = World::Transaction{};
    tr.removeBlock({ 0,4,0 });
    tr.addBlock({ {0,4,0}, maxBlockStress, heavyMass, false });
    world.modify(tr);
}

This transaction replaced the block at the top of the tower with a block of mass heavyMass.

Possible output of printBlocks():

List of blocks (size = 5):
- { "x": 0, "y": 0, "z": 0}: mass = 10000, isFoundation = 1
- { "x": 0, "y": 1, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 3, "z": 0}: mass = 1000, isFoundation = 0
- { "x": 0, "y": 4, "z": 0}: mass = 10000, isFoundation = 0
- { "x": 0, "y": 2, "z": 0}: mass = 1000, isFoundation = 0

Inspect a block

There are two ways to get a BlockReference by index:

  • world.blocks().at(BlockIndex): returns a BlockReference only if the block exists, throws otherwise.
  • world.blocks().find(BlockIndex): always returns a BlockReference that must be tested for validity (block.isValid()) before use.

The following example shows how to safely inspect a block using find():

auto inspectBlock = [&world](World::BlockIndex const& blockId) -> void {
    std::cout << "Block at " << blockId << ": ";
    auto const blockRef = world.blocks().find(blockId);
    if (blockRef.isValid()) {
        std::cout << "mass = " << blockRef.mass() << ", isFoundation = " << blockRef.isFoundation();
    } else {
        std::cout << "invalid";
    }
    std::cout << '\n';
};
inspectBlock({ 0,0,0 });
inspectBlock({ 0,1,0 });
inspectBlock({ 9,9,9 });

Expected output:

Block at { "x": 0, "y": 0, "z": 0}: mass =  10000, isFoundation = 1
Block at { "x": 0, "y": 1, "z": 0}: mass =  1000, isFoundation = 0
Block at { "x": 9, "y": 9, "z": 9}: invalid