Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/aa-stroke/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 25 kB image not shown  

Quelle  bezierflattener.rs   Sprache: unbekannt

 
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#![allow(non_snake_case)]

use std::ops::{Sub, Mul, Add, AddAssign, SubAssign, MulAssign, Div};

macro_rules! IFC {
    ($e: expr) => {
        assert_eq!($e, S_OK);
    }
}

pub type HRESULT = i32;

pub const S_OK: i32 = 0;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct GpPointR {
    pub x: f32,
    pub y: f32
}

impl Sub for GpPointR {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self::Output {
        GpPointR { x: self.x - rhs.x, y: self.y - rhs.y }
    }
}

impl Add for GpPointR {
    type Output = Self;

    fn add(self, rhs: Self) -> Self::Output {
        GpPointR { x: self.x + rhs.x, y: self.y + rhs.y }
    }
}

impl AddAssign for GpPointR {
    fn add_assign(&mut self, rhs: Self) {
        *self = *self + rhs;
    }
}

impl SubAssign for GpPointR {
    fn sub_assign(&mut self, rhs: Self) {
        *self = *self - rhs;
    }
}

impl MulAssign<f32> for GpPointR {
    fn mul_assign(&mut self, rhs: f32) {
        *self = *self * rhs;
    }
}


impl Mul<f32> for GpPointR {
    type Output = Self;

    fn mul(self, rhs: f32) -> Self::Output {
        GpPointR { x: self.x * rhs, y: self.y * rhs }
    }
}

impl Div<f32> for GpPointR {
    type Output = Self;

    fn div(self, rhs: f32) -> Self::Output {
        GpPointR { x: self.x / rhs, y: self.y / rhs }
    }
}


impl Mul for GpPointR {
    type Output = f32;

    fn mul(self, rhs: Self) -> Self::Output {
        self.x * rhs.x +  self.y * rhs.y
    }
}

impl GpPointR {
    pub fn ApproxNorm(&self) -> f32 {
        self.x.abs().max(self.y.abs())
    }
    pub fn Norm(&self) -> f32 {
        self.x.hypot(self.y)
    }
}

// Relative to this is relative to the tolerance squared. In other words, a vector
// whose length is less than .01*tolerance will be considered 0
const  SQ_LENGTH_FUZZ: f32 = 1.0e-4;

// Some of these constants need further thinking

//const FUZZ: f64 = 1.0e-6;           // Relative 0
// Minimum allowed tolerance - should probably be adjusted to the size of the
// geometry we are rendering, but for now ---

/* 
const FUZZ_DOUBLE: f64 = 1.0e-12;           // Double-precision relative 0
const MIN_TOLERANCE: f64 = 1.0e-6;
const DEFAULT_FLATTENING_TOLERANCE: f64 = 0.25;*/
const TWICE_MIN_BEZIER_STEP_SIZE: f32 = 1.0e-3; // The step size in the Bezier flattener should
                                                 // never go below half this amount.
//+-----------------------------------------------------------------------------
//

//
//  $TAG ENGR

//      $Module:    win_mil_graphics_geometry
//      $Keywords:
//
//  $Description:
//      Definition of CBezierFlattener.
//
//  $ENDTAG
//
//------------------------------------------------------------------------------

//+-----------------------------------------------------------------------------
//
//  Class:
//      CFlatteningSink
//
//  Synopsis:
//      Callback interface for the results of curve flattening
//
//  Notes:
//      Methods are implemented rather than pure, for callers who do not use all
//      of them.
//
//------------------------------------------------------------------------------
//
//  Definition of CFlatteningSink
//
//------------------------------------------------------------------------------
/* 
struct CFlatteningSink
{
public:
    CFlatteningSink() {}

    virtual ~CFlatteningSink() {}

    virtual HRESULT Begin(
        __in_ecount(1) const GpPointR &)
            // First point (transformed)
    {
        // Do nothing stub, should not be called
        RIP("Base class Begin called");
        return E_NOTIMPL;
    }

    virtual HRESULT AcceptPoint(
        __in_ecount(1) const GpPointR &pt,
            // The point
        IN GpReal t,
            // Parameter we're at
        __out_ecount(1) bool &fAborted)
            // Set to true to signal aborting
    {
        UNREFERENCED_PARAMETER(pt);
        UNREFERENCED_PARAMETER(t);
        UNREFERENCED_PARAMETER(fAborted);

        // Do nothing stub, should not be called
        RIP("Base class AcceptPoint called");
        return E_NOTIMPL;
    }

    virtual HRESULT AcceptPointAndTangent(
        __in_ecount(1) const GpPointR &,
            //The point
        __in_ecount(1) const GpPointR &,
            //The tangent there
        IN bool fLast)         // Is this the last point on the curve?
    {
        // Do nothing stub, should not be called
        RIP("Base class AcceptPointAndTangent called");
        return E_NOTIMPL;
    }
};



*/
#[derive(Clone, Debug)]

