summaryrefslogtreecommitdiffstats
path: root/third_party/rust/aa-stroke/src/bezierflattener.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/aa-stroke/src/bezierflattener.rs')
-rw-r--r--third_party/rust/aa-stroke/src/bezierflattener.rs830
1 files changed, 830 insertions, 0 deletions
diff --git a/third_party/rust/aa-stroke/src/bezierflattener.rs b/third_party/rust/aa-stroke/src/bezierflattener.rs
new file mode 100644
index 0000000000..1a615941b6
--- /dev/null
+++ b/third_party/rust/aa-stroke/src/bezierflattener.rs
@@ -0,0 +1,830 @@
+// 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: f64,
+ pub y: f64
+}
+
+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<f64> for GpPointR {
+ fn mul_assign(&mut self, rhs: f64) {
+ *self = *self * rhs;
+ }
+}
+
+
+impl Mul<f64> for GpPointR {
+ type Output = Self;
+
+ fn mul(self, rhs: f64) -> Self::Output {
+ GpPointR { x: self.x * rhs, y: self.y * rhs }
+ }
+}
+
+impl Div<f64> for GpPointR {
+ type Output = Self;
+
+ fn div(self, rhs: f64) -> Self::Output {
+ GpPointR { x: self.x / rhs, y: self.y / rhs }
+ }
+}
+
+
+impl Mul for GpPointR {
+ type Output = f64;
+
+ fn mul(self, rhs: Self) -> Self::Output {
+ self.x * rhs.x + self.y * rhs.y
+ }
+}
+
+impl GpPointR {
+ pub fn ApproxNorm(&self) -> f64 {
+ self.x.abs().max(self.y.abs())
+ }
+ pub fn Norm(&self) -> f64 {
+ 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: f64 = 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: f64 = 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 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: f64,
+ // 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: f64, // Prescribed tolerance
+ m_fWithTangents: bool, // Generate tangent vectors if true
+ m_rQuarterTolerance: f64,// Prescribed tolerance/4 (for doubling the step)
+ m_rFuzz: f64, // 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: f64, // Parameter value
+ m_rStepSize: f64, // 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: f64) // 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;
+
+ 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.
+//
+//------------------------------------------------------------------------------
+#[allow(dead_code)]
+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;
+}
+}
+
+