How to resolve the algorithm Bézier curves/Intersections step by step in the Go programming language

Published on 12 May 2024 09:40 PM
#Go

How to resolve the algorithm Bézier curves/Intersections step by step in the Go programming language

Table of Contents

Problem Statement

You are given two planar quadratic Bézier curves, having control points

( − 1 , 0 ) , ( 0 , 10 ) , ( 1 , 0 )

{\displaystyle (-1,0),(0,10),(1,0)}

and

( 2 , 1 ) , ( − 8 , 2 ) , ( 2 , 3 )

{\displaystyle (2,1),(-8,2),(2,3)}

, respectively. They are parabolas intersecting at four points, as shown in the following diagram:

The task is to write a program that finds all four intersection points and prints their

( x , y )

{\displaystyle (x,y)}

coordinates. You may use any algorithm you know of or can think of, including any of those that others have used.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Bézier curves/Intersections step by step in the Go programming language

Overview

This code addresses a problem in computational geometry: finding the intersections between two planar quadratic Bézier curves. These curves are defined by three control points, and the overall idea is to subdivide the curves into smaller segments until the rectangles containing these segments overlap, suggesting a potential intersection. If the overlap is small enough, the midpoint of the overlapping rectangle is considered an intersection point, but with a tolerance to avoid duplicates.

Data Structures

  • point: Represents a 2D point with x and y coordinates
  • quadSpline: A parametric spline representing a quadratic curve segment
  • quadCurve: A parametric planar curve composed of two quadSpline curves (x and y components)

Key Functions

  • subdivideQuadSpline: Subdivides a quadSpline into two new quadSpline curves based on a parameter t.
  • subdivideQuadCurve: Subdivides a quadCurve into two new quadCurve curves based on a parameter t.
  • rectsOverlap: Checks if two rectangles (defined by their minimum and maximum x and y values) overlap.
  • testIntersect: Tests if two quadCurve curves intersect, updating a flag indicating if the intersection can be excluded or accepted (found), and storing the intersection point if found.
  • seemsToBeDuplicate: Checks if a given point is close enough to any existing intersection points (within a specified tolerance) to be considered a duplicate.
  • findIntersects: Divides the quadCurve curves into smaller segments until potential intersections are found.

Main Function

The main function:

  • Defines two quadCurve curves (p and q).
  • Defines a tolerance threshold (tol) and a spacing threshold to avoid duplicate intersections (spacing).
  • Invokes findIntersects to find the intersections between the curves.
  • Prints the found intersection points.

Usage

Imagine two quadratic Bézier curves, each defined by three control points. This code repeatedly subdivides both curves until their bounding rectangles overlap within a certain tolerance. If the overlap is small enough, it considers the midpoint of the overlapping rectangle an intersection point. It also checks for duplicate intersections within a specified tolerance to avoid reporting the same intersection multiple times.

Example

Consider the following two quadratic Bézier curves:

Curve p:

  • Control points: (-1, 0), (0, 10), (1, 0)

Curve q:

  • Control points: (2, -8), (2, 3)

Using a tolerance and spacing threshold of 0.0000001 and 0.000001 respectively, the program would identify and print the following intersection point:

(0.333333, 2.666667)

Source code in the go programming language

/* The control points of a planar quadratic Bézier curve form a
   triangle--called the "control polygon"--that completely contains
   the curve. Furthermore, the rectangle formed by the minimum and
   maximum x and y values of the control polygon completely contain
   the polygon, and therefore also the curve.

   Thus a simple method for narrowing down where intersections might
   be is: subdivide both curves until you find "small enough" regions
   where these rectangles overlap.
*/

package main

import (
    "fmt"
    "log"
    "math"
)

type point struct {
    x, y float64
}

type quadSpline struct { // Non-parametric spline.
    c0, c1, c2 float64
}

type quadCurve struct { // Planar parametric spline.
    x, y quadSpline
}

// Subdivision by de Casteljau's algorithm.
func subdivideQuadSpline(q quadSpline, t float64, u, v *quadSpline) {
    s := 1.0 - t
    c0 := q.c0
    c1 := q.c1
    c2 := q.c2
    u.c0 = c0
    v.c2 = c2
    u.c1 = s*c0 + t*c1
    v.c1 = s*c1 + t*c2
    u.c2 = s*u.c1 + t*v.c1
    v.c0 = u.c2
}

func subdivideQuadCurve(q quadCurve, t float64, u, v *quadCurve) {
    subdivideQuadSpline(q.x, t, &u.x, &v.x)
    subdivideQuadSpline(q.y, t, &u.y, &v.y)
}

