Skip to content
......@@ -160,16 +160,22 @@ autodoc_member_order = 'alphabetical' # groupwise bysource
# # 'exclude-members',
# ]
excluded_members = [
'__dict__',
'__module__',
'__weakref__',
]
autodoc_default_options = {
'members': None,
# 'member-order': 'alphabetical' ,
'undoc-members': None,
# 'private-members': ,
# 'special-members': ,
'private-members': None,
'special-members': None,
# 'inherited-members': ,
# 'show-inheritance': ,
'show-inheritance': None,
'ignore-module-all': None,
# 'exclude-members': ,
'exclude-members': ','.join(excluded_members),
}
####################################################################################################
......
.. include:: /abbreviation.txt
.. _design-note-page:
==============
Design Notes
==============
This section contains design notes.
Contents:
.. toctree::
:maxdepth: 2
question-answer.rst
performance.rst
.. include:: /abbreviation.txt
.. _design-note-performance-page:
==============
Performances
==============
SVG Import
----------
For the complex dress pattern "Veravenus" of the file
:file:`veravenus-little-bias-dress.pattern-a0.svg` made of 270 SVG paths and 1051 segments, the
parsing and rendering time is of the order of one seconde ( cf. following log ). In comparison,
Inkscape launched in parallel using a shell script, takes a little more times to start and open this
file. Of course the comparison is not perfect since Inkscape is a heavier software to load, but it
shows a full Python implementation ( excepted the work done by Qt ) is competitive for a such file.
.. image:: /_static/patro-svg-import.png
:alt: Patro SVG Import
:width: 300px
:height: 300px
:align: center
.. code-block:: text
> ./bin/patro --user-script examples/file-format/svg/test-svg-import.py
... :mm:ss,sss
... :11:26,528 - __main__.<module> - INFO - Started Patro
... :11:27,111 - Patro.QtApplication.QmlApplication.Application._message_handler - INFO - main.qml onCompleted
... :11:27,113 - Patro.QtApplication.QmlApplication.Application._post_init - INFO - post init
... :11:27,113 - Patro.QtApplication.QmlApplication.Application.execute_user_script - INFO - Execute user script:
... patro/examples/file-format/svg/test-svg-import.py
... :11:27,361 - builtins.SceneImporter.__init__ - INFO - Number of SVG item: 270
... :11:27,361 - builtins.SceneImporter.__init__ - INFO - Number of scene item: 1051
... :11:27,361 - Patro.QtApplication.QmlApplication.QmlApplication.scene - INFO - set scene
... :11:27,362 - Patro.GraphicEngine.Painter.QtPainter.QtQuickPaintedSceneItem.scene - INFO - set scene
... :11:27,362 - Patro.QtApplication.QmlApplication.Application.execute_user_script - INFO - User script done
... :11:27,387 - Patro.GraphicEngine.Painter.QtPainter.QtQuickPaintedSceneItem.paint - INFO - Start painting
... :11:27,530 - Patro.GraphicEngine.Painter.QtPainter.QtQuickPaintedSceneItem.paint - INFO - Paint done
.. include:: abbreviation.txt
.. include:: /abbreviation.txt
.. _design-note-page:
.. _design-note-question-answer-page:
==============
Design Notes
==============
==================================
Design Notes Questions & Answers
==================================
Originally, Patro was a Python implementation of the Valentina pattern making software, which only
focus to implement the core engine and not the graphical user interface.
......
.. include:: /abbreviation.txt
.. _examples-dxf-import-page:
============
DXF Import
============
x
This screenshot show the file :file:`test-dxf-r15.dxf` used as a test case.
.. image:: /_static/patro-dxf-import.png
:alt: Patro DXF Import
:align: center
.. include:: abbreviation.txt
.. include:: /abbreviation.txt
.. _examples-page:
......@@ -7,3 +7,11 @@
==========
The *examples* directory contains several Python scripts showing how to use Patro.
Contents:
.. toctree::
:maxdepth: 2
svg-import.rst
dxf-import.rst
.. include:: /abbreviation.txt
.. _examples-svg-import-page:
============
SVG Import
============
This screenshot show the complex dress pattern "Veravenus" of the file
:file:`veravenus-little-bias-dress.pattern-a0.svg` made of 270 SVG paths and 1051 segments.
.. image:: /_static/patro-svg-import.png
:alt: Patro SVG Import
:align: center
.. image:: /_static/patro-svg-import-selection.png
:alt: Patro SVG Import Item Selection
:align: center
.. include:: project-links.txt
.. include:: abbreviation.txt
.. _graphic-engine-review-page:
=======================
Graphic Engine Review
=======================
Common Features
---------------
* transformation: reflect, slant
* clipping path
* path
* pen: invisible, hex
* arrow tips
* marker
* label
Tikz
----
* circle on path
* sin parabola path
* filling: shading
* scope : style, transformation
* label : anchor, alignment, position on path
* pen
* line width : ultra thin, very thin, thin, semithick, thick, very thick, ultra thick
* line cape / join
* dash pattern
* double line
* node : edge, anchor, shape
Asymptote
---------
http://asymptote.sourceforge.net
Examples::
draw((0,0)--(100,100));
draw((0,0)--(2,1),Arrow);
draw((0,0)--(1,0)--(1,1)--(0,1)--cycle);
label("$A$",(0,0),SW);
DPic
----
......@@ -146,9 +146,10 @@ If you want to donate to the project or need a more professional support.
news.rst
roadmap.rst
installation.rst
examples.rst
examples/index.rst
faq.rst
design-notes.rst
design-notes/index.rst
resources/index.rst
reference-manual.rst
development.rst
how-to-refer.rst
......
.. include:: /abbreviation.txt
.. _dxf-ressources-page:
=================
DXF File Format
=================
.. include:: /abbreviation.txt
.. _file-format-ressources-page:
==============
File Formats
==============
This section contains resource on file format.
Contents:
.. toctree::
:maxdepth: 2
dxf.rst
svg.rst
valentina.rst
.. include:: /abbreviation.txt
.. _svg-ressources-page:
=================
SVG File Format
=================
Reference Documentations
------------------------
* `Scalable Vector Graphics (SVG) W3C Home Page <https://www.w3.org/Graphics/SVG>`_
* `An SVG Primer for Today's Browsers W3C Working Draft — September 2010 <https://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html>`_
* `Scalable Vector Graphics (SVG) 1.1 (Second Edition) W3C Recommendation 16 August 2011 <https://www.w3.org/TR/SVG11/>`_
* `Mozilla SVG Documentation <https://developer.mozilla.org/en-US/docs/Web/SVG>`_
SVG Coordinate System
---------------------
SVG uses the screen coordinate system where the X axis points towards the right direction and the Y
axis points towards the bottom direction, thus the origin is at the upper left-hand corner of the
drawing frame.
Inkscape Software
-----------------
Inkscape can save a SVG file in several ways:
* simple: the file will only contains pure SVG
* Inkscape: the file will contains Inkscape extension
* optimised: perform some optimisations on the output
* compressed: file is compressed using the zip algorithm
**Notes on how Inkscape generates SVG**:
* As opposite to the SVG Specification, Inkscape set the origin at the bottom of the document, thus
:math:`\mathbf{Y}_{\mathrm{Inkscape}} = \mathbf{Page\ Height} - \mathbf{Y}_{\mathrm{SVG}}`
The transformation is a composition of a Y axis parity and a translation on the Y axis of the page height.
See also https://bugs.launchpad.net/inkscape/+bug/170049
* It uses a *group* as top layer and a *transform*
* It uses a mix of absolute and incremental coordinates
* **It spoils in several ways the object's coordinates**: values are transformed and rounded.
Thus **Inkscape should not be be used to make or edit a file which require accurate coordinates.**
Inkscape SVG example
~~~~~~~~~~~~~~~~~~~~
This example uses a margin of 20 and paint
* an horizontal line of length 100 from the origin (20, 20)
* a vertical line of length 100 from the origin
* a 45° line of length 80 from the origin
* the viewport is (0, 0, 140, 140)
**Note: this extract is incomplete**
.. code-block:: text
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="140.00003mm" height="141mm" viewBox="0 0 140.00003 141">
<g id="layer1" transform="translate(2.037847e-4,-156)">
<path id="path-x" d="m 19.999999,276.73109 99.999651,0.13765" />
<path id="path-y" d="M 20.145247,277 V 177" />
<path id="path-45" d="m 20.084712,276.91488 79.830575,-79.8386" />
</g>
</svg>
This SVG file must be interpreted as follow
* Draw a line from (20, 276-156) to +(100, 0) (note: l is deduced from m),
which gives (20, 120) to (120, 120),
and in Inkscape coordinates (20, 20) to (120, 20) since 140 - 120 = 20
* Draw a line from (20, 276-156) to (20, 176-156),
which gives (20, 120) to (20, 20),
and in Inkscape coordinates (20, 20) to (20, 120)
* Draw a line from (20, 276-156) to +(80, -80),
which gives (20, 120) to (100, 40),
and in Inkscape coordinates (20, 20) to (100, 100)
.. include:: /abbreviation.txt
.. _valentina-file-format-ressources-page:
=======================
Valentina File Format
=======================
.. include:: /abbreviation.txt
.. _bezier-geometry-ressources-page:
===============
Bézier Curves
===============
.. contents:: :local:
Definitions
-----------
A Bézier curve is defined by a set of control points :math:`\mathbf{P}_0` through
:math:`\mathbf{P}_n`, where :math:`n` is called its order (:math:`n = 1` for linear, 2 for
quadratic, 3 for cubic etc.). The first and last control points are always the end points of the
curve;
In the following :math:`0 \le t \le 1` and :math:`u = 1 - t`
Linear Bézier Curves
--------------------
Given distinct points :math:`\mathbf{P}_0` and :math:`\mathbf{P}_1`, a linear Bézier curve is simply
a straight line between those two points. The curve is given by
.. math::
\begin{align}
\mathbf{B}(t) &= \mathbf{P}_0 + t (\mathbf{P}_1 - \mathbf{P}_0) \\[.5em]
&= (1-t) \mathbf{P}_0 + t \mathbf{P}_1 \\[.5em]
&= u \mathbf{P}_0 + t \mathbf{P}_1
\end{align}
and is equivalent to linear interpolation.
Quadratic Bézier Curves
-----------------------
A quadratic Bézier curve is the path traced by the function :math:`\mathbf{B}(t)`, given points
:math:`\mathbf{P}_0`, :math:`\mathbf{P}_1`, and :math:`\mathbf{P}_2`,
.. math::
\begin{align}
\mathbf{B}(t) &= (1-t)[(1-t) \mathbf{P}_0 + t \mathbf{P}_1] + t [(1-t) \mathbf{P}_1 + t \mathbf{P}_2] \\[.5em]
&= u[u \mathbf{P}_0 + t \mathbf{P}_1] + t [u \mathbf{P}_1 + t \mathbf{P}_2]
\end{align}
which can be interpreted as the linear interpolant of corresponding points on the linear Bézier
curves from :math:`\mathbf{P}_0` to :math:`\mathbf{P}_1` and from :math:`\mathbf{P}_1` to
:math:`\mathbf{P}_2` respectively.
Rearranging the preceding equation yields:
.. math::
\begin{align}
\mathbf{B}(t) &= (1-t)^2 \mathbf{P}_0 + 2(1-t)t \mathbf{P}_1 + t^2 \mathbf{P}_2 \\[.5em]
&= u^2 \mathbf{P}_0 + 2ut \mathbf{P}_1 + t^2 \mathbf{P}_2 \\[.5em]
&= (\mathbf{P}_0 - 2\mathbf{P}_1 + \mathbf{P}_2) t^2 +
2(-\mathbf{P}_0 + \mathbf{P}_1) t +
\mathbf{P}_0
\end{align}
This can be written in a way that highlights the symmetry with respect to :math:`\mathbf{P}_1`:
.. math::
\begin{align}
\mathbf{B}(t) &= \mathbf{P}_1 + (1-t)^2 ( \mathbf{P}_0 - \mathbf{P}_1) + t^2 (\mathbf{P}_2 - \mathbf{P}_1) \\[.5em]
&= \mathbf{P}_1 + u^2 ( \mathbf{P}_0 - \mathbf{P}_1) + t^2 (\mathbf{P}_2 - \mathbf{P}_1)
\end{align}
Which immediately gives the derivative of the Bézier curve with respect to `t`:
.. math::
\begin{align}
\mathbf{B}'(t) &= 2(1-t) (\mathbf{P}_1 - \mathbf{P}_0) + 2t (\mathbf{P}_2 - \mathbf{P}_1) \\[.5em]
&= 2 (\mathbf{P}_1 - \mathbf{P}_0) + 2(\mathbf{P}_0 - 2\mathbf{P}_1 + \mathbf{P}_2) t \\[.5em]
&= 2u (\mathbf{P}_1 - \mathbf{P}_0) + 2t (\mathbf{P}_2 - \mathbf{P}_1)
\end{align}
from which it can be concluded that the tangents to the curve at :math:`\mathbf{P}_0` and
:math:`\mathbf{P}_2` intersect at :math:`\mathbf{P}_1`. As :math:`t` increases from 0 to 1, the
curve departs from :math:`\mathbf{P}_0` in the direction of :math:`\mathbf{P}_1`, then bends to
arrive at :math:`\mathbf{P}_2` from the direction of :math:`\mathbf{P}_1`.
The second derivative of the Bézier curve with respect to :math:`t` is
.. math::
\mathbf{B}''(t) = 2 (\mathbf{P}_2 - 2 \mathbf{P}_1 + \mathbf{P}_0)
Cubic Bézier Curves
-------------------
Four points :math:`\mathbf{P}_0`, :math:`\mathbf{P}_1`, :math:`\mathbf{P}_2` and
:math:`\mathbf{P}_3` in the plane or in higher-dimensional space define a cubic Bézier curve. The
curve starts at :math:`\mathbf{P}_0` going toward :math:`\mathbf{P}_1` and arrives at
:math:`\mathbf{P}_3` coming from the direction of :math:`\mathbf{P}_2`. Usually, it will not pass
through :math:`\mathbf{P}_1` or :math:`\mathbf{P}_2`; these points are only there to provide
directional information. The distance between :math:`\mathbf{P}_1` and :math:`\mathbf{P}_2`
determines "how far" and "how fast" the curve moves towards :math:`\mathbf{P}_1` before turning
towards :math:`\mathbf{P}_2`.
Writing :math:`\mathbf{B}_{\mathbf P_i,\mathbf P_j,\mathbf P_k}(t)` for the quadratic Bézier curve
defined by points :math:`\mathbf{P}_i`, :math:`\mathbf{P}_j`, and :math:`\mathbf{P}_k`, the cubic
Bézier curve can be defined as an affine combination of two quadratic Bézier curves:
.. math::
\mathbf{B}(t) = (1-t) \mathbf{B}_{\mathbf P_0,\mathbf P_1,\mathbf P_2}(t) +
t \mathbf{B}_{\mathbf P_1,\mathbf P_2,\mathbf P_3}(t)
The explicit form of the curve is:
.. math::
\begin{align}
\mathbf{B}(t) &= (1-t)^3 \mathbf{P}_0 + 3(1-t)^2t \mathbf{P}_1 + 3(1-t)t^2 \mathbf{P}_2 + t^3\mathbf{P}_3 \\[.5em]
&= u^3 \mathbf{P}_0 + 3u^2t \mathbf{P}_1 + 3ut^2 \mathbf{P}_2 + t^3\mathbf{P}_3 \\[.5em]
&= (\mathbf{P}_3 - 3\mathbf{P}_2 + 3\mathbf{P}_1 - \mathbf{P}_0) t^3 +
3(\mathbf{P}_2 - 2\mathbf{P}_1 + \mathbf{P}_0) t^2 +
3(\mathbf{P}_1 - \mathbf{P}_0) t +
\mathbf{P}_0
\end{align}
For some choices of :math:`\mathbf{P}_1` and :math:`\mathbf{P}_2` the curve may intersect itself, or
contain a cusp.
The derivative of the cubic Bézier curve with respect to :math:`t` is
.. math::
\begin{align}
\mathbf{B}'(t) &= 3(1-t)^2 (\mathbf{P}_1 - \mathbf{P}_0) + 6(1-t)t (\mathbf{P}_2 - \mathbf{P}_1) + 3t^2 (\mathbf{P}_3 - \mathbf{P}_2) \\[.5em]
&= 3u^2 (\mathbf{P}_1 - \mathbf{P}_0) + 6ut (\mathbf{P}_2 - \mathbf{P}_1) + 3t^2 (\mathbf{P}_3 - \mathbf{P}_2)
\end{align}
The second derivative of the Bézier curve with respect to :math:`t` is
.. math::
\begin{align}
\mathbf{B}''(t) &= 6(1-t) (\mathbf{P}_2 - 2 \mathbf{P}_1 + \mathbf{P}_0) + 6t (\mathbf{P}_3 - 2 \mathbf{P}_2 + \mathbf{P}_1) \\[.5em]
&= 6u (\mathbf{P}_2 - 2 \mathbf{P}_1 + \mathbf{P}_0) + 6t (\mathbf{P}_3 - 2 \mathbf{P}_2 + \mathbf{P}_1)
\end{align}
Recursive definition
--------------------
A recursive definition for the Bézier curve of degree :math:`n` expresses it as a point-to-point
linear combination of a pair of corresponding points in two Bézier curves of degree :math:`n-1`.
Let :math:`\mathbf{B}_{\mathbf{P}_0\mathbf{P}_1\ldots\mathbf{P}_n}` denote the Bézier curve
determined by any selection of points :math:`\mathbf{P}_0`, :math:`\mathbf{P}_1`, :math:`\ldots`,
:math:`\mathbf{P}_{n-1}`.
The recursive definition is
.. math::
\begin{align}
\mathbf{B}_{\mathbf{P}_0}(t) &= \mathbf{P}_0 \\[1em]
\mathbf{B}(t) &= \mathbf{B}_{\mathbf{P}_0\mathbf{P}_1\ldots\mathbf{P}_n}(t) \\[.5em]
&= (1-t) \mathbf{B}_{\mathbf{P}_0\mathbf{P}_1\ldots\mathbf{P}_{n-1}}(t) +
t \mathbf{B}_{\mathbf{P}_1\mathbf{P}_2\ldots\mathbf{P}_n}(t)
\end{align}
The formula can be expressed explicitly as follows:
.. math::
\begin{align}
\mathbf{B}(t) &= \sum_{i=0}^n b_{i,n}(t) \mathbf{P}_i \\[.5em]
&= \sum_{i=0}^n {n\choose i}(1-t)^{n - i}t^i \mathbf{P}_i \\[.5em]
&= (1-t)^n \mathbf{P}_0 +
{n\choose 1}(1-t)^{n - 1}t \mathbf{P}_1 +
\cdots +
{n\choose n - 1}(1-t)t^{n - 1} \mathbf{P}_{n - 1} +
t^n \mathbf{P}_n
\end{align}
where :math:`b_{i,n}(t)` are the Bernstein basis polynomials of degree :math:`n` and :math:`n
\choose i` are the binomial coefficients.
.. _bezier-curve-degree-elevation-section:
Degree elevation
----------------
A Bézier curve of degree :math:`n` can be converted into a Bézier curve of degree :math:`n + 1` with
the same shape.
To do degree elevation, we use the equality
.. math::
\mathbf{B}(t) = (1-t) \mathbf{B}(t) + t \mathbf{B}(t)
Each component :math:`\mathbf{b}_{i,n}(t) \mathbf{P}_i` is multiplied by :math:`(1-t)` and
:math:`t`, thus increasing a degree by one, without changing the value.
For arbitrary :math:`n`, we have
.. math::
\begin{align}
\mathbf{B}(t) &= (1-t) \sum_{i=0}^n \mathbf{b}_{i,n}(t) \mathbf{P}_i +
t \sum_{i=0}^n \mathbf{b}_{i,n}(t) \mathbf{P}_i \\[.5em]
&= \sum_{i=0}^n \frac{n + 1 - i}{n + 1} \mathbf{b}_{i, n + 1}(t) \mathbf{P}_i +
\sum_{i=0}^n \frac{i + 1}{n + 1} \mathbf{b}_{i + 1, n + 1}(t) \mathbf{P}_i \\[.5em]
&= \sum_{i=0}^{n + 1} \mathbf{b}_{i, n + 1}(t)
\left(\frac{i}{n + 1} \mathbf{P}_{i - 1} +
\frac{n + 1 - i}{n + 1} \mathbf{P}_i\right) \\[.5em]
&= \sum_{i=0}^{n + 1} \mathbf{b}_{i, n + 1}(t) \mathbf{P'}_i
\end{align}
Therefore the new control points are
.. math::
\mathbf{P'}_i = \frac{i}{n + 1} \mathbf{P}_{i - 1} + \frac{n + 1 - i}{n + 1} \mathbf{P}_i
It introduces two arbitrary points :math:`\mathbf{P}_{-1}` and :math:`\mathbf{P}_{n+1}` which are
cancelled in :math:`\mathbf{P'}_i`.
Example for a quadratic Bézier curve:
.. math::
\begin{align}
\mathbf{P'}_0 &= \mathbf{P}_0 \\[.5em]
\mathbf{P'}_1 &= \mathbf{P}_0 + \frac{2}{3} (\mathbf{P}_1 - \mathbf{P}_0) \\[.5em]
\mathbf{P'}_1 &= \mathbf{P}_2 + \frac{2}{3} (\mathbf{P}_1 - \mathbf{P}_2) \\[.5em]
\mathbf{P'}_2 &= \mathbf{P}_2
\end{align}
Matrix Forms
------------
.. math::
\mathbf{B}(t) = \mathbf{Transformation} \; \mathbf{Control} \; \mathbf{Basis} \; \mathbf{T}(t)
.. math::
\begin{align}
\mathbf{B^2}(t) &= \mathbf{Tr}
\begin{pmatrix}
P_{1x} & P_{2x} & P_{3x} \\
P_{1y} & P_{2x} & P_{3x} \\
1 & 1 & 1
\end{pmatrix}
\begin{pmatrix}
1 & -2 & 1 \\
0 & 2 & -2 \\
0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
1 \\
t \\
t^2
\end{pmatrix} \\[1em]
\mathbf{B^3}(t) &= \mathbf{Tr}
\begin{pmatrix}
P_{1x} & P_{2x} & P_{3x} & P_{4x} \\
P_{1y} & P_{2x} & P_{3x} & P_{4x} \\
0 & 0 & 0 & 0 \\
1 & 1 & 1 & 1
\end{pmatrix}
\begin{pmatrix}
1 & -3 & 3 & -1 \\
0 & 3 & -6 & 3 \\
0 & 0 & 3 & -3 \\
0 & 0 & 0 & 1
\end{pmatrix}
\begin{pmatrix}
1 \\
t \\
t^2 \\
t^3
\end{pmatrix}
\end{align}
.. B(t) = P0 (1 - 2t + t^2) +
P1 ( 2t - t^2) +
P2 t^2
Symbolic Calculation
--------------------
.. code-block:: py3
>>> from sympy import *
>>> P0, P1, P2, P3, P, t = symbols('P0 P1 P2 P3 P t')
>>> B2 = (1-t)*((1-t)*P0 + t*P1) + t*((1-t)*P1 + t*P2)
>>> collect(expand(B2), t)
P0 + t**2*(P0 - 2*P1 + P2) + t*(-2*P0 + 2*P1)
>>> B2_012 = (1-t)*((1-t)*P0 + t*P1) + t*((1-t)*P1 + t*P2)
>>> B2_123 = (1-t)*((1-t)*P1 + t*P2) + t*((1-t)*P2 + t*P3)
>>> B3 = (1-t)*B2_012 + t*B2_123
>>> collect(expand(B3), t)
P0 + t**3*(-P0 + 3*P1 - 3*P2 + P3) + t**2*(3*P0 - 6*P1 + 3*P2) + t*(-3*P0 + 3*P1)
# Compute derivative
>>> B2p = collect(simplify(B2.diff(t)), t)
-2*P0 + 2*P1 + t*(2*P0 - 4*P1 + 2*P2)
>>> B3p = collect(simplify(B3.diff(t)), t)
-3*P0 + 3*P1 + t**2*(-3*P0 + 9*P1 - 9*P2 + 3*P3) + t*(6*P0 - 12*P1 + 6*P2)
.. _bezier-curve-length-section:
Curve Length
------------
Reference
* http://www.gamedev.net/topic/551455-length-of-a-generalized-quadratic-bezier-curve-in-3d
* Dave Eberly Posted October 25, 2009
The quadratic Bézier is
.. math::
\mathbf{B}(t) = (1-t)^2 \mathbf{P}_0 + 2t(1-t) \mathbf{P}_1 + t^2 \mathbf{P}_2
The derivative is
.. math::
\mathbf{B'}(t) = -2(1-t) \mathbf{P}_0 + (2-4t) \mathbf{P}_1 + 2t \mathbf{P}_2
The length of the curve for :math:`0 <= t <= 1` is
.. math::
\int_0^1 \sqrt{(x'(t))^2 + (y'(t))^2} dt
The integrand is of the form :math:`\sqrt{c t^2 + b t + a}`
You have three separate cases: :math:`c = 0`, :math:`c > 0`, or :math:`c < 0`.
The case :math:`c = 0` is easy.
For the case :math:`c > 0`, an antiderivative is
.. math::
\frac{2ct + b}{4c} \sqrt{ct^2 + bt + a} + \frac{k}{2\sqrt{c}} \ln{\left(2\sqrt{c(ct^2 + bt + a)} + 2ct + b\right)}
For the case :math:`c < 0`, an antiderivative is
.. math::
\frac{2ct + b}{4c} \sqrt{ct^2 + bt + a} - \frac{k}{2\sqrt{-c}} \arcsin{\frac{2ct + b}{\sqrt{-q}}}
where :math:`k = \frac{4c}{q}` with :math:`q = 4ac - b^2`.
.. _bezier-curve-flatness-section:
Determine the curve flatness
----------------------------
Reference
* Kaspar Fischer and Roger Willcocks http://hcklbrrfnn.files.wordpress.com/2012/08/bez.pdf
* PostScript Language Reference. Addison- Wesley, third edition, 1999
*flatness* is the maximum error allowed for the straight line to deviate from the curve.
Algorithm
We define the flatness of the curve as the argmax of the distance from the curve to the
line passing by the start and stop point.
:math:`\mathrm{flatness} = argmax(d(t))` for :math:`t \in [0, 1]` where :math:`d(t) = \vert B(t) - L(t) \vert`
The line equation is
.. math::
L = (1-t) \mathbf{P}_0 + t \mathbf{P}_1
Let
.. math::
\begin{align}
U &= 3\mathbf{P}_1 - 2\mathbf{P}_0 - \mathbf{P}_3 \\[.5em]
V &= 3\mathbf{P}_2 - \mathbf{P}_0 - 2\mathbf{P}_3
\end{align}
The distance is
.. math::
\begin{align}
d(t) &= (1-t)^2 t \left(3\mathbf{P}_1 - 2\mathbf{P}_0 - \mathbf{P}_3\right) + (1-t) t^2 (3\mathbf{P}_2 - \mathbf{P}_0 - 2\mathbf{P}_3) \\[.5em]
&= (1-t)^2 t u + (1-t) t^2 v
\end{align}
The square of the distance is
.. math::
d(t)^2 = (1-t)^2 t^2 (((1-t) U_x + t V_x)^2 + ((1-t) U_y + t V_y)^2
From
.. math::
\begin{align}
argmax((1-t)^2 t^2) &= \frac{1}{16} \\[.5em]
argmax((1-t) a + t b) &= argmax(a, b)
\end{align}
we can express a bound on the flatness
.. math::
\mathrm{flatness}^2 = argmax(d(t)^2) \leq \frac{1}{16} (argmax(U_x^2, V_x^2) + argmax(U_y^2, V_y^2))
Thus an upper bound of :math:`16\,\mathrm{flatness}^2` is
.. math::
argmax(U_x^2, V_x^2) + argmax(U_y^2, V_y^2)
.. _bezier-curve-line-intersection-section:
Intersection of Bézier Curve with a Line
----------------------------------------
Algorithm
* Apply a transformation to the curve that maps the line onto the X-axis.
* Then we only need to test the Y-values for a zero.
.. _bezier-curve-closest-point-section:
Closest Point
-------------
Reference
* https://hal.archives-ouvertes.fr/inria-00518379/document
Improved Algebraic Algorithm On Point Projection For Bézier Curves
Xiao-Diao Chen, Yin Zhou, Zhenyu Shu, Hua Su, Jean-Claude Paul
Let a point :math:`\mathbf{P}` and the closest point :math:`\mathbf{B}(t)` on the curve, we have this
condition:
.. math::
(\mathbf{P} - \mathbf{B}(t)) \cdot \mathbf{B}'(t) = 0 \\[.5em]
\mathbf{P} \cdot \mathbf{B}'(t) - \mathbf{B}(t) \cdot \mathbf{B}'(t) = 0
Quadratic Bézier Curve
~~~~~~~~~~~~~~~~~~~~~~
Let
.. math::
\begin{align}
\mathbf{A} &= \mathbf{P}_1 - \mathbf{P}_0 \\[.5em]
\mathbf{B} &= \mathbf{P}_2 - \mathbf{P}_1 -\mathbf{A} \\[.5em]
\mathbf{M} &= \mathbf{P}_0 - \mathbf{P}
\end{align}
We have
.. math::
\mathbf{B}'(t) = 2(\mathbf{A} + \mathbf{B} t)
.. factorisation
(P0 - 2*P1 + P2)**2 * t**3
3*(P1 - P0)*(P0 - 2*P1 + P2) * t**2
...
(P0 - P)*(P1 - P0)
The condition can be expressed as
.. math::
\mathbf{B}^2 t^3 + 3\mathbf{A}\mathbf{B} t^2 + (2\mathbf{A}^2 + \mathbf{M}\mathbf{B}) t + \mathbf{M}\mathbf{A} = 0
.. code-block:: py3
>>> C = collect(expand((P*B2p - B2*B2p)/-2), t)
P*P0 - P*P1 - P0**2 + P0*P1 +
t**3 * (P0**2 - 4*P0*P1 + 2*P0*P2 + 4*P1**2 - 4*P1*P2 + P2**2) +
t**2 * (-3*P0**2 + 9*P0*P1 - 3*P0*P2 - 6*P1**2 + 3*P1*P2) +
t * (-P*P0 + 2*P*P1 - P*P2 + 3*P0**2 - 6*P0*P1 + P0*P2 + 2*P1**2)
>>> A = P1 - P0
B = P2 - P1 - A
M = P0 - P
>>> C2 = B**2 * t**3 + 3*A*B * t**2 + (2*A**2 + M*B) * t + M*A
>>> expand(C - C2)
0
Cubic Bézier Curve
~~~~~~~~~~~~~~~~~~
Let
.. math::
\begin{align}
n &= \mathbf{P}_3 - 3\mathbf{P}_2 + 3\mathbf{P}_1 - \mathbf{P}_0 \\[.5em]
r &= 3(\mathbf{P}_2 - 2\mathbf{P}_1 + \mathbf{P}_0 \\[.5em]
s &= 3(\mathbf{P}_1 - \mathbf{P}_0) \\[.5em]
v &= \mathbf{P}_0
\end{align}
We have
.. math::
\begin{align}
\mathbf{B}^3(t) &= nt^3 + rt^2 + st + v \\[.5em]
\mathbf{B}'(t) &= 3nt^2 + 2rt + s
\end{align}
.. n = P3 - 3*P2 + 3*P1 - P0
r = 3*(P2 - 2*P1 + P0
s = 3*(P1 - P0)
v = P0
.. code-block:: py3
>>> n, r, s, v = symbols('n r s v')
>>> B3 = n*t**3 + r*t**2 + s*t + v
>>> B3p = simplify(B3.diff(t))
>>> C = collect(expand((P*B3p - B3*B3p)), t)
-3*n**2 * t**5 +
-5*n*r * t**4 +
-2*(2*n*s + r**2) * t**3 +
3*(P*n - n*v - r*s) * t**2 +
(2*P*r - 2*r*v - s**2) * t +
P*s - s*v
.. include:: /abbreviation.txt
.. _geometry-ressources-page:
====================
Geometry Resources
====================
This section contains resource on geometry.
Contents:
.. toctree::
:maxdepth: 2
bezier.rst
path.rst
spline.rst
transformations.rst
.. include:: /abbreviation.txt
.. _path-geometry-ressources-page:
======
Path
======
Bulge
-----
Let :math:`\mathbf{P}_0`, :math:`\mathbf{P}_1`, :math:`\mathbf{P}_2` the vertices and :math:`R` the
bulge radius.
The deflection :math:`\theta = 2 \alpha` at the corner is
.. math::
\mathbf{D}_1 \cdot \mathbf{D}_0 = (\mathbf{P}_2 - \mathbf{P}_1) \cdot (\mathbf{P}_1 - \mathbf{P}_0) = \cos(\pi - \theta)
The bisector direction is
.. math::
\mathbf{Bis} = \mathbf{D}_1 - \mathbf{D}_0 = (\mathbf{P}_2 - \mathbf{P}_1) - (\mathbf{P}_1 - \mathbf{P}_0) = \mathbf{P}_2 -2 \mathbf{P}_1 + \mathbf{P}_0
Bulge Center is
.. math::
\mathbf{C} = \mathbf{P}_1 + \frac{R}{\sin \alpha} \mathbf{Bis}
Extremities are
.. math::
\begin{align}
\mathbf{P}_1' &= \mathbf{P}_1 - \frac{R}{\tan \alpha} \mathbf{D}_0 \\
\mathbf{P}_1'' &= \mathbf{P}_1 + \frac{R}{\tan \alpha} \mathbf{D}_1
\end{align}
.. include:: /abbreviation.txt
.. _spline-geometry-ressources-page:
===============
Spline Curves
===============
.. contents:: :local:
.. The DeBoor-Cox algorithm permits to evaluate recursively a B-Spline in a similar way to the De
Casteljaud algorithm for Bézier curves.
Given `k` the degree of the B-spline, `n + 1` control points :math:`p_0, \ldots, p_n`, and an
increasing series of scalars :math:`t_0 \le t_1 \le \ldots \le t_m` with :math:`m = n + k + 1`,
called knots.
The number of points must respect the condition :math:`n + 1 \le k`, e.g. a B-spline of degree 3
must have 4 control points.
References
----------
* Computer Graphics, Principle and Practice, Foley et al., Adison Wesley
* http://web.mit.edu/hyperbook/Patrikalakis-Maekawa-Cho/node15.html
B-spline Basis
--------------
A nonuniform, nonrational B-spline of order `k` is a piecewise polynomial function of degree
:math:`k - 1` in a variable `t`.
.. check: k+1 knots ???
.. It is defined over :math:`k + 1` locations :math:`t_i`, called knots, which must be in
non-descending order :math:`t_i \leq t_{i+1}`. This series defines a knot vector :math:`T = (t_0,
\ldots, t_{k})`.
A set of non-descending breaking points, called knot, :math:`t_0 \le t_1 \le \ldots \le t_m` defines
a knot vector :math:`T = (t_0, \ldots, t_{m})`.
If each knot is separated by the same distance `h` (where :math:`h = t_{i+1} - t_i`) from its
predecessor, the knot vector and the corresponding B-splines are called "uniform".
Given a knot vector `T`, the associated B-spline basis functions, :math:`B_i^k(t)` are defined as:
.. t \in [t_i, t_{i+1}[
.. math::
B_i^1(t) =
\left\lbrace
\begin{array}{l}
1 \;\textrm{if}\; t_i \le t < t_{i+1} \\
0 \;\textrm{otherwise}
\end{array}
\right.
.. math::
\begin{split}
B_i^k(t) &= \frac{t - t_i}{t_{i+k-1} - t_i} B_i^{k-1}(t)
+ \frac{t_{i+k} - t}{t_{i+k} - t_{i+1}} B_{i+1}^{k-1}(t) \\
&= w_i^{k-1}(t) B_i^{k-1}(t) + [1 - w_{i+1}^{k-1}(t)] B_{i+1}^{k-1}(t)
\end{split}
where
.. math::
w_i^k(t) =
\left\lbrace
\begin{array}{l}
\frac{t - t_i}{t_{i+k} - t_i} \;\textrm{if}\; t_i < t_{i+k} \\
0 \;\textrm{otherwise}
\end{array}
\right.
These equations have the following properties, for :math:`k > 1` and :math:`i = 0, 1, \ldots, n` :
* Positivity: :math:`B_i^k(t) > 0`, for :math:`t_i < t < t_{i+k}`
* Local Support: :math:`B_i^k(t) = 0`, for :math:`t_0 \le t \le t_i` and :math:`t_{i+k} \le t \le t_{n+k}`
* Partition of unity: :math:`\sum_{i=0}^n B_i^k(t)= 1`, for :math:`t \in [t_0, t_m]`
* Continuity: :math:`B_i^k(t)` as :math:`C^{k-2}` continuity at each simple knot
.. The B-spline contributes only in the range between the first and last of these knots and is zero
elsewhere.
B-spline Curve
--------------
A B-spline curve of order `k` is defined as a linear combination of control points :math:`p_i` and
B-spline basis functions :math:`B_i^k(t)` given by
.. math::
S^k(t) = \sum_{i=0}^{n} p_i\; B_i^k(t) ,\quad n \ge k - 1,\; t \in [t_{k-1}, t_{n+1}]
In this context the control points are called De Boor points. The basis functions :math:`B_i^k(t)`
is defined on a knot vector
.. math::
T = (t_0, t_1, \ldots, t_{k-1}, t_k, t_{k+1}, \ldots, t_{n-1}, t_n, t_{n+1}, \ldots, t_{n+k})
where there are :math:`n+k+1` elements, i.e. the number of control points :math:`n+1` plus the order
of the curve `k`. Each knot span :math:`t_i \le t \le t_{i+1}` is mapped onto a polynomial curve
between two successive joints :math:`S(t_i)` and :math:`S(t_{i+1})`.
Unlike Bézier curves, B-spline curves do not in general pass through the two end control points.
Increasing the multiplicity of a knot reduces the continuity of the curve at that knot.
Specifically, the curve is :math:`(k-p-1)` times continuously differentiable at a knot with
multiplicity :math:`p (\le k)`, and thus has :math:`C^{k-p-1}` continuity. Therefore, the control
polygon will coincide with the curve at a knot of multiplicity :math:`k-1`, and a knot with
multiplicity `k` indicates :math:`C^{-1}` continuity, or a discontinuous curve. Repeating the knots
at the end `k` times will force the endpoints to coincide with the control polygon. Thus the first
and the last control points of a curve with a knot vector described by
.. math::
\begin{eqnarray}
T = (
\underbrace{t_0, t_1, \ldots, t_{k-1},}_{\mbox{$k$ equal knots}}
\quad
\underbrace{t_k, t_{k+1}, \ldots, t_{n-1}, t_n,}_{\mbox{$n$-$k$+1 internal knots}}
\quad
\underbrace{t_{n+1}, \ldots, t_{n+k}}_{\mbox{$k$ equal knots}})
\end{eqnarray}
coincide with the endpoints of the curve. Such knot vectors and curves are known as *clamped*. In
other words, *clamped/unclamped* refers to whether both ends of the knot vector have multiplicity
equal to `k` or not.
**Local support property**: A single span of a B-spline curve is controlled only by `k` control
points, and any control point affects `k` spans. Specifically, changing :math:`p_i` affects the
curve in the parameter range :math:`t_i < t < t_{i+k}` and the curve at a point where :math:`t_r < t
< t_{r+1}` is determined completely by the control points :math:`p_{r-(k-1)}, \ldots, p_r`.
**B-spline to Bézier property**: From the discussion of end points geometric property, it can be
seen that a Bézier curve of order `k` (degree :math:`k-1`) is a B-spline curve with no internal
knots and the end knots repeated `k` times. The knot vector is thus
.. math::
\begin{eqnarray}
T = (
\underbrace{t_0, t_1, \ldots, t_{k-1}}_{\mbox{$k$ equal knots}}
,\quad
\underbrace{t_{n+1}, \ldots, t_{n+k}}_{\mbox{$k$ equal knots}}
)
\end{eqnarray}
where :math:`n+k+1 = 2k` or :math:`n = k-1`.
Algorithms for B-spline curves
------------------------------
Evaluation and subdivision algorithm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
A B-spline curve can be evaluated at a specific parameter value `t` using the de Boor algorithm,
which is a generalization of the de Casteljau algorithm. The repeated substitution of the recursive
definition of the B-spline basis function into the previous definition and re-indexing leads to the
following de Boor algorithm:
.. math::
S(t) = \sum_{i=0}^{n+j} p_i^j B_i^{k-j}(t) ,\quad j = 0, 1, \ldots, k-1
where
.. math::
p_i^j = \Big[1 - w_i^j\Big] p_{i-1}^{j-1} + w_i^j p_i^{j-1}, \; j > 0
with
.. math::
w_i^j = \frac{t - t_i}{t_{i+k-j} - t_i} \quad \textrm{and} \; p_j^0 = p_j
For :math:`j = k-1`, the B-spline basis function reduces to :math:`B_l^1` for :math:`t \in [t_l,
t_{l+1}]`, and :math:`p_l^{k-1}` coincides with the curve :math:`S(t) = p_l^{k-1}`.
The de Boor algorithm is a generalization of the de Casteljau algorithm. The de Boor algorithm also
permits the subdivision of the B-spline curve into two segments of the same order.
De Boor Algorithm
~~~~~~~~~~~~~~~~~
Let the index `l` define the knot interval that contains the position, :math:`t \in [t_l ,
t_{l+1}]`. We can see in the recursion formula that only B-splines with :math:`i = l-K, \dots, l`
are non-zero for this knot interval, where :math:`K = k - 1` is the degree. Thus, the sum is
reduced to:
.. math::
S^k(t) = \sum _{i=l-K}^{l} p_{i} B_i^k(t)
The algorithm does not compute the B-spline functions :math:`B_i^k(t)` directly. Instead it
evaluates :math:`S(t)` through an equivalent recursion formula.
Let :math:`d _i^r` be new control points with :math:`d_i^1 = p_i` for :math:`i = l-K, \dots, l`.
For :math:`r = 2, \dots, k` the following recursion is applied:
.. math::
d_i^r = (1 - w_i^r) d_{i-1}^{r-1} + w_i^r d_i^{r-1} \quad i = l-K+r, \dots, l
w_i^r = \frac{t - t_i}{t_{i+1+l-r} - t_{i}}
Once the iterations are complete, we have :math:`S^k(t) = d_l^k`.
.. , meaning that :math:`d_l^k` is the desired result.
De Boor's algorithm is more efficient than an explicit calculation of B-splines :math:`B_i^k(t)`
with the Cox-de Boor recursion formula, because it does not compute terms which are guaranteed to be
multiplied by zero.
..
:math:`S(t) = p_j^k` for :math:`t \in [t_j , t_{j+1}[` for :math:`k \le j \le n` with the following relation:
.. math::
\begin{split}
p_i^{r+1} &= \frac{t - t_i}{t_{i+k-r} - t} p_i^r + \frac{t_{i+k-r} - t_i}{t_{i+k-r} - t_i} p_{i-1}^r \\
&= w_i^{k-r}(t) p_i^r + (1 - w_i^{k-r}(t)) p_{i-1}^r
\end{split}
Knot insertion
~~~~~~~~~~~~~~
A knot can be inserted into a B-spline curve without changing the
geometry of the curve. The new curve is identical to
.. math::
\begin{array}{lcl}
\sum_{i=0}^n p_i B_i^k(t) & \textrm{becomes} & \sum_{i=0}^{n+1} \bar{p}_i \bar B_i^k(t) \\
\mbox{over}\; T = (t_0, t_1, \ldots, t_l, t_{l+1}, \ldots) & &
\mbox{over}\; T = (t_0, t_1, \ldots, t_l, \bar t, t_{l+1}, \ldots) & &
\end{array}
when a new knot :math:`\bar t` is inserted between knots :math:`t_l` and :math:`t_{l+1}`. The new
de Boor points are given by
.. math::
\bar{p}_i = (1 - w_i) p_{i-1} + w_i p_i
where
.. math::
w_i =
\left\{ \begin{array}{ll}
1 & i \le l-k+1 \\
0 & i \ge l+1 \\
\frac{\bar{t} - t_i}{t_{l+k-1} - t_i} & l-k+2 \le i \leq l
\end{array}
\right.
The above algorithm is also known as **Boehm's algorithm**. A more general (but also more complex)
insertion algorithm permitting insertion of several (possibly multiple) knots into a B-spline knot
vector, known as the Oslo algorithm, was developed by Cohen et al.
A B-spline curve is :math:`C^{\infty}` continuous in the interior of a span. Within exact
arithmetic, inserting a knot does not change the curve, so it does not change the continuity.
However, if any of the control points are moved after knot insertion, the continuity at the knot
will become :math:`C^{k-p-1}`, where `p` is the multiplicity of the knot.
The B-spline curve can be subdivided into Bézier segments by knot insertion at each internal knot
until the multiplicity of each internal knot is equal to `k`.