A dive into the Python code.

dsp2017-1.png

As I’ve written before: loading and displaying model on a isometric game level is a first goal. So let’s have a look into Python scripts loading Blender cubic models and into resultant JSON files structure. The code is here https://github.com/adamskopl/cubicforest/tree/master/cubicLoader.

The code’s structure reflects the structure of the model:

  • cubicLoader.py
  • cubicModel.py
  • cubicCubesGroup.py
  • cubucCube.py

Loader makes an instance of a Model and invokes loadGroups.

"""
cubicModel.py
"""
    def loadGroups(self):
        """
        Fill groups list with CubesGroup objects.
        """
        scene = bpy.data.scenes[0]
        sceneGroups = bpy.data.groups
        for sGroup in sceneGroups:
            groupName = sGroup.name
            groupCubes = sGroup.objects
            cubicGroup = cubicCubesGroup.CubesGroup(groupName)
            cubicGroup.loadCubes(groupCubes)
            self.groups.append(cubicGroup)

Having in mind that maybe I’ll want to divide model into groups (e.g. for simple animation purposed), loading starts with reading them:

sceneGroups = bpy.data.groups

The bpy object is generated by Blender. Next the group loads its cubes and the result is appended to the groups collection.

"""
cubicCubesGroup.py
"""
    def loadCubes(self, groupCubes):
        """
        Fill cubes list with objects from groupCubes list.
        """
        for groupCube in groupCubes:
            cube = cubicCube.Cube(groupCube.name)
            cube.setPos(groupCube.location)
            cube.setRot(groupCube.rotation_euler)
            cube.setCol(groupCube.data.materials[0].name)
            self.cubes.append(cube)

The Cube class is filled with data:

  • position
  • rotation
  • color

The location, rotation_euler and data.materials[0].name are provided by Blender. I’m saving material’s name, which will be later used to indicate the texture needed to render cube.

"""
cubicCube.py
"""
class Cube:
    """
    Class defining model's cube.

    Blender's equivalent: Cube Object.
    """
    def __init__(self, name):
        """ x """
        self.name = name
        self.pos = Vector()
        self.rot = Euler()

    def setPos(self, pos):
        self.pos = pos

    def setRot(self, rot):
        self.rot = rot

    def setCol(self, colName):
        self.colName = colName

Looks simple but it took some time to figure out how to extract needed data from the Blender’s scene.

Next step is to save the model as a JSON file:

"""
cubicLoader.py
"""
    def saveModel(self, fStart, fEnd):
        currentDir=os.path.dirname(os.path.abspath(__file__))
        config = configparser.ConfigParser()
        config.read(currentDir + "/config.ini")
        modelJson = self.model.toJson(fStart, fEnd)
        jsonData = simplejson.dumps(modelJson)
        fd = open(config['Paths']['writePath'] + '/' + self.modelName + ".json", "w")
        fd.write(jsonData)
        fd.close
"""
cubicModel.py
"""
    def toJson(self, fStart, fEnd):
        scene = bpy.data.scenes[0]
        modelDict = dict()
        framesList = list()
        for frameNum in range(fStart, fEnd+1):
            scene.frame_set(frameNum)
            frameDict = dict()
            groupsList = list()
            for frameGroup in self.groups:
                groupDict = frameGroup.toJson()
                groupsList.append(groupDict)
            frameDict["number"] = frameNum
            frameDict["groups"] = groupsList
            framesList.append(frameDict)
        modelDict["name"] = self.name
        modelDict["frames"] = framesList
        return modelDict
"""
cubicCubesGroup.python
"""
    def toJson(self):
        groupDict = dict()
        cubesList = list()
        for c in self.cubes:
            cubeDict = c.toJson()
            cubesList.append(cubeDict)
        groupDict["name"] = self.name
        groupDict["cubes"] = cubesList
        return groupDict