// It is assumed that xa0 <= xa1, ya0 <= ya1, xb0 <= xb1, and yb0 <= yb1.
func rectsOverlap(xa0, ya0, xa1, ya1, xb0, yb0, xb1, yb1 float64) bool {
    return (xb0 <= xa1 && xa0 <= xb1 && yb0 <= ya1 && ya0 <= yb1)
}

func max3(x, y, z float64) float64 { return math.Max(math.Max(x, y), z) }
func min3(x, y, z float64) float64 { return math.Min(math.Min(x, y), z) }

// This accepts the point as an intersection if the boxes are small enough.
func testIntersect(p, q quadCurve, tol float64, exclude, accept *bool, intersect *point) {
    pxmin := min3(p.x.c0, p.x.c1, p.x.c2)
    pymin := min3(p.y.c0, p.y.c1, p.y.c2)
    pxmax := max3(p.x.c0, p.x.c1, p.x.c2)
    pymax := max3(p.y.c0, p.y.c1, p.y.c2)

    qxmin := min3(q.x.c0, q.x.c1, q.x.c2)
    qymin := min3(q.y.c0, q.y.c1, q.y.c2)
    qxmax := max3(q.x.c0, q.x.c1, q.x.c2)
    qymax := max3(q.y.c0, q.y.c1, q.y.c2)

    *exclude = true
    *accept = false
    if rectsOverlap(pxmin, pymin, pxmax, pymax, qxmin, qymin, qxmax, qymax) {
        *exclude = false
        xmin := math.Max(pxmin, qxmin)
        xmax := math.Min(pxmax, pxmax)
        if xmax < xmin {
            log.Fatalf("Assertion failure: %f < %f\n", xmax, xmin)
        }
        if xmax-xmin <= tol {
            ymin := math.Max(pymin, qymin)
            ymax := math.Min(pymax, qymax)
            if ymax < ymin {
                log.Fatalf("Assertion failure: %f < %f\n", ymax, ymin)
            }
            if ymax-ymin <= tol {
                *accept = true
                intersect.x = 0.5*xmin + 0.5*xmax
                intersect.y = 0.5*ymin + 0.5*ymax
            }
        }
    }
}

func seemsToBeDuplicate(intersects []point, xy point, spacing float64) bool {
    seemsToBeDup := false
    i := 0
    for !seemsToBeDup && i != len(intersects) {
        pt := intersects[i]
        seemsToBeDup = math.Abs(pt.x-xy.x) < spacing && math.Abs(pt.y-xy.y) < spacing
        i++
    }
    return seemsToBeDup
}

func findIntersects(p, q quadCurve, tol, spacing float64) []point {
    var intersects []point
    type workset struct {
        p, q quadCurve
    }
    workload := []workset{workset{p, q}}

    // Quit looking after having emptied the workload.
    for len(workload) > 0 {
        l := len(workload)
        work := workload[l-1]
        workload = workload[0 : l-1]
        var exclude, accept bool
        intersect := point{0, 0}
        testIntersect(work.p, work.q, tol, &exclude, &accept, &intersect)
        if accept {
            // To avoid detecting the same intersection twice, require some
            // space between intersections.
            if !seemsToBeDuplicate(intersects, intersect, spacing) {
                intersects = append(intersects, intersect)
            }
        } else if !exclude {
            var p0, p1, q0, q1 quadCurve
            subdivideQuadCurve(work.p, 0.5, &p0, &p1)
            subdivideQuadCurve(work.q, 0.5, &q0, &q1)
            workload = append(workload, workset{p0, q0})
            workload = append(workload, workset{p0, q1})
            workload = append(workload, workset{p1, q0})
            workload = append(workload, workset{p1, q1})
        }
    }
    return intersects
}

func main() {
    var p, q quadCurve
    p.x = quadSpline{-1.0, 0.0, 1.0}
    p.y = quadSpline{0.0, 10.0, 0.0}
    q.x = quadSpline{2.0, -8.0, 2.0}
    q.y = quadSpline{1.0, 2.0, 3.0}
    tol := 0.0000001
    spacing := tol * 10
    intersects := findIntersects(p, q, tol, spacing)
    for _, intersect := range intersects {
        fmt.Printf("(% f, %f)\n", intersect.x, intersect.y)
    }
}


  

You may also check:How to resolve the algorithm Determine if two triangles overlap step by step in the Pascal programming language
You may also check:How to resolve the algorithm Speech synthesis step by step in the BASIC256 programming language
You may also check:How to resolve the algorithm Sequence of non-squares step by step in the Eiffel programming language
You may also check:How to resolve the algorithm Call an object method step by step in the Ursa programming language
You may also check:How to resolve the algorithm Haversine formula step by step in the Raku programming language