Navigation About

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

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:

  1. What does facing mean in this context and how the context might change
  2. 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