Three Cubes Task Solved
a minimized assignment
the minimized assignment is solve the 3 cubes as 3 squares(1x1) an a plain(4x4).
chatGTP was close but did not finished the code.
Missing the last lines seams to be usual for chatGTP.
ChatGTP can not work with abstract units - a unit is translated into pixels and a 512 times 512 pixels canvas.
That’s basically a light blue 4x4 pixels square in the centre of the canvas.
The interface functions where implemented but not associated with the buttons.
The actions where disabled.
<!DOCTYPE html>
<html>
<head>
<title>Open Squares Generator</title>
<style>
canvas {
/*border: 1px solid black; FIX: i don't like it */
}
#controls {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
button {
padding: 10px;
font-size: 18px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="controls">
<!-- FIX
<button id="restart-btn">Restart</button>
<button id="save-btn">Save</button>
FIX -->
<button id="restart-btn" onclick="restartGenerator()">Restart</button>
<button id="save-btn" onclick="saveArrangement()">Save</button>
</div>
<script>
// FIX src="script.js">
// Get the canvas and the context
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Set the width and height of the canvas
canvas.width = 500;
canvas.height = 500;
// Set the colors for the squares and the base plate
const squareColor = "#FF4136";
const baseColor = "#0074D9";
// Set the base plate dimensions and position
const baseWidth = 4;
const baseHeight = 4;
const baseX = (canvas.width - baseWidth) / 2;
const baseY = (canvas.height - baseHeight) / 2;
// Set the squares dimensions and initial position
const squareSize = 1;
let square1X = 0;
let square1Y = 0;
let square2X = 0;
let square2Y = 0;
let square3X = 0;
let square3Y = 0;
// Restart the generator
function restartGenerator() {
// Set the squares random positions and rotations
square1X = Math.random() * (canvas.width - squareSize);
square1Y = Math.random() * (canvas.height - squareSize);
square2X = Math.random() * (canvas.width - squareSize);
square2Y = Math.random() * (canvas.height - squareSize);
square3X = Math.random() * (canvas.width - squareSize);
square3Y = Math.random() * (canvas.height - squareSize);
const square1Angle = Math.random() * 360;
const square2Angle = Math.random() * 360;
const square3Angle = Math.random() * 360;
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the base plate
ctx.fillStyle = baseColor;
ctx.fillRect(baseX, baseY, baseWidth, baseHeight);
// Draw the squares
drawSquare(square1X, square1Y, squareSize, squareColor, square1Angle);
drawSquare(square2X, square2Y, squareSize, squareColor, square2Angle);
drawSquare(square3X, square3Y, squareSize, squareColor, square3Angle);
}
// Save the current arrangement
function saveArrangement() {
// Open a new window with the canvas image
const dataUrl = canvas.toDataURL();
const newWindow = window.open();
newWindow.document.write(`<img src="${dataUrl}">`);
}
// Draw a square with the specified parameters
function drawSquare(x, y, size, color, angle) {
// ctx.save(); // FIX !
ctx.translate(x + size / 2, y + size / 2);
ctx.rotate((angle * Math.PI) / 180);
ctx.fillStyle = color;
ctx.fillRect(
-size / 2, // FIX adding missing code
-size / 2,
size,
size
);
// ctx.restore(); // FIX !
}
</script>
</body>
</html>
fixing chatGTP code
<!DOCTYPE html>
<html>
<head>
<title>Open Squares Generator</title>
<style>
canvas {
/*border: 1px solid black;*/
}
#controls {
display: flex;
justify-content: space-around;
margin-top: 10px;
}
button {
padding: 10px;
font-size: 18px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<div id="controls">
<button id="restart-btn" onclick="restartGenerator()">Restart</button>
<button id="save-btn" onclick="saveArrangement()">Save</button>
</div>
<script>
// Get the canvas and the context
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
// Set the width and height of the canvas
canvas.width = 500;
canvas.height = 500;
// Set the colors for the squares and the base plate
let squareColor1 = "#" + Math.floor(0xfff * Math.random()).toString(16);
console.log(squareColor1);
let squareColor2 = "#" + Math.floor(0xfff * Math.random()).toString(16);
let squareColor3 = "#" + Math.floor(0xfff * Math.random()).toString(16);
let baseColor = "#" + Math.floor(0xfff * Math.random()).toString(16);
// Set the base plate dimensions and position
const baseWidth = 320;
const baseHeight = 320;
const baseX = (canvas.width - baseWidth) / 2;
const baseY = (canvas.height - baseHeight) / 2;
// Set the squares dimensions and initial position
const squareSize = 80;
let square1X = 0;
let square1Y = 0;
let square2X = 0;
let square2Y = 0;
let square3X = 0;
let square3Y = 0;
//set new colours by random and avoid doublettes
function newColors() {
squareColor1 = "#" + Math.floor(0xfff * Math.random()).toString(16);
squareColor2 = "#" + Math.floor(0xfff * Math.random()).toString(16);
squareColor3 = "#" + Math.floor(0xfff * Math.random()).toString(16);
baseColor = "#" + Math.floor(0xfff * Math.random()).toString(16);
if (
baseColor.length < 4 ||
squareColor1.length < 4 ||
squareColor2.length < 4 ||
squareColor3.length < 4 ||
squareColor1 == baseColor ||
squareColor2 == baseColor ||
squareColor3 == baseColor ||
squareColor1 == squareColor2 ||
squareColor1 == squareColor3 ||
squareColor3 == squareColor2
) {
newColors();
} else {
console.log(squareColor1, squareColor2, squareColor3, baseColor);
}
}
// Restart the generator
function restartGenerator() {
// Set the squares random positions and rotations
square1X = 40 + Math.random() * (canvas.width - 80 - squareSize);
square1Y = 40 + Math.random() * (canvas.height - 80 - squareSize);
square2X = 40 + Math.random() * (canvas.width - 80 - squareSize);
square2Y = 40 + Math.random() * (canvas.height - 80 - squareSize);
square3X = 40 + Math.random() * (canvas.width - 80 - squareSize);
square3Y = 40 + Math.random() * (canvas.height - 80 - squareSize);
newColors();
const square1Angle = Math.random() * 360;
const square2Angle = Math.random() * 360;
const square3Angle = Math.random() * 360;
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the base plate
ctx.fillStyle = baseColor;
ctx.fillRect(baseX, baseY, baseWidth, baseHeight);
// Draw the squares
drawSquare(square1X, square1Y, squareSize, squareColor1, square1Angle);
drawSquare(square2X, square2Y, squareSize, squareColor2, square2Angle);
drawSquare(square3X, square3Y, squareSize, squareColor3, square3Angle);
}
// Save the current arrangement
function saveArrangement() {
// Open a new window with the canvas image
const dataUrl = canvas.toDataURL();
const newWindow = window.open();
newWindow.document.write(`<img src="${dataUrl}">`);
}
// Draw a square with the specified parameters
function drawSquare(x, y, size, color, angle) {
ctx.save();
ctx.translate(x + size / 2, y + size / 2);
ctx.rotate((angle * Math.PI) / 180);
ctx.fillStyle = color;
ctx.fillRect(-size / 2, -size / 2, size, size);
ctx.restore();
}
</script>
</body>
</html>
Which did not helped at all and here is why:
Once 3
coloured
cubes +transparent material
can be rendered in the current version of three.js
50 % of the assignment is fulfilled. The rest is to build the programmatic structure around the “kernel” code
thanks to we all know how use the misleading google
Older tools like stackoverflow, the documentation and examples of three.js, and google still provide helpful information to solve the assignment, though the answers do not fit to the question directly because they often point in a different problem.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(55, 512 / 512, 0.1, 1000);
camera.position.set(0, 1, 4.5);
var renderer = new THREE.WebGLRenderer({
antialias: true,
});
renderer.setSize(512, 512);
var container = document.createElement("div");
document.body.appendChild(container);
container.appendChild(renderer.domElement);
var material1 = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
var material2 = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
var material3 = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
var material4 = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
var material5 = new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
var materialTransparent = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
wireframe: true,
side: THREE.DoubleSide,
});
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
const materials1 = [
material1,
material2,
materialTransparent,
materialTransparent,
material3,
material5,
];
const materials2 = [
material5,
materialTransparent,
material2,
materialTransparent,
material3,
material4,
];
const materials3 = [
materialTransparent,
material2,
material3,
material1,
material4,
material5,
];
const mesh1 = new THREE.Mesh(geometry, materials1);
scene.add(mesh1);
mesh1.position.set(-1.5, 1, -3);
const mesh2 = new THREE.Mesh(geometry, materials2);
scene.add(mesh2);
mesh2.position.set(0, 1, -3);
const mesh3 = new THREE.Mesh(geometry, materials3);
scene.add(mesh3);
mesh3.position.set(1.5, 1, -3);
animate();
function animate() {
requestAnimationFrame(animate);
mesh1.rotation.x += 0.014;
mesh1.rotation.y += 0.013;
mesh2.rotation.x += 0.0125;
mesh2.rotation.y += 0.01;
mesh3.rotation.x += 0.015;
mesh3.rotation.y += 0.02;
renderer.render(scene, camera);
}
</script>
</body>
</html>
the program solution
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
#controls {
display: flex;
flex-flow: column;
justify-content: space-around;
margin-top: 10px;
}
button {
padding: 10px;
font-size: 18px;
width: 30%;
margin: 0 10%;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="controls">
<br />
<button onclick="rotateCamera()">rotate camera</button><br />
<button onclick="save()">save</button><br />
<button onclick="regenerate()">regenerate</button><br />
<br />
</div>
<script>
let scene = new THREE.Scene();
scene.rotation.x = 45;
let camera = new THREE.PerspectiveCamera(75, 512 / 512, 1, 1000);
camera.position.set(0, 1, 4.5);
const renderer = new THREE.WebGLRenderer({
antialias: true,
preserveDrawingBuffer: true,
});
renderer.setSize(512, 512);
const container = document.querySelector("#container");
container.appendChild(renderer.domElement);
const rotateCamera = () => {
if (scene.rotation.y >= 300) {
scene.rotation.y = 0;
} else {
scene.rotation.y += 45;
}
console.log(scene.rotation.y);
animate();
};
const save = async () => {
const dataUrl = renderer.domElement.toDataURL("image/png");
const newWindow = window.open();
newWindow.document.write(`<img src="${dataUrl}">`);
};
const createColorMat = () => {
return new THREE.MeshBasicMaterial({
color: 0xffffff * Math.random(),
side: THREE.DoubleSide,
});
};
var materialTransparent = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0,
wireframe: true,
side: THREE.DoubleSide,
});
const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
const geometryBase = new THREE.BoxBufferGeometry(4, 1, 4);
/**
*
*/
const regenerate = () => {
scene = new THREE.Scene();
scene.rotation.x = 45;
const materialsBase = [
createColorMat(),
createColorMat(),
createColorMat(),
createColorMat(),
createColorMat(),
createColorMat(),
];
const materials1 = () => {
return [
createColorMat(),
createColorMat(),
materialTransparent,
materialTransparent,
createColorMat(),
createColorMat(),
];
};
const materials2 = () => {
return [
createColorMat(),
materialTransparent,
createColorMat(),
materialTransparent,
createColorMat(),
createColorMat(),
];
};
const materials3 = () => {
return [
materialTransparent,
createColorMat(),
createColorMat(),
createColorMat(),
createColorMat(),
createColorMat(),
];
};
const meshBase = new THREE.Mesh(geometryBase, materialsBase);
scene.add(meshBase);
meshBase.position.set(0, 0, -1.2);
const xRot = Math.random() < 0.5;
const yRot = Math.random() < 0.5;
const mesh1 = new THREE.Mesh(
geometry,
Math.random() < 0.6
? materials3()
: Math.random() < 0.6
? materials2()
: materials1()
);
scene.add(mesh1);
mesh1.position.set(
2 - Math.random() * 4,
0.5 + Math.random(),
0.8 - Math.random() * 4
);
// the mesh rotation.x should/is be optional
if (xRot && Math.random() < 0.65) {
mesh1.rotation.x = Math.floor(Math.random() * 360);
}
// the mesh rotation.y should be optional independently from mesh.rotation.x
if (yRot && Math.random() < 0.65) {
mesh1.rotation.y = Math.floor(Math.random() * 360);
}
const mesh2 = new THREE.Mesh(
geometry,
Math.random() < 0.6
? materials3()
: Math.random() < 0.6
? materials2()
: materials1()
);
scene.add(mesh2);
const mbb1 = new THREE.Box3().setFromObject(mesh1);
mesh2.position.set(
2 - Math.random() * 4,
0.5 + Math.random(),
0.8 - Math.random() * 4
);
if (xRot && Math.random() < 0.65) {
mesh2.rotation.x = Math.floor(Math.random() * 360);
}
if (yRot && Math.random() < 0.65) {
mesh2.rotation.y = Math.floor(Math.random() * 360);
}
let mbb2 = new THREE.Box3().setFromObject(mesh2);
while (mbb1.intersectsBox(mbb2)) {
mesh2.position.set(
2 - Math.random() * 4,
0.5 + Math.random(),
0.8 - Math.random() * 4
);
if (xRot && Math.random() < 0.65) {
mesh2.rotation.x = Math.floor(Math.random() * 360);
}
if (yRot && Math.random() < 0.65) {
mesh2.rotation.y = Math.floor(Math.random() * 360);
}
mbb2 = new THREE.Box3().setFromObject(mesh2);
}
const mesh3 = new THREE.Mesh(
geometry,
Math.random() < 0.6
? materials3()
: Math.random() < 0.6
? materials2()
: materials1()
);
scene.add(mesh3);
mesh3.position.set(
2 - Math.random() * 4,
0.5 + Math.random(),
0.8 - Math.random() * 4
);
if (xRot && Math.random() < 0.65) {
mesh3.rotation.x = Math.floor(Math.random() * 360);
}
if (yRot && Math.random() < 0.65) {
mesh3.rotation.y = Math.floor(Math.random() * 360);
}
let mbb3 = new THREE.Box3().setFromObject(mesh3);
while (mbb1.intersectsBox(mbb3) || mbb2.intersectsBox(mbb3)) {
mesh3.position.set(
2 - Math.random() * 4,
0.5 + Math.random(),
0.8 - Math.random() * 4
);
if (xRot && Math.random() < 0.65) {
mesh3.rotation.x = Math.floor(Math.random() * 360);
}
if (yRot && Math.random() < 0.65) {
mesh3.rotation.y = Math.floor(Math.random() * 360);
}
mbb3 = new THREE.Box3().setFromObject(mesh3);
}
animate();
};
regenerate();
function animate() {
//requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</body>
</html>
the resulting images of this generator
lieschen mueller ©: May 2023 |
3 cubes assignment generator |
further missing features
cubes facing towards the observer / out of the picture, base plate
- manually turn off and on rotation of the cubes
- correct rotation of the scene
- selection of cube types
- grouping (semiotic and semantic)
- manipulating the colour palette
- better cuts
- exporting the model
- textures and texture generators
- …
the hidden in plain sight fish
The fish always starts stinking from the head
The requirement cubes facing towards the observer / out of the picture, base plate
is a fish hidden in plain sight, because it involves a decision that affects the effectiveness of programming.
THe fish in short terms:
- What does facing mean in this context and how the context might change
- implicit or explicit coding
On the one hand one can do the programming implicit, DNA like, fast and reliable, covering every known options.
In this case we have three types of cubes with different facings.
On the other hand this could be solved explicitly, doing the generation, checking and correction, which is highly flexible, because now it is very flexible as it can now be optional and toggled on and off.
This way covers future development changes and iterations of the code.
Both ways of coding have their strengths, whereas the strength of the one is the weakness of the other vice versa.
A code generator should identify the fish, and ask the user for clarifying the problem. Assumptions would be bad and start to stink very soon.
a look into the real life (counter hacking)
… and where we should go:
lieschen mueller © September 2022 |
“Half-Cubes Compositions” |
lieschen mueller © April/May 2023 |
“Three+ Cubes On A Base” variations |
||
Navigation Three Cubes Part 1 Three Cubes Part 3 Counter Hacking