베지어 곡선을 동일한 세그먼트로 나누기
종속성
이 자습서에서는 Go 프로그래밍 언어와 Pixel 게임 라이브러리를 사용합니다. 여기서 설명하는 알고리즘은 모든 종류의 곡선 궤적에 적용할 수 있지만 임의의 궤적을 생성하고 수동으로 편집할 수 있을 만큼 충분히 유연하기 때문에 Bezier curves을 사용하겠습니다. gonum 패키지를 사용하여 베지어 곡선을 만들 수 있습니다. 이것이 가져오기 섹션입니다.
import (
"fmt"
"time"
"github.com/faiface/pixel"
"github.com/faiface/pixel/imdraw"
"github.com/faiface/pixel/pixelgl"
colors "golang.org/x/image/colornames"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/tools/bezier"
"gonum.org/v1/plot/vg"
)
보시다시피 사전 정의된 색상에 대해
colornames
패키지도 사용합니다.곡선 만들기
먼저 새로운 곡선을 만들어야 합니다. 4개의 제어점이 있는 3차 베지어 곡선을 사용합니다.
controlPoints := []vg.Point{
{X: 0.45, Y: 0.328},
{X: 1.403, Y: 0.12},
{X: 0.62, Y: 1.255},
{X: 1.521, Y: 0.593},
}
보시다시피 숫자가 매우 작습니다. 걱정하지 마십시오. 곡선의 크기가 조정되고 화면에서 잘 보이도록 변환됩니다.
또한 나중에 필요한 상수도 있습니다.
const (
screenWidth = 1280
screenHeight = 720
offsetX float64 = 400
offsetY float64 = 300
scaleX float64 = 300
scaleY float64 = 300
numberOfSegments = 10
epsilon float64 = 0.001
dt float64 = 0.5
)
제어점 외부에 새 곡선을 만들려면:
// Form the curve.
curve := bezier.New(controlPoints...)
이제 곡선의 점을 계산할 시간입니다. 베지어 곡선의 단일 점을 얻으려면 매개변수 t(0 ≤ t ≤ 1)를 사용해야 합니다. 메소드
(c Curve) Point(t float64) vg.Point
는 매개변수에 해당하는 곡선의 점을 반환합니다. 예를 들어 t = 0.5인 경우 메서드는 곡선의 중간점을 반환합니다.가능한 한 많은 베지어 곡선 점을 얻는 것이 좋습니다. 이를 위해
dt
단계를 사용합니다. 단계가 적을수록 더 많은 포인트를 얻을 수 있습니다.points := make(plotter.XYs, 0)
for t := 0.0; t < 100.0; t += dt {
point := curve.Point(t / 100.0)
points = append(points, plotter.XY{
X: float64(point.X)*scaleX + offsetX,
Y: float64(point.Y)*scaleY + offsetY})
}
곡선 그리기
곡선의 점을 그리기 위해 새 창과
IMDraw
개체를 만듭니다.cfg := pixelgl.WindowConfig{
Title: "Bezier curve",
Bounds: pixel.R(0, 0, screenWidth, screenHeight),
}
win, err := pixelgl.NewWindow(cfg)
handleError(err)
imd := imdraw.New(nil)
또한 FPS 카운터를 설정하고 싶습니다.
fps := 0
perSecond := time.Tick(time.Second)
이제 애플리케이션 메인 루프에 들어가 각 프레임마다 곡선을 그릴 준비가 되었습니다.
for !win.Closed() {
win.Clear(colors.White)
imd.Clear()
// Draw the curve and other things.
imd.Color = colors.Red
for _, point := range points {
imd.Push(gonumToPixel(point))
imd.Circle(1, 1)
}
imd.Draw(win)
win.Update()
// Show FPS in the window title.
fps++
select {
case <-perSecond:
win.SetTitle(fmt.Sprintf("%s | FPS: %d", cfg.Title, fps))
fps = 0
default:
}
}
그런데 위에 쓰여진 모든 내용은
run()
에서 호출되는 main()
함수 안에 있어야 합니다.func main() {
pixelgl.Run(run)
}
메인 고루틴이 다른 스레드에 할당되지 않도록 하는 데 필요합니다.
이제 우리가 얻은 것을 보자:
보시다시피 곡선의 인접한 두 점 사이의 거리가 항상 같지는 않습니다. 또한 그래프는 곡선의 첫 번째 및 마지막 제어점에 가까워질수록 밀도가 특히 낮아집니다.
사실, 그것은 우리가 보고 싶은 것이 아닙니다. 모든 점이 곡선을 따라 균등하게 분산되어야 합니다. 이에 도달하려면 곡선을 동일한 세그먼트로 나누는 알고리즘을 도입해야 합니다.
이제 새 함수
getSegmentPoints(points plotter.XYs, numberOfSegments int) []pixel.Vec
를 정의해 보겠습니다.곡선 점을 선으로 연결:
// Create lines out of bezier
// curve points.
lines := []pixel.Line{}
for i := 0; i < len(points)-1; i++ {
line := pixel.L(gonumToPixel(points[i]),
gonumToPixel(points[i+1]))
lines = append(lines, line)
}
참고: 기능
gonumToPixel(xy plotter.XY) pixel.Vec
gonum
벡터를 pixel
벡터로 변환합니다.선의 총 길이를 계산합니다.
// Compute the length
// of the bezier curve
// interpolated with lines.
length := 0.0
for _, line := range lines {
length += line.Len()
}
단일 세그먼트의 길이인 단계를 계산합니다.
step := length / float64(numberOfSegments)
글쎄, 우리는 그것을 다각형 체인을 분할하는 작업으로 줄였습니다. 더 많은 곡선 점을 얻을수록 원래 곡선의 분할이 더 정확해집니다.
먼저 몇 가지 초기화를 수행해야 합니다.
segmentPoints := []pixel.Vec{}
lastLine := 0
lastPoint := lines[0].A
segmentPoints = append(segmentPoints, lastPoint)
따라서
lastPoint
는 마지막으로 형성된 세그먼트의 마지막 지점입니다. lastLine
는 마지막 점이 포함된 라인의 인덱스입니다. 이제 루프로: for i := 0; i < numberOfSegments; i++ {
subsegments := []pixel.Line{}
startLine := pixel.L(lastPoint, lines[lastLine].B)
subsegments = append(subsegments, startLine)
localLength := startLine.Len()
for step-localLength > epsilon {
line := lines[lastLine+1]
subsegments = append(subsegments, line)
localLength += line.Len()
lastLine++
}
line := lines[lastLine]
if localLength > step {
difference := localLength - step
t := difference / line.Len()
lastPoint = pixel.V(t*line.A.X+(1-t)*line.B.X,
t*line.A.Y+(1-t)*line.B.Y)
} else {
lastPoint = line.B
lastLine++
}
segmentPoints = append(segmentPoints, lastPoint)
}
이 루프에서 총 길이가 세그먼트 길이를 초과할 때까지 라인을 선택합니다. 이 경우 선형 보간을 사용하여 마지막 줄에 있는 분할 지점을 계산합니다. 라인의 끝과 일치하면 마지막 라인 카운터를 증가시키고 다음 라인에서 새로운 반복을 시작합니다.
이제 곡선을 10개의 동일한 세그먼트로 나누겠습니다.
글쎄요, 그게 훨씬 낫습니다. 읽어 주셔서 감사합니다. 다음은 source code 입니다.
Reference
이 문제에 관하여(베지어 곡선을 동일한 세그먼트로 나누기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/zergon321/dividing-a-bezier-curve-into-equal-segments-2hh8텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)