3 килобайта длинная статья движущийся холст произвольный обычный полигон (точка, линия, поверхность)

внешний интерфейс Canvas
3 килобайта длинная статья движущийся холст произвольный обычный полигон (точка, линия, поверхность)

Это 8-й день моего участия в Gengwen Challenge, смотрите подробности мероприятия:Обновить вызов

предисловие

В прошлой статье я просто реализовал движущиеся прямоугольники (точки, линии и поверхности) в канве, для тех, кто не понял, прошу прочитать мою статью:Используйте холст для реализации движения прямоугольников (точек, линий, поверхностей) (1). ок, не несите чушь, переходите сразу к теме статьи, я оставил много вопросов в прошлой статье, то есть как я узнаю, какой тип я двигаю, когда я двигаюсь в шаге рисования, является ли он является точкой, линией или поверхностью, это проблема книги, которая будет решена в этой статье. Прочитав эту статью, вы сможете узнать следующие моменты:

  1. Определить расстояние между точками
  2. Оценка отношения между точкой и прямой линией (использование перекрестного произведения)
  3. Как нарисовать обычный n-угольник на холсте. (вращение вектора)

На самом деле, я так много сказал выше, на самом деле, это сделать эффект в 2d графике.snap— Адсорбция для определения отношения между текущей точкой и многоугольником на текущем холсте.

Адсорбция - точка реализации

Читатели, вы можете подумать, если бы вас попросили это сделать, как бы вы это сделали? Предположим, что на холсте много полигонов и много точек. Было сказано, что то, что близко к нему, не является тем, чем оно является. хорошо, вы правильно поняли. Фактически, это сравнение текущей точки со всеми точками на холсте. Какая из них ближе к выбранной точке. Будет ли это связано с проблемой производительности запроса? Некоторые ученики спросят, а что, если на холсте много точек? Надо ли пробегать и сравнивать размеры один за другим, конечно, не здесь, чтобы дать вам научно-популярныйАлгоритм пространственного геометрического индексирования Rbush

RBush — это высокопроизводительная библиотека JavaScript для двумерного пространственного индексирования точек и прямоугольников. Он основан на оптимизированной структуре данных R-дерева и поддерживает пакетные вставки.

Я покажу тебе Рбуш, когда у меня будет время позже, здесь я даюСсылка на ссылкуЗаинтересованные студенты могут узнать это самостоятельно. В этой статье не используется Rbush, а для хранения данных используются коллекции! Здесь следует подчеркнуть еще один момент: каждая точка холста должна быть экземпляром с уникальным идентификатором. Мы восстановим его следующим образом:

const current = 0;
const pointMap = []

constructor(x,y) {
    this.x = x || 0;
    this.y = y || 0;
    this.id = ++current;
}
// 增加到Map上
add2Map() {
  pointMap.push(this)
  return this
}
//用来随机生成一个点
random(width,height){
    this.x = Math.random() * width;
    this.y = Math.random() * height;
    return this;
}

// 取绝对值
abs() {
    return [Math.abs(this.x), Math.abs(this.y)]
}

//计算两个点之间的距离
distance(p) {
    const [x,y] = this.clone().sub(p).abs();
    return Math.sqrt(x*x + y * y);
}

Я переписал код метода для рисования полигонов следующим образом:

// 画多边形
function drawAnyPolygon(points) {
    if(!Array.isArray(points)) {
        return;
    }
    ctx.strokeStyle = 'black'
    ctx.setLineDash([]); 
    ctx.beginPath();
    // 第一个点是开始点
    const start = points[0];
    ctx.moveTo(start.x,start.y)
    points.slice(1).forEach(item => {
        ctx.lineTo(item.x,item.y)
    })
    ctx.closePath()
    ctx.stroke()
}

Что самое важное в этом, как мы генерируем список точек правильных многоугольников на основе точки

Как нарисовать положительный многоугольник в Canvas?

Здесь мы смотрим на определение многоугольника:

Правильный многоугольник — это многоугольник с равными сторонами и равными углами в двумерной плоскости, также называемый правильным многоугольником.

Даю следующую схему:

polygon.pngИз рисунка мы можем получить: Формирование регулярного полиморфизма есть не что иное, как два

  1. Взяв текущую точку за центр, нарисуйте описанную окружность, а затем разделите ее поровну по количеству сторон.
  2. Возьмите текущую точку за центр, начертите вписанный круг, а затем разделите его поровну по количеству сторон

