WIP: more user friendly documentation on implementation details

This commit is contained in:
Youmu 2020-07-22 22:02:16 -04:00
parent f5ce7716bf
commit 7a06ca83ca
3 changed files with 381 additions and 6 deletions

View File

@ -48,10 +48,10 @@ The algorithms used are based on a paper by Erickson and Whittlesey \cgalCite{ew
\subsection SMTopology_simplicity Simplicity Test
Given a cycle drawn on a surface one can ask if the cycle can be continously deformed to a cycle that does not intersect with itself. Cycles that deform to a non-self-intersecting cycle are said <em>simple</em>. Any contractible cycle is simple but this is not true for cycles on more complicated topology. The algorithm in this section is purely topological and do not assume any geometry on the input surface.
Given a cycle drawn on a surface one can ask if the cycle can be continously deformed to a cycle that does not intersect with itself. Any contractible cycle deforms to a simple cycle but this is not true for more complicated cycles. The algorithm in this section is purely topological and do not assume any geometry on the input surface.
The algorithm implemented in this package builds a data structure to efficiently answer queries of the following forms:
- Given a combinatorial surface \f$\cal{M}\f$ and a closed combinatorial curve specified as a sequence of edges of \f$\cal{M}\f$, decide if the curve is simple on \f$\cal{M}\f$.
- Given a combinatorial surface \f$\cal{M}\f$ and a closed combinatorial curve specified as a sequence of edges of \f$\cal{M}\f$, decide if the curve is homotopic to a simple one on \f$\cal{M}\f$.
The algorithm used is based on a paper by Despré and Lazarus \cgalCite{cgal:dl-cginc-19}, providing a \f$O(n + l\log{l})\f$-time algorithm where \f$n\f$ is the complexity of \f$\cal{M}\f$ and \f$l\f$ is the length of the path.
@ -286,13 +286,33 @@ The canonical form of a curve is obtained by flattening its brackets, removing i
It can be proven that the canonical form is uniquely defined and only depends on the homotopy class of the curve. Hence, the curves \f$C'\f$ and \f$D'\f$ in \f$\cal{Q}\f$ are homotopic if and only if their canonical forms are equal. Since each curve is defined as a sequence of (oriented) edges up to a cyclic permutation, we resort to the Knuth-Morris-Pratt algorithm to decide in linear time if the canonical forms are the same up to a cyclic permutation.
\subsection SMTopology_Simplicity_Test Simplicity Test
It can be shown that a closed curve is simple up to homotopy if and only if there is no intersection between any two geodesic liftings of the curve to the universal covering space embedded in a Poincaré Disk. Moreover, the Poincaré Disk can be tessellated by regular fundamental polygons.
It can be shown that a closed curve is homotopic to a simple one if and only if its canonical form can be made intersection free via infinitesimal perturbations and some homotopy preserving operations. One can imagine each edge in the quadrangulation has width and each vertex in the quadrangulation has area so that paths visiting the same vertex/edge multiple times can avoid intersection within a vertex or an edge. See \cgalFigureRef{fig_perturbation_sample} for an example.
\cgalFigureBegin{fig_perturbation_sample,perturbation_sample.svg}
Applying perturbation to reduce 2 intersections between red subpath and blue subpath.
\cgalFigureEnd
The quadrangulation represents the local connectivity information of the tesselation. Let \f$C\f$ be a closed curve and \f$\cal{C}\f$ be its canonical form. It can be proven that instead of considering the geodesic in a Poincaré Disk, \f$C\f$ is simple up to homotopy if and only if it is possible to arrange an ordering for edges in \f$\cal{C}\f$ with the same projection such that there is no self-intersection in the projection of \f$\cal{C}\f$ while allowing certain homotopy-preserving operation known as <em>switch</em>.
Such perturbation can be expressed as orderings of edges from the canonical form which cross the same edge in the quadrangulation. The idea of the algorithm is to inductively build such ordering and try to avoid intersection as best as we can.
Given \f$\cal{C}\f$, the algorithm preprocesses the path in linear time to identify all possible switches. The algorithm then processes edges in \f$\cal{C}\f$ one by one, tries to switch if an intersection can be avoided and then tries to insert the edge in the existing ordering without creating any intersection. Finally the ordering is checked to make sure it is indeed intersection free.
\subsubsection SMTopology_Simplicity_Test_Primitive Detect Repetition
It can be proven that if the canonical form can be expressed as concatenation of two or more copies of the same path, the path can never be made to be intersection free. So the first step of the algorithm is to detect repetition.
To speed up determining relative order to the first edge, the algorithm uses a modified version of Knuth-Morris-Pratt algorithm to pre-compute the relative ordering in linear time. The orderings are maintained in red-black trees, contributing to the \f$\log{l}\f$ factor in the running time.
Let \f$P\f$ be a path and let \f$+\f$ be the operator of concatenation. It can be shown that \f$P\f$ contains no repetition if and only if there are only 2 matchings of \f$P\f$ in \f$P+P\f$ (matching the first and the second copy). The algorithm resorts to the Knuth-Morris-Pratt algorithm to decide in linear time if the canonical form contains no repetition.
\subsubsection SMTopology_Simplicity_Test_Switch Avoid Crossing by Switching
Apart from applying perturbation, the algorithm also tries to avoid crossing using a homotopy-preserving operation known as <em>switch</em>. A switch is triggered when intersection could be avoided by turning a left L-shaped subpath into a right L-shaped subpath. See \cgalFigureRef{fig_switch_sample} for an example.
\subsubsection SMTopology_Simplicity_Test_Relative_Order Decide Relative Order
Since the algorithm inductively builds an ordering, it has to determine relative order between the edge being processed and edges that has been ordered. There are three cases to consider.
-# The predecessor of the current edge is adjacent to another edge which forms the same turn of the predecessor and the current edge. In this case, the current edge must be adjacent to the edge forming the turn. See \cgalFigureRef{fig_relative_order_corner}.
\cgalFigureBegin{fig_relative_order_corner,relative_order_corner.svg}
The red edge is being processed. The blue edge is adjacent to green edge (the predecessor of the red edge) in the previously constructed ordering and forms a turn (blue-pink turn) same to the green-red turn. The red edge must be right next to the pink edge in the ordering.
\cgalFigureEnd
-# Comparing the current edge with another edge by the predecessor of the current edge. If the edge being compared against has predecessor/successor extended to the direction of the predecessor of the current edge, and they doesn't form the same turn. The relative order of edges around the vertex can be used as the relative order between the edges. See \cgalFigureRef{fig_relative_order_normal}.
\cgalFigureBegin{fig_relative_order_normal,relative_order_normal.svg}
The red edge is being processed and is compared against the pink edge. Since the green edge (the predecessor of the red edge) is to the right of the blue edge around the vertex, red edge must be to the right of the pink edge in the ordering.
\cgalFigureEnd
\section Implementation History

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="111.61311mm"
height="86.782814mm"
viewBox="0 0 111.61311 86.782814"
version="1.1"
id="svg8"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
sodipodi:docname="relative_order_corner.svg">
<defs
id="defs2">
<marker
inkscape:stockid="Arrow2Send"
orient="auto"
refY="0"
refX="0"
id="Arrow2Send"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path973"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-0.3,0,0,-0.3,0.69,0)" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="marker2042"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Send">
<path
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
style="fill:#00b600;fill-opacity:1;fill-rule:evenodd;stroke:#00b600;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path2040" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="Arrow1Send"
style="overflow:visible"
inkscape:isstock="true"
inkscape:collect="always">
<path
id="path955"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path949"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#00b600;fill-opacity:1;fill-rule:evenodd;stroke:#00b600;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="DotL"
orient="auto"
refY="0"
refX="0"
id="DotL"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path1001"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
style="fill:#c8b7b7;fill-opacity:1;fill-rule:evenodd;stroke:#c8b7b7;stroke-width:1pt;stroke-opacity:1"
transform="matrix(0.8,0,0,0.8,5.92,0.8)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.979899"
inkscape:cx="23.433098"
inkscape:cy="248.07296"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
inkscape:snap-object-midpoints="false"
inkscape:snap-global="false"
fit-margin-top="1"
fit-margin-left="1"
fit-margin-bottom="1"
fit-margin-right="1"
inkscape:window-width="2560"
inkscape:window-height="1361"
inkscape:window-x="-9"
inkscape:window-y="472"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-12.488216,-63.752846)">
<circle
style="fill:#c8b7b7;fill-opacity:1;stroke:#c8b7b7;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path1896"
cx="109.4241"
cy="82.115334"
r="6.3311014" />
<circle
r="6.3311014"
cy="128.22842"
cx="38.45908"
id="path1896-7"
style="fill:#c8b7b7;fill-opacity:1;stroke:#c8b7b7;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
<circle
r="6.3311014"
cy="91.09227"
cx="63.688988"
id="path1896-1"
style="fill:#c8b7b7;fill-opacity:1;stroke:#c8b7b7;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
<path
style="fill:none;stroke:#c8b7b7;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 109.4241,82.115334 63.688986,91.092268 38.459079,128.22842"
id="path1966" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 106.77827,81.075893 15.9695,-15.969494"
id="path1978" />
<path
id="path2790"
d="M 62.082589,90.052828 106.77827,81.075893"
style="fill:none;stroke:#ff00ff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1976"
d="M 37.325149,126.62202 62.082589,90.052828"
style="fill:none;stroke:#0a0aff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 13.701637,137.77232 23.623512,-11.1503"
id="path1970" />
<path
style="fill:none;stroke:#ff0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Send)"
d="M 64.673853,92.202613 108.63969,83.38272"
id="path1986" />
<path
style="fill:none;stroke:#00b600;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker2042)"
d="M 40.218691,128.81854 64.673853,92.202613"
id="path1984" />
<path
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow2Send)"
d="m 35.942378,149.3983 0.267269,-0.93545 4.009044,-19.64431"
id="path1980" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.0 KiB

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
sodipodi:docname="relative_order_normal.svg"
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"
id="svg8"
version="1.1"
viewBox="0 0 106.64698 66.343393"
height="66.343391mm"
width="106.64698mm">
<defs
id="defs2">
<marker
inkscape:isstock="true"
style="overflow:visible"
id="Arrow2Send"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow2Send">
<path
transform="matrix(-0.3,0,0,-0.3,0.69,0)"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
style="fill:#0a0aff;fill-opacity:1;fill-rule:evenodd;stroke:#0a0aff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
id="path973" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0"
refX="0"
id="marker2042"
style="overflow:visible"
inkscape:isstock="true">
<path
id="path2040"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
style="fill:#00b600;fill-opacity:1;fill-rule:evenodd;stroke:#00b600;stroke-width:1pt;stroke-opacity:1"
transform="matrix(-0.2,0,0,-0.2,-1.2,0)" />
</marker>
<marker
inkscape:collect="always"
inkscape:isstock="true"
style="overflow:visible"
id="Arrow1Send"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Send">
<path
transform="matrix(-0.2,0,0,-0.2,-1.2,0)"
style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path955" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="Arrow1Mend"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="Arrow1Mend">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:#00b600;fill-opacity:1;fill-rule:evenodd;stroke:#00b600;stroke-width:1pt;stroke-opacity:1"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path949" />
</marker>
<marker
inkscape:isstock="true"
style="overflow:visible"
id="DotL"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotL">
<path
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
style="fill:#c8b7b7;fill-opacity:1;fill-rule:evenodd;stroke:#c8b7b7;stroke-width:1pt;stroke-opacity:1"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path1001" />
</marker>
</defs>
<sodipodi:namedview
inkscape:window-maximized="1"
inkscape:window-y="472"
inkscape:window-x="-9"
inkscape:window-height="1361"
inkscape:window-width="2560"
fit-margin-right="1"
fit-margin-bottom="1"
fit-margin-left="1"
fit-margin-top="1"
inkscape:snap-global="false"
inkscape:snap-object-midpoints="false"
showgrid="false"
inkscape:document-rotation="0"
inkscape:current-layer="layer1"
inkscape:document-units="mm"
inkscape:cy="248.07296"
inkscape:cx="101.89067"
inkscape:zoom="1.979899"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
borderopacity="1.0"
bordercolor="#666666"
pagecolor="#ffffff"
id="base" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(-17.454339,-63.752846)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<circle
r="6.3311014"
cy="82.115334"
cx="109.4241"
id="path1896"
style="fill:#c8b7b7;fill-opacity:1;stroke:#c8b7b7;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal" />
<circle
style="fill:#c8b7b7;fill-opacity:1;stroke:#c8b7b7;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="path1896-1"
cx="63.688988"
cy="91.09227"
r="6.3311014" />
<path
sodipodi:nodetypes="cc"
id="path1966"
d="M 109.4241,82.115334 63.688986,91.092268"
style="fill:none;stroke:#c8b7b7;stroke-width:6;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1978"
d="m 106.77827,81.075893 15.9695,-15.969494"
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path2794"
d="M 62.082589,90.052828 106.77827,81.075893"
style="fill:none;stroke:#ff00ff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1976"
d="M 18.616282,75.172636 62.082589,90.052828"
style="fill:none;stroke:#0a0aff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1986"
d="M 64.673853,92.202613 108.63969,83.38272"
style="fill:none;stroke:#ff0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Send)" />
<path
id="path1984"
d="M 40.218691,128.81854 64.673853,92.202613"
style="fill:none;stroke:#00b600;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker2042)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB