javascript - 四面二维形状的分割

标签 javascript svg grid quadratic cubic-bezier

我正在寻找一种将四边形拆分为网格的方法。例如: enter image description here

最终我需要能够将生成的形状转换为 SVG,但我很乐意处理与另一个库或坐标系之间的转换。我正在寻找的是如何进行计算。

假设形状是一个四边形,四边形绘制成二次方,每边可以是凹的或凸的,但没有边与其他边或它们自身重叠,并且四个边中的任何一个都可以是弯曲的。

四边形的相同方法(具有直边的形状是微不足道的),如果两条相对的边是直线,则很容易找到相交点,因为它们将位于在分割之间绘制的直线上对方。从那里可以相对容易地计算出将它们连接到沿替代轴的前一点所需的曲线:

enter image description here

然而,当没有两条笔直的相对边时(如上面的第三个示例),我不确定如何找到这些点,因为不再确定这些点位于一条直线上。

我花了很长时间寻找记录的方法,但无济于事。

下面是那种用SVG描述的起始形状的例子(不用SVG处理,只要我能输出成SVG即可。

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
     viewBox="0 0 406.4 233.4" xml:space="preserve">
  <path class="st0" d="M394.3,232.7c-106-37.8-353.7,0-353.7,0s-90.4-151.2,0-207.3s353.7,0,353.7,0S420.3,154.7,394.3,232.7z"/>
</svg>

编辑:我问了一个类似的 question在 Stack Exchange Maths 上,其中一个答案描述了一种方法——使用 Coons Patch . Quora解释here .

最佳答案

您可以在 Codepen here 上查看实时示例和完整代码.

数据表示

下图最简单的数据表示使用三次贝塞尔曲线。我相信这也将涵盖您的所有用例。为了不让各种特殊情况污染我们的代码,我们将要求输入始终采用四个后续三次贝塞尔曲线的格式,就像我们绘制它们一样。这意味着我们不能使用:

  • 二次贝塞尔曲线(通过镜像另一个控制点可转换为三次曲线)
  • Segments(通过将控制点等距离地放置在直线的端点之间,可转换为三次贝塞尔曲线)
  • 关闭路径 [Z SVG 命令](通过计算给定的线段然后从那里获取它,可转换为三次贝塞尔曲线)

More on the subject of paths in SVG

pure shape

它的 SVG 表示

<path d=" M50 50
     C 100 100 400 100 450 50
     C 475 250 475 250 450 450
     C 250 300 250 300 50 450
     C 150 100 150 250 50 50"
 fill="transparent"
 stroke="black"
/>

但是,为了方便起见,我们将定义自己的数据结构。

Point 只是一个普通的旧 Vector2D 类。

class Point {
  constructor (x, y) {
    this.x = x
    this.y = y
  }
}

Curve 是三次贝塞尔曲线。

cubic bézier

class Curve {
  constructor (
    startPointX, startPointY,
    controlPointAX, controlPointAY,
    controlPointBX, controlPointBY,
    endPointX, endPointY) {
    this.start = new Point(startPointX, startPointY)
    this.controlA = new Point(controlPointAX, controlPointAY)
    this.controlB = new Point(controlPointBX, controlPointBY)
    this.end = new Point(endPointX, endPointY)
  }

}

Grid 只是曲线的容器。

class Grid {
  constructor (topSide, rightSide, bottomSide, leftSide, horizontalCuts, verticalCuts) {
    this.topSide = topSide
    this.rightSide = rightSide
    this.bottomSide = bottomSide
    this.leftSide = leftSide

    // These define how we want to slice our shape. Just ignore them for now
    this.verticalCuts = verticalCuts
    this.horizontalCuts = horizontalCuts
  }
}

让我们用相同的形状填充它。

let grid = new Grid(
  new Curve(50, 50, 100, 100, 400, 100, 450, 50),
  new Curve(450, 50, 475, 250, 475, 250, 450, 450),
  new Curve(450, 450, 250, 300, 250, 300, 50, 450),
  new Curve(50, 450, 150, 100, 150, 250, 50, 50),
  8,
  6
)

寻找交点

intersection points

显然你已经实现了它using the t approach (as opposed to true curve splice length)所以我把它放在这里只是为了完整起见。

请注意 cuts 是您将获得的交点(红点)的实际数量。也就是说,起点和终点不存在(但通过对 cut() 进行少量编辑,它们可以存在)

function cut (cuts, callback) {
  cuts++
  for (let j = 1; j < cuts; j++) {
    const t = j / cuts
    callback(t)
  }
}

class Curve {

// ...

  getIntersectionPoints (cuts) {
    let points = []
    cut(cuts, (t) => {
      points.push(new Point(this.x(t), this.y(t)))
    })
    return points
  }

  x (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.start.x +
            3 * ((1 - t) * (1 - t)) * t * this.controlA.x +
            3 * (1 - t) * (t * t) * this.controlB.x +
            (t * t * t) * this.end.x
  }

  y (t) {
    return ((1 - t) * (1 - t) * (1 - t)) * this.start.y +
            3 * ((1 - t) * (1 - t)) * t * this.controlA.y +
            3 * (1 - t) * (t * t) * this.controlB.y +
            (t * t * t) * this.end.y
  }
}

寻找 split 曲线

function lerp (from, to, t) {
  return from * (1.0 - t) + (to * t)
}

class Curve {

// ...

  getSplitCurves (cuts, oppositeCurve, fromCurve, toCurve) {
    let curves = []
    cut(cuts, (t) => {
      let start = new Point(this.x(t), this.y(t))
      // NOTE1: We must go backwards
      let end = new Point(oppositeCurve.x(1 - t), oppositeCurve.y(1 - t))
      let controlA = new Point(
        // NOTE2: Interpolate control points
        lerp(fromCurve.controlA.x, toCurve.controlA.x, t),
        lerp(fromCurve.controlA.y, toCurve.controlA.y, t)
      )
      let controlB = new Point(
        // NOTE2: Interpolate control points
        lerp(fromCurve.controlB.x, toCurve.controlB.x, t),
        lerp(fromCurve.controlB.y, toCurve.controlB.y, t)
      )
      curves.push(new Curve(
        start.x, start.y,
        controlA.x, controlA.y,
        controlB.x, controlB.y,
        end.x, end.y
      ))
    })
    return curves
  }
}

上面的代码有一些可疑的东西。

注意 1:由于曲线是按照您绘制它们的顺序表示的,因此相对的边朝向不同的方向。例如,顶部是从左到右绘制的,而底部是从右到左绘制的。也许图片会有所帮助:

order of endpoints

NOTE2:这就是我们如何获得贝塞尔曲线分割形状的控制点。 t 在连接对边控制点的线段上进行插值。

这是那些片段。它们的端点是各自曲线的控制点。

control point segments Inkscape screenshot

这是我们渲染曲线时的最终结果: grid

您可以在 Codepen here 上查看实时示例和完整代码.

从这里去哪里

更多路口

这显然不是最终结果。我们仍然需要找到生成曲线的交点。然而,找到两条贝塞尔曲线的交点并非易事。 这是一个 nice StackOverflow answer关于将带您进入此主题的主题 neat library这将为您完成繁重的工作(查看 bezier3bezier3()code,您就会明白)

分割曲线

找到交点后,您将想要找到at which t they occur。 .你问为什么 t?所以你可以split the curve .

实际最终结果

最后,您需要选择曲线的那些部分并将它们排列以表示网格的各个字段。

如您所见,您还有很长的路要走,我只走了其中的一小部分(并且仍然设法写了一个冗长的答案:D)。

关于javascript - 四面二维形状的分割,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50784235/

相关文章:

javascript - Chrome 和 Firefox 中的 SVG 文本对齐不一致

css - 在网格中格式化 div 高度

javascript - 我想使用 javascript 在 DOM 上附加 `onclick`

javascript - 在 SVG 中嵌入工具提示时出现问题

javascript - webpack 不生成 bundle.js

css - 图像上缩进的透明箭头/三 Angular 形

php - 2 表MYSQL PHP查询并应用PHP填充HTML网格显示

javascript - 将 vtype 添加到网格 EXT-JS 中的列

javascript - 从多个 div 获取属性以切换类

javascript - SAPUI5 表排序不起作用