Моя сетка быстро исчезает при загрузке страницы из-за неправильного применения силы? - PullRequest
0 голосов
/ 20 сентября 2019

Итак, я воссоздаю деформирующую сетку из Geometry Wars на веб-странице, чтобы дополнительно проверить свои навыки с помощью JavaScript, и я столкнулся с еще одной загадкой.Я следую учебному пособию, написанному на C# поверх TutsPlus , которое я использовал давно, чтобы воссоздать его во время изучения XNA Framework.Это простое руководство, и большая часть кода не требует пояснений, но я думаю, что мое отсутствие высшего образования по математике подводит меня еще раз.

Я успешно преобразовал сетку в 300x300 canvas без проблем и даже реплицировал весь код в учебнике, но поскольку они используют библиотеки XNA Framework, у них есть преимущество в том, что им не нужно писать математические функции типа Vector3.Я реализовал только то, что мне нужно, но я считаю, что, возможно, я неправильно понял мою математику или, возможно, реализацию.

Initial Grid

Исходная сетка (см. Выше) должен выглядеть так, пока я не начну с ним взаимодействовать, и это так, пока я отключаю функцию Update моего Grid.Я прошел через код, и проблема, кажется, связана с моим вычислением для величины моих векторов.Библиотеки XNA Framework всегда называли его Length и LengthSquared, но каждый поиск Google, который я выполнял, возвращал результаты для вычисления величины как:

Magnitude of a 3D Vector

Теперь это невероятно просто воссоздать в коде, и мой класс Vector3 учитывает Magnitude и MagnitudeSquared, так как учебник требует обоих.Я сравнил результаты моего вычисления величины с онлайн-калькулятором , и результаты были такими же:

V = (2, 3, 4)

| V |= 5.385164807134504

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