Мы знаем принцип, как применить его к нашему холсту? На самом деле это тоже очень просто, мы используем центр окружности и точку на окружности в качестве начального вектора. Затем продолжайте вращать на 2π/n, чтобы получить все очки. С помощью точек мы можем рисовать правильные многоугольники. Вот идея вычисления многоугольника с описанной окружностью.Насчет того, как вычислить вписанную окружность, я задам вам внеклассный вопрос 🤔 Думайте сами. Я даю следующую реализацию кода:

Первая часть точки вращается вокруг некоторой центральной точки:

 rotate(center, angle) {
      const c = Math.cos( angle ), s = Math.sin( angle );
      const x = this.x - center.x;
      const y = this.y - center.y;
      this.x = x * c - y * s + center.x;
      this.y = x * s + y * c + center.y;
      return this;

  }

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

Вторая часть, если генерируются вершины многоугольника:

function getAnyPolygonPoints(start, end, n = 3) {
    const angle = (Math.PI * 2) / n
    const points = [end]
    for (let i = 1; i < n; i++) {
      points.push(
        end
          .clone()
          .rotate(start.clone(), angle * i)
          .add2Map()
      )
    }
    return points
  }

Далее я покажу вам эти правильные многоугольники с n = 5|10|20|50. Затем вы обнаружите, что по мере увеличения числа сторон многоугольник, который мы рисуем, становится все больше и больше похожим на круг.

多边形演进图.pngЭто открыло ваш новый мир? Уважаемые читатели. См. здесь, если вы найдете это полезным.поставить лайкПродолжай читать. 👇 Есть также введение в математические методы.

Реализовать перемещение точек произвольного правильного многоугольника

Представим, что мышь постоянно движется по холсту, я уверен, какая точка мне близка, и какую точку я выберу. Следовательно, для вынесения суждений необходимо постоянно сравнивать расстояние между точкой, перемещаемой мышью, и существующей точкой. Хорошо, идея есть, я даю следующий код:

function calcClosestPoint() {
    const minMap = []
    for (let value of pointMap) {
      const dis = value.distance(start.clone())
      minMap.push({ ...value, dis })
    }
    // 找出最近的的一个点
    const sort = minMap.sort((a, b) => a.dis - b.dis)
    return sort[0]
}

Что этот код может говорить о том, чтобы найти расстояние между двумя точками? Это очень просто, то есть вычесть абсолютное значение двух координат, а затем взять квадрат. Наверное, так думает большинство людей, и я тоже так думал вначале. Это нормально так думать, но на самом деле рецепт мне не нужен Нам нужно сравнить расстояние. Здесь будет небольшая оптимизация производительности. Потому что вам нужно прописать, а затем вычислить процессор, если количество точек в холсте слишком много, и число велико, есть еще одна важная проблема, что Js имеет проблемы с точностью. код показывает, как показано ниже:

distance(p) {
  const [x, y] = this.clone().sub(p).abs()
  return x * x + y * y
}

distanceSq(p) {
  const [x, y] = this.clone().sub(p).abs()
  return Math.sqrt(x * x + y * y)
}

Найдите самую маленькую точку, мы можем повторить предыдущую статью, чтобы добиться движения. Я не буду здесь слишком много объяснять, для тех, кто не понял, вы можете прочитать предыдущую статью. Учитывая следующий код:

//画出任意多边形 满足顺时针方向
  function drawAnyPolygon(points) {
    if (!Array.isArray(points)) {
      return
    }
    ctx.strokeStyle = 'black'
    ctx.setLineDash([])
    ctx.beginPath()
    // 存在移动的点
    if (movePoint.length > 0) {
      // 移动的向量
      const moveVec = end.clone().sub(start)
      points = points.map((item) => {
        // 点列表中去找到和移动相等的就是把它的数据变化了
        if (item.equal(movePoint[0])) {
          return item.clone().add(moveVec)
        }
        return item
      })
    }
    ctx.moveTo(points[0].x, points[0].y)
    points.slice(1).forEach((item) => {
      ctx.lineTo(item.x, item.y)
    })
    ctx.closePath()
    ctx.stroke()
  }

canvas.addEventListener('click', (e) => {
  if (e.altKey) {
    isMove = false
    return
  }
  isMove = !isMove
  const x = e.clientX
  const y = e.clientY
  start = new Point2d(x, y)
  movePoint.length = 0
  movePoint.push(calcClosestPoint())
  isSelect = true
})

Здесь я щелкаю мышью ниже, чтобы определить движущуюся точку и начальную точку движущегося вектора, movePoint — это фактически все точки, которые нужно переместить. Просто посмотрите на рендеры.

Jun-27-2021 11-09-33.gif

Реализовать движение любой линии правильного многоугольника

Мы поняли движение точки, момент, когда мы щелкаем мышью, как мы можем определить, что щелкнули по линии, что также связано с математической проблемой? Это сравнение расстояния от точки до линии и расстояния от точки до линии.Первое решение - решить уравнение линии. Что такое уравнение прямой линии?