"""
cubicCube.py
"""
    def toJson(self):
        cubeDict = dict()

        posDict=dict()
        posDict["x"] = self.pos.x
        posDict["y"] = self.pos.y
        posDict["z"] = self.pos.z

        rotDict=dict()
        rotDict["x"] = self.rot.x
        rotDict["y"] = self.rot.y
        rotDict["z"] = self.rot.z

        cubeDict["name"] = self.name
        cubeDict["pos"] = posDict
        cubeDict["rot"] = rotDict
        cubeDict["colName"] = self.colName
        return cubeDict

Cubes are converted to JSON, added to the groups, which are also converted to JSON and passed to the model. Every part has its own toJson function.

Here’s the result file with data describing a model:

  • named ‘prize’
  • composed of 3x3x3=27 cubes
  • one frame, one group
  • all cubes are ‘red’
{"name": "prize", "frames": [{"number": 1, "groups": [{"name":
"Group", "cubes": [{"name": "Cube.027", "colName": "red", "pos": {"z":
7.0, "x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}},
{"name": "Cube.026", "colName": "red", "pos": {"z": 7.0, "x": 1.0,
"y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.025", "colName": "red", "pos": {"z": 7.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.024",
"colName": "red", "pos": {"z": 6.0, "x": 0.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.023", "colName": "gray",
"pos": {"z": 6.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.022", "colName": "red", "pos": {"z": 6.0,
"x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.021", "colName": "red", "pos": {"z": 6.0, "x": 2.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.020",
"colName": "red", "pos": {"z": 6.0, "x": -2.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.019", "colName": "red",
"pos": {"z": 5.0, "x": -2.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.018", "colName": "red", "pos": {"z": 4.0,
"x": -2.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.016", "colName": "red", "pos": {"z": 5.0, "x": 2.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.015",
"colName": "red", "pos": {"z": 4.0, "x": 2.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.013", "colName": "red",
"pos": {"z": 3.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.012", "colName": "red", "pos": {"z": 3.0,
"x": -1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.011", "colName": "red", "pos": {"z": 4.0, "x": -1.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.010",
"colName": "red", "pos": {"z": 5.0, "x": -1.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.009", "colName": "red",
"pos": {"z": 4.0, "x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.008", "colName": "red", "pos": {"z": 5.0,
"x": 1.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.007", "colName": "brown", "pos": {"z": 2.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.006",
"colName": "red", "pos": {"z": 3.0, "x": 0.0, "y": 0.0}, "rot": {"z":
0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.005", "colName": "red",
"pos": {"z": 5.0, "x": 0.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0,
"y": 0.0}}, {"name": "Cube.004", "colName": "red", "pos": {"z": 4.0,
"x": 0.0, "y": 0.0}, "rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name":
"Cube.003", "colName": "brown", "pos": {"z": 0.0, "x": 0.0, "y": 0.0},
"rot": {"z": 0.0, "x": 0.0, "y": 0.0}}, {"name": "Cube.002",
"colName": "brown", "pos": {"z": 1.0, "x": 0.0, "y": 0.0}, "rot":
{"z": 0.0, "x": 0.0, "y": 0.0}}]}]}]}

written in Emacs with org2blog mode

Cubicforest JS setup

dsp2017-1.png

I’ve spent some time lately to prepare JS environment for the Cubicforest project:

  • Editor (Webstorm)
  • Eslint (plus Webstorm)
  • Babel (plus Webstorm)

It’s not much, but before starting the development, I’ve had some problems with the setup. Some of them were fixed in the way I can’t remember, so the exact steps of setup will not be shown.

Without much experience with linting tools (which check Javascript code for e.g. syntax errors) I chose ESlint. The next step was the configuration. eslint-config-airbn choice made https://github.com/airbnb/javascript. Setup steps with npm are listed here https://www.npmjs.com/package/eslint-config-airbnb, everything went smooth.

Having some experience with Webstorm, I’m declaring it as the best IDE for JS development. Previously I was working with Brackets and Sublime. I even have spent some time with Emacs configuration, which gave pretty satisfying results, but in the end Webstorm is the clear winner.

Coming back to the Eslint: Webstorm needs a .eslintrc.js configuration file and path to the binary. After that it shows the errors during the coding. If it comes to airbnb, these are some rules which convinced me to that choice (all of the rules listed here):

  • support for ES6: forcing many ES6 improvements like:
    • using const for all references (let eventually)
    • object method shorthand, property value shorthand
    • default parameter syntax rather than mutating function arguments
    • others..
  • single quotes for strings
  • named function expressions instead of function declarations
  • Never reassign parameters
  • aaaaand many others…

Having linter helps to improve code quality.

Digression about shorthands… really cool example of the issue where ES6 really shines:

const hello = 'hello';

// old way
var obj = {
  hello: hello
};

// es6 Airbnb way
const obj = {
  hello,
};

Notice the trailing comma: it’s another example of the detail which intuitively seems to be a better option.

Next: transpiling ES6 with Babel. Haven’t used yet syntax which would not be understood by Chrome except the modules. Another thing in which ES6 rocks and gives shock about how the things can be simple. Few notes about that: Babel has to choose the way in which ES5 modules will be translated. I’ve chosen AMD + RequierJS. Example how the final code looks like (taken from https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-amd):

from:

export default 42;

to:

define(["exports"], function (exports) {
  "use strict";

  Object.defineProperty(exports, "__esModule", {
    value: true
  });

  exports.default = 42;
});

How to watch files for Babel transpile: again Webstorm comes with help. When .babelrc is properly configured like this:

{
  "presets": ["env", "es2015"],
  "plugins": ["babel-plugin-transform-es2015-modules-amd"]
}

Webstorm launches Babel on every file save and exports results by default into the dist directory. I have lost a lot of time because of the lack of the ‘babel-plugin-transform-es2015-modules-amd’ plugin. Without it Requirejs wasn’t able to load the files.

written in Emacs with org2blog mode

Python paths madness.

dsp2017-1.png

I’ve spent whole evening to launch my models loader Python scripts. Since I didn’t remember anything, I was gladly surprised, that I left usage instructions at the beginning of the file.

""" 
Load and run this script in the Blender Console
e.g.:

import sys
sys.path.append("/path_to_cubicLoader_folder")
import cubicLoader
cubicLoader.run("cubicModel", frameStart, frameEnd)

requirements:
https://github.com/simplejson/simplejson
"""

I opened the source code of pythonLoader.py in Emacs. Copied the path through it:

~/git/cubicforest-java/python/jsonLoader/

And according to the instructions wrote in Blender’s Python console:

import sys
sys.path.append("~/git/cubicforest-java/python/jsonLoader/")
import cubicLoader

Result:

ImportError: No module named 'cubicLoader'

Ok, here it goes. Always something. So I’ve thought that maybe I previously had a different version of Python? Checked. No: the Python version is not guilty.

I started digging into the Python’s documentation about writing modules. I started with a simple ‘fibo’ module example. And it worked. I’ve created directory ‘~/fibo’, changed to it, created fibo.py. In console I’ve written like in my previous instructions:

import sys
sys.path.append.path("~/fibo")
import fibo
fibo.fib(10)

And no errors. The ‘fibo’ module is visible. So I’ve returned to Blender, tried again. Same errors. I’ve opened second Python shell and tried writing outside the Blender shell. Maybe it has some different default paths set. That would explain why fibo example works in normal shell and Blender shell gives errors. So again: I’ve tried exactly the same like like in Blender. Same errors.

Started to check many variations of sys.path.append having a fibo example as a guideline. Still nothing. Why fibo works and my loader not? I’ve changed pythonLoader.py file’s content to the same like in fibo.py. Loaded it in the same way. Still import errors.

I started to fiddle with a fibo example again… and noticed that commenting out sys.path.append.path(“~/fibo”) still results in a valid import. Ok, so more confusion. And then ‘aha’: the fibo.py is in the same directory as the launched Python shell. Import works without sys.append, because I’m in the same folder as fibo.py.

Great. So problem almost solved. I just have to do the same with Blender. Don’t minding why the import worked a year ago. Maybe I had a different Xubuntu installations with some paths set… So I’ve tried to solve the problem by making symbolic links in home directory, so the import will behave like in a fibo example: to make import from the current path.

Something moved forward: but other modules used by cubicLoader started to give import errors. Right: sys.path.append worked then, so now I’ve to make more symbolic links so the other modules could be found.

And then I started another try to make things work like they should. Opened the normal console, launched Python shell, split the console window and moved to the ~/git/cubicforest-java/python/jsonLoader path. Typed ‘pwd’ to get a path. Got /home/adamsko/git/cubicforest-java/python/jsonLoader. In Python shell:

import sys
sys.path.append("/home/adamsko/git/cubicforest-java/python/jsonLoader")
import cubicLoader

Silence. No errors. Confusion. Made a longer break, played http://phoboslab.org/xibalba/ (careful, addictive stuff!) for half an hour. Came back. Spot the mistake.

“~/git/cubicforest-java/python/jsonLoader” “/home/adamsko/git/cubicforest-java/python/jsonLoader”

Wind howling through my head.

Goodnight.

written in Emacs with org2blog mode

The figure-rectangle-quad fetish.

dsp2017-1.png

The inheritance pattern was always like an assumption of the best, one and only solution of creating groups of concepts. In the workplace, on the university, in hobby projects, in books. Because if quad is a rectangle, then it’s afolso a figure. So it’s a perfect candidate for an inheritance design.

IS A, IS A, IS A

One of the guidelines taught on the university and in some books about c++:

  • Q: ‘When should you use composition and when inheritance?’
  • A: ‘When the relationship is more like ‘IT IS’, use inheritance. If the ‘IS COMPOSED FROM’ is more accurate, use composition.’

Whatever project I’ve seen, most of them had an impressive inheritance tree. Sometimes it seemed that multiple level of classes was just for the source code to look smart. In the advanced stage of the project, the whole designed structure was turning out to be flawed. In the result it was bent in an ugly way to fit the new requirements. Not to mention about the journey through all the ancestors to figure out how the youngest class is really behaving.

We have a BehaviorA, BehaviorB and BehaviorC. Then we have a concept, which should behave using A and B, and another concept which should behave using B and C. Inheritance says: make base class with A behavior. And then add two classes: one for B and one for C separately. Both of them should inherit from A. Think about it: what’s the point of that? What if another concept is introduced, which should have A,B,C,D behavior? Why not use a factory function which will know A, B, C, D and then just compose those behaviors into a needed concept? Ok, it’s not so sophisticated as inheritance. But isn’t that just simpler?

Inheritance locks groups of behaviors into permanent concepts. Shouldn’t those behaviors be freed and used as the building blocks?

BEHAVES LIKE, BEHAVES LIKE, BEHAVES LIKE

Recently I was relieved, that what intuitively seems to me as a winning solution was backed in this video:

I don’t see the point of using the inheritance at all. I don’t think it’s a good pattern. I don’t think it should be used.

Also the author of the ‘You Don’t Know Js’ series strongly encourages to not to try to simulate the inheritance in Javascript. Even despite the class keyword being introduced in ES6! The prototype mechanism fits the composition perfectly. As the common behavior can be delegated to another object. Not copied and locked into an inelastic structure.

Another article, this time from Eric Elliot: https://medium.com/@_ericelliott/what-you-were-taught-was-not-prototypal-inheritance-f853ce3db00e#.cgs1zt2di

“Favor composition over class inheritance” – the Gang of Four

‘Composition over inheritance’ and yet the second one seems to reign in majority of the projects.

written in Emacs with org2blog mode