pub struct CBezier
{
    /* 
public:
    CBezier()
    {
    }

    CBezier(
        __in_ecount(4) const GpPointR *pPt)
            // The defining Bezier points
    {
        Assert(pPt);
        memcpy(&m_ptB, pPt, 4 * sizeof(GpPointR)); 
    }

    CBezier(
        __in_ecount(1) const CBezier &other)
            // Another Bezier to copy
    {
        Copy(other); 
    }

    void Copy(
        __in_ecount(1) const CBezier &other)
            // Another Bezier to copy
    {
        memcpy(&m_ptB, other.m_ptB, 4 * sizeof(GpPointR)); 
    }

    void Initialize(
        __in_ecount(1) const GpPointR &ptFirst,
            // The first Bezier point
        __in_ecount(3) const GpPointR *pPt)
            // The remaining 3 Bezier points
    {
        m_ptB[0] = ptFirst;
        memcpy(m_ptB + 1, pPt, 3 * sizeof(GpPointR)); 
    }

    __outro_ecount(1) const GpPointR &GetControlPoint(__range(0, 3) UINT i) const
    {
        Assert(i < 4);
        return m_ptB[i];
    }

    __outro_ecount(1) const GpPointR &GetFirstPoint() const
    {
        return m_ptB[0];
    }
    
    __outro_ecount(1) const GpPointR &GetLastPoint() const
    {
        return m_ptB[3];
    }

    void GetPoint(
        _In_ double t,
            // Parameter value
        __out_ecount(1) GpPointR &pt) const; 
            // Point there

    void GetPointAndDerivatives(
        __in double t,
            // Parameter value
        __out_ecount(3) GpPointR *pValues) const;
                // Point, first derivative and second derivative there

    void TrimToStartAt(
        IN double t);             // Parameter value
        
    void TrimToEndAt(
        IN double t);             // Parameter value

    bool TrimBetween(
        __in double rStart,
            // Parameter value for the new start, must be between 0 and 1
        __in double rEnd);
            // Parameter value for the new end, must be between 0 and 1

    bool operator ==(__in_ecount(1) const CBezier &other) const
    {
        return (m_ptB[0] == other.m_ptB[0]) &&
               (m_ptB[1] == other.m_ptB[1]) &&
               (m_ptB[2] == other.m_ptB[2]) &&
               (m_ptB[3] == other.m_ptB[3]);
    }

    void AssertEqualOrNaN(__in_ecount(1) const CBezier &other) const
    {
        m_ptB[0].AssertEqualOrNaN(other.m_ptB[0]);
        m_ptB[1].AssertEqualOrNaN(other.m_ptB[1]);
        m_ptB[2].AssertEqualOrNaN(other.m_ptB[2]);
        m_ptB[3].AssertEqualOrNaN(other.m_ptB[3]);
    }

protected:
    */
    // Data
    m_ptB: [GpPointR; 4],
            // The defining Bezier points
}

impl CBezier {
    pub fn new(curve: [GpPointR; 4]) -> Self {
        Self { m_ptB: curve }
    }

    pub fn is_degenerate(&self) -> bool {
        self.m_ptB[0] == self.m_ptB[1] &&
            self.m_ptB[0] == self.m_ptB[2] &&
            self.m_ptB[0] == self.m_ptB[3]
    }
}

pub trait CFlatteningSink {
    fn FirstTangent(&mut self, vecTangent: Option<GpPointR>);
    fn AcceptPointAndTangent(&mut self,
        pt: &GpPointR,
            // The point
        vec: &GpPointR,
            // The tangent there
        fLast: bool
            // Is this the last point on the curve?
        ) -> HRESULT;

        fn AcceptPoint(&mut self,
            pt: &GpPointR,
                // The point
            t: f32,
                // Parameter we're at
            fAborted: &mut bool,
            lastPoint: bool
        ) -> HRESULT;
}

