This week, I spent time planning a function that could be used to position objects in the room. For example, I might want to place two cubes around a plant, but they should never overlap with the plant or each other. When trying to understand this problem, I found this video by Daniel Shiffman very useful. That combined with pseudocode that Nikolai provided was the key to create some functions.

The pseudocode:

``````for (let i = 0; i < max; i++) {
outer: for (;;) {
let point = make_random_point();
for (let j = 0; j < i; j++) {
if (dist(locations[j], point) < limit) {
continue outer;
}
}
locations.push(point);
break;
}
}
``````

We wanted to create arrays for the possible locations of objects, either around a circle or semicircle. There is a central object with some orbiting objects:

``````const locationsCircle = [];
const locationsHalfCircle = [];

function neighbour_location_circle({ cx, cy, cz, min, max, startAngle = 0, endAngle = 2 * Math.PI }) {
// distance from the object in the center
const distance = min + (max - min) * Math.random();
const angle = startAngle + (endAngle - startAngle) * Math.random();
return [cx + distance * Math.cos(angle), cy, cz + distance * Math.sin(angle)];
}

// how close to each other the orbiting objects can be
let limit = 6;
// number of positions to create
let numOfLoc = 3;

function dist(x1, x2, y1, y2) {
var a = x1 - x2;
var b = y1 - y2;
return Math.sqrt(a * a + b * b);
}
``````

Creating positions for orbiting objects by using `limit` and checking that they don't overlap:

``````for (let i = 0; i < numOfLoc; i++) {
outer: for (;;) {
let point = neighbour_location_circle({
cx: xLoc,
cy: yLoc,
cz: 5,
min: 15,
max: 25
});
for (let j = 0; j < i; j++) {
if (dist(locationsCircle[j], point, locationsCircle[j], point) < limit) {
continue outer;
}
}
locationsCircle.push(point);
break;
}
}

// now we add spheres that orbit the box (as a circle)
for (let i = 0; i < numOfLoc; i++) {
const ballGeometry = new THREE.SphereGeometry(5, 5, 5);
const material = new THREE.MeshPhongMaterial({
color: 0xffffff,
emissive: 0x444444
});
const ball = new THREE.Mesh(ballGeometry, material);
ball.position.x = locationsCircle[i];
ball.position.y = locationsCircle[i];
ball.position.z = locationsCircle[i];
}
``````

For placing the objects in a semicircle (so that they don't end up inside a wall), we modify the start and end angle in the `neighbour_location_circle` function:

``````for (let i = 0; i < numOfLoc; i++) {
outer: for (;;) {
let point = neighbour_location_circle({
cx: 45,
cy: 5,
cz: 5,
min: 15,
max: 25,
startAngle: Math.PI / 2,
endAngle: (3 / 2) * Math.PI
});
for (let j = 0; j < i; j++) {
if (dist(locationsCircle[j], point, locationsCircle[j], point) < limit) {
continue outer;
}
}
locationsHalfCircle.push(point);
break;
}
}
``````

It was also time to make the room visuals a bit more advanced. One of the elements of the room is a big blob that floats in the middle – a bit like a brain inside a head. Luckily, when working on my own art project on my free time, I had bumped into this resource about deformations with Perlin noise.

This animated blob is based on a vertex shader and fragment shader, added with a texture image. It's a good example on how to use normals and materials in deformations. An interesting thing with textures is that they can add a whole other dimension to the visual: you can use any image as a texture, and the shape will then reflect that image, hinting at a space or object that exists outside the viewport.

Finally, I spent time on the Python side of the app, adding a bunch of new variables that are based on the NLP calculations. The values that client side receives are now:

• `avgTone`
• `avgSubj`
• `numOfMe` (number of "I" and "me" words)
• `avgSentenceLength`
• `numOfUnique` (number of unique words)
• `interactionAmount` (number of @ tags)
• `numOfNeg` (number of negative tweets)
• `numOfPos`
• `numOfNeutr`
• `numOfAll`

I always enjoy exploring how easily Python can handle data. For example, I was prepared for requiring multiple lines of code to extract all the @ tags from an account. Finally it was just:

``````interactions = re.findall(r'@[\w\.-]+',str(whole_text_sample))
numOfInteractions = len(interactions)
``````