p5. js Flood Fill (инструмент ведра) работает медленно и странно - PullRequest
2 голосов
/ 28 мая 2020

Итак, я написал функцию заливки заливкой, которая работает как инструмент ведра в приложении для рисования: вы щелкаете внутри замкнутой формы, и она заполняется цветом.

У меня две проблемы с этим:

  1. производительность - допустим, мой холст имеет размер 600 * 600 (370000 пикселей), и я рисую на нем большой круг, который, например, имеет около 100 тыс. Пикселей, это может занять около 40 (!! !) секунд, чтобы заполнить этот круг! это безумие! Создание sqaure размером ровно 10 000 пикселей занимает в среднем 0,4-0,5 секунды, но (я полагаю), поскольку размеры массивов, используемых программой, так сильно растут, для заполнения sqaure, в 10 раз превышающего размер, требуется примерно в 100 раз больше длины.

  2. в начинке есть что-то странное. Я не совсем уверен, как это происходит, но всегда остается несколько незаполненных пикселей. Совсем немного, но это действительно странно.

Моя функция заливки заливкой использует 4 вспомогательные функции: получение и установка цвета пикселя, проверка того, является ли цвет для заливки, и проверка того, является ли это пиксель, проверенный ранее. Вот все функции:

getPixelColor = (x, y) => {
  let pixelColor = [];
  for (let i = 0; i < pixDens; ++i) {
    for (let j = 0; j < pixDens; ++j) {
      index = 4 * ((y * pixDens + j) * width * pixDens + (x * pixDens + i));
      pixelColor[0] = pixels[index];
      pixelColor[1] = pixels[index + 1];
      pixelColor[2] = pixels[index + 2];
      pixelColor[3] = pixels[index + 3];
    }
  }
  return pixelColor;
};

setPixelColor = (x, y, currentColor) => { //Remember to loadPixels() before using this function, and to updatePixels() after.
  for (let i = 0; i < pixDens; ++i) {
    for (let j = 0; j < pixDens; ++j) {
      index = 4 * ((y * pixDens + j) * width * pixDens + (x * pixDens + i));
      pixels[index] = currentColor[0];
      pixels[index + 1] = currentColor[1];
      pixels[index + 2] = currentColor[2];
      pixels[index + 3] = currentColor[3];
    }
  }
}

isDuplicate = (posHistory, vector) => {
  for (let i = 0; i < posHistory.length; ++i) {
    if (posHistory[i].x === vector.x && posHistory[i].y === vector.y) {
      return true;
    }
  }
  return false;
}

compareColors = (firstColor, secondColor) => {
  for (let i = 0; i < firstColor.length; ++i) {
    if (firstColor[i] !== secondColor[i]) {
      return false;
    }
  }
  return true;
}

floodFill = () => {
  loadPixels();
  let x = floor(mouseX);
  let y = floor(mouseY);
  let startingColor = getPixelColor(x, y);
  if (compareColors(startingColor, currentColor)) {
    return false;
  }
  let pos = [];
  pos.push(createVector(x, y));
  let posHistory = [];
  posHistory.push(createVector(x, y));
  while (pos.length > 0) {
    x = pos[0].x;
    y = pos[0].y;
    pos.shift();
    if (x <= width && x >= 0 && y <= height && y >= 0) {
      setPixelColor(x, y, currentColor);
      let xMinus = createVector(x - 1, y);
      if (!isDuplicate(posHistory, xMinus) && compareColors(getPixelColor(xMinus.x, xMinus.y), startingColor)) {
        pos.push(xMinus);
        posHistory.push(xMinus);
      }
      let xPlus = createVector(x + 1, y);
      if (!isDuplicate(posHistory, xPlus) && compareColors(getPixelColor(xPlus.x, xPlus.y), startingColor)) {
        pos.push(xPlus);
        posHistory.push(xPlus);
      }
      let yMinus = createVector(x, y - 1);
      if (!isDuplicate(posHistory, yMinus) && compareColors(getPixelColor(yMinus.x, yMinus.y), startingColor)) {
        pos.push(yMinus);
        posHistory.push(yMinus);
      }
      let yPlus = createVector(x, y + 1);
      if (!isDuplicate(posHistory, yPlus) && compareColors(getPixelColor(yPlus.x, yPlus.y), startingColor)) {
        pos.push(yPlus);
        posHistory.push(yPlus);
      }
    }
  }
  updatePixels();
}

Я бы очень оценил, если бы кто-нибудь помог мне решить проблемы с функциями. Большое спасибо !!

РЕДАКТИРОВАТЬ: Итак, я обновил свою функцию заливки заливки и удалил массив цветов, который никогда не использовал. этот массив был довольно большим, и несколько методов pu sh () и shift () вызывали его практически при каждом запуске. К сожалению, время выполнения на 99,9% одинаково для небольших форм (например, заливка 10000 занимает те же 0,5 секунды, но большие заливки, такие как 100000 пикселей, теперь занимают около 30 секунд, а не 40, так что это шаг вправо Я предполагаю, что использование ОЗУ также снизилось, поскольку это был довольно большой массив, но я не измерял его. Странная проблема, когда остаются незаполненные пиксели, все еще существует.

1 Ответ

0 голосов
/ 12 июня 2020

Небольшое предложение:

На самом деле вам не нужно использовать массив posHistory, чтобы определить, устанавливать ли цвет. Если текущий пиксель имеет тот же цвет, что и startingColor, установите цвет, иначе не устанавливайте. Это будет иметь тот же эффект.

Массив posHistory будет становиться все больше и больше во время выполнения. В результате нужно проделать много работы, чтобы определить, нужно ли заливать один пиксель. Я думаю, это может быть причиной того, что ваш код работает медленно.

Что касается «странности»:

Это тоже случалось со мной раньше. Я думаю, это потому, что незаполненные пиксели не имеют того же цвета, что и startingColor. Скажем, вы рисуете черную фигуру на белом фоне, вы ожидаете увидеть несколько серых пикселей (близких к белому) где-то между черной и белой частями. Эти пиксели играют роль сглаживания формы.

...