Skip to content

Concept

Vladimir Elistratov edited this page Aug 7, 2020 · 9 revisions

Repository

The concept is being implemented in the git repository prochitecture/pml.

Basic idea

image

Everything starts from a building Footprint. A number of levels, roof shape and other attributes can be defined for the Footprint:

footprint[condition] {
    numLevels: 5;
    roofShape: gabled;
}

Here and below a domain specific language PML (Prochitecture Markup Language) is used to describe the style of the building. The language is designed to be human-readable. A text file with the domain specific language can be used to share and exchange the building style. Other means are possible to describe the building style. For example XML, visual node-based editor (see below), etc. In each case the text, binary or visual representation of the building style must parsed and a Python structure similar to the one used in the code must be created.

A building volume is generated for the Footprint definition. The building volume contains a number of Facades, RoofSides, Ridges. A style can be defined for them. For example, the front Facade is divided into repeating sections or divisions (Divs):

[Window and Balcony] [Staircase] [Balcony and Window symmemtrically and flipped]

The above Div is repeated 3 times for the example building.

The look of a Facade is defined through the concept of markup. A Facade element can have a markup which can contain a number of Divs or Levels. A Level is the special case of the Div element where its elements are arranged horizontally and the level's index should be taken into account.

footprint[condition] {
    numLevels: attr("building:levels") | random_weighted((4, 10), (5, 40), (6, 10));
    roofShape: gabled;
}

facade@brown_brick {
    claddingMaterial: brick;
    claddingColor: brown;
}

level@staircase {
    offset: 0.5 level;
}

window@back_facade_window {
    width: 1.2;
    height: 1.8;
    panels: 1;
}

window@roof_window{
    width: 0.8;
    height: 0.8;
    rows: 1;
    panels: 1;
}

div@window_and_balcony{
    label: Window and Balcony;
    markup: [
        level{
            markup: [
                window{
                    width: 1.8;
                    height: 2.10;
                    rows: 1;
                    panels: 2;
                }
                balcony{

                }
            ]
        }
        basement{
            markup: [
                window{
                    width: 1;
                    height: 1;
                    rows: 1;
                    panels: 1;
                }
            ]
        }
    ]
}

div@staircase {
    use: staircase;
    label: Staircase;
    markup: [
        level {
            markup: [
                window{
                    width: 0.8;
                    height: 0.8;
                    rows: 1;
                    panels: 1; 
                }
            ]
        }
        level[i=0] {
            markup: [
                door{
                    label: entrance door;
                }
            ]
        }
    ]
}

div@roof_side {
    width: from(main_section);
    symmetry: rightmost-of-last;
}



facade[front] {
    use: brown_brick;
    symmetry: middle-of-last;
    symmetryFlip: true;
    markup: [
        div{
            use: window_and_balcony;
            id: main_section;
            label: Window and Balcony;
        }
        div{
            use: staircase;
            label: Staircase;
        }      
    ]
}

facade[back] {
    use: brown_brick;
    symmetry: rightmost-of-last;
    markup: [
        level {
            balcony{}
            window{
                use: back_facade_window
            }
            window{
                use: back_facade_window;
            }
        }
    ]
}


roof_side[front] {
    markup: [
        div{
            use: roof_side;
            markup: [
                window{
                    use: roof_window;
                }
                window{
                    use: roof_window;
                }
            ]
        }
    ]
}

roof_side[back] {
    markup: [
        div {
            use: roof_side;
            markup: [
                Dormer{}
            ]
        }
    ]
}

ridge {
   markup: [
       div {
           width: from(main_section);
           repeat: no;
           markup: [
               chimney{}
           ]
       }
   ]
}

Reusing style block definitions

The style block

facade@brown_brick {
    claddingMaterial: brick;
    claddingColor: brown;
}

is a reusable style definition under the name brown_brick. It can be referenced by its name in another style block via the attribute use. If a style block is defined using the syntaxis above, it is not used in style calculation in any way, unless it is referenced by name in another style block.

