【问题标题】:Rotating a 'body along a path using the external angles使用外角沿路径旋转“身体”
【发布时间】:2018-04-11 00:12:45
【问题描述】:

我有一个小脚本,其中:

  • 身体沿着路径移动。
  • 当它到达段的末尾时,它会开始围绕其中心旋转,直到它与下一段的切线对齐。
  • 然后它开始沿着下一段移动。

一切正常,但我在旋转方面遇到了一个小问题。身体应旋转以与反射/外角对齐。

正如你在下面的 MCVE 中看到的那样,第二次旋转是顺时针的,而它应该是逆时针的。

相反的情况发生在第 3 段。它逆时针旋转,应该顺时针旋转,因为旋转会跟随外角。

我做错了什么?

paper.setup(document.querySelector('canvas'))

// Path

const path = new paper.Path({
  segments: [[-100, 300], [100, 300], [100, 0], [0, 100], [-100, 200], [-200, -50]],
  strokeColor: '#E4141B',
  strokeWidth: 5,
  strokeCap: 'round',
  position: paper.view.center
})

path.segments.forEach(segment => {
  const text = new paper.PointText({
    point: [50, 50],
    content: `${parseInt(path.getTangentAt(segment.location).angle)} deg`,
    fillColor: 'black',
    fontFamily: 'Courier New',
    fontWeight: 'bold',
    fontSize: 15,
    position: segment.point
  })
})

// Car

const car = new paper.Path.Rectangle(
  new paper.Rectangle(new paper.Point(50, 50), new paper.Point(150, 100))
)
car.fillColor = '#e9e9ff'
car.rotationLabel = new paper.PointText({
  point: [50, 50],
  content: '0',
  fillColor: 'black',
  fontFamily: 'Courier New',
  fontWeight: 'bold',
  fontSize: 10,
  position: car.position
})

// Car custom

car.currentRotation = 0
car.rotateAroundCenter = function(rotation) {
  rotation = parseInt(rotation)
  this.rotate(rotation)
  this.currentRotation += rotation
}

car.updateRotationLabel = function() {
  this.rotationLabel.position = this.position
  this.rotationLabel.content = this.currentRotation
}

car.getCurrentRotation = function() {
  return this.currentRotation
}

car.isNotAlignedWith = function(rotation) {
  return this.currentRotation !== parseInt(rotation)
}

// Animation-along-a-path