//+-----------------------------------------------------------------------------
//
//  Class:
//      CBezierFlattener
//
//  Synopsis:
//      Generates a polygonal apprximation to a given Bezier curve
//
//------------------------------------------------------------------------------
pub struct CBezierFlattener<'a>
{
    bezier: CBezier,
        // Flattening defining data
        m_pSink: &'a mut dyn CFlatteningSink,           // The recipient of the flattening data
        m_rTolerance: f32,       // Prescribed tolerance
        m_fWithTangents: bool,    // Generate tangent vectors if true
        m_rQuarterTolerance: f32,// Prescribed tolerance/4 (for doubling the step)
        m_rFuzz: f32,            // Computational zero
    
        // Flattening working data
        m_ptE: [GpPointR; 4],           // The moving basis of the curve definition
        m_cSteps: i32,           // The number of steps left to the end of the curve
        m_rParameter: f32,       // Parameter value
        m_rStepSize: f32,        // Steps size in parameter domain
}
impl<'a> CBezierFlattener<'a> {
    /*fn new(
        __in_ecount_opt(1) CFlatteningSink *pSink,
            // The reciptient of the flattened data
        IN GpReal          rTolerance)
            // Flattening tolerance 
    {
        Initialize(pSink, rTolerance);
    }*/
/* 
    void SetTarget(__in_ecount_opt(1) CFlatteningSink *pSink)
    {
        m_pSink = pSink;
    }

    void Initialize(
        __in_ecount_opt(1) CFlatteningSink *pSink,
            // The reciptient of the flattened data
        IN GpReal rTolerance);
        // Flattening tolerance 

    void SetPoint(
        __in UINT i,
            // index of the point (must be between 0 and 3)
        __in_ecount(1) const GpPointR &pt)
            // point value
    {
        Assert(i < 4);
        m_ptB[i] = pt;
    }

    HRESULT GetFirstTangent(
        __out_ecount(1) GpPointR &vecTangent) const;
            // Tangent vector there

    GpPointR GetLastTangent() const;

    HRESULT Flatten( 
        IN bool fWithTangents);   // Return tangents with the points if true

private:
    // Disallow copy constructor
    CBezierFlattener(__in_ecount(1) const CBezierFlattener &)
    {
        RIP("CBezierFlattener copy constructor reached.");
    }

protected:
*/
/*  fn Step(
        __out_ecount(1) bool &fAbort);   // Set to true if flattening should be aborted

    fn HalveTheStep();

    fn TryDoubleTheStep();*/

}




// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.


//+-----------------------------------------------------------------------------
//

//
//  $TAG ENGR

//      $Module:    win_mil_graphics_geometry
//      $Keywords:
//
//  $Description:
//      Implementation of CBezierFlattener.
//
//  $ENDTAG
//
//------------------------------------------------------------------------------