Как найти расстояние от точки до прямой 1

Пусть прямая Lуравнениеравно Ax+By+C=0, а координаты точки P равны (x0, y0), то расстояние от точки P до прямой L равно:

img

Аналогично, когда P(x0, y0), когда аналитическая формула прямой L имеет вид y=kx+b, то расстояние от точки P до прямой L равно

img

Учитывая точку (x0, y0, z0) и пространственную прямую x-x1/l=y-y1/m=z-z1/n, имеем d=|(x1-x0, y1-y0, z1-z0 )×(l,m,n)|/√(l²+m²+n²)

То есть наклон и точка пересечения вычисляются из двух точек, но необходимо учитывать частный случай прямой и оси Y, то есть момент, когда наклон бесконечен. Расстояние в это время является вычитанием координаты x. Таким образом, мы можем вычислить расстояние от точки до линии, затем сравнить, чтобы найти линию с наименьшим расстоянием, а затем найти движущуюся точку, но это не оптимальное решение. Оптимальным решением является использование вектора для расчета, если вы хотите рассчитать вертикальную ногу, вы можете использовать этот метод для этого.

Как найти расстояние от точки до прямой 2

Сначала позвольте задать вопрос? В чем заключается геометрический смысл векторного произведения векторов, то есть площади параллелограмма, заключенной между двумя векторами.Когда мы вычисляем расстояние от точки до прямой, это не просто вычисление, а высота параллелограмма, поэтому просто вычисляем площадь и делим ее на основание, чтобы вычислить расстояние от точки до прямой.. Хахахаха, вас снова покорила прелесть математики? Покажу картинку:

Xnip2021-06-27_11-38-27.pngКрасная линия — это расстояние от точки до линии. Мы начали кодить напрямую, и сразу началась теория.

Сначала напишите метод для преобразования точки в отрезок, потому что мы соединены конец в конец, поэтому количество точек, последнее должно быть таким же, как начальная точка.

function points2Segs(points) {
    const start = points[0]
    points.push(start)
    const segs = []
    points.forEach((point, index) => {
      if (index !== points.length - 1) {
        segs.push([point, points[index + 1]])
      }
    })
    return segs
}

Метод перекрестного произведения заключается в следующем:

cross(v) {
   return this.x * v.y - this.y * v.x
}

Рассчитайте расстояние от точки до прямой следующим образом:

function pointDistanceToLine(target, line) {
  const [start, end] = line
  const vec1 = start.clone().sub(target)
  const vec2 = end.clone().sub(target)
  return vec1.clone().cross(vec2) / start.clone().distanceSq(target)
}
// 找出最近的线
function calcClosestLine() {
  let minMap = []
  segs.forEach((line) => {
    const dis = pointDistanceToLine(start, line)
    minMap.push({
      dis,
      line,
    })
  })
  minMap = minMap.sort((a, b) => a.dis - b.dis)
  // 找出最近的直线然后将点放入到movePoint 中其实就好了
  movePoint.push(...minMap[0].line)
}

Переместите код туда и перепишите его:

 if (movePoint.length > 0) {
    const moveVec = end.clone().sub(start)
    points = points.map((item) => {
      // 线的移动对应的是两个点 面的话应该就是所有的点
      if (item.equal(movePoint[0]) || item.equal(movePoint[1])) {
        return item.clone().add(moveVec)
      }
      return item
    })
  }

Посмотрите непосредственно на эффект:

Jun-27-2021 12-32-26.gif

Идеальная реализация Спасибо, что все еще видите это здесь. В этот момент, поскольку точка и линия на самом деле знакомы, поверхность представляет собой движение всех точек. Это несложно. Вы можете попрактиковаться в этом позже.

Суммировать

Эта статья в основном знакомит с движением графики в 2D, точками, линиями и поверхностями. По сути, это движение точки плюс вектор движения. Суть в том, что на самом деле есть еще много вещей, которые нужно потихоньку осознать всем. Формирование замкнутой области, порядок точек, должны быть связаны встык, согласно определенному направлению. Существует также некоторое понимание перекрестного произведения и скалярного произведения. Его можно гибко использовать в сочетании с проектом реализации. Весь код для этой статьи находится в моемgithub, Если все считают, что чтение полезно для вас, вы можете пометить его.

И последнее, но не менее важное: я надеюсь, что вы все лайкните 👍 и прокомментируете. Вывод знаний не прост.Если вам интересна графика, вы можете подписаться на мой официальный аккаунт:Графика интерфейсаПродолжайте делиться знаниями о canvas, svg, webgl.