class Vector3 {
  constructor(x, y, z) {
    this.X = x;
    this.Y = y;
    this.Z = z;
  }
  Add(val) {
    this.X += val.X;
    this.Y += val.Y;
    this.Z += val.Z;
  }
  Subtract(val) {
    this.X -= val.X;
    this.Y -= val.Y;
    this.Z -= val.Z;
  }
  MultiplyByScalar(val) {
    let result = new Vector3(0, 0, 0);
    result.X = this.X * val;
    result.Y = this.Y * val;
    result.Z = this.Z * val;
    return result;
  }
  DivideByScalar(val) {
    let result = new Vector3(0, 0, 0);
    result.X = this.X / val;
    result.Y = this.Y / val;
    result.Z = this.Z / val;
    return result;
  }
  Magnitude() {
    if (this.X == 0 && this.Y == 0 && this.Z == 0)
      return 0;
    return Math.sqrt(Math.pow(this.X, 2) +
      Math.pow(this.Y, 2) +
      Math.pow(this.Z, 2));
  }
  MagnitudeSquared() {
    return Math.pow(this.Magnitude(), 2);
  }
  DistanceFrom(to) {
    let x = Math.pow(this.X - to.X, 2);
    let y = Math.pow(this.Y - to.Y, 2);
    let z = Math.pow(this.Z - to.Z, 2);
    return Math.sqrt(x + y + z);
  }
}
class PointMass {
  Acceleration = new Vector3(0, 0, 0);
  Velocity = new Vector3(0, 0, 0);
  Damping = 0.95;
  constructor(position, inverseMass) {
    this.Position = position;
    this.InverseMass = inverseMass;
  }
  IncreaseDamping(factor) {
    this.Damping *= factor;
  }
  ApplyForce(force) {
    this.Acceleration.Add(force.MultiplyByScalar(this.InverseMass));
  }
  Update() {
    this.Velocity.Add(this.Acceleration);
    this.Position.Add(this.Velocity);
    this.Acceleration = new Vector3(0, 0, 0);

    if (this.Velocity.MagnitudeSquared() < 0.001 * 0.001)
      Velocity = new Vector3(0, 0, 0);

    this.Velocity.MultiplyByScalar(this.Damping);
    this.Damping = 0.95;
  }
}
class Spring {
  constructor(startPoint, endPoint, stiffness, damping) {
    this.StartPoint = startPoint;
    this.EndPoint = endPoint;
    this.Stiffness = stiffness;
    this.Damping = damping;
    this.TargetLength = startPoint.Position.DistanceFrom(endPoint.Position) * 0.95;
  }
  Update() {
    let x = this.StartPoint.Position;
    x.Subtract(this.EndPoint.Position);
    let magnitude = x.Magnitude();
    if (magnitude < this.TargetLength || magnitude == 0)
      return;

    x = x.DivideByScalar(magnitude).MultiplyByScalar(magnitude - this.TargetLength);
    let dv = this.EndPoint.Velocity;
    dv.Subtract(this.StartPoint.Velocity);
    let force = x.MultiplyByScalar(this.Stiffness)
    force.Subtract(dv.MultiplyByScalar(this.Damping));
    this.StartPoint.ApplyForce(force);
    this.EndPoint.ApplyForce(force);
  }
}
class Grid {
  Springs = [];
  Points = [];
  constructor(containerID, spacing) {
    this.Container = document.getElementById(containerID);
    this.Width = this.Container.width;
    this.Height = this.Container.height;

    this.ColumnCount = this.Width / spacing + 1;
    this.RowCount = this.Height / spacing + 1;

    let columns = [];
    let fixedColumns = [];
    let rows = [];
    let fixedRows = [];
    let fixedPoints = [];
    for (let y = 0; y < this.Height; y += spacing) {
      for (let x = 0; x < this.Width; x += spacing) {
        columns.push(new PointMass(new Vector3(x, y, 0), 1));
        fixedColumns.push(new PointMass(new Vector3(x, y, 0), 0));
      }
      rows.push(columns);
      fixedRows.push(fixedColumns);
      columns = [];
      fixedColumns = [];
    }
    this.Points = rows;

    for (let y = 0; y < rows.length; y++) {
      for (let x = 0; x < rows[y].length; x++) {
        if (x == 0 || y == 0 || x == rows.length - 1 || x == rows[y].length - 1)
          this.Springs.push(new Spring(fixedRows[x][y], this.Points[x][y], 0.1, 0.1));
        else if (x % 3 == 0 && y % 3 == 0)
          this.Springs.push(new Spring(fixedRows[x][y], this.Points[x][y], 0.002, 0.002));

        const stiffness = 0.28;
        const damping = 0.06;
        if (x > 0)
          this.Springs.push(new Spring(this.Points[x - 1][y], this.Points[x][y], stiffness, damping));
        if (y > 0)
          this.Springs.push(new Spring(this.Points[x][y - 1], this.Points[x][y], stiffness, damping));
      }
    }
  }
  ApplyDirectedForce(force, position, radius) {
    this.Points.forEach(function(row) {
      row.forEach(function(point) {
        if (point.Position.DistanceFrom(position) < Math.pow(radius, 2))
          point.ApplyForce(force.MultiplyByScalar(10).DivideByScalar(10 + point.Position.DistanceFrom(position)));
      });
    });
  }
  ApplyImplosiveForce(force, position, radius) {
    this.Points.forEach(function(point) {
      let distance_squared = Math.pow(point.Position.DistanceFrom(position));
      if (distance_squared < Math.pow(radius, 2)) {

        point.ApplyForce(force.MultiplyByScalar(10).Multiply(position.Subtract(point.Position)).DivideByScalar(100 + distance_squared));
        point.IncreaseDamping(0.6);
      }
    });
  }
  ApplyExplosiveForce(force, position, radius) {
    this.Points.forEach(function(point) {
      let distance_squared = Math.pow(point.Position.DistanceFrom(position));
      if (distance_squared < Math.pow(radius, 2)) {
        point.ApplyForce(force.MultiplyByScalar(100).Multiply(point.Position.Subtract(position)).DivideByScalar(10000 + distance_squared));
        point.IncreaseDamping(0.6);
      }
    });
  }
  Update() {
    this.Springs.forEach(function(spring) {
      spring.Update();
    });
    this.Points.forEach(function(row) {
      row.forEach(function(point) {
        point.Update();
      });
    });
  }
  Draw() {
    const context = this.Container.getContext('2d');
    context.clearRect(0, 0, this.Width, this.Height);
    context.strokeStyle = "#ffffff";
    context.fillStyle = "#ffffff";
    for (let y = 1; y < this.Points.length; y++) {
      for (let x = 1; x < this.Points[y].length; x++) {
        let left = new Vector3(0, 0, 0);
        let up = new Vector3(0, 0, 0);

        if (x > 1) {
          left = this.Points[x - 1][y].Position;
          context.beginPath();
          context.moveTo(left.X, left.Y);
          context.lineTo(this.Points[x][y].Position.X, this.Points[x][y].Position.Y);
          context.stroke();
        }
        if (y > 1) {
          up = this.Points[x][y - 1].Position;
          context.beginPath();
          context.moveTo(up.X, up.Y);
          context.lineTo(this.Points[x][y].Position.X, this.Points[x][y].Position.Y);
          context.stroke();
        }

        let radius = 3;
        if (y % 3 == 1)
          radius = 5;

        context.beginPath();
        context.arc(this.Points[x][y].Position.X, this.Points[x][y].Position.Y, radius, 0, 2 * Math.PI);
        context.fill();
      }
    }
  }
}