let i = 0
paper.view.onFrame = () => {
  car.updateRotationLabel()

  const rotation = path.getTangentAt(i).angle
  const rotationSign = car.getCurrentRotation() < rotation ? 1 : -1

  car.position = path.getPointAt(i)

  if (car.isNotAlignedWith(rotation)) {
    car.rotateAroundCenter(rotationSign)
  } else {
    car.position = path.getPointAt(i);

    i++

    if (i > path.length - 1) {
      paper.view.onFrame = () => {}
      console.log('done')
    }
  }
}
canvas {
  width: 100%;
  height: 100%;
  background: #666;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.min.js"></script>
<canvas></canvas>

FWIW 我已经绘制了路径在旋转时应沿其旋转的外部(反射)角度。

注意:每个段上的黑色角文本是该段的切线。

【问题讨论】:

    标签: javascript html5-canvas paperjs


    【解决方案1】:

    我建议使用方向向量作为当前方向,而不仅仅是角度,因为这样会更容易确定应该旋转的方向等。

    paper.setup(document.querySelector('canvas'))
    
    // Path
    
    const path = new paper.Path({
      segments: [[-100, 300], [100, 300], [100, 0], [0, 100], [-100, 200], [-200, -50]],
      strokeColor: '#E4141B',
      strokeWidth: 5,
      strokeCap: 'round',
      position: paper.view.center
    })
    
    path.segments.forEach(segment => {
      const text = new paper.PointText({
        point: [50, 50],
        content: `${parseInt(path.getTangentAt(segment.location).angle)} deg`,
        fillColor: 'black',
        fontFamily: 'Courier New',
        fontWeight: 'bold',
        fontSize: 15,
        position: segment.point
      })
    })
    
    // Car
    
    const car = new paper.Path.Rectangle(
      new paper.Rectangle(new paper.Point(50, 50), new paper.Point(150, 100))
    )
    car.fillColor = '#e9e9ff'
    car.rotationLabel = new paper.PointText({
      point: [50, 50],
      content: '0',
      fillColor: 'black',
      fontFamily: 'Courier New',
      fontWeight: 'bold',
      fontSize: 10,
      position: car.position
    })
    
    // Car custom
    
    car.currentRotation = new paper.Point(1, 0)
    car.rotateAroundCenter = function(rotation) {
      this.rotate(rotation)
      this.currentRotation = this.currentRotation.rotate(rotation)
    }
    
    car.updateRotationLabel = function() {
      this.rotationLabel.position = this.position
      this.rotationLabel.content = this.currentRotation.angle;
    }
    
    car.getCurrentRotation = function() {
      return this.currentRotation
    }
    
    car.isNotAlignedWith = function(rotation) {
      const precision = 0.00001;
      return Math.abs(1 - rotation.dot(this.currentRotation)) <= precision ? false : true;
    }
    
    // Animation-along-a-path
    
    let i = 0
    
    paper.view.onFrame = () => {
      car.updateRotationLabel()
    
      const requiredDirection = path.getTangentAt(i)
      const normal = requiredDirection.rotate(-90);
      const rotationSign = car.getCurrentRotation().dot(normal) > 0 ? 1 : -1
    
      car.position = path.getPointAt(i)
    
      if (car.isNotAlignedWith(requiredDirection)) {
        car.rotateAroundCenter(rotationSign);
      } else {
        car.position = path.getPointAt(i);
        i++
    
        if (i > path.length - 1) {
          paper.view.onFrame = () => {}
          console.log('done')
        }
      }
    }
    canvas {
      width: 100%;
      height: 100%;
      background: #666;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.min.js"></script>
    <canvas></canvas>

    【讨论】:

    • 真的很时尚,很有意义。不过小问题:为什么你在isNotAlignedWith() 中使用浮点/精度而不是整数?
    • 舍入误差累积,最后所需方向的转角和当前直接不匹配大约 0.00001 或类似的东西。我尝试在 Point 对象上使用 IsCollinear 方法,但因此失败。也许你可以做 toFixed 或类似的事情,但我决定更精确)
    【解决方案2】:

    解决问题有两件事。

    首先是你的程序应该工作的真实角度范围只有 0 到 360 度。我通过计算角度的模 360 并在它们仍低于 0 时添加 360 来解决这个问题,以确保它们在 0-360 范围内是安全的。

    第二件事是,在两种情况下,方块应该向 1 方向旋转。一旦它应该指向的角度大于它现在面对的角度。但是当差值超过 180 时,情况正好相反,因为角度是一个类似数字的圆,这意味着环绕可能是到达另一个值的最短路径(例如,从 0 到 350° 的差值范围为 0如果绕行,则到 360 度仅为 20°,而不是正常行驶时的 340°)。

    paper.setup(document.querySelector('canvas'))
    const MAXANGLE = 360;
    // Path
    
    const path = new paper.Path({
      segments: [
        [-100, 300],
        [100, 300],
        [100, 0],
        [0, 100],
        [-100, 200],
        [-200, -50]
      ],
      strokeColor: '#E4141B',
      strokeWidth: 5,
      strokeCap: 'round',
      position: paper.view.center
    })
    
    path.segments.forEach(segment => {
      const text = new paper.PointText({
        point: [50, 50],
        content: `${parseInt(path.getTangentAt(segment.location).angle)} deg`,
        fillColor: 'black',
        fontFamily: 'Courier New',
        fontWeight: 'bold',
        fontSize: 15,
        position: segment.point
      })
    })
    
    // Car
    
    const car = new paper.Path.Rectangle(
      new paper.Rectangle(new paper.Point(50, 50), new paper.Point(150, 100))
    )
    car.fillColor = '#e9e9ff'
    car.rotationLabel = new paper.PointText({
      point: [50, 50],
      content: '0',
      fillColor: 'black',
      fontFamily: 'Courier New',
      fontWeight: 'bold',
      fontSize: 10,
      position: car.position
    })
    
    // Car custom
    
    car.currentRotation = 0
    car.rotateAroundCenter = function(rotation) {
      rotation = parseInt(rotation)
      this.rotate(rotation)
      this.currentRotation += rotation
    }
    
    car.updateRotationLabel = function() {
      this.rotationLabel.position = this.position
      this.rotationLabel.content = this.currentRotation
    }
    
    car.getCurrentRotation = function() {
      return this.currentRotation
    }
    
    car.isNotAlignedWith = function(rotation) {
      var a1 = this.currentRotation % MAXANGLE;
      var a2 = parseInt(rotation) % MAXANGLE;
      if (a1 < 0) a1 += MAXANGLE;
      if (a2 < 0) a2 += MAXANGLE;
      return a1 !== a2;
    }
    
    car.getRotationAngle = function(rotation) {
      var a1 = this.currentRotation % MAXANGLE;
      var a2 = parseInt(rotation) % MAXANGLE;
      if (a1 < 0) a1 += MAXANGLE;
      if (a2 < 0) a2 += MAXANGLE;
      return (a2 > a1 && a2 - a1 <= MAXANGLE / 2) || (a1 > a2 && a1 - a2 >= MAXANGLE / 2) ? 1 : -1;
    }
    
    // Animation-along-a-path
    
    let i = 0
    paper.view.onFrame = () => {
      car.updateRotationLabel()
    
      const rotation = path.getTangentAt(i).angle
      const rotationSign = car.getRotationAngle(rotation);
    
      car.position = path.getPointAt(i)
    
      if (car.isNotAlignedWith(rotation)) {
        car.rotateAroundCenter(rotationSign)
      } else {
        car.position = path.getPointAt(i);
    
        i++
    
        if (i > path.length - 1) {
          paper.view.onFrame = () => {}
          console.log('done')
        }
      }
    }
    canvas {
      width: 100%;
      height: 100%;
      background: #666;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-core.min.js"></script>
    <canvas></canvas>

    【讨论】:

    • 但是等等,这并没有使用我的绘图所示的外部角度旋转。
    • 抱歉,这应该可以修复
    • 我解决了。但现在“身体”的方向确实很重要,所以我不得不回到 360 度而不是 180 度
    猜你喜欢
    • 2019-10-05
    • 1970-01-01
    • 2016-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2020-02-06
    相关资源
    最近更新 更多