Как я могу узнать размер, какой должна быть прямая кишка, чтобы вместить все прямые кишки внутри холста, используя фиксированное количество прямых кишок?

#javascript #math #canvas

Вопрос:

Контекст

Я создаю игровой клон с цветными пикселями, используя canvas я сохраняю состояние холста внутри массива, который выглядит следующим образом:

 [{"x":0,"y":0,"pickedColor":"white","colorCode":null},{"x":0,"y":1,"pickedColor":"white","colorCode":null},{"x":0,"y":2,"pickedColor":"white","colorCode":null},{"x":0,"y":3,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":4,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":5,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":6,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":7,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":8,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":9,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":10,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":11,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":12,"pickedColor":"#8bc34a","colorCode":null},{"x":0,"y":13,"pickedColor":"white","colorCode":null},{"x":0,"y":14,"pickedColor":"white","colorCode":null},{"x":0,"y":15,"pickedColor":"white","colorCode":null},{"x":0,"y":16,"pickedColor":"white","colorCode":null},{"x":0,"y":17,"pickedColor":"white","colorCode":null},{"x":0,"y":18,"pickedColor":"white","colorCode":null},{"x":0,"y":19,"pickedColor":"white","colorCode":null},{"x":1,"y":0,"pickedColor":"white","colorCode":null},{"x":1,"y":1,"pickedColor":"white","colorCode":null},{"x":1,"y":2,"pickedColor":"white","colorCode":null},{"x":1,"y":3,"pickedColor":"white","colorCode":null},{"x":1,"y":4,"pickedColor":"white","colorCode":null},{"x":1,"y":5,"pickedColor":"white","colorCode":null},{"x":1,"y":6,"pickedColor":"white","colorCode":null},{"x":1,"y":7,"pickedColor":"white","colorCode":null},{"x":1,"y":8,"pickedColor":"white","colorCode":null},{"x":1,"y":9,"pickedColor":"white","colorCode":null},{"x":1,"y":10,"pickedColor":"white","colorCode":null},{"x":1,"y":11,"pickedColor":"white","colorCode":null},{"x":1,"y":12,"pickedColor":"#8bc34a","colorCode":null},{"x":1,"y":13,"pickedColor":"#8bc34a","colorCode":null},{"x":1,"y":14,"pickedColor":"white","colorCode":null},{"x":1,"y":15,"pickedColor":"white","colorCode":null},{"x":1,"y":16,"pickedColor":"white","colorCode":null},{"x":1,"y":17,"pickedColor":"white","colorCode":null},{"x":1,"y":18,"pickedColor":"white","colorCode":null},{"x":1,"y":19,"pickedColor":"white","colorCode":null},{"x":2,"y":0,"pickedColor":"white","colorCode":null},{"x":2,"y":1,"pickedColor":"white","colorCode":null},{"x":2,"y":2,"pickedColor":"white","colorCode":null},{"x":2,"y":3,"pickedColor":"white","colorCode":null}]
 

Таким образом, каждая прямая кишка имеет x свои координаты и y координаты на ней.
Чтобы нарисовать прямую линию на экране, я использую эту функцию, чтобы рассчитать, насколько большой должна быть каждая «прямая линия», чтобы поместиться внутри границ холста:

 // width / height comes from props and rectSize comes from props
const [rectCountX, setRectCountX] = useState(Math.floor(width / rectSize));
const [rectCountY, setRectCountY] = useState(Math.floor(height / rectSize));
 

Например width , и height может быть 800 , и 600 и то rectSize может быть 30 .
Это вычисляет, сколько rects я могу нарисовать в каждом направлении.

Вот как я рисую начальную доску:

 const generateDrawingBoard = (ctx) => {
    // Generate an Array of pixels that have all the things we need to redraw

    for (var i = 0; i < rectCountX; i  ) {
      for (var j = 0; j < rectCountY; j  ) {
        // this is the quint essence whats saved in a huge array. 1000's of these pixels.
        // With the help of this, we can redraw the whole canvas although canvas has not state or save functionality :)
        const pixel = {
          x: i,
          y: j,
          pickedColor: "white",
          // we don't know the color code yet, we generate that afterwards
          colorCode: null,
        };
        updateBoardData(pixel);
        ctx.fillStyle = "white";
        ctx.strokeRect(i * rectSize, j * rectSize, rectSize, rectSize);
      }
    }
  };
 

Это прекрасно работает. Пользователь рисует холст и сохраняет его в базе данных.

Проблема

У меня есть pixelArtPreview компоненты. Это получает данные из базы данных, и для каждого пиксельного рисунка он будет рисовать прямую линию, но меньшего размера, поэтому я могу разместить много прямых линий на странице, чтобы представить пользователю список пиксельных рисунков.

Поэтому мне нужно пересчитать rectSize каждую прямую в массиве, чтобы вписаться в новую width и height . Это именно то, о чем я сейчас бьюсь головой.

Итак, вот компонент, о котором я упоминал:

 import { useEffect, useRef, useState } from "react";
import { drawPixelArtFromState } from "../utils/drawPixelArtFromState";

const PixelArtPreview = ({ pixelArt }) => {
  const canvasRef = useRef(null);
  const [ctx, setCtx] = useState(null);
  const [canvas, setCanvas] = useState(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    // This is where I scale the original size
    // the whole pixelArt comes from the database and looks like this (example data):

    // { pixelArtTitle: "some title", pixelArtWidth: 1234, pixelArtHeight: 1234, pixels: [... (the array I shows above with pixels)]}
    canvas.width = pixelArt.pixelArtWidth * 0.5;
    canvas.height = pixelArt.pixelArtHeight * 0.5;
    setCanvas(canvas);
    const context = canvas.getContext("2d");
    setCtx(context);
  }, []);

  useEffect(() => {
    if (!ctx) return;
    drawPixelArtFromState(pixelArt, ctx);
  }, [pixelArt, ctx]);
  return <canvas className="m-4 border-4" ref={canvasRef} />;
};

export default PixelArtPreview;
 

Но волшебство происходит внутри импортированной функции drawPixelFromState(pixelArt, ctx)

Это указанная функция (с комментариями, каким был мой процесс):

 export const drawPixelArtFromState = (pixelArt, ctx) => {
  // how much pixels have been saved from the original scale when the art has been created
  const canvasCount= JSON.parse(pixelArt.pixels).length;

  // how many pixels we have on X
  const xCount = JSON.parse(pixelArt.pixels)[canvasCount- 1].x;
  // how many pixels we have on Y
  const yCount = JSON.parse(pixelArt.pixels)[canvasCount- 1].y;

  // total pixles (canvas height * canvas.width with the scale of 0.5 so it matches the canvas from the component before)
  // this should give me all the pixels inside the canvas
  const canvasPixelsCount =
    pixelArt.pixelArtWidth * 0.5 * (pixelArt.pixelArtHeight * 0.5);

  // now i try to find out how big each pixel has to be
  const newRectSize = canvasPixelsCount / canvasCount;

  // this is for example 230 rects which can't be I see only 2 rects on the canvas with that much of a rectSize
  console.log(newRectSize);

  // TODO: Parse it instantly where we fetch it
  JSON.parse(pixelArt.pixels).forEach((pixel) => {
    ctx.fillStyle = "white";
    ctx.strokeRect(
      pixel.x * newRectSize,
      pixel.y * newRectSize,
      newRectSize,
      newRectSize
    );

    ctx.fillStyle = pixel.pickedColor;
    ctx.fillRect(
      pixel.x * newRectSize,
      pixel.y * newRectSize,
      newRectSize,
      newRectSize
    );
  });
};
 

Вот как этот пример выглядит на экране (это 4 отдельных холста, которые видны на серой границе — я ожидаю увидеть всю пиксельную графику внутри маленького холста):

введите описание изображения здесь

вопрос:

Мне нужно найти правильную формулу для вычисления нового rectSize , чтобы все прямые в массиве могли поместиться внутри нового холста width и height . Возможно ли это вообще или мне нужно старое rectSize , чтобы расчет работал?

Итак, TL;DR: насколько велика должна быть каждая прямая x, чтобы вместить все x прямых в y холст.

Большое спасибо!

Ответ №1:

Извините, ребята, после долгих часов борьбы, наконец, что-то щелкнуло. Расчет, который я сделал:

 // now i try to find out how big each pixel has to be
  const newRectSize = canvasPixelsCount / rectCount;
 

Дает мне площадь пикселя. Но мне нужна только одна его сторона (так canvas.fillRect как она заботится только о значении x и заботится об этой области). Поэтому мне нужен квадратный корень из этого.

 // now i try to find out how big each pixel has to be
  const newRectSize = Math.sqrt(canvasPixelsCount / rectCount);
 

Теперь это работает идеально.

Скриншот:

введите описание изображения здесь