impl<'a> CBezierFlattener<'a> {
/////////////////////////////////////////////////////////////////////////////////
//
//              Implementation of CBezierFlattener

//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::Initialize
//
//  Synopsis:
//      Initialize the sink and tolerance
//
//------------------------------------------------------------------------------
pub fn new(bezier: &CBezier,
    pSink: &'a mut dyn CFlatteningSink,
        // The reciptient of the flattened data
    rTolerance: f32)       // Flattening tolerance
    -> Self 
{
    let mut result = CBezierFlattener {
        bezier: bezier.clone(),
        // Flattening defining data
        m_pSink: pSink,           // The recipient of the flattening data
        m_rTolerance: 0.,       // Prescribed tolerance
        m_fWithTangents: false,    // Generate tangent vectors if true
        m_rQuarterTolerance: 0.,// Prescribed tolerance/4 (for doubling the step)
        m_rFuzz: 0.,            // Computational zero
    
        // Flattening working data
        m_ptE: [GpPointR { x: 0., y: 0.}; 4],           // The moving basis of the curve definition
        m_cSteps: 0,           // The number of steps left to the end of the curve
        m_rParameter: 0.,       // Parameter value
        m_rStepSize: 0.,        // Steps size in parameter domain
    };

    // If rTolerance == NaN or less than 0, we'll treat it as 0.
    result.m_rTolerance = if rTolerance >= 0.0 { rTolerance } else { 0.0 };
    result.m_rFuzz = rTolerance * rTolerance * SQ_LENGTH_FUZZ;
    
    // The error is tested on max(|e2|, |e2|), which represent 6 times the actual error, so:
    result.m_rTolerance *= 6.;
    result.m_rQuarterTolerance = result.m_rTolerance * 0.25;
    result
}

//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::Flatten
//
//  Synopsis:
//      Flatten this curve
//
//  Notes:
                                                                                
// The algorithm is described in detail in the 1995 patent # 5367617 "System and 
// method of hybrid forward differencing to render Bezier splines" to be found 
// on the Microsoft legal dept. web site (LCAWEB).  Additional references are:
//     Lien, Shantz and Vaughan Pratt, "Adaptive Forward Differencing for
//     Rendering Curves and Surfaces", Computer Graphics, July 1987
//     Chang and Shantz, "Rendering Trimmed NURBS with Adaptive Forward
//         Differencing", Computer Graphics, August 1988
//     Foley and Van Dam, "Fundamentals of Interactive Computer Graphics"
//
// The basic idea is to replace the Bernstein basis (underlying Bezier curves) 
// with the Hybrid Forward Differencing (HFD) basis which is more efficient at 
// for flattening.  Each one of the 3 actions - Step, Halve and Double (step 
// size) this basis affords very efficient formulas for computing coefficients
// for the new interval.
//
// The coefficients of the HFD basis are defined in terms of the Bezier 
// coefficients as follows:
//
//          e0 = p0, e1 = p3 - p0, e2 = 6(p1 - 2p2 + p3), e3 = 6(p0 - 2p1 + p2),
//
// but formulas may be easier to understand by going through the power basis 
// representation:  f(t) = a*t + b*t + c * t^2 + d * t^3.
//
//  The conversion is then:            
//                               e0 = a
//                               e1 = f(1) - f(0) = b + c + d
//                               e2 = f"(1) = 2c + 6d
//                               e3 = f"(0) = 2c
//
// This is inverted to:
//                              a = e0
//                              c = e3 / 2
//                              d = (e2 - 2c) / 6 = (e2 - e3) / 6
//                              b = e1 - c - d = e1 - e2 / 6 - e3 / 3
//
// a, b, c, d for the new (halved, doubled or forwarded) interval are derived 
// and then converted to e0, e1, e2, e3 using these relationships.
//
// An exact integer version is implemented in Bezier.h and Bezier.cpp.
//
//------------------------------------------------------------------------------


pub fn Flatten(&mut self,
    fWithTangents: bool)   // Return tangents with the points if true
    -> HRESULT
{

    let hr = S_OK;
    let mut fAbort = false;

    /*if (!self.m_pSink)
    {
        return E_UNEXPECTED;
    }*/

    self.m_fWithTangents = fWithTangents;
    if self.m_fWithTangents {
        self.m_pSink.FirstTangent(self.GetFirstTangent())
    }

    self.m_cSteps = 1;

    self.m_rParameter = 0.;
    self.m_rStepSize = 1.;

    // Compute the HFD basis
    self.m_ptE[0] = self.bezier.m_ptB[0]; 
    self.m_ptE[1] = self.bezier.m_ptB[3] - self.bezier.m_ptB[0]; 
    self.m_ptE[2] = (self.bezier.m_ptB[1] - self.bezier.m_ptB[2] * 2. + self.bezier.m_ptB[3]) * 6.;    // The second derivative at curve end
    self.m_ptE[3] = (self.bezier.m_ptB[0] - self.bezier.m_ptB[1] * 2. + self.bezier.m_ptB[2]) * 6.;    // The second derivative at curve start

    // Determine the initial step size
    self.m_cSteps = 1;
    while ((self.m_ptE[2].ApproxNorm() > self.m_rTolerance)  ||  (self.m_ptE[3].ApproxNorm() > self.m_rTolerance)) &&
           (self.m_rStepSize > TWICE_MIN_BEZIER_STEP_SIZE)
        
    {
        self.HalveTheStep();
    }

    while self.m_cSteps > 1
    {
        IFC!(self.Step(&mut fAbort));
        if fAbort {
            return hr;
        }

        // E[3] was already tested as E[2] in the previous step
        if self.m_ptE[2].ApproxNorm() > self.m_rTolerance &&
            self.m_rStepSize > TWICE_MIN_BEZIER_STEP_SIZE
        {
            // Halving the step once is provably sufficient (see Notes above), so ---
            self.HalveTheStep();
        }
        else
        {
            // --- but the step can possibly be more than doubled, hence the while loop
            while self.TryDoubleTheStep() {
                continue;
            }
        }
    }

    // Last point
    if self.m_fWithTangents
    {
        IFC!(self.m_pSink.AcceptPointAndTangent(&self.bezier.m_ptB[3], &self.GetLastTangent(), true /* last point */));
    }
    else
    {
        IFC!(self.m_pSink.AcceptPoint(&self.bezier.m_ptB[3], 1., &mut fAbort, true));
    }

    return hr;
}
//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::Step
//
//  Synopsis:
//      Step forward on the polygonal approximation of the curve
//
//  Notes:
//      Taking a step means replacing a,b,c,d by coefficients of g(t) = f(t+1). 
//      Express those in terms of a,b,c,d and convert to e0, e1, e2, e3 to get:
//
//       New e0 = e0 + e1
//       New e1 = e1 + e2
//       New e2 = 2e2 - e3
//       New e3 = e2
//
//  The patent application (see above) explains why.
//
//  Getting a tangent vector is a minor enhancement along the same lines:
//      f'(0) = b = 6e1 - e2 - 2e3.
//
//------------------------------------------------------------------------------

fn Step(&mut self,
    fAbort: &mut bool)  -> HRESULT  // Set to true if flattening should be aborted, untouched otherwise
{
    let hr = S_OK;
    
    // Compute the basis for the same curve on the next interval
    let mut pt;

    self.m_ptE[0] += self.m_ptE[1];
    pt = self.m_ptE[2];
    self.m_ptE[1] += pt;
    self.m_ptE[2] += pt;  self.m_ptE[2] -= self.m_ptE[3];
    self.m_ptE[3] = pt;

    // Increment the parameter
    self.m_rParameter += self.m_rStepSize;

    // Generate the start point of the new interval
    if self.m_fWithTangents
    {
        // Compute the tangent there
        pt = self.m_ptE[1] * 6. - self.m_ptE[2] - self.m_ptE[3] * 2.;  //  = twice the derivative at E[0]
        IFC!(self.m_pSink.AcceptPointAndTangent(&self.m_ptE[0], &pt, false /* not the last point */));
    }
    else
    {
        IFC!(self.m_pSink.AcceptPoint(&self.m_ptE[0], self.m_rParameter, fAbort, false));
    }
    
    self.m_cSteps-=1;
    return hr;
}
//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::HalveTheStep
//
//  Synopsis:
//      Halve the size of the step
//
//  Notes:
//      Halving the step means replacing a,b,c,d by coefficients of g(t) =
//      f(t/2). Experss those in terms of a,b,c,d and convert to e0, e1, e2, e3
//      to get:
//
//       New e0 = e0
//       New e1 = (e1 - e2) / 2
//       New e2 = (e2 + e3) / 8
//       New e3 = e3 / 4
//
//  The patent application (see above) explains why.
//
//------------------------------------------------------------------------------
fn HalveTheStep(&mut self)
{
    self.m_ptE[2] += self.m_ptE[3];   self.m_ptE[2] *= 0.125;
    self.m_ptE[1] -= self.m_ptE[2];   self.m_ptE[1] *= 0.5;
    self.m_ptE[3] *= 0.25;

    self.m_cSteps *= 2;  // Double the number of steps left
    self.m_rStepSize *= 0.5;
}
//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::TryDoubleTheStep
//
//  Synopsis:
//      Double the step size if possible within tolerance.
//
//  Notes:
//      Coubling the step means replacing a,b,c,d by coefficients of g(t) =
//      f(2t). Experss those in terms of a,b,c,d and convert to e0, e1, e2, e3
//      to get:
//
//       New e0 = e0
//       New e1 = 2e1 + e2
//       New e2 = 8e2 - 4e3
//       New e3 = 4e3
//
//  The patent application (see above) explains why.  Note also that these
//  formulas are the inverse of those for halving the step.
//
//------------------------------------------------------------------------------
fn
TryDoubleTheStep(&mut self) -> bool
{
    let mut fDoubled = 0 == (self.m_cSteps & 1);
    if fDoubled
    {
        let ptTemp = self.m_ptE[2] * 2. - self.m_ptE[3];

        fDoubled = (self.m_ptE[3].ApproxNorm() <= self.m_rQuarterTolerance) && 
                   (ptTemp.ApproxNorm() <= self.m_rQuarterTolerance);

        if fDoubled
        {
            self.m_ptE[1] *= 2.;  self.m_ptE[1] += self.m_ptE[2];
            self.m_ptE[3] *= 4.;
            self.m_ptE[2] = ptTemp * 4.;

            self.m_cSteps /= 2;      // Halve the number of steps left
            self.m_rStepSize *= 2.;
        }
    }

    return fDoubled;
}
//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::GetFirstTangent
//
//  Synopsis:
//      Get the tangent at curve start
//
//  Return:
//      WGXERR_ZEROVECTOR if the tangent vector has practically 0 length
//
//  Notes:
//      This method can return an error if all the points are bunched together.
//      The idea is that the caller will detect that, abandon this curve, and
//      never call GetLasttangent, which can therefore be presumed to succeed. 
//      The failure here is benign.
//
//------------------------------------------------------------------------------
pub fn GetFirstTangent(&self) -> Option<GpPointR> // Tangent vector there
    
{

    let mut vecTangent = self.bezier.m_ptB[1] - self.bezier.m_ptB[0];
    if vecTangent * vecTangent > self.m_rFuzz
    {
        return Some(vecTangent);  // - we're done
    }
    // Zero first derivative, go for the second
    vecTangent = self.bezier.m_ptB[2] - self.bezier.m_ptB[0];
    if vecTangent * vecTangent > self.m_rFuzz
    {
        return Some(vecTangent);  // - we're done
    }
    // Zero second derivative, go for the third
    vecTangent = self.bezier.m_ptB[3] - self.bezier.m_ptB[0];

    if vecTangent * vecTangent <= self.m_rFuzz
    {
        return None;
    }

    return Some(vecTangent);      // no RRETURN, error is expected
}
//+-----------------------------------------------------------------------------
//
//  Member:
//      CBezierFlattener::GetLastTangent
//
//  Synopsis:
//      Get the tangent at curve end
//
//  Return:
//      The tangent
//
//  Notes:
//      This method has no error return while GetFirstTangent returns
//      WGXERR_ZEROVECTOR if the tangent is zero.  The idea is that we should
//      only fail if all the control points coincide, that should have been
//      detected at GetFirstTangent, and then we should have not be called.
//
//------------------------------------------------------------------------------
fn GetLastTangent(&self) -> GpPointR
{
    let mut vecTangent = self.bezier.m_ptB[3] - self.bezier.m_ptB[2];
    
    // If the curve is degenerate, we should have detected it at curve-start, skipped this curve
    // altogether and not be here.  But the test in GetFirstTangent is for the point-differences
    // 1-0, 2-0 and 3-0, while here it is for points 3-2, 3-1 and 3-0, which is not quite the same.
    // Still, In a disk of radius r no 2 points are more than 2r apart.  The tests are done with
    // squared distance, and m_rFuzz is the minimal accepted squared distance.  GetFirstTangent()
    // succeeded, so there is a pair of points whose squared distance is greater than m_rfuzz.
    // So the squared radius of a disk about point 3 that contains the remaining points must be 
    // at least m_rFuzz/4.  Allowing some margin for arithmetic error:

    let rLastTangentFuzz = self.m_rFuzz/8.;

    if vecTangent * vecTangent <= rLastTangentFuzz
    {
        // Zero first derivative, go for the second
        vecTangent = self.bezier.m_ptB[3] - self.bezier.m_ptB[1];
        if vecTangent * vecTangent <= rLastTangentFuzz
        {
            // Zero second derivative, go for the third
            vecTangent = self.bezier.m_ptB[3] - self.bezier.m_ptB[0];
        }
    }

    debug_assert! (!(vecTangent * vecTangent < rLastTangentFuzz)); // Ignore NaNs

    return vecTangent;
}
}


#[test]
#[cfg_attr(debug_assertions, should_panic)]
fn degenerate_tangent() {
    struct Sink;
    impl CFlatteningSink for Sink {
        fn FirstTangent(&mut self, _: Option<GpPointR>) { }
        fn AcceptPointAndTangent(&mut self,
            _: &GpPointR, _: &GpPointR, _: bool
            ) -> HRESULT {
            return S_OK;
        }

        fn AcceptPoint(&mut self,
            _: &GpPointR, _: f32,
            _: &mut bool, _: bool
        ) -> HRESULT {
            return S_OK;
        }
    }

    let bezier = CBezier {m_ptB: [
            GpPointR {x: 0., y: 0.0005},
            GpPointR {x: 0., y: 0.0005},
            GpPointR {x: 0., y: 0.},
            GpPointR {x: 0., y: 0.}]};

    let mut t = Sink;
    let mut f = CBezierFlattener::new(&bezier, &mut t, 0.25);
    f.Flatten(true);
}

[ Dauer der Verarbeitung: 0.47 Sekunden  ]