Explicit referencing of other style blocks by name should allow sharing and reusing the styles of building elements, i.e. creating libraries of styles.

It is also to allow better handling resources for rendering (e.g. reusing the same material or 3D geometry).

Style blocks to be referenced by name must be defined at the very top of style hierarchy. The following syntaxis is proposed to define style blocks that can be referenced by name:

element_type@style_block_name {
...
}

Here is how a style block can be referenced by name in another style block:

    use: style_block_name;

Names of style blocks must be comma separated if several of them are to be used. The order of the names is important in that case. A style block referenced by name later, overrides the attributes of the style blocks referenced earlier

    use: style_block_name1, style_block_name2, style_block_name3;

Further notes

The style block

facade[front] {
    use: brown_brick;
    symmetry: middle-of-last;
    symmetryFlip: true;
    markup: [
        div{
            use: window_and_balcony;
            id: main_section;
            label: Window and Balcony;
        }
        div{
            use: staircase;
            label: Staircase;
        }      
    ]
}

defines the appearance of the front facade. A pair of square brackets [ ] after the attributed markup is used to define a markup. Which facade is taken as the front, back, side one, is to be defined. The facade is divided into two Divs with the names Window and Balcony and Staircase. They are totally symmetrical relative to the middle of the Staircase.

Next we define how each Level and the Basement look like.

Only one type of the elements Divs or Levels is allowed within a markup definition. We define the markup for each level as Window and Balcony.

Syntaxis of a style block

A style block can have a condition in the header of the style block. The condition must be surrounded be the square brackets:

footprint[condition] {
    levels: 5;
    roofShape: gabled;
}

The style block is taken into account only if the condition is evaluated to true.

The structure of a style block is shown below:

element[condition] {
    <pairs: attribute: value;>
    markup: [
        <markup definition>
    ]
}

There can 2 parts in each style block. All parts are optional. The order of the parts is not important.

The first part is one or more pairs:

attribute: value;

It resembles CSS. Each pair attribute-value must end with a semicolon.

Finally, a markup can be defined within the square brackets [ ] preceded by the attribute markup.

Markup in the elements Facade, Div, Level define how the related element looks like.

Elements of the type Footprint and Facade are also possible within two square brackets [ ] for the element Footprint. However, they do not define markup. If an element Footprint is located within two square brackets [ ], it defines another Footprint to generate.

The syntaxis

    levels: attr("building:levels") | random_weighted((4, 10), (5, 40), (6, 10));

means: if the attribute building:levels is undefined, then the alternative to the right is used.

Node-based visual editor

An initial code for the editor has been already created. It can be found in the repository blosm-nodes. There is also a list of some tasks and ideas.

Each node has its counterpart in the above domain specific language and in the Python classes.

Each node has the output sockets:

  • markup
  • defs

The markup socket is used to start a markup: connect it with the input socket markup or previous of the first node of the markup definition. Once connected, the input socket changes its label to markup. To add additional nodes to the markup definiton, connect the output socket next of the preceding node in the markup definition with the input socket markup or previous of the subsequent node. Once connected with the output socket next, the input socket markup or previous changes its label to previous.

The nodes connected to the defs sockets correspond to the items located next to the markup definition as described above.

The task is to create the parser for the nodes that can produce a number of outputs:

  • The Python structures similar to the ones from the source code. If both blender-osm and blosm-nodes are installed, a style created and saved in the node editor can be directly read by the blender-osm addon.
  • A text Python file that contains the same Python structures. In this way, a building style can be quickly designed with the node-based visual editor, then exported to a Python file and used by the blender-osm addon without the need to have the blosm-nodes installed.
  • A text file with the domain specific language used in this document (this option is less important at the moment). An importer is also required to create the nodes setup out of a text file with the domain specific language.

Domain specific language: further notes

The ANTLR framework can be used to parse a building style expressed in the domain specific language in Python and other programming languages. See also the notes how to install and use the ANTLR and a tutorial.

Clone this wiki locally