2

我正在尝试制作一个需要 4 个参数的贝塞尔函数:

> import Diagrams.Backend.SVG.CmdLine
> import Diagrams.Prelude
> import Control.Applicative

> bezier4 x1 c1 c2 x2 = bezier3 (c1 ^-^ x1) (c2 ^-^ x1) (x2 ^-^ x1) # translate x1
> lineBtwPoints p1 p2 = fromOffsets [p2 ^-^ p1] # translate p1
> illustrateBézier x1 c1 c2 x2
>     =  endpt  # translate x1
>     <> endpt  # translate x2
>     <> ctrlpt # translate c1
>     <> ctrlpt # translate c2
>     <> l1
>     <> l2
>     <> fromSegments [bezier4 x1 c1 c2 x2]
>   where
>     dashed  = dashingN [0.03,0.03] 0
>     endpt   = circle 0.05 # fc red  # lw none
>     ctrlpt  = circle 0.05 # fc blue # lw none
>     l1      = lineBtwPoints x1 c1 # dashed
>     l2      =  lineBtwPoints x2 c2 # dashed
>
> x1      = r2 (0.3, 0.5) :: R2
> x2      = r2 (3,-1) :: R2         -- endpoint
> [c1,c2] = map r2 [(1,2), (3,0)]   -- control points
> example = illustrateBézier x1 c1 c2 x2

但结果似乎不是我想要的:

在此处输入图像描述

4

1 回答 1

4

首先让我们解决名称。通常bezier4是给出四次贝塞尔曲线段的函数的名称。更好的名称是fixedBezier3,更好的形式是使用Points参数而不是向量。事实上,这个函数作为FCubic数据FixedSegment类型存在。

如果我们查看类型,bezier4我们可以看到哪里出了问题:

bezier4 x1 c1 c2 x2 = bezier3 (c1 ^-^ x1) (c2 ^-^ x1) (x2 ^-^ x1) # translate x1

ghci> :t bezier4
bezier4'
  :: (Data.Basis.HasBasis v,
      Data.MemoTrie.HasTrie (Data.Basis.Basis v)) =>
     v -> v -> v -> v -> Segment Closed v

重要的部分是结果是Segment Closed v. 阅读以下文档Segment

片段是平移不变的,也就是说,它们没有特定的“位置”并且不受翻译的影响。但是,它们会受到其他变换(例如旋转和缩放)的影响。

最后的翻译bezier4不会有任何影响,因为类型Segment不能表达具有位置的值,它只是表达“形状”和位移。我们可以在 GHCi 中看到这一点:

ghci> bezier4 x1 c1 c2 x2
Cubic (0.7 ^& 1.5) (2.7 ^& (-0.5)) (OffsetClosed (2.7 ^& (-1.5)))
ghci> bezier4' x1 c1 c2 x2 # translate (r2 (1000,1000))
Cubic (0.7 ^& 1.5) (2.7 ^& (-0.5)) (OffsetClosed (2.7 ^& (-1.5)))

一种解决方法是将bezier4结果类型设置为Located (Segment Closed v). 使用这种类型,我们至少可以表达想要的曲线:

bezier4' x1 c1 c2 x2 = bezier3 (c1 ^-^ x1) (c2 ^-^ x1) (x2 ^-^ x1) `at` (0 .+^ x1)

ghci> bezier4' x1 c1 c2 x2
Loc { loc = P (0.3 ^& 0.5)
    , unLoc = Cubic (0.7 ^& 1.5) (2.7 ^& (-0.5)) (OffsetClosed (2.7 ^& (-1.5)))
    }

请注意,我们得到了与以前相同的段,但现在我们有了一个位置。

ghci> bezier4' x1 c1 c2 x2 # translate (r2 (1000,1000))
Loc { loc = P (1000.3 ^& 1000.5)
    , unLoc = Cubic (0.7 ^& 1.5) (2.7 ^& (-0.5)) (OffsetClosed (2.7 ^& (-1.5)))
    }

不过,我们在这一点上有点卡住了。定位段并不是特别有趣,因为通常我们希望将许多段串在一起作为Trail. 定位的细分列表fromLocSegments将我们带到那里,我们可以使用:

fromLocSegments :: TrailLike t => Located [Segment Closed (V t)] -> t

现在我们有了一些可以工作的东西(在使用站点进行了额外的更改bezier4):

bezier4 x1 c1 c2 x2 = fromSegments [bezier3 (c1 ^-^ x1) (c2 ^-^ x1) (x2 ^-^ x1)]
                    # translate x1

请注意,我们不能将此函数的输出与其他段串在一起以形成更长的轨迹。Diagrams 选择使用带有Segment, Trail, , 的强类型Located,并且Path只允许与输出中表达的内容精确匹配的值(“含义”)。例如,假设我们要写 fromFixedSegments

fromFixedSegments :: TrailLike t => [FizedSegment (V t)] -> t

每个立方段将有四个点,但结果将是一条具有“无间隙”含义的轨迹。为此,我们将不得不丢弃相邻段的第一个或最后一个点的信息。这里没有好的选择!

于 2015-03-29T00:42:52.453 回答