var grid = new Grid("grid", 40);
setInterval(function() {
  grid.Update();
  grid.Draw();
}, 5);

var mouseX = 0;
var mouseY = 0;

function updateMouseCoordinates(evt) {
  var rect = grid.Container.getBoundingClientRect();
  mouseX = evt.clientX - rect.left;
  mouseY = evt.clientY - rect.top;

  const context = grid.Container.getContext('2d');
  context.clearRect(0, 0, this.Width, this.Height);
  context.strokeStyle = "#ffffff";
  context.fillStyle = "#ff3333";
  context.beginPath();
  context.arc(mouseX, mouseY, 15, 0, 2 * Math.PI);
  context.fill();
  grid.ApplyDirectedForce(new Vector3(0, 0, 5000), new Vector3(mouseX, mouseY, 0), 50);
}
html,
body {
  margin: 0;
  height: 100%;
  background: #213;
  background: linear-gradient(45deg, #213, #c13);
  background: -webkit-linear-gradient(45deg, #213, #c13);
}

.container {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
}
<div class="container">
  <canvas onmousemove="updateMouseCoordinates(event)" id="grid" class="grid" width="300" height="300"></canvas>
</div>

Я считаю, что проблема как-то связана с методом Update в классах Spring и PointMass, как когда я проходил черезВ моем коде я обнаружил, что PointMass объекты, похоже, ускоряются, когда не должны (как, например, я еще не взаимодействовал с ними).Честно говоря, я думаю, что именно реализация моих пользовательских функций Vector3 в этих функциях обновления вызывает проблему, но на всю жизнь я не могу понять, что я сделал неправильно здесь.

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


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

1 Ответ

0 голосов
/ 20 сентября 2019

Мой совет - уменьшите проблему.Имейте только одну точку, замедлите интервал, сделайте шаг, чтобы увидеть, что происходит.Мышь, похоже, ничего не делает.Комментирование строки grid.ApplyDirectedForce(new Vector3(0, 0, 5000), new Vector3(mouseX, mouseY, 0), 50); не меняет вывод.В grid.Update () это идет не так, по какой-то причине grid.Update () что-то делает, даже если сила не применяется, возможно, это означает, что в коде пружины есть ошибка.Нижняя правая точка не перемещает кадр один, может быть, это что-то значит.Отладчик твой друг.Добавьте точку останова в grid.Update () и посмотрите, что на самом деле делает код.Я знаю, что это не прямой ответ, но я надеюсь, что это направит вас в правильном направлении.

Я также хочу отметить, что обычно целая точка квадрата величины такова, что вы можете сравнивать векторы или расстояния, не выполняя операции с квадратным корнем.То есть в вашей функции Magnitude вы выполняете операцию квадратного корня, а затем в своей функции Magnitude Squared вы ее возводите в квадрат.Это то же самое, что просто перейти x ^ 2 + y ^ 2 + z ^ 2

кадр 1:

enter image description here

кадр2:

enter image description here

...