summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKrzysztof Kosi??ski <tweenk.pl@gmail.com>2015-07-04 16:15:46 +0000
committerKrzysztof KosiƄski <tweenk.pl@gmail.com>2015-07-04 16:15:46 +0000
commit1112ab0a12fc0cb5a6b00d1bbd5b100c55eedff8 (patch)
treea91517f9165322b4e42c6cdeb4263beaedc4d02f /src
parentPackaging. New Win32 installer Danish translation. (diff)
parentUpgrade to 2Geom r2413 (diff)
downloadinkscape-1112ab0a12fc0cb5a6b00d1bbd5b100c55eedff8.tar.gz
inkscape-1112ab0a12fc0cb5a6b00d1bbd5b100c55eedff8.zip
Sync 2Geom to revision 2413.
May introduce regressions. (bzr r14226)
Diffstat (limited to 'src')
-rw-r--r--src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README18
-rw-r--r--src/2geom/2geom.h6
-rw-r--r--src/2geom/CMakeLists.txt30
-rw-r--r--src/2geom/Makefile_insert41
-rw-r--r--src/2geom/affine.cpp118
-rw-r--r--src/2geom/affine.h6
-rw-r--r--src/2geom/angle.h332
-rw-r--r--src/2geom/basic-intersection.cpp177
-rw-r--r--src/2geom/basic-intersection.h94
-rw-r--r--src/2geom/bezier-clipping.cpp345
-rw-r--r--src/2geom/bezier-curve.cpp153
-rw-r--r--src/2geom/bezier-curve.h133
-rw-r--r--src/2geom/bezier-to-sbasis.h10
-rw-r--r--src/2geom/bezier-utils.cpp6
-rw-r--r--src/2geom/bezier.cpp324
-rw-r--r--src/2geom/bezier.h505
-rw-r--r--src/2geom/cairo-path-sink.cpp123
-rw-r--r--src/2geom/cairo-path-sink.h (renamed from src/2geom/conjugate_gradient.h)59
-rw-r--r--src/2geom/circle-circle.cpp139
-rw-r--r--src/2geom/circle.cpp291
-rw-r--r--src/2geom/circle.h142
-rw-r--r--src/2geom/circulator.h9
-rw-r--r--src/2geom/concepts.h56
-rw-r--r--src/2geom/conic_section_clipper.h11
-rw-r--r--src/2geom/conic_section_clipper_cr.h11
-rw-r--r--src/2geom/conic_section_clipper_impl.cpp17
-rw-r--r--src/2geom/conic_section_clipper_impl.h22
-rw-r--r--src/2geom/conicsec.cpp116
-rw-r--r--src/2geom/conicsec.h31
-rw-r--r--src/2geom/conjugate_gradient.cpp143
-rw-r--r--src/2geom/convex-cover.h204
-rw-r--r--src/2geom/convex-hull.cpp (renamed from src/2geom/convex-cover.cpp)462
-rw-r--r--src/2geom/convex-hull.h346
-rw-r--r--src/2geom/coord.cpp3688
-rw-r--r--src/2geom/coord.h108
-rw-r--r--src/2geom/crossing.cpp2
-rw-r--r--src/2geom/crossing.h29
-rw-r--r--src/2geom/curve.cpp157
-rw-r--r--src/2geom/curve.h141
-rw-r--r--src/2geom/curves.h11
-rw-r--r--src/2geom/d2-sbasis.cpp4
-rw-r--r--src/2geom/d2-sbasis.h10
-rw-r--r--src/2geom/d2.h153
-rw-r--r--src/2geom/ellipse.cpp656
-rw-r--r--src/2geom/ellipse.h261
-rw-r--r--src/2geom/elliptical-arc-from-sbasis.cpp (renamed from src/2geom/svg-elliptical-arc.cpp)174
-rw-r--r--src/2geom/elliptical-arc.cpp593
-rw-r--r--src/2geom/elliptical-arc.h274
-rw-r--r--src/2geom/exception.h4
-rw-r--r--src/2geom/forward.h25
-rw-r--r--src/2geom/generic-interval.h29
-rw-r--r--src/2geom/generic-rect.h36
-rw-r--r--src/2geom/geom.cpp13
-rw-r--r--src/2geom/geom.h42
-rw-r--r--src/2geom/hvlinesegment.h283
-rw-r--r--src/2geom/int-point.h47
-rw-r--r--src/2geom/intersection-graph.cpp394
-rw-r--r--src/2geom/intersection-graph.h128
-rw-r--r--src/2geom/intersection.h147
-rw-r--r--src/2geom/interval.h44
-rw-r--r--src/2geom/line.cpp455
-rw-r--r--src/2geom/line.h402
-rw-r--r--src/2geom/linear.h66
-rw-r--r--src/2geom/nearest-time.cpp (renamed from src/2geom/nearest-point.cpp)211
-rw-r--r--src/2geom/nearest-time.h (renamed from src/2geom/nearest-point.h)86
-rw-r--r--src/2geom/numeric/fitting-model.h6
-rw-r--r--src/2geom/numeric/matrix.h20
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs-operation.h7
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs-trace.h8
-rw-r--r--src/2geom/numeric/symmetric-matrix-fs.h5
-rw-r--r--src/2geom/numeric/vector.h13
-rw-r--r--src/2geom/ord.h9
-rw-r--r--src/2geom/path-intersection.cpp107
-rw-r--r--src/2geom/path-intersection.h24
-rw-r--r--src/2geom/path-sink.cpp95
-rw-r--r--src/2geom/path-sink.h111
-rw-r--r--src/2geom/path.cpp1398
-rw-r--r--src/2geom/path.h1450
-rw-r--r--src/2geom/pathvector.cpp233
-rw-r--r--src/2geom/pathvector.h309
-rw-r--r--src/2geom/piecewise.h19
-rw-r--r--src/2geom/point-ops.h25
-rw-r--r--src/2geom/point.cpp14
-rw-r--r--src/2geom/point.h179
-rw-r--r--src/2geom/polynomial.cpp (renamed from src/2geom/poly.cpp)133
-rw-r--r--src/2geom/polynomial.h (renamed from src/2geom/poly.h)21
-rw-r--r--src/2geom/quadtree.cpp290
-rw-r--r--src/2geom/quadtree.h99
-rw-r--r--src/2geom/ray.h4
-rw-r--r--src/2geom/rect.cpp11
-rw-r--r--src/2geom/rect.h8
-rw-r--r--src/2geom/recursive-bezier-intersection.cpp5
-rw-r--r--src/2geom/region.cpp45
-rw-r--r--src/2geom/region.h130
-rw-r--r--src/2geom/sbasis-2d.cpp2
-rw-r--r--src/2geom/sbasis-2d.h8
-rw-r--r--src/2geom/sbasis-curve.h27
-rw-r--r--src/2geom/sbasis-geometric.h44
-rw-r--r--src/2geom/sbasis-math.cpp4
-rw-r--r--src/2geom/sbasis-math.h11
-rw-r--r--src/2geom/sbasis-poly.h23
-rw-r--r--src/2geom/sbasis-roots.cpp52
-rw-r--r--src/2geom/sbasis-to-bezier.cpp44
-rw-r--r--src/2geom/sbasis-to-bezier.h14
-rw-r--r--src/2geom/sbasis.cpp10
-rw-r--r--src/2geom/sbasis.h115
-rw-r--r--src/2geom/shape.cpp689
-rw-r--r--src/2geom/shape.h148
-rw-r--r--src/2geom/solve-bezier-one-d.cpp30
-rw-r--r--src/2geom/solve-bezier-parametric.cpp51
-rw-r--r--src/2geom/solve-bezier.cpp57
-rw-r--r--src/2geom/solver.h11
-rw-r--r--src/2geom/svg-elliptical-arc.h282
-rw-r--r--src/2geom/svg-path-parser.cpp2191
-rw-r--r--src/2geom/svg-path-parser.h155
-rw-r--r--src/2geom/svg-path-writer.cpp296
-rw-r--r--src/2geom/svg-path-writer.h122
-rw-r--r--src/2geom/sweep-bounds.cpp (renamed from src/2geom/sweep.cpp)28
-rw-r--r--src/2geom/sweep-bounds.h (renamed from src/2geom/sweep.h)21
-rw-r--r--src/2geom/sweeper.h252
-rw-r--r--src/2geom/toposweep.cpp16
-rw-r--r--src/2geom/toposweep.h18
-rw-r--r--src/2geom/transforms.cpp26
-rw-r--r--src/2geom/transforms.h6
-rw-r--r--src/2geom/utils.h18
-rw-r--r--src/2geom/viewbox.cpp133
-rw-r--r--src/2geom/viewbox.h102
-rw-r--r--src/conn-avoid-ref.cpp4
-rw-r--r--src/desktop-events.cpp4
-rw-r--r--src/display/cairo-utils.cpp8
-rw-r--r--src/display/curve-test.h4
-rw-r--r--src/display/curve.cpp16
-rw-r--r--src/display/curve.h2
-rw-r--r--src/display/drawing-image.cpp2
-rw-r--r--src/extension/implementation/implementation.h9
-rw-r--r--src/extension/internal/emf-print.cpp20
-rw-r--r--src/extension/internal/javafx-out.cpp9
-rw-r--r--src/extension/internal/latex-pstricks.cpp5
-rw-r--r--src/extension/internal/metafile-print.cpp8
-rw-r--r--src/extension/internal/odf.cpp5
-rw-r--r--src/extension/internal/pov-out.cpp5
-rw-r--r--src/extension/internal/wmf-print.cpp19
-rw-r--r--src/gradient-chemistry.cpp6
-rw-r--r--src/gradient-drag.cpp22
-rw-r--r--src/helper/geom-curves.h10
-rw-r--r--src/helper/geom-pathstroke.cpp153
-rw-r--r--src/helper/geom.cpp48
-rw-r--r--src/helper/geom.h7
-rw-r--r--src/libdepixelize/priv/splines-kopf2011.h2
-rw-r--r--src/livarot/PathConversion.cpp6
-rw-r--r--src/livarot/PathCutting.cpp24
-rw-r--r--src/livarot/PathOutline.cpp8
-rw-r--r--src/livarot/PathSimplify.cpp2
-rw-r--r--src/livarot/PathStroke.cpp16
-rw-r--r--src/livarot/Shape.cpp4
-rw-r--r--src/livarot/ShapeSweep.cpp38
-rw-r--r--src/livarot/sweep-tree.cpp10
-rw-r--r--src/live_effects/Makefile_insert2
-rw-r--r--src/live_effects/effect.cpp94
-rw-r--r--src/live_effects/effect.h8
-rw-r--r--src/live_effects/lpe-angle_bisector.cpp10
-rw-r--r--src/live_effects/lpe-angle_bisector.h2
-rw-r--r--src/live_effects/lpe-attach-path.cpp16
-rw-r--r--src/live_effects/lpe-boolops.cpp79
-rw-r--r--src/live_effects/lpe-boolops.h53
-rw-r--r--src/live_effects/lpe-bounding-box.cpp2
-rw-r--r--src/live_effects/lpe-bspline.cpp12
-rw-r--r--src/live_effects/lpe-circle_3pts.cpp11
-rw-r--r--src/live_effects/lpe-circle_3pts.h2
-rw-r--r--src/live_effects/lpe-circle_with_radius.cpp16
-rw-r--r--src/live_effects/lpe-circle_with_radius.h2
-rw-r--r--src/live_effects/lpe-clone-original.cpp2
-rw-r--r--src/live_effects/lpe-constructgrid.cpp6
-rw-r--r--src/live_effects/lpe-constructgrid.h2
-rw-r--r--src/live_effects/lpe-curvestitch.cpp10
-rw-r--r--src/live_effects/lpe-curvestitch.h2
-rw-r--r--src/live_effects/lpe-ellipse_5pts.cpp8
-rw-r--r--src/live_effects/lpe-ellipse_5pts.h2
-rw-r--r--src/live_effects/lpe-fill-between-many.cpp4
-rw-r--r--src/live_effects/lpe-fill-between-strokes.cpp26
-rw-r--r--src/live_effects/lpe-fillet-chamfer.cpp32
-rw-r--r--src/live_effects/lpe-fillet-chamfer.h8
-rw-r--r--src/live_effects/lpe-gears.cpp6
-rw-r--r--src/live_effects/lpe-gears.h2
-rw-r--r--src/live_effects/lpe-interpolate.cpp2
-rw-r--r--src/live_effects/lpe-interpolate.h2
-rw-r--r--src/live_effects/lpe-interpolate_points.h2
-rw-r--r--src/live_effects/lpe-jointype.cpp4
-rw-r--r--src/live_effects/lpe-jointype.h2
-rw-r--r--src/live_effects/lpe-knot.cpp20
-rw-r--r--src/live_effects/lpe-knot.h10
-rw-r--r--src/live_effects/lpe-lattice2.cpp4
-rw-r--r--src/live_effects/lpe-line_segment.cpp16
-rw-r--r--src/live_effects/lpe-line_segment.h2
-rw-r--r--src/live_effects/lpe-mirror_symmetry.cpp10
-rw-r--r--src/live_effects/lpe-mirror_symmetry.h2
-rw-r--r--src/live_effects/lpe-offset.cpp6
-rw-r--r--src/live_effects/lpe-parallel.cpp2
-rw-r--r--src/live_effects/lpe-perp_bisector.cpp4
-rw-r--r--src/live_effects/lpe-perspective-envelope.cpp4
-rw-r--r--src/live_effects/lpe-powerstroke.cpp122
-rw-r--r--src/live_effects/lpe-powerstroke.h4
-rw-r--r--src/live_effects/lpe-rough-hatches.cpp3
-rw-r--r--src/live_effects/lpe-roughen.cpp4
-rw-r--r--src/live_effects/lpe-ruler.cpp2
-rw-r--r--src/live_effects/lpe-show_handles.cpp19
-rw-r--r--src/live_effects/lpe-show_handles.h2
-rw-r--r--src/live_effects/lpe-simplify.cpp15
-rw-r--r--src/live_effects/lpe-skeleton.cpp6
-rw-r--r--src/live_effects/lpe-skeleton.h4
-rw-r--r--src/live_effects/lpe-sketch.cpp2
-rw-r--r--src/live_effects/lpe-spiro.cpp3
-rw-r--r--src/live_effects/lpe-tangent_to_curve.cpp8
-rw-r--r--src/live_effects/lpe-taperstroke.cpp11
-rw-r--r--src/live_effects/lpe-test-doEffect-stack.cpp6
-rw-r--r--src/live_effects/lpe-test-doEffect-stack.h2
-rw-r--r--src/live_effects/lpe-vonkoch.cpp22
-rw-r--r--src/live_effects/lpe-vonkoch.h2
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.cpp29
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.h6
-rw-r--r--src/live_effects/parameter/originalpatharray.h2
-rw-r--r--src/live_effects/parameter/path.cpp6
-rw-r--r--src/live_effects/parameter/path.h6
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp4
-rw-r--r--src/main.cpp2
-rw-r--r--src/object-snapper.cpp12
-rw-r--r--src/sp-conn-end.cpp2
-rw-r--r--src/sp-ellipse.cpp1
-rw-r--r--src/sp-gradient-test.h14
-rw-r--r--src/sp-offset.cpp2
-rw-r--r--src/sp-path.cpp3
-rw-r--r--src/sp-polygon.cpp3
-rw-r--r--src/splivarot.cpp47
-rw-r--r--src/svg/svg-path.cpp36
-rw-r--r--src/ui/tool/node.cpp11
-rw-r--r--src/ui/tool/node.h4
-rw-r--r--src/ui/tool/path-manipulator.cpp39
-rw-r--r--src/ui/tools/calligraphic-tool.cpp3
-rw-r--r--src/ui/tools/dropper-tool.cpp3
-rw-r--r--src/ui/tools/eraser-tool.cpp24
-rw-r--r--src/ui/tools/gradient-tool.cpp2
-rw-r--r--src/ui/tools/mesh-tool.cpp2
-rw-r--r--src/ui/tools/pen-tool.cpp19
-rw-r--r--src/ui/tools/spray-tool.cpp3
-rw-r--r--src/ui/tools/tweak-tool.cpp3
245 files changed, 15310 insertions, 9139 deletions
diff --git a/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README b/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README
index 3ced704c7..074921de2 100644
--- a/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README
+++ b/src/2geom/!PLEASE DON'T MAKE CHANGES IN THESE FILES.README
@@ -1,9 +1,13 @@
-All code files in this directory are *direct* copies of the files in 2geom's BZR repo.
-If you want to change the code, please change it in 2geom, then copy the files here.
-Otherwise, I will probably miss that you changed something in Inkscape's copy, and
-destroy your changes by copying 2geom's files over it during the next time I update
-Inkscape's copy of 2geom.
- - Johan Engelen
+This is an in-tree copy of lib2geom, a 2D geometry library started
+by Inkscape developers. If you want to change code in 2Geom, you should
+commit it first to upstream repository and then execute this command:
+
+rsync -r --existing /path/to/lib2geom/src/2geom /path/to/inkscape/src/2geom
+
+The command above will only update existing files. If you add new files
+to 2Geom, you'll need to copy the new files manually. Same if you remove
+some files.
2geom's BZR repo = lp:lib2geom
-http://lib2geom.sourceforge.net \ No newline at end of file
+http://lib2geom.sourceforge.net
+
diff --git a/src/2geom/2geom.h b/src/2geom/2geom.h
index 000f3423d..813e243b3 100644
--- a/src/2geom/2geom.h
+++ b/src/2geom/2geom.h
@@ -31,8 +31,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LIB2GEOM_2GEOM_H
-#define SEEN_LIB2GEOM_2GEOM_H
+#ifndef LIB2GEOM_SEEN_2GEOM_H
+#define LIB2GEOM_SEEN_2GEOM_H
#include <2geom/forward.h>
@@ -62,7 +62,7 @@
#include <2geom/math-utils.h>
#include <2geom/utils.h>
-#endif // SEEN_LIB2GEOM_HEADER_H
+#endif // LIB2GEOM_SEEN_2GEOM_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/CMakeLists.txt b/src/2geom/CMakeLists.txt
index eeaecaa39..8a2884a2a 100644
--- a/src/2geom/CMakeLists.txt
+++ b/src/2geom/CMakeLists.txt
@@ -2,23 +2,26 @@
set(2geom_SRC
affine.cpp
basic-intersection.cpp
+ bezier.cpp
bezier-clipping.cpp
bezier-curve.cpp
bezier-utils.cpp
- circle-circle.cpp
+ cairo-path-sink.cpp
circle.cpp
# conic_section_clipper_impl.cpp
# conicsec.cpp
- conjugate_gradient.cpp
- convex-cover.cpp
+ convex-hull.cpp
+ coord.cpp
crossing.cpp
curve.cpp
d2-sbasis.cpp
ellipse.cpp
elliptical-arc.cpp
+ elliptical-arc-from-sbasis.cpp
geom.cpp
+ intersection-graph.cpp
line.cpp
- nearest-point.cpp
+ nearest-time.cpp
numeric/matrix.cpp
path-intersection.cpp
path-sink.cpp
@@ -26,11 +29,9 @@ set(2geom_SRC
pathvector.cpp
piecewise.cpp
point.cpp
- poly.cpp
- quadtree.cpp
+ polynomial.cpp
rect.cpp
# recursive-bezier-intersection.cpp
- region.cpp
sbasis-2d.cpp
sbasis-geometric.cpp
sbasis-math.cpp
@@ -38,16 +39,16 @@ set(2geom_SRC
sbasis-roots.cpp
sbasis-to-bezier.cpp
sbasis.cpp
- shape.cpp
solve-bezier.cpp
solve-bezier-one-d.cpp
solve-bezier-parametric.cpp
- svg-elliptical-arc.cpp
svg-path-parser.cpp
- sweep.cpp
+ svg-path-writer.cpp
+ sweep-bounds.cpp
toposweep.cpp
transforms.cpp
utils.cpp
+ viewbox.cpp
# -------
@@ -62,7 +63,6 @@ set(2geom_SRC
bezier.h
choose.h
circle.h
- circulator.h
concepts.h
conic_section_clipper.h
conic_section_clipper_cr.h
@@ -100,8 +100,7 @@ set(2geom_SRC
piecewise.h
point-ops.h
point.h
- poly.h
- quadtree.h
+ polynomial.h
ray.h
rect.h
region.h
@@ -114,9 +113,10 @@ set(2geom_SRC
sbasis.h
shape.h
solver.h
- svg-elliptical-arc.h
svg-path-parser.h
- sweep.h
+ svg-path-writer.h
+ sweeper.h
+ sweep-bounds.h
toposweep.h
transforms.h
utils.h
diff --git a/src/2geom/Makefile_insert b/src/2geom/Makefile_insert
index e77f413cb..01f48ab39 100644
--- a/src/2geom/Makefile_insert
+++ b/src/2geom/Makefile_insert
@@ -15,12 +15,14 @@
2geom/bezier-clipping.cpp \
2geom/bezier-curve.cpp \
2geom/bezier-curve.h \
+ 2geom/bezier.cpp \
2geom/bezier.h \
2geom/bezier-to-sbasis.h \
2geom/bezier-utils.cpp \
2geom/bezier-utils.h \
+ 2geom/cairo-path-sink.cpp \
+ 2geom/cairo-path-sink.h \
2geom/choose.h \
- 2geom/circle-circle.cpp \
2geom/circle.cpp \
2geom/circle.h \
2geom/circulator.h \
@@ -32,10 +34,9 @@
2geom/conic_section_clipper.h \
2geom/conic_section_clipper_impl.cpp \
2geom/conic_section_clipper_impl.h \
- 2geom/conjugate_gradient.cpp \
- 2geom/conjugate_gradient.h \
- 2geom/convex-cover.cpp \
- 2geom/convex-cover.h \
+ 2geom/convex-hull.cpp \
+ 2geom/convex-hull.h \
+ 2geom/coord.cpp \
2geom/coord.h \
2geom/crossing.cpp \
2geom/crossing.h \
@@ -49,6 +50,7 @@
2geom/ellipse.h \
2geom/elliptical-arc.cpp \
2geom/elliptical-arc.h \
+ 2geom/elliptical-arc-from-sbasis.cpp \
2geom/exception.h \
2geom/forward.h \
2geom/generic-interval.h \
@@ -56,6 +58,9 @@
2geom/geom.cpp \
2geom/geom.h \
2geom/hvlinesegment.h \
+ 2geom/intersection.h \
+ 2geom/intersection-graph.cpp \
+ 2geom/intersection-graph.h \
2geom/interval.h \
2geom/int-interval.h \
2geom/int-point.h \
@@ -64,8 +69,8 @@
2geom/line.cpp \
2geom/line.h \
2geom/math-utils.h \
- 2geom/nearest-point.cpp \
- 2geom/nearest-point.h \
+ 2geom/nearest-time.cpp \
+ 2geom/nearest-time.h \
2geom/ord.h \
2geom/path.cpp \
2geom/path.h \
@@ -79,17 +84,12 @@
2geom/piecewise.h \
2geom/point.cpp \
2geom/point.h \
- 2geom/point-ops.h \
- 2geom/poly.cpp \
- 2geom/poly.h \
- 2geom/quadtree.cpp \
- 2geom/quadtree.h \
+ 2geom/polynomial.cpp \
+ 2geom/polynomial.h \
2geom/ray.h \
2geom/rect.cpp \
2geom/rect.h \
2geom/recursive-bezier-intersection.cpp \
- 2geom/region.cpp \
- 2geom/region.h \
2geom/sbasis-2d.cpp \
2geom/sbasis-2d.h \
2geom/sbasis.cpp \
@@ -104,24 +104,25 @@
2geom/sbasis-roots.cpp \
2geom/sbasis-to-bezier.cpp \
2geom/sbasis-to-bezier.h \
- 2geom/shape.cpp \
- 2geom/shape.h \
2geom/solve-bezier.cpp \
2geom/solve-bezier-one-d.cpp \
2geom/solve-bezier-parametric.cpp \
2geom/solver.h \
- 2geom/svg-elliptical-arc.cpp \
- 2geom/svg-elliptical-arc.h \
2geom/svg-path-parser.cpp \
2geom/svg-path-parser.h \
- 2geom/sweep.cpp \
- 2geom/sweep.h \
+ 2geom/svg-path-writer.cpp \
+ 2geom/svg-path-writer.h \
+ 2geom/sweep-bounds.cpp \
+ 2geom/sweep-bounds.h \
+ 2geom/sweeper.h \
2geom/toposweep.cpp \
2geom/toposweep.h \
2geom/transforms.cpp \
2geom/transforms.h \
2geom/utils.cpp \
2geom/utils.h \
+ 2geom/viewbox.cpp \
+ 2geom/viewbox.h \
2geom/numeric/fitting-model.h \
2geom/numeric/fitting-tool.h \
2geom/numeric/linear_system.h \
diff --git a/src/2geom/affine.cpp b/src/2geom/affine.cpp
index 738d0fc48..48179e864 100644
--- a/src/2geom/affine.cpp
+++ b/src/2geom/affine.cpp
@@ -6,9 +6,10 @@
* This code is in public domain
*/
-#include <2geom/utils.h>
#include <2geom/affine.h>
#include <2geom/point.h>
+#include <2geom/polynomial.h>
+#include <2geom/utils.h>
namespace Geom {
@@ -36,8 +37,7 @@ Point Affine::yAxis() const {
return Point(_c[2], _c[3]);
}
-/** Gets the translation imparted by the Affine.
- */
+/// Gets the translation imparted by the Affine.
Point Affine::translation() const {
return Point(_c[4], _c[5]);
}
@@ -52,8 +52,7 @@ void Affine::setYAxis(Point const &vec) {
_c[i + 2] = vec[i];
}
-/** Sets the translation imparted by the Affine.
- */
+/// Sets the translation imparted by the Affine.
void Affine::setTranslation(Point const &loc) {
for(int i = 0; i < 2; i++)
_c[i + 4] = loc[i];
@@ -61,33 +60,35 @@ void Affine::setTranslation(Point const &loc) {
/** Calculates the amount of x-scaling imparted by the Affine. This is the scaling applied to
* the original x-axis region. It is \emph{not} the overall x-scaling of the transformation.
- * Equivalent to L2(m.xAxis())
- */
+ * Equivalent to L2(m.xAxis()). */
double Affine::expansionX() const {
return sqrt(_c[0] * _c[0] + _c[1] * _c[1]);
}
/** Calculates the amount of y-scaling imparted by the Affine. This is the scaling applied before
* the other transformations. It is \emph{not} the overall y-scaling of the transformation.
- * Equivalent to L2(m.yAxis())
- */
+ * Equivalent to L2(m.yAxis()). */
double Affine::expansionY() const {
return sqrt(_c[2] * _c[2] + _c[3] * _c[3]);
}
void Affine::setExpansionX(double val) {
double exp_x = expansionX();
- if(!are_near(exp_x, 0.0)) { //TODO: best way to deal with it is to skip op?
+ if (exp_x != 0.0) { //TODO: best way to deal with it is to skip op?
double coef = val / expansionX();
- for(unsigned i=0;i<2;i++) _c[i] *= coef;
+ for (unsigned i = 0; i < 2; ++i) {
+ _c[i] *= coef;
+ }
}
}
void Affine::setExpansionY(double val) {
double exp_y = expansionY();
- if(!are_near(exp_y, 0.0)) { //TODO: best way to deal with it is to skip op?
+ if (exp_y != 0.0) { //TODO: best way to deal with it is to skip op?
double coef = val / expansionY();
- for(unsigned i=2; i<4; i++) _c[i] *= coef;
+ for (unsigned i = 2; i < 4; ++i) {
+ _c[i] *= coef;
+ }
}
}
@@ -222,6 +223,29 @@ bool Affine::isNonzeroRotation(Coord eps) const {
are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
}
+/** @brief Check whether this matrix represents a non-zero rotation about any point.
+ * @param eps Numerical tolerance
+ * @return True iff the matrix is of the form
+ * \f$\left[\begin{array}{ccc}
+ a & b & 0 \\
+ -b & a & 0 \\
+ c & d & 1 \end{array}\right]\f$, \f$a^2 + b^2 = 1\f$ and \f$a \neq 1\f$. */
+bool Affine::isNonzeroNonpureRotation(Coord eps) const {
+ return !are_near(_c[0], 1.0, eps) &&
+ are_near(_c[0], _c[3], eps) && are_near(_c[1], -_c[2], eps) &&
+ are_near(_c[0]*_c[0] + _c[1]*_c[1], 1.0, eps);
+}
+
+/** @brief For a (possibly non-pure) non-zero-rotation matrix, calculate the rotation center.
+ * @pre The matrix must be a non-zero-rotation matrix to prevent division by zero, see isNonzeroNonpureRotation().
+ * @return The rotation center x, the solution to the equation
+ * \f$A x = x\f$. */
+Point Affine::rotationCenter() const {
+ Coord x = (_c[2]*_c[5]+_c[4]-_c[4]*_c[3]) / (1-_c[3]-_c[0]+_c[0]*_c[3]-_c[2]*_c[1]);
+ Coord y = (_c[1]*x + _c[5]) / (1 - _c[3]);
+ return Point(x,y);
+};
+
/** @brief Check whether this matrix represents pure horizontal shearing.
* @param eps Numerical tolerance
* @return True iff the matrix is of the form
@@ -342,8 +366,7 @@ bool Affine::preservesDistances(Coord eps) const
/** @brief Check whether this transformation flips objects.
* A transformation flips objects if it has a negative scaling component. */
bool Affine::flips() const {
- // TODO shouldn't this be det() < 0?
- return cross(xAxis(), yAxis()) > 0;
+ return det() < 0;
}
/** @brief Check whether this matrix is singular.
@@ -356,7 +379,7 @@ bool Affine::isSingular(Coord eps) const {
}
/** @brief Compute the inverse matrix.
- * Inverse is a matrix (denoted \f$A^{-1}) such that \f$AA^{-1} = A^{-1}A = I\f$.
+ * Inverse is a matrix (denoted \f$A^{-1}\f$) such that \f$AA^{-1} = A^{-1}A = I\f$.
* Singular matrices have no inverse (for example a matrix that has two of its columns equal).
* For such matrices, the identity matrix will be returned instead.
* @param eps Numerical tolerance
@@ -369,7 +392,7 @@ Affine Affine::inverse() const {
fabs(_c[2]) + fabs(_c[3])); // a random matrix norm (either l1 or linfty
if(mx > 0) {
Geom::Coord const determ = det();
- if (!rel_error_bound(determ, mx*mx)) {
+ if (!rel_error_bound(std::sqrt(fabs(determ)), mx)) {
Geom::Coord const ideterm = 1.0 / (determ);
d._c[0] = _c[3] * ideterm;
@@ -446,55 +469,38 @@ Affine elliptic_quadratic_form(Affine const &m) {
Eigen::Eigen(Affine const &m) {
double const B = -m[0] - m[3];
double const C = m[0]*m[3] - m[1]*m[2];
- double const center = -B/2.0;
- double const delta = sqrt(B*B-4*C)/2.0;
- values[0] = center + delta; values[1] = center - delta;
- for (int i = 0; i < 2; i++) {
- vectors[i] = unit_vector(rot90(Point(m[0]-values[i], m[1])));
- }
-}
-static void quadratic_roots(const double q0, const double q1, const double q2, int &n, double&r0, double&r1) {
- if(q2 == 0) {
- if(q1 == 0) { // zero or infinite roots
- n = 0;
- } else {
- n = 1;
- r0 = -q0/q1;
- }
- } else {
- double desc = q1*q1 - 4*q2*q0;
- if (desc < 0)
- n = 0;
- else if (desc == 0) {
- n = 1;
- r0 = -q1/(2*q2);
- } else {
- n = 2;
- desc = std::sqrt(desc);
- double t = -0.5*(q1+sgn(q1)*desc);
- r0 = t/q2;
- r1 = q0/t;
- }
+ std::vector<double> v = solve_quadratic(1, B, C);
+
+ for (unsigned i = 0; i < v.size(); ++i) {
+ values[i] = v[i];
+ vectors[i] = unit_vector(rot90(Point(m[0] - values[i], m[1])));
+ }
+ for (unsigned i = v.size(); i < 2; ++i) {
+ values[i] = 0;
+ vectors[i] = Point(0,0);
}
}
Eigen::Eigen(double m[2][2]) {
double const B = -m[0][0] - m[1][1];
double const C = m[0][0]*m[1][1] - m[1][0]*m[0][1];
- //double const desc = B*B-4*C;
- //double t = -0.5*(B+sgn(B)*desc);
- int n;
- values[0] = values[1] = 0;
- quadratic_roots(C, B, 1, n, values[0], values[1]);
- for (int i = 0; i < n; i++)
- vectors[i] = unit_vector(rot90(Point(m[0][0]-values[i], m[0][1])));
- for (int i = n; i < 2; i++)
+
+ std::vector<double> v = solve_quadratic(1, B, C);
+
+ for (unsigned i = 0; i < v.size(); ++i) {
+ values[i] = v[i];
+ vectors[i] = unit_vector(rot90(Point(m[0][0] - values[i], m[0][1])));
+ }
+ for (unsigned i = v.size(); i < 2; ++i) {
+ values[i] = 0;
vectors[i] = Point(0,0);
+ }
}
-/** @brief Nearness predicate for affine transforms
- * @returns True if all entries of matrices are within eps of each other */
+/** @brief Nearness predicate for affine transforms.
+ * @returns True if all entries of matrices are within eps of each other.
+ * @relates Affine */
bool are_near(Affine const &a, Affine const &b, Coord eps)
{
return are_near(a[0], b[0], eps) && are_near(a[1], b[1], eps) &&
diff --git a/src/2geom/affine.h b/src/2geom/affine.h
index af7b39360..470d5fc40 100644
--- a/src/2geom/affine.h
+++ b/src/2geom/affine.h
@@ -11,8 +11,8 @@
* This code is in public domain.
*/
-#ifndef SEEN_LIB2GEOM_AFFINE_H
-#define SEEN_LIB2GEOM_AFFINE_H
+#ifndef LIB2GEOM_SEEN_AFFINE_H
+#define LIB2GEOM_SEEN_AFFINE_H
#include <boost/operators.hpp>
#include <2geom/forward.h>
@@ -150,6 +150,8 @@ public:
bool isNonzeroScale(Coord eps = EPSILON) const;
bool isNonzeroUniformScale(Coord eps = EPSILON) const;
bool isNonzeroRotation(Coord eps = EPSILON) const;
+ bool isNonzeroNonpureRotation(Coord eps = EPSILON) const;
+ Point rotationCenter() const;
bool isNonzeroHShear(Coord eps = EPSILON) const;
bool isNonzeroVShear(Coord eps = EPSILON) const;
diff --git a/src/2geom/angle.h b/src/2geom/angle.h
index 1faf63c3f..af4442e88 100644
--- a/src/2geom/angle.h
+++ b/src/2geom/angle.h
@@ -59,32 +59,48 @@ namespace Geom {
* to <tt>double</tt> is in the range \f$[-\pi, \pi)\f$ - the convention used by C's
* math library.
*
+ * This class holds only a single floating point value, so passing it by value will generally
+ * be faster than passing it by const reference.
+ *
* @ingroup Primitives
*/
class Angle
: boost::additive< Angle
+ , boost::additive< Angle, Coord
, boost::equality_comparable< Angle
- > >
+ , boost::equality_comparable< Angle, Coord
+ > > > >
{
public:
- Angle() : _angle(0) {} //added default constructor because of cython
+ Angle() : _angle(0) {}
Angle(Coord v) : _angle(v) { _normalize(); } // this can be called implicitly
- explicit Angle(Point p) : _angle(atan2(p)) { _normalize(); }
- Angle(Point a, Point b) : _angle(angle_between(a, b)) { _normalize(); }
+ explicit Angle(Point const &p) : _angle(atan2(p)) { _normalize(); }
+ Angle(Point const &a, Point const &b) : _angle(angle_between(a, b)) { _normalize(); }
operator Coord() const { return radians(); }
- Angle &operator+=(Angle const &o) {
+ Angle &operator+=(Angle o) {
_angle += o._angle;
_normalize();
return *this;
}
- Angle &operator-=(Angle const &o) {
+ Angle &operator-=(Angle o) {
_angle -= o._angle;
_normalize();
return *this;
}
- bool operator==(Angle const &o) const {
+ Angle &operator+=(Coord a) {
+ *this += Angle(a);
+ return *this;
+ }
+ Angle &operator-=(Coord a) {
+ *this -= Angle(a);
+ return *this;
+ }
+ bool operator==(Angle o) const {
return _angle == o._angle;
}
+ bool operator==(Coord c) const {
+ return _angle == Angle(c)._angle;
+ }
/** @brief Get the angle as radians.
* @return Number in range \f$[-\pi, \pi)\f$. */
@@ -136,12 +152,22 @@ public:
private:
void _normalize() {
- _angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI;
+ _angle = std::fmod(_angle, 2*M_PI);
+ if (_angle < 0) _angle += 2*M_PI;
+ //_angle -= floor(_angle * (1.0/(2*M_PI))) * 2*M_PI;
}
Coord _angle; // this is always in [0, 2pi)
friend class AngleInterval;
};
+inline Angle distance(Angle const &a, Angle const &b) {
+ // the distance cannot be larger than M_PI.
+ Coord ac = a.radians0();
+ Coord bc = b.radians0();
+ Coord d = fabs(ac - bc);
+ return Angle(d > M_PI ? 2*M_PI - d : d);
+}
+
/** @brief Directed angular interval.
*
* Wrapper for directed angles with defined start and end values. Useful e.g. for representing
@@ -149,50 +175,156 @@ private:
* in the interval (it is a closed interval). Angular intervals can also be interptered
* as functions \f$f: [0, 1] \to [-\pi, \pi)\f$, which return the start angle for 0,
* the end angle for 1, and interpolate linearly for other values. Note that such functions
- * are not continuous if the interval contains the zero angle.
+ * are not continuous if the interval crosses the angle \f$\pi\f$.
*
- * This class is immutable - you cannot change the values of start and end angles
- * without creating a new instance of this class.
+ * This class can represent all directed angular intervals, including empty ones.
+ * However, not all possible intervals can be created with the constructors.
+ * For full control, use the setInitial(), setFinal() and setAngles() methods.
*
* @ingroup Primitives
*/
-class AngleInterval {
+class AngleInterval
+ : boost::equality_comparable< AngleInterval >
+{
public:
- AngleInterval(Angle const &s, Angle const &e, bool cw = false)
- : _start_angle(s), _end_angle(e), _sweep(cw)
+ AngleInterval() {}
+ /** @brief Create an angular interval from two angles and direction.
+ * If the initial and final angle are the same, a degenerate interval
+ * (containing only one angle) will be created.
+ * @param s Starting angle
+ * @param e Ending angle
+ * @param cw Which direction the interval goes. True means that it goes
+ * in the direction of increasing angles, while false means in the direction
+ * of decreasing angles. */
+ AngleInterval(Angle s, Angle e, bool cw = false)
+ : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
{}
AngleInterval(double s, double e, bool cw = false)
- : _start_angle(s), _end_angle(e), _sweep(cw)
+ : _start_angle(s), _end_angle(e), _sweep(cw), _full(false)
{}
- /** @brief Get the angular coordinate of the interval's initial point
- * @return Angle in range \f$[0,2\pi)\f$ corresponding to the start of arc */
- Angle const &initialAngle() const { return _start_angle; }
- /** @brief Get the angular coordinate of the interval's final point
- * @return Angle in range \f$[0,2\pi)\f$ corresponding to the end of arc */
- Angle const &finalAngle() const { return _end_angle; }
- bool isDegenerate() const { return initialAngle() == finalAngle(); }
- /** @brief Get an angle corresponding to the specified time value. */
+ /** @brief Create an angular interval from three angles.
+ * If the inner angle is exactly equal to initial or final angle,
+ * the sweep flag will be set to true, i.e. the interval will go
+ * in the direction of increasing angles.
+ *
+ * If the initial and final angle are the same, but the inner angle
+ * is different, a full angle in the direction of increasing angles
+ * will be created.
+ *
+ * @param s Initial angle
+ * @param inner Angle contained in the interval
+ * @param e Final angle */
+ AngleInterval(Angle s, Angle inner, Angle e)
+ : _start_angle(s)
+ , _end_angle(e)
+ , _sweep((inner-s).radians0() <= (e-s).radians0())
+ , _full(s == e && s != inner)
+ {
+ if (_full) {
+ _sweep = true;
+ }
+ }
+
+ /// Get the start angle.
+ Angle initialAngle() const { return _start_angle; }
+ /// Get the end angle.
+ Angle finalAngle() const { return _end_angle; }
+ /// Check whether the interval goes in the direction of increasing angles.
+ bool sweep() const { return _sweep; }
+ /// Check whether the interval contains only a single angle.
+ bool isDegenerate() const {
+ return _start_angle == _end_angle && !_full;
+ }
+ /// Check whether the interval contains all angles.
+ bool isFull() const {
+ return _start_angle == _end_angle && _full;
+ }
+
+ /** @brief Set the initial angle.
+ * @param a Angle to set
+ * @param prefer_full Whether to set a full angular interval when
+ * the initial angle is set to the final angle */
+ void setInitial(Angle a, bool prefer_full = false) {
+ _start_angle = a;
+ _full = prefer_full && a == _end_angle;
+ }
+
+ /** @brief Set the final angle.
+ * @param a Angle to set
+ * @param prefer_full Whether to set a full angular interval when
+ * the initial angle is set to the final angle */
+ void setFinal(Angle a, bool prefer_full = false) {
+ _end_angle = a;
+ _full = prefer_full && a == _start_angle;
+ }
+ /** @brief Set both angles at once.
+ * The direction (sweep flag) is left unchanged.
+ * @param s Initial angle
+ * @param e Final angle
+ * @param prefer_full Whether to set a full interval when the passed
+ * initial and final angle are the same */
+ void setAngles(Angle s, Angle e, bool prefer_full = false) {
+ _start_angle = s;
+ _end_angle = e;
+ _full = prefer_full && s == e;
+ }
+ /// Set whether the interval goes in the direction of increasing angles.
+ void setSweep(bool s) { _sweep = s; }
+
+ /// Reverse the direction of the interval while keeping contained values the same.
+ void reverse() {
+ using std::swap;
+ swap(_start_angle, _end_angle);
+ _sweep = !_sweep;
+ }
+ /// Get a new interval with reversed direction.
+ AngleInterval reversed() const {
+ AngleInterval result(*this);
+ result.reverse();
+ return result;
+ }
+
+ /// Get an angle corresponding to the specified time value.
Angle angleAt(Coord t) const {
Coord span = extent();
Angle ret = _start_angle.radians0() + span * (_sweep ? t : -t);
return ret;
}
Angle operator()(Coord t) const { return angleAt(t); }
-#if 0
- /** @brief Find an angle nearest to the specified time value.
- * @param a Query angle
- * @return If the interval contains the query angle, a number from the range [0, 1]
- * such that a = angleAt(t); otherwise, 0 or 1, depending on which extreme
- * angle is nearer. */
- Coord nearestAngle(Angle const &a) const {
- Coord dist = distance(_start_angle, a, _sweep).radians0();
- Coord span = distance(_start_angle, _end_angle, _sweep).radians0();
- if (dist <= span) return dist / span;
- else return distance_abs(_start_angle, a).radians0() > distance_abs(_end_angle, a).radians0() ? 1.0 : 0.0;
+
+ /** @brief Compute a time value that would evaluate to the given angle.
+ * If the start and end angle are exactly the same, NaN will be returned.
+ * Negative values will be returned for angles between the initial angle
+ * and the angle exactly opposite the midpoint of the interval. */
+ Coord timeAtAngle(Angle a) const {
+ if (_full) {
+ Angle ta = _sweep ? a - _start_angle : _start_angle - a;
+ return ta.radians0() / (2*M_PI);
+ }
+ Coord ex = extent();
+ Coord outex = 2*M_PI - ex;
+ if (_sweep) {
+ Angle midout = _start_angle - outex / 2;
+ Angle acmp = a - midout, scmp = _start_angle - midout;
+ if (acmp.radians0() >= scmp.radians0()) {
+ return (a - _start_angle).radians0() / ex;
+ } else {
+ return -(_start_angle - a).radians0() / ex;
+ }
+ } else {
+ Angle midout = _start_angle + outex / 2;
+ Angle acmp = a - midout, scmp = _start_angle - midout;
+ if (acmp.radians0() <= scmp.radians0()) {
+ return (_start_angle - a).radians0() / ex;
+ } else {
+ return -(a - _start_angle).radians0() / ex;
+ }
+ }
}
-#endif
- /** @brief Check whether the interval includes the given angle. */
- bool contains(Angle const &a) const {
+
+ /// Check whether the interval includes the given angle.
+ bool contains(Angle a) const {
+ if (_full) return true;
Coord s = _start_angle.radians0();
Coord e = _end_angle.radians0();
Coord x = a.radians0();
@@ -205,18 +337,48 @@ public:
}
}
/** @brief Extent of the angle interval.
- * @return Extent in range \f$[0, 2\pi)\f$ */
+ * Equivalent to the absolute value of the sweep angle.
+ * @return Extent in range \f$[0, 2\pi)\f$. */
Coord extent() const {
- Coord d = _end_angle - _start_angle;
- if (!_sweep) d = -d;
- if (d < 0) d += 2*M_PI;
- return d;
+ if (_full) return 2*M_PI;
+ return _sweep
+ ? (_end_angle - _start_angle).radians0()
+ : (_start_angle - _end_angle).radians0();
}
-protected:
- AngleInterval() {}
+ /** @brief Get the sweep angle of the interval.
+ * This is the value you need to add to the initial angle to get the final angle.
+ * It is positive when sweep is true. Denoted as \f$\Delta\theta\f$ in the SVG
+ * elliptical arc implementation notes. */
+ Coord sweepAngle() const {
+ if (_full) return _sweep ? 2*M_PI : -2*M_PI;
+ Coord sa = _end_angle.radians0() - _start_angle.radians0();
+ if (_sweep && sa < 0) sa += 2*M_PI;
+ if (!_sweep && sa > 0) sa -= 2*M_PI;
+ return sa;
+ }
+
+ /// Check another interval for equality.
+ bool operator==(AngleInterval const &other) const {
+ if (_start_angle != other._start_angle) return false;
+ if (_end_angle != other._end_angle) return false;
+ if (_sweep != other._sweep) return false;
+ if (_full != other._full) return false;
+ return true;
+ }
+
+ static AngleInterval create_full(Angle start, bool sweep = true) {
+ AngleInterval result;
+ result._start_angle = result._end_angle = start;
+ result._sweep = sweep;
+ result._full = true;
+ return result;
+ }
+
+private:
Angle _start_angle;
Angle _end_angle;
bool _sweep;
+ bool _full;
};
/** @brief Given an angle in degrees, return radians
@@ -226,88 +388,12 @@ inline Coord deg_to_rad(Coord deg) { return deg*M_PI/180.0;}
* @relates Angle */
inline Coord rad_to_deg(Coord rad) { return rad*180.0/M_PI;}
-/*
- * start_angle and angle must belong to [0, 2PI[
- * and angle must belong to the cirsular arc defined by
- * start_angle, end_angle and with rotation direction cw
- */
-inline
-double map_circular_arc_on_unit_interval( double angle, double start_angle, double end_angle, bool cw = true )
-{
- double d = end_angle - start_angle;
- double t = angle - start_angle;
- if ( !cw )
- {
- d = -d;
- t = -t;
- }
- d = std::fmod(d, 2*M_PI);
- t = std::fmod(t, 2*M_PI);
- if ( d < 0 ) d += 2*M_PI;
- if ( t < 0 ) t += 2*M_PI;
- return t / d;
-}
-
-inline
-Coord map_unit_interval_on_circular_arc(Coord t, double start_angle, double end_angle, bool cw = true)
-{
- double sweep_angle = end_angle - start_angle;
- if ( !cw ) sweep_angle = -sweep_angle;
- sweep_angle = std::fmod(sweep_angle, 2*M_PI);
- if ( sweep_angle < 0 ) sweep_angle += 2*M_PI;
-
- Coord angle = start_angle;
- if ( cw )
- {
- angle += sweep_angle * t;
- }
- else
- {
- angle -= sweep_angle * t;
- }
- angle = std::fmod(angle, 2*M_PI);
- if (angle < 0) angle += 2*M_PI;
- return angle;
-}
-
-/*
- * Return true if the given angle is contained in the circular arc determined
- * by the passed angles.
- *
- * a: the angle to be tested
- * sa: the angle the arc start from
- * ia: an angle strictly inner to the arc
- * ea: the angle the arc end to
- *
- * prerequisite: the inner angle has to be not equal (mod 2PI) to the start
- * or the end angle, except when they are equal each other, too.
- * warning: when sa == ea (mod 2PI) they define a whole circle
- * if ia != sa (mod 2PI), on the contrary if ia == sa == ea (mod 2PI)
- * they define a single point.
- */
-inline
-bool arc_contains (double a, double sa, double ia, double ea)
-{
- a -= sa;
- a = std::fmod(a, 2*M_PI);
- if (a < 0) a += 2*M_PI;
- ia -= sa;
- ia = std::fmod(ia, 2*M_PI);
- if (ia < 0) ia += 2*M_PI;
- ea -= sa;
- ea = std::fmod(ea, 2*M_PI);
- if (ea < 0) ea += 2*M_PI;
+} // end namespace Geom
- if (ia == 0 && ea == 0) return (a == 0);
- if (ia == 0 || ia == ea)
- {
- THROW_RANGEERROR ("arc_contains: passed angles do not define an arc");
- }
- return (ia < ea && a <= ea) || (ia > ea && (a >= ea || a == 0));
+namespace std {
+template <> class iterator_traits<Geom::Angle> {};
}
-} // end namespace Geom
-
#endif // LIB2GEOM_SEEN_ANGLE_H
/*
diff --git a/src/2geom/basic-intersection.cpp b/src/2geom/basic-intersection.cpp
index 379ec597c..54374e282 100644
--- a/src/2geom/basic-intersection.cpp
+++ b/src/2geom/basic-intersection.cpp
@@ -1,3 +1,38 @@
+/** @file
+ * @brief Basic intersection routines
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@njhurst.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Jean-François Barraud <jf.barraud@gmail.com>
+ *
+ * Copyright 2008-2009 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
#include <2geom/basic-intersection.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/exception.h>
@@ -33,24 +68,35 @@ namespace Geom {
//#else
namespace detail{ namespace bezier_clipping {
-void portion (std::vector<Point> & B, Interval const& I);
+void portion(std::vector<Point> &B, Interval const &I);
+void derivative(std::vector<Point> &D, std::vector<Point> const &B);
}; };
void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const & A,
+ D2<Bezier> const & B,
+ double precision)
+{
+ find_intersections_bezier_clipping(xs, bezier_points(A), bezier_points(B), precision);
+}
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const & A,
- D2<SBasis> const & B) {
+ D2<SBasis> const & B,
+ double precision)
+{
vector<Point> BezA, BezB;
sbasis_to_bezier(BezA, A);
sbasis_to_bezier(BezB, B);
-
- xs.clear();
- find_intersections_bezier_clipping(xs, BezA, BezB);
+ find_intersections_bezier_clipping(xs, BezA, BezB, precision);
}
+
void find_intersections(std::vector< std::pair<double, double> > & xs,
- std::vector<Point> const& A,
- std::vector<Point> const& B,
- double precision){
+ std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision)
+{
find_intersections_bezier_clipping(xs, A, B, precision);
}
@@ -61,6 +107,7 @@ void find_intersections(std::vector< std::pair<double, double> > & xs,
* Temporary storage is minimized by using part of the storage for the result
* to hold an intermediate value until it is no longer needed.
*/
+// TODO replace with Bezier method
void split(vector<Point> const &p, double t,
vector<Point> &left, vector<Point> &right) {
const unsigned sz = p.size();
@@ -88,44 +135,48 @@ void split(vector<Point> const &p, double t,
}
-void
-find_self_intersections(std::vector<std::pair<double, double> > &xs,
- D2<SBasis> const & A) {
- vector<double> dr = roots(derivative(A[X]));
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ double precision)
+{
+ std::vector<double> dr = derivative(A[X]).roots();
{
- vector<double> dyr = roots(derivative(A[Y]));
+ std::vector<double> dyr = derivative(A[Y]).roots();
dr.insert(dr.begin(), dyr.begin(), dyr.end());
}
dr.push_back(0);
dr.push_back(1);
// We want to be sure that we have no empty segments
- sort(dr.begin(), dr.end());
- vector<double>::iterator new_end = unique(dr.begin(), dr.end());
+ std::sort(dr.begin(), dr.end());
+ std::vector<double>::iterator new_end = std::unique(dr.begin(), dr.end());
dr.resize( new_end - dr.begin() );
- vector<vector<Point> > pieces;
- {
- vector<Point> in, l, r;
- sbasis_to_bezier(in, A);
+ std::vector< D2<Bezier> > pieces;
+ for (unsigned i = 0; i < dr.size() - 1; ++i) {
+ pieces.push_back(portion(A, dr[i], dr[i+1]));
+ }
+ /*{
+ vector<Point> l, r, in = A;
for(unsigned i = 0; i < dr.size()-1; i++) {
split(in, (dr[i+1]-dr[i]) / (1 - dr[i]), l, r);
pieces.push_back(l);
in = r;
}
- }
+ }*/
for(unsigned i = 0; i < dr.size()-1; i++) {
for(unsigned j = i+1; j < dr.size()-1; j++) {
std::vector<std::pair<double, double> > section;
- find_intersections( section, pieces[i], pieces[j]);
+ find_intersections(section, pieces[i], pieces[j], precision);
for(unsigned k = 0; k < section.size(); k++) {
double l = section[k].first;
double r = section[k].second;
// XXX: This condition will prune out false positives, but it might create some false negatives. Todo: Confirm it is correct.
if(j == i+1)
//if((l == 1) && (r == 0))
- if( ( l > 1-1e-4 ) && (r < 1e-4) )//FIXME: what precision should be used here???
+ if( ( l > precision ) && (r < precision) )//FIXME: what precision should be used here???
continue;
xs.push_back(std::make_pair((1-l)*dr[i] + l*dr[i+1],
(1-r)*dr[j] + r*dr[j+1]));
@@ -138,6 +189,46 @@ find_self_intersections(std::vector<std::pair<double, double> > &xs,
//unique(xs.begin(), xs.end());
}
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ double precision)
+{
+ D2<Bezier> in;
+ sbasis_to_bezier(in, A);
+ find_self_intersections(xs, in, precision);
+}
+
+
+void subdivide(D2<Bezier> const &a,
+ D2<Bezier> const &b,
+ std::vector< std::pair<double, double> > const &xs,
+ std::vector< D2<Bezier> > &av,
+ std::vector< D2<Bezier> > &bv)
+{
+ if (xs.empty()) {
+ av.push_back(a);
+ bv.push_back(b);
+ return;
+ }
+
+ std::pair<double, double> prev = std::make_pair(0., 0.);
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ av.push_back(portion(a, prev.first, xs[i].first));
+ bv.push_back(portion(b, prev.second, xs[i].second));
+ av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0());
+ av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1());
+ av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0());
+ av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1());
+ prev = xs[i];
+ }
+ av.push_back(portion(a, prev.first, 1));
+ bv.push_back(portion(b, prev.second, 1));
+ av.back()[X].at0() = bv.back()[X].at0() = lerp(0.5, av.back()[X].at0(), bv.back()[X].at0());
+ av.back()[X].at1() = bv.back()[X].at1() = lerp(0.5, av.back()[X].at1(), bv.back()[X].at1());
+ av.back()[Y].at0() = bv.back()[Y].at0() = lerp(0.5, av.back()[Y].at0(), bv.back()[Y].at0());
+ av.back()[Y].at1() = bv.back()[Y].at1() = lerp(0.5, av.back()[Y].at1(), bv.back()[Y].at1());
+}
+
#ifdef HAVE_GSL
#include <gsl/gsl_multiroots.h>
@@ -304,38 +395,6 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs,
B, xs[i].second);
}
-
- /**
- * Compute the Hausdorf distance from A to B only.
- */
-
-
-#if 0
-/** Compute the value of a bezier
- Todo: find a good palce for this.
- */
-// suggested by Sederberg.
-Point OldBezier::operator()(double t) const {
- int n = p.size()-1;
- double u, bc, tn, tmp;
- int i;
- Point r;
- for(int dim = 0; dim < 2; dim++) {
- u = 1.0 - t;
- bc = 1;
- tn = 1;
- tmp = p[0][dim]*u;
- for(i=1; i<n; i++){
- tn = tn*t;
- bc = bc*(n-i+1)/i;
- tmp = (tmp + tn*bc*p[i][dim])*u;
- }
- r[dim] = (tmp + tn*t*p[n][dim]);
- }
- return r;
-}
-#endif
-
/**
* Compute the Hausdorf distance from A to B only.
*/
@@ -350,7 +409,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
double h_dist = 0, h_a_t = 0, h_b_t = 0;
double dist = 0;
Point Ax = A.at0();
- double t = Geom::nearest_point(Ax, B);
+ double t = Geom::nearest_time(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 0;
@@ -358,7 +417,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
h_dist = dist;
}
Ax = A.at1();
- t = Geom::nearest_point(Ax, B);
+ t = Geom::nearest_time(Ax, B);
dist = Geom::distance(Ax, B(t));
if (dist > h_dist) {
h_a_t = 1;
@@ -370,7 +429,7 @@ double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
Point At = A(xs[i].first);
Point Bu = B(xs[i].second);
double distAtBu = Geom::distance(At, Bu);
- t = Geom::nearest_point(At, B);
+ t = Geom::nearest_time(At, B);
dist = Geom::distance(At, B(t));
//FIXME: we might miss it due to floating point precision...
if (dist >= distAtBu-.1 && distAtBu > h_dist) {
@@ -396,7 +455,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
double dist = 0;
Point Bx = B.at0();
- double t = Geom::nearest_point(Bx, A);
+ double t = Geom::nearest_time(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
@@ -404,7 +463,7 @@ double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
h_dist = dist;
}
Bx = B.at1();
- t = Geom::nearest_point(Bx, A);
+ t = Geom::nearest_time(Bx, A);
dist = Geom::distance(Bx, A(t));
if (dist > h_dist) {
if(a_t) *a_t = t;
diff --git a/src/2geom/basic-intersection.h b/src/2geom/basic-intersection.h
index 5a813ae99..0b0bf8930 100644
--- a/src/2geom/basic-intersection.h
+++ b/src/2geom/basic-intersection.h
@@ -1,11 +1,12 @@
-/**
- * \file
- * \brief Basic intersection routines
- *
+/** @file
+ * @brief Basic intersection routines
+ *//*
* Authors:
- * ? <?@?.?>
+ * Nathan Hurst <njh@njhurst.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Jean-François Barraud <jf.barraud@gmail.com>
*
- * Copyright ?-? authors
+ * Copyright 2008-2009 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,10 +33,11 @@
*
*/
-#ifndef SEEN_GEOM_BASICINTERSECTION_H
-#define SEEN_GEOM_BASICINTERSECTION_H
+#ifndef LIB2GEOM_SEEN_BASIC_INTERSECTION_H
+#define LIB2GEOM_SEEN_BASIC_INTERSECTION_H
#include <2geom/point.h>
+#include <2geom/bezier.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
@@ -47,41 +49,29 @@
namespace Geom {
-//why not allowing precision to be set here?
-void find_intersections(std::vector<std::pair<double, double> >& xs,
- D2<SBasis> const & A,
- D2<SBasis> const & B);
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ D2<Bezier> const &B,
+ double precision = EPSILON);
+
+void find_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ D2<SBasis> const &B,
+ double precision = EPSILON);
+
+void find_intersections(std::vector< std::pair<double, double> > &xs,
+ std::vector<Point> const &A,
+ std::vector<Point> const &B,
+ double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<SBasis> const &A,
+ double precision = EPSILON);
+
+void find_self_intersections(std::vector<std::pair<double, double> > &xs,
+ D2<Bezier> const &A,
+ double precision = EPSILON);
-void find_intersections(std::vector< std::pair<double, double> > & xs,
- std::vector<Point> const& A,
- std::vector<Point> const& B,
- double precision = 1e-5);
-
-//why not allowing precision to be set here?
-void find_self_intersections(std::vector<std::pair<double, double> >& xs,
- D2<SBasis> const & A);
-
-
-//--not implemented
-//void find_self_intersections(std::vector<std::pair<double, double> >& xs,
-// std::vector<Point> const & A);
-
-
-//TODO: this should be moved to .cpp, shouldn't it?
-// #ifdef USE_RECURSIVE_INTERSECTOR
-// /*
-// * find_intersection
-// *
-// * input: A, B - set of control points of two Bezier curve
-// * input: precision - required precision of computation
-// * output: xs - set of pairs of parameter values
-// * at which crossing happens
-// */
-// void find_intersections_bezier_recursive (std::vector< std::pair<double, double> > & xs,
-// std::vector<Point> const& A,
-// std::vector<Point> const& B,
-// double precision = 1e-5);
-// #else
/*
* find_intersection
*
@@ -96,10 +86,14 @@ void find_self_intersections(std::vector<std::pair<double, double> >& xs,
void find_intersections_bezier_clipping (std::vector< std::pair<double, double> > & xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
- double precision = 1e-5);
+ double precision = EPSILON);
//#endif
-
+void subdivide(D2<Bezier> const &a,
+ D2<Bezier> const &b,
+ std::vector< std::pair<double, double> > const &xs,
+ std::vector< D2<Bezier> > &av,
+ std::vector< D2<Bezier> > &bv);
/*
* find_collinear_normal
@@ -115,7 +109,7 @@ void find_intersections_bezier_clipping (std::vector< std::pair<double, double>
void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
std::vector<Point> const& A,
std::vector<Point> const& B,
- double precision = 1e-5);
+ double precision = EPSILON);
void polish_intersections(std::vector<std::pair<double, double> > &xs,
D2<SBasis> const &A,
@@ -125,19 +119,19 @@ void polish_intersections(std::vector<std::pair<double, double> > &xs,
/**
* Compute the Hausdorf distance from A to B only.
*/
-double hausdorfl(D2<SBasis>& A, D2<SBasis> const& B,
+double hausdorfl(D2<SBasis>& A, D2<SBasis> const &B,
double m_precision,
- double *a_t=0, double* b_t=0);
+ double *a_t=NULL, double *b_t=NULL);
/**
* Compute the symmetric Hausdorf distance.
*/
-double hausdorf(D2<SBasis>& A, D2<SBasis> const& B,
+double hausdorf(D2<SBasis> &A, D2<SBasis> const &B,
double m_precision,
- double *a_t=0, double* b_t=0);
+ double *a_t=NULL, double *b_t=NULL);
}
-#endif // !SEEN_GEOM_BASICINTERSECTION_H
+#endif // !LIB2GEOM_SEEN_BASIC_INTERSECTION_H
/*
Local Variables:
diff --git a/src/2geom/bezier-clipping.cpp b/src/2geom/bezier-clipping.cpp
index 9a055204f..be8dd5a5f 100644
--- a/src/2geom/bezier-clipping.cpp
+++ b/src/2geom/bezier-clipping.cpp
@@ -39,8 +39,9 @@
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/bezier.h>
-//#include <2geom/convex-cover.h>
#include <2geom/numeric/matrix.h>
+#include <2geom/convex-hull.h>
+#include <2geom/line.h>
#include <cassert>
#include <vector>
@@ -48,7 +49,7 @@
#include <utility>
//#include <iomanip>
-
+using std::swap;
#define VERBOSE 0
@@ -63,7 +64,6 @@ namespace detail { namespace bezier_clipping {
// for debugging
//
-inline
void print(std::vector<Point> const& cp, const char* msg = "")
{
std::cerr << msg << std::endl;
@@ -72,7 +72,6 @@ void print(std::vector<Point> const& cp, const char* msg = "")
}
template< class charT >
-inline
std::basic_ostream<charT> &
operator<< (std::basic_ostream<charT> & os, const Interval & I)
{
@@ -80,7 +79,6 @@ operator<< (std::basic_ostream<charT> & os, const Interval & I)
return os;
}
-inline
double angle (std::vector<Point> const& A)
{
size_t n = A.size() -1;
@@ -88,7 +86,6 @@ double angle (std::vector<Point> const& A)
return (180 * a / M_PI);
}
-inline
size_t get_precision(Interval const& I)
{
double d = I.extent();
@@ -103,7 +100,6 @@ size_t get_precision(Interval const& I)
return n;
}
-inline
void range_assertion(int k, int m, int n, const char* msg)
{
if ( k < m || k > n)
@@ -118,92 +114,11 @@ void range_assertion(int k, int m, int n, const char* msg)
////////////////////////////////////////////////////////////////////////////////
-// convex hull
-
-/*
- * return true in case the oriented polyline p0, p1, p2 is a right turn
- */
-inline
-bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
-{
- if (p1 == p2) return false;
- Point q1 = p1 - p0;
- Point q2 = p2 - p0;
- if (q1 == -q2) return false;
- return (cross (q1, q2) < 0);
-}
-
-/*
- * return true if p < q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_less
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[X] < q[X]) || (p[X] == q[X] && p[Y] < q[Y]));
- }
-};
-
-/*
- * return true if p > q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_greater
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[X] > q[X]) || (p[X] == q[X] && p[Y] > q[Y]));
- }
-};
-
-/*
- * Compute the convex hull of a set of points.
- * The implementation is based on the Andrew's scan algorithm
- * note: in the Bezier clipping for collinear normals it seems
- * to be more stable wrt the Graham's scan algorithm and in general
- * a bit quikier
- */
-void convex_hull (std::vector<Point> & P)
-{
- size_t n = P.size();
- if (n < 2) return;
- std::sort(P.begin(), P.end(), lex_less());
- if (n < 4) return;
- // upper hull
- size_t u = 2;
- for (size_t i = 2; i < n; ++i)
- {
- while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
- {
- --u;
- }
- std::swap(P[u], P[i]);
- ++u;
- }
- std::sort(P.begin() + u, P.end(), lex_greater());
- std::rotate(P.begin(), P.begin() + 1, P.end());
- // lower hull
- size_t l = u;
- size_t k = u - 1;
- for (size_t i = l; i < n; ++i)
- {
- while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
- {
- --l;
- }
- std::swap(P[l], P[i]);
- ++l;
- }
- P.resize(l);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
// numerical routines
/*
* Compute the binomial coefficient (n, k)
*/
-inline
double binomial(unsigned int n, unsigned int k)
{
return choose<double>(n, k);
@@ -212,7 +127,6 @@ double binomial(unsigned int n, unsigned int k)
/*
* Compute the determinant of the 2x2 matrix with column the point P1, P2
*/
-inline
double det(Point const& P1, Point const& P2)
{
return P1[X]*P2[Y] - P1[Y]*P2[X];
@@ -222,7 +136,6 @@ double det(Point const& P1, Point const& P2)
* Solve the linear system [P1,P2] * P = Q
* in case there isn't exactly one solution the routine returns false
*/
-inline
bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
{
double d = det(P1, P2);
@@ -239,27 +152,11 @@ bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
/*
* Map the sub-interval I in [0,1] into the interval J and assign it to J
*/
-inline
void map_to(Interval & J, Interval const& I)
{
- double length = J.extent();
- J[1] = I.max() * length + J[0];
- J[0] = I.min() * length + J[0];
+ J.setEnds(J.valueAt(I.min()), J.valueAt(I.max()));
}
-/*
- * The interval [1,0] is used to represent the empty interval, this routine
- * is just an helper function for creating such an interval
- */
-inline
-Interval make_empty_interval()
-{
- Interval I(0);
- I[0] = 1;
- return I;
-}
-
-
////////////////////////////////////////////////////////////////////////////////
// bezier curve routines
@@ -267,8 +164,8 @@ Interval make_empty_interval()
* Return true if all the Bezier curve control points are near,
* false otherwise
*/
-inline
-bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
+// Bezier.isConstant(precision)
+bool is_constant(std::vector<Point> const& A, double precision)
{
for (unsigned int i = 1; i < A.size(); ++i)
{
@@ -281,7 +178,7 @@ bool is_constant(std::vector<Point> const& A, double precision = EPSILON)
/*
* Compute the hodograph of the bezier curve B and return it in D
*/
-inline
+// derivative(Bezier)
void derivative(std::vector<Point> & D, std::vector<Point> const& B)
{
D.clear();
@@ -304,7 +201,7 @@ void derivative(std::vector<Point> & D, std::vector<Point> const& B)
* Compute the hodograph of the Bezier curve B rotated of 90 degree
* and return it in D; we have N(t) orthogonal to B(t) for any t
*/
-inline
+// rot90(derivative(Bezier))
void normal(std::vector<Point> & N, std::vector<Point> const& B)
{
derivative(N,B);
@@ -317,7 +214,7 @@ void normal(std::vector<Point> & N, std::vector<Point> const& B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval [0,t]
*/
-inline
+// portion(Bezier, 0, t)
void left_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
@@ -333,7 +230,7 @@ void left_portion(Coord t, std::vector<Point> & B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval [t,1]
*/
-inline
+// portion(Bezier, t, 1)
void right_portion(Coord t, std::vector<Point> & B)
{
size_t n = B.size();
@@ -349,7 +246,7 @@ void right_portion(Coord t, std::vector<Point> & B)
/*
* Compute the portion of the Bezier curve "B" wrt the interval "I"
*/
-inline
+// portion(Bezier, I)
void portion (std::vector<Point> & B , Interval const& I)
{
if (I.min() == 0)
@@ -371,9 +268,9 @@ void portion (std::vector<Point> & B , Interval const& I)
struct intersection_point_tag;
struct collinear_normal_tag;
template <typename Tag>
-void clip(Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B);
+OptInterval clip(std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision);
template <typename Tag>
void iterate(std::vector<Interval>& domsA,
std::vector<Interval>& domsB,
@@ -392,14 +289,14 @@ void iterate(std::vector<Interval>& domsA,
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
-inline
+// Line(c[i], c[j])
void orientation_line (std::vector<double> & l,
std::vector<Point> const& c,
size_t i, size_t j)
{
l[0] = c[j][Y] - c[i][Y];
l[1] = c[i][X] - c[j][X];
- l[2] = cross(c[i], c[j]);
+ l[2] = cross(c[j], c[i]);
double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
assert (length != 0);
l[0] /= length;
@@ -411,22 +308,20 @@ void orientation_line (std::vector<double> & l,
* Pick up an orientation line for the Bezier curve "c" and return it in
* the output parameter "l"
*/
-inline
-void pick_orientation_line (std::vector<double> & l,
- std::vector<Point> const& c)
+Line pick_orientation_line (std::vector<Point> const &c, double precision)
{
size_t i = c.size();
- while (--i > 0 && are_near(c[0], c[i]))
+ while (--i > 0 && are_near(c[0], c[i], precision))
{}
- if (i == 0)
- {
- // this should never happen because when a new curve portion is created
- // we check that it is not constant;
- // however this requires that the precision used in the is_constant
- // routine has to be the same used here in the are_near test
- assert(i != 0);
- }
- orientation_line(l, c, 0, i);
+
+ // this should never happen because when a new curve portion is created
+ // we check that it is not constant;
+ // however this requires that the precision used in the is_constant
+ // routine has to be the same used here in the are_near test
+ assert(i != 0);
+
+ Line line(c[0], c[i]);
+ return line;
//std::cerr << "i = " << i << std::endl;
}
@@ -436,29 +331,25 @@ void pick_orientation_line (std::vector<double> & l,
* the line is returned in the output parameter "l" in the form of a 3 element
* vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
*/
-inline
-void orthogonal_orientation_line (std::vector<double> & l,
- std::vector<Point> const& c,
- Point const& p)
+Line orthogonal_orientation_line (std::vector<Point> const &c,
+ Point const &p,
+ double precision)
{
- if (is_constant(c))
- {
- // this should never happen
- assert(!is_constant(c));
- }
- std::vector<Point> ol(2);
- ol[0] = p;
- ol[1] = (c.back() - c.front()).cw() + p;
- orientation_line(l, ol, 0, 1);
+ // this should never happen
+ assert(!is_constant(c, precision));
+
+ Line line(p, (c.back() - c.front()).cw() + p);
+ return line;
}
/*
* Compute the signed distance of the point "P" from the normalized line l
*/
-inline
-double distance (Point const& P, std::vector<double> const& l)
+double signed_distance(Point const &p, Line const &l)
{
- return l[X] * P[X] + l[Y] * P[Y] + l[2];
+ Coord a, b, c;
+ l.coefficients(a, b, c);
+ return a * p[X] + b * p[Y] + c;
}
/*
@@ -466,26 +357,20 @@ double distance (Point const& P, std::vector<double> const& l)
* curve "c" from the normalized orientation line "l".
* This bounds are returned through the output Interval parameter"bound".
*/
-inline
-void fat_line_bounds (Interval& bound,
- std::vector<Point> const& c,
- std::vector<double> const& l)
+Interval fat_line_bounds (std::vector<Point> const &c,
+ Line const &l)
{
- bound[0] = 0;
- bound[1] = 0;
- for (size_t i = 0; i < c.size(); ++i)
- {
- const double d = distance(c[i], l);
- if (bound[0] > d) bound[0] = d;
- if (bound[1] < d) bound[1] = d;
+ Interval bound(0, 0);
+ for (size_t i = 0; i < c.size(); ++i) {
+ bound.expandTo(signed_distance(c[i], l));
}
+ return bound;
}
/*
* return the x component of the intersection point between the line
* passing through points p1, p2 and the line Y = "y"
*/
-inline
double intersect (Point const& p1, Point const& p2, double y)
{
// we are sure that p2[Y] != p1[Y] because this routine is called
@@ -500,23 +385,22 @@ double intersect (Point const& p1, Point const& p2, double y)
* line "l" and the interval range "bound", the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
-void clip_interval (Interval& dom,
- std::vector<Point> const& B,
- std::vector<double> const& l,
- Interval const& bound)
+OptInterval clip_interval (std::vector<Point> const& B,
+ Line const &l,
+ Interval const &bound)
{
double n = B.size() - 1; // number of sub-intervals
std::vector<Point> D; // distance curve control points
D.reserve (B.size());
for (size_t i = 0; i < B.size(); ++i)
{
- const double d = distance (B[i], l);
+ const double d = signed_distance(B[i], l);
D.push_back (Point(i/n, d));
}
//print(D);
- convex_hull(D);
- std::vector<Point> & p = D;
+ ConvexHull p;
+ p.swap(D);
//print(p);
bool plower, phigher;
@@ -589,8 +473,11 @@ void clip_interval (Interval& dom,
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
- dom[0] = tmin;
- dom[1] = tmax;
+ if (tmin == 1 && tmax == 0) {
+ return OptInterval();
+ } else {
+ return Interval(tmin, tmax);
+ }
}
/*
@@ -599,24 +486,20 @@ void clip_interval (Interval& dom,
* is returned through the output parameter "dom"
*/
template <>
-inline
-void clip<intersection_point_tag> (Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B)
+OptInterval clip<intersection_point_tag> (std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double precision)
{
- std::vector<double> bl(3);
- Interval bound;
- if (is_constant(A))
- {
+ Line bl;
+ if (is_constant(A, precision)) {
Point M = middle_point(A.front(), A.back());
- orthogonal_orientation_line(bl, B, M);
+ bl = orthogonal_orientation_line(B, M, precision);
+ } else {
+ bl = pick_orientation_line(A, precision);
}
- else
- {
- pick_orientation_line(bl, A);
- }
- fat_line_bounds(bound, A, bl);
- clip_interval(dom, B, bl, bound);
+ bl.normalize();
+ Interval bound = fat_line_bounds(A, bl);
+ return clip_interval(B, bl, bound);
}
@@ -627,7 +510,6 @@ void clip<intersection_point_tag> (Interval & dom,
* Compute a closed focus for the Bezier curve B and return it in F
* A focus is any curve through which all lines perpendicular to B(t) pass.
*/
-inline
void make_focus (std::vector<Point> & F, std::vector<Point> const& B)
{
assert (B.size() > 2);
@@ -743,9 +625,8 @@ void distance_control_points (std::vector<Point> & D,
* Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
* the clipped curve is returned through the output parameter "dom"
*/
-void clip_interval (Interval& dom,
- std::vector<Point> const& B,
- std::vector<Point> const& F)
+OptInterval clip_interval (std::vector<Point> const& B,
+ std::vector<Point> const& F)
{
std::vector<Point> D; // distance curve control points
distance_control_points(D, B, F);
@@ -753,8 +634,8 @@ void clip_interval (Interval& dom,
// ConvexHull chD(D);
// std::vector<Point>& p = chD.boundary; // convex hull vertices
- convex_hull(D);
- std::vector<Point> & p = D;
+ ConvexHull p;
+ p.swap(D);
//print(p, "CH(D)");
bool plower, clower;
@@ -803,8 +684,11 @@ void clip_interval (Interval& dom,
// std::cerr << "0 : lower " << p[0]
// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
}
- dom[0] = tmin;
- dom[1] = tmax;
+ if (tmin == 1 && tmax == 0) {
+ return OptInterval();
+ } else {
+ return Interval(tmin, tmax);
+ }
}
/*
@@ -813,14 +697,13 @@ void clip_interval (Interval& dom,
* for the clipped curve is returned through the output parameter "dom"
*/
template <>
-inline
-void clip<collinear_normal_tag> (Interval & dom,
- std::vector<Point> const& A,
- std::vector<Point> const& B)
+OptInterval clip<collinear_normal_tag> (std::vector<Point> const& A,
+ std::vector<Point> const& B,
+ double /*precision*/)
{
std::vector<Point> F;
make_focus(F, A);
- clip_interval(dom, B, F);
+ return clip_interval(B, F);
}
@@ -828,9 +711,9 @@ void clip<collinear_normal_tag> (Interval & dom,
const double MAX_PRECISION = 1e-8;
const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
const Interval UNIT_INTERVAL(0,1);
-const Interval EMPTY_INTERVAL = make_empty_interval();
+const OptInterval EMPTY_INTERVAL;
const Interval H1_INTERVAL(0, 0.5);
-const Interval H2_INTERVAL(0.5 + MAX_PRECISION, 1.0);
+const Interval H2_INTERVAL(nextafter(0.5, 1.0), 1.0);
/*
* iterate
@@ -884,9 +767,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
- Interval dom;
+ OptInterval dom;
- if ( is_constant(A) && is_constant(B) ){
+ if ( is_constant(A, precision) && is_constant(B, precision) ){
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
if (are_near(M1,M2)){
@@ -903,10 +786,9 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
- clip<intersection_point_tag>(dom, *C1, *C2);
+ dom = clip<intersection_point_tag>(*C1, *C2, precision);
- // [1,0] is utilized to represent an empty interval
- if (dom == EMPTY_INTERVAL)
+ if (dom.isEmpty())
{
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
@@ -917,15 +799,12 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
std::cerr << "dom : " << dom << std::endl;
#endif
// all other cases where dom[0] > dom[1] are invalid
- if (dom.min() > dom.max())
- {
- assert(dom.min() < dom.max());
- }
+ assert(dom->min() <= dom->max());
- map_to(*dom2, dom);
+ map_to(*dom2, *dom);
- portion(*C2, dom);
- if (is_constant(*C2) && is_constant(*C1))
+ portion(*C2, *dom);
+ if (is_constant(*C2, precision) && is_constant(*C1, precision))
{
Point M1 = middle_point(C1->front(), C1->back());
Point M2 = middle_point(C2->front(), C2->back());
@@ -945,10 +824,10 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
- if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+ if (dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
- std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+ std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
@@ -983,8 +862,8 @@ void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
return;
}
- std::swap(C1, C2);
- std::swap(dom1, dom2);
+ swap(C1, C2);
+ swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
@@ -1047,7 +926,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
Interval* dom1 = &dompA;
Interval* dom2 = &dompB;
- Interval dom;
+ OptInterval dom;
size_t iter = 0;
while (++iter < 100
@@ -1056,11 +935,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "iter: " << iter << std::endl;
#endif
- clip<collinear_normal_tag>(dom, *C1, *C2);
+ dom = clip<collinear_normal_tag>(*C1, *C2, precision);
- // [1,0] is utilized to represent an empty interval
- if (dom == EMPTY_INTERVAL)
- {
+ if (dom.isEmpty()) {
#if VERBOSE
std::cerr << "dom: empty" << std::endl;
#endif
@@ -1069,13 +946,9 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
#if VERBOSE
std::cerr << "dom : " << dom << std::endl;
#endif
- // all other cases where dom[0] > dom[1] are invalid
- if (dom.min() > dom.max())
- {
- assert(dom.min() < dom.max());
- }
+ assert(dom->min() <= dom->max());
- map_to(*dom2, dom);
+ map_to(*dom2, *dom);
// it's better to stop before losing computational precision
if (iter > 1 && (dom2->extent() <= MAX_PRECISION))
@@ -1086,8 +959,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
- portion(*C2, dom);
- if (iter > 1 && is_constant(*C2))
+ portion(*C2, *dom);
+ if (iter > 1 && is_constant(*C2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1098,10 +971,10 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
// if we have clipped less than 20% than we need to subdive the curve
// with the largest domain into two sub-curves
- if ( dom.extent() > MIN_CLIPPED_SIZE_THRESHOLD)
+ if ( dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
{
#if VERBOSE
- std::cerr << "clipped less than 20% : " << dom.extent() << std::endl;
+ std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
std::cerr << "angle(pA) : " << angle(pA) << std::endl;
std::cerr << "angle(pB) : " << angle(pB) << std::endl;
#endif
@@ -1115,7 +988,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
}
pC1 = pC2 = pA;
portion(pC1, H1_INTERVAL);
- if (false && is_constant(pC1))
+ if (false && is_constant(pC1, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1123,7 +996,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
portion(pC2, H2_INTERVAL);
- if (is_constant(pC2))
+ if (is_constant(pC2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
@@ -1146,7 +1019,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
}
pC1 = pC2 = pB;
portion(pC1, H1_INTERVAL);
- if (is_constant(pC1))
+ if (is_constant(pC1, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC1 is constant" << std::endl;
@@ -1154,7 +1027,7 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
break;
}
portion(pC2, H2_INTERVAL);
- if (is_constant(pC2))
+ if (is_constant(pC2, precision))
{
#if VERBOSE
std::cerr << "new curve portion pC2 is constant" << std::endl;
@@ -1172,8 +1045,8 @@ void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
return;
}
- std::swap(C1, C2);
- std::swap(dom1, dom2);
+ swap(C1, C2);
+ swap(dom1, dom2);
#if VERBOSE
std::cerr << "dom(pA) : " << dompA << std::endl;
std::cerr << "dom(pB) : " << dompB << std::endl;
diff --git a/src/2geom/bezier-curve.cpp b/src/2geom/bezier-curve.cpp
index 6dfb0f0b3..17221264b 100644
--- a/src/2geom/bezier-curve.cpp
+++ b/src/2geom/bezier-curve.cpp
@@ -32,6 +32,9 @@
*/
#include <2geom/bezier-curve.h>
+#include <2geom/path-sink.h>
+#include <2geom/basic-intersection.h>
+#include <2geom/nearest-time.h>
namespace Geom
{
@@ -97,7 +100,7 @@ namespace Geom
* @class BezierCurveN
* @brief Bezier curve with compile-time specified order.
*
- * @tparam degree unsigned value indicating the order of the bezier curve
+ * @tparam degree unsigned value indicating the order of the Bezier curve
*
* @relates BezierCurve
* @ingroup Curves
@@ -105,12 +108,10 @@ namespace Geom
BezierCurve::BezierCurve(std::vector<Point> const &pts)
+ : inner(pts)
{
- inner = D2<Bezier>(Bezier::Order(pts.size() - 1), Bezier::Order(pts.size() - 1));
- for (unsigned d = 0; d < 2; ++d) {
- for (unsigned i = 0; i < pts.size(); i++) {
- inner[d][i] = pts[i][d];
- }
+ if (pts.size() < 2) {
+ THROW_RANGEERROR("Bezier curve must have at least 2 control points");
}
}
@@ -124,16 +125,95 @@ Coord BezierCurve::length(Coord tolerance) const
return distance(initialPoint(), finalPoint());
case 2:
{
- std::vector<Point> pts = points();
+ std::vector<Point> pts = controlPoints();
return bezier_length(pts[0], pts[1], pts[2], tolerance);
}
case 3:
{
- std::vector<Point> pts = points();
+ std::vector<Point> pts = controlPoints();
return bezier_length(pts[0], pts[1], pts[2], pts[3], tolerance);
}
default:
- return bezier_length(points(), tolerance);
+ return bezier_length(controlPoints(), tolerance);
+ }
+}
+
+std::vector<CurveIntersection>
+BezierCurve::intersect(Curve const &other, Coord eps) const
+{
+ std::vector<CurveIntersection> result;
+
+ // in case we encounter an order-1 curve created from a vector
+ // or a degenerate elliptical arc
+ if (isLineSegment()) {
+ LineSegment ls(initialPoint(), finalPoint());
+ result = ls.intersect(other);
+ return result;
+ }
+
+ // here we are sure that this curve is at least a quadratic Bezier
+ BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other);
+ if (bez) {
+ std::vector<std::pair<double, double> > xs;
+ find_intersections(xs, inner, bez->inner, eps);
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ CurveIntersection x(*this, other, xs[i].first, xs[i].second);
+ result.push_back(x);
+ }
+ return result;
+ }
+
+ // pass other intersection types to the other curve
+ result = other.intersect(*this, eps);
+ transpose_in_place(result);
+ return result;
+}
+
+bool BezierCurve::operator==(Curve const &c) const
+{
+ if (this == &c) return true;
+
+ BezierCurve const *other = dynamic_cast<BezierCurve const *>(&c);
+ if (!other) return false;
+ if (size() != other->size()) return false;
+
+ for (unsigned i = 0; i < size(); ++i) {
+ if (controlPoint(i) != other->controlPoint(i)) return false;
+ }
+ return true;
+}
+
+Coord BezierCurve::nearestTime(Point const &p, Coord from, Coord to) const
+{
+ return nearest_time(p, inner, from, to);
+}
+
+void BezierCurve::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (size() > 4) {
+ Curve::feed(sink, moveto_initial);
+ return;
+ }
+
+ Point ip = controlPoint(0);
+ if (moveto_initial) {
+ sink.moveTo(ip);
+ }
+ switch (size()) {
+ case 2:
+ sink.lineTo(controlPoint(1));
+ break;
+ case 3:
+ sink.quadTo(controlPoint(1), controlPoint(2));
+ break;
+ case 4:
+ sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
+ break;
+ default:
+ // TODO: add a path sink method that accepts a vector of control points
+ // and converts to cubic spline by default
+ assert(false);
+ break;
}
}
@@ -164,9 +244,11 @@ Curve *BezierCurveN<1>::derivative() const {
}
template<>
-Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const
+Coord BezierCurveN<1>::nearestTime(Point const& p, Coord from, Coord to) const
{
- if ( from > to ) std::swap(from, to);
+ using std::swap;
+
+ if ( from > to ) swap(from, to);
Point ip = pointAt(from);
Point fp = pointAt(to);
Point v = fp - ip;
@@ -178,6 +260,53 @@ Coord BezierCurveN<1>::nearestPoint(Point const& p, Coord from, Coord to) const
else return from + t*(to-from);
}
+template <>
+std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &other, Coord eps) const
+{
+ std::vector<CurveIntersection> result;
+
+ // only handle intersections with other LineSegments here
+ if (other.isLineSegment()) {
+ Line this_line(initialPoint(), finalPoint());
+ Line other_line(other.initialPoint(), other.finalPoint());
+ result = this_line.intersect(other_line);
+ filter_line_segment_intersections(result, true, true);
+ return result;
+ }
+
+ // pass all other types to the other curve
+ result = other.intersect(*this, eps);
+ transpose_in_place(result);
+ return result;
+}
+
+template <>
+void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.lineTo(controlPoint(1));
+}
+
+template <>
+void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.quadTo(controlPoint(1), controlPoint(2));
+}
+
+template <>
+void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(controlPoint(0));
+ }
+ sink.curveTo(controlPoint(1), controlPoint(2), controlPoint(3));
+}
+
static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
{
@@ -186,7 +315,7 @@ static Coord bezier_length_internal(std::vector<Point> &v1, Coord tolerance)
* but shorter than the length of the polyline formed by its control
* points. When the difference between the two values is smaller than the
* error tolerance, we can be sure that the true value is no further than
- * 2*tolerance from their arithmetic mean. When it's larger, we recursively
+ * 0.5 * tolerance from their arithmetic mean. When it's larger, we recursively
* subdivide the Bezier curve into two parts and add their lengths.
*/
Coord lower = distance(v1.front(), v1.back());
diff --git a/src/2geom/bezier-curve.h b/src/2geom/bezier-curve.h
index d379526fa..9ac4d7b4d 100644
--- a/src/2geom/bezier-curve.h
+++ b/src/2geom/bezier-curve.h
@@ -33,8 +33,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LIB2GEOM_BEZIER_CURVE_H
-#define SEEN_LIB2GEOM_BEZIER_CURVE_H
+#ifndef LIB2GEOM_SEEN_BEZIER_CURVE_H
+#define LIB2GEOM_SEEN_BEZIER_CURVE_H
#include <2geom/curve.h>
#include <2geom/sbasis-curve.h> // for non-native winding method
@@ -48,25 +48,35 @@ class BezierCurve : public Curve {
protected:
D2<Bezier> inner;
BezierCurve() {}
- BezierCurve(D2<Bezier> const &b) : inner(b) {}
BezierCurve(Bezier const &x, Bezier const &y) : inner(x, y) {}
BezierCurve(std::vector<Point> const &pts);
public:
+ explicit BezierCurve(D2<Bezier> const &b) : inner(b) {}
+
/// @name Access and modify control points
/// @{
/** @brief Get the order of the Bezier curve.
* A Bezier curve has order() + 1 control points. */
unsigned order() const { return inner[X].order(); }
+ /** @brief Get the number of control points. */
+ unsigned size() const { return inner[X].order() + 1; }
+ /** @brief Access control points of the curve.
+ * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
+ * @return The control point. No-reference return, use setPoint() to modify control points. */
+ Point controlPoint(unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
+ Point operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
/** @brief Get the control points.
* @return Vector with order() + 1 control points. */
- std::vector<Point> points() const { return bezier_points(inner); }
+ std::vector<Point> controlPoints() const { return bezier_points(inner); }
+ D2<Bezier> const &fragment() const { return inner; }
+
/** @brief Modify a control point.
* @param ix The zero-based index of the point to modify. Note that the caller is responsible for checking that this value is <= order().
* @param v The new value of the point */
- void setPoint(unsigned ix, Point v) {
- inner[X].setPoint(ix, v[X]);
- inner[Y].setPoint(ix, v[Y]);
+ void setPoint(unsigned ix, Point const &v) {
+ inner[X][ix] = v[X];
+ inner[Y][ix] = v[Y];
}
/** @brief Set new control points.
* @param ps Vector which must contain order() + 1 points.
@@ -80,23 +90,22 @@ public:
setPoint(i, ps[i]);
}
}
- /** @brief Access control points of the curve.
- * @param ix The (zero-based) index of the control point. Note that the caller is responsible for checking that this value is <= order().
- * @return The control point. No-reference return, use setPoint() to modify control points. */
- Point const operator[](unsigned ix) const { return Point(inner[X][ix], inner[Y][ix]); }
/// @}
/// @name Construct a Bezier curve with runtime-determined order.
/// @{
- /** @brief Construct a curve from a vector of control points. */
+ /** @brief Construct a curve from a vector of control points.
+ * This will construct the appropriate specialization of BezierCurve (i.e. LineSegment,
+ * QuadraticBezier or Cubic Bezier) if the number of control points in the passed vector
+ * does not exceed 4. */
static BezierCurve *create(std::vector<Point> const &pts);
/// @}
// implementation of virtual methods goes here
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
- virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual bool isDegenerate() const { return inner.isConstant(0); }
+ virtual bool isLineSegment() const { return size() == 2; }
virtual void setInitial(Point const &v) { setPoint(0, v); }
virtual void setFinal(Point const &v) { setPoint(order(), v); }
virtual Rect boundsFast() const { return *bounds_fast(inner); }
@@ -120,19 +129,24 @@ public:
return new BezierCurve(Geom::reverse(inner));
}
- virtual Curve *transformed(Affine const &m) const {
- BezierCurve *ret = new BezierCurve();
- std::vector<Point> ps = points();
- for (unsigned i = 0; i <= order(); i++) {
- ps[i] = ps[i] * m;
+ using Curve::operator*=;
+ virtual void operator*=(Translate const &tr) {
+ for (unsigned i = 0; i < size(); ++i) {
+ inner[X][i] += tr[X];
+ inner[Y][i] += tr[Y];
+ }
+ }
+ virtual void operator*=(Scale const &s) {
+ for (unsigned i = 0; i < size(); ++i) {
+ inner[X][i] *= s[X];
+ inner[Y][i] *= s[Y];
+ }
+ }
+ virtual void operator*=(Affine const &m) {
+ for (unsigned i = 0; i < size(); ++i) {
+ setPoint(i, controlPoint(i) * m);
}
- ret->setPoints(ps);
- return ret;
}
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
- };
virtual Curve *derivative() const {
return new BezierCurve(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
@@ -143,26 +157,32 @@ public:
virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
return (inner[d] - v).roots();
}
+ virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const;
virtual Coord length(Coord tolerance) const;
- virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const { return inner.valueAndDerivatives(t, n); }
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const;
+ virtual Point pointAt(Coord t) const { return inner.pointAt(t); }
+ virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
+ return inner.valueAndDerivatives(t, n);
+ }
virtual Coord valueAt(Coord t, Dim2 d) const { return inner[d].valueAt(t); }
virtual D2<SBasis> toSBasis() const {return inner.toSBasis(); }
-#endif
+ virtual bool operator==(Curve const &c) const;
+ virtual void feed(PathSink &sink, bool) const;
};
template <unsigned degree>
-class BezierCurveN : public BezierCurve {
+class BezierCurveN
+ : public BezierCurve
+{
+ template <unsigned required_degree>
+ static void assert_degree(BezierCurveN<required_degree> const *) {}
public:
- template <unsigned required_degree>
- static void assert_degree(BezierCurveN<required_degree> const *) {}
-
/// @name Construct Bezier curves
/// @{
/** @brief Construct a Bezier curve of the specified order with all points zero. */
BezierCurveN() {
- inner = D2<Bezier> (Bezier::Order(degree), Bezier::Order(degree));
+ inner = D2<Bezier>(Bezier(Bezier::Order(degree)), Bezier(Bezier::Order(degree)));
}
/** @brief Construct from 2D Bezier polynomial. */
@@ -224,7 +244,10 @@ public:
BezierCurveN(sx.second, sy.second));
}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ virtual bool isLineSegment() const {
+ return size() == 2;
+ }
+
virtual Curve *duplicate() const {
return new BezierCurveN(*this);
}
@@ -242,30 +265,19 @@ public:
return new BezierCurveN(Geom::reverse(inner));
}
}
- virtual Curve *transformed(Affine const &m) const {
- if (degree == 1) {
- return new BezierCurveN<1>(initialPoint() * m, finalPoint() * m);
- } else {
- BezierCurveN *ret = new BezierCurveN();
- std::vector<Point> ps = points();
- for (unsigned i = 0; i <= degree; i++) {
- ps[i] = ps[i] * m;
- }
- ret->setPoints(ps);
- return ret;
- }
+ virtual Curve *derivative() const;
+
+ virtual Coord nearestTime(Point const &p, Coord from = 0, Coord to = 1) const {
+ return BezierCurve::nearestTime(p, from, to);
}
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const {
+ // call super. this is implemented only to allow specializations
+ return BezierCurve::intersect(other, eps);
}
- virtual Curve *derivative() const;
-
- // the method below is defined so that LineSegment can specialize it
- virtual Coord nearestPoint(Point const& p, Coord from = 0, Coord to = 1) const {
- return Curve::nearestPoint(p, from, to);
+ virtual void feed(PathSink &sink, bool moveto_initial) const {
+ // call super. this is implemented only to allow specializations
+ BezierCurve::feed(sink, moveto_initial);
}
-#endif
};
// BezierCurveN<0> is meaningless; specialize it out
@@ -291,9 +303,14 @@ Curve *BezierCurveN<degree>::derivative() const {
return new BezierCurveN<degree-1>(Geom::derivative(inner[X]), Geom::derivative(inner[Y]));
}
-// optimized specializations for LineSegment
+// optimized specializations
+template <> inline bool BezierCurveN<1>::isLineSegment() const { return true; }
template <> Curve *BezierCurveN<1>::derivative() const;
-template <> Coord BezierCurveN<1>::nearestPoint(Point const &, Coord, Coord) const;
+template <> Coord BezierCurveN<1>::nearestTime(Point const &, Coord, Coord) const;
+template <> std::vector<CurveIntersection> BezierCurveN<1>::intersect(Curve const &, Coord) const;
+template <> void BezierCurveN<1>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<2>::feed(PathSink &sink, bool moveto_initial) const;
+template <> void BezierCurveN<3>::feed(PathSink &sink, bool moveto_initial) const;
inline Point middle_point(LineSegment const& _segment) {
return ( _segment.initialPoint() + _segment.finalPoint() ) / 2;
@@ -309,7 +326,7 @@ Coord bezier_length(Point p0, Point p1, Point p2, Point p3, Coord tolerance = 0.
} // end namespace Geom
-#endif // _2GEOM_BEZIER_CURVE_H_
+#endif // LIB2GEOM_SEEN_BEZIER_CURVE_H
/*
Local Variables:
diff --git a/src/2geom/bezier-to-sbasis.h b/src/2geom/bezier-to-sbasis.h
index 8cd4bf444..73c55d9b2 100644
--- a/src/2geom/bezier-to-sbasis.h
+++ b/src/2geom/bezier-to-sbasis.h
@@ -29,8 +29,8 @@
*
*/
-#ifndef _BEZIER_TO_SBASIS
-#define _BEZIER_TO_SBASIS
+#ifndef LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
+#define LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
#include <2geom/coord.h>
#include <2geom/point.h>
@@ -79,13 +79,9 @@ D2<SBasis> handles_to_sbasis(T const& handles, unsigned order)
return sbc;
}
-
} // end namespace Geom
-
-
-
-#endif
+#endif // LIB2GEOM_SEEN_BEZIER_TO_SBASIS_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/bezier-utils.cpp b/src/2geom/bezier-utils.cpp
index ec17f6869..816bcdeb4 100644
--- a/src/2geom/bezier-utils.cpp
+++ b/src/2geom/bezier-utils.cpp
@@ -666,9 +666,9 @@ Point
bezier_pt(unsigned const degree, Point const V[], double const t)
{
/** Pascal's triangle. */
- static int const pascal[4][4] = {{1},
- {1, 1},
- {1, 2, 1},
+ static int const pascal[4][4] = {{1, 0, 0, 0},
+ {1, 1, 0, 0},
+ {1, 2, 1, 0},
{1, 3, 3, 1}};
assert( degree < 4);
double const s = 1.0 - t;
diff --git a/src/2geom/bezier.cpp b/src/2geom/bezier.cpp
new file mode 100644
index 000000000..0c9d12c3b
--- /dev/null
+++ b/src/2geom/bezier.cpp
@@ -0,0 +1,324 @@
+/**
+ * @file
+ * @brief Bernstein-Bezier polynomial
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Michael Sloan <mgsloan@gmail.com>
+ * Nathan Hurst <njh@njhurst.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <2geom/bezier.h>
+#include <2geom/solver.h>
+#include <2geom/concepts.h>
+
+namespace Geom {
+
+std::vector<Coord> Bezier::valueAndDerivatives(Coord t, unsigned n_derivs) const {
+ /* This is inelegant, as it uses several extra stores. I think there might be a way to
+ * evaluate roughly in situ. */
+
+ // initialize return vector with zeroes, such that we only need to replace the non-zero derivs
+ std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0));
+
+ // initialize temp storage variables
+ std::valarray<Coord> d_(order()+1);
+ for(unsigned i = 0; i < size(); i++) {
+ d_[i] = c_[i];
+ }
+
+ unsigned nn = n_derivs + 1;
+ if(n_derivs > order()) {
+ nn = order()+1; // only calculate the non zero derivs
+ }
+ for(unsigned di = 0; di < nn; di++) {
+ //val_n_der[di] = (casteljau_subdivision(t, &d_[0], NULL, NULL, order() - di));
+ val_n_der[di] = bernstein_value_at(t, &d_[0], order() - di);
+ for(unsigned i = 0; i < order() - di; i++) {
+ d_[i] = (order()-di)*(d_[i+1] - d_[i]);
+ }
+ }
+
+ return val_n_der;
+}
+
+void Bezier::subdivide(Coord t, Bezier *left, Bezier *right) const
+{
+ if (left) {
+ left->c_.resize(size());
+ if (right) {
+ right->c_.resize(size());
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ &left->c_[0], &right->c_[0], order());
+ } else {
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ &left->c_[0], NULL, order());
+ }
+ } else if (right) {
+ right->c_.resize(size());
+ casteljau_subdivision<double>(t, &const_cast<std::valarray<Coord>&>(c_)[0],
+ NULL, &right->c_[0], order());
+ }
+}
+
+std::pair<Bezier, Bezier> Bezier::subdivide(Coord t) const
+{
+ std::pair<Bezier, Bezier> ret;
+ subdivide(t, &ret.first, &ret.second);
+ return ret;
+}
+
+std::vector<Coord> Bezier::roots() const
+{
+ std::vector<Coord> solutions;
+ find_bezier_roots(solutions, 0, 1);
+ std::sort(solutions.begin(), solutions.end());
+ return solutions;
+}
+
+std::vector<Coord> Bezier::roots(Interval const &ivl) const
+{
+ std::vector<Coord> solutions;
+ find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max());
+ std::sort(solutions.begin(), solutions.end());
+ return solutions;
+}
+
+Bezier Bezier::forward_difference(unsigned k) const
+{
+ Bezier fd(Order(order()-k));
+ unsigned n = fd.size();
+
+ for(unsigned i = 0; i < n; i++) {
+ fd[i] = 0;
+ for(unsigned j = i; j < n; j++) {
+ fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i);
+ }
+ }
+ return fd;
+}
+
+Bezier Bezier::elevate_degree() const
+{
+ Bezier ed(Order(order()+1));
+ unsigned n = size();
+ ed[0] = c_[0];
+ ed[n] = c_[n-1];
+ for(unsigned i = 1; i < n; i++) {
+ ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n);
+ }
+ return ed;
+}
+
+Bezier Bezier::reduce_degree() const
+{
+ if(order() == 0) return *this;
+ Bezier ed(Order(order()-1));
+ unsigned n = size();
+ ed[0] = c_[0];
+ ed[n-1] = c_[n]; // ensure exact endpoints
+ unsigned middle = n/2;
+ for(unsigned i = 1; i < middle; i++) {
+ ed[i] = (n*c_[i] - i*ed[i-1])/(n-i);
+ }
+ for(unsigned i = n-1; i >= middle; i--) {
+ ed[i] = (n*c_[i] - i*ed[n-i])/(i);
+ }
+ return ed;
+}
+
+Bezier Bezier::elevate_to_degree(unsigned newDegree) const
+{
+ Bezier ed = *this;
+ for(unsigned i = degree(); i < newDegree; i++) {
+ ed = ed.elevate_degree();
+ }
+ return ed;
+}
+
+Bezier Bezier::deflate() const
+{
+ if(order() == 0) return *this;
+ unsigned n = order();
+ Bezier b(Order(n-1));
+ for(unsigned i = 0; i < n; i++) {
+ b[i] = (n*c_[i+1])/(i+1);
+ }
+ return b;
+}
+
+SBasis Bezier::toSBasis() const
+{
+ SBasis sb;
+ bezier_to_sbasis(sb, (*this));
+ return sb;
+ //return bezier_to_sbasis(&c_[0], order());
+}
+
+Bezier &Bezier::operator+=(Bezier const &other)
+{
+ if (c_.size() > other.size()) {
+ c_ += other.elevate_to_degree(degree()).c_;
+ } else if (c_.size() < other.size()) {
+ *this = elevate_to_degree(other.degree());
+ c_ += other.c_;
+ } else {
+ c_ += other.c_;
+ }
+ return *this;
+}
+
+Bezier &Bezier::operator-=(Bezier const &other)
+{
+ if (c_.size() > other.size()) {
+ c_ -= other.elevate_to_degree(degree()).c_;
+ } else if (c_.size() < other.size()) {
+ *this = elevate_to_degree(other.degree());
+ c_ -= other.c_;
+ } else {
+ c_ -= other.c_;
+ }
+ return *this;
+}
+
+
+
+Bezier operator*(Bezier const &f, Bezier const &g)
+{
+ unsigned m = f.order();
+ unsigned n = g.order();
+ Bezier h(Bezier::Order(m+n));
+ // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k)
+
+ for(unsigned i = 0; i <= m; i++) {
+ const double fi = choose<double>(m,i)*f[i];
+ for(unsigned j = 0; j <= n; j++) {
+ h[i+j] += fi * choose<double>(n,j)*g[j];
+ }
+ }
+ for(unsigned k = 0; k <= m+n; k++) {
+ h[k] /= choose<double>(m+n, k);
+ }
+ return h;
+}
+
+Bezier portion(Bezier const &a, double from, double to)
+{
+ Bezier ret(a);
+
+ bool reverse_result = false;
+ if (from > to) {
+ std::swap(from, to);
+ reverse_result = true;
+ }
+
+ do {
+ if (from == 0) {
+ if (to == 1) {
+ break;
+ }
+ casteljau_subdivision<double>(to, &ret.c_[0], &ret.c_[0], NULL, ret.order());
+ break;
+ }
+ casteljau_subdivision<double>(from, &ret.c_[0], NULL, &ret.c_[0], ret.order());
+ if (to == 1) break;
+ casteljau_subdivision<double>((to - from) / (1 - from), &ret.c_[0], &ret.c_[0], NULL, ret.order());
+ // to protect against numerical inaccuracy in the above expression, we manually set
+ // the last coefficient to a value evaluated directly from the original polynomial
+ ret.c_[ret.order()] = a.valueAt(to);
+ } while(0);
+
+ if (reverse_result) {
+ std::reverse(&ret.c_[0], &ret.c_[0] + ret.c_.size());
+ }
+ return ret;
+}
+
+Bezier derivative(Bezier const &a)
+{
+ //if(a.order() == 1) return Bezier(0.0);
+ if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]);
+ Bezier der(Bezier::Order(a.order()-1));
+
+ for(unsigned i = 0; i < a.order(); i++) {
+ der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]);
+ }
+ return der;
+}
+
+Bezier integral(Bezier const &a)
+{
+ Bezier inte(Bezier::Order(a.order()+1));
+
+ inte[0] = 0;
+ for(unsigned i = 0; i < inte.order(); i++) {
+ inte[i+1] = inte[i] + a[i]/(inte.order());
+ }
+ return inte;
+}
+
+OptInterval bounds_fast(Bezier const &b)
+{
+ OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size());
+ return ret;
+}
+
+OptInterval bounds_exact(Bezier const &b)
+{
+ OptInterval ret(b.at0(), b.at1());
+ std::vector<Coord> r = derivative(b).roots();
+ for (unsigned i = 0; i < r.size(); ++i) {
+ ret->expandTo(b.valueAt(r[i]));
+ }
+ return ret;
+}
+
+OptInterval bounds_local(Bezier const &b, OptInterval const &i)
+{
+ //return bounds_local(b.toSBasis(), i);
+ if (i) {
+ return bounds_fast(portion(b, i->min(), i->max()));
+ } else {
+ return OptInterval();
+ }
+}
+
+} // end namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/bezier.h b/src/2geom/bezier.h
index 51d5211d9..c41c2b3a7 100644
--- a/src/2geom/bezier.h
+++ b/src/2geom/bezier.h
@@ -1,13 +1,14 @@
/**
* @file
- * @brief Bezier polynomial
+ * @brief Bernstein-Bezier polynomial
*//*
* Authors:
* MenTaLguY <mental@rydia.net>
* Michael Sloan <mgsloan@gmail.com>
* Nathan Hurst <njh@njhurst.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2007 Authors
+ * Copyright 2007-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -37,70 +38,23 @@
#ifndef LIB2GEOM_SEEN_BEZIER_H
#define LIB2GEOM_SEEN_BEZIER_H
+#include <algorithm>
#include <valarray>
#include <boost/optional.hpp>
-#include <2geom/coord.h>
#include <2geom/choose.h>
-#include <valarray>
-#include <2geom/math-utils.h>
+#include <2geom/coord.h>
#include <2geom/d2.h>
-#include <2geom/solver.h>
+#include <2geom/math-utils.h>
namespace Geom {
-inline Coord subdivideArr(Coord t, Coord const *v, Coord *left, Coord *right, unsigned order) {
-/*
- * Bernstein :
- * Evaluate a Bernstein function at a particular parameter value
- * Fill in control points for resulting sub-curves.
- *
- */
-
- unsigned N = order+1;
- std::valarray<Coord> row(N);
- for (unsigned i = 0; i < N; i++)
- row[i] = v[i];
-
- // Triangle computation
- const double omt = (1-t);
- if(left)
- left[0] = row[0];
- if(right)
- right[order] = row[order];
- for (unsigned i = 1; i < N; i++) {
- for (unsigned j = 0; j < N - i; j++) {
- row[j] = omt*row[j] + t*row[j+1];
- }
- if(left)
- left[i] = row[0];
- if(right)
- right[order-i] = row[order-i];
- }
- return (row[0]);
-/*
- Coord vtemp[order+1][order+1];
-
- // Copy control points
- std::copy(v, v+order+1, vtemp[0]);
-
- // Triangle computation
- for (unsigned i = 1; i <= order; i++) {
- for (unsigned j = 0; j <= order - i; j++) {
- vtemp[i][j] = lerp(t, vtemp[i-1][j], vtemp[i-1][j+1]);
- }
- }
- if(left != NULL)
- for (unsigned j = 0; j <= order; j++)
- left[j] = vtemp[j][0];
- if(right != NULL)
- for (unsigned j = 0; j <= order; j++)
- right[j] = vtemp[order-j][j];
-
- return (vtemp[order][0]);*/
-}
-
+/** @brief Compute the value of a Bernstein-Bezier polynomial.
+ * This method uses a Horner-like fast evaluation scheme.
+ * @param t Time value
+ * @param c_ Pointer to coefficients
+ * @param n Degree of the polynomial (number of coefficients minus one) */
template <typename T>
-inline T bernsteinValueAt(double t, T const *c_, unsigned n) {
+inline T bernstein_value_at(double t, T const *c_, unsigned n) {
double u = 1.0 - t;
double bc = 1;
double tn = 1;
@@ -113,16 +67,71 @@ inline T bernsteinValueAt(double t, T const *c_, unsigned n) {
return (tmp + tn*t*c_[n]);
}
-class Bezier {
+/** @brief Perform Casteljau subdivision of a Bezier polynomial.
+ * Given an array of coefficients and a time value, computes two new Bernstein-Bezier basis
+ * polynomials corresponding to the \f$[0, t]\f$ and \f$[t, 1]\f$ intervals of the original one.
+ * @param t Time value
+ * @param v Array of input coordinates
+ * @param left Output polynomial corresponding to \f$[0, t]\f$
+ * @param right Output polynomial corresponding to \f$[t, 1]\f$
+ * @param order Order of the input polynomial, equal to one less the number of coefficients
+ * @return Value of the polynomial at @a t */
+template <typename T>
+inline T casteljau_subdivision(double t, T const *v, T *left, T *right, unsigned order) {
+ // The Horner-like scheme gives very slightly different results, but we need
+ // the result of subdivision to match exactly with Bezier's valueAt function.
+ T val = bernstein_value_at(t, v, order);
+
+ if (!left && !right) {
+ return val;
+ }
+
+ if (!right) {
+ if (left != v) {
+ std::copy(v, v + order + 1, left);
+ }
+ for (std::size_t i = order; i > 0; --i) {
+ for (std::size_t j = i; j <= order; ++j) {
+ left[j] = lerp(t, left[j-1], left[j]);
+ }
+ }
+ left[order] = val;
+ return left[order];
+ }
+
+ if (right != v) {
+ std::copy(v, v + order + 1, right);
+ }
+ for (std::size_t i = 1; i <= order; ++i) {
+ if (left) {
+ left[i-1] = right[0];
+ }
+ for (std::size_t j = i; j > 0; --j) {
+ right[j-1] = lerp(t, right[j-1], right[j]);
+ }
+ }
+ right[0] = val;
+ if (left) {
+ left[order] = right[0];
+ }
+ return right[0];
+}
+
+/**
+ * @brief Polynomial in Bernstein-Bezier basis
+ * @ingroup Fragments
+ */
+class Bezier
+ : boost::arithmetic< Bezier, double
+ , boost::additive< Bezier
+ > >
+{
private:
std::valarray<Coord> c_;
friend Bezier portion(const Bezier & a, Coord from, Coord to);
-
friend OptInterval bounds_fast(Bezier const & b);
-
friend Bezier derivative(const Bezier & a);
-
friend class Bernstein;
void
@@ -130,13 +139,14 @@ private:
double l, double r) const;
protected:
- Bezier(Coord const c[], unsigned ord) : c_(c, ord+1){
- //std::copy(c, c+order()+1, &c_[0]);
- }
+ Bezier(Coord const c[], unsigned ord)
+ : c_(c, ord+1)
+ {}
public:
- unsigned int order() const { return c_.size()-1;}
- unsigned int size() const { return c_.size();}
+ unsigned order() const { return c_.size()-1;}
+ unsigned degree() const { return order(); }
+ unsigned size() const { return c_.size();}
Bezier() {}
Bezier(const Bezier& b) :c_(b.c_) {}
@@ -160,260 +170,147 @@ public:
assert(ord.order == order());
}
+ /// @name Construct Bezier polynomials from their control points
+ /// @{
explicit Bezier(Coord c0) : c_(0., 1) {
c_[0] = c0;
}
-
- //Construct an order-1 bezier (linear Bézier)
Bezier(Coord c0, Coord c1) : c_(0., 2) {
c_[0] = c0; c_[1] = c1;
}
-
- //Construct an order-2 bezier (quadratic Bézier)
Bezier(Coord c0, Coord c1, Coord c2) : c_(0., 3) {
c_[0] = c0; c_[1] = c1; c_[2] = c2;
}
-
- //Construct an order-3 bezier (cubic Bézier)
Bezier(Coord c0, Coord c1, Coord c2, Coord c3) : c_(0., 4) {
c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3;
}
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4) : c_(0., 5) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5) : c_(0., 6) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6) : c_(0., 7) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7) : c_(0., 8) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7, Coord c8) : c_(0., 9) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8;
+ }
+ Bezier(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4,
+ Coord c5, Coord c6, Coord c7, Coord c8, Coord c9) : c_(0., 10) {
+ c_[0] = c0; c_[1] = c1; c_[2] = c2; c_[3] = c3; c_[4] = c4;
+ c_[5] = c5; c_[6] = c6; c_[7] = c7; c_[8] = c8; c_[9] = c9;
+ }
- void resize (unsigned int n, Coord v = 0)
- {
- c_.resize (n, v);
+ template <typename Iter>
+ Bezier(Iter first, Iter last) {
+ c_.resize(std::distance(first, last));
+ for (std::size_t i = 0; first != last; ++first, ++i) {
+ c_[i] = *first;
+ }
}
+ Bezier(std::vector<Coord> const &vec)
+ : c_(&vec[0], vec.size())
+ {}
+ /// @}
- void clear()
- {
+ void resize (unsigned int n, Coord v = 0) {
+ c_.resize (n, v);
+ }
+ void clear() {
c_.resize(0);
}
- inline unsigned degree() const { return order(); }
-
//IMPL: FragmentConcept
typedef Coord output_type;
- inline bool isZero(double eps=EPSILON) const {
+ bool isZero(double eps=EPSILON) const {
for(unsigned i = 0; i <= order(); i++) {
if( ! are_near(c_[i], 0., eps) ) return false;
}
return true;
}
- inline bool isConstant(double eps=EPSILON) const {
+ bool isConstant(double eps=EPSILON) const {
for(unsigned i = 1; i <= order(); i++) {
if( ! are_near(c_[i], c_[0], eps) ) return false;
}
return true;
}
- inline bool isFinite() const {
+ bool isFinite() const {
for(unsigned i = 0; i <= order(); i++) {
if(!IS_FINITE(c_[i])) return false;
}
return true;
}
- inline Coord at0() const { return c_[0]; }
- inline Coord at1() const { return c_[order()]; }
-
- inline Coord valueAt(double t) const {
- int n = order();
- double u, bc, tn, tmp;
- int i;
- u = 1.0 - t;
- bc = 1;
- tn = 1;
- tmp = c_[0]*u;
- for(i=1; i<n; i++){
- tn = tn*t;
- bc = bc*(n-i+1)/i;
- tmp = (tmp + tn*bc*c_[i])*u;
- }
- return (tmp + tn*t*c_[n]);
+ Coord at0() const { return c_[0]; }
+ Coord &at0() { return c_[0]; }
+ Coord at1() const { return c_[order()]; }
+ Coord &at1() { return c_[order()]; }
+
+ Coord valueAt(double t) const {
+ return bernstein_value_at(t, &c_[0], order());
}
- inline Coord operator()(double t) const { return valueAt(t); }
+ Coord operator()(double t) const { return valueAt(t); }
SBasis toSBasis() const;
- //Only mutator
- inline Coord &operator[](unsigned ix) { return c_[ix]; }
- inline Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
- //inline Coord const &operator[](unsigned ix) const { return c_[ix]; }
- inline void setPoint(unsigned ix, double val) { c_[ix] = val; }
-
- /**
- * The size of the returned vector equals n_derivs+1.
- */
- std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const {
- /* This is inelegant, as it uses several extra stores. I think there might be a way to
- * evaluate roughly in situ. */
-
- // initialize return vector with zeroes, such that we only need to replace the non-zero derivs
- std::vector<Coord> val_n_der(n_derivs + 1, Coord(0.0));
-
- // initialize temp storage variables
- std::valarray<Coord> d_(order()+1);
- for(unsigned i = 0; i < size(); i++) {
- d_[i] = c_[i];
- }
+ Coord &operator[](unsigned ix) { return c_[ix]; }
+ Coord const &operator[](unsigned ix) const { return const_cast<std::valarray<Coord>&>(c_)[ix]; }
- unsigned nn = n_derivs + 1;
- if(n_derivs > order()) {
- nn = order()+1; // only calculate the non zero derivs
- }
- for(unsigned di = 0; di < nn; di++) {
- //val_n_der[di] = (subdivideArr(t, &d_[0], NULL, NULL, order() - di));
- val_n_der[di] = bernsteinValueAt(t, &d_[0], order() - di);
- for(unsigned i = 0; i < order() - di; i++) {
- d_[i] = (order()-di)*(d_[i+1] - d_[i]);
- }
- }
+ void setCoeff(unsigned ix, double val) { c_[ix] = val; }
- return val_n_der;
- }
+ // The size of the returned vector equals n_derivs+1.
+ std::vector<Coord> valueAndDerivatives(Coord t, unsigned n_derivs) const;
- std::pair<Bezier, Bezier > subdivide(Coord t) const {
- Bezier a(Bezier::Order(*this)), b(Bezier::Order(*this));
- subdivideArr(t, &const_cast<std::valarray<Coord>&>(c_)[0], &a.c_[0], &b.c_[0], order());
- return std::pair<Bezier, Bezier >(a, b);
- }
+ void subdivide(Coord t, Bezier *left, Bezier *right) const;
+ std::pair<Bezier, Bezier> subdivide(Coord t) const;
- std::vector<double> roots() const {
- std::vector<double> solutions;
- find_bezier_roots(solutions, 0, 1);
- return solutions;
- }
- std::vector<double> roots(Interval const ivl) const {
- std::vector<double> solutions;
- find_bernstein_roots(&const_cast<std::valarray<Coord>&>(c_)[0], order(), solutions, 0, ivl.min(), ivl.max());
- return solutions;
- }
-
- Bezier forward_difference(unsigned k) {
- Bezier fd(Order(order()-k));
- unsigned n = fd.size();
-
- for(unsigned i = 0; i < n; i++) {
- fd[i] = 0;
- for(unsigned j = i; j < n; j++) {
- fd[i] += (((j)&1)?-c_[j]:c_[j])*choose<double>(n, j-i);
- }
- }
- return fd;
- }
-
- Bezier elevate_degree() const {
- Bezier ed(Order(order()+1));
- unsigned n = size();
- ed[0] = c_[0];
- ed[n] = c_[n-1];
- for(unsigned i = 1; i < n; i++) {
- ed[i] = (i*c_[i-1] + (n - i)*c_[i])/(n);
- }
- return ed;
- }
+ std::vector<Coord> roots() const;
+ std::vector<Coord> roots(Interval const &ivl) const;
- Bezier reduce_degree() const {
- if(order() == 0) return *this;
- Bezier ed(Order(order()-1));
- unsigned n = size();
- ed[0] = c_[0];
- ed[n-1] = c_[n]; // ensure exact endpoints
- unsigned middle = n/2;
- for(unsigned i = 1; i < middle; i++) {
- ed[i] = (n*c_[i] - i*ed[i-1])/(n-i);
- }
- for(unsigned i = n-1; i >= middle; i--) {
- ed[i] = (n*c_[i] - i*ed[n-i])/(i);
- }
- return ed;
- }
+ Bezier forward_difference(unsigned k) const;
+ Bezier elevate_degree() const;
+ Bezier reduce_degree() const;
+ Bezier elevate_to_degree(unsigned newDegree) const;
+ Bezier deflate() const;
- Bezier elevate_to_degree(unsigned newDegree) const {
- Bezier ed = *this;
- for(unsigned i = degree(); i < newDegree; i++) {
- ed = ed.elevate_degree();
- }
- return ed;
+ // basic arithmetic operators
+ Bezier &operator+=(double v) {
+ c_ += v;
+ return *this;
}
-
- Bezier deflate() {
- if(order() == 0) return *this;
- unsigned n = order();
- Bezier b(Order(n-1));
- for(unsigned i = 0; i < n; i++) {
- b[i] = (n*c_[i+1])/(i+1);
- }
- return b;
+ Bezier &operator-=(double v) {
+ c_ -= v;
+ return *this;
}
-};
-
-
-void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
-
-inline
-Bezier multiply(Bezier const& f, Bezier const& g) {
- unsigned m = f.order();
- unsigned n = g.order();
- Bezier h(Bezier::Order(m+n));
- // h_k = sum_(i+j=k) (m i)f_i (n j)g_j / (m+n k)
-
- for(unsigned i = 0; i <= m; i++) {
- const double fi = choose<double>(m,i)*f[i];
- for(unsigned j = 0; j <= n; j++) {
- h[i+j] += fi * choose<double>(n,j)*g[j];
- }
+ Bezier &operator*=(double v) {
+ c_ *= v;
+ return *this;
}
- for(unsigned k = 0; k <= m+n; k++) {
- h[k] /= choose<double>(m+n, k);
+ Bezier &operator/=(double v) {
+ c_ /= v;
+ return *this;
}
- return h;
-}
-
-inline
-SBasis Bezier::toSBasis() const {
- SBasis sb;
- bezier_to_sbasis(sb, (*this));
- return sb;
- //return bezier_to_sbasis(&c_[0], order());
-}
-
-//TODO: implement others
-inline Bezier operator+(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] + v;
- return result;
-}
-
-inline Bezier operator-(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] - v;
- return result;
-}
+ Bezier &operator+=(Bezier const &other);
+ Bezier &operator-=(Bezier const &other);
+};
-inline Bezier& operator+=(Bezier & a, double v) {
- for(unsigned i = 0; i <= a.order(); ++i)
- a[i] = a[i] + v;
- return a;
-}
-inline Bezier& operator-=(Bezier & a, double v) {
- for(unsigned i = 0; i <= a.order(); ++i)
- a[i] = a[i] - v;
- return a;
-}
+void bezier_to_sbasis (SBasis &sb, Bezier const &bz);
-inline Bezier operator*(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] * v;
- return result;
-}
-
-inline Bezier operator/(const Bezier & a, double v) {
- Bezier result = Bezier(Bezier::Order(a));
- for(unsigned i = 0; i <= a.order(); i++)
- result[i] = a[i] / v;
+Bezier operator*(Bezier const &f, Bezier const &g);
+inline Bezier multiply(Bezier const &f, Bezier const &g) {
+ Bezier result = f * g;
return result;
}
@@ -424,20 +321,7 @@ inline Bezier reverse(const Bezier & a) {
return result;
}
-inline Bezier portion(const Bezier & a, double from, double to) {
- //TODO: implement better?
- std::valarray<Coord> res(a.order() + 1);
- if(from == 0) {
- if(to == 1) { return Bezier(a); }
- subdivideArr(to, &const_cast<Bezier&>(a).c_[0], &res[0], NULL, a.order());
- return Bezier(&res[0], a.order());
- }
- subdivideArr(from, &const_cast<Bezier&>(a).c_[0], NULL, &res[0], a.order());
- if(to == 1) return Bezier(&res[0], a.order());
- std::valarray<Coord> res2(a.order()+1);
- subdivideArr((to - from)/(1 - from), &res[0], &res2[0], NULL, a.order());
- return Bezier(&res2[0], a.order());
-}
+Bezier portion(const Bezier & a, double from, double to);
// XXX Todo: how to handle differing orders
inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
@@ -450,52 +334,19 @@ inline std::vector<Point> bezier_points(const D2<Bezier > & a) {
return result;
}
-inline Bezier derivative(const Bezier & a) {
- //if(a.order() == 1) return Bezier(0.0);
- if(a.order() == 1) return Bezier(a.c_[1]-a.c_[0]);
- Bezier der(Bezier::Order(a.order()-1));
-
- for(unsigned i = 0; i < a.order(); i++) {
- der.c_[i] = a.order()*(a.c_[i+1] - a.c_[i]);
- }
- return der;
-}
-
-inline Bezier integral(const Bezier & a) {
- Bezier inte(Bezier::Order(a.order()+1));
-
- inte[0] = 0;
- for(unsigned i = 0; i < inte.order(); i++) {
- inte[i+1] = inte[i] + a[i]/(inte.order());
- }
- return inte;
-}
-
-inline OptInterval bounds_fast(Bezier const & b) {
- OptInterval ret = Interval::from_array(&const_cast<Bezier&>(b).c_[0], b.size());
- return ret;
-}
-
-//TODO: better bounds exact
-inline OptInterval bounds_exact(Bezier const & b) {
- return bounds_exact(b.toSBasis());
-}
-
-inline OptInterval bounds_local(Bezier const & b, OptInterval i) {
- //return bounds_local(b.toSBasis(), i);
- if (i) {
- return bounds_fast(portion(b, i->min(), i->max()));
- } else {
- return OptInterval();
- }
-}
+Bezier derivative(Bezier const &a);
+Bezier integral(Bezier const &a);
+OptInterval bounds_fast(Bezier const &b);
+OptInterval bounds_exact(Bezier const &b);
+OptInterval bounds_local(Bezier const &b, OptInterval const &i);
-inline std::ostream &operator<< (std::ostream &out_file, const Bezier & b) {
- out_file << "Bezier(";
- for(unsigned i = 0; i < b.size(); i++) {
- out_file << b[i] << ", ";
+inline std::ostream &operator<< (std::ostream &os, const Bezier & b) {
+ os << "Bezier(";
+ for(unsigned i = 0; i < b.order(); i++) {
+ os << format_coord_nice(b[i]) << ", ";
}
- return out_file << ")";
+ os << format_coord_nice(b[b.order()]) << ")";
+ return os;
}
}
diff --git a/src/2geom/cairo-path-sink.cpp b/src/2geom/cairo-path-sink.cpp
new file mode 100644
index 000000000..244a08ba4
--- /dev/null
+++ b/src/2geom/cairo-path-sink.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file
+ * @brief Path sink for Cairo contexts
+ *//*
+ * Copyright 2014 Krzysztof KosiƄski
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, output to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#include <cairo.h>
+#include <2geom/cairo-path-sink.h>
+#include <2geom/elliptical-arc.h>
+
+namespace Geom {
+
+CairoPathSink::CairoPathSink(cairo_t *cr)
+ : _cr(cr)
+{}
+
+void CairoPathSink::moveTo(Point const &p)
+{
+ cairo_move_to(_cr, p[X], p[Y]);
+ _current_point = p;
+}
+
+void CairoPathSink::lineTo(Point const &p)
+{
+ cairo_line_to(_cr, p[X], p[Y]);
+ _current_point = p;
+}
+
+void CairoPathSink::curveTo(Point const &p1, Point const &p2, Point const &p3)
+{
+ cairo_curve_to(_cr, p1[X], p1[Y], p2[X], p2[Y], p3[X], p3[Y]);
+ _current_point = p3;
+}
+
+void CairoPathSink::quadTo(Point const &p1, Point const &p2)
+{
+ // degree-elevate to cubic Bezier, since Cairo doesn't do quad Beziers
+ // google "Bezier degree elevation" for more info
+ Point q1 = (1./3.) * _current_point + (2./3.) * p1;
+ Point q2 = (2./3.) * p1 + (1./3.) * p2;
+ // q3 = p2
+ cairo_curve_to(_cr, q1[X], q1[Y], q2[X], q2[Y], p2[X], p2[Y]);
+ _current_point = p2;
+}
+
+void CairoPathSink::arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ EllipticalArc arc(_current_point, rx, ry, angle, large_arc, sweep, p);
+ // Cairo only does circular arcs.
+ // To do elliptical arcs, we must use a temporary transform.
+ Affine uct = arc.unitCircleTransform();
+
+ // TODO move Cairo-2Geom matrix conversion into a common location
+ cairo_matrix_t cm;
+ cm.xx = uct[0];
+ cm.xy = uct[2];
+ cm.x0 = uct[4];
+ cm.yx = uct[1];
+ cm.yy = uct[3];
+ cm.y0 = uct[5];
+
+ cairo_save(_cr);
+ cairo_transform(_cr, &cm);
+ if (sweep) {
+ cairo_arc(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle());
+ } else {
+ cairo_arc_negative(_cr, 0, 0, 1, arc.initialAngle(), arc.finalAngle());
+ }
+ _current_point = p;
+ cairo_restore(_cr);
+
+ /* Note that an extra linear segment will be inserted before the arc
+ * if Cairo considers the current point distinct from the initial point
+ * of the arc; we could partially alleviate this by not emitting
+ * linear segments that are followed by arc segments, but this would require
+ * buffering the input curves. */
+}
+
+void CairoPathSink::closePath()
+{
+ cairo_close_path(_cr);
+}
+
+void CairoPathSink::flush() {}
+
+} // namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/conjugate_gradient.h b/src/2geom/cairo-path-sink.h
index 4f500c0e6..9fec7e0ab 100644
--- a/src/2geom/conjugate_gradient.h
+++ b/src/2geom/cairo-path-sink.h
@@ -1,8 +1,8 @@
/**
* @file
- * @brief Routines for solving a system of linear equations using the conjugate gradient method
+ * @brief Path sink for Cairo contexts
*//*
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2014 Krzysztof KosiƄski
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -29,29 +29,52 @@
*
*/
-#ifndef _2GEOM_CONJUGATE_GRADIENT_H
-#define _2GEOM_CONJUGATE_GRADIENT_H
+#ifndef LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
+#define LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
-#include <valarray>
+#include <2geom/path-sink.h>
+#include <cairo.h>
+
+namespace Geom {
-namespace Geom
-{
-double
-inner(std::valarray<double> const &x,
- std::valarray<double> const &y);
+/** @brief Output paths to a Cairo drawing context
+ *
+ * This class converts from 2Geom path representation to the Cairo representation.
+ * Use it to simplify visualizing the results of 2Geom operations with the Cairo library,
+ * for example:
+ * @code
+ * CairoPathSink sink(cr);
+ * sink.feed(pv);
+ * cairo_stroke(cr);
+ * @endcode
+ *
+ * Currently the flush method is a no-op, but this is not guaranteed
+ * to hold forever.
+ */
+class CairoPathSink
+ : public PathSink
+{
+public:
+ CairoPathSink(cairo_t *cr);
-void
-conjugate_gradient(std::valarray<double> const &A,
- std::valarray<double> &x,
- std::valarray<double> const &b,
- unsigned n, double tol,
- unsigned max_iterations, bool ortho1);
+ void moveTo(Point const &p);
+ void lineTo(Point const &p);
+ void curveTo(Point const &c0, Point const &c1, Point const &p);
+ void quadTo(Point const &c, Point const &p);
+ void arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p);
+ void closePath();
+ void flush();
-} // namespace Geom
+private:
+ cairo_t *_cr;
+ Point _current_point;
+};
-#endif // _2GEOM_CONJUGATE_GRADIENT_H
+}
+#endif // !LIB2GEOM_SEEN_CAIRO_PATH_SINK_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/circle-circle.cpp b/src/2geom/circle-circle.cpp
deleted file mode 100644
index 134fa33a2..000000000
--- a/src/2geom/circle-circle.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/* circle_circle_intersection() *
- * Determine the points where 2 circles in a common plane intersect.
- *
- * int circle_circle_intersection(
- * // center and radius of 1st circle
- * double x0, double y0, double r0,
- * // center and radius of 2nd circle
- * double x1, double y1, double r1,
- * // 1st intersection point
- * double *xi, double *yi,
- * // 2nd intersection point
- * double *xi_prime, double *yi_prime)
- *
- * This is a public domain work. 3/26/2005 Tim Voght
- * Ported to lib2geom, 2006 Nathan Hurst
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-#include <stdio.h>
-#include <math.h>
-#include <2geom/point.h>
-
-namespace Geom{
-
-int circle_circle_intersection(Point X0, double r0,
- Point X1, double r1,
- Point & p0, Point & p1)
-{
- /* dx and dy are the vertical and horizontal distances between
- * the circle centers.
- */
- Point D = X1 - X0;
-
- /* Determine the straight-line distance between the centers. */
- double d = L2(D);
-
- /* Check for solvability. */
- if (d > (r0 + r1))
- {
- /* no solution. circles do not intersect. */
- return 0;
- }
- if (d <= fabs(r0 - r1))
- {
- /* no solution. one circle is contained in the other */
- return 1;
- }
-
- /* 'point 2' is the point where the line through the circle
- * intersection points crosses the line between the circle
- * centers.
- */
-
- /* Determine the distance from point 0 to point 2. */
- double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
-
- /* Determine the coordinates of point 2. */
- Point p2 = X0 + D * (a/d);
-
- /* Determine the distance from point 2 to either of the
- * intersection points.
- */
- double h = sqrt((r0*r0) - (a*a));
-
- /* Now determine the offsets of the intersection points from
- * point 2.
- */
- Point r = (h/d)*rot90(D);
-
- /* Determine the absolute intersection points. */
- p0 = p2 + r;
- p1 = p2 - r;
-
- return 2;
-}
-
-};
-
-
-#ifdef TEST
-
-void run_test(double x0, double y0, double r0,
- double x1, double y1, double r1)
-{
- printf("x0=%F, y0=%F, r0=%F, x1=%F, y1=%F, r1=%F :\n",
- x0, y0, r0, x1, y1, r1);
- Geom::Point p0, p1;
- Geom::circle_circle_intersection(Geom::Point(x0, y0), r0,
- Geom::Point(x1, y1), r1,
- p0, p1);
- printf(" x3=%F, y3=%F, x3_prime=%F, y3_prime=%F\n",
- p0[0], p0[1], p1[0], p1[1]);
-}
-
-int main(void)
-{
- /* Add more! */
- run_test(-1.0, -1.0, 1.5, 1.0, 1.0, 2.0);
- run_test(1.0, -1.0, 1.5, -1.0, 1.0, 2.0);
- run_test(-1.0, 1.0, 1.5, 1.0, -1.0, 2.0);
- run_test(1.0, 1.0, 1.5, -1.0, -1.0, 2.0);
- exit(0);
-}
-#endif
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/circle.cpp b/src/2geom/circle.cpp
index d021882ea..553981a72 100644
--- a/src/2geom/circle.cpp
+++ b/src/2geom/circle.cpp
@@ -1,10 +1,11 @@
-/*
- * Circle Curve
- *
+/** @file
+ * @brief Circle shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -30,77 +31,241 @@
* the specific language governing rights and limitations.
*/
-
#include <2geom/circle.h>
#include <2geom/ellipse.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
+namespace Geom {
-
-namespace Geom
+Rect Circle::boundsFast() const
{
+ Point rr(_radius, _radius);
+ Rect bbox(_center - rr, _center + rr);
+ return bbox;
+}
-void Circle::set(double A, double B, double C, double D)
+void Circle::setCoefficients(Coord A, Coord B, Coord C, Coord D)
{
- if (A == 0)
- {
+ if (A == 0) {
THROW_RANGEERROR("square term coefficient == 0");
}
//std::cerr << "B = " << B << " C = " << C << " D = " << D << std::endl;
- double b = B / A;
- double c = C / A;
- double d = D / A;
+ Coord b = B / A;
+ Coord c = C / A;
+ Coord d = D / A;
- m_centre[X] = -b/2;
- m_centre[Y] = -c/2;
- double r2 = m_centre[X] * m_centre[X] + m_centre[Y] * m_centre[Y] - d;
+ _center[X] = -b/2;
+ _center[Y] = -c/2;
+ Coord r2 = _center[X] * _center[X] + _center[Y] * _center[Y] - d;
- if (r2 < 0)
- {
+ if (r2 < 0) {
THROW_RANGEERROR("ray^2 < 0");
}
- m_ray = std::sqrt(r2);
+ _radius = std::sqrt(r2);
}
+void Circle::coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const
+{
+ A = 1;
+ B = -2 * _center[X];
+ C = -2 * _center[Y];
+ D = _center[X] * _center[X] + _center[Y] * _center[Y] - _radius * _radius;
+}
-void Circle::set(std::vector<Point> const& points)
+std::vector<Coord> Circle::coefficients() const
{
- size_t sz = points.size();
- if (sz < 3)
- {
- THROW_RANGEERROR("fitting error: too few points passed");
+ std::vector<Coord> c(4);
+ coefficients(c[0], c[1], c[2], c[3]);
+ return c;
+}
+
+
+Zoom Circle::unitCircleTransform() const
+{
+ Zoom ret(_radius, _center / _radius);
+ return ret;
+}
+
+Zoom Circle::inverseUnitCircleTransform() const
+{
+ if (_radius == 0) {
+ THROW_RANGEERROR("degenerate circle does not have an inverse unit circle transform");
}
- NL::LFMCircle model;
- NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz);
- for (size_t i = 0; i < sz; ++i)
- {
- fitter.append(points[i]);
+ Zoom ret(1/_radius, Translate(-_center));
+ return ret;
+}
+
+Point Circle::initialPoint() const
+{
+ Point p(_center);
+ p[X] += _radius;
+ return p;
+}
+
+Point Circle::pointAt(Coord t) const {
+ return _center + Point::polar(t) * _radius;
+}
+
+Coord Circle::valueAt(Coord t, Dim2 d) const {
+ Coord delta = (d == X ? std::cos(t) : std::sin(t));
+ return _center[d] + delta * _radius;
+}
+
+Coord Circle::timeAt(Point const &p) const {
+ if (_center == p) return 0;
+ return atan2(p - _center);
+}
+
+Coord Circle::nearestTime(Point const &p) const {
+ return timeAt(p);
+}
+
+bool Circle::contains(Rect const &r) const
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (!contains(r.corner(i))) return false;
}
- fitter.update();
+ return true;
+}
- NL::Vector z(sz, 0.0);
- model.instance(*this, fitter.result(z));
+bool Circle::contains(Circle const &other) const
+{
+ Coord cdist = distance(_center, other._center);
+ Coord rdist = fabs(_radius - other._radius);
+ return cdist <= rdist;
+}
+
+bool Circle::intersects(Line const &l) const
+{
+ // http://mathworld.wolfram.com/Circle-LineIntersection.html
+ Coord dr = l.versor().length();
+ Coord r = _radius;
+ Coord D = cross(l.initialPoint(), l.finalPoint());
+ Coord delta = r*r * dr*dr - D*D;
+ if (delta >= 0) return true;
+ return false;
+}
+
+bool Circle::intersects(Circle const &other) const
+{
+ Coord cdist = distance(_center, other._center);
+ Coord rsum = _radius + other._radius;
+ return cdist <= rsum;
+}
+
+
+std::vector<ShapeIntersection> Circle::intersect(Line const &l) const
+{
+ // http://mathworld.wolfram.com/Circle-LineIntersection.html
+ Coord dr = l.versor().length();
+ Coord dx = l.versor().x();
+ Coord dy = l.versor().y();
+ Coord D = cross(l.initialPoint() - _center, l.finalPoint() - _center);
+ Coord delta = _radius*_radius * dr*dr - D*D;
+
+ std::vector<ShapeIntersection> result;
+ if (delta < 0) return result;
+ if (delta == 0) {
+ Coord ix = (D*dy) / (dr*dr);
+ Coord iy = (-D*dx) / (dr*dr);
+ Point ip(ix, iy); ip += _center;
+ result.push_back(ShapeIntersection(timeAt(ip), l.timeAt(ip), ip));
+ return result;
+ }
+
+ Coord sqrt_delta = std::sqrt(delta);
+ Coord signmod = dy < 0 ? -1 : 1;
+
+ Coord i1x = (D*dy + signmod * dx * sqrt_delta) / (dr*dr);
+ Coord i1y = (-D*dx + fabs(dy) * sqrt_delta) / (dr*dr);
+ Point i1p(i1x, i1y); i1p += _center;
+
+ Coord i2x = (D*dy - signmod * dx * sqrt_delta) / (dr*dr);
+ Coord i2y = (-D*dx - fabs(dy) * sqrt_delta) / (dr*dr);
+ Point i2p(i2x, i2y); i2p += _center;
+
+ result.push_back(ShapeIntersection(timeAt(i1p), l.timeAt(i1p), i1p));
+ result.push_back(ShapeIntersection(timeAt(i2p), l.timeAt(i2p), i2p));
+ return result;
+}
+
+std::vector<ShapeIntersection> Circle::intersect(LineSegment const &l) const
+{
+ std::vector<ShapeIntersection> result = intersect(Line(l));
+ filter_line_segment_intersections(result);
+ return result;
+}
+
+std::vector<ShapeIntersection> Circle::intersect(Circle const &other) const
+{
+ std::vector<ShapeIntersection> result;
+
+ if (*this == other) {
+ THROW_INFINITESOLUTIONS();
+ }
+ if (contains(other)) return result;
+ if (!intersects(other)) return result;
+
+ // See e.g. http://mathworld.wolfram.com/Circle-CircleIntersection.html
+ // Basically, we figure out where is the third point of a triangle
+ // with two points in the centers and with edge lengths equal to radii
+ Point cv = other._center - _center;
+ Coord d = cv.length();
+ Coord R = radius(), r = other.radius();
+
+ if (d == R + r) {
+ Point px = lerp(R / d, _center, other._center);
+ Coord T = timeAt(px), t = other.timeAt(px);
+ result.push_back(ShapeIntersection(T, t, px));
+ return result;
+ }
+
+ // q is the distance along the line between centers to the perpendicular line
+ // that goes through both intersections.
+ Coord q = (d*d - r*r + R*R) / (2*d);
+ Point qp = lerp(q/d, _center, other._center);
+
+ // The triangle given by the points:
+ // _center, qp, intersection
+ // is a right triangle. Determine the distance between qp and intersection
+ // using the Pythagorean theorem.
+ Coord h = std::sqrt(R*R - q*q);
+ Point qd = (h/d) * cv.cw();
+
+ // now compute the intersection points
+ Point x1 = qp + qd;
+ Point x2 = qp - qd;
+
+ result.push_back(ShapeIntersection(timeAt(x1), other.timeAt(x1), x1));
+ result.push_back(ShapeIntersection(timeAt(x2), other.timeAt(x2), x2));
+ return result;
}
/**
@param inner a point whose angle with the circle center is inside the angle that the arc spans
*/
EllipticalArc *
-Circle::arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant)
+Circle::arc(Point const& initial, Point const& inner, Point const& final) const
{
// TODO native implementation!
- Ellipse e(center(X), center(Y), ray(), ray(), 0);
- return e.arc(initial, inner, final, _svg_compliant);
+ Ellipse e(_center[X], _center[Y], _radius, _radius, 0);
+ return e.arc(initial, inner, final);
}
-D2<SBasis> Circle::toSBasis()
+bool Circle::operator==(Circle const &other) const
+{
+ if (_center != other._center) return false;
+ if (_radius != other._radius) return false;
+ return true;
+}
+
+D2<SBasis> Circle::toSBasis() const
{
D2<SBasis> B;
Linear bo = Linear(0, 2 * M_PI);
@@ -108,27 +273,57 @@ D2<SBasis> Circle::toSBasis()
B[0] = cos(bo,4);
B[1] = sin(bo,4);
- B = B * m_ray + m_centre;
+ B = B * _radius + _center;
return B;
}
-void
-Circle::getPath(std::vector<Path> &path_out) {
- Path pb;
- D2<SBasis> B = toSBasis();
+void Circle::fit(std::vector<Point> const& points)
+{
+ size_t sz = points.size();
+ if (sz < 2) {
+ THROW_RANGEERROR("fitting error: too few points passed");
+ }
+ if (sz == 2) {
+ _center = points[0] * 0.5 + points[1] * 0.5;
+ _radius = distance(points[0], points[1]) / 2;
+ return;
+ }
- pb.append(SBasisCurve(B));
+ NL::LFMCircle model;
+ NL::least_squeares_fitter<NL::LFMCircle> fitter(model, sz);
- path_out.push_back(pb);
-}
+ for (size_t i = 0; i < sz; ++i) {
+ fitter.append(points[i]);
+ }
+ fitter.update();
+ NL::Vector z(sz, 0.0);
+ model.instance(*this, fitter.result(z));
+}
-} // end namespace Geom
+bool are_near(Circle const &a, Circle const &b, Coord eps)
+{
+ // to check whether no point on a is further than eps from b,
+ // we check two things:
+ // 1. if radii differ by more than eps, there is definitely a point that fails
+ // 2. if they differ by less, we check the centers. They have to be closer
+ // together if the radius differs, since the maximum distance will be
+ // equal to sum of radius difference and distance between centers.
+ if (!are_near(a.radius(), b.radius(), eps)) return false;
+ Coord adjusted_eps = eps - fabs(a.radius() - b.radius());
+ return are_near(a.center(), b.center(), adjusted_eps);
+}
+std::ostream &operator<<(std::ostream &out, Circle const &c)
+{
+ out << "Circle(" << c.center() << ", " << format_coord_nice(c.radius()) << ")";
+ return out;
+}
+} // end namespace Geom
/*
Local Variables:
diff --git a/src/2geom/circle.h b/src/2geom/circle.h
index ca9241047..a4d5f2097 100644
--- a/src/2geom/circle.h
+++ b/src/2geom/circle.h
@@ -1,11 +1,11 @@
-/**
- * \file
- * \brief Circles
+/** @file
+ * @brief Circle shape
*//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -34,83 +34,119 @@
#ifndef LIB2GEOM_SEEN_CIRCLE_H
#define LIB2GEOM_SEEN_CIRCLE_H
-#include <vector>
+#include <2geom/forward.h>
+#include <2geom/intersection.h>
#include <2geom/point.h>
-#include <2geom/exception.h>
-#include <2geom/path.h>
+#include <2geom/rect.h>
+#include <2geom/transforms.h>
namespace Geom {
class EllipticalArc;
+/** @brief Set of all points at a fixed distance from the center
+ * @ingroup Shapes */
class Circle
+ : boost::equality_comparable1< Circle
+ , MultipliableNoncommutative< Circle, Translate
+ , MultipliableNoncommutative< Circle, Rotate
+ , MultipliableNoncommutative< Circle, Zoom
+ > > > >
{
- public:
- Circle()
+ Point _center;
+ Coord _radius;
+
+public:
+ Circle() {}
+ Circle(Coord cx, Coord cy, Coord r)
+ : _center(cx, cy), _radius(r)
+ {}
+ Circle(Point const &center, Coord r)
+ : _center(center), _radius(r)
{}
- Circle(double cx, double cy, double r)
- : m_centre(cx, cy), m_ray(r)
- {
+ Circle(Coord A, Coord B, Coord C, Coord D) {
+ setCoefficients(A, B, C, D);
}
- Circle(Point center, double r)
- : m_centre(center), m_ray(r)
- {
- }
+ // Construct the unique circle passing through three points.
+ //Circle(Point const &a, Point const &b, Point const &c);
- Circle(double A, double B, double C, double D)
- {
- set(A, B, C, D);
- }
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ Coord radius() const { return _radius; }
+ Coord area() const { return M_PI * _radius * _radius; }
+ bool isDegenerate() const { return _radius == 0; }
- Circle(std::vector<Point> const& points)
- {
- set(points);
- }
+ void setCenter(Point const &p) { _center = p; }
+ void setRadius(Coord c) { _radius = c; }
- void set(double cx, double cy, double r)
- {
- m_centre[X] = cx;
- m_centre[Y] = cy;
- m_ray = r;
- }
+ Rect boundsFast() const;
+ Rect boundsExact() const { return boundsFast(); }
+
+ Point initialPoint() const;
+ Point finalPoint() const { return initialPoint(); }
+ Point pointAt(Coord t) const;
+ Coord valueAt(Coord t, Dim2 d) const;
+ Coord timeAt(Point const &p) const;
+ Coord nearestTime(Point const &p) const;
+
+ bool contains(Point const &p) const { return distance(p, _center) <= _radius; }
+ bool contains(Rect const &other) const;
+ bool contains(Circle const &other) const;
+ bool intersects(Line const &l) const;
+ bool intersects(LineSegment const &l) const;
+ bool intersects(Circle const &other) const;
+
+ std::vector<ShapeIntersection> intersect(Line const &other) const;
+ std::vector<ShapeIntersection> intersect(LineSegment const &other) const;
+ std::vector<ShapeIntersection> intersect(Circle const &other) const;
// build a circle by its implicit equation:
// Ax^2 + Ay^2 + Bx + Cy + D = 0
- void set(double A, double B, double C, double D);
+ void setCoefficients(Coord A, Coord B, Coord C, Coord D);
+ void coefficients(Coord &A, Coord &B, Coord &C, Coord &D) const;
+ std::vector<Coord> coefficients() const;
- // build up the best fitting circle wrt the passed points
- // prerequisite: at least 3 points must be passed
- void set(std::vector<Point> const& points);
+ Zoom unitCircleTransform() const;
+ Zoom inverseUnitCircleTransform() const;
EllipticalArc *
- arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant = true);
+ arc(Point const& initial, Point const& inner, Point const& final) const;
- D2<SBasis> toSBasis();
- void getPath(std::vector<Path> &path_out);
+ D2<SBasis> toSBasis() const;
- Point center() const
- {
- return m_centre;
+ Circle &operator*=(Translate const &t) {
+ _center *= t;
+ return *this;
}
-
- Coord center(Dim2 d) const
- {
- return m_centre[d];
+ Circle &operator*=(Rotate const &) {
+ return *this;
}
-
- Coord ray() const
- {
- return m_ray;
+ Circle &operator*=(Zoom const &z) {
+ _center *= z;
+ _radius *= z.scale();
+ return *this;
}
+ bool operator==(Circle const &other) const;
+
+ /** @brief Fit the circle to the passed points using the least squares method.
+ * @param points Samples at the perimeter of the circle */
+ void fit(std::vector<Point> const &points);
+};
+
+bool are_near(Circle const &a, Circle const &b, Coord eps=EPSILON);
+
+std::ostream &operator<<(std::ostream &out, Circle const &c);
- private:
- Point m_centre;
- Coord m_ray;
+template <>
+struct ShapeTraits<Circle> {
+ typedef Coord TimeType;
+ typedef Interval IntervalType;
+ typedef Ellipse AffineClosureType;
+ typedef Intersection<> IntersectionType;
};
} // end namespace Geom
diff --git a/src/2geom/circulator.h b/src/2geom/circulator.h
index 9671ce4a9..06e4d2c4e 100644
--- a/src/2geom/circulator.h
+++ b/src/2geom/circulator.h
@@ -1,5 +1,4 @@
-/**
- * @file circulator.h
+/** @file
* @brief Circular iterator adapter
*//*
* Copyright 2006 MenTaLguY <mental@rydia.net>
@@ -29,8 +28,8 @@
*
*/
-#ifndef SEEN_Circulator_H
-#define SEEN_Circulator_H
+#ifndef LIB2GEOM_SEEN_CIRCULATOR_H
+#define LIB2GEOM_SEEN_CIRCULATOR_H
#include <iterator>
@@ -137,7 +136,7 @@ Geom::Circulator<T> operator+(int n, Geom::Circulator<T> const &c) {
return c + n;
}
-#endif // SEEN_Circulator_H
+#endif // LIB2GEOM_SEEN_CIRCULATOR_H
/*
Local Variables:
diff --git a/src/2geom/concepts.h b/src/2geom/concepts.h
index c89c3a224..c331f078b 100644
--- a/src/2geom/concepts.h
+++ b/src/2geom/concepts.h
@@ -35,9 +35,11 @@
#include <2geom/interval.h>
#include <2geom/point.h>
#include <2geom/rect.h>
+#include <2geom/intersection.h>
#include <vector>
-#include <boost/concept_check.hpp>
+#include <boost/concept/assert.hpp>
#include <2geom/forward.h>
+#include <2geom/transforms.h>
namespace Geom {
@@ -71,16 +73,18 @@ struct FragmentConcept {
SbType sb;
void constraints() {
t = T(o);
- b = t.isZero();
- b = t.isConstant();
+ b = t.isZero(d);
+ b = t.isConstant(d);
b = t.isFinite();
o = t.at0();
o = t.at1();
+ t.at0() = o;
+ t.at1() = o;
o = t.valueAt(d);
o = t(d);
v = t.valueAndDerivatives(d, u-1);
- //Is a pure derivative (ignoring others) accessor ever much faster?
- //u = number of values returned. first val is value.
+ //Is a pure derivative (ignoring others) accessor ever much faster?
+ //u = number of values returned. first val is value.
sb = t.toSBasis();
t = reverse(t);
i = bounds_fast(t);
@@ -97,9 +101,51 @@ struct FragmentConcept {
};
template <typename T>
+struct ShapeConcept {
+ typedef typename ShapeTraits<T>::TimeType Time;
+ typedef typename ShapeTraits<T>::IntervalType Interval;
+ typedef typename ShapeTraits<T>::AffineClosureType AffineClosure;
+ typedef typename ShapeTraits<T>::IntersectionType Isect;
+
+ T shape, other;
+ Time t;
+ Point p;
+ AffineClosure ac;
+ Affine m;
+ Translate tr;
+ Coord c;
+ bool bool_;
+ std::vector<Isect> ivec;
+
+ void constraints() {
+ p = shape.pointAt(t);
+ c = shape.valueAt(t, X);
+ ivec = shape.intersect(other);
+ t = shape.nearestTime(p);
+ shape *= tr;
+ ac = shape;
+ ac *= m;
+ bool_ = (shape == shape);
+ bool_ = (shape != other);
+ bool_ = shape.isDegenerate();
+ //bool_ = are_near(shape, other, c);
+ }
+};
+
+template <typename T>
inline T portion(const T& t, const Interval& i) { return portion(t, i.min(), i.max()); }
template <typename T>
+struct EqualityComparableConcept {
+ T a, b;
+ bool bool_;
+ void constaints() {
+ bool_ = (a == b);
+ bool_ = (a != b);
+ }
+};
+
+template <typename T>
struct NearConcept {
T a, b;
double tol;
diff --git a/src/2geom/conic_section_clipper.h b/src/2geom/conic_section_clipper.h
index a02cda4d3..38bba338e 100644
--- a/src/2geom/conic_section_clipper.h
+++ b/src/2geom/conic_section_clipper.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -34,8 +33,8 @@
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_H
#undef CLIP_WITH_CAIRO_SUPPORT
diff --git a/src/2geom/conic_section_clipper_cr.h b/src/2geom/conic_section_clipper_cr.h
index 31f5a4269..6c62494de 100644
--- a/src/2geom/conic_section_clipper_cr.h
+++ b/src/2geom/conic_section_clipper_cr.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -39,8 +38,8 @@
////////////////////////////////////////////////////////////////////////////////
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_CR_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_CR_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_CR_H
#define CLIP_WITH_CAIRO_SUPPORT
diff --git a/src/2geom/conic_section_clipper_impl.cpp b/src/2geom/conic_section_clipper_impl.cpp
index 2867e243c..c57307974 100644
--- a/src/2geom/conic_section_clipper_impl.cpp
+++ b/src/2geom/conic_section_clipper_impl.cpp
@@ -173,7 +173,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const
cpts.size())
// remove duplicates
- std::sort (cpts.begin(), cpts.end(), Point::LexOrder<X>());
+ std::sort (cpts.begin(), cpts.end(), Point::LexLess<X>());
cpts.erase (std::unique (cpts.begin(), cpts.end()), cpts.end());
@@ -203,7 +203,7 @@ bool CLIPPER_CLASS::intersect (std::vector<Point> & crossing_points) const
inline
double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
{
- return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1));
+ return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2));
}
@@ -216,6 +216,8 @@ double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
*/
bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) const
{
+ using std::swap;
+
/*
* we looks for the points on the conic whose tangent is parallel to the
* arc chord P1P2, they will be extrema of the conic arc P1P2 wrt the
@@ -257,9 +259,8 @@ bool CLIPPER_CLASS::are_paired (Point& M, const Point & P1, const Point & P2) co
if (sgn(side0) == sgn(side1))
{
- if (std::fabs(side0) > std::fabs(side1))
- {
- std::swap (extrema[0], extrema[1]);
+ if (std::fabs(side0) > std::fabs(side1)) {
+ swap(extrema[0], extrema[1]);
}
extrema.pop_back();
}
@@ -371,6 +372,8 @@ void CLIPPER_CLASS::pairing (std::vector<Point> & paired_points,
*/
bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs)
{
+ using std::swap;
+
arcs.clear();
std::vector<Point> crossing_points;
std::vector<Point> paired_points;
@@ -454,14 +457,14 @@ bool CLIPPER_CLASS::clip (std::vector<RatQuad> & arcs)
double angle = cs.axis_angle();
Line axis1 (*c, angle);
rts = cs.roots (axis1);
- if (rts[0] > rts[1]) std::swap (rts[0], rts[1]);
+ if (rts[0] > rts[1]) swap (rts[0], rts[1]);
paired_points[0] = axis1.pointAt (rts[0]);
paired_points[1] = axis1.pointAt (rts[1]);
paired_points[2] = paired_points[1];
paired_points[3] = paired_points[0];
Line axis2 (*c, angle + M_PI/2);
rts = cs.roots (axis2);
- if (rts[0] > rts[1]) std::swap (rts[0], rts[1]);
+ if (rts[0] > rts[1]) swap (rts[0], rts[1]);
inner_points.push_back (axis2.pointAt (rts[0]));
inner_points.push_back (axis2.pointAt (rts[1]));
}
diff --git a/src/2geom/conic_section_clipper_impl.h b/src/2geom/conic_section_clipper_impl.h
index 37415df97..e38a6d416 100644
--- a/src/2geom/conic_section_clipper_impl.h
+++ b/src/2geom/conic_section_clipper_impl.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic section clipping with respect to a rectangle
- *
+/** @file
+ * @brief Conic section clipping with respect to a rectangle
+ *//*
* Authors:
* Marco Cecchetti <mrcekets at gmail>
*
@@ -31,11 +30,8 @@
* the specific language governing rights and limitations.
*/
-
-
-
-#ifndef _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
-#define _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
+#ifndef LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
+#define LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
#include <2geom/conicsec.h>
@@ -336,13 +332,7 @@ void CLIPPER_CLASS::rsplit (std::list<Point> & points,
} // end namespace Geom
-
-
-
-#endif // _2GEOM_CONIC_SECTION_CLIPPER_IMPL_H_
-
-
-
+#endif // LIB2GEOM_SEEN_CONIC_SECTION_CLIPPER_IMPL_H
/*
Local Variables:
diff --git a/src/2geom/conicsec.cpp b/src/2geom/conicsec.cpp
index 367dc2503..3b36137be 100644
--- a/src/2geom/conicsec.cpp
+++ b/src/2geom/conicsec.cpp
@@ -40,44 +40,16 @@
#include <sstream>
#include <stdexcept>
-
-
-
-
namespace Geom
{
LineSegment intersection(Line l, Rect r) {
- Point p0, p1;
- double a,b,c;
- std::vector<double> ifc = l.coefficients();
- a = ifc[0];
- b = ifc[1];
- c = ifc[2];
- if (fabs(b) > fabs(a)) {
- p0 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p0[1] < r[1][0])
- p0 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p0[1] > r[1][1])
- p0 = Point((-c - b*r[1][1])/a, r[1][1]);
- p1 = Point(r[0][1], (-c - a*r[0][1])/b);
- if (p1[1] < r[1][0])
- p1 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p1[1] > r[1][1])
- p1 = Point((-c - b*r[1][1])/a, r[1][1]);
+ boost::optional<LineSegment> seg = l.clip(r);
+ if (seg) {
+ return *seg;
} else {
- p0 = Point((-c - b*r[1][0])/a, r[1][0]);
- if (p0[0] < r[0][0])
- p0 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p0[0] > r[0][1])
- p0 = Point(r[0][1], (-c - a*r[0][1])/b);
- p1 = Point((-c - b*r[1][1])/a, r[1][1]);
- if (p1[0] < r[0][0])
- p1 = Point(r[0][0], (-c - a*r[0][0])/b);
- if (p1[0] > r[0][1])
- p1 = Point(r[0][1], (-c - a*r[0][1])/b);
+ return LineSegment(Point(0,0), Point(0,0));
}
- return LineSegment(p0, p1);
}
static double det(Point a, Point b) {
@@ -108,41 +80,6 @@ static double boxprod(Point a, Point b, Point c) {
return det(a,b) - det(a,c) + det(b,c);
}
-
-/**
- * Find the roots of (q2x + q1)x+q0 = 0
- * Tries to be numerically robust.
- */
-template <typename T>
-static std::vector<T> quadratic_roots(T q0, T q1, T q2) {
- std::vector<double> r;
- if(q2 == 0) {
- if(q1 == 0) { // zero or infinite roots
- return r;
- }
- r.push_back(-q0/q1);
- } else {
- double desc = q1*q1 - 4*q2*q0;
- /*cout << q2 << ", "
- << q1 << ", "
- << q0 << "; "
- << desc << "\n";*/
- if (desc < 0)
- return r;
- else if (desc == 0)
- r.push_back(-q1/(2*q2));
- else {
- desc = std::sqrt(desc);
- double t = -0.5*(q1+sgn(q1)*desc);
- r.push_back(t/q2);
- r.push_back(q0/t);
- }
- }
- return r;
-}
-
-
-
class BadConversion : public std::runtime_error {
public:
BadConversion(const std::string& s)
@@ -673,7 +610,7 @@ std::vector<double> xAx::roots(Line const &l) const {
Interval xAx::quad_ex(double a, double b, double c, Interval ivl) {
double cx = -b*0.5/a;
- Interval bnds((a*ivl[0]+b)*ivl[0]+c, (a*ivl[1]+b)*ivl[1]+c);
+ Interval bnds((a*ivl.min()+b)*ivl.min()+c, (a*ivl.max()+b)*ivl.max()+c);
if(ivl.contains(cx))
bnds.expandTo((a*cx+b)*cx+c);
return bnds;
@@ -714,14 +651,14 @@ Interval xAx::extrema(Rect r) const {
ext |= Interval(valueAt(r.corner(i)));
return ext;
}
- double k = r[0][0];
- Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]);
- k = r[0][1];
- ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[1]);
- k = r[1][0];
- ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]);
- k = r[1][1];
- ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[0]);
+ double k = r[X].min();
+ Interval ext = quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]);
+ k = r[X].max();
+ ext |= quad_ex(c[2], c[1]*k+c[4], (c[0]*k + c[3])*k + c[5], r[Y]);
+ k = r[Y].min();
+ ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]);
+ k = r[Y].max();
+ ext |= quad_ex(c[0], c[1]*k+c[3], (c[2]*k + c[4])*k + c[5], r[X]);
boost::optional<Point> B0 = bottom();
if (B0 && r.contains(*B0))
ext.expandTo(0);
@@ -753,7 +690,7 @@ bool at_infinity (Point const& p)
inline
double signed_triangle_area (Point const& p1, Point const& p2, Point const& p3)
{
- return (cross(p3, p2) - cross(p3, p1) + cross(p2, p1));
+ return (cross(p2, p3) - cross(p1, p3) + cross(p1, p2));
}
@@ -801,6 +738,8 @@ void xAx::set(std::vector<Point> const& points)
*/
void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2)
{
+ using std::swap;
+
if (_dist2 == infinity() || _dist2 == -infinity()) // parabola
{
if (_dist1 == infinity()) // degenerate to a line
@@ -842,7 +781,7 @@ void xAx::set (const Point& _vertex, double _angle, double _dist1, double _dist2
if (std::fabs(_dist1) > std::fabs(_dist2))
{
- std::swap (_dist1, _dist2);
+ swap (_dist1, _dist2);
}
if (_dist1 < 0)
{
@@ -1442,38 +1381,35 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const
if (sgn(Mside) == sgn(Qside))
{
//std::cout << "BOUND: M.size() == 1" << std::endl;
- if (M[0][dim] > B[dim][1])
- B[dim][1] = M[0][dim];
- else if (M[0][dim] < B[dim][0])
- B[dim][0] = M[0][dim];
+ B[dim].expandTo(M[0][dim]);
}
}
else if (M.size() == 2)
{
//std::cout << "BOUND: M.size() == 2" << std::endl;
if (M[0][dim] > M[1][dim])
- std::swap (M[0], M[1]);
+ swap (M[0], M[1]);
- if (M[0][dim] > B[dim][1])
+ if (M[0][dim] > B[dim].max())
{
double Mside = signed_triangle_area (P1, M[0], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][1] = M[0][dim];
+ B[dim].setMax(M[0][dim]);
}
- else if (M[1][dim] < B[dim][0])
+ else if (M[1][dim] < B[dim].min())
{
double Mside = signed_triangle_area (P1, M[1], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][0] = M[1][dim];
+ B[dim].setMin(M[1][dim]);
}
else
{
double Mside = signed_triangle_area (P1, M[0], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][0] = M[0][dim];
+ B[dim].setMin(M[0][dim]);
Mside = signed_triangle_area (P1, M[1], P2);
if (sgn(Mside) == sgn(Qside))
- B[dim][1] = M[1][dim];
+ B[dim].setMax(M[1][dim]);
}
}
}
@@ -1486,7 +1422,7 @@ Rect xAx::arc_bound (const Point & P1, const Point & Q, const Point & P2) const
*
* P: the point to compute the nearest one
*/
-std::vector<Point> xAx::allNearestPoints (const Point &P) const
+std::vector<Point> xAx::allNearestTimes (const Point &P) const
{
// TODO: manage the circle - centre case
std::vector<Point> points;
diff --git a/src/2geom/conicsec.h b/src/2geom/conicsec.h
index ec9a430d9..dbe564872 100644
--- a/src/2geom/conicsec.h
+++ b/src/2geom/conicsec.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Conic Section
- *
+/** @file
+ * @brief Conic Section
+ *//*
* Authors:
* Nathan Hurst <njh@njhurst.com>
*
@@ -32,8 +31,8 @@
*/
-#ifndef _2GEOM_CONIC_SECTION_H_
-#define _2GEOM_CONIC_SECTION_H_
+#ifndef LIB2GEOM_SEEN_CONICSEC_H
+#define LIB2GEOM_SEEN_CONICSEC_H
#include <2geom/exception.h>
#include <2geom/angle.h>
@@ -463,33 +462,28 @@ public:
bool arc_contains (const Point & _point, const Point & _initial,
const Point & _inner, const Point & _final) const
{
- double pa = angle_at (_point);
- double sa = angle_at (_initial);
- double ia = angle_at (_inner);
- double ea = angle_at (_final);
- // we test if _point and _inner have the same position
- // wrt _initial and _final
- return Geom::arc_contains (pa, sa, ia, ea);
+ AngleInterval ai(angle_at(_initial), angle_at(_inner), angle_at(_final));
+ return ai.contains(angle_at(_point));
}
Rect arc_bound (const Point & P1, const Point & Q, const Point & P2) const;
- std::vector<Point> allNearestPoints (const Point &P) const;
+ std::vector<Point> allNearestTimes (const Point &P) const;
/*
* Return the point on the conic section nearest to the passed point "P".
*
* P: the point to compute the nearest one
*/
- Point nearestPoint (const Point &P) const
+ Point nearestTime (const Point &P) const
{
- std::vector<Point> points = allNearestPoints (P);
+ std::vector<Point> points = allNearestTimes (P);
if ( !points.empty() )
{
return points.front();
}
// else
- THROW_LOGICALERROR ("nearestPoint: no nearest point found");
+ THROW_LOGICALERROR ("nearestTime: no nearest point found");
return Point();
}
@@ -509,8 +503,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const xAx &x) {
};
-#endif // _2GEOM_CONIC_SECTION_H_
-
+#endif // LIB2GEOM_SEEN_CONICSEC_H
/*
Local Variables:
diff --git a/src/2geom/conjugate_gradient.cpp b/src/2geom/conjugate_gradient.cpp
deleted file mode 100644
index 588513414..000000000
--- a/src/2geom/conjugate_gradient.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * conjugate_gradient.cpp
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#include <math.h>
-#include <stdlib.h>
-#include <valarray>
-#include <cassert>
-#include <2geom/conjugate_gradient.h>
-
-/* lifted wholely from wikipedia. */
-
-namespace Geom
-{
-
-using std::valarray;
-
-static void
-matrix_times_vector(valarray<double> const &matrix, /* m * n */
- valarray<double> const &vec, /* n */
- valarray<double> &result) /* m */
-{
- unsigned n = vec.size();
- unsigned m = result.size();
- assert(m*n == matrix.size());
- const double* mp = &const_cast<valarray<double>&>(matrix)[0];
- for (unsigned i = 0; i < m; i++) {
- double res = 0;
- for (unsigned j = 0; j < n; j++)
- res += *mp++ * vec[j];
- result[i] = res;
- }
-}
-
-/**
-// only used in commented code below
-static double Linfty(valarray<double> const &vec) {
- return std::max(vec.max(), -vec.min());
-}
-**/
-
-double
-inner(valarray<double> const &x,
- valarray<double> const &y) {
- double total = 0;
- for(unsigned i = 0; i < x.size(); i++)
- total += x[i]*y[i];
- return total;// (x*y).sum(); <- this is more concise, but ineff
-}
-
-void
-conjugate_gradient(double **A,
- double *x,
- double *b,
- unsigned n,
- double tol,
- int max_iterations,
- bool ortho1) {
- valarray<double> vA(n*n);
- valarray<double> vx(n);
- valarray<double> vb(n);
- for(unsigned i=0;i<n;i++) {
- vx[i]=x[i];
- vb[i]=b[i];
- for(unsigned j=0;j<n;j++) {
- vA[i*n+j]=A[i][j];
- }
- }
- conjugate_gradient(vA,vx,vb,n,tol,max_iterations,ortho1);
- for(unsigned i=0;i<n;i++) {
- x[i]=vx[i];
- }
-}
-void
-conjugate_gradient(valarray<double> const &A,
- valarray<double> &x,
- valarray<double> const &b,
- unsigned n, double tol,
- unsigned max_iterations, bool /*ortho1*/) {
- valarray<double> Ap(n), p(n), r(n);
- matrix_times_vector(A,x,Ap);
- r=b-Ap;
- double r_r = inner(r,r);
- unsigned k = 0;
- tol *= tol;
- while(k < max_iterations && r_r > tol) {
- k++;
- double r_r_new = r_r;
- if(k == 1)
- p = r;
- else {
- r_r_new = inner(r,r);
- p = r + (r_r_new/r_r)*p;
- }
- matrix_times_vector(A, p, Ap);
- double alpha_k = r_r_new / inner(p, Ap);
- x += alpha_k*p;
- r -= alpha_k*Ap;
- r_r = r_r_new;
- }
- //printf("njh: %d iters, Linfty = %g L2 = %g\n", k,
- //std::max(-r.min(), r.max()), sqrt(r_r));
- // x is solution
-}
-
-} // namespace Geom
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/convex-cover.h b/src/2geom/convex-cover.h
deleted file mode 100644
index d290b7e80..000000000
--- a/src/2geom/convex-cover.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/**
- * \file
- * \brief Dynamic convex hull structure
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef GEOM_CONVEX_COVER_H
-#define GEOM_CONVEX_COVER_H
-
-#include <2geom/point.h>
-#include <vector>
-
-namespace Geom{
-
-/* A convex cover is a sequence of convex polygons that completely cover the path. For now a
- * convex hull class is included here (the convex-hull header is wrong)
- */
-
-/** ConvexHull
- * A convexhull is a convex region - every point between two points in the convex hull is also in
- * the convex hull. It is defined by a set of points travelling in a clockwise direction. We require the first point to be top most, and of the topmost, leftmost.
-
- * An empty hull has no points, we allow a single point or two points degenerate cases.
-
- * We could provide the centroid as a member for efficient direction determination. We can update the
- * centroid with all operations with the same time complexity as the operation.
- */
-
-class ConvexHull{
-public: // XXX: should be private :)
- // extracts the convex hull of boundary. internal use only
- void find_pivot();
- void angle_sort();
- void graham_scan();
- void andrew_scan();
- void graham();
-public:
- std::vector<Point> boundary;
- //Point centroid;
-
- void merge(Point p);
- bool contains_point(Point p);
- bool strict_contains_point(Point p);
-
- inline size_t size() const { return boundary.size();}
- inline Point operator[](int i) const {
-
- int l = boundary.size();
- if(l == 0) return Point();
- return boundary[i >= 0 ? i % l : (i % l) + l];
- }
-
- /*inline Point &operator[](unsigned i) {
- int l = boundary.size();
- if(l == 0) return Point();
- return boundary[i >= 0 ? i % l : i % l + l];
- }*/
-
-public:
- ConvexHull() {}
- ConvexHull(std::vector<Point> const & points) :
- boundary (points)
- {
- graham();
- }
-
- template <typename T>
- ConvexHull(T b, T e) :boundary(b,e) {}
-
- ~ConvexHull()
- {
- }
-
-public:
- /** Is the convex hull clockwise? We use the definition of clockwise from point.h
- **/
- bool is_clockwise() const;
- bool top_point_first() const;
- bool meets_invariants() const;
-
- // contains no points
- bool empty() const { return boundary.empty();}
-
- // contains exactly one point
- bool singular() const { return boundary.size() == 1;}
-
- // all points are on a line
- bool linear() const { return boundary.size() == 2;}
- bool is_degenerate() const;
-
- // area of the convex hull
- double centroid_and_area(Geom::Point& centroid) const;
- double area() const {
- Point tmp;
- return centroid_and_area(tmp);
- }
-
- // furthest point in a direction (lg time)
- Point const * furthest(Point direction) const;
-
- bool is_left(Point p, int n);
- bool is_strict_left(Point p, int n);
- int find_left(Point p);
- int find_strict_left(Point p);
- double narrowest_diameter(Point &a, Point &b, Point &c);
-
-};
-/** @brief Output operator for points.
- * Prints out all the coordinates. */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) {
- out_file << "ConvexHull(";
- for(unsigned i = 0; i < in_cvx.size(); i++) {
- out_file << in_cvx.boundary[i] << ", ";
- }
- out_file << ")";
- return out_file;
-}
-
-// do two convex hulls intersect?
-bool intersectp(ConvexHull a, ConvexHull b);
-
-std::vector<std::pair<int, int> > bridges(ConvexHull a, ConvexHull b);
-
-// find the convex hull intersection
-ConvexHull intersection(ConvexHull a, ConvexHull b);
-ConvexHull sweepline_intersection(ConvexHull const &a, ConvexHull const &b);
-
-// find the convex hull of a set of convex hulls
-ConvexHull merge(ConvexHull a, ConvexHull b);
-
-// naive approach
-ConvexHull graham_merge(ConvexHull a, ConvexHull b);
-
-// naive approach
-ConvexHull andrew_merge(ConvexHull a, ConvexHull b);
-
-unsigned find_bottom_right(ConvexHull const &a);
-
-/*** Arbitrary transform operator.
- * Take a convex hull and apply an arbitrary convexity preserving transform.
- * we should be concerned about singular tranforms here.
- */
-template <class T> ConvexHull operator*(ConvexHull const &p, T const &m) {
- ConvexHull pr;
-
- pr.boundary.reserve(p.boundary.size());
-
- for(unsigned i = 0; i < p.boundary.size(); i++) {
- pr.boundary.push_back(p.boundary[i]*m);
- }
- return pr;
-}
-
-ConvexHull clip(ConvexHull const & ch, Point n, double d);
-
-//TODO: reinstate
-/*class ConvexCover{
-public:
- Path const* path;
- std::vector<ConvexHull> cc;
-
- ConvexCover(Path const &sp);
-};*/
-
-};
-
-#endif //2GEOM_CONVEX_COVER_H
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/convex-cover.cpp b/src/2geom/convex-hull.cpp
index 5e599fdde..4f5e06733 100644
--- a/src/2geom/convex-cover.cpp
+++ b/src/2geom/convex-hull.cpp
@@ -1,8 +1,11 @@
-/*
- * convex-cover.cpp
- *
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
- * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+/** @file
+ * @brief Convex hull of a set of points
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Michael G. Sloan <mgsloan@gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ * Copyright 2006-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -29,11 +32,13 @@
*
*/
-#include <2geom/convex-cover.h>
+#include <2geom/convex-hull.h>
#include <2geom/exception.h>
#include <algorithm>
#include <map>
-#include <assert.h>
+#include <iostream>
+#include <cassert>
+#include <boost/array.hpp>
/** Todo:
+ modify graham scan to work top to bottom, rather than around angles
@@ -50,9 +55,237 @@ using std::vector;
using std::map;
using std::pair;
using std::make_pair;
+using std::swap;
+
+namespace Geom {
+
+ConvexHull::ConvexHull(Point const &a, Point const &b)
+ : _boundary(2)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c)
+ : _boundary(3)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ _boundary[2] = c;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d)
+ : _boundary(4)
+ , _lower(0)
+{
+ _boundary[0] = a;
+ _boundary[1] = b;
+ _boundary[2] = c;
+ _boundary[3] = d;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+
+ConvexHull::ConvexHull(std::vector<Point> const &pts)
+ : _lower(0)
+{
+ //if (pts.size() > 16) { // arbitrary threshold
+ // _prune(pts.begin(), pts.end(), _boundary);
+ //} else {
+ _boundary = pts;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ //}
+ _construct();
+}
+
+bool ConvexHull::_is_clockwise_turn(Point const &a, Point const &b, Point const &c)
+{
+ if (b == c) return false;
+ return cross(b-a, c-a) > 0;
+}
+
+void ConvexHull::_construct()
+{
+ // _boundary must already be sorted in LexLess<X> order
+ if (_boundary.empty()) {
+ _lower = 0;
+ return;
+ }
+ if (_boundary.size() == 1 || (_boundary.size() == 2 && _boundary[0] == _boundary[1])) {
+ _boundary.resize(1);
+ _lower = 1;
+ return;
+ }
+ if (_boundary.size() == 2) {
+ _lower = 2;
+ return;
+ }
+
+ std::size_t k = 2;
+ for (std::size_t i = 2; i < _boundary.size(); ++i) {
+ while (k >= 2 && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) {
+ --k;
+ }
+ std::swap(_boundary[k++], _boundary[i]);
+ }
+
+ _lower = k;
+ std::sort(_boundary.begin() + k, _boundary.end(), Point::LexGreater<X>());
+ _boundary.push_back(_boundary.front());
+ for (std::size_t i = _lower; i < _boundary.size(); ++i) {
+ while (k > _lower && !_is_clockwise_turn(_boundary[k-2], _boundary[k-1], _boundary[i])) {
+ --k;
+ }
+ std::swap(_boundary[k++], _boundary[i]);
+ }
+
+ _boundary.resize(k-1);
+}
+
+double ConvexHull::area() const
+{
+ if (size() <= 2) return 0;
+
+ double a = 0;
+ for (std::size_t i = 0; i < size()-1; ++i) {
+ a += cross(_boundary[i], _boundary[i+1]);
+ }
+ a += cross(_boundary.back(), _boundary.front());
+ return fabs(a * 0.5);
+}
+
+OptRect ConvexHull::bounds() const
+{
+ OptRect ret;
+ if (empty()) return ret;
+ ret = Rect(left(), top(), right(), bottom());
+ return ret;
+}
+
+Point ConvexHull::topPoint() const
+{
+ Point ret;
+ ret[Y] = std::numeric_limits<Coord>::infinity();
+
+ for (UpperIterator i = upperHull().begin(); i != upperHull().end(); ++i) {
+ if (ret[Y] >= i->y()) {
+ ret = *i;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+Point ConvexHull::bottomPoint() const
+{
+ Point ret;
+ ret[Y] = -std::numeric_limits<Coord>::infinity();
+
+ for (LowerIterator j = lowerHull().begin(); j != lowerHull().end(); ++j) {
+ if (ret[Y] <= j->y()) {
+ ret = *j;
+ } else {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+template <typename Iter, typename Lex>
+bool below_x_monotonic_polyline(Point const &p, Iter first, Iter last, Lex lex)
+{
+ typename Lex::Secondary above;
+ Iter f = std::lower_bound(first, last, p, lex);
+ if (f == last) return false;
+ if (f == first) {
+ if (p == *f) return true;
+ return false;
+ }
+
+ Point a = *(f-1), b = *f;
+ if (a[X] == b[X]) {
+ if (above(p[Y], a[Y]) || above(b[Y], p[Y])) return false;
+ } else {
+ // TODO: maybe there is a more numerically stable method
+ Coord y = lerp((p[X] - a[X]) / (b[X] - a[X]), a[Y], b[Y]);
+ if (above(p[Y], y)) return false;
+ }
+ return true;
+}
+
+bool ConvexHull::contains(Point const &p) const
+{
+ if (_boundary.empty()) return false;
+ if (_boundary.size() == 1) {
+ if (_boundary[0] == p) return true;
+ return false;
+ }
+
+ // 1. verify that the point is in the relevant X range
+ if (p[X] < _boundary[0][X] || p[X] > _boundary[_lower-1][X]) return false;
+
+ // 2. check whether it is below the upper hull
+ UpperIterator ub = upperHull().begin(), ue = upperHull().end();
+ if (!below_x_monotonic_polyline(p, ub, ue, Point::LexLess<X>())) return false;
+
+ // 3. check whether it is above the lower hull
+ LowerIterator lb = lowerHull().begin(), le = lowerHull().end();
+ if (!below_x_monotonic_polyline(p, lb, le, Point::LexGreater<X>())) return false;
+
+ return true;
+}
+
+bool ConvexHull::contains(Rect const &r) const
+{
+ for (unsigned i = 0; i < 4; ++i) {
+ if (!contains(r.corner(i))) return false;
+ }
+ return true;
+}
+
+bool ConvexHull::contains(ConvexHull const &ch) const
+{
+ // TODO: requires interiorContains.
+ // We have to check all points of ch, and each point takes logarithmic time.
+ // If there are more points in ch that here, it is faster to make the check
+ // the other way around.
+ /*if (ch.size() > size()) {
+ for (iterator i = begin(); i != end(); ++i) {
+ if (ch.interiorContains(*i)) return false;
+ }
+ return true;
+ }*/
+
+ for (iterator i = ch.begin(); i != ch.end(); ++i) {
+ if (!contains(*i)) return false;
+ }
+ return true;
+}
+
+void ConvexHull::swap(ConvexHull &other)
+{
+ _boundary.swap(other._boundary);
+ std::swap(_lower, other._lower);
+}
-namespace Geom{
+void ConvexHull::swap(std::vector<Point> &pts)
+{
+ _boundary.swap(pts);
+ _lower = 0;
+ std::sort(_boundary.begin(), _boundary.end(), Point::LexLess<X>());
+ _construct();
+}
+#if 0
/*** SignedTriangleArea
* returns the area of the triangle defined by p0, p1, p2. A clockwise triangle has positive area.
*/
@@ -118,147 +351,6 @@ public:
#endif
};
-void
-ConvexHull::find_pivot() {
- // Find pivot P;
- unsigned pivot = 0;
- for (unsigned i = 1; i < boundary.size(); i++)
- if(boundary[i] <= boundary[pivot])
- pivot = i;
-
- std::swap(boundary[0], boundary[pivot]);
-}
-
-void
-ConvexHull::angle_sort() {
-// sort points by angle (resolve ties in favor of point farther from P);
-// we leave the first one in place as our pivot
- std::sort(boundary.begin()+1, boundary.end(), angle_cmp(boundary[0]));
-}
-
-
-void
-ConvexHull::graham_scan() {
- // prune out equal points. points are sorted, so equals are adjacent
- std::vector<Point>::iterator e =
- std::unique(boundary.begin(), boundary.end());
- boundary.resize(e - boundary.begin());
- for(unsigned int i = 2; i < boundary.size(); i++) {
-
- }
- if (boundary.size() < 4) {
- return;
- }
- unsigned stac = 2;
- for(unsigned int i = 2; i < boundary.size(); i++) {
- double o = SignedTriangleArea(boundary[stac-2],
- boundary[stac-1],
- boundary[i]);
- if(o == 0) { // colinear - dangerous...
- stac--;
- } else if(o < 0) { // anticlockwise
- } else { // remove concavity
- while(o >= 0 && stac > 2) {
- stac--;
- o = SignedTriangleArea(boundary[stac-2],
- boundary[stac-1],
- boundary[i]);
- }
- }
- boundary[stac++] = boundary[i];
- }
- boundary.resize(stac);
-}
-
-// following code is from marco.
-
-/*
- * return true in case the oriented polyline p0, p1, p2 is a right turn
- */
-inline
-bool is_a_right_turn (Point const& p0, Point const& p1, Point const& p2)
-{
- if (p1 == p2) return false;
- Point q1 = p1 - p0;
- Point q2 = p2 - p0;
- if (q1 == -q2) return false;
- return (cross (q1, q2) < 0);
-}
-
-/*
- * return true if p < q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_less
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[Y] < q[Y]) || (p[Y] == q[Y] && p[X] < q[X]));
- }
-};
-
-/*
- * return true if p > q wrt the lexicographyc order induced by the coordinates
- */
-struct lex_greater
-{
- bool operator() (Point const& p, Point const& q)
- {
- return ((p[Y] > q[Y]) || (p[Y] == q[Y] && p[X] > q[X]));
- }
-};
-
-/*
- * Compute the convex hull of a set of points.
- * The implementation is based on the Andrew's scan algorithm
- * note: in the Bezier clipping for collinear normals it seems
- * to be more stable wrt the Graham's scan algorithm and in general
- * a bit quikier
- */
-void ConvexHull::andrew_scan ()
-{
- vector<Point> & P = boundary;
- size_t n = P.size();
- if (n < 2) return;
- std::sort(P.begin(), P.end(), lex_less());
- if (n < 4) return;
- // upper hull
- size_t u = 2;
- for (size_t i = 2; i < n; ++i)
- {
- while (u > 1 && !is_a_right_turn(P[u-2], P[u-1], P[i]))
- {
- --u;
- }
- std::swap(P[u], P[i]);
- ++u;
- }
- std::sort(P.begin() + u, P.end(), lex_greater());
- std::rotate(P.begin(), P.begin() + 1, P.end());
- // lower hull
- size_t l = u;
- size_t k = u - 1;
- for (size_t i = l; i < n; ++i)
- {
- while (l > k && !is_a_right_turn(P[l-2], P[l-1], P[i]))
- {
- --l;
- }
- std::swap(P[l], P[i]);
- ++l;
- }
- P.resize(l);
-}
-
-void
-ConvexHull::graham() {
- /*if(is_degenerate()) // nothing to do
- return;*/
- //find_pivot();
- //angle_sort();
- //graham_scan();
- andrew_scan();
-}
-
//Mathematically incorrect mod, but more useful.
int mod(int i, int l) {
return i >= 0 ?
@@ -266,65 +358,6 @@ int mod(int i, int l) {
}
//OPT: usages can often be replaced by conditions
-/*** ConvexHull::left
- * Tests if a point is left (outside) of a particular segment, n. */
-bool
-ConvexHull::is_left(Point p, int n) {
- return SignedTriangleArea((*this)[n], (*this)[n+1], p) > 0;
-}
-
-/*** ConvexHull::strict_left
- * Tests if a point is left (outside) of a particular segment, n. */
-bool
-ConvexHull::is_strict_left(Point p, int n) {
- return SignedTriangleArea((*this)[n], (*this)[n+1], p) >= 0;
-}
-
-/*** ConvexHull::find_left
- * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such
- * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/
-int
-ConvexHull::find_left(Point p) {
- int l = boundary.size(); //Who knows if C++ is smart enough to optimize this?
- for(int i = 0; i < l; i++) {
- if(is_left(p, i)) return i;
- }
- return -1;
-}
-
-
-/*** ConvexHull::find_positive
- * May return any number n where the segment n -> n + 1 (possibly looped around) in the hull such
- * that the point is on the wrong side to be within the hull. Returns -1 if it is within the hull.*/
-int
-ConvexHull::find_strict_left(Point p) {
- int l = boundary.size(); //Who knows if C++ is smart enough to optimize this?
- for(int i = 0; i < l; i++) {
- if(is_strict_left(p, i)) return i;
- }
- return -1;
-}
-
-//OPT: do a spread iteration - quasi-random with no repeats and full coverage.
-
-/*** ConvexHull::contains_point
- * In order to test whether a point is inside a convex hull we can travel once around the outside making
- * sure that each triangle made from an edge and the point has positive area. */
-bool
-ConvexHull::contains_point(Point p) {
- if(size() == 0) return false;
- return find_left(p) == -1;
-}
-
-/*** ConvexHull::strict_contains_point
- * In order to test whether a point is strictly inside (not on the boundary) a convex hull we can travel once around the outside making
- * sure that each triangle made from an edge and the point has positive area. */
-bool
-ConvexHull::strict_contains_point(Point p) {
- if(size() == 0) return false;
- return find_strict_left(p) == -1;
-}
-
/*** ConvexHull::add_point
* to add a point we need to find whether the new point extends the boundary, and if so, what it
* obscures. Tarjan? Jarvis?*/
@@ -332,18 +365,18 @@ void
ConvexHull::merge(Point p) {
std::vector<Point> out;
- int l = boundary.size();
+ int len = boundary.size();
- if(l < 2) {
+ if(len < 2) {
if(boundary.empty() || boundary[0] != p)
boundary.push_back(p);
return;
}
bool pushed = false;
-
+
bool pre = is_left(p, -1);
- for(int i = 0; i < l; i++) {
+ for(int i = 0; i < len; i++) {
bool cur = is_left(p, i);
if(pre) {
if(cur) {
@@ -595,7 +628,7 @@ ConvexHull graham_merge(ConvexHull a, ConvexHull b) {
// we can avoid the find pivot step because of top_point_first
if(b.boundary[0] <= a.boundary[0])
- std::swap(a, b);
+ swap(a, b);
result.boundary = a.boundary;
result.boundary.insert(result.boundary.end(),
@@ -615,7 +648,7 @@ ConvexHull andrew_merge(ConvexHull a, ConvexHull b) {
// we can avoid the find pivot step because of top_point_first
if(b.boundary[0] <= a.boundary[0])
- std::swap(a, b);
+ swap(a, b);
result.boundary = a.boundary;
result.boundary.insert(result.boundary.end(),
@@ -697,6 +730,7 @@ double ConvexHull::narrowest_diameter(Point &a, Point &b, Point &c) {
}
return d;
}
+#endif
};
diff --git a/src/2geom/convex-hull.h b/src/2geom/convex-hull.h
new file mode 100644
index 000000000..4f4d10bd6
--- /dev/null
+++ b/src/2geom/convex-hull.h
@@ -0,0 +1,346 @@
+/** @file
+ * @brief Convex hull data structures
+ *//*
+ * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+#ifndef LIB2GEOM_SEEN_CONVEX_HULL_H
+#define LIB2GEOM_SEEN_CONVEX_HULL_H
+
+#include <2geom/point.h>
+#include <2geom/rect.h>
+#include <vector>
+#include <algorithm>
+#include <boost/operators.hpp>
+#include <boost/optional.hpp>
+#include <boost/range/iterator_range.hpp>
+
+namespace Geom {
+
+namespace {
+
+/** @brief Iterator for the lower convex hull.
+ * This iterator allows us to avoid duplicating any points in the hull
+ * boundary and still express most algorithms in a concise way. */
+class ConvexHullLowerIterator
+ : public boost::random_access_iterator_helper
+ < ConvexHullLowerIterator
+ , Point
+ , std::ptrdiff_t
+ , Point const *
+ , Point const &
+ >
+{
+public:
+ typedef ConvexHullLowerIterator Self;
+ ConvexHullLowerIterator()
+ : _data(NULL)
+ , _size(0)
+ , _x(0)
+ {}
+ ConvexHullLowerIterator(std::vector<Point> const &pts, std::size_t x)
+ : _data(&pts[0])
+ , _size(pts.size())
+ , _x(x)
+ {}
+
+ Self &operator++() {
+ *this += 1;
+ return *this;
+ }
+ Self &operator--() {
+ *this -= 1;
+ return *this;
+ }
+ Self &operator+=(std::ptrdiff_t d) {
+ _x += d;
+ return *this;
+ }
+ Self &operator-=(std::ptrdiff_t d) {
+ _x -= d;
+ return *this;
+ }
+ std::ptrdiff_t operator-(Self const &other) const {
+ return _x - other._x;
+ }
+ Point const &operator*() const {
+ if (_x < _size) {
+ return _data[_x];
+ } else {
+ return *_data;
+ }
+ }
+ bool operator==(Self const &other) const {
+ return _data == other._data && _x == other._x;
+ }
+ bool operator<(Self const &other) const {
+ return _data == other._data && _x < other._x;
+ }
+
+private:
+ Point const *_data;
+ std::size_t _size;
+ std::size_t _x;
+};
+
+} // end anonymous namespace
+
+/**
+ * @brief Convex hull based on the Andrew's monotone chain algorithm.
+ * @ingroup Shapes
+ */
+class ConvexHull {
+public:
+ typedef std::vector<Point>::const_iterator iterator;
+ typedef std::vector<Point>::const_iterator const_iterator;
+ typedef std::vector<Point>::const_iterator UpperIterator;
+ typedef ConvexHullLowerIterator LowerIterator;
+
+ /// @name Construct a convex hull.
+ /// @{
+
+ /// Create an empty convex hull.
+ ConvexHull() {}
+ /// Construct a singular convex hull.
+ explicit ConvexHull(Point const &a)
+ : _boundary(1, a)
+ , _lower(1)
+ {}
+ /// Construct a convex hull of two points.
+ ConvexHull(Point const &a, Point const &b);
+ /// Construct a convex hull of three points.
+ ConvexHull(Point const &a, Point const &b, Point const &c);
+ /// Construct a convex hull of four points.
+ ConvexHull(Point const &a, Point const &b, Point const &c, Point const &d);
+ /// Create a convex hull of a vector of points.
+ ConvexHull(std::vector<Point> const &pts);
+
+ /// Create a convex hull of a range of points.
+ template <typename Iter>
+ ConvexHull(Iter first, Iter last)
+ : _lower(0)
+ {
+ _prune(first, last, _boundary);
+ _construct();
+ }
+ /// @}
+
+ /// @name Inspect basic properties.
+ /// @{
+
+ /// Check for emptiness.
+ bool empty() const { return _boundary.empty(); }
+ /// Get the number of points in the hull.
+ size_t size() const { return _boundary.size(); }
+ /// Check whether the hull contains only one point.
+ bool isSingular() const { return _boundary.size() == 1; }
+ /// Check whether the hull is a line.
+ bool isLinear() const { return _boundary.size() == 2; }
+ /// Check whether the hull has zero area.
+ bool isDegenerate() const { return _boundary.size() < 3; }
+ /// Calculate the area of the convex hull.
+ double area() const;
+ //Point centroid() const;
+ //double areaAndCentroid(Point &c);
+ //FatLine maxDiameter() const;
+ //FatLine minDiameter() const;
+ /// @}
+
+ /// @name Inspect bounds and extreme points.
+ /// @{
+
+ /// Compute the bounding rectangle of the convex hull.
+ OptRect bounds() const;
+
+ /// Get the leftmost (minimum X) coordinate of the hull.
+ Coord left() const { return _boundary[0][X]; }
+ /// Get the rightmost (maximum X) coordinate of the hull.
+ Coord right() const { return _boundary[_lower-1][X]; }
+ /// Get the topmost (minimum Y) coordinate of the hull.
+ Coord top() const { return topPoint()[Y]; }
+ /// Get the bottommost (maximum Y) coordinate of the hull.
+ Coord bottom() const { return bottomPoint()[Y]; }
+
+ /// Get the leftmost (minimum X) point of the hull.
+ /// If the leftmost edge is vertical, the top point of the edge is returned.
+ Point leftPoint() const { return _boundary[0]; }
+ /// Get the rightmost (maximum X) point of the hull.
+ /// If the rightmost edge is vertical, the bottom point edge is returned.
+ Point rightPoint() const { return _boundary[_lower-1]; }
+ /// Get the topmost (minimum Y) point of the hull.
+ /// If the topmost edge is horizontal, the right point of the edge is returned.
+ Point topPoint() const;
+ /// Get the bottommost (maximum Y) point of the hull.
+ /// If the bottommost edge is horizontal, the left point of the edge is returned.
+ Point bottomPoint() const;
+ ///@}
+
+ /// @name Iterate over points.
+ /// @{
+ /** @brief Get the begin iterator to the points that form the hull.
+ * Points are are returned beginning the the leftmost one, going along
+ * the upper (minimum Y) side, and then along the bottom.
+ * Thus the points are always ordered clockwise. No point is
+ * repeated. */
+ iterator begin() const { return _boundary.begin(); }
+ /// Get the end iterator to the points that form the hull.
+ iterator end() const { return _boundary.end(); }
+ /// Get the first, leftmost point in the hull.
+ Point const &front() const { return _boundary.front(); }
+ /// Get the penultimate point of the lower hull.
+ Point const &back() const { return _boundary.back(); }
+ Point const &operator[](std::size_t i) const {
+ return _boundary[i];
+ }
+
+ /** @brief Get an iterator range to the upper part of the hull.
+ * This returns a range that includes the leftmost point,
+ * all points of the upper hull, and the rightmost point. */
+ boost::iterator_range<UpperIterator> upperHull() const {
+ boost::iterator_range<UpperIterator> r(_boundary.begin(), _boundary.begin() + _lower);
+ return r;
+ }
+
+ /** @brief Get an iterator range to the lower part of the hull.
+ * This returns a range that includes the leftmost point,
+ * all points of the lower hull, and the rightmost point. */
+ boost::iterator_range<LowerIterator> lowerHull() const {
+ if (_boundary.empty()) {
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+ LowerIterator(_boundary, 0));
+ return r;
+ }
+ if (_boundary.size() == 1) {
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, 0),
+ LowerIterator(_boundary, 1));
+ return r;
+ }
+ boost::iterator_range<LowerIterator> r(LowerIterator(_boundary, _lower - 1),
+ LowerIterator(_boundary, _boundary.size() + 1));
+ return r;
+ }
+ /// @}
+
+ /// @name Check for containment and intersection.
+ /// @{
+ /** @brief Check whether the given point is inside the hull.
+ * This takes logarithmic time. */
+ bool contains(Point const &p) const;
+ /** @brief Check whether the given axis-aligned rectangle is inside the hull.
+ * A rectangle is inside the hull if all of its corners are inside. */
+ bool contains(Rect const &r) const;
+ /// Check whether the given convex hull is completely contained in this one.
+ bool contains(ConvexHull const &other) const;
+ //bool interiorContains(Point const &p) const;
+ //bool interiorContains(Rect const &r) const;
+ //bool interiorContains(ConvexHull const &other) const;
+ //bool intersects(Rect const &r) const;
+ //bool intersects(ConvexHull const &other) const;
+
+ //ConvexHull &operator|=(ConvexHull const &other);
+ //ConvexHull &operator&=(ConvexHull const &other);
+ //ConvexHull &operator*=(Affine const &m);
+
+ //ConvexHull &expand(Point const &p);
+ //void unifyWith(ConvexHull const &other);
+ //void intersectWith(ConvexHull const &other);
+ /// @}
+
+ void swap(ConvexHull &other);
+ void swap(std::vector<Point> &pts);
+
+private:
+ void _construct();
+ static bool _is_clockwise_turn(Point const &a, Point const &b, Point const &c);
+
+ /// Take a vector of points and produce a pruned sorted vector.
+ template <typename Iter>
+ static void _prune(Iter first, Iter last, std::vector<Point> &out) {
+ boost::optional<Point> ymin, ymax, xmin, xmax;
+ for (Iter i = first; i != last; ++i) {
+ Point p = *i;
+ if (!ymin || Point::LexLess<Y>()(p, *ymin)) {
+ ymin = p;
+ }
+ if (!xmin || Point::LexLess<X>()(p, *xmin)) {
+ xmin = p;
+ }
+ if (!ymax || Point::LexGreater<Y>()(p, *ymax)) {
+ ymax = p;
+ }
+ if (!xmax || Point::LexGreater<X>()(p, *xmax)) {
+ xmax = p;
+ }
+ }
+ if (!ymin) return;
+
+ ConvexHull qhull(*xmin, *xmax, *ymin, *ymax);
+ for (Iter i = first; i != last; ++i) {
+ if (qhull.contains(*i)) continue;
+ out.push_back(*i);
+ }
+
+ out.push_back(*xmin);
+ out.push_back(*xmax);
+ out.push_back(*ymin);
+ out.push_back(*ymax);
+ std::sort(out.begin(), out.end(), Point::LexLess<X>());
+ out.erase(std::unique(out.begin(), out.end()), out.end());
+ }
+
+ /// Sequence of points forming the convex hull polygon.
+ std::vector<Point> _boundary;
+ /// Index one past the rightmost point, where the lower part of the boundary starts.
+ std::size_t _lower;
+};
+
+/** @brief Output operator for convex hulls.
+ * Prints out all the coordinates. */
+inline std::ostream &operator<< (std::ostream &out_file, const Geom::ConvexHull &in_cvx) {
+ out_file << "ConvexHull(";
+ for(unsigned i = 0; i < in_cvx.size(); i++) {
+ out_file << in_cvx[i] << ", ";
+ }
+ out_file << ")";
+ return out_file;
+}
+
+} // end namespace Geom
+
+#endif // LIB2GEOM_SEEN_CONVEX_HULL_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/coord.cpp b/src/2geom/coord.cpp
new file mode 100644
index 000000000..8b5e28586
--- /dev/null
+++ b/src/2geom/coord.cpp
@@ -0,0 +1,3688 @@
+/** @file
+ * @brief Conversion between Coord and strings
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+// Most of the code in this file is derived from:
+// https://code.google.com/p/double-conversion/
+// The copyright notice for that code is attached below.
+//
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <2geom/coord.h>
+#include <stdint.h>
+#include <cstdlib>
+#include <cassert>
+#include <cstring>
+#include <climits>
+#include <cstdarg>
+#include <cmath>
+
+#ifndef ASSERT
+#define ASSERT(condition) \
+ assert(condition);
+#endif
+#ifndef UNIMPLEMENTED
+#define UNIMPLEMENTED() (abort())
+#endif
+#ifndef UNREACHABLE
+#define UNREACHABLE() (abort())
+#endif
+
+#define UINT64_2PART_C(a, b) (((static_cast<uint64_t>(a) << 32) + 0x##b##u))
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+#endif
+
+#ifndef DISALLOW_COPY_AND_ASSIGN
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+#endif
+
+#ifndef DISALLOW_IMPLICIT_CONSTRUCTORS
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+#endif
+
+#if defined(__GNUC__)
+#define DOUBLE_CONVERSION_UNUSED __attribute__((unused))
+#else
+#define DOUBLE_CONVERSION_UNUSED
+#endif
+
+namespace Geom {
+
+namespace {
+
+inline int StrLength(const char* string) {
+ size_t length = strlen(string);
+ ASSERT(length == static_cast<size_t>(static_cast<int>(length)));
+ return static_cast<int>(length);
+}
+
+template <typename T>
+class Vector {
+ public:
+ Vector() : start_(NULL), length_(0) {}
+ Vector(T* data, int length) : start_(data), length_(length) {
+ ASSERT(length == 0 || (length > 0 && data != NULL));
+ }
+
+ Vector<T> SubVector(int from, int to) {
+ ASSERT(to <= length_);
+ ASSERT(from < to);
+ ASSERT(0 <= from);
+ return Vector<T>(start() + from, to - from);
+ }
+ int length() const { return length_; }
+ bool is_empty() const { return length_ == 0; }
+
+ T* start() const { return start_; }
+
+ T& operator[](int index) const {
+ ASSERT(0 <= index && index < length_);
+ return start_[index];
+ }
+ T& first() { return start_[0]; }
+ T& last() { return start_[length_ - 1]; }
+
+ private:
+ T* start_;
+ int length_;
+};
+
+template <class Dest, class Source>
+inline Dest BitCast(const Source& source) {
+ DOUBLE_CONVERSION_UNUSED
+ typedef char VerifySizesAreEqual[sizeof(Dest) == sizeof(Source) ? 1 : -1];
+ Dest dest;
+ memmove(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+template <class Dest, class Source>
+inline Dest BitCast(Source* source) {
+ return BitCast<Dest>(reinterpret_cast<uintptr_t>(source));
+}
+
+// We assume that doubles and uint64_t have the same endianness.
+static uint64_t double_to_uint64(double d) { return BitCast<uint64_t>(d); }
+static double uint64_to_double(uint64_t d64) { return BitCast<double>(d64); }
+
+// This "Do It Yourself Floating Point" class
+class DiyFp {
+ public:
+ static const int kSignificandSize = 64;
+
+ DiyFp() : f_(0), e_(0) {}
+ DiyFp(uint64_t f, int e) : f_(f), e_(e) {}
+
+ void Subtract(const DiyFp& other) {
+ ASSERT(e_ == other.e_);
+ ASSERT(f_ >= other.f_);
+ f_ -= other.f_;
+ }
+
+ static DiyFp Minus(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Subtract(b);
+ return result;
+ }
+
+ void Multiply(const DiyFp& other) {
+ const uint64_t kM32 = 0xFFFFFFFFU;
+ uint64_t a = f_ >> 32;
+ uint64_t b = f_ & kM32;
+ uint64_t c = other.f_ >> 32;
+ uint64_t d = other.f_ & kM32;
+ uint64_t ac = a * c;
+ uint64_t bc = b * c;
+ uint64_t ad = a * d;
+ uint64_t bd = b * d;
+ uint64_t tmp = (bd >> 32) + (ad & kM32) + (bc & kM32);
+ // By adding 1U << 31 to tmp we round the final result.
+ // Halfway cases will be round up.
+ tmp += 1U << 31;
+ uint64_t result_f = ac + (ad >> 32) + (bc >> 32) + (tmp >> 32);
+ e_ += other.e_ + 64;
+ f_ = result_f;
+ }
+
+ static DiyFp Times(const DiyFp& a, const DiyFp& b) {
+ DiyFp result = a;
+ result.Multiply(b);
+ return result;
+ }
+
+ void Normalize() {
+ ASSERT(f_ != 0);
+ uint64_t f = f_;
+ int e = e_;
+
+ const uint64_t k10MSBits = UINT64_2PART_C(0xFFC00000, 00000000);
+ while ((f & k10MSBits) == 0) {
+ f <<= 10;
+ e -= 10;
+ }
+ while ((f & kUint64MSB) == 0) {
+ f <<= 1;
+ e--;
+ }
+ f_ = f;
+ e_ = e;
+ }
+
+ static DiyFp Normalize(const DiyFp& a) {
+ DiyFp result = a;
+ result.Normalize();
+ return result;
+ }
+
+ uint64_t f() const { return f_; }
+ int e() const { return e_; }
+
+ void set_f(uint64_t new_value) { f_ = new_value; }
+ void set_e(int new_value) { e_ = new_value; }
+
+ private:
+ static const uint64_t kUint64MSB = UINT64_2PART_C(0x80000000, 00000000);
+
+ uint64_t f_;
+ int e_;
+};
+
+class Double {
+ public:
+ static const uint64_t kSignMask = UINT64_2PART_C(0x80000000, 00000000);
+ static const uint64_t kExponentMask = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kSignificandMask = UINT64_2PART_C(0x000FFFFF, FFFFFFFF);
+ static const uint64_t kHiddenBit = UINT64_2PART_C(0x00100000, 00000000);
+ static const int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
+ static const int kSignificandSize = 53;
+
+ Double() : d64_(0) {}
+ explicit Double(double d) : d64_(double_to_uint64(d)) {}
+ explicit Double(uint64_t d64) : d64_(d64) {}
+ explicit Double(DiyFp diy_fp)
+ : d64_(DiyFpToUint64(diy_fp)) {}
+
+ DiyFp AsDiyFp() const {
+ ASSERT(Sign() > 0);
+ ASSERT(!IsSpecial());
+ return DiyFp(Significand(), Exponent());
+ }
+
+ DiyFp AsNormalizedDiyFp() const {
+ ASSERT(value() > 0.0);
+ uint64_t f = Significand();
+ int e = Exponent();
+
+ // The current double could be a denormal.
+ while ((f & kHiddenBit) == 0) {
+ f <<= 1;
+ e--;
+ }
+ // Do the final shifts in one go.
+ f <<= DiyFp::kSignificandSize - kSignificandSize;
+ e -= DiyFp::kSignificandSize - kSignificandSize;
+ return DiyFp(f, e);
+ }
+
+ uint64_t AsUint64() const {
+ return d64_;
+ }
+
+ double NextDouble() const {
+ if (d64_ == kInfinity) return Double(kInfinity).value();
+ if (Sign() < 0 && Significand() == 0) {
+ // -0.0
+ return 0.0;
+ }
+ if (Sign() < 0) {
+ return Double(d64_ - 1).value();
+ } else {
+ return Double(d64_ + 1).value();
+ }
+ }
+
+ double PreviousDouble() const {
+ if (d64_ == (kInfinity | kSignMask)) return -Double::Infinity();
+ if (Sign() < 0) {
+ return Double(d64_ + 1).value();
+ } else {
+ if (Significand() == 0) return -0.0;
+ return Double(d64_ - 1).value();
+ }
+ }
+
+ int Exponent() const {
+ if (IsDenormal()) return kDenormalExponent;
+
+ uint64_t d64 = AsUint64();
+ int biased_e =
+ static_cast<int>((d64 & kExponentMask) >> kPhysicalSignificandSize);
+ return biased_e - kExponentBias;
+ }
+
+ uint64_t Significand() const {
+ uint64_t d64 = AsUint64();
+ uint64_t significand = d64 & kSignificandMask;
+ if (!IsDenormal()) {
+ return significand + kHiddenBit;
+ } else {
+ return significand;
+ }
+ }
+
+ bool IsDenormal() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == 0;
+ }
+
+ // We consider denormals not to be special.
+ // Hence only Infinity and NaN are special.
+ bool IsSpecial() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kExponentMask) == kExponentMask;
+ }
+
+ bool IsNan() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) != 0);
+ }
+
+ bool IsInfinite() const {
+ uint64_t d64 = AsUint64();
+ return ((d64 & kExponentMask) == kExponentMask) &&
+ ((d64 & kSignificandMask) == 0);
+ }
+
+ int Sign() const {
+ uint64_t d64 = AsUint64();
+ return (d64 & kSignMask) == 0? 1: -1;
+ }
+
+ DiyFp UpperBoundary() const {
+ ASSERT(Sign() > 0);
+ return DiyFp(Significand() * 2 + 1, Exponent() - 1);
+ }
+
+ void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const {
+ ASSERT(value() > 0.0);
+ DiyFp v = this->AsDiyFp();
+ DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1));
+ DiyFp m_minus;
+ if (LowerBoundaryIsCloser()) {
+ m_minus = DiyFp((v.f() << 2) - 1, v.e() - 2);
+ } else {
+ m_minus = DiyFp((v.f() << 1) - 1, v.e() - 1);
+ }
+ m_minus.set_f(m_minus.f() << (m_minus.e() - m_plus.e()));
+ m_minus.set_e(m_plus.e());
+ *out_m_plus = m_plus;
+ *out_m_minus = m_minus;
+ }
+
+ bool LowerBoundaryIsCloser() const {
+ bool physical_significand_is_zero = ((AsUint64() & kSignificandMask) == 0);
+ return physical_significand_is_zero && (Exponent() != kDenormalExponent);
+ }
+
+ double value() const { return uint64_to_double(d64_); }
+
+ static int SignificandSizeForOrderOfMagnitude(int order) {
+ if (order >= (kDenormalExponent + kSignificandSize)) {
+ return kSignificandSize;
+ }
+ if (order <= kDenormalExponent) return 0;
+ return order - kDenormalExponent;
+ }
+
+ static double Infinity() {
+ return Double(kInfinity).value();
+ }
+
+ static double NaN() {
+ return Double(kNaN).value();
+ }
+
+ private:
+ static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
+ static const int kDenormalExponent = -kExponentBias + 1;
+ static const int kMaxExponent = 0x7FF - kExponentBias;
+ static const uint64_t kInfinity = UINT64_2PART_C(0x7FF00000, 00000000);
+ static const uint64_t kNaN = UINT64_2PART_C(0x7FF80000, 00000000);
+
+ const uint64_t d64_;
+
+ static uint64_t DiyFpToUint64(DiyFp diy_fp) {
+ uint64_t significand = diy_fp.f();
+ int exponent = diy_fp.e();
+ while (significand > kHiddenBit + kSignificandMask) {
+ significand >>= 1;
+ exponent++;
+ }
+ if (exponent >= kMaxExponent) {
+ return kInfinity;
+ }
+ if (exponent < kDenormalExponent) {
+ return 0;
+ }
+ while (exponent > kDenormalExponent && (significand & kHiddenBit) == 0) {
+ significand <<= 1;
+ exponent--;
+ }
+ uint64_t biased_exponent;
+ if (exponent == kDenormalExponent && (significand & kHiddenBit) == 0) {
+ biased_exponent = 0;
+ } else {
+ biased_exponent = static_cast<uint64_t>(exponent + kExponentBias);
+ }
+ return (significand & kSignificandMask) |
+ (biased_exponent << kPhysicalSignificandSize);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(Double);
+};
+
+template<typename S>
+static int BitSize(S value) {
+ (void) value; // Mark variable as used.
+ return 8 * sizeof(value);
+}
+
+class Bignum {
+ public:
+ // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
+ // This bignum can encode much bigger numbers, since it contains an
+ // exponent.
+ static const int kMaxSignificantBits = 3584;
+
+ Bignum()
+ : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0)
+ {
+ for (int i = 0; i < kBigitCapacity; ++i) {
+ bigits_[i] = 0;
+ }
+ }
+ void AssignUInt16(uint16_t value) {
+ ASSERT(kBigitSize >= BitSize(value));
+ Zero();
+ if (value == 0) return;
+
+ EnsureCapacity(1);
+ bigits_[0] = value;
+ used_digits_ = 1;
+ }
+ void AssignUInt64(uint64_t value) {
+ const int kUInt64Size = 64;
+
+ Zero();
+ if (value == 0) return;
+
+ int needed_bigits = kUInt64Size / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ for (int i = 0; i < needed_bigits; ++i) {
+ bigits_[i] = value & kBigitMask;
+ value = value >> kBigitSize;
+ }
+ used_digits_ = needed_bigits;
+ Clamp();
+ }
+ void AssignBignum(const Bignum& other) {
+ exponent_ = other.exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ bigits_[i] = other.bigits_[i];
+ }
+ // Clear the excess digits (if there were any).
+ for (int i = other.used_digits_; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = other.used_digits_;
+ }
+
+ void AssignDecimalString(Vector<const char> value);
+ void AssignHexString(Vector<const char> value);
+
+ void AssignPowerUInt16(uint16_t base, int exponent);
+
+ void AddUInt16(uint16_t operand);
+ void AddUInt64(uint64_t operand);
+ void AddBignum(const Bignum& other);
+ // Precondition: this >= other.
+ void SubtractBignum(const Bignum& other);
+
+ void Square();
+ void ShiftLeft(int shift_amount);
+ void MultiplyByUInt32(uint32_t factor);
+ void MultiplyByUInt64(uint64_t factor);
+ void MultiplyByPowerOfTen(int exponent);
+ void Times10() { return MultiplyByUInt32(10); }
+ // Pseudocode:
+ // int result = this / other;
+ // this = this % other;
+ // In the worst case this function is in O(this/other).
+ uint16_t DivideModuloIntBignum(const Bignum& other);
+
+ bool ToHexString(char* buffer, int buffer_size) const;
+
+ // Returns
+ // -1 if a < b,
+ // 0 if a == b, and
+ // +1 if a > b.
+ static int Compare(const Bignum& a, const Bignum& b);
+ static bool Equal(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) == 0;
+ }
+ static bool LessEqual(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) <= 0;
+ }
+ static bool Less(const Bignum& a, const Bignum& b) {
+ return Compare(a, b) < 0;
+ }
+ // Returns Compare(a + b, c);
+ static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c);
+ // Returns a + b == c
+ static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) == 0;
+ }
+ // Returns a + b <= c
+ static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) <= 0;
+ }
+ // Returns a + b < c
+ static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) {
+ return PlusCompare(a, b, c) < 0;
+ }
+ private:
+ typedef uint32_t Chunk;
+ typedef uint64_t DoubleChunk;
+
+ static const int kChunkSize = sizeof(Chunk) * 8;
+ static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8;
+ // With bigit size of 28 we loose some bits, but a double still fits easily
+ // into two chunks, and more importantly we can use the Comba multiplication.
+ static const int kBigitSize = 28;
+ static const Chunk kBigitMask = (1 << kBigitSize) - 1;
+ // Every instance allocates kBigitLength chunks on the stack. Bignums cannot
+ // grow. There are no checks if the stack-allocated space is sufficient.
+ static const int kBigitCapacity = kMaxSignificantBits / kBigitSize;
+
+ void EnsureCapacity(int size) {
+ if (size > kBigitCapacity) {
+ UNREACHABLE();
+ }
+ }
+ void Align(const Bignum& other);
+ void Clamp();
+ bool IsClamped() const;
+ void Zero();
+ // Requires this to have enough capacity (no tests done).
+ // Updates used_digits_ if necessary.
+ // shift_amount must be < kBigitSize.
+ void BigitsShiftLeft(int shift_amount);
+ // BigitLength includes the "hidden" digits encoded in the exponent.
+ int BigitLength() const { return used_digits_ + exponent_; }
+ Chunk BigitAt(int index) const;
+ void SubtractTimes(const Bignum& other, int factor);
+
+ Chunk bigits_buffer_[kBigitCapacity];
+ // A vector backed by bigits_buffer_. This way accesses to the array are
+ // checked for out-of-bounds errors.
+ Vector<Chunk> bigits_;
+ int used_digits_;
+ // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
+ int exponent_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bignum);
+};
+
+static uint64_t ReadUInt64(Vector<const char> buffer,
+ int from,
+ int digits_to_read) {
+ uint64_t result = 0;
+ for (int i = from; i < from + digits_to_read; ++i) {
+ int digit = buffer[i] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = result * 10 + digit;
+ }
+ return result;
+}
+
+
+void Bignum::AssignDecimalString(Vector<const char> value) {
+ // 2^64 = 18446744073709551616 > 10^19
+ const int kMaxUint64DecimalDigits = 19;
+ Zero();
+ int length = value.length();
+ int pos = 0;
+ // Let's just say that each digit needs 4 bits.
+ while (length >= kMaxUint64DecimalDigits) {
+ uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits);
+ pos += kMaxUint64DecimalDigits;
+ length -= kMaxUint64DecimalDigits;
+ MultiplyByPowerOfTen(kMaxUint64DecimalDigits);
+ AddUInt64(digits);
+ }
+ uint64_t digits = ReadUInt64(value, pos, length);
+ MultiplyByPowerOfTen(length);
+ AddUInt64(digits);
+ Clamp();
+}
+
+
+static int HexCharValue(char c) {
+ if ('0' <= c && c <= '9') return c - '0';
+ if ('a' <= c && c <= 'f') return 10 + c - 'a';
+ ASSERT('A' <= c && c <= 'F');
+ return 10 + c - 'A';
+}
+
+
+void Bignum::AssignHexString(Vector<const char> value) {
+ Zero();
+ int length = value.length();
+
+ int needed_bigits = length * 4 / kBigitSize + 1;
+ EnsureCapacity(needed_bigits);
+ int string_index = length - 1;
+ for (int i = 0; i < needed_bigits - 1; ++i) {
+ // These bigits are guaranteed to be "full".
+ Chunk current_bigit = 0;
+ for (int j = 0; j < kBigitSize / 4; j++) {
+ current_bigit += HexCharValue(value[string_index--]) << (j * 4);
+ }
+ bigits_[i] = current_bigit;
+ }
+ used_digits_ = needed_bigits - 1;
+
+ Chunk most_significant_bigit = 0; // Could be = 0;
+ for (int j = 0; j <= string_index; ++j) {
+ most_significant_bigit <<= 4;
+ most_significant_bigit += HexCharValue(value[j]);
+ }
+ if (most_significant_bigit != 0) {
+ bigits_[used_digits_] = most_significant_bigit;
+ used_digits_++;
+ }
+ Clamp();
+}
+
+
+void Bignum::AddUInt64(uint64_t operand) {
+ if (operand == 0) return;
+ Bignum other;
+ other.AssignUInt64(operand);
+ AddBignum(other);
+}
+
+
+void Bignum::AddBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+
+ Align(other);
+
+ EnsureCapacity(1 + std::max(BigitLength(), other.BigitLength()) - exponent_);
+ Chunk carry = 0;
+ int bigit_pos = other.exponent_ - exponent_;
+ ASSERT(bigit_pos >= 0);
+ for (int i = 0; i < other.used_digits_; ++i) {
+ Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+
+ while (carry != 0) {
+ Chunk sum = bigits_[bigit_pos] + carry;
+ bigits_[bigit_pos] = sum & kBigitMask;
+ carry = sum >> kBigitSize;
+ bigit_pos++;
+ }
+ used_digits_ = std::max(bigit_pos, used_digits_);
+ ASSERT(IsClamped());
+}
+
+
+void Bignum::SubtractBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ // We require this to be bigger than other.
+ ASSERT(LessEqual(other, *this));
+
+ Align(other);
+
+ int offset = other.exponent_ - exponent_;
+ Chunk borrow = 0;
+ int i;
+ for (i = 0; i < other.used_digits_; ++i) {
+ ASSERT((borrow == 0) || (borrow == 1));
+ Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ while (borrow != 0) {
+ Chunk difference = bigits_[i + offset] - borrow;
+ bigits_[i + offset] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ ++i;
+ }
+ Clamp();
+}
+
+
+void Bignum::ShiftLeft(int shift_amount) {
+ if (used_digits_ == 0) return;
+ exponent_ += shift_amount / kBigitSize;
+ int local_shift = shift_amount % kBigitSize;
+ EnsureCapacity(used_digits_ + 1);
+ BigitsShiftLeft(local_shift);
+}
+
+
+void Bignum::MultiplyByUInt32(uint32_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ if (used_digits_ == 0) return;
+
+ ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1);
+ DoubleChunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry;
+ bigits_[i] = static_cast<Chunk>(product & kBigitMask);
+ carry = (product >> kBigitSize);
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByUInt64(uint64_t factor) {
+ if (factor == 1) return;
+ if (factor == 0) {
+ Zero();
+ return;
+ }
+ ASSERT(kBigitSize < 32);
+ uint64_t carry = 0;
+ uint64_t low = factor & 0xFFFFFFFF;
+ uint64_t high = factor >> 32;
+ for (int i = 0; i < used_digits_; ++i) {
+ uint64_t product_low = low * bigits_[i];
+ uint64_t product_high = high * bigits_[i];
+ uint64_t tmp = (carry & kBigitMask) + product_low;
+ bigits_[i] = tmp & kBigitMask;
+ carry = (carry >> kBigitSize) + (tmp >> kBigitSize) +
+ (product_high << (32 - kBigitSize));
+ }
+ while (carry != 0) {
+ EnsureCapacity(used_digits_ + 1);
+ bigits_[used_digits_] = carry & kBigitMask;
+ used_digits_++;
+ carry >>= kBigitSize;
+ }
+}
+
+
+void Bignum::MultiplyByPowerOfTen(int exponent) {
+ const uint64_t kFive27 = UINT64_2PART_C(0x6765c793, fa10079d);
+ const uint16_t kFive1 = 5;
+ const uint16_t kFive2 = kFive1 * 5;
+ const uint16_t kFive3 = kFive2 * 5;
+ const uint16_t kFive4 = kFive3 * 5;
+ const uint16_t kFive5 = kFive4 * 5;
+ const uint16_t kFive6 = kFive5 * 5;
+ const uint32_t kFive7 = kFive6 * 5;
+ const uint32_t kFive8 = kFive7 * 5;
+ const uint32_t kFive9 = kFive8 * 5;
+ const uint32_t kFive10 = kFive9 * 5;
+ const uint32_t kFive11 = kFive10 * 5;
+ const uint32_t kFive12 = kFive11 * 5;
+ const uint32_t kFive13 = kFive12 * 5;
+ const uint32_t kFive1_to_12[] =
+ { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
+ kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
+
+ ASSERT(exponent >= 0);
+ if (exponent == 0) return;
+ if (used_digits_ == 0) return;
+
+ int remaining_exponent = exponent;
+ while (remaining_exponent >= 27) {
+ MultiplyByUInt64(kFive27);
+ remaining_exponent -= 27;
+ }
+ while (remaining_exponent >= 13) {
+ MultiplyByUInt32(kFive13);
+ remaining_exponent -= 13;
+ }
+ if (remaining_exponent > 0) {
+ MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
+ }
+ ShiftLeft(exponent);
+}
+
+
+void Bignum::Square() {
+ ASSERT(IsClamped());
+ int product_length = 2 * used_digits_;
+ EnsureCapacity(product_length);
+
+ if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
+ UNIMPLEMENTED();
+ }
+ DoubleChunk accumulator = 0;
+ // First shift the digits so we don't overwrite them.
+ int copy_offset = used_digits_;
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[copy_offset + i] = bigits_[i];
+ }
+ // We have two loops to avoid some 'if's in the loop.
+ for (int i = 0; i < used_digits_; ++i) {
+ // Process temporary digit i with power i.
+ // The sum of the two indices must be equal to i.
+ int bigit_index1 = i;
+ int bigit_index2 = 0;
+ // Sum all of the sub-products.
+ while (bigit_index1 >= 0) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+ for (int i = used_digits_; i < product_length; ++i) {
+ int bigit_index1 = used_digits_ - 1;
+ int bigit_index2 = i - bigit_index1;
+
+ while (bigit_index2 < used_digits_) {
+ Chunk chunk1 = bigits_[copy_offset + bigit_index1];
+ Chunk chunk2 = bigits_[copy_offset + bigit_index2];
+ accumulator += static_cast<DoubleChunk>(chunk1) * chunk2;
+ bigit_index1--;
+ bigit_index2++;
+ }
+ bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask;
+ accumulator >>= kBigitSize;
+ }
+
+ ASSERT(accumulator == 0);
+
+ used_digits_ = product_length;
+ exponent_ *= 2;
+ Clamp();
+}
+
+
+void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) {
+ ASSERT(base != 0);
+ ASSERT(power_exponent >= 0);
+ if (power_exponent == 0) {
+ AssignUInt16(1);
+ return;
+ }
+ Zero();
+ int shifts = 0;
+
+ while ((base & 1) == 0) {
+ base >>= 1;
+ shifts++;
+ }
+ int bit_size = 0;
+ int tmp_base = base;
+ while (tmp_base != 0) {
+ tmp_base >>= 1;
+ bit_size++;
+ }
+ int final_size = bit_size * power_exponent;
+
+ EnsureCapacity(final_size / kBigitSize + 2);
+
+ // Left to Right exponentiation.
+ int mask = 1;
+ while (power_exponent >= mask) mask <<= 1;
+
+ mask >>= 2;
+ uint64_t this_value = base;
+
+ bool delayed_multipliciation = false;
+ const uint64_t max_32bits = 0xFFFFFFFF;
+ while (mask != 0 && this_value <= max_32bits) {
+ this_value = this_value * this_value;
+ // Verify that there is enough space in this_value to perform the
+ // multiplication. The first bit_size bits must be 0.
+ if ((power_exponent & mask) != 0) {
+ uint64_t base_bits_mask =
+ ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1);
+ bool high_bits_zero = (this_value & base_bits_mask) == 0;
+ if (high_bits_zero) {
+ this_value *= base;
+ } else {
+ delayed_multipliciation = true;
+ }
+ }
+ mask >>= 1;
+ }
+ AssignUInt64(this_value);
+ if (delayed_multipliciation) {
+ MultiplyByUInt32(base);
+ }
+
+ // Now do the same thing as a bignum.
+ while (mask != 0) {
+ Square();
+ if ((power_exponent & mask) != 0) {
+ MultiplyByUInt32(base);
+ }
+ mask >>= 1;
+ }
+
+ // And finally add the saved shifts.
+ ShiftLeft(shifts * power_exponent);
+}
+
+
+uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) {
+ ASSERT(IsClamped());
+ ASSERT(other.IsClamped());
+ ASSERT(other.used_digits_ > 0);
+
+ if (BigitLength() < other.BigitLength()) {
+ return 0;
+ }
+
+ Align(other);
+
+ uint16_t result = 0;
+
+ while (BigitLength() > other.BigitLength()) {
+ ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
+ ASSERT(bigits_[used_digits_ - 1] < 0x10000);
+ result += static_cast<uint16_t>(bigits_[used_digits_ - 1]);
+ SubtractTimes(other, bigits_[used_digits_ - 1]);
+ }
+
+ ASSERT(BigitLength() == other.BigitLength());
+
+ Chunk this_bigit = bigits_[used_digits_ - 1];
+ Chunk other_bigit = other.bigits_[other.used_digits_ - 1];
+
+ if (other.used_digits_ == 1) {
+ int quotient = this_bigit / other_bigit;
+ bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
+ ASSERT(quotient < 0x10000);
+ result += static_cast<uint16_t>(quotient);
+ Clamp();
+ return result;
+ }
+
+ int division_estimate = this_bigit / (other_bigit + 1);
+ ASSERT(division_estimate < 0x10000);
+ result += static_cast<uint16_t>(division_estimate);
+ SubtractTimes(other, division_estimate);
+
+ if (other_bigit * (division_estimate + 1) > this_bigit) {
+ return result;
+ }
+
+ while (LessEqual(other, *this)) {
+ SubtractBignum(other);
+ result++;
+ }
+ return result;
+}
+
+
+template<typename S>
+static int SizeInHexChars(S number) {
+ ASSERT(number > 0);
+ int result = 0;
+ while (number != 0) {
+ number >>= 4;
+ result++;
+ }
+ return result;
+}
+
+
+static char HexCharOfValue(int value) {
+ ASSERT(0 <= value && value <= 16);
+ if (value < 10) return static_cast<char>(value + '0');
+ return static_cast<char>(value - 10 + 'A');
+}
+
+
+bool Bignum::ToHexString(char* buffer, int buffer_size) const {
+ ASSERT(IsClamped());
+ // Each bigit must be printable as separate hex-character.
+ ASSERT(kBigitSize % 4 == 0);
+ const int kHexCharsPerBigit = kBigitSize / 4;
+
+ if (used_digits_ == 0) {
+ if (buffer_size < 2) return false;
+ buffer[0] = '0';
+ buffer[1] = '\0';
+ return true;
+ }
+ // We add 1 for the terminating '\0' character.
+ int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit +
+ SizeInHexChars(bigits_[used_digits_ - 1]) + 1;
+ if (needed_chars > buffer_size) return false;
+ int string_index = needed_chars - 1;
+ buffer[string_index--] = '\0';
+ for (int i = 0; i < exponent_; ++i) {
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = '0';
+ }
+ }
+ for (int i = 0; i < used_digits_ - 1; ++i) {
+ Chunk current_bigit = bigits_[i];
+ for (int j = 0; j < kHexCharsPerBigit; ++j) {
+ buffer[string_index--] = HexCharOfValue(current_bigit & 0xF);
+ current_bigit >>= 4;
+ }
+ }
+ // And finally the last bigit.
+ Chunk most_significant_bigit = bigits_[used_digits_ - 1];
+ while (most_significant_bigit != 0) {
+ buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF);
+ most_significant_bigit >>= 4;
+ }
+ return true;
+}
+
+
+Bignum::Chunk Bignum::BigitAt(int index) const {
+ if (index >= BigitLength()) return 0;
+ if (index < exponent_) return 0;
+ return bigits_[index - exponent_];
+}
+
+
+int Bignum::Compare(const Bignum& a, const Bignum& b) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ int bigit_length_a = a.BigitLength();
+ int bigit_length_b = b.BigitLength();
+ if (bigit_length_a < bigit_length_b) return -1;
+ if (bigit_length_a > bigit_length_b) return +1;
+ for (int i = bigit_length_a - 1; i >= std::min(a.exponent_, b.exponent_); --i) {
+ Chunk bigit_a = a.BigitAt(i);
+ Chunk bigit_b = b.BigitAt(i);
+ if (bigit_a < bigit_b) return -1;
+ if (bigit_a > bigit_b) return +1;
+ }
+ return 0;
+}
+
+
+int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) {
+ ASSERT(a.IsClamped());
+ ASSERT(b.IsClamped());
+ ASSERT(c.IsClamped());
+ if (a.BigitLength() < b.BigitLength()) {
+ return PlusCompare(b, a, c);
+ }
+ if (a.BigitLength() + 1 < c.BigitLength()) return -1;
+ if (a.BigitLength() > c.BigitLength()) return +1;
+
+ if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) {
+ return -1;
+ }
+
+ Chunk borrow = 0;
+ // Starting at min_exponent all digits are == 0. So no need to compare them.
+ int min_exponent = std::min(std::min(a.exponent_, b.exponent_), c.exponent_);
+ for (int i = c.BigitLength() - 1; i >= min_exponent; --i) {
+ Chunk chunk_a = a.BigitAt(i);
+ Chunk chunk_b = b.BigitAt(i);
+ Chunk chunk_c = c.BigitAt(i);
+ Chunk sum = chunk_a + chunk_b;
+ if (sum > chunk_c + borrow) {
+ return +1;
+ } else {
+ borrow = chunk_c + borrow - sum;
+ if (borrow > 1) return -1;
+ borrow <<= kBigitSize;
+ }
+ }
+ if (borrow == 0) return 0;
+ return -1;
+}
+
+
+void Bignum::Clamp() {
+ while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
+ used_digits_--;
+ }
+ if (used_digits_ == 0) {
+ // Zero.
+ exponent_ = 0;
+ }
+}
+
+
+bool Bignum::IsClamped() const {
+ return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
+}
+
+
+void Bignum::Zero() {
+ for (int i = 0; i < used_digits_; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ = 0;
+ exponent_ = 0;
+}
+
+
+void Bignum::Align(const Bignum& other) {
+ if (exponent_ > other.exponent_) {
+ int zero_digits = exponent_ - other.exponent_;
+ EnsureCapacity(used_digits_ + zero_digits);
+ for (int i = used_digits_ - 1; i >= 0; --i) {
+ bigits_[i + zero_digits] = bigits_[i];
+ }
+ for (int i = 0; i < zero_digits; ++i) {
+ bigits_[i] = 0;
+ }
+ used_digits_ += zero_digits;
+ exponent_ -= zero_digits;
+ ASSERT(used_digits_ >= 0);
+ ASSERT(exponent_ >= 0);
+ }
+}
+
+
+void Bignum::BigitsShiftLeft(int shift_amount) {
+ ASSERT(shift_amount < kBigitSize);
+ ASSERT(shift_amount >= 0);
+ Chunk carry = 0;
+ for (int i = 0; i < used_digits_; ++i) {
+ Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount);
+ bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
+ carry = new_carry;
+ }
+ if (carry != 0) {
+ bigits_[used_digits_] = carry;
+ used_digits_++;
+ }
+}
+
+
+void Bignum::SubtractTimes(const Bignum& other, int factor) {
+ ASSERT(exponent_ <= other.exponent_);
+ if (factor < 3) {
+ for (int i = 0; i < factor; ++i) {
+ SubtractBignum(other);
+ }
+ return;
+ }
+ Chunk borrow = 0;
+ int exponent_diff = other.exponent_ - exponent_;
+ for (int i = 0; i < other.used_digits_; ++i) {
+ DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i];
+ DoubleChunk remove = borrow + product;
+ Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask);
+ bigits_[i + exponent_diff] = difference & kBigitMask;
+ borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) +
+ (remove >> kBigitSize));
+ }
+ for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
+ if (borrow == 0) return;
+ Chunk difference = bigits_[i] - borrow;
+ bigits_[i] = difference & kBigitMask;
+ borrow = difference >> (kChunkSize - 1);
+ }
+ Clamp();
+}
+
+class PowersOfTenCache {
+public:
+ static const int kDecimalExponentDistance;
+
+ static const int kMinDecimalExponent;
+ static const int kMaxDecimalExponent;
+
+ static void GetCachedPowerForBinaryExponentRange(int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent);
+
+ static void GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent);
+};
+
+struct CachedPower {
+ uint64_t significand;
+ int16_t binary_exponent;
+ int16_t decimal_exponent;
+};
+
+static const CachedPower kCachedPowers[] = {
+ {UINT64_2PART_C(0xfa8fd5a0, 081c0288), -1220, -348},
+ {UINT64_2PART_C(0xbaaee17f, a23ebf76), -1193, -340},
+ {UINT64_2PART_C(0x8b16fb20, 3055ac76), -1166, -332},
+ {UINT64_2PART_C(0xcf42894a, 5dce35ea), -1140, -324},
+ {UINT64_2PART_C(0x9a6bb0aa, 55653b2d), -1113, -316},
+ {UINT64_2PART_C(0xe61acf03, 3d1a45df), -1087, -308},
+ {UINT64_2PART_C(0xab70fe17, c79ac6ca), -1060, -300},
+ {UINT64_2PART_C(0xff77b1fc, bebcdc4f), -1034, -292},
+ {UINT64_2PART_C(0xbe5691ef, 416bd60c), -1007, -284},
+ {UINT64_2PART_C(0x8dd01fad, 907ffc3c), -980, -276},
+ {UINT64_2PART_C(0xd3515c28, 31559a83), -954, -268},
+ {UINT64_2PART_C(0x9d71ac8f, ada6c9b5), -927, -260},
+ {UINT64_2PART_C(0xea9c2277, 23ee8bcb), -901, -252},
+ {UINT64_2PART_C(0xaecc4991, 4078536d), -874, -244},
+ {UINT64_2PART_C(0x823c1279, 5db6ce57), -847, -236},
+ {UINT64_2PART_C(0xc2109436, 4dfb5637), -821, -228},
+ {UINT64_2PART_C(0x9096ea6f, 3848984f), -794, -220},
+ {UINT64_2PART_C(0xd77485cb, 25823ac7), -768, -212},
+ {UINT64_2PART_C(0xa086cfcd, 97bf97f4), -741, -204},
+ {UINT64_2PART_C(0xef340a98, 172aace5), -715, -196},
+ {UINT64_2PART_C(0xb23867fb, 2a35b28e), -688, -188},
+ {UINT64_2PART_C(0x84c8d4df, d2c63f3b), -661, -180},
+ {UINT64_2PART_C(0xc5dd4427, 1ad3cdba), -635, -172},
+ {UINT64_2PART_C(0x936b9fce, bb25c996), -608, -164},
+ {UINT64_2PART_C(0xdbac6c24, 7d62a584), -582, -156},
+ {UINT64_2PART_C(0xa3ab6658, 0d5fdaf6), -555, -148},
+ {UINT64_2PART_C(0xf3e2f893, dec3f126), -529, -140},
+ {UINT64_2PART_C(0xb5b5ada8, aaff80b8), -502, -132},
+ {UINT64_2PART_C(0x87625f05, 6c7c4a8b), -475, -124},
+ {UINT64_2PART_C(0xc9bcff60, 34c13053), -449, -116},
+ {UINT64_2PART_C(0x964e858c, 91ba2655), -422, -108},
+ {UINT64_2PART_C(0xdff97724, 70297ebd), -396, -100},
+ {UINT64_2PART_C(0xa6dfbd9f, b8e5b88f), -369, -92},
+ {UINT64_2PART_C(0xf8a95fcf, 88747d94), -343, -84},
+ {UINT64_2PART_C(0xb9447093, 8fa89bcf), -316, -76},
+ {UINT64_2PART_C(0x8a08f0f8, bf0f156b), -289, -68},
+ {UINT64_2PART_C(0xcdb02555, 653131b6), -263, -60},
+ {UINT64_2PART_C(0x993fe2c6, d07b7fac), -236, -52},
+ {UINT64_2PART_C(0xe45c10c4, 2a2b3b06), -210, -44},
+ {UINT64_2PART_C(0xaa242499, 697392d3), -183, -36},
+ {UINT64_2PART_C(0xfd87b5f2, 8300ca0e), -157, -28},
+ {UINT64_2PART_C(0xbce50864, 92111aeb), -130, -20},
+ {UINT64_2PART_C(0x8cbccc09, 6f5088cc), -103, -12},
+ {UINT64_2PART_C(0xd1b71758, e219652c), -77, -4},
+ {UINT64_2PART_C(0x9c400000, 00000000), -50, 4},
+ {UINT64_2PART_C(0xe8d4a510, 00000000), -24, 12},
+ {UINT64_2PART_C(0xad78ebc5, ac620000), 3, 20},
+ {UINT64_2PART_C(0x813f3978, f8940984), 30, 28},
+ {UINT64_2PART_C(0xc097ce7b, c90715b3), 56, 36},
+ {UINT64_2PART_C(0x8f7e32ce, 7bea5c70), 83, 44},
+ {UINT64_2PART_C(0xd5d238a4, abe98068), 109, 52},
+ {UINT64_2PART_C(0x9f4f2726, 179a2245), 136, 60},
+ {UINT64_2PART_C(0xed63a231, d4c4fb27), 162, 68},
+ {UINT64_2PART_C(0xb0de6538, 8cc8ada8), 189, 76},
+ {UINT64_2PART_C(0x83c7088e, 1aab65db), 216, 84},
+ {UINT64_2PART_C(0xc45d1df9, 42711d9a), 242, 92},
+ {UINT64_2PART_C(0x924d692c, a61be758), 269, 100},
+ {UINT64_2PART_C(0xda01ee64, 1a708dea), 295, 108},
+ {UINT64_2PART_C(0xa26da399, 9aef774a), 322, 116},
+ {UINT64_2PART_C(0xf209787b, b47d6b85), 348, 124},
+ {UINT64_2PART_C(0xb454e4a1, 79dd1877), 375, 132},
+ {UINT64_2PART_C(0x865b8692, 5b9bc5c2), 402, 140},
+ {UINT64_2PART_C(0xc83553c5, c8965d3d), 428, 148},
+ {UINT64_2PART_C(0x952ab45c, fa97a0b3), 455, 156},
+ {UINT64_2PART_C(0xde469fbd, 99a05fe3), 481, 164},
+ {UINT64_2PART_C(0xa59bc234, db398c25), 508, 172},
+ {UINT64_2PART_C(0xf6c69a72, a3989f5c), 534, 180},
+ {UINT64_2PART_C(0xb7dcbf53, 54e9bece), 561, 188},
+ {UINT64_2PART_C(0x88fcf317, f22241e2), 588, 196},
+ {UINT64_2PART_C(0xcc20ce9b, d35c78a5), 614, 204},
+ {UINT64_2PART_C(0x98165af3, 7b2153df), 641, 212},
+ {UINT64_2PART_C(0xe2a0b5dc, 971f303a), 667, 220},
+ {UINT64_2PART_C(0xa8d9d153, 5ce3b396), 694, 228},
+ {UINT64_2PART_C(0xfb9b7cd9, a4a7443c), 720, 236},
+ {UINT64_2PART_C(0xbb764c4c, a7a44410), 747, 244},
+ {UINT64_2PART_C(0x8bab8eef, b6409c1a), 774, 252},
+ {UINT64_2PART_C(0xd01fef10, a657842c), 800, 260},
+ {UINT64_2PART_C(0x9b10a4e5, e9913129), 827, 268},
+ {UINT64_2PART_C(0xe7109bfb, a19c0c9d), 853, 276},
+ {UINT64_2PART_C(0xac2820d9, 623bf429), 880, 284},
+ {UINT64_2PART_C(0x80444b5e, 7aa7cf85), 907, 292},
+ {UINT64_2PART_C(0xbf21e440, 03acdd2d), 933, 300},
+ {UINT64_2PART_C(0x8e679c2f, 5e44ff8f), 960, 308},
+ {UINT64_2PART_C(0xd433179d, 9c8cb841), 986, 316},
+ {UINT64_2PART_C(0x9e19db92, b4e31ba9), 1013, 324},
+ {UINT64_2PART_C(0xeb96bf6e, badf77d9), 1039, 332},
+ {UINT64_2PART_C(0xaf87023b, 9bf0ee6b), 1066, 340},
+};
+
+static const int kCachedPowersLength = ARRAY_SIZE(kCachedPowers);
+static const int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
+static const double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
+// Difference between the decimal exponents in the table above.
+const int PowersOfTenCache::kDecimalExponentDistance = 8;
+const int PowersOfTenCache::kMinDecimalExponent = -348;
+const int PowersOfTenCache::kMaxDecimalExponent = 340;
+
+void PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ int min_exponent,
+ int max_exponent,
+ DiyFp* power,
+ int* decimal_exponent) {
+ int kQ = DiyFp::kSignificandSize;
+ double k = ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
+ int foo = kCachedPowersOffset;
+ int index =
+ (foo + static_cast<int>(k) - 1) / kDecimalExponentDistance + 1;
+ ASSERT(0 <= index && index < kCachedPowersLength);
+ CachedPower cached_power = kCachedPowers[index];
+ ASSERT(min_exponent <= cached_power.binary_exponent);
+ (void) max_exponent; // Mark variable as used.
+ ASSERT(cached_power.binary_exponent <= max_exponent);
+ *decimal_exponent = cached_power.decimal_exponent;
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+}
+
+
+void PowersOfTenCache::GetCachedPowerForDecimalExponent(int requested_exponent,
+ DiyFp* power,
+ int* found_exponent) {
+ ASSERT(kMinDecimalExponent <= requested_exponent);
+ ASSERT(requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
+ int index =
+ (requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
+ CachedPower cached_power = kCachedPowers[index];
+ *power = DiyFp(cached_power.significand, cached_power.binary_exponent);
+ *found_exponent = cached_power.decimal_exponent;
+ ASSERT(*found_exponent <= requested_exponent);
+ ASSERT(requested_exponent < *found_exponent + kDecimalExponentDistance);
+}
+
+enum BignumDtoaMode {
+ BIGNUM_DTOA_SHORTEST,
+ BIGNUM_DTOA_FIXED,
+ BIGNUM_DTOA_PRECISION
+};
+
+static int NormalizedExponent(uint64_t significand, int exponent) {
+ ASSERT(significand != 0);
+ while ((significand & Double::kHiddenBit) == 0) {
+ significand = significand << 1;
+ exponent = exponent - 1;
+ }
+ return exponent;
+}
+
+static int EstimatePower(int exponent);
+
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus);
+
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus);
+
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length);
+
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length);
+
+
+void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits,
+ Vector<char> buffer, int* length, int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+ uint64_t significand;
+ int exponent;
+ bool lower_boundary_is_closer;
+
+ significand = Double(v).Significand();
+ exponent = Double(v).Exponent();
+ lower_boundary_is_closer = Double(v).LowerBoundaryIsCloser();
+
+ bool need_boundary_deltas =
+ (mode == BIGNUM_DTOA_SHORTEST);
+
+ bool is_even = (significand & 1) == 0;
+ int normalized_exponent = NormalizedExponent(significand, exponent);
+ // estimated_power might be too low by 1.
+ int estimated_power = EstimatePower(normalized_exponent);
+
+ if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) {
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -requested_digits;
+ return;
+ }
+
+ Bignum numerator;
+ Bignum denominator;
+ Bignum delta_minus;
+ Bignum delta_plus;
+
+ ASSERT(Bignum::kMaxSignificantBits >= 324*4);
+ InitialScaledStartValues(significand, exponent, lower_boundary_is_closer,
+ estimated_power, need_boundary_deltas,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+
+ FixupMultiply10(estimated_power, is_even, decimal_point,
+ &numerator, &denominator,
+ &delta_minus, &delta_plus);
+
+ switch (mode) {
+ case BIGNUM_DTOA_SHORTEST:
+ GenerateShortestDigits(&numerator, &denominator,
+ &delta_minus, &delta_plus,
+ is_even, buffer, length);
+ break;
+ case BIGNUM_DTOA_FIXED:
+ BignumToFixed(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ case BIGNUM_DTOA_PRECISION:
+ GenerateCountedDigits(requested_digits, decimal_point,
+ &numerator, &denominator,
+ buffer, length);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ buffer[*length] = '\0';
+}
+
+static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus,
+ bool is_even,
+ Vector<char> buffer, int* length) {
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_plus = delta_minus;
+ }
+ *length = 0;
+ for (;;) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9);
+
+ buffer[(*length)++] = static_cast<char>(digit + '0');
+
+ bool in_delta_room_minus;
+ bool in_delta_room_plus;
+ if (is_even) {
+ in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus);
+ } else {
+ in_delta_room_minus = Bignum::Less(*numerator, *delta_minus);
+ }
+ if (is_even) {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_delta_room_plus =
+ Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (!in_delta_room_minus && !in_delta_room_plus) {
+ numerator->Times10();
+ delta_minus->Times10();
+
+ if (delta_minus != delta_plus) {
+ delta_plus->Times10();
+ }
+ } else if (in_delta_room_minus && in_delta_room_plus) {
+
+ int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator);
+ if (compare < 0) {
+ // Remaining digits are less than .5. -> Round down (== do nothing).
+ } else if (compare > 0) {
+ // Remaining digits are more than .5 of denominator. -> Round up.
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ } else {
+ if ((buffer[(*length) - 1] - '0') % 2 == 0) {
+ // Round down => Do nothing.
+ } else {
+ ASSERT(buffer[(*length) - 1] != '9');
+ buffer[(*length) - 1]++;
+ }
+ }
+ return;
+ } else if (in_delta_room_minus) {
+ return;
+ } else { // in_delta_room_plus
+ // Round up
+ ASSERT(buffer[(*length) -1] != '9');
+ buffer[(*length) - 1]++;
+ return;
+ }
+ }
+}
+
+static void GenerateCountedDigits(int count, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char> buffer, int* length) {
+ ASSERT(count >= 0);
+ for (int i = 0; i < count - 1; ++i) {
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ ASSERT(digit <= 9);
+
+ buffer[i] = static_cast<char>(digit + '0');
+ numerator->Times10();
+ }
+
+ uint16_t digit;
+ digit = numerator->DivideModuloIntBignum(*denominator);
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ digit++;
+ }
+ ASSERT(digit <= 10);
+ buffer[count - 1] = static_cast<char>(digit + '0');
+
+ for (int i = count - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+ *length = count;
+}
+
+static void BignumToFixed(int requested_digits, int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Vector<char>(buffer), int* length)
+{
+ if (-(*decimal_point) > requested_digits) {
+ *decimal_point = -requested_digits;
+ *length = 0;
+ return;
+ } else if (-(*decimal_point) == requested_digits) {
+ ASSERT(*decimal_point == -requested_digits);
+
+ denominator->Times10();
+ if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) {
+ buffer[0] = '1';
+ *length = 1;
+ (*decimal_point)++;
+ } else {
+ *length = 0;
+ }
+ return;
+ } else {
+ int needed_digits = (*decimal_point) + requested_digits;
+ GenerateCountedDigits(needed_digits, decimal_point,
+ numerator, denominator,
+ buffer, length);
+ }
+}
+
+static int EstimatePower(int exponent) {
+ const double k1Log10 = 0.30102999566398114; // 1/lg(10)
+
+ // For doubles len(f) == 53 (don't forget the hidden bit).
+ const int kSignificandSize = Double::kSignificandSize;
+ double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
+ return static_cast<int>(estimate);
+}
+
+static void InitialScaledStartValuesPositiveExponent(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ ASSERT(estimated_power >= 0);
+
+ numerator->AssignUInt64(significand);
+ numerator->ShiftLeft(exponent);
+ denominator->AssignPowerUInt16(10, estimated_power);
+
+ if (need_boundary_deltas) {
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ delta_plus->AssignUInt16(1);
+ delta_plus->ShiftLeft(exponent);
+ delta_minus->AssignUInt16(1);
+ delta_minus->ShiftLeft(exponent);
+ }
+}
+
+static void InitialScaledStartValuesNegativeExponentPositivePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ numerator->AssignUInt64(significand);
+ denominator->AssignPowerUInt16(10, estimated_power);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ denominator->ShiftLeft(1);
+ numerator->ShiftLeft(1);
+ delta_plus->AssignUInt16(1);
+ delta_minus->AssignUInt16(1);
+ }
+}
+
+static void InitialScaledStartValuesNegativeExponentNegativePower(
+ uint64_t significand, int exponent,
+ int estimated_power, bool need_boundary_deltas,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus)
+{
+ Bignum* power_ten = numerator;
+ power_ten->AssignPowerUInt16(10, -estimated_power);
+
+ if (need_boundary_deltas) {
+ delta_plus->AssignBignum(*power_ten);
+ delta_minus->AssignBignum(*power_ten);
+ }
+
+ ASSERT(numerator == power_ten);
+ numerator->MultiplyByUInt64(significand);
+
+ denominator->AssignUInt16(1);
+ denominator->ShiftLeft(-exponent);
+
+ if (need_boundary_deltas) {
+ numerator->ShiftLeft(1);
+ denominator->ShiftLeft(1);
+ }
+}
+
+static void InitialScaledStartValues(uint64_t significand,
+ int exponent,
+ bool lower_boundary_is_closer,
+ int estimated_power,
+ bool need_boundary_deltas,
+ Bignum* numerator,
+ Bignum* denominator,
+ Bignum* delta_minus,
+ Bignum* delta_plus)
+{
+ if (exponent >= 0) {
+ InitialScaledStartValuesPositiveExponent(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else if (estimated_power >= 0) {
+ InitialScaledStartValuesNegativeExponentPositivePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ } else {
+ InitialScaledStartValuesNegativeExponentNegativePower(
+ significand, exponent, estimated_power, need_boundary_deltas,
+ numerator, denominator, delta_minus, delta_plus);
+ }
+
+ if (need_boundary_deltas && lower_boundary_is_closer) {
+ denominator->ShiftLeft(1); // *2
+ numerator->ShiftLeft(1); // *2
+ delta_plus->ShiftLeft(1); // *2
+ }
+}
+
+static void FixupMultiply10(int estimated_power, bool is_even,
+ int* decimal_point,
+ Bignum* numerator, Bignum* denominator,
+ Bignum* delta_minus, Bignum* delta_plus) {
+ bool in_range;
+ if (is_even) {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0;
+ } else {
+ in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0;
+ }
+ if (in_range) {
+ *decimal_point = estimated_power + 1;
+ } else {
+ *decimal_point = estimated_power;
+ numerator->Times10();
+ if (Bignum::Equal(*delta_minus, *delta_plus)) {
+ delta_minus->Times10();
+ delta_plus->AssignBignum(*delta_minus);
+ } else {
+ delta_minus->Times10();
+ delta_plus->Times10();
+ }
+ }
+}
+
+enum FastDtoaMode {
+ FAST_DTOA_SHORTEST,
+ FAST_DTOA_PRECISION
+};
+
+static const int kFastDtoaMaximalLength = 17;
+
+bool FastDtoa(double d,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point);
+
+static const int kMinimalTargetExponent = -60;
+static const int kMaximalTargetExponent = -32;
+
+static bool RoundWeed(Vector<char> buffer, int length,
+ uint64_t distance_too_high_w, uint64_t unsafe_interval,
+ uint64_t rest, uint64_t ten_kappa, uint64_t unit)
+{
+ uint64_t small_distance = distance_too_high_w - unit;
+ uint64_t big_distance = distance_too_high_w + unit;
+
+ ASSERT(rest <= unsafe_interval);
+ while (rest < small_distance && // Negated condition 1
+ unsafe_interval - rest >= ten_kappa && // Negated condition 2
+ (rest + ten_kappa < small_distance || // buffer{-1} > w_high
+ small_distance - rest >= rest + ten_kappa - small_distance)) {
+ buffer[length - 1]--;
+ rest += ten_kappa;
+ }
+
+ if (rest < big_distance &&
+ unsafe_interval - rest >= ten_kappa &&
+ (rest + ten_kappa < big_distance ||
+ big_distance - rest > rest + ten_kappa - big_distance)) {
+ return false;
+ }
+
+ return (2 * unit <= rest) && (rest <= unsafe_interval - 4 * unit);
+}
+
+static bool RoundWeedCounted(Vector<char> buffer, int length,
+ uint64_t rest, uint64_t ten_kappa, uint64_t unit,
+ int* kappa)
+{
+ ASSERT(rest < ten_kappa);
+
+ if (unit >= ten_kappa) return false;
+ if (ten_kappa - unit <= unit) return false;
+ if ((ten_kappa - rest > rest) && (ten_kappa - 2 * rest >= 2 * unit)) {
+ return true;
+ }
+
+ if ((rest > unit) && (ten_kappa - (rest - unit) <= (rest - unit))) {
+ buffer[length - 1]++;
+ for (int i = length - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) break;
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*kappa) += 1;
+ }
+ return true;
+ }
+ return false;
+}
+
+static unsigned int const kSmallPowersOfTen[] =
+ {0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
+ 1000000000};
+
+static void BiggestPowerTen(uint32_t number,
+ int number_bits,
+ uint32_t* power,
+ int* exponent_plus_one) {
+ ASSERT(number < (1u << (number_bits + 1)));
+
+ int exponent_plus_one_guess = ((number_bits + 1) * 1233 >> 12);
+ exponent_plus_one_guess++;
+
+ if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
+ exponent_plus_one_guess--;
+ }
+ *power = kSmallPowersOfTen[exponent_plus_one_guess];
+ *exponent_plus_one = exponent_plus_one_guess;
+}
+
+static bool DigitGen(DiyFp low, DiyFp w, DiyFp high, Vector<char> buffer,
+ int* length, int* kappa)
+{
+ ASSERT(low.e() == w.e() && w.e() == high.e());
+ ASSERT(low.f() + 1 <= high.f() - 1);
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+
+ uint64_t unit = 1;
+ DiyFp too_low = DiyFp(low.f() - unit, low.e());
+ DiyFp too_high = DiyFp(high.f() + unit, high.e());
+ DiyFp unsafe_interval = DiyFp::Minus(too_high, too_low);
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+
+ uint32_t integrals = static_cast<uint32_t>(too_high.f() >> -one.e());
+ uint64_t fractionals = too_high.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ integrals %= divisor;
+ (*kappa)--;
+
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+
+ if (rest < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f(),
+ unsafe_interval.f(), rest,
+ static_cast<uint64_t>(divisor) << -one.e(), unit);
+ }
+ divisor /= 10;
+ }
+
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+
+ for (;;) {
+ fractionals *= 10;
+ unit *= 10;
+ unsafe_interval.set_f(unsafe_interval.f() * 10);
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ if (fractionals < unsafe_interval.f()) {
+ return RoundWeed(buffer, *length, DiyFp::Minus(too_high, w).f() * unit,
+ unsafe_interval.f(), fractionals, one.f(), unit);
+ }
+ }
+}
+
+static bool DigitGenCounted(DiyFp w, int requested_digits, Vector<char> buffer,
+ int* length, int* kappa)
+{
+ ASSERT(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
+ ASSERT(kMinimalTargetExponent >= -60);
+ ASSERT(kMaximalTargetExponent <= -32);
+
+ uint64_t w_error = 1;
+ DiyFp one = DiyFp(static_cast<uint64_t>(1) << -w.e(), w.e());
+ uint32_t integrals = static_cast<uint32_t>(w.f() >> -one.e());
+ uint64_t fractionals = w.f() & (one.f() - 1);
+ uint32_t divisor;
+ int divisor_exponent_plus_one;
+ BiggestPowerTen(integrals, DiyFp::kSignificandSize - (-one.e()),
+ &divisor, &divisor_exponent_plus_one);
+ *kappa = divisor_exponent_plus_one;
+ *length = 0;
+
+ while (*kappa > 0) {
+ int digit = integrals / divisor;
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ integrals %= divisor;
+ (*kappa)--;
+ if (requested_digits == 0) break;
+ divisor /= 10;
+ }
+
+ if (requested_digits == 0) {
+ uint64_t rest =
+ (static_cast<uint64_t>(integrals) << -one.e()) + fractionals;
+ return RoundWeedCounted(buffer, *length, rest,
+ static_cast<uint64_t>(divisor) << -one.e(), w_error,
+ kappa);
+ }
+
+ ASSERT(one.e() >= -60);
+ ASSERT(fractionals < one.f());
+ ASSERT(UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF) / 10 >= one.f());
+
+ while (requested_digits > 0 && fractionals > w_error) {
+ fractionals *= 10;
+ w_error *= 10;
+ // Integer division by one.
+ int digit = static_cast<int>(fractionals >> -one.e());
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ requested_digits--;
+ fractionals &= one.f() - 1; // Modulo by one.
+ (*kappa)--;
+ }
+ if (requested_digits != 0) return false;
+ return RoundWeedCounted(buffer, *length, fractionals, one.f(), w_error,
+ kappa);
+}
+
+static bool Grisu3(double v, FastDtoaMode mode, Vector<char> buffer,
+ int* length, int* decimal_exponent)
+{
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp boundary_minus, boundary_plus;
+
+ ASSERT(mode == FAST_DTOA_SHORTEST);
+ Double(v).NormalizedBoundaries(&boundary_minus, &boundary_plus);
+
+ ASSERT(boundary_plus.e() == w.e());
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+ ASSERT(scaled_w.e() ==
+ boundary_plus.e() + ten_mk.e() + DiyFp::kSignificandSize);
+
+ DiyFp scaled_boundary_minus = DiyFp::Times(boundary_minus, ten_mk);
+ DiyFp scaled_boundary_plus = DiyFp::Times(boundary_plus, ten_mk);
+
+ int kappa;
+ bool result = DigitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+static bool Grisu3Counted(double v, int requested_digits, Vector<char> buffer,
+ int* length, int* decimal_exponent)
+{
+ DiyFp w = Double(v).AsNormalizedDiyFp();
+ DiyFp ten_mk; // Cached power of ten: 10^-k
+ int mk; // -k
+ int ten_mk_minimal_binary_exponent =
+ kMinimalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ int ten_mk_maximal_binary_exponent =
+ kMaximalTargetExponent - (w.e() + DiyFp::kSignificandSize);
+ PowersOfTenCache::GetCachedPowerForBinaryExponentRange(
+ ten_mk_minimal_binary_exponent,
+ ten_mk_maximal_binary_exponent,
+ &ten_mk, &mk);
+ ASSERT((kMinimalTargetExponent <= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize) &&
+ (kMaximalTargetExponent >= w.e() + ten_mk.e() +
+ DiyFp::kSignificandSize));
+
+ DiyFp scaled_w = DiyFp::Times(w, ten_mk);
+
+ int kappa;
+ bool result = DigitGenCounted(scaled_w, requested_digits,
+ buffer, length, &kappa);
+ *decimal_exponent = -mk + kappa;
+ return result;
+}
+
+
+bool FastDtoa(double v,
+ FastDtoaMode mode,
+ int requested_digits,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ ASSERT(v > 0);
+ ASSERT(!Double(v).IsSpecial());
+
+ bool result = false;
+ int decimal_exponent = 0;
+ switch (mode) {
+ case FAST_DTOA_SHORTEST:
+ result = Grisu3(v, mode, buffer, length, &decimal_exponent);
+ break;
+ case FAST_DTOA_PRECISION:
+ result = Grisu3Counted(v, requested_digits,
+ buffer, length, &decimal_exponent);
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (result) {
+ *decimal_point = *length + decimal_exponent;
+ buffer[*length] = '\0';
+ }
+ return result;
+}
+
+// Represents a 128bit type. This class should be replaced by a native type on
+// platforms that support 128bit integers.
+class UInt128 {
+ public:
+ UInt128() : high_bits_(0), low_bits_(0) { }
+ UInt128(uint64_t high, uint64_t low) : high_bits_(high), low_bits_(low) { }
+
+ void Multiply(uint32_t multiplicand) {
+ uint64_t accumulator;
+
+ accumulator = (low_bits_ & kMask32) * multiplicand;
+ uint32_t part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (low_bits_ >> 32) * multiplicand;
+ low_bits_ = (accumulator << 32) + part;
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
+ part = static_cast<uint32_t>(accumulator & kMask32);
+ accumulator >>= 32;
+ accumulator = accumulator + (high_bits_ >> 32) * multiplicand;
+ high_bits_ = (accumulator << 32) + part;
+ ASSERT((accumulator >> 32) == 0);
+ }
+
+ void Shift(int shift_amount) {
+ ASSERT(-64 <= shift_amount && shift_amount <= 64);
+ if (shift_amount == 0) {
+ return;
+ } else if (shift_amount == -64) {
+ high_bits_ = low_bits_;
+ low_bits_ = 0;
+ } else if (shift_amount == 64) {
+ low_bits_ = high_bits_;
+ high_bits_ = 0;
+ } else if (shift_amount <= 0) {
+ high_bits_ <<= -shift_amount;
+ high_bits_ += low_bits_ >> (64 + shift_amount);
+ low_bits_ <<= -shift_amount;
+ } else {
+ low_bits_ >>= shift_amount;
+ low_bits_ += high_bits_ << (64 - shift_amount);
+ high_bits_ >>= shift_amount;
+ }
+ }
+
+ // Modifies *this to *this MOD (2^power).
+ // Returns *this DIV (2^power).
+ int DivModPowerOf2(int power) {
+ if (power >= 64) {
+ int result = static_cast<int>(high_bits_ >> (power - 64));
+ high_bits_ -= static_cast<uint64_t>(result) << (power - 64);
+ return result;
+ } else {
+ uint64_t part_low = low_bits_ >> power;
+ uint64_t part_high = high_bits_ << (64 - power);
+ int result = static_cast<int>(part_low + part_high);
+ high_bits_ = 0;
+ low_bits_ -= part_low << power;
+ return result;
+ }
+ }
+
+ bool IsZero() const {
+ return high_bits_ == 0 && low_bits_ == 0;
+ }
+
+ int BitAt(int position) {
+ if (position >= 64) {
+ return static_cast<int>(high_bits_ >> (position - 64)) & 1;
+ } else {
+ return static_cast<int>(low_bits_ >> position) & 1;
+ }
+ }
+
+ private:
+ static const uint64_t kMask32 = 0xFFFFFFFF;
+ // Value == (high_bits_ << 64) + low_bits_
+ uint64_t high_bits_;
+ uint64_t low_bits_;
+};
+
+
+static const int kDoubleSignificandSize = 53; // Includes the hidden bit.
+
+
+static void FillDigits32FixedLength(uint32_t number, int requested_length,
+ Vector<char> buffer, int* length) {
+ for (int i = requested_length - 1; i >= 0; --i) {
+ buffer[(*length) + i] = '0' + number % 10;
+ number /= 10;
+ }
+ *length += requested_length;
+}
+
+
+static void FillDigits32(uint32_t number, Vector<char> buffer, int* length) {
+ int number_length = 0;
+ // We fill the digits in reverse order and exchange them afterwards.
+ while (number != 0) {
+ int digit = number % 10;
+ number /= 10;
+ buffer[(*length) + number_length] = static_cast<char>('0' + digit);
+ number_length++;
+ }
+ // Exchange the digits.
+ int i = *length;
+ int j = *length + number_length - 1;
+ while (i < j) {
+ char tmp = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = tmp;
+ i++;
+ j--;
+ }
+ *length += number_length;
+}
+
+
+static void FillDigits64FixedLength(uint64_t number,
+ Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ FillDigits32FixedLength(part0, 3, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+}
+
+
+static void FillDigits64(uint64_t number, Vector<char> buffer, int* length) {
+ const uint32_t kTen7 = 10000000;
+ // For efficiency cut the number into 3 uint32_t parts, and print those.
+ uint32_t part2 = static_cast<uint32_t>(number % kTen7);
+ number /= kTen7;
+ uint32_t part1 = static_cast<uint32_t>(number % kTen7);
+ uint32_t part0 = static_cast<uint32_t>(number / kTen7);
+
+ if (part0 != 0) {
+ FillDigits32(part0, buffer, length);
+ FillDigits32FixedLength(part1, 7, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else if (part1 != 0) {
+ FillDigits32(part1, buffer, length);
+ FillDigits32FixedLength(part2, 7, buffer, length);
+ } else {
+ FillDigits32(part2, buffer, length);
+ }
+}
+
+
+static void RoundUp(Vector<char> buffer, int* length, int* decimal_point) {
+ // An empty buffer represents 0.
+ if (*length == 0) {
+ buffer[0] = '1';
+ *decimal_point = 1;
+ *length = 1;
+ return;
+ }
+
+ buffer[(*length) - 1]++;
+ for (int i = (*length) - 1; i > 0; --i) {
+ if (buffer[i] != '0' + 10) {
+ return;
+ }
+ buffer[i] = '0';
+ buffer[i - 1]++;
+ }
+
+ if (buffer[0] == '0' + 10) {
+ buffer[0] = '1';
+ (*decimal_point)++;
+ }
+}
+
+static void FillFractionals(uint64_t fractionals, int exponent,
+ int fractional_count, Vector<char> buffer,
+ int* length, int* decimal_point)
+{
+ ASSERT(-128 <= exponent && exponent <= 0);
+
+ if (-exponent <= 64) {
+ ASSERT(fractionals >> 56 == 0);
+ int point = -exponent;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals == 0) break;
+ fractionals *= 5;
+ point--;
+ int digit = static_cast<int>(fractionals >> point);
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ fractionals -= static_cast<uint64_t>(digit) << point;
+ }
+
+ if (((fractionals >> (point - 1)) & 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ } else { // We need 128 bits.
+ ASSERT(64 < -exponent && -exponent <= 128);
+ UInt128 fractionals128 = UInt128(fractionals, 0);
+ fractionals128.Shift(-exponent - 64);
+ int point = 128;
+ for (int i = 0; i < fractional_count; ++i) {
+ if (fractionals128.IsZero()) break;
+ fractionals128.Multiply(5);
+ point--;
+ int digit = fractionals128.DivModPowerOf2(point);
+ ASSERT(digit <= 9);
+ buffer[*length] = static_cast<char>('0' + digit);
+ (*length)++;
+ }
+ if (fractionals128.BitAt(point - 1) == 1) {
+ RoundUp(buffer, length, decimal_point);
+ }
+ }
+}
+
+
+// Removes leading and trailing zeros.
+// If leading zeros are removed then the decimal point position is adjusted.
+static void TrimZeros(Vector<char> buffer, int* length, int* decimal_point) {
+ while (*length > 0 && buffer[(*length) - 1] == '0') {
+ (*length)--;
+ }
+ int first_non_zero = 0;
+ while (first_non_zero < *length && buffer[first_non_zero] == '0') {
+ first_non_zero++;
+ }
+ if (first_non_zero != 0) {
+ for (int i = first_non_zero; i < *length; ++i) {
+ buffer[i - first_non_zero] = buffer[i];
+ }
+ *length -= first_non_zero;
+ *decimal_point -= first_non_zero;
+ }
+}
+
+
+bool FastFixedDtoa(double v,
+ int fractional_count,
+ Vector<char> buffer,
+ int* length,
+ int* decimal_point) {
+ const uint32_t kMaxUInt32 = 0xFFFFFFFF;
+ uint64_t significand = Double(v).Significand();
+ int exponent = Double(v).Exponent();
+
+ if (exponent > 20) return false;
+ if (fractional_count > 20) return false;
+ *length = 0;
+
+ if (exponent + kDoubleSignificandSize > 64) {
+ const uint64_t kFive17 = UINT64_2PART_C(0xB1, A2BC2EC5); // 5^17
+ uint64_t divisor = kFive17;
+ int divisor_power = 17;
+ uint64_t dividend = significand;
+ uint32_t quotient;
+ uint64_t remainder;
+
+ if (exponent > divisor_power) {
+ dividend <<= exponent - divisor_power;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << divisor_power;
+ } else {
+ divisor <<= divisor_power - exponent;
+ quotient = static_cast<uint32_t>(dividend / divisor);
+ remainder = (dividend % divisor) << exponent;
+ }
+ FillDigits32(quotient, buffer, length);
+ FillDigits64FixedLength(remainder, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent >= 0) {
+ // 0 <= exponent <= 11
+ significand <<= exponent;
+ FillDigits64(significand, buffer, length);
+ *decimal_point = *length;
+ } else if (exponent > -kDoubleSignificandSize) {
+ uint64_t integrals = significand >> -exponent;
+ uint64_t fractionals = significand - (integrals << -exponent);
+ if (integrals > kMaxUInt32) {
+ FillDigits64(integrals, buffer, length);
+ } else {
+ FillDigits32(static_cast<uint32_t>(integrals), buffer, length);
+ }
+ *decimal_point = *length;
+ FillFractionals(fractionals, exponent, fractional_count,
+ buffer, length, decimal_point);
+ } else if (exponent < -128) {
+ // This configuration (with at most 20 digits) means that all digits must be
+ // 0.
+ ASSERT(fractional_count <= 20);
+ buffer[0] = '\0';
+ *length = 0;
+ *decimal_point = -fractional_count;
+ } else {
+ *decimal_point = 0;
+ FillFractionals(significand, exponent, fractional_count,
+ buffer, length, decimal_point);
+ }
+ TrimZeros(buffer, length, decimal_point);
+ buffer[*length] = '\0';
+ if ((*length) == 0) {
+ *decimal_point = -fractional_count;
+ }
+ return true;
+}
+
+static const int kMaxExactDoubleIntegerDecimalDigits = 15;
+static const int kMaxUint64DecimalDigits = 19;
+
+static const int kMaxDecimalPower = 309;
+static const int kMinDecimalPower = -324;
+
+// 2^64 = 18446744073709551616
+static const uint64_t kMaxUint64 = UINT64_2PART_C(0xFFFFFFFF, FFFFFFFF);
+static const int kMaxSignificantDecimalDigits = 780;
+
+static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) {
+ for (int i = 0; i < buffer.length(); i++) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(i, buffer.length());
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) {
+ for (int i = buffer.length() - 1; i >= 0; --i) {
+ if (buffer[i] != '0') {
+ return buffer.SubVector(0, i + 1);
+ }
+ }
+ return Vector<const char>(buffer.start(), 0);
+}
+
+
+static void CutToMaxSignificantDigits(Vector<const char> buffer,
+ int exponent,
+ char* significant_buffer,
+ int* significant_exponent) {
+ for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) {
+ significant_buffer[i] = buffer[i];
+ }
+
+ ASSERT(buffer[buffer.length() - 1] != '0');
+
+ significant_buffer[kMaxSignificantDecimalDigits - 1] = '1';
+ *significant_exponent =
+ exponent + (buffer.length() - kMaxSignificantDecimalDigits);
+}
+
+static void TrimAndCut(Vector<const char> buffer, int exponent,
+ char* buffer_copy_space, int space_size,
+ Vector<const char>* trimmed, int* updated_exponent) {
+ Vector<const char> left_trimmed = TrimLeadingZeros(buffer);
+ Vector<const char> right_trimmed = TrimTrailingZeros(left_trimmed);
+ exponent += left_trimmed.length() - right_trimmed.length();
+ if (right_trimmed.length() > kMaxSignificantDecimalDigits) {
+ (void) space_size; // Mark variable as used.
+ ASSERT(space_size >= kMaxSignificantDecimalDigits);
+ CutToMaxSignificantDigits(right_trimmed, exponent,
+ buffer_copy_space, updated_exponent);
+ *trimmed = Vector<const char>(buffer_copy_space,
+ kMaxSignificantDecimalDigits);
+ } else {
+ *trimmed = right_trimmed;
+ *updated_exponent = exponent;
+ }
+}
+
+static uint64_t ReadUint64(Vector<const char> buffer,
+ int* number_of_read_digits) {
+ uint64_t result = 0;
+ int i = 0;
+ while (i < buffer.length() && result <= (kMaxUint64 / 10 - 1)) {
+ int digit = buffer[i++] - '0';
+ ASSERT(0 <= digit && digit <= 9);
+ result = 10 * result + digit;
+ }
+ *number_of_read_digits = i;
+ return result;
+}
+
+static void ReadDiyFp(Vector<const char> buffer,
+ DiyFp* result,
+ int* remaining_decimals) {
+ int read_digits;
+ uint64_t significand = ReadUint64(buffer, &read_digits);
+ if (buffer.length() == read_digits) {
+ *result = DiyFp(significand, 0);
+ *remaining_decimals = 0;
+ } else {
+ // Round the significand.
+ if (buffer[read_digits] >= '5') {
+ significand++;
+ }
+ // Compute the binary exponent.
+ int exponent = 0;
+ *result = DiyFp(significand, exponent);
+ *remaining_decimals = buffer.length() - read_digits;
+ }
+}
+
+static DiyFp AdjustmentPowerOfTen(int exponent) {
+ ASSERT(0 < exponent);
+ ASSERT(exponent < PowersOfTenCache::kDecimalExponentDistance);
+ // Simply hardcode the remaining powers for the given decimal exponent
+ // distance.
+ ASSERT(PowersOfTenCache::kDecimalExponentDistance == 8);
+ switch (exponent) {
+ case 1: return DiyFp(UINT64_2PART_C(0xa0000000, 00000000), -60);
+ case 2: return DiyFp(UINT64_2PART_C(0xc8000000, 00000000), -57);
+ case 3: return DiyFp(UINT64_2PART_C(0xfa000000, 00000000), -54);
+ case 4: return DiyFp(UINT64_2PART_C(0x9c400000, 00000000), -50);
+ case 5: return DiyFp(UINT64_2PART_C(0xc3500000, 00000000), -47);
+ case 6: return DiyFp(UINT64_2PART_C(0xf4240000, 00000000), -44);
+ case 7: return DiyFp(UINT64_2PART_C(0x98968000, 00000000), -40);
+ default:
+ UNREACHABLE();
+ }
+}
+
+static bool DiyFpStrtod(Vector<const char> buffer,
+ int exponent,
+ double* result) {
+ DiyFp input;
+ int remaining_decimals;
+ ReadDiyFp(buffer, &input, &remaining_decimals);
+
+ const int kDenominatorLog = 3;
+ const int kDenominator = 1 << kDenominatorLog;
+ // Move the remaining decimals into the exponent.
+ exponent += remaining_decimals;
+ int error = (remaining_decimals == 0 ? 0 : kDenominator / 2);
+
+ int old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ ASSERT(exponent <= PowersOfTenCache::kMaxDecimalExponent);
+ if (exponent < PowersOfTenCache::kMinDecimalExponent) {
+ *result = 0.0;
+ return true;
+ }
+ DiyFp cached_power;
+ int cached_decimal_exponent;
+ PowersOfTenCache::GetCachedPowerForDecimalExponent(exponent,
+ &cached_power,
+ &cached_decimal_exponent);
+
+ if (cached_decimal_exponent != exponent) {
+ int adjustment_exponent = exponent - cached_decimal_exponent;
+ DiyFp adjustment_power = AdjustmentPowerOfTen(adjustment_exponent);
+ input.Multiply(adjustment_power);
+ if (kMaxUint64DecimalDigits - buffer.length() >= adjustment_exponent) {
+ // The product of input with the adjustment power fits into a 64 bit
+ // integer.
+ ASSERT(DiyFp::kSignificandSize == 64);
+ } else {
+ // The adjustment power is exact. There is hence only an error of 0.5.
+ error += kDenominator / 2;
+ }
+ }
+
+ input.Multiply(cached_power);
+
+ int error_b = kDenominator / 2;
+ int error_ab = (error == 0 ? 0 : 1); // We round up to 1.
+ int fixed_error = kDenominator / 2;
+ error += error_b + error_ab + fixed_error;
+
+ old_e = input.e();
+ input.Normalize();
+ error <<= old_e - input.e();
+
+ int order_of_magnitude = DiyFp::kSignificandSize + input.e();
+ int effective_significand_size =
+ Double::SignificandSizeForOrderOfMagnitude(order_of_magnitude);
+ int precision_digits_count =
+ DiyFp::kSignificandSize - effective_significand_size;
+ if (precision_digits_count + kDenominatorLog >= DiyFp::kSignificandSize) {
+ int shift_amount = (precision_digits_count + kDenominatorLog) -
+ DiyFp::kSignificandSize + 1;
+ input.set_f(input.f() >> shift_amount);
+ input.set_e(input.e() + shift_amount);
+ error = (error >> shift_amount) + 1 + kDenominator;
+ precision_digits_count -= shift_amount;
+ }
+
+ ASSERT(DiyFp::kSignificandSize == 64);
+ ASSERT(precision_digits_count < 64);
+ uint64_t one64 = 1;
+ uint64_t precision_bits_mask = (one64 << precision_digits_count) - 1;
+ uint64_t precision_bits = input.f() & precision_bits_mask;
+ uint64_t half_way = one64 << (precision_digits_count - 1);
+ precision_bits *= kDenominator;
+ half_way *= kDenominator;
+ DiyFp rounded_input(input.f() >> precision_digits_count,
+ input.e() + precision_digits_count);
+ if (precision_bits >= half_way + error) {
+ rounded_input.set_f(rounded_input.f() + 1);
+ }
+
+ *result = Double(rounded_input).value();
+ if (half_way - error < precision_bits && precision_bits < half_way + error) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+static int CompareBufferWithDiyFp(Vector<const char> buffer,
+ int exponent,
+ DiyFp diy_fp) {
+ ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1);
+ ASSERT(buffer.length() + exponent > kMinDecimalPower);
+ ASSERT(buffer.length() <= kMaxSignificantDecimalDigits);
+ ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits);
+
+ Bignum buffer_bignum;
+ Bignum diy_fp_bignum;
+ buffer_bignum.AssignDecimalString(buffer);
+ diy_fp_bignum.AssignUInt64(diy_fp.f());
+ if (exponent >= 0) {
+ buffer_bignum.MultiplyByPowerOfTen(exponent);
+ } else {
+ diy_fp_bignum.MultiplyByPowerOfTen(-exponent);
+ }
+ if (diy_fp.e() > 0) {
+ diy_fp_bignum.ShiftLeft(diy_fp.e());
+ } else {
+ buffer_bignum.ShiftLeft(-diy_fp.e());
+ }
+ return Bignum::Compare(buffer_bignum, diy_fp_bignum);
+}
+
+static bool ComputeGuess(Vector<const char> trimmed, int exponent,
+ double* guess)
+{
+ if (trimmed.length() == 0) {
+ *guess = 0.0;
+ return true;
+ }
+ if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) {
+ *guess = Double::Infinity();
+ return true;
+ }
+ if (exponent + trimmed.length() <= kMinDecimalPower) {
+ *guess = 0.0;
+ return true;
+ }
+
+ if (DiyFpStrtod(trimmed, exponent, guess)) {
+ return true;
+ }
+ if (*guess == Double::Infinity()) {
+ return true;
+ }
+ return false;
+}
+
+double Strtod(Vector<const char> buffer, int exponent)
+{
+ char copy_buffer[kMaxSignificantDecimalDigits];
+ Vector<const char> trimmed;
+ int updated_exponent;
+ TrimAndCut(buffer, exponent, copy_buffer, kMaxSignificantDecimalDigits,
+ &trimmed, &updated_exponent);
+ exponent = updated_exponent;
+
+ double guess;
+ bool is_correct = ComputeGuess(trimmed, exponent, &guess);
+ if (is_correct) return guess;
+
+ DiyFp upper_boundary = Double(guess).UpperBoundary();
+ int comparison = CompareBufferWithDiyFp(trimmed, exponent, upper_boundary);
+ if (comparison < 0) {
+ return guess;
+ } else if (comparison > 0) {
+ return Double(guess).NextDouble();
+ } else if ((Double(guess).Significand() & 1) == 0) {
+ // Round towards even.
+ return guess;
+ } else {
+ return Double(guess).NextDouble();
+ }
+}
+
+class DoubleToStringConverter {
+public:
+ static const int kMaxFixedDigitsBeforePoint = 60;
+ static const int kMaxFixedDigitsAfterPoint = 60;
+ static const int kMaxExponentialDigits = 120;
+ static const int kMinPrecisionDigits = 1;
+ static const int kMaxPrecisionDigits = 120;
+
+ enum Flags {
+ NO_FLAGS = 0,
+ EMIT_POSITIVE_EXPONENT_SIGN = 1,
+ EMIT_TRAILING_DECIMAL_POINT = 2,
+ EMIT_TRAILING_ZERO_AFTER_POINT = 4,
+ UNIQUE_ZERO = 8
+ };
+
+ DoubleToStringConverter(int flags,
+ const char* infinity_symbol,
+ const char* nan_symbol,
+ char exponent_character,
+ int decimal_in_shortest_low,
+ int decimal_in_shortest_high,
+ int max_leading_padding_zeroes_in_precision_mode,
+ int max_trailing_padding_zeroes_in_precision_mode)
+ : flags_(flags),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol),
+ exponent_character_(exponent_character),
+ decimal_in_shortest_low_(decimal_in_shortest_low),
+ decimal_in_shortest_high_(decimal_in_shortest_high),
+ max_leading_padding_zeroes_in_precision_mode_(
+ max_leading_padding_zeroes_in_precision_mode),
+ max_trailing_padding_zeroes_in_precision_mode_(
+ max_trailing_padding_zeroes_in_precision_mode) {
+ // When 'trailing zero after the point' is set, then 'trailing point'
+ // must be set too.
+ ASSERT(((flags & EMIT_TRAILING_DECIMAL_POINT) != 0) ||
+ !((flags & EMIT_TRAILING_ZERO_AFTER_POINT) != 0));
+ }
+
+ bool ToShortest(double value, std::string &s) const {
+ return ToShortestIeeeNumber(value, s, SHORTEST);
+ }
+
+ bool ToFixed(double value,
+ int requested_digits,
+ std::string &s) const;
+
+ bool ToExponential(double value,
+ int requested_digits,
+ std::string &s) const;
+
+ bool ToPrecision(double value,
+ int precision,
+ std::string &s) const;
+
+ enum DtoaMode {
+ SHORTEST,
+ FIXED, // Produce a fixed number of digits after the decimal point
+ PRECISION // Fixed number of digits (independent of the decimal point)
+ };
+
+ static const int kBase10MaximalLength = 17;
+
+ // The result should be interpreted as buffer * 10^(point-length).
+ static void DoubleToAscii(double v,
+ DtoaMode mode,
+ int requested_digits,
+ char* buffer,
+ int buffer_length,
+ bool* sign,
+ int* length,
+ int* point);
+
+ private:
+ // Implementation for ToShortest.
+ bool ToShortestIeeeNumber(double value,
+ std::string &s,
+ DtoaMode mode) const;
+
+ bool HandleSpecialValues(double value, std::string &s) const;
+
+ void CreateExponentialRepresentation(const char* decimal_digits,
+ int length,
+ int exponent,
+ std::string &s) const;
+
+ void CreateDecimalRepresentation(const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ std::string &s) const;
+
+ const int flags_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+ const char exponent_character_;
+ const int decimal_in_shortest_low_;
+ const int decimal_in_shortest_high_;
+ const int max_leading_padding_zeroes_in_precision_mode_;
+ const int max_trailing_padding_zeroes_in_precision_mode_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DoubleToStringConverter);
+};
+
+
+class StringToDoubleConverter {
+ public:
+ enum Flags {
+ NO_FLAGS = 0,
+ ALLOW_HEX = 1,
+ ALLOW_OCTALS = 2,
+ ALLOW_TRAILING_JUNK = 4,
+ ALLOW_LEADING_SPACES = 8,
+ ALLOW_TRAILING_SPACES = 16,
+ ALLOW_SPACES_AFTER_SIGN = 32
+ };
+
+ StringToDoubleConverter(int flags,
+ double empty_string_value,
+ double junk_string_value,
+ const char* infinity_symbol,
+ const char* nan_symbol)
+ : flags_(flags),
+ empty_string_value_(empty_string_value),
+ junk_string_value_(junk_string_value),
+ infinity_symbol_(infinity_symbol),
+ nan_symbol_(nan_symbol) {
+ }
+
+ double StringToDouble(const char* buffer,
+ int length,
+ int* processed_characters_count) const;
+
+ private:
+ const int flags_;
+ const double empty_string_value_;
+ const double junk_string_value_;
+ const char* const infinity_symbol_;
+ const char* const nan_symbol_;
+
+ double StringToIeee(const char *start_pointer,
+ int length,
+ int* processed_characters_count) const;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringToDoubleConverter);
+};
+
+bool DoubleToStringConverter::HandleSpecialValues(
+ double value,
+ std::string &result) const {
+ Double double_inspect(value);
+ if (double_inspect.IsInfinite()) {
+ if (infinity_symbol_ == NULL) return false;
+ if (value < 0) {
+ result += '-';
+ }
+ result += infinity_symbol_;
+ return true;
+ }
+ if (double_inspect.IsNan()) {
+ if (nan_symbol_ == NULL) return false;
+ result = nan_symbol_;
+ return true;
+ }
+ return false;
+}
+
+
+void DoubleToStringConverter::CreateExponentialRepresentation(
+ const char* decimal_digits,
+ int length,
+ int exponent,
+ std::string &result) const {
+ ASSERT(length != 0);
+ result += decimal_digits[0];
+ if (length != 1) {
+ result += '.';
+ result.append(&decimal_digits[1], length-1);
+ }
+ result += exponent_character_;
+ if (exponent < 0) {
+ result += '-';
+ exponent = -exponent;
+ } else {
+ if ((flags_ & EMIT_POSITIVE_EXPONENT_SIGN) != 0) {
+ result += '+';
+ }
+ }
+ if (exponent == 0) {
+ result += '0';
+ return;
+ }
+ ASSERT(exponent < 1e4);
+ const int kMaxExponentLength = 5;
+ char buffer[kMaxExponentLength + 1];
+ buffer[kMaxExponentLength] = '\0';
+ int first_char_pos = kMaxExponentLength;
+ while (exponent > 0) {
+ buffer[--first_char_pos] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ result.append(&buffer[first_char_pos],
+ kMaxExponentLength - first_char_pos);
+}
+
+
+void DoubleToStringConverter::CreateDecimalRepresentation(
+ const char* decimal_digits,
+ int length,
+ int decimal_point,
+ int digits_after_point,
+ std::string &result) const {
+ // Create a representation that is padded with zeros if needed.
+ if (decimal_point <= 0) {
+ // "0.00000decimal_rep".
+ result += '0';
+ if (digits_after_point > 0) {
+ result += '.';
+ result.append(-decimal_point, '0');
+ ASSERT(length <= digits_after_point - (-decimal_point));
+ result.append(decimal_digits, length);
+ int remaining_digits = digits_after_point - (-decimal_point) - length;
+ result.append(remaining_digits, '0');
+ }
+ } else if (decimal_point >= length) {
+ // "decimal_rep0000.00000" or "decimal_rep.0000"
+ result.append(decimal_digits, length);
+ result.append(decimal_point - length, '0');
+ if (digits_after_point > 0) {
+ result += '.';
+ result.append(digits_after_point, '0');
+ }
+ } else {
+ // "decima.l_rep000"
+ ASSERT(digits_after_point > 0);
+ result.append(decimal_digits, decimal_point);
+ result += '.';
+ ASSERT(length - decimal_point <= digits_after_point);
+ result.append(&decimal_digits[decimal_point], length - decimal_point);
+ int remaining_digits = digits_after_point - (length - decimal_point);
+ result.append(remaining_digits, '0');
+ }
+ if (digits_after_point == 0) {
+ if ((flags_ & EMIT_TRAILING_DECIMAL_POINT) != 0) {
+ result += '.';
+ }
+ if ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) {
+ result += '0';
+ }
+ }
+}
+
+
+bool DoubleToStringConverter::ToShortestIeeeNumber(
+ double value,
+ std::string &result,
+ DoubleToStringConverter::DtoaMode mode) const {
+ ASSERT(mode == SHORTEST);
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ int decimal_point;
+ bool sign;
+ const int kDecimalRepCapacity = kBase10MaximalLength + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, mode, 0, decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = (flags_ & UNIQUE_ZERO) != 0;
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ int exponent = decimal_point - 1;
+ if ((decimal_in_shortest_low_ <= exponent) &&
+ (exponent < decimal_in_shortest_high_)) {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ std::max(0, decimal_rep_length - decimal_point),
+ result);
+ } else {
+ CreateExponentialRepresentation(decimal_rep, decimal_rep_length, exponent, result);
+ }
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToFixed(double value,
+ int requested_digits,
+ std::string &result) const
+{
+ ASSERT(kMaxFixedDigitsBeforePoint == 60);
+ const double kFirstNonFixed = 1e60;
+
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (requested_digits > kMaxFixedDigitsAfterPoint) return false;
+ if (value >= kFirstNonFixed || value <= -kFirstNonFixed) return false;
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add space for the '\0' byte.
+ const int kDecimalRepCapacity =
+ kMaxFixedDigitsBeforePoint + kMaxFixedDigitsAfterPoint + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+ DoubleToAscii(value, FIXED, requested_digits,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ requested_digits, result);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToExponential(
+ double value,
+ int requested_digits,
+ std::string &result) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (requested_digits < -1) return false;
+ if (requested_digits > kMaxExponentialDigits) return false;
+
+ int decimal_point;
+ bool sign;
+ // Add space for digit before the decimal point and the '\0' character.
+ const int kDecimalRepCapacity = kMaxExponentialDigits + 2;
+ ASSERT(kDecimalRepCapacity > kBase10MaximalLength);
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ if (requested_digits == -1) {
+ DoubleToAscii(value, SHORTEST, 0,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ } else {
+ DoubleToAscii(value, PRECISION, requested_digits + 1,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ ASSERT(decimal_rep_length <= requested_digits + 1);
+
+ for (int i = decimal_rep_length; i < requested_digits + 1; ++i) {
+ decimal_rep[i] = '0';
+ }
+ decimal_rep_length = requested_digits + 1;
+ }
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ int exponent = decimal_point - 1;
+ CreateExponentialRepresentation(decimal_rep,
+ decimal_rep_length,
+ exponent, result);
+ return true;
+}
+
+
+bool DoubleToStringConverter::ToPrecision(double value,
+ int precision,
+ std::string &result) const {
+ if (Double(value).IsSpecial()) {
+ return HandleSpecialValues(value, result);
+ }
+
+ if (precision < kMinPrecisionDigits || precision > kMaxPrecisionDigits) {
+ return false;
+ }
+
+ // Find a sufficiently precise decimal representation of n.
+ int decimal_point;
+ bool sign;
+ // Add one for the terminating null character.
+ const int kDecimalRepCapacity = kMaxPrecisionDigits + 1;
+ char decimal_rep[kDecimalRepCapacity];
+ int decimal_rep_length;
+
+ DoubleToAscii(value, PRECISION, precision,
+ decimal_rep, kDecimalRepCapacity,
+ &sign, &decimal_rep_length, &decimal_point);
+ ASSERT(decimal_rep_length <= precision);
+
+ bool unique_zero = ((flags_ & UNIQUE_ZERO) != 0);
+ if (sign && (value != 0.0 || !unique_zero)) {
+ result += '-';
+ }
+
+ // The exponent if we print the number as x.xxeyyy. That is with the
+ // decimal point after the first digit.
+ int exponent = decimal_point - 1;
+
+ int extra_zero = ((flags_ & EMIT_TRAILING_ZERO_AFTER_POINT) != 0) ? 1 : 0;
+ if ((-decimal_point + 1 > max_leading_padding_zeroes_in_precision_mode_) ||
+ (decimal_point - precision + extra_zero >
+ max_trailing_padding_zeroes_in_precision_mode_)) {
+ for (int i = decimal_rep_length; i < precision; ++i) {
+ decimal_rep[i] = '0';
+ }
+
+ CreateExponentialRepresentation(decimal_rep,
+ precision,
+ exponent,
+ result);
+ } else {
+ CreateDecimalRepresentation(decimal_rep, decimal_rep_length, decimal_point,
+ std::max(0, precision - decimal_point),
+ result);
+ }
+ return true;
+}
+
+
+static BignumDtoaMode DtoaToBignumDtoaMode(
+ DoubleToStringConverter::DtoaMode dtoa_mode) {
+ switch (dtoa_mode) {
+ case DoubleToStringConverter::SHORTEST: return BIGNUM_DTOA_SHORTEST;
+ case DoubleToStringConverter::FIXED: return BIGNUM_DTOA_FIXED;
+ case DoubleToStringConverter::PRECISION: return BIGNUM_DTOA_PRECISION;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void DoubleToStringConverter::DoubleToAscii(double v, DtoaMode mode, int requested_digits,
+ char* buffer, int buffer_length,
+ bool* sign, int* length, int* point)
+{
+ Vector<char> vector(buffer, buffer_length);
+ ASSERT(!Double(v).IsSpecial());
+ ASSERT(mode == SHORTEST || requested_digits >= 0);
+
+ if (Double(v).Sign() < 0) {
+ *sign = true;
+ v = -v;
+ } else {
+ *sign = false;
+ }
+
+ if (mode == PRECISION && requested_digits == 0) {
+ vector[0] = '\0';
+ *length = 0;
+ return;
+ }
+
+ if (v == 0) {
+ vector[0] = '0';
+ vector[1] = '\0';
+ *length = 1;
+ *point = 1;
+ return;
+ }
+
+ bool fast_worked;
+ switch (mode) {
+ case SHORTEST:
+ fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, vector, length, point);
+ break;
+ case FIXED:
+ fast_worked = FastFixedDtoa(v, requested_digits, vector, length, point);
+ break;
+ case PRECISION:
+ fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits,
+ vector, length, point);
+ break;
+ default:
+ fast_worked = false;
+ UNREACHABLE();
+ }
+ if (fast_worked) return;
+
+ // If the fast dtoa didn't succeed use the slower bignum version.
+ BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode);
+ BignumDtoa(v, bignum_mode, requested_digits, vector, length, point);
+ vector[*length] = '\0';
+}
+
+template <class Iterator>
+static bool ConsumeSubString(Iterator* current,
+ Iterator end,
+ const char* substring) {
+ ASSERT(**current == *substring);
+ for (substring++; *substring != '\0'; substring++) {
+ ++*current;
+ if (*current == end || **current != *substring) return false;
+ }
+ ++*current;
+ return true;
+}
+
+const int kMaxSignificantDigits = 772;
+
+static const char kWhitespaceTable7[] = { 32, 13, 10, 9, 11, 12 };
+static const int kWhitespaceTable7Length = ARRAY_SIZE(kWhitespaceTable7);
+
+static bool isWhitespace(int x) {
+ if (x < 128) {
+ for (int i = 0; i < kWhitespaceTable7Length; i++) {
+ if (kWhitespaceTable7[i] == x) return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if a nonspace found and false if the end has reached.
+template <class Iterator>
+static inline bool AdvanceToNonspace(Iterator* current, Iterator end) {
+ while (*current != end) {
+ if (!isWhitespace(**current)) return true;
+ ++*current;
+ }
+ return false;
+}
+
+static bool isDigit(int x, int radix) {
+ return (x >= '0' && x <= '9' && x < '0' + radix)
+ || (radix > 10 && x >= 'a' && x < 'a' + radix - 10)
+ || (radix > 10 && x >= 'A' && x < 'A' + radix - 10);
+}
+
+static double SignedZero(bool sign) {
+ return sign ? -0.0 : 0.0;
+}
+
+static bool IsDecimalDigitForRadix(int c, int radix) {
+ return '0' <= c && c <= '9' && (c - '0') < radix;
+}
+
+static bool IsCharacterDigitForRadix(int c, int radix, char a_character) {
+ return radix > 10 && c >= a_character && c < a_character + radix - 10;
+}
+
+template <int radix_log_2, class Iterator>
+static double RadixStringToIeee(Iterator* current, Iterator end,
+ bool sign, bool allow_trailing_junk, double junk_string_value,
+ bool* result_is_junk)
+{
+ ASSERT(*current != end);
+
+ const int kSignificandSize = Double::kSignificandSize;
+
+ *result_is_junk = true;
+
+ // Skip leading 0s.
+ while (**current == '0') {
+ ++(*current);
+ if (*current == end) {
+ *result_is_junk = false;
+ return SignedZero(sign);
+ }
+ }
+
+ int64_t number = 0;
+ int exponent = 0;
+ const int radix = (1 << radix_log_2);
+
+ do {
+ int digit;
+ if (IsDecimalDigitForRadix(**current, radix)) {
+ digit = static_cast<char>(**current) - '0';
+ } else if (IsCharacterDigitForRadix(**current, radix, 'a')) {
+ digit = static_cast<char>(**current) - 'a' + 10;
+ } else if (IsCharacterDigitForRadix(**current, radix, 'A')) {
+ digit = static_cast<char>(**current) - 'A' + 10;
+ } else {
+ if (allow_trailing_junk || !AdvanceToNonspace(current, end)) {
+ break;
+ } else {
+ return junk_string_value;
+ }
+ }
+
+ number = number * radix + digit;
+ int overflow = static_cast<int>(number >> kSignificandSize);
+ if (overflow != 0) {
+ // Overflow occurred. Need to determine which direction to round the
+ // result.
+ int overflow_bits_count = 1;
+ while (overflow > 1) {
+ overflow_bits_count++;
+ overflow >>= 1;
+ }
+
+ int dropped_bits_mask = ((1 << overflow_bits_count) - 1);
+ int dropped_bits = static_cast<int>(number) & dropped_bits_mask;
+ number >>= overflow_bits_count;
+ exponent = overflow_bits_count;
+
+ bool zero_tail = true;
+ for (;;) {
+ ++(*current);
+ if (*current == end || !isDigit(**current, radix)) break;
+ zero_tail = zero_tail && **current == '0';
+ exponent += radix_log_2;
+ }
+
+ if (!allow_trailing_junk && AdvanceToNonspace(current, end)) {
+ return junk_string_value;
+ }
+
+ int middle_value = (1 << (overflow_bits_count - 1));
+ if (dropped_bits > middle_value) {
+ number++; // Rounding up.
+ } else if (dropped_bits == middle_value) {
+ // Rounding to even to consistency with decimals: half-way case rounds
+ // up if significant part is odd and down otherwise.
+ if ((number & 1) != 0 || !zero_tail) {
+ number++; // Rounding up.
+ }
+ }
+
+ // Rounding up may cause overflow.
+ if ((number & ((int64_t)1 << kSignificandSize)) != 0) {
+ exponent++;
+ number >>= 1;
+ }
+ break;
+ }
+ ++(*current);
+ } while (*current != end);
+
+ ASSERT(number < ((int64_t)1 << kSignificandSize));
+ ASSERT(static_cast<int64_t>(static_cast<double>(number)) == number);
+
+ *result_is_junk = false;
+
+ if (exponent == 0) {
+ if (sign) {
+ if (number == 0) return -0.0;
+ number = -number;
+ }
+ return static_cast<double>(number);
+ }
+
+ ASSERT(number != 0);
+ return Double(DiyFp(number, exponent)).value();
+}
+
+double StringToDoubleConverter::StringToIeee(
+ const char *input,
+ int length,
+ int* processed_characters_count) const {
+ const char *current = input;
+ const char *end = input + length;
+
+ *processed_characters_count = 0;
+
+ const bool allow_trailing_junk = (flags_ & ALLOW_TRAILING_JUNK) != 0;
+ const bool allow_leading_spaces = (flags_ & ALLOW_LEADING_SPACES) != 0;
+ const bool allow_trailing_spaces = (flags_ & ALLOW_TRAILING_SPACES) != 0;
+ const bool allow_spaces_after_sign = (flags_ & ALLOW_SPACES_AFTER_SIGN) != 0;
+
+ if (current == end) return empty_string_value_;
+
+ if (allow_leading_spaces || allow_trailing_spaces) {
+ if (!AdvanceToNonspace(&current, end)) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return empty_string_value_;
+ }
+ if (!allow_leading_spaces && (input != current)) {
+ return junk_string_value_;
+ }
+ }
+
+ const int kBufferSize = kMaxSignificantDigits + 10;
+ char buffer[kBufferSize]; // NOLINT: size is known at compile time.
+ int buffer_pos = 0;
+
+ int exponent = 0;
+ int significant_digits = 0;
+ int insignificant_digits = 0;
+ bool nonzero_digit_dropped = false;
+
+ bool sign = false;
+
+ if (*current == '+' || *current == '-') {
+ sign = (*current == '-');
+ ++current;
+ const char *next_non_space = current;
+
+ if (!AdvanceToNonspace(&next_non_space, end)) return junk_string_value_;
+ if (!allow_spaces_after_sign && (current != next_non_space)) {
+ return junk_string_value_;
+ }
+ current = next_non_space;
+ }
+
+ if (infinity_symbol_ != NULL) {
+ if (*current == infinity_symbol_[0]) {
+ if (!ConsumeSubString(&current, end, infinity_symbol_)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ ASSERT(buffer_pos == 0);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::Infinity() : Double::Infinity();
+ }
+ }
+
+ if (nan_symbol_ != NULL) {
+ if (*current == nan_symbol_[0]) {
+ if (!ConsumeSubString(&current, end, nan_symbol_)) {
+ return junk_string_value_;
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+
+ ASSERT(buffer_pos == 0);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign ? -Double::NaN() : Double::NaN();
+ }
+ }
+
+ bool leading_zero = false;
+ if (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+
+ leading_zero = true;
+
+ // It could be hexadecimal value.
+ if ((flags_ & ALLOW_HEX) && (*current == 'x' || *current == 'X')) {
+ ++current;
+ if (current == end || !isDigit(*current, 16)) {
+ return junk_string_value_; // "0x".
+ }
+
+ bool result_is_junk;
+ double result = RadixStringToIeee<4>(&current,
+ end,
+ sign,
+ allow_trailing_junk,
+ junk_string_value_,
+ &result_is_junk);
+ if (!result_is_junk) {
+ if (allow_trailing_spaces) AdvanceToNonspace(&current, end);
+ *processed_characters_count = static_cast<int>(current - input);
+ }
+ return result;
+ }
+
+ // Ignore leading zeros in the integer part.
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ }
+ }
+
+ bool octal = leading_zero && (flags_ & ALLOW_OCTALS) != 0;
+
+ // Copy significant digits of the integer part (if any) to the buffer.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ // Will later check if it's an octal in the buffer.
+ } else {
+ insignificant_digits++; // Move the digit into the exponential part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ octal = octal && *current < '8';
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+
+ if (significant_digits == 0) {
+ octal = false;
+ }
+
+ if (*current == '.') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+
+ ++current;
+ if (current == end) {
+ if (significant_digits == 0 && !leading_zero) {
+ return junk_string_value_;
+ } else {
+ goto parsing_done;
+ }
+ }
+
+ if (significant_digits == 0) {
+ // octal = false;
+ // Integer part consists of 0 or is absent. Significant digits start after
+ // leading zeros (if any).
+ while (*current == '0') {
+ ++current;
+ if (current == end) {
+ *processed_characters_count = static_cast<int>(current - input);
+ return SignedZero(sign);
+ }
+ exponent--; // Move this 0 into the exponent.
+ }
+ }
+
+ // There is a fractional part.
+ // We don't emit a '.', but adjust the exponent instead.
+ while (*current >= '0' && *current <= '9') {
+ if (significant_digits < kMaxSignificantDigits) {
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos++] = static_cast<char>(*current);
+ significant_digits++;
+ exponent--;
+ } else {
+ // Ignore insignificant digits in the fractional part.
+ nonzero_digit_dropped = nonzero_digit_dropped || *current != '0';
+ }
+ ++current;
+ if (current == end) goto parsing_done;
+ }
+ }
+
+ if (!leading_zero && exponent == 0 && significant_digits == 0) {
+ // If leading_zeros is true then the string contains zeros.
+ // If exponent < 0 then string was [+-]\.0*...
+ // If significant_digits != 0 the string is not equal to 0.
+ // Otherwise there are no digits in the string.
+ return junk_string_value_;
+ }
+
+ // Parse exponential part.
+ if (*current == 'e' || *current == 'E') {
+ if (octal && !allow_trailing_junk) return junk_string_value_;
+ if (octal) goto parsing_done;
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ char sign = '+';
+ if (*current == '+' || *current == '-') {
+ sign = static_cast<char>(*current);
+ ++current;
+ if (current == end) {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+ }
+
+ if (current == end || *current < '0' || *current > '9') {
+ if (allow_trailing_junk) {
+ goto parsing_done;
+ } else {
+ return junk_string_value_;
+ }
+ }
+
+ const int max_exponent = INT_MAX / 2;
+ ASSERT(-max_exponent / 2 <= exponent && exponent <= max_exponent / 2);
+ int num = 0;
+ do {
+ // Check overflow.
+ int digit = *current - '0';
+ if (num >= max_exponent / 10
+ && !(num == max_exponent / 10 && digit <= max_exponent % 10)) {
+ num = max_exponent;
+ } else {
+ num = num * 10 + digit;
+ }
+ ++current;
+ } while (current != end && *current >= '0' && *current <= '9');
+
+ exponent += (sign == '-' ? -num : num);
+ }
+
+ if (!(allow_trailing_spaces || allow_trailing_junk) && (current != end)) {
+ return junk_string_value_;
+ }
+ if (!allow_trailing_junk && AdvanceToNonspace(&current, end)) {
+ return junk_string_value_;
+ }
+ if (allow_trailing_spaces) {
+ AdvanceToNonspace(&current, end);
+ }
+
+ parsing_done:
+ exponent += insignificant_digits;
+
+ if (octal) {
+ double result;
+ bool result_is_junk;
+ char* start = buffer;
+ result = RadixStringToIeee<3>(&start,
+ buffer + buffer_pos,
+ sign,
+ allow_trailing_junk,
+ junk_string_value_,
+ &result_is_junk);
+ ASSERT(!result_is_junk);
+ *processed_characters_count = static_cast<int>(current - input);
+ return result;
+ }
+
+ if (nonzero_digit_dropped) {
+ buffer[buffer_pos++] = '1';
+ exponent--;
+ }
+
+ ASSERT(buffer_pos < kBufferSize);
+ buffer[buffer_pos] = '\0';
+
+ double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent);
+ *processed_characters_count = static_cast<int>(current - input);
+ return sign? -converted: converted;
+}
+
+
+double StringToDoubleConverter::StringToDouble(
+ const char* buffer,
+ int length,
+ int* processed_characters_count) const {
+ return StringToIeee(buffer, length, processed_characters_count);
+}
+
+} // end anonymous namespace
+
+std::string format_coord_shortest(Coord x)
+{
+ char buf[20];
+ bool sign;
+ int length, point;
+
+ DoubleToStringConverter::DoubleToAscii(x, DoubleToStringConverter::SHORTEST,
+ 0, buf, 20, &sign, &length, &point);
+
+ int exponent = point - length;
+
+ std::string ret;
+ ret.reserve(32);
+
+ if (sign) {
+ ret += '-';
+ }
+
+ if (exponent == 0) {
+ // return digits without any changes
+ ret += buf;
+ } else if (point >= 0 && point <= length) {
+ // insert decimal point
+ ret.append(buf, point);
+ ret += '.';
+ ret.append(&buf[point], length - point);
+ } else if (exponent > 0 && exponent <= 2) {
+ // add trailing zeroes
+ ret += buf;
+ ret.append(exponent, '0');
+ } else if (point >= -3 && point <= -1) {
+ // add leading zeroes
+ ret += '.';
+ ret.append(-point, '0');
+ ret += buf;
+ } else {
+ // exponential form
+ ret += buf;
+ ret += 'e';
+ if (exponent < 0) {
+ ret += '-';
+ exponent = -exponent;
+ }
+
+ /* Convert exponent by hand.
+ * Using ostringstream is ~3x slower */
+ int const buflen = 6;
+ int i = 0;
+ char expdigits[buflen+1];
+ expdigits[buflen] = 0;
+
+ for (; exponent && i < buflen; ++i) {
+ expdigits[buflen - 1 - i] = '0' + (exponent % 10);
+ exponent /= 10;
+ }
+ ret.append(&expdigits[buflen - i]);
+ }
+
+ return ret;
+}
+
+std::string format_coord_nice(Coord x)
+{
+ static DoubleToStringConverter conv(
+ DoubleToStringConverter::UNIQUE_ZERO,
+ "inf", "NaN", 'e', -6, 21, 0, 0);
+ std::string ret;
+ ret.reserve(32);
+ conv.ToShortest(x, ret);
+ return ret;
+}
+
+Coord parse_coord(std::string const &s)
+{
+ static StringToDoubleConverter conv(
+ StringToDoubleConverter::ALLOW_LEADING_SPACES |
+ StringToDoubleConverter::ALLOW_TRAILING_SPACES |
+ StringToDoubleConverter::ALLOW_SPACES_AFTER_SIGN,
+ 0.0, nan(""), "inf", "NaN");
+ int dummy;
+ return conv.StringToDouble(s.c_str(), s.length(), &dummy);
+}
+
+} // namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/coord.h b/src/2geom/coord.h
index 78a852f32..9cc220db7 100644
--- a/src/2geom/coord.h
+++ b/src/2geom/coord.h
@@ -1,8 +1,10 @@
-/**
- * \file
- * \brief Defines the Coord "real" type with sufficient precision for coordinates.
+/** @file
+ * @brief Integral and real coordinate types and some basic utilities
*//*
- * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Authors:
+ * Nathan Hurst <njh@mail.csse.monash.edu.au>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ * Copyright 2006-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -34,34 +36,92 @@
#include <cmath>
#include <limits>
+#include <string>
+#include <functional>
#include <boost/operators.hpp>
#include <2geom/forward.h>
namespace Geom {
-/** @brief 2D axis enumeration (X or Y). */
+/** @brief 2D axis enumeration (X or Y).
+ * @ingroup Primitives */
enum Dim2 { X=0, Y=1 };
-/**
- * @brief Floating point type used to store coordinates.
- *
- * You may safely assume that double (or even float) provides enough precision for storing
- * on-canvas points, and hence that double provides enough precision for dot products of
- * differences of on-canvas points.
- */
+/** @brief Get the other (perpendicular) dimension.
+ * @ingroup Primitives */
+inline Dim2 other_dimension(Dim2 d) { return d == Y ? X : Y; }
+
+// TODO: make a smarter implementation with C++11
+template <typename T>
+struct D2Traits {
+ typedef typename T::D1Value D1Value;
+ typedef typename T::D1Reference D1Reference;
+ typedef typename T::D1ConstReference D1ConstReference;
+};
+
+/** @brief Axis extraction functor.
+ * For use with things such as Boost's transform_iterator.
+ * @ingroup Utilities */
+template <Dim2 D, typename T>
+struct GetAxis {
+ typedef typename D2Traits<T>::D1Value result_type;
+ typedef T argument_type;
+ typename D2Traits<T>::D1Value operator()(T const &a) const {
+ return a[D];
+ }
+};
+
+/** @brief Floating point type used to store coordinates.
+ * @ingroup Primitives */
typedef double Coord;
+
+/** @brief Type used for integral coordinates.
+ * @ingroup Primitives */
typedef int IntCoord;
-const Coord EPSILON = 1e-5; //1e-18;
+/** @brief Default "acceptably small" value.
+ * @ingroup Primitives */
+const Coord EPSILON = 1e-6; //1e-18;
+/** @brief Get a value representing infinity.
+ * @ingroup Primitives */
inline Coord infinity() { return std::numeric_limits<Coord>::infinity(); }
-//IMPL: NearConcept
+/** @brief Nearness predicate for values.
+ * @ingroup Primitives */
inline bool are_near(Coord a, Coord b, double eps=EPSILON) { return a-b <= eps && a-b >= -eps; }
inline bool rel_error_bound(Coord a, Coord b, double eps=EPSILON) { return a <= eps*b && a >= -eps*b; }
+/** @brief Numerically stable linear interpolation.
+ * @ingroup Primitives */
+inline Coord lerp(Coord t, Coord a, Coord b) {
+ return (1 - t) * a + t * b;
+}
+
+/** @brief Traits class used with coordinate types.
+ * Defines point, interval and rectangle types for the given coordinate type.
+ * @ingroup Utilities */
template <typename C>
-struct CoordTraits {};
+struct CoordTraits {
+ typedef D2<C> PointType;
+ typedef GenericInterval<C> IntervalType;
+ typedef GenericOptInterval<C> OptIntervalType;
+ typedef GenericRect<C> RectType;
+ typedef GenericOptRect<C> OptRectType;
+
+ typedef
+ boost::equality_comparable< IntervalType
+ , boost::orable< IntervalType
+ > >
+ IntervalOps;
+
+ typedef
+ boost::equality_comparable< RectType
+ , boost::orable< RectType
+ , boost::orable< RectType, OptRectType
+ > > >
+ RectOps;
+};
// NOTE: operator helpers for Rect and Interval are defined here.
// This is to avoid increasing their size through multiple inheritance.
@@ -120,6 +180,24 @@ struct CoordTraits<Coord> {
RectOps;
};
+/** @brief Convert coordinate to shortest possible string.
+ * @return The shortest string that parses back to the original value.
+ * @relates Coord */
+std::string format_coord_shortest(Coord x);
+
+/** @brief Convert coordinate to human-readable string.
+ * Unlike format_coord_shortest, this function will not omit a leading zero
+ * before a decimal point or use small negative exponents. The output format
+ * is similar to Javascript functions.
+ * @relates Coord */
+std::string format_coord_nice(Coord x);
+
+/** @brief Parse coordinate string.
+ * When using this function in conjunction with format_coord_shortest()
+ * or format_coord_nice(), the value is guaranteed to be preserved exactly.
+ * @relates Coord */
+Coord parse_coord(std::string const &s);
+
} // end namespace Geom
#endif // LIB2GEOM_SEEN_COORD_H
diff --git a/src/2geom/crossing.cpp b/src/2geom/crossing.cpp
index 513327271..e27a2fc43 100644
--- a/src/2geom/crossing.cpp
+++ b/src/2geom/crossing.cpp
@@ -28,7 +28,7 @@ double wrap_dist(double from, double to, double size, bool rev) {
}
}
/*
-CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs) {
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs) {
std::vector<Point> locs;
CrossingGraph ret;
for(unsigned i = 0; i < crs.size(); i++) {
diff --git a/src/2geom/crossing.h b/src/2geom/crossing.h
index d5012ae2b..425fa58f5 100644
--- a/src/2geom/crossing.h
+++ b/src/2geom/crossing.h
@@ -33,14 +33,14 @@
*
*/
-#ifndef __GEOM_CROSSING_H
-#define __GEOM_CROSSING_H
+#ifndef LIB2GEOM_SEEN_CROSSING_H
+#define LIB2GEOM_SEEN_CROSSING_H
#include <vector>
#include <2geom/rect.h>
-#include <2geom/sweep.h>
+#include <2geom/sweep-bounds.h>
#include <boost/optional/optional.hpp>
-#include <2geom/path.h>
+#include <2geom/pathvector.h>
namespace Geom {
@@ -99,7 +99,7 @@ struct TimeOrder {
};
class Path;
-CrossingGraph create_crossing_graph(std::vector<Path> const &p, Crossings const &crs);
+CrossingGraph create_crossing_graph(PathVector const &p, Crossings const &crs);
*/
/*inline bool are_near(Crossing a, Crossing b) {
@@ -143,11 +143,24 @@ std::vector<Rect> bounds(Path const &a);
inline void sort_crossings(Crossings &cr, unsigned ix) { std::sort(cr.begin(), cr.end(), CrossingOrder(ix)); }
+template <typename T>
+struct CrossingTraits {
+ typedef std::vector<T> VectorT;
+ static inline VectorT init(T const &x) { return VectorT(1, x); }
+};
+template <>
+struct CrossingTraits<Path> {
+ typedef PathVector VectorT;
+ static inline VectorT vector_one(Path const &x) { return VectorT(x); }
+};
+
template<typename T>
struct Crosser {
+ typedef typename CrossingTraits<T>::VectorT VectorT;
virtual ~Crosser() {}
- virtual Crossings crossings(T const &a, T const &b) { return crossings(std::vector<T>(1,a), std::vector<T>(1,b))[0]; }
- virtual CrossingSet crossings(std::vector<T> const &a, std::vector<T> const &b) {
+ virtual Crossings crossings(T const &a, T const &b) {
+ return crossings(CrossingTraits<T>::vector_one(a), CrossingTraits<T>::vector_one(b))[0]; }
+ virtual CrossingSet crossings(VectorT const &a, VectorT const &b) {
CrossingSet results(a.size() + b.size(), Crossings());
std::vector<std::vector<unsigned> > cull = sweep_bounds(bounds(a), bounds(b));
@@ -185,7 +198,7 @@ CrossingSet reverse_tb(CrossingSet const &cr, unsigned split, std::vector<double
void clean(Crossings &cr_a, Crossings &cr_b);
void delete_duplicates(Crossings &crs);
-}
+} // end namespace Geom
#endif
/*
diff --git a/src/2geom/curve.cpp b/src/2geom/curve.cpp
index c0f2bf883..b45228514 100644
--- a/src/2geom/curve.cpp
+++ b/src/2geom/curve.cpp
@@ -32,62 +32,26 @@
*/
#include <2geom/curve.h>
-#include <2geom/nearest-point.h>
+#include <2geom/exception.h>
+#include <2geom/nearest-time.h>
#include <2geom/sbasis-geometric.h>
+#include <2geom/sbasis-to-bezier.h>
#include <2geom/ord.h>
+#include <2geom/path-sink.h>
+
+//#include <iostream>
namespace Geom
{
-int CurveHelpers::root_winding(Curve const &c, Point p) {
- std::vector<double> ts = c.roots(p[Y], Y);
-
- if(ts.empty()) return 0;
-
- double const fudge = 0.01; //fudge factor used on first and last
-
- std::sort(ts.begin(), ts.end());
-
- // winding determined by crossings at roots
- int wind=0;
- // previous time
- double pt = ts.front() - fudge;
- for ( std::vector<double>::iterator ti = ts.begin()
- ; ti != ts.end()
- ; ++ti )
- {
- double t = *ti;
- if ( t <= 0. || t >= 1. ) continue; //skip endpoint roots
- if ( c.valueAt(t, X) > p[X] ) { // root is ray intersection
- // Get t of next:
- std::vector<double>::iterator next = ti;
- ++next;
- double nt;
- if(next == ts.end()) nt = t + fudge; else nt = *next;
-
- // Check before in time and after in time for positions
- // Currently we're using the average times between next and previous segs
- Cmp after_to_ray = cmp(c.valueAt((t + nt) / 2, Y), p[Y]);
- Cmp before_to_ray = cmp(c.valueAt((t + pt) / 2, Y), p[Y]);
- // if y is included, these will have opposite values, giving order.
- Cmp dt = cmp(after_to_ray, before_to_ray);
- if(dt != EQUAL_TO) //Should always be true, but yah never know..
- wind += dt;
- pt = t;
- }
- }
-
- return wind;
-}
-
-Coord Curve::nearestPoint(Point const& p, Coord a, Coord b) const
+Coord Curve::nearestTime(Point const& p, Coord a, Coord b) const
{
- return nearest_point(p, toSBasis(), a, b);
+ return nearest_time(p, toSBasis(), a, b);
}
-std::vector<Coord> Curve::allNearestPoints(Point const& p, Coord from, Coord to) const
+std::vector<Coord> Curve::allNearestTimes(Point const& p, Coord from, Coord to) const
{
- return all_nearest_points(p, toSBasis(), from, to);
+ return all_nearest_times(p, toSBasis(), from, to);
}
Coord Curve::length(Coord tolerance) const
@@ -95,6 +59,97 @@ Coord Curve::length(Coord tolerance) const
return ::Geom::length(toSBasis(), tolerance);
}
+int Curve::winding(Point const &p) const
+{
+ try {
+ std::vector<Coord> ts = roots(p[Y], Y);
+ if(ts.empty()) return 0;
+ std::sort(ts.begin(), ts.end());
+
+ // skip endpoint roots when they are local maxima on the Y axis
+ // this follows the convention used in other winding routines,
+ // i.e. that the bottommost coordinate is not part of the shape
+ bool ingore_0 = unitTangentAt(0)[Y] <= 0;
+ bool ignore_1 = unitTangentAt(1)[Y] >= 0;
+
+ int wind = 0;
+ for (std::size_t i = 0; i < ts.size(); ++i) {
+ Coord t = ts[i];
+ //std::cout << t << std::endl;
+ if ((t == 0 && ingore_0) || (t == 1 && ignore_1)) continue;
+ if (valueAt(t, X) > p[X]) { // root is ray intersection
+ Point tangent = unitTangentAt(t);
+ if (tangent[Y] > 0) {
+ // at the point of intersection, curve goes in +Y direction,
+ // so it winds in the direction of positive angles
+ ++wind;
+ } else if (tangent[Y] < 0) {
+ --wind;
+ }
+ }
+ }
+ return wind;
+ } catch (InfiniteSolutions const &e) {
+ // this means we encountered a line segment exactly coincident with the point
+ // skip, since this will be taken care of by endpoint roots in other segments
+ return 0;
+ }
+}
+
+std::vector<CurveIntersection> Curve::intersect(Curve const &/*other*/, Coord /*eps*/) const
+{
+ // TODO: approximate as Bezier
+ THROW_NOTIMPLEMENTED();
+}
+
+std::vector<CurveIntersection> Curve::intersectSelf(Coord eps) const
+{
+ std::vector<CurveIntersection> result;
+ // Monotonic segments cannot have self-intersections.
+ // Thus, we can split the curve at roots and intersect the portions.
+ std::vector<Coord> splits;
+ std::auto_ptr<Curve> deriv(derivative());
+ splits = deriv->roots(0, X);
+ if (splits.empty()) {
+ return result;
+ }
+ deriv.reset();
+ splits.push_back(1.);
+
+ boost::ptr_vector<Curve> parts;
+ Coord previous = 0;
+ for (unsigned i = 0; i < splits.size(); ++i) {
+ if (splits[i] == 0.) continue;
+ parts.push_back(portion(previous, splits[i]));
+ previous = splits[i];
+ }
+
+ Coord prev_i = 0;
+ for (unsigned i = 0; i < parts.size()-1; ++i) {
+ Interval dom_i(prev_i, splits[i]);
+ prev_i = splits[i];
+
+ Coord prev_j = 0;
+ for (unsigned j = i+1; j < parts.size(); ++j) {
+ Interval dom_j(prev_j, splits[j]);
+ prev_j = splits[j];
+
+ std::vector<CurveIntersection> xs = parts[i].intersect(parts[j], eps);
+ for (unsigned k = 0; k < xs.size(); ++k) {
+ // to avoid duplicated intersections, skip values at exactly 1
+ if (xs[k].first == 1. || xs[k].second == 1.) continue;
+
+ Coord ti = dom_i.valueAt(xs[k].first);
+ Coord tj = dom_j.valueAt(xs[k].second);
+
+ CurveIntersection real(ti, tj, xs[k].point());
+ result.push_back(real);
+ }
+ }
+ }
+ return result;
+}
+
Point Curve::unitTangentAt(Coord t, unsigned n) const
{
std::vector<Point> derivs = pointAndDerivatives(t, n);
@@ -108,6 +163,16 @@ Point Curve::unitTangentAt(Coord t, unsigned n) const
return Point (0,0);
};
+void Curve::feed(PathSink &sink, bool moveto_initial) const
+{
+ std::vector<Point> pts;
+ sbasis_to_bezier(pts, toSBasis(), 2); //TODO: use something better!
+ if (moveto_initial) {
+ sink.moveTo(initialPoint());
+ }
+ sink.curveTo(pts[0], pts[1], pts[2]);
+}
+
} // namespace Geom
/*
diff --git a/src/2geom/curve.h b/src/2geom/curve.h
index 172fd7ddc..abbdb1100 100644
--- a/src/2geom/curve.h
+++ b/src/2geom/curve.h
@@ -35,27 +35,23 @@
*/
-#ifndef _2GEOM_CURVE_H_
-#define _2GEOM_CURVE_H_
+#ifndef LIB2GEOM_SEEN_CURVE_H
+#define LIB2GEOM_SEEN_CURVE_H
#include <vector>
+#include <boost/operators.hpp>
#include <2geom/coord.h>
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/sbasis.h>
#include <2geom/d2.h>
#include <2geom/affine.h>
+#include <2geom/intersection.h>
-namespace Geom
-{
-
-class Curve;
-
-struct CurveHelpers {
-protected:
- static int root_winding(Curve const &c, Point p);
-};
+namespace Geom {
+class PathSink;
+typedef Intersection<> CurveIntersection;
/**
* @brief Abstract continuous curve on a plane defined on [0,1].
@@ -77,7 +73,9 @@ protected:
*
* @ingroup Curves
*/
-class Curve : private CurveHelpers {
+class Curve
+ : boost::equality_comparable<Curve>
+{
public:
virtual ~Curve() {}
@@ -86,26 +84,41 @@ public:
/** @brief Retrieve the start of the curve.
* @return The point corresponding to \f$\mathbf{C}(0)\f$. */
virtual Point initialPoint() const = 0;
+
/** Retrieve the end of the curve.
* @return The point corresponding to \f$\mathbf{C}(1)\f$. */
virtual Point finalPoint() const = 0;
+
/** @brief Check whether the curve has exactly zero length.
* @return True if the curve's initial point is exactly the same as its final point, and it contains
- * no other points (its value set contains only one element).
- */
+ * no other points (its value set contains only one element). */
virtual bool isDegenerate() const = 0;
+
+ /// Check whether the curve is a line segment.
+ virtual bool isLineSegment() const { return false; }
+
+ /** @brief Get the interval of allowed time values.
+ * @return \f$[0, 1]\f$ */
+ virtual Interval timeRange() const {
+ Interval tr(0, 1);
+ return tr;
+ }
+
/** @brief Evaluate the curve at a specified time value.
* @param t Time value
* @return \f$\mathbf{C}(t)\f$ */
virtual Point pointAt(Coord t) const { return pointAndDerivatives(t, 0).front(); }
+
/** @brief Evaluate one of the coordinates at the specified time value.
* @param t Time value
* @param d The dimension to evaluate
* @return The specified coordinate of \f$\mathbf{C}(t)\f$ */
virtual Coord valueAt(Coord t, Dim2 d) const { return pointAt(t)[d]; }
+
/** @brief Evaluate the function at the specified time value. Allows curves to be used
* as functors. */
virtual Point operator() (Coord t) const { return pointAt(t); }
+
/** @brief Evaluate the curve and its derivatives.
* This will return a vector that contains the value of the curve and the specified number
* of derivatives. However, the returned vector might contain less elements than specified
@@ -125,6 +138,7 @@ public:
* type.
* @param p New starting point of the curve */
virtual void setInitial(Point const &v) = 0;
+
/** @brief Change the ending point of the curve.
* After calling this method, it is guaranteed that \f$\mathbf{C}(0) = \mathbf{p}\f$,
* and the curve is still continuous. The precise new shape of the curve varies
@@ -140,12 +154,15 @@ public:
* but it might not be the smallest such rectangle. This method is usually fast.
* @return A rectangle that contains all points belonging to the curve. */
virtual Rect boundsFast() const = 0;
+
/** @brief Compute the curve's exact bounding box.
* This method can be dramatically slower than boundsExact() depending on the curve type.
* @return The smallest possible rectangle containing all of the curve's points. */
virtual Rect boundsExact() const = 0;
+
// I have no idea what the 'deg' parameter is for, so this is undocumented for now.
virtual OptRect boundsLocal(OptInterval const &i, unsigned deg) const = 0;
+
/** @brief Compute the bounding box of a part of the curve.
* Since this method returns the smallest possible bounding rectangle of the specified portion,
* it can also be rather slow.
@@ -163,21 +180,31 @@ public:
* @return Pointer to a newly allocated curve, identical to the original */
virtual Curve *duplicate() const = 0;
+ /** @brief Transform this curve by an affine transformation.
+ * Because of this method, all curve types must be closed under affine
+ * transformations.
+ * @param m Affine describing the affine transformation */
+ void transform(Affine const &m) {
+ *this *= m;
+ }
+
+ virtual void operator*=(Translate const &tr) { *this *= Affine(tr); }
+ virtual void operator*=(Scale const &s) { *this *= Affine(s); }
+ virtual void operator*=(Rotate const &r) { *this *= Affine(r); }
+ virtual void operator*=(HShear const &hs) { *this *= Affine(hs); }
+ virtual void operator*=(VShear const &vs) { *this *= Affine(vs); }
+ virtual void operator*=(Zoom const &z) { *this *= Affine(z); }
+ virtual void operator*=(Affine const &m) = 0;
+
/** @brief Create a curve transformed by an affine transformation.
- * This method returns a new curve instead modifying the existing one, because some curve
- * types are not closed under affine transformations. The returned curve may be of different
- * underlying type (as is the case for horizontal and vertical line segments).
+ * This method returns a new curve instead modifying the existing one.
* @param m Affine describing the affine transformation
* @return Pointer to a new, transformed curve */
- virtual Curve *transformed(Affine const &m) const = 0;
-
- /** @brief Translate the curve (i.e. displace by Point)
- * This method modifies the curve; all curve types are closed under
- * translations (the result can be expressed in its own curve type).
- * This function yields the same result as transformed(m).
- * @param p Point by which to translate the curve
- * @return reference to self */
- virtual Curve &operator*=(Translate const &m) = 0;
+ virtual Curve *transformed(Affine const &m) const {
+ Curve *ret = duplicate();
+ ret->transform(m);
+ return ret;
+ }
/** @brief Create a curve that corresponds to a part of this curve.
* For \f$a > b\f$, the returned portion will be reversed with respect to the original.
@@ -190,13 +217,16 @@ public:
* - \f$\mathbf{D}[ [0, 1] ] = \mathbf{C}[ [a?b] ]\f$,
* where \f$[a?b] = [\min(a, b), \max(a, b)]\f$ */
virtual Curve *portion(Coord a, Coord b) const = 0;
+
/** @brief A version of that accepts an Interval. */
Curve *portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
/** @brief Create a reversed version of this curve.
* The result corresponds to <code>portion(1, 0)</code>, but this method might be faster.
* @return Pointer to a new curve \f$\mathbf{D}\f$ such that
* \f$\forall_{x \in [0, 1]} \mathbf{D}(x) = \mathbf{C}(1-x)\f$ */
virtual Curve *reverse() const { return portion(1, 0); }
+
/** @brief Create a derivative of this curve.
* It's best to think of the derivative in physical terms: if the curve describes
* the position of some object on the plane from time \f$t=0\f$ to \f$t=1\f$ as said in the
@@ -215,22 +245,26 @@ public:
* @param b Maximum time value to consider; \f$a < b\f$
* @return \f$q \in [a, b]: ||\mathbf{C}(q) - \mathbf{p}|| =
\inf(\{r \in \mathbb{R} : ||\mathbf{C}(r) - \mathbf{p}||\})\f$ */
- virtual Coord nearestPoint( Point const& p, Coord a = 0, Coord b = 1 ) const;
+ virtual Coord nearestTime( Point const& p, Coord a = 0, Coord b = 1 ) const;
+
/** @brief A version that takes an Interval. */
- Coord nearestPoint(Point const &p, Interval const &i) const {
- return nearestPoint(p, i.min(), i.max());
+ Coord nearestTime(Point const &p, Interval const &i) const {
+ return nearestTime(p, i.min(), i.max());
}
+
/** @brief Compute time values at which the curve comes closest to a specified point.
* @param p Query point
* @param a Minimum time value to consider
* @param b Maximum time value to consider; \f$a < b\f$
* @return Vector of points closest and equally far away from the query point */
- virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
Coord to = 1 ) const;
+
/** @brief A version that takes an Interval. */
- std::vector<Coord> allNearestPoints(Point const &p, Interval const &i) {
- return allNearestPoints(p, i.min(), i.max());
+ std::vector<Coord> allNearestTimes(Point const &p, Interval const &i) {
+ return allNearestTimes(p, i.min(), i.max());
}
+
/** @brief Compute the arc length of this curve.
* For a curve \f$\mathbf{C}(t) = (C_x(t), C_y(t))\f$, arc length is defined for 2D curves as
* \f[ \ell = \int_{0}^{1} \sqrt { [C_x'(t)]^2 + [C_y'(t)]^2 }\, \text{d}t \f]
@@ -242,13 +276,28 @@ public:
* @param tolerance Maximum allowed error
* @return Total distance the curve's value travels on the plane when going from 0 to 1 */
virtual Coord length(Coord tolerance=0.01) const;
+
/** @brief Computes time values at which the curve intersects an axis-aligned line.
* @param v The coordinate of the line
* @param d Which axis the coordinate is on. X means a vertical line, Y a horizontal line. */
virtual std::vector<Coord> roots(Coord v, Dim2 d) const = 0;
- /** @brief Compute the winding number of the curve at the specified point.
- * @todo Does this makes sense for curves at all? */
- virtual int winding(Point const &p) const { return root_winding(*this, p); }
+
+ /** @brief Compute the partial winding number of this curve.
+ * The partial winding number is equal to the difference between the number
+ * of roots at which the curve goes in the +Y direction and the number of roots
+ * at which the curve goes in the -Y direction. This method is mainly useful
+ * for implementing path winding calculation. It will ignore roots which
+ * are local maxima on the Y axis.
+ * @param p Point where the winding number should be determined
+ * @return Winding number contribution at p */
+ virtual int winding(Point const &p) const;
+
+ /// Compute intersections with another curve.
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps = EPSILON) const;
+
+ /// Compute intersections of this curve with itself.
+ virtual std::vector<CurveIntersection> intersectSelf(Coord eps = EPSILON) const;
+
/** @brief Compute a vector tangent to the curve.
* This will return an unit vector (a Point with length() equal to 1) that denotes a vector
* tangent to the curve. This vector is defined as
@@ -258,14 +307,9 @@ public:
* derivative could be found.
* @param t Time value
* @param n The maximum order of derivative to consider
- * @return Unit tangent vector \f$\mathbf{v}(t)\f$
- * @bug This method might currently break for the case of t being exactly 1. A workaround
- * is to reverse the curve and use the negated unit tangent at 0 like this:
- * @code
- Curve *c_reverse = c.reverse();
- Point tangent = - c_reverse->unitTangentAt(0);
- delete c_reverse; @endcode */
+ * @return Unit tangent vector \f$\mathbf{v}(t)\f$ */
virtual Point unitTangentAt(Coord t, unsigned n = 3) const;
+
/** @brief Convert the curve to a symmetric power basis polynomial.
* Symmetric power basis polynomials (S-basis for short) are numerical representations
* of curves with excellent numerical properties. Most high level operations provided by 2Geom
@@ -281,15 +325,22 @@ public:
* of this curve. For example, for Bezier curves it returns the curve's order
* multiplied by 2. */
virtual int degreesOfFreedom() const { return 0;}
+
/** @brief Test equality of two curves.
+ * Equality means that for any time value, the evaluation of either curve will yield
+ * the same value. This means non-degenerate curves are not equal to their reverses.
+ * Note that this tests for exact equality.
* @return True if the curves are identical, false otherwise */
- virtual bool operator==(Curve const &c) const { return this == &c;}
+ virtual bool operator==(Curve const &c) const = 0;
+
+ /** @brief Feed the curve to a PathSink */
+ virtual void feed(PathSink &sink, bool moveto_initial) const;
/// @}
};
inline
-Coord nearest_point(Point const& p, Curve const& c) {
- return c.nearestPoint(p);
+Coord nearest_time(Point const& p, Curve const& c) {
+ return c.nearestTime(p);
}
// for make benefit glorious library of Boost Pointer Container
diff --git a/src/2geom/curves.h b/src/2geom/curves.h
index 319b1924d..46fb6d973 100644
--- a/src/2geom/curves.h
+++ b/src/2geom/curves.h
@@ -1,10 +1,9 @@
-/**
- * \file
- * \brief Include all curve types
+/** @file
+ * @brief Include all curve types
*//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -38,9 +37,7 @@
#include <2geom/curve.h>
#include <2geom/sbasis-curve.h>
#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
#include <2geom/elliptical-arc.h>
-#include <2geom/svg-elliptical-arc.h>
#endif // LIB2GEOM_SEEN_CURVES_H
diff --git a/src/2geom/d2-sbasis.cpp b/src/2geom/d2-sbasis.cpp
index 486ada9a2..ebec16fdd 100644
--- a/src/2geom/d2-sbasis.cpp
+++ b/src/2geom/d2-sbasis.cpp
@@ -200,7 +200,7 @@ Point unitTangentAt(D2<SBasis> const & a, Coord t, unsigned n)
static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
- f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
+ f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y])))));
return;
}
for (unsigned dim=0; dim<2; dim++){
@@ -213,7 +213,7 @@ static void set_first_point(Piecewise<D2<SBasis> > &f, Point a){
}
static void set_last_point(Piecewise<D2<SBasis> > &f, Point a){
if ( f.empty() ){
- f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(Linear(a[X]),Linear(a[Y]))));
+ f.concat(Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(Linear(a[X])), SBasis(Linear(a[Y])))));
return;
}
for (unsigned dim=0; dim<2; dim++){
diff --git a/src/2geom/d2-sbasis.h b/src/2geom/d2-sbasis.h
index 965ec8746..a0769b314 100644
--- a/src/2geom/d2-sbasis.h
+++ b/src/2geom/d2-sbasis.h
@@ -35,11 +35,11 @@
*
*/
-#ifdef SEEN_LIB2GEOM_D2_H /*This is intentional: we don't actually want anyone to
+#ifdef LIB2GEOM_SEEN_D2_H /*This is intentional: we don't actually want anyone to
include this, other than D2.h. If somone else tries, D2
won't be defined. If it is, this will already be included. */
-#ifndef SEEN_LIB2GEOM_D2_SBASIS_H
-#define SEEN_LIB2GEOM_D2_SBASIS_H
+#ifndef LIB2GEOM_SEEN_D2_SBASIS_H
+#define LIB2GEOM_SEEN_D2_SBASIS_H
#include <2geom/sbasis.h>
#include <2geom/sbasis-2d.h>
@@ -149,8 +149,8 @@ std::vector<std::vector<Interval> > level_sets( D2<SBasis> const &f, std::vector
}
-#endif
-#endif
+#endif // LIB2GEOM_SEEN_D2_SBASIS_H
+#endif // LIB2GEOM_SEEN_D2_H
/*
diff --git a/src/2geom/d2.h b/src/2geom/d2.h
index 5fc955854..dca6fa614 100644
--- a/src/2geom/d2.h
+++ b/src/2geom/d2.h
@@ -29,32 +29,34 @@
*
*/
-#ifndef SEEN_LIB2GEOM_D2_H
-#define SEEN_LIB2GEOM_D2_H
+#ifndef LIB2GEOM_SEEN_D2_H
+#define LIB2GEOM_SEEN_D2_H
+#include <iterator>
+#include <boost/concept/assert.hpp>
+#include <boost/iterator/transform_iterator.hpp>
#include <2geom/point.h>
#include <2geom/interval.h>
#include <2geom/affine.h>
#include <2geom/rect.h>
-
-#include <boost/concept_check.hpp>
#include <2geom/concepts.h>
-namespace Geom{
+namespace Geom {
/**
- * The D2 class takes two instances of a scalar data type and treats them
- * like a point. All operations which make sense on a point are deïŹned for D2.
- * A D2<double> is a Point. A D2<Interval> is a standard axis aligned rectangle.
- * D2<SBasis> provides a 2d parametric function which maps t to a point
- * x(t), y(t)
+ * @brief Adaptor that creates 2D functions from 1D ones.
+ * @ingroup Fragments
*/
-template <class T>
-class D2{
- //BOOST_CLASS_REQUIRE(T, boost, AssignableConcept);
- private:
+template <typename T>
+class D2
+{
+private:
T f[2];
- public:
+public:
+ typedef T D1Value;
+ typedef T &D1Reference;
+ typedef T const &D1ConstReference;
+
D2() {f[X] = f[Y] = T();}
explicit D2(Point const &a) {
f[X] = T(a[X]); f[Y] = T(a[Y]);
@@ -65,38 +67,72 @@ class D2{
f[Y] = b;
}
+ template <typename Iter>
+ D2(Iter first, Iter last) {
+ typedef typename std::iterator_traits<Iter>::value_type V;
+ typedef typename boost::transform_iterator<GetAxis<X,V>, Iter> XIter;
+ typedef typename boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+ XIter xfirst(first, GetAxis<X,V>()), xlast(last, GetAxis<X,V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(first, GetAxis<Y,V>()), ylast(last, GetAxis<Y,V>());
+ f[Y] = T(yfirst, ylast);
+ }
+
+ D2(std::vector<Point> const &vec) {
+ typedef Point V;
+ typedef std::vector<Point>::const_iterator Iter;
+ typedef boost::transform_iterator<GetAxis<X,V>, Iter> XIter;
+ typedef boost::transform_iterator<GetAxis<Y,V>, Iter> YIter;
+
+ XIter xfirst(vec.begin(), GetAxis<X,V>()), xlast(vec.end(), GetAxis<X,V>());
+ f[X] = T(xfirst, xlast);
+ YIter yfirst(vec.begin(), GetAxis<Y,V>()), ylast(vec.end(), GetAxis<Y,V>());
+ f[Y] = T(yfirst, ylast);
+ }
+
//TODO: ask mental about operator= as seen in Point
T& operator[](unsigned i) { return f[i]; }
T const & operator[](unsigned i) const { return f[i]; }
+ Point point(unsigned i) const {
+ Point ret(f[X][i], f[Y][i]);
+ return ret;
+ }
//IMPL: FragmentConcept
typedef Point output_type;
bool isZero(double eps=EPSILON) const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return f[X].isZero(eps) && f[Y].isZero(eps);
}
bool isConstant(double eps=EPSILON) const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return f[X].isConstant(eps) && f[Y].isConstant(eps);
}
bool isFinite() const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return f[X].isFinite() && f[Y].isFinite();
}
Point at0() const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return Point(f[X].at0(), f[Y].at0());
}
Point at1() const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return Point(f[X].at1(), f[Y].at1());
}
+ Point pointAt(double t) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
+ return (*this)(t);
+ }
Point valueAt(double t) const {
- boost::function_requires<FragmentConcept<T> >();
+ // TODO: remove this alias
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return (*this)(t);
}
std::vector<Point > valueAndDerivatives(double t, unsigned n) const {
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
std::vector<Coord> x = f[X].valueAndDerivatives(t, n),
y = f[Y].valueAndDerivatives(t, n); // always returns a vector of size n+1
std::vector<Point> res(n+1);
@@ -106,7 +142,7 @@ class D2{
return res;
}
D2<SBasis> toSBasis() const {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return D2<SBasis>(f[X].toSBasis(), f[Y].toSBasis());
}
@@ -115,33 +151,33 @@ class D2{
};
template <typename T>
inline D2<T> reverse(const D2<T> &a) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return D2<T>(reverse(a[X]), reverse(a[Y]));
}
template <typename T>
inline D2<T> portion(const D2<T> &a, Coord f, Coord t) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return D2<T>(portion(a[X], f, t), portion(a[Y], f, t));
}
template <typename T>
inline D2<T> portion(const D2<T> &a, Interval i) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return D2<T>(portion(a[X], i), portion(a[Y], i));
}
-//IMPL: boost::EqualityComparableConcept
+//IMPL: EqualityComparableConcept
template <typename T>
inline bool
operator==(D2<T> const &a, D2<T> const &b) {
- boost::function_requires<boost::EqualityComparableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
return a[0]==b[0] && a[1]==b[1];
}
template <typename T>
inline bool
operator!=(D2<T> const &a, D2<T> const &b) {
- boost::function_requires<boost::EqualityComparableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((EqualityComparableConcept<T>));
return a[0]!=b[0] || a[1]!=b[1];
}
@@ -149,7 +185,7 @@ operator!=(D2<T> const &a, D2<T> const &b) {
template <typename T>
inline bool
are_near(D2<T> const &a, D2<T> const &b, double tol) {
- boost::function_requires<NearConcept<T> >();
+ BOOST_CONCEPT_ASSERT((NearConcept<T>));
return are_near(a[0], b[0], tol) && are_near(a[1], b[1], tol);
}
@@ -157,7 +193,7 @@ are_near(D2<T> const &a, D2<T> const &b, double tol) {
template <typename T>
inline D2<T>
operator+(D2<T> const &a, D2<T> const &b) {
- boost::function_requires<AddableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
@@ -167,7 +203,7 @@ operator+(D2<T> const &a, D2<T> const &b) {
template <typename T>
inline D2<T>
operator-(D2<T> const &a, D2<T> const &b) {
- boost::function_requires<AddableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
@@ -177,7 +213,7 @@ operator-(D2<T> const &a, D2<T> const &b) {
template <typename T>
inline D2<T>
operator+=(D2<T> &a, D2<T> const &b) {
- boost::function_requires<AddableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
for(unsigned i = 0; i < 2; i++)
a[i] += b[i];
@@ -186,7 +222,7 @@ operator+=(D2<T> &a, D2<T> const &b) {
template <typename T>
inline D2<T>
operator-=(D2<T> &a, D2<T> const & b) {
- boost::function_requires<AddableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
for(unsigned i = 0; i < 2; i++)
a[i] -= b[i];
@@ -197,7 +233,7 @@ operator-=(D2<T> &a, D2<T> const & b) {
template <typename T>
inline D2<T>
operator-(D2<T> const & a) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
r[i] = -a[i];
@@ -206,7 +242,7 @@ operator-(D2<T> const & a) {
template <typename T>
inline D2<T>
operator*(D2<T> const & a, Point const & b) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
@@ -216,7 +252,7 @@ operator*(D2<T> const & a, Point const & b) {
template <typename T>
inline D2<T>
operator/(D2<T> const & a, Point const & b) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
//TODO: b==0?
D2<T> r;
for(unsigned i = 0; i < 2; i++)
@@ -226,7 +262,7 @@ operator/(D2<T> const & a, Point const & b) {
template <typename T>
inline D2<T>
operator*=(D2<T> &a, Point const & b) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
for(unsigned i = 0; i < 2; i++)
a[i] *= b[i];
@@ -235,7 +271,7 @@ operator*=(D2<T> &a, Point const & b) {
template <typename T>
inline D2<T>
operator/=(D2<T> &a, Point const & b) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
//TODO: b==0?
for(unsigned i = 0; i < 2; i++)
a[i] /= b[i];
@@ -253,8 +289,8 @@ inline D2<T> operator/=(D2<T> & a, double b) { a[0] /= b; a[1] /= b; return a; }
template<typename T>
D2<T> operator*(D2<T> const &v, Affine const &m) {
- boost::function_requires<AddableConcept<T> >();
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
D2<T> ret;
for(unsigned i = 0; i < 2; i++)
ret[i] = v[X] * m[i] + v[Y] * m[i + 2] + m[i + 4];
@@ -265,7 +301,7 @@ D2<T> operator*(D2<T> const &v, Affine const &m) {
template <typename T>
inline D2<T>
operator*(D2<T> const & a, T const & b) {
- boost::function_requires<MultiplicableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
D2<T> ret;
for(unsigned i = 0; i < 2; i++)
ret[i] = a[i] * b;
@@ -278,7 +314,7 @@ operator*(D2<T> const & a, T const & b) {
template <typename T>
inline D2<T>
operator+(D2<T> const & a, Point b) {
- boost::function_requires<OffsetableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
r[i] = a[i] + b[i];
@@ -287,7 +323,7 @@ operator+(D2<T> const & a, Point b) {
template <typename T>
inline D2<T>
operator-(D2<T> const & a, Point b) {
- boost::function_requires<OffsetableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
D2<T> r;
for(unsigned i = 0; i < 2; i++)
r[i] = a[i] - b[i];
@@ -296,7 +332,7 @@ operator-(D2<T> const & a, Point b) {
template <typename T>
inline D2<T>
operator+=(D2<T> & a, Point b) {
- boost::function_requires<OffsetableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
for(unsigned i = 0; i < 2; i++)
a[i] += b[i];
return a;
@@ -304,7 +340,7 @@ operator+=(D2<T> & a, Point b) {
template <typename T>
inline D2<T>
operator-=(D2<T> & a, Point b) {
- boost::function_requires<OffsetableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((OffsetableConcept<T>));
for(unsigned i = 0; i < 2; i++)
a[i] -= b[i];
return a;
@@ -313,8 +349,8 @@ operator-=(D2<T> & a, Point b) {
template <typename T>
inline T
dot(D2<T> const & a, D2<T> const & b) {
- boost::function_requires<AddableConcept<T> >();
- boost::function_requires<MultiplicableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
T r;
for(unsigned i = 0; i < 2; i++)
@@ -328,8 +364,8 @@ dot(D2<T> const & a, D2<T> const & b) {
template <typename T>
inline T
dot(D2<T> const & a, Point const & b) {
- boost::function_requires<AddableConcept<T> >();
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((AddableConcept<T>));
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
T r;
for(unsigned i = 0; i < 2; i++) {
@@ -344,8 +380,8 @@ dot(D2<T> const & a, Point const & b) {
template <typename T>
inline T
cross(D2<T> const & a, D2<T> const & b) {
- boost::function_requires<ScalableConcept<T> >();
- boost::function_requires<MultiplicableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
+ BOOST_CONCEPT_ASSERT((MultiplicableConcept<T>));
return a[1] * b[0] - a[0] * b[1];
}
@@ -355,7 +391,7 @@ cross(D2<T> const & a, D2<T> const & b) {
template <typename T>
inline D2<T>
rot90(D2<T> const & a) {
- boost::function_requires<ScalableConcept<T> >();
+ BOOST_CONCEPT_ASSERT((ScalableConcept<T>));
return D2<T>(-a[Y], a[X]);
}
@@ -429,26 +465,28 @@ inline std::ostream &operator<< (std::ostream &out_file, const Geom::D2<T> &in_d
#include <2geom/d2-sbasis.h>
-namespace Geom{
+namespace Geom {
//Some D2 Fragment implementation which requires rect:
template <typename T>
OptRect bounds_fast(const D2<T> &a) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return OptRect(bounds_fast(a[X]), bounds_fast(a[Y]));
}
template <typename T>
OptRect bounds_exact(const D2<T> &a) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return OptRect(bounds_exact(a[X]), bounds_exact(a[Y]));
}
template <typename T>
OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
- boost::function_requires<FragmentConcept<T> >();
+ BOOST_CONCEPT_ASSERT((FragmentConcept<T>));
return OptRect(bounds_local(a[X], t), bounds_local(a[Y], t));
}
-};
+} // end namespace Geom
+
+#endif
/*
Local Variables:
mode:c++
@@ -459,4 +497,3 @@ OptRect bounds_local(const D2<T> &a, const OptInterval &t) {
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-#endif
diff --git a/src/2geom/ellipse.cpp b/src/2geom/ellipse.cpp
index 2686844b2..0264ab4ae 100644
--- a/src/2geom/ellipse.cpp
+++ b/src/2geom/ellipse.cpp
@@ -1,10 +1,11 @@
-/*
- * Ellipse Curve
- *
+/** @file
+ * @brief Ellipse shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -30,138 +31,161 @@
* the specific language governing rights and limitations.
*/
-
#include <2geom/ellipse.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
-using std::swap;
+namespace Geom {
-namespace Geom
-{
+Ellipse::Ellipse(Geom::Circle const &c)
+ : _center(c.center())
+ , _rays(c.radius(), c.radius())
+ , _angle(0)
+{}
-void Ellipse::set(double A, double B, double C, double D, double E, double F)
+void Ellipse::setCoefficients(double A, double B, double C, double D, double E, double F)
{
double den = 4*A*C - B*B;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing ellipse centre");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing ellipse centre");
}
- m_centre[X] = (B*E - 2*C*D) / den;
- m_centre[Y] = (B*D - 2*A*E) / den;
+ _center[X] = (B*E - 2*C*D) / den;
+ _center[Y] = (B*D - 2*A*E) / den;
// evaluate the a coefficient of the ellipse equation in normal form
// E(x,y) = a*(x-cx)^2 + b*(x-cx)*(y-cy) + c*(y-cy)^2 = 1
// where b = a*B , c = a*C, (cx,cy) == centre
- double num = A * sqr(m_centre[X])
- + B * m_centre[X] * m_centre[Y]
- + C * sqr(m_centre[Y])
+ double num = A * sqr(_center[X])
+ + B * _center[X] * _center[Y]
+ + C * sqr(_center[Y])
- F;
//evaluate ellipse rotation angle
- double rot = std::atan2( -B, -(A - C) )/2;
-// std::cerr << "rot = " << rot << std::endl;
- bool swap_axes = false;
- if ( are_near(rot, 0) ) rot = 0;
- if ( are_near(rot, M_PI/2) || rot < 0 )
- {
- swap_axes = true;
- }
+ _angle = std::atan2( -B, -(A - C) )/2;
// evaluate the length of the ellipse rays
- double cosrot = std::cos(rot);
- double sinrot = std::sin(rot);
+ double sinrot, cosrot;
+ sincos(_angle, sinrot, cosrot);
double cos2 = cosrot * cosrot;
double sin2 = sinrot * sinrot;
double cossin = cosrot * sinrot;
den = A * cos2 + B * cossin + C * sin2;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing 'rx' coefficient");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing 'rx' coefficient");
}
- double rx2 = num/den;
- if ( rx2 < 0 )
- {
- THROW_LOGICALERROR("rx2 < 0, while computing 'rx' coefficient");
+ double rx2 = num / den;
+ if (rx2 < 0) {
+ THROW_RANGEERROR("rx2 < 0, while computing 'rx' coefficient");
}
- double rx = std::sqrt(rx2);
+ _rays[X] = std::sqrt(rx2);
den = C * cos2 - B * cossin + A * sin2;
- if ( den == 0 )
- {
- THROW_LOGICALERROR("den == 0, while computing 'ry' coefficient");
+ if (den == 0) {
+ THROW_RANGEERROR("den == 0, while computing 'ry' coefficient");
}
- double ry2 = num/den;
- if ( ry2 < 0 )
- {
- THROW_LOGICALERROR("ry2 < 0, while computing 'rx' coefficient");
+ double ry2 = num / den;
+ if (ry2 < 0) {
+ THROW_RANGEERROR("ry2 < 0, while computing 'rx' coefficient");
}
- double ry = std::sqrt(ry2);
+ _rays[Y] = std::sqrt(ry2);
// the solution is not unique so we choose always the ellipse
// with a rotation angle between 0 and PI/2
- if ( swap_axes ) swap(rx, ry);
- if ( are_near(rot, M_PI/2)
- || are_near(rot, -M_PI/2)
- || are_near(rx, ry) )
- {
- rot = 0;
- }
- else if ( rot < 0 )
- {
- rot += M_PI/2;
+ makeCanonical();
+}
+
+Point Ellipse::initialPoint() const
+{
+ Coord sinrot, cosrot;
+ sincos(_angle, sinrot, cosrot);
+ Point p(ray(X) * cosrot + center(X), ray(X) * sinrot + center(Y));
+ return p;
+}
+
+
+Affine Ellipse::unitCircleTransform() const
+{
+ Affine ret = Scale(ray(X), ray(Y)) * Rotate(_angle);
+ ret.setTranslation(center());
+ return ret;
+}
+
+Affine Ellipse::inverseUnitCircleTransform() const
+{
+ if (ray(X) == 0 || ray(Y) == 0) {
+ THROW_RANGEERROR("a degenerate ellipse doesn't have an inverse unit circle transform");
}
+ Affine ret = Translate(-center()) * Rotate(-_angle) * Scale(1/ray(X), 1/ray(Y));
+ return ret;
+}
+
- m_ray[X] = rx;
- m_ray[Y] = ry;
- m_angle = rot;
+LineSegment Ellipse::axis(Dim2 d) const
+{
+ Point a(0, 0), b(0, 0);
+ a[d] = -1;
+ b[d] = 1;
+ LineSegment ls(a, b);
+ ls.transform(unitCircleTransform());
+ return ls;
+}
+
+LineSegment Ellipse::semiaxis(Dim2 d, int sign) const
+{
+ Point a(0, 0), b(0, 0);
+ b[d] = sgn(sign);
+ LineSegment ls(a, b);
+ ls.transform(unitCircleTransform());
+ return ls;
}
-std::vector<double> Ellipse::implicit_form_coefficients() const
+std::vector<double> Ellipse::coefficients() const
{
- if (ray(X) == 0 || ray(Y) == 0)
- {
- THROW_LOGICALERROR("a degenerate ellipse doesn't own an implicit form");
+ std::vector<double> c(6);
+ coefficients(c[0], c[1], c[2], c[3], c[4], c[5]);
+ return c;
+}
+
+void Ellipse::coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const
+{
+ if (ray(X) == 0 || ray(Y) == 0) {
+ THROW_RANGEERROR("a degenerate ellipse doesn't have an implicit form");
}
- std::vector<double> coeff(6);
- double cosrot = std::cos(rot_angle());
- double sinrot = std::sin(rot_angle());
+ double cosrot, sinrot;
+ sincos(_angle, sinrot, cosrot);
double cos2 = cosrot * cosrot;
double sin2 = sinrot * sinrot;
double cossin = cosrot * sinrot;
double invrx2 = 1 / (ray(X) * ray(X));
double invry2 = 1 / (ray(Y) * ray(Y));
- coeff[0] = invrx2 * cos2 + invry2 * sin2;
- coeff[1] = 2 * (invrx2 - invry2) * cossin;
- coeff[2] = invrx2 * sin2 + invry2 * cos2;
- coeff[3] = -(2 * coeff[0] * center(X) + coeff[1] * center(Y));
- coeff[4] = -(2 * coeff[2] * center(Y) + coeff[1] * center(X));
- coeff[5] = coeff[0] * center(X) * center(X)
- + coeff[1] * center(X) * center(Y)
- + coeff[2] * center(Y) * center(Y)
- - 1;
- return coeff;
+ A = invrx2 * cos2 + invry2 * sin2;
+ B = 2 * (invrx2 - invry2) * cossin;
+ C = invrx2 * sin2 + invry2 * cos2;
+ D = -2 * A * center(X) - B * center(Y);
+ E = -2 * C * center(Y) - B * center(X);
+ F = A * center(X) * center(X)
+ + B * center(X) * center(Y)
+ + C * center(Y) * center(Y)
+ - 1;
}
-void Ellipse::set(std::vector<Point> const& points)
+void Ellipse::fit(std::vector<Point> const &points)
{
size_t sz = points.size();
- if (sz < 5)
- {
+ if (sz < 5) {
THROW_RANGEERROR("fitting error: too few points passed");
}
NL::LFMEllipse model;
NL::least_squeares_fitter<NL::LFMEllipse> fitter(model, sz);
- for (size_t i = 0; i < sz; ++i)
- {
+ for (size_t i = 0; i < sz; ++i) {
fitter.append(points[i]);
}
fitter.update();
@@ -172,109 +196,415 @@ void Ellipse::set(std::vector<Point> const& points)
EllipticalArc *
-Ellipse::arc(Point const& initial, Point const& inner, Point const& final,
- bool _svg_compliant)
+Ellipse::arc(Point const &ip, Point const &inner, Point const &fp)
{
- Point sp_cp = initial - center();
- Point ep_cp = final - center();
- Point ip_cp = inner - center();
-
- double angle1 = angle_between(sp_cp, ep_cp);
- double angle2 = angle_between(sp_cp, ip_cp);
- double angle3 = angle_between(ip_cp, ep_cp);
-
- bool large_arc_flag = true;
- bool sweep_flag = true;
-
- if ( angle1 > 0 )
- {
- if ( angle2 > 0 && angle3 > 0 )
- {
- large_arc_flag = false;
- sweep_flag = true;
- }
- else
- {
- large_arc_flag = true;
- sweep_flag = false;
- }
- }
- else
- {
- if ( angle2 < 0 && angle3 < 0 )
- {
- large_arc_flag = false;
- sweep_flag = false;
- }
- else
- {
- large_arc_flag = true;
- sweep_flag = true;
- }
+ // This is resistant to degenerate ellipses:
+ // both flags evaluate to false in that case.
+
+ bool large_arc_flag = false;
+ bool sweep_flag = false;
+
+ // Determination of large arc flag:
+ // The arc is larger than half of the ellipse if the inner point
+ // is on the same side of the line going from the initial
+ // to the final point as the center of the ellipse
+ Point versor = fp - ip;
+ double sdist_c = cross(versor, _center - ip);
+ double sdist_inner = cross(versor, inner - ip);
+
+ // if we have exactly half of an arc, do not set the large flag.
+ if (sdist_c != 0 && sgn(sdist_c) == sgn(sdist_inner)) {
+ large_arc_flag = true;
}
- EllipticalArc *ret_arc;
- if (_svg_compliant) {
- ret_arc = new SVGEllipticalArc(initial, ray(X), ray(Y), rot_angle(),
- large_arc_flag, sweep_flag, final);
- } else {
- ret_arc = new EllipticalArc(initial, ray(X), ray(Y), rot_angle(),
- large_arc_flag, sweep_flag, final);
+ // Determination of sweep flag:
+ // If the inner point is on the left side of the ip-fp line,
+ // we go in clockwise direction.
+ if (sdist_inner < 0) {
+ sweep_flag = true;
}
+
+ EllipticalArc *ret_arc = new EllipticalArc(ip, ray(X), ray(Y), rotationAngle(),
+ large_arc_flag, sweep_flag, fp);
return ret_arc;
}
-Ellipse Ellipse::transformed(Affine const& m) const
+Ellipse &Ellipse::operator*=(Rotate const &r)
+{
+ _angle += r.angle();
+ _center *= r;
+ return *this;
+}
+
+Ellipse &Ellipse::operator*=(Affine const& m)
{
- double cosrot = std::cos(rot_angle());
- double sinrot = std::sin(rot_angle());
- Affine A( ray(X) * cosrot, ray(X) * sinrot,
- -ray(Y) * sinrot, ray(Y) * cosrot,
- 0, 0 );
- Point new_center = center() * m;
- Affine M = m.withoutTranslation();
- Affine AM = A * M;
- if ( are_near(std::sqrt(fabs(AM.det())), 0) )
- {
+ Affine a = Scale(ray(X), ray(Y)) * Rotate(_angle);
+ Affine mwot = m.withoutTranslation();
+ Affine am = a * mwot;
+ Point new_center = _center * m;
+
+ if (are_near(am.descrim(), 0)) {
double angle;
- if (AM[0] != 0)
- {
- angle = std::atan2(AM[2], AM[0]);
- }
- else if (AM[1] != 0)
- {
- angle = std::atan2(AM[3], AM[1]);
- }
- else
- {
+ if (am[0] != 0) {
+ angle = std::atan2(am[2], am[0]);
+ } else if (am[1] != 0) {
+ angle = std::atan2(am[3], am[1]);
+ } else {
angle = M_PI/2;
}
- Point V(std::cos(angle), std::sin(angle));
- V *= AM;
- double rx = L2(V);
- angle = atan2(V);
- return Ellipse(new_center[X], new_center[Y], rx, 0, angle);
+ Point v = Point::polar(angle) * am;
+ _center = new_center;
+ _rays[X] = L2(v);
+ _rays[Y] = 0;
+ _angle = atan2(v);
+ return *this;
}
- std::vector<double> coeff = implicit_form_coefficients();
- Affine Q( coeff[0], coeff[1]/2,
+ std::vector<double> coeff = coefficients();
+ Affine q( coeff[0], coeff[1]/2,
coeff[1]/2, coeff[2],
0, 0 );
- Affine invm = M.inverse();
- Q = invm * Q ;
- swap( invm[1], invm[2] );
- Q *= invm;
- Ellipse e(Q[0], 2*Q[1], Q[3], 0, 0, -1);
- e.m_centre = new_center;
+ Affine invm = mwot.inverse();
+ q = invm * q ;
+ std::swap(invm[1], invm[2]);
+ q *= invm;
+ setCoefficients(q[0], 2*q[1], q[3], 0, 0, -1);
+ _center = new_center;
- return e;
+ return *this;
}
-Ellipse::Ellipse(Geom::Circle const &c)
+Ellipse Ellipse::canonicalForm() const
+{
+ Ellipse result(*this);
+ result.makeCanonical();
+ return result;
+}
+
+void Ellipse::makeCanonical()
+{
+ if (_rays[X] == _rays[Y]) {
+ _angle = 0;
+ return;
+ }
+
+ if (_angle < 0) {
+ _angle += M_PI;
+ }
+ if (_angle >= M_PI/2) {
+ std::swap(_rays[X], _rays[Y]);
+ _angle -= M_PI/2;
+ }
+}
+
+Point Ellipse::pointAt(Coord t) const
+{
+ Point p = Point::polar(t);
+ p *= unitCircleTransform();
+ return p;
+}
+
+Coord Ellipse::valueAt(Coord t, Dim2 d) const
+{
+ Coord sinrot, cosrot, cost, sint;
+ sincos(rotationAngle(), sinrot, cosrot);
+ sincos(t, sint, cost);
+
+ if ( d == X ) {
+ return ray(X) * cosrot * cost
+ - ray(Y) * sinrot * sint
+ + center(X);
+ } else {
+ return ray(X) * sinrot * cost
+ + ray(Y) * cosrot * sint
+ + center(Y);
+ }
+}
+
+Coord Ellipse::timeAt(Point const &p) const
+{
+ // degenerate ellipse is basically a reparametrized line segment
+ if (ray(X) == 0 || ray(Y) == 0) {
+ if (ray(X) != 0) {
+ return asin(Line(axis(X)).timeAt(p));
+ } else if (ray(Y) != 0) {
+ return acos(Line(axis(Y)).timeAt(p));
+ } else {
+ return 0;
+ }
+ }
+ Affine iuct = inverseUnitCircleTransform();
+ return Angle(atan2(p * iuct)).radians0(); // return a value in [0, 2pi)
+}
+
+Point Ellipse::unitTangentAt(Coord t) const
+{
+ Point p = Point::polar(t + M_PI/2);
+ p *= unitCircleTransform().withoutTranslation();
+ p.normalize();
+ return p;
+}
+
+bool Ellipse::contains(Point const &p) const
+{
+ Point tp = p * inverseUnitCircleTransform();
+ return tp.length() <= 1;
+}
+
+std::vector<ShapeIntersection> Ellipse::intersect(Line const &line) const
+{
+
+ std::vector<ShapeIntersection> result;
+
+ if (line.isDegenerate()) return result;
+ if (ray(X) == 0 || ray(Y) == 0) {
+ // TODO intersect with line segment.
+ return result;
+ }
+
+ // Ax^2 + Bxy + Cy^2 + Dx + Ey + F
+ Coord A, B, C, D, E, F;
+ coefficients(A, B, C, D, E, F);
+ Affine iuct = inverseUnitCircleTransform();
+
+ if (line.isHorizontal()) {
+ // substitute y into the ellipse equation and solve
+ Coord y = line.initialPoint()[Y];
+ std::vector<Coord> xs = solve_quadratic(A, B*y + D, C*y*y + E*y + F);
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ Point p(xs[i], y);
+ result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ }
+ return result;
+ }
+ if (line.isVertical()) {
+ // substitute y into the ellipse equation and solve
+ Coord x = line.initialPoint()[X];
+ std::vector<Coord> ys = solve_quadratic(C, B*x + E, A*x*x + D*x + F);
+ for (unsigned i = 0; i < ys.size(); ++i) {
+ Point p(x, ys[i]);
+ result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ }
+ return result;
+ }
+
+ // generic case
+ Coord a, b, c;
+ line.coefficients(a, b, c);
+
+ // y = -a/b x - C/B
+ // TODO: when is it better to substitute X?
+ Coord q = -a/b;
+ Coord r = -c/b;
+
+ // substitute that into the ellipse equation, making it quadratic in x
+ Coord I = A + B*q + C*q*q; // x^2 terms
+ Coord J = B*r + C*2*q*r + D + E*q; // x^1 terms
+ Coord K = C*r*r + E*r + F; // x^0 terms
+ std::vector<Coord> xs = solve_quadratic(I, J, K);
+
+ for (unsigned i = 0; i < xs.size(); ++i) {
+ Point p(xs[i], q*xs[i] + r);
+ result.push_back(ShapeIntersection(atan2(p * iuct), line.timeAt(p), p));
+ }
+ return result;
+}
+
+std::vector<ShapeIntersection> Ellipse::intersect(LineSegment const &seg) const
+{
+ // we simply re-use the procedure for lines and filter out
+ // results where the line time value is outside of the unit interval.
+ std::vector<ShapeIntersection> result = intersect(Line(seg));
+ filter_line_segment_intersections(result);
+ return result;
+}
+
+std::vector<ShapeIntersection> Ellipse::intersect(Ellipse const &other) const
+{
+ // handle degenerate cases first
+ if (ray(X) == 0 || ray(Y) == 0) {
+
+ }
+ // intersection of two ellipses can be solved analytically.
+ // http://maptools.home.comcast.net/~maptools/BivariateQuadratics.pdf
+
+ Coord A, B, C, D, E, F;
+ Coord a, b, c, d, e, f;
+
+ // NOTE: the order of coefficients is different to match the convention in the PDF above
+ // Ax^2 + Bx^2 + Cx + Dy + Exy + F
+ this->coefficients(A, E, B, C, D, F);
+ other.coefficients(a, e, b, c, d, f);
+
+ // Assume that Q is the ellipse equation given by uppercase letters
+ // and R is the equation given by lowercase ones. An intersection exists when
+ // there is a coefficient mu such that
+ // mu Q + R = 0
+ //
+ // This can be written in the following way:
+ //
+ // | ff cc/2 dd/2 | |1|
+ // mu Q + R = [1 x y] | cc/2 aa ee/2 | |x| = 0
+ // | dd/2 ee/2 bb | |y|
+ //
+ // where aa = mu A + a and so on. The determinant can be explicitly written out,
+ // giving an equation which is cubic in mu and can be solved analytically.
+
+ Coord I, J, K, L;
+ I = (-E*E*F + 4*A*B*F + C*D*E - A*D*D - B*C*C) / 4;
+ J = -((E*E - 4*A*B) * f + (2*E*F - C*D) * e + (2*A*D - C*E) * d +
+ (2*B*C - D*E) * c + (C*C - 4*A*F) * b + (D*D - 4*B*F) * a) / 4;
+ K = -((e*e - 4*a*b) * F + (2*e*f - c*d) * E + (2*a*d - c*e) * D +
+ (2*b*c - d*e) * C + (c*c - 4*a*f) * B + (d*d - 4*b*f) * A) / 4;
+ L = (-e*e*f + 4*a*b*f + c*d*e - a*d*d - b*c*c) / 4;
+
+ std::vector<Coord> mus = solve_cubic(I, J, K, L);
+ Coord mu = infinity();
+ std::vector<ShapeIntersection> result;
+
+ // Now that we have solved for mu, we need to check whether the conic
+ // determined by mu Q + R is reducible to a product of two lines. If it's not,
+ // it means that there are no intersections. If it is, the intersections of these
+ // lines with the original ellipses (if there are any) give the coordinates
+ // of intersections.
+
+ // Prefer middle root if there are three.
+ // Out of three possible pairs of lines that go through four points of intersection
+ // of two ellipses, this corresponds to cross-lines. These intersect the ellipses
+ // at less shallow angles than the other two options.
+ if (mus.size() == 3) {
+ std::swap(mus[1], mus[0]);
+ }
+ for (unsigned i = 0; i < mus.size(); ++i) {
+ Coord aa = mus[i] * A + a;
+ Coord bb = mus[i] * B + b;
+ Coord ee = mus[i] * E + e;
+ Coord delta = ee*ee - 4*aa*bb;
+ if (delta < 0) continue;
+ mu = mus[i];
+ break;
+ }
+
+ // if no suitable mu was found, there are no intersections
+ if (mu == infinity()) return result;
+
+ Coord aa = mu * A + a;
+ Coord bb = mu * B + b;
+ Coord cc = mu * C + c;
+ Coord dd = mu * D + d;
+ Coord ee = mu * E + e;
+ Coord ff = mu * F + f;
+
+ Line lines[2];
+
+ if (aa != 0) {
+ bb /= aa; cc /= aa; dd /= aa; ee /= aa; ff /= aa;
+ Coord s = (ee + std::sqrt(ee*ee - 4*bb)) / 2;
+ Coord q = ee - s;
+ Coord alpha = (dd - cc*q) / (s - q);
+ Coord beta = cc - alpha;
+
+ lines[0] = Line(1, q, alpha);
+ lines[1] = Line(1, s, beta);
+ } else if (bb != 0) {
+ cc /= bb; dd /= bb; ee /= bb; ff /= bb;
+ Coord s = ee;
+ Coord q = 0;
+ Coord alpha = cc / ee;
+ Coord beta = ff * ee / cc;
+
+ lines[0] = Line(q, 1, alpha);
+ lines[1] = Line(s, 1, beta);
+ } else {
+ lines[0] = Line(ee, 0, dd);
+ lines[1] = Line(0, 1, cc/ee);
+ }
+
+ // intersect with the obtained lines and report intersections
+ for (unsigned li = 0; li < 2; ++li) {
+ std::vector<ShapeIntersection> as = intersect(lines[li]);
+ std::vector<ShapeIntersection> bs = other.intersect(lines[li]);
+
+ if (!as.empty() && as.size() == bs.size()) {
+ for (unsigned i = 0; i < as.size(); ++i) {
+ ShapeIntersection ix(as[i].first, bs[i].first,
+ middle_point(as[i].point(), bs[i].point()));
+ result.push_back(ix);
+ }
+ }
+ }
+ return result;
+}
+
+std::vector<ShapeIntersection> Ellipse::intersect(D2<Bezier> const &b) const
+{
+ Coord A, B, C, D, E, F;
+ coefficients(A, B, C, D, E, F);
+
+ Bezier x = A*b[X]*b[X] + B*b[X]*b[Y] + C*b[Y]*b[Y] + D*b[X] + E*b[Y] + F;
+ std::vector<Coord> r = x.roots();
+
+ std::vector<ShapeIntersection> result;
+ for (unsigned i = 0; i < r.size(); ++i) {
+ Point p = b.valueAt(r[i]);
+ result.push_back(ShapeIntersection(timeAt(p), r[i], p));
+ }
+ return result;
+}
+
+bool Ellipse::operator==(Ellipse const &other) const
+{
+ if (_center != other._center) return false;
+
+ Ellipse a = this->canonicalForm();
+ Ellipse b = other.canonicalForm();
+
+ if (a._rays != b._rays) return false;
+ if (a._angle != b._angle) return false;
+
+ return true;
+}
+
+
+bool are_near(Ellipse const &a, Ellipse const &b, Coord precision)
+{
+ // We want to know whether no point on ellipse a is further than precision
+ // from the corresponding point on ellipse b. To check this, we compute
+ // the four extreme points at the end of each ray for each ellipse
+ // and check whether they are sufficiently close.
+
+ // First, we need to correct the angles on the ellipses, so that they are
+ // no further than M_PI/4 apart. This can always be done by rotating
+ // and exchanging axes.
+ Ellipse ac = a, bc = b;
+ if (distance(ac.rotationAngle(), bc.rotationAngle()).radians0() >= M_PI/2) {
+ ac.setRotationAngle(ac.rotationAngle() + M_PI);
+ }
+ if (distance(ac.rotationAngle(), bc.rotationAngle()) >= M_PI/4) {
+ Angle d1 = distance(ac.rotationAngle() + M_PI/2, bc.rotationAngle());
+ Angle d2 = distance(ac.rotationAngle() - M_PI/2, bc.rotationAngle());
+ Coord adj = d1.radians0() < d2.radians0() ? M_PI/2 : -M_PI/2;
+ ac.setRotationAngle(ac.rotationAngle() + adj);
+ ac.setRays(ac.ray(Y), ac.ray(X));
+ }
+
+ // Do the actual comparison by computing four points on each ellipse.
+ Point tps[] = {Point(1,0), Point(0,1), Point(-1,0), Point(0,-1)};
+ for (unsigned i = 0; i < 4; ++i) {
+ if (!are_near(tps[i] * ac.unitCircleTransform(),
+ tps[i] * bc.unitCircleTransform(),
+ precision))
+ return false;
+ }
+ return true;
+}
+
+std::ostream &operator<<(std::ostream &out, Ellipse const &e)
{
- m_centre = c.center();
- m_ray[X] = m_ray[Y] = c.ray();
+ out << "Ellipse(" << e.center() << ", " << e.rays()
+ << ", " << format_coord_nice(e.rotationAngle()) << ")";
+ return out;
}
} // end namespace Geom
diff --git a/src/2geom/ellipse.h b/src/2geom/ellipse.h
index c971c6065..4b1701cce 100644
--- a/src/2geom/ellipse.h
+++ b/src/2geom/ellipse.h
@@ -1,9 +1,9 @@
-/**
- * \file
- * \brief Ellipse Curve
- *
+/** @file
+ * @brief Ellipse shape
+ *//*
* Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
* Copyright 2008 authors
*
@@ -32,104 +32,211 @@
*/
-#ifndef _2GEOM_ELLIPSE_H_
-#define _2GEOM_ELLIPSE_H_
+#ifndef LIB2GEOM_SEEN_ELLIPSE_H
+#define LIB2GEOM_SEEN_ELLIPSE_H
#include <vector>
-#include <2geom/point.h>
+#include <2geom/angle.h>
+#include <2geom/bezier-curve.h>
#include <2geom/exception.h>
-#include <2geom/affine.h>
+#include <2geom/forward.h>
+#include <2geom/line.h>
+#include <2geom/transforms.h>
-namespace Geom
-{
+namespace Geom {
class EllipticalArc;
class Circle;
+/** @brief Set of points with a constant sum of distances from two foci.
+ *
+ * An ellipse can be specified in several ways. Internally, 2Geom uses
+ * the SVG style representation: center, rays and angle between the +X ray
+ * and the +X axis. Another popular way is to use an implicit equation,
+ * which is as follows:
+ * \f$Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0\f$
+ *
+ * @ingroup Shapes */
class Ellipse
+ : boost::multipliable< Ellipse, Translate
+ , boost::multipliable< Ellipse, Scale
+ , boost::multipliable< Ellipse, Rotate
+ , boost::multipliable< Ellipse, Zoom
+ , boost::multipliable< Ellipse, Affine
+ , boost::equality_comparable< Ellipse
+ > > > > > >
{
- public:
- Ellipse():
- m_centre(),
- m_ray(),
- m_angle(0)
+ Point _center;
+ Point _rays;
+ Angle _angle;
+public:
+ Ellipse() {}
+ Ellipse(Point const &c, Point const &r, Coord angle)
+ : _center(c)
+ , _rays(r)
+ , _angle(angle)
{}
-
- Ellipse(double cx, double cy, double rx, double ry, double a)
- : m_centre(cx, cy), m_ray(rx, ry), m_angle(a)
- {
+ Ellipse(Coord cx, Coord cy, Coord rx, Coord ry, Coord angle)
+ : _center(cx, cy)
+ , _rays(rx, ry)
+ , _angle(angle)
+ {}
+ Ellipse(double A, double B, double C, double D, double E, double F) {
+ setCoefficients(A, B, C, D, E, F);
}
+ /// Construct ellipse from a circle.
+ Ellipse(Geom::Circle const &c);
- // build an ellipse by its implicit equation:
- // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
- Ellipse(double A, double B, double C, double D, double E, double F)
- {
- set(A, B, C, D, E, F);
+ /// Set center, rays and angle.
+ void set(Point const &c, Point const &r, Coord angle) {
+ _center = c;
+ _rays = r;
+ _angle = angle;
}
-
- Ellipse(std::vector<Point> const& points)
- {
- set(points);
+ /// Set center, rays and angle as constituent values.
+ void set(Coord cx, Coord cy, Coord rx, Coord ry, Coord a) {
+ _center[X] = cx;
+ _center[Y] = cy;
+ _rays[X] = rx;
+ _rays[Y] = ry;
+ _angle = a;
}
-
- Ellipse(Geom::Circle const &c);
-
- void set(double cx, double cy, double rx, double ry, double a)
- {
- m_centre[X] = cx;
- m_centre[Y] = cy;
- m_ray[X] = rx;
- m_ray[Y] = ry;
- m_angle = a;
+ /// Set an ellipse by solving its implicit equation.
+ void setCoefficients(double A, double B, double C, double D, double E, double F);
+ /// Set the center.
+ void setCenter(Point const &p) { _center = p; }
+ /// Set the center by coordinates.
+ void setCenter(Coord cx, Coord cy) { _center[X] = cx; _center[Y] = cy; }
+ /// Set both rays of the ellipse.
+ void setRays(Point const &p) { _rays = p; }
+ /// Set both rays of the ellipse as coordinates.
+ void setRays(Coord x, Coord y) { _rays[X] = x; _rays[Y] = y; }
+ /// Set one of the rays of the ellipse.
+ void setRay(Coord r, Dim2 d) { _rays[d] = r; }
+ /// Set the angle the X ray makes with the +X axis.
+ void setRotationAngle(Angle a) { _angle = a; }
+
+ Point center() const { return _center; }
+ Coord center(Dim2 d) const { return _center[d]; }
+ /// Get both rays as a point.
+ Point rays() const { return _rays; }
+ /// Get one ray of the ellipse.
+ Coord ray(Dim2 d) const { return _rays[d]; }
+ /// Get the angle the X ray makes with the +X axis.
+ Angle rotationAngle() const { return _angle; }
+ /// Get the point corresponding to the +X ray of the ellipse.
+ Point initialPoint() const;
+ /// Get the point corresponding to the +X ray of the ellipse.
+ Point finalPoint() const { return initialPoint(); }
+
+ /** @brief Create an ellipse passing through the specified points
+ * At least five points have to be specified. */
+ void fit(std::vector<Point> const& points);
+
+ /** @brief Create an elliptical arc from a section of the ellipse.
+ * This is mainly useful to determine the flags of the new arc.
+ * The passed points should lie on the ellipse, otherwise the results
+ * will be undefined.
+ * @param ip Initial point of the arc
+ * @param inner Point in the middle of the arc, used to pick one of two possibilities
+ * @param fp Final point of the arc
+ * @return Newly allocated arc, delete when no longer used */
+ EllipticalArc *arc(Point const &ip, Point const &inner, Point const &fp);
+
+ /** @brief Return an ellipse with less degrees of freedom.
+ * The canonical form always has the angle less than \f$\frac{\pi}{2}\f$,
+ * and zero if the rays are equal (i.e. the ellipse is a circle). */
+ Ellipse canonicalForm() const;
+ void makeCanonical();
+
+ /** @brief Compute the transform that maps the unit circle to this ellipse.
+ * Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
+ * This function returns the transform that maps the unit circle to this ellipse.
+ * @return Transform from unit circle to the ellipse */
+ Affine unitCircleTransform() const;
+ /** @brief Compute the transform that maps this ellipse to the unit circle.
+ * This may be a little more precise and/or faster than simply using
+ * unitCircleTransform().inverse(). An exception will be thrown for
+ * degenerate ellipses. */
+ Affine inverseUnitCircleTransform() const;
+
+ LineSegment majorAxis() const { return ray(X) >= ray(Y) ? axis(X) : axis(Y); }
+ LineSegment minorAxis() const { return ray(X) < ray(Y) ? axis(X) : axis(Y); }
+ LineSegment semimajorAxis(int sign = 1) const {
+ return ray(X) >= ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
}
-
- // build an ellipse by its implicit equation:
- // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
- void set(double A, double B, double C, double D, double E, double F);
-
- // biuld up the best fitting ellipse wrt the passed points
- // prerequisite: at least 5 points must be passed
- void set(std::vector<Point> const& points);
-
- EllipticalArc *
- arc(Point const& initial, Point const& inner, Point const& final, bool svg_compliant = true);
-
- Point center() const
- {
- return m_centre;
+ LineSegment semiminorAxis(int sign = 1) const {
+ return ray(X) < ray(Y) ? semiaxis(X, sign) : semiaxis(Y, sign);
}
-
- Coord center(Dim2 d) const
- {
- return m_centre[d];
+ LineSegment axis(Dim2 d) const;
+ LineSegment semiaxis(Dim2 d, int sign = 1) const;
+
+ /// Get the coefficients of the ellipse's implicit equation.
+ std::vector<double> coefficients() const;
+ void coefficients(Coord &A, Coord &B, Coord &C, Coord &D, Coord &E, Coord &F) const;
+
+ /** @brief Evaluate a point on the ellipse.
+ * The parameter range is \f$[0, 2\pi)\f$; larger and smaller values
+ * wrap around. */
+ Point pointAt(Coord t) const;
+ /// Evaluate a single coordinate of a point on the ellipse.
+ Coord valueAt(Coord t, Dim2 d) const;
+
+ /** @brief Find the time value of a point on an ellipse.
+ * If the point is not on the ellipse, the returned time value will correspond
+ * to an intersection with a ray from the origin passing through the point
+ * with the ellipse. Note that this is NOT the nearest point on the ellipse. */
+ Coord timeAt(Point const &p) const;
+
+ /// Get the value of the derivative at time t normalized to unit length.
+ Point unitTangentAt(Coord t) const;
+
+ /// Check whether the ellipse contains the given point.
+ bool contains(Point const &p) const;
+
+ /// Compute intersections with an infinite line.
+ std::vector<ShapeIntersection> intersect(Line const &line) const;
+ /// Compute intersections with a line segment.
+ std::vector<ShapeIntersection> intersect(LineSegment const &seg) const;
+ /// Compute intersections with another ellipse.
+ std::vector<ShapeIntersection> intersect(Ellipse const &other) const;
+ /// Compute intersections with a 2D Bezier polynomial.
+ std::vector<ShapeIntersection> intersect(D2<Bezier> const &other) const;
+
+ Ellipse &operator*=(Translate const &t) {
+ _center *= t;
+ return *this;
}
-
- Coord ray(Dim2 d) const
- {
- return m_ray[d];
+ Ellipse &operator*=(Scale const &s) {
+ _center *= s;
+ _rays *= s;
+ return *this;
}
-
- Coord rot_angle() const
- {
- return m_angle;
+ Ellipse &operator*=(Zoom const &z) {
+ _center *= z;
+ _rays *= z.scale();
+ return *this;
}
+ Ellipse &operator*=(Rotate const &r);
+ Ellipse &operator*=(Affine const &m);
- std::vector<double> implicit_form_coefficients() const;
-
- Ellipse transformed(Affine const& m) const;
-
- private:
- Point m_centre, m_ray;
- double m_angle;
+ /// Compare ellipses for exact equality.
+ bool operator==(Ellipse const &other) const;
};
+/** @brief Test whether two ellipses are approximately the same.
+ * This will check whether no point on ellipse a is further away from
+ * the corresponding point on ellipse b than precision.
+ * @relates Ellipse */
+bool are_near(Ellipse const &a, Ellipse const &b, Coord precision = EPSILON);
-} // end namespace Geom
-
+/** @brief Outputs ellipse data, useful for debugging.
+ * @relates Ellipse */
+std::ostream &operator<<(std::ostream &out, Ellipse const &e);
+} // end namespace Geom
-#endif // _2GEOM_ELLIPSE_H_
-
+#endif // LIB2GEOM_SEEN_ELLIPSE_H
/*
Local Variables:
diff --git a/src/2geom/svg-elliptical-arc.cpp b/src/2geom/elliptical-arc-from-sbasis.cpp
index 96a4f99d8..54f995fb6 100644
--- a/src/2geom/svg-elliptical-arc.cpp
+++ b/src/2geom/elliptical-arc-from-sbasis.cpp
@@ -1,6 +1,8 @@
-/*
- * SVG Elliptical Arc Class
+/** @file
+ * @brief Fitting elliptical arc to SBasis
*
+ * This file contains the implementation of the function arc_from_sbasis.
+ *//*
* Copyright 2008 Marco Cecchetti <mrcekets at gmail.com>
*
* This library is free software; you can redistribute it and/or
@@ -27,33 +29,120 @@
* the specific language governing rights and limitations.
*/
-
+#include <2geom/curve.h>
+#include <2geom/angle.h>
+#include <2geom/utils.h>
#include <2geom/bezier-curve.h>
-#include <2geom/ellipse.h>
-#include <2geom/numeric/fitting-model.h>
-#include <2geom/numeric/fitting-tool.h>
+#include <2geom/elliptical-arc.h>
+#include <2geom/sbasis-curve.h> // for non-native methods
#include <2geom/numeric/vector.h>
-#include <2geom/poly.h>
-#include <2geom/sbasis-geometric.h>
-#include <2geom/svg-elliptical-arc.h>
-
-#include <cfloat>
-#include <limits>
-#include <memory>
+#include <2geom/numeric/fitting-tool.h>
+#include <2geom/numeric/fitting-model.h>
+#include <algorithm>
+namespace Geom {
-namespace Geom
+// forward declation
+namespace detail
{
+ struct ellipse_equation;
+}
-/**
- * @class SVGEllipticalArc
- * @brief SVG 1.1-compliant elliptical arc.
+/*
+ * make_elliptical_arc
*
- * This class is almost identical to the normal elliptical arc, but it differs slightly
- * in the handling of degenerate arcs to be compliant with SVG 1.1 implementation guidelines.
+ * convert a parametric polynomial curve given in symmetric power basis form
+ * into an EllipticalArc type; in order to be successfull the input curve
+ * has to look like an actual elliptical arc even if a certain tolerance
+ * is allowed through an ad-hoc parameter.
+ * The conversion is performed through an interpolation on a certain amount of
+ * sample points computed on the input curve;
+ * the interpolation computes the coefficients of the general implicit equation
+ * of an ellipse (A*X^2 + B*XY + C*Y^2 + D*X + E*Y + F = 0), then from the
+ * implicit equation we compute the parametric form.
*
- * @ingroup Curves
*/
+class make_elliptical_arc
+{
+ public:
+ typedef D2<SBasis> curve_type;
+
+ /*
+ * constructor
+ *
+ * it doesn't execute the conversion but set the input and output parameters
+ *
+ * _ea: the output EllipticalArc that will be generated;
+ * _curve: the input curve to be converted;
+ * _total_samples: the amount of sample points to be taken
+ * on the input curve for performing the conversion
+ * _tolerance: how much likelihood is required between the input curve
+ * and the generated elliptical arc; the smaller it is the
+ * the tolerance the higher it is the likelihood.
+ */
+ make_elliptical_arc( EllipticalArc& _ea,
+ curve_type const& _curve,
+ unsigned int _total_samples,
+ double _tolerance );
+
+ private:
+ bool bound_exceeded( unsigned int k, detail::ellipse_equation const & ee,
+ double e1x, double e1y, double e2 );
+
+ bool check_bound(double A, double B, double C, double D, double E, double F);
+
+ void fit();
+
+ bool make_elliptiarc();
+
+ void print_bound_error(unsigned int k)
+ {
+ std::cerr
+ << "tolerance error" << std::endl
+ << "at point: " << k << std::endl
+ << "error value: "<< dist_err << std::endl
+ << "bound: " << dist_bound << std::endl
+ << "angle error: " << angle_err
+ << " (" << angle_tol << ")" << std::endl;
+ }
+
+ public:
+ /*
+ * perform the actual conversion
+ * return true if the conversion is successfull, false on the contrary
+ */
+ bool operator()()
+ {
+ // initialize the reference
+ const NL::Vector & coeff = fitter.result();
+ fit();
+ if ( !check_bound(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]) )
+ return false;
+ if ( !(make_elliptiarc()) ) return false;
+ return true;
+ }
+
+ private:
+ EllipticalArc& ea; // output elliptical arc
+ const curve_type & curve; // input curve
+ Piecewise<D2<SBasis> > dcurve; // derivative of the input curve
+ NL::LFMEllipse model; // model used for fitting
+ // perform the actual fitting task
+ NL::least_squeares_fitter<NL::LFMEllipse> fitter;
+ // tolerance: the user-defined tolerance parameter;
+ // tol_at_extr: the tolerance at end-points automatically computed
+ // on the value of "tolerance", and usually more strict;
+ // tol_at_center: tolerance at the center of the ellipse
+ // angle_tol: tolerance for the angle btw the input curve tangent
+ // versor and the ellipse normal versor at the sample points
+ double tolerance, tol_at_extr, tol_at_center, angle_tol;
+ Point initial_point, final_point; // initial and final end-points
+ unsigned int N; // total samples
+ unsigned int last; // N-1
+ double partitions; // N-1
+ std::vector<Point> p; // sample points
+ double dist_err, dist_bound, angle_err;
+};
namespace detail
{
@@ -97,7 +186,7 @@ struct ellipse_equation
double A, B, C, D, E, F;
};
-}
+} // end namespace detail
make_elliptical_arc::
make_elliptical_arc( EllipticalArc& _ea,
@@ -110,8 +199,7 @@ make_elliptical_arc( EllipticalArc& _ea,
tolerance(_tolerance), tol_at_extr(tolerance/2),
tol_at_center(0.1), angle_tol(0.1),
initial_point(curve.at0()), final_point(curve.at1()),
- N(_total_samples), last(N-1), partitions(N-1), p(N),
- svg_compliant(true)
+ N(_total_samples), last(N-1), partitions(N-1), p(N)
{
}
@@ -207,7 +295,7 @@ bool make_elliptical_arc::make_elliptiarc()
Ellipse e;
try
{
- e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
}
catch(LogicalError const &exc)
{
@@ -216,33 +304,12 @@ bool make_elliptical_arc::make_elliptiarc()
Point inner_point = curve(0.5);
- if (svg_compliant_flag())
- {
-#ifdef CPP11
- std::unique_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) );
-#else
- std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point, true) );
-#endif
- ea = *arc;
- }
- else
- {
- try
- {
#ifdef CPP11
- std::unique_ptr<EllipticalArc>
+ std::unique_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) );
#else
- std::auto_ptr<EllipticalArc>
+ std::auto_ptr<EllipticalArc> arc( e.arc(initial_point, inner_point, final_point) );
#endif
- eap( e.arc(initial_point, inner_point, final_point, false) );
- ea = *eap;
- }
- catch(RangeError const &exc)
- {
- return false;
- }
- }
-
+ ea = *arc;
if ( !are_near( e.center(),
ea.center(),
@@ -257,10 +324,14 @@ bool make_elliptical_arc::make_elliptiarc()
-} // end namespace Geom
-
-
+bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in,
+ double tolerance, unsigned num_samples)
+{
+ make_elliptical_arc convert(ea, in, num_samples, tolerance);
+ return convert();
+}
+} // end namespace Geom
/*
Local Variables:
@@ -272,4 +343,3 @@ bool make_elliptical_arc::make_elliptiarc()
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-
diff --git a/src/2geom/elliptical-arc.cpp b/src/2geom/elliptical-arc.cpp
index c96d5f1a6..25a78b4ad 100644
--- a/src/2geom/elliptical-arc.cpp
+++ b/src/2geom/elliptical-arc.cpp
@@ -34,11 +34,11 @@
#include <limits>
#include <memory>
-#include <2geom/elliptical-arc.h>
+#include <2geom/bezier-curve.h>
#include <2geom/ellipse.h>
+#include <2geom/elliptical-arc.h>
+#include <2geom/path-sink.h>
#include <2geom/sbasis-geometric.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/poly.h>
#include <2geom/transforms.h>
#include <2geom/utils.h>
@@ -46,7 +46,6 @@
#include <2geom/numeric/fitting-tool.h>
#include <2geom/numeric/fitting-model.h>
-
namespace Geom
{
@@ -84,18 +83,27 @@ namespace Geom
* and the ellipse's rotation. Each set of those parameters corresponds to four different arcs,
* with two of them larger than half an ellipse and two of them turning clockwise while traveling
* from initial to final point. The two flags disambiguate between them: "large arc flag" selects
- * the bigger arc, while the "sweep flag" selects the clockwise arc.
+ * the bigger arc, while the "sweep flag" selects the arc going in the direction of positive
+ * angles. Angles always increase when going from the +X axis in the direction of the +Y axis,
+ * so if Y grows downwards, this means clockwise.
*
- * @image html elliptical-arc-flags.png "The four possible arcs and the meaning of flags"
+ * @image html elliptical-arc-flags.png "Meaning of arc flags (Y grows downwards)"
*
* @ingroup Curves
*/
Rect EllipticalArc::boundsExact() const
{
+ if (isChord()) {
+ return chord().boundsExact();
+ }
+
+ using std::swap;
+
+ // TODO: simplify / document what is going on here.
double extremes[4];
double sinrot, cosrot;
- sincos(_rot_angle, sinrot, cosrot);
+ sincos(rotationAngle(), sinrot, cosrot);
extremes[0] = std::atan2( -ray(Y) * sinrot, ray(X) * cosrot );
extremes[1] = extremes[0] + M_PI;
@@ -109,11 +117,11 @@ Rect EllipticalArc::boundsExact() const
arc_extremes[0] = initialPoint()[X];
arc_extremes[1] = finalPoint()[X];
if ( arc_extremes[0] < arc_extremes[1] )
- std::swap(arc_extremes[0], arc_extremes[1]);
+ swap(arc_extremes[0], arc_extremes[1]);
arc_extremes[2] = initialPoint()[Y];
arc_extremes[3] = finalPoint()[Y];
if ( arc_extremes[2] < arc_extremes[3] )
- std::swap(arc_extremes[2], arc_extremes[3]);
+ swap(arc_extremes[2], arc_extremes[3]);
if ( !are_near(initialPoint(), finalPoint()) ) {
for (unsigned i = 0; i < 4; ++i) {
@@ -130,37 +138,23 @@ Rect EllipticalArc::boundsExact() const
Point EllipticalArc::pointAtAngle(Coord t) const
{
- Point ret = Point::polar(t) * unitCircleTransform();
+ Point ret = _ellipse.pointAt(t);
return ret;
}
Coord EllipticalArc::valueAtAngle(Coord t, Dim2 d) const
{
- Coord sinrot, cosrot, cost, sint;
- sincos(_rot_angle, sinrot, cosrot);
- sincos(t, sint, cost);
-
- if ( d == X ) {
- return ray(X) * cosrot * cost
- - ray(Y) * sinrot * sint
- + center(X);
- } else {
- return ray(X) * sinrot * cost
- + ray(Y) * cosrot * sint
- + center(Y);
- }
-}
-
-Affine EllipticalArc::unitCircleTransform() const
-{
- Affine ret = Scale(ray(X), ray(Y)) * Rotate(_rot_angle);
- ret.setTranslation(center());
- return ret;
+ return _ellipse.valueAt(t, d);
}
std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
+ if (isChord()) {
+ return chord().roots(v, d);
+ }
+
std::vector<Coord> sol;
+ Interval unit_interval(0, 1);
if ( are_near(ray(X), 0) && are_near(ray(Y), 0) ) {
if ( center(d) == v )
@@ -188,7 +182,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
for ( unsigned int dim = 0; dim < 2; ++dim )
{
- if ( are_near(ray((Dim2) dim), 0) )
+ if (ray((Dim2) dim) == 0)
{
if ( initialPoint()[d] == v && finalPoint()[d] == v )
{
@@ -210,18 +204,18 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
case X:
switch(dim)
{
- case X: ray_prj = -ray(Y) * std::sin(_rot_angle);
+ case X: ray_prj = -ray(Y) * std::sin(rotationAngle());
break;
- case Y: ray_prj = ray(X) * std::cos(_rot_angle);
+ case Y: ray_prj = ray(X) * std::cos(rotationAngle());
break;
}
break;
case Y:
switch(dim)
{
- case X: ray_prj = ray(Y) * std::cos(_rot_angle);
+ case X: ray_prj = ray(Y) * std::cos(rotationAngle());
break;
- case Y: ray_prj = ray(X) * std::sin(_rot_angle);
+ case Y: ray_prj = ray(X) * std::sin(rotationAngle());
break;
}
break;
@@ -236,7 +230,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
{
case X:
s = std::asin(s); // return a value in [-PI/2,PI/2]
- if ( logical_xor( _sweep, are_near(initialAngle(), M_PI/2) ) )
+ if ( logical_xor( sweep(), are_near(initialAngle(), M_PI/2) ) )
{
if ( s < 0 ) s += 2*M_PI;
}
@@ -248,7 +242,7 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
break;
case Y:
s = std::acos(s); // return a value in [0,PI]
- if ( logical_xor( _sweep, are_near(initialAngle(), 0) ) )
+ if ( logical_xor( sweep(), are_near(initialAngle(), 0) ) )
{
s = 2*M_PI - s;
if ( !(s < 2*M_PI) ) s -= 2*M_PI;
@@ -257,17 +251,22 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
}
//std::cerr << "s = " << rad_to_deg(s);
- s = map_to_01(s);
+ s = timeAtAngle(s);
//std::cerr << " -> t: " << s << std::endl;
- if ( !(s < 0 || s > 1) )
+ if (unit_interval.contains(s)) {
sol.push_back(s);
+ }
return sol;
}
}
double rotx, roty;
- sincos(_rot_angle, roty, rotx); /// \todo sin and cos are calculated in many places in this function, optimize this a bit!
- if (d == X) roty = -roty;
+ if (d == X) {
+ sincos(rotationAngle(), roty, rotx);
+ roty = -roty;
+ } else {
+ sincos(rotationAngle(), rotx, roty);
+ }
double rxrotx = ray(X) * rotx;
double c_v = center(d) - v;
@@ -312,13 +311,13 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
}
std::vector<double> arc_sol;
- for (unsigned int i = 0; i < sol.size(); ++i )
- {
+ for (unsigned int i = 0; i < sol.size(); ++i ) {
//std::cerr << "s = " << rad_to_deg(sol[i]);
- sol[i] = map_to_01(sol[i]);
+ sol[i] = timeAtAngle(sol[i]);
//std::cerr << " -> t: " << sol[i] << std::endl;
- if ( !(sol[i] < 0 || sol[i] > 1) )
+ if (unit_interval.contains(sol[i])) {
arc_sol.push_back(sol[i]);
+ }
}
return arc_sol;
}
@@ -330,18 +329,14 @@ std::vector<Coord> EllipticalArc::roots(Coord v, Dim2 d) const
// of such an angle in the cw direction
Curve *EllipticalArc::derivative() const
{
- EllipticalArc *result = static_cast<EllipticalArc*>(duplicate());
- result->_center[X] = result->_center[Y] = 0;
- result->_start_angle += M_PI/2;
- if( !( result->_start_angle < 2*M_PI ) )
- {
- result->_start_angle -= 2*M_PI;
- }
- result->_end_angle += M_PI/2;
- if( !( result->_end_angle < 2*M_PI ) )
- {
- result->_end_angle -= 2*M_PI;
+ if (isChord()) {
+ return chord().derivative();
}
+
+ EllipticalArc *result = static_cast<EllipticalArc*>(duplicate());
+ result->_ellipse.setCenter(0, 0);
+ result->_angles.setInitial(result->_angles.initialAngle() + M_PI/2);
+ result->_angles.setFinal(result->_angles.finalAngle() + M_PI/2);
result->_initial_point = result->pointAtAngle( result->initialAngle() );
result->_final_point = result->pointAtAngle( result->finalAngle() );
return result;
@@ -351,18 +346,21 @@ Curve *EllipticalArc::derivative() const
std::vector<Point>
EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
{
+ if (isChord()) {
+ return chord().pointAndDerivatives(t, n);
+ }
+
unsigned int nn = n+1; // nn represents the size of the result vector.
std::vector<Point> result;
result.reserve(nn);
- double angle = map_unit_interval_on_circular_arc(t, initialAngle(),
- finalAngle(), _sweep);
+ double angle = angleAt(t);
std::auto_ptr<EllipticalArc> ea( static_cast<EllipticalArc*>(duplicate()) );
- ea->_center = Point(0,0);
+ ea->_ellipse.setCenter(0, 0);
unsigned int m = std::min(nn, 4u);
for ( unsigned int i = 0; i < m; ++i )
{
result.push_back( ea->pointAtAngle(angle) );
- angle += (_sweep ? M_PI/2 : -M_PI/2);
+ angle += (sweep() ? M_PI/2 : -M_PI/2);
if ( !(angle < 2*M_PI) ) angle -= 2*M_PI;
}
m = nn / 4;
@@ -381,9 +379,16 @@ EllipticalArc::pointAndDerivatives(Coord t, unsigned int n) const
return result;
}
-bool EllipticalArc::containsAngle(Coord angle) const
+Point EllipticalArc::pointAt(Coord t) const
{
- return AngleInterval::contains(angle);
+ if (isChord()) return chord().pointAt(t);
+ return _ellipse.pointAt(angleAt(t));
+}
+
+Coord EllipticalArc::valueAt(Coord t, Dim2 d) const
+{
+ if (isChord()) return chord().valueAt(t, d);
+ return valueAtAngle(angleAt(t), d);
}
Curve* EllipticalArc::portion(double f, double t) const
@@ -394,41 +399,35 @@ Curve* EllipticalArc::portion(double f, double t) const
if (t < 0) t = 0;
if (t > 1) t = 1;
- if ( are_near(f, t) )
- {
- EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
- arc->_center = arc->_initial_point = arc->_final_point = pointAt(f);
- arc->_start_angle = arc->_end_angle = _start_angle;
- arc->_rot_angle = _rot_angle;
- arc->_sweep = _sweep;
- arc->_large_arc = _large_arc;
+ if (f == t) {
+ EllipticalArc *arc = new EllipticalArc();
+ arc->_initial_point = arc->_final_point = pointAt(f);
return arc;
}
EllipticalArc *arc = static_cast<EllipticalArc*>(duplicate());
arc->_initial_point = pointAt(f);
arc->_final_point = pointAt(t);
- if ( f > t ) arc->_sweep = !_sweep;
- if ( _large_arc && fabs(sweepAngle() * (t-f)) < M_PI)
+ arc->_angles.setAngles(angleAt(f), angleAt(t));
+ if (f > t) arc->_angles.setSweep(!sweep());
+ if ( _large_arc && fabs(angularExtent() * (t-f)) <= M_PI) {
arc->_large_arc = false;
- arc->_updateCenterAndAngles(arc->isSVGCompliant()); //TODO: be more clever
+ }
return arc;
}
// the arc is the same but traversed in the opposite direction
-Curve *EllipticalArc::reverse() const {
+Curve *EllipticalArc::reverse() const
+{
+ using std::swap;
EllipticalArc *rarc = static_cast<EllipticalArc*>(duplicate());
- rarc->_sweep = !_sweep;
- rarc->_initial_point = _final_point;
- rarc->_final_point = _initial_point;
- rarc->_start_angle = _end_angle;
- rarc->_end_angle = _start_angle;
- rarc->_updateCenterAndAngles(rarc->isSVGCompliant());
+ rarc->_angles.reverse();
+ swap(rarc->_initial_point, rarc->_final_point);
return rarc;
}
#ifdef HAVE_GSL // GSL is required for function "solve_reals"
-std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from, double to ) const
+std::vector<double> EllipticalArc::allNearestTimes( Point const& p, double from, double to ) const
{
std::vector<double> result;
@@ -446,11 +445,11 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
else if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
{
LineSegment seg(pointAt(from), pointAt(to));
- Point np = seg.pointAt( seg.nearestPoint(p) );
+ Point np = seg.pointAt( seg.nearestTime(p) );
if ( are_near(ray(Y), 0) )
{
- if ( are_near(_rot_angle, M_PI/2)
- || are_near(_rot_angle, 3*M_PI/2) )
+ if ( are_near(rotationAngle(), M_PI/2)
+ || are_near(rotationAngle(), 3*M_PI/2) )
{
result = roots(np[Y], Y);
}
@@ -461,8 +460,8 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
}
else
{
- if ( are_near(_rot_angle, M_PI/2)
- || are_near(_rot_angle, 3*M_PI/2) )
+ if ( are_near(rotationAngle(), M_PI/2)
+ || are_near(rotationAngle(), 3*M_PI/2) )
{
result = roots(np[X], X);
}
@@ -521,7 +520,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
Point p_c = p - center();
double rx2_ry2 = (ray(X) - ray(Y)) * (ray(X) + ray(Y));
double sinrot, cosrot;
- sincos(_rot_angle, sinrot, cosrot);
+ sincos(rotationAngle(), sinrot, cosrot);
double expr1 = ray(X) * (p_c[X] * cosrot + p_c[Y] * sinrot);
Poly coeff;
coeff.resize(5);
@@ -570,7 +569,7 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
double mindistsq1 = std::numeric_limits<double>::max();
double mindistsq2 = std::numeric_limits<double>::max();
- double dsq;
+ double dsq = 0;
unsigned int mi1 = 0, mi2 = 0;
for ( unsigned int i = 0; i < real_sol.size(); ++i )
{
@@ -589,14 +588,14 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
}
}
- double t = map_to_01( real_sol[mi1] );
+ double t = timeAtAngle(real_sol[mi1]);
if ( !(t < from || t > to) )
{
result.push_back(t);
}
bool second_sol = false;
- t = map_to_01( real_sol[mi2] );
+ t = timeAtAngle(real_sol[mi2]);
if ( real_sol.size() == 4 && !(t < from || t > to) )
{
if ( result.empty() || are_near(mindistsq1, mindistsq2) )
@@ -656,227 +655,155 @@ std::vector<double> EllipticalArc::allNearestPoints( Point const& p, double from
}
#endif
-/*
- * NOTE: this implementation follows Standard SVG 1.1 implementation guidelines
- * for elliptical arc curves. See Appendix F.6.
- */
-void EllipticalArc::_updateCenterAndAngles(bool svg)
-{
- Point d = initialPoint() - finalPoint();
- // TODO move this to SVGElipticalArc?
- if (svg)
- {
- if ( initialPoint() == finalPoint() )
- {
- _rot_angle = _start_angle = _end_angle = 0;
- _center = initialPoint();
- _rays = Geom::Point(0,0);
- _large_arc = _sweep = false;
- return;
+void EllipticalArc::_filterIntersections(std::vector<ShapeIntersection> &xs, bool is_first) const
+{
+ Interval unit(0, 1);
+ std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend();
+ while (i != last) {
+ Coord &t = is_first ? i->first : i->second;
+ assert(are_near(_ellipse.pointAt(t), i->point(), 1e-6));
+ t = timeAtAngle(t);
+ if (!unit.contains(t)) {
+ xs.erase((++i).base());
+ continue;
+ } else {
+ assert(are_near(pointAt(t), i->point(), 1e-6));
+ ++i;
}
+ }
+}
- _rays[X] = std::fabs(_rays[X]);
- _rays[Y] = std::fabs(_rays[Y]);
-
- if ( are_near(ray(X), 0) || are_near(ray(Y), 0) )
- {
- _rays[X] = L2(d) / 2;
- _rays[Y] = 0;
- _rot_angle = std::atan2(d[Y], d[X]);
- _start_angle = 0;
- _end_angle = M_PI;
- _center = middle_point(initialPoint(), finalPoint());
- _large_arc = false;
- _sweep = false;
- return;
- }
+std::vector<CurveIntersection> EllipticalArc::intersect(Curve const &other, Coord eps) const
+{
+ if (isLineSegment()) {
+ LineSegment ls(_initial_point, _final_point);
+ return ls.intersect(other, eps);
}
- else
- {
- if ( are_near(initialPoint(), finalPoint()) )
- {
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- {
- _start_angle = _end_angle = 0;
- _center = initialPoint();
- return;
- }
- else
- {
- THROW_RANGEERROR("initial and final point are the same");
- }
- }
- if ( are_near(ray(X), 0) && are_near(ray(Y), 0) )
- { // but initialPoint != finalPoint
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 && ray(Y) == 0 but initialPoint != finalPoint"
- );
- }
- if ( are_near(ray(Y), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(X)*ray(X)) )
- {
- Angle angle(v);
- if ( are_near( angle, _rot_angle ) )
- {
- _start_angle = 0;
- _end_angle = M_PI;
- _center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( are_near( angle, _rot_angle ) )
- {
- _start_angle = M_PI;
- _end_angle = 0;
- _center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle "
- "and != rotation_angle + PI"
- );
- }
- if ( L2sq(v) > 4*ray(X)*ray(X) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) > 2*ray(X)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(Y) == 0 and distance(initialPoint, finalPoint) < 2*ray(X)"
- );
- }
- }
+ std::vector<CurveIntersection> result;
- if ( are_near(ray(X), 0) )
- {
- Point v = initialPoint() - finalPoint();
- if ( are_near(L2sq(v), 4*ray(Y)*ray(Y)) )
- {
- double angle = std::atan2(v[Y], v[X]);
- if (angle < 0) angle += 2*M_PI;
- double rot_angle = _rot_angle.radians() + M_PI/2;
- if ( !(rot_angle < 2*M_PI) ) rot_angle -= 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- _start_angle = M_PI/2;
- _end_angle = 3*M_PI/2;
- _center = v/2 + finalPoint();
- return;
- }
- angle -= M_PI;
- if ( angle < 0 ) angle += 2*M_PI;
- if ( are_near( angle, rot_angle ) )
- {
- _start_angle = 3*M_PI/2;
- _end_angle = M_PI/2;
- _center = v/2 + finalPoint();
- return;
- }
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 "
- "and slope(initialPoint - finalPoint) != rotation_angle + PI/2 "
- "and != rotation_angle + (3/2)*PI"
- );
- }
- if ( L2sq(v) > 4*ray(Y)*ray(Y) )
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) > 2*ray(Y)"
- );
- }
- else
- {
- THROW_RANGEERROR(
- "there is infinite ellipses that satisfy the given constraints: "
- "ray(X) == 0 and distance(initialPoint, finalPoint) < 2*ray(Y)"
- );
- }
+ if (other.isLineSegment()) {
+ LineSegment ls(other.initialPoint(), other.finalPoint());
+ result = _ellipse.intersect(ls);
+ _filterIntersections(result, true);
+ return result;
+ }
- }
+ BezierCurve const *bez = dynamic_cast<BezierCurve const *>(&other);
+ if (bez) {
+ result = _ellipse.intersect(bez->fragment());
+ _filterIntersections(result, true);
+ return result;
+ }
+ EllipticalArc const *arc = dynamic_cast<EllipticalArc const *>(&other);
+ if (arc) {
+ result = _ellipse.intersect(arc->_ellipse);
+ _filterIntersections(result, true);
+ arc->_filterIntersections(result, false);
+ return result;
}
- Rotate rm(_rot_angle);
- Affine m(rm);
- m[1] = -m[1];
- m[2] = -m[2];
-
- Point p = (d / 2) * m;
- double rx2 = _rays[X] * _rays[X];
- double ry2 = _rays[Y] * _rays[Y];
- double rxpy = _rays[X] * p[Y];
- double rypx = _rays[Y] * p[X];
- double rx2py2 = rxpy * rxpy;
- double ry2px2 = rypx * rypx;
- double num = rx2 * ry2;
- double den = rx2py2 + ry2px2;
- assert(den != 0);
- double rad = num / den;
- Point c(0,0);
- if (rad > 1)
- {
- rad -= 1;
- rad = std::sqrt(rad);
+ // in case someone wants to make a custom curve type
+ result = other.intersect(*this, eps);
+ transpose_in_place(result);
+ return result;
+}
- if (_large_arc == _sweep) rad = -rad;
- c = rad * Point(rxpy / ray(Y), -rypx / ray(X));
- _center = c * rm + middle_point(initialPoint(), finalPoint());
- }
- else if (rad == 1 || svg)
- {
- double lamda = std::sqrt(1 / rad);
- _rays[X] *= lamda;
- _rays[Y] *= lamda;
- _center = middle_point(initialPoint(), finalPoint());
- }
- else
- {
- THROW_RANGEERROR(
- "there is no ellipse that satisfies the given constraints"
- );
+void EllipticalArc::_updateCenterAndAngles()
+{
+ // See: http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ Point d = initialPoint() - finalPoint();
+ Point mid = middle_point(initialPoint(), finalPoint());
+
+ // if ip = sp, the arc contains no other points
+ if (initialPoint() == finalPoint()) {
+ _ellipse = Ellipse();
+ _ellipse.setCenter(initialPoint());
+ _angles = AngleInterval();
+ _large_arc = false;
+ return;
+ }
+
+ // rays should be positive
+ _ellipse.setRays(std::fabs(ray(X)), std::fabs(ray(Y)));
+
+ if (isChord()) {
+ _ellipse.setRays(L2(d) / 2, 0);
+ _ellipse.setRotationAngle(atan2(d));
+ _ellipse.setCenter(mid);
+ _angles.setAngles(0, M_PI);
+ _angles.setSweep(false);
+ _large_arc = false;
+ return;
+ }
+
+ Rotate rot(rotationAngle()); // the matrix in F.6.5.3
+ Rotate invrot = rot.inverse(); // the matrix in F.6.5.1
+
+ Point r = rays();
+ Point p = (initialPoint() - mid) * invrot; // x', y' in F.6.5.1
+ Point c(0,0); // cx', cy' in F.6.5.2
+
+ // Correct out-of-range radii
+ Coord lambda = hypot(p[X]/r[X], p[Y]/r[Y]);
+ if (lambda > 1) {
+ r *= lambda;
+ _ellipse.setRays(r);
+ _ellipse.setCenter(mid);
+ } else {
+ // evaluate F.6.5.2
+ Coord rxry = r[X]*r[X] * r[Y]*r[Y];
+ Coord pxry = p[X]*p[X] * r[Y]*r[Y];
+ Coord rxpy = r[X]*r[X] * p[Y]*p[Y];
+ Coord rad = (rxry - pxry - rxpy)/(rxpy + pxry);
+ // normally rad should never be negative, but numerical inaccuracy may cause this
+ if (rad > 0) {
+ rad = std::sqrt(rad);
+ if (sweep() == _large_arc) {
+ rad = -rad;
+ }
+ c = rad * Point(r[X]*p[Y]/r[Y], -r[Y]*p[X]/r[X]);
+ _ellipse.setCenter(c * rot + mid);
+ } else {
+ _ellipse.setCenter(mid);
+ }
}
- Point sp((p[X] - c[X]) / ray(X), (p[Y] - c[Y]) / ray(Y));
- Point ep((-p[X] - c[X]) / ray(X), (-p[Y] - c[Y]) / ray(Y));
+ // Compute start and end angles.
+ // If the ellipse was enlarged, c will be zero - this is correct.
+ Point sp((p[X] - c[X]) / r[X], (p[Y] - c[Y]) / r[Y]);
+ Point ep((-p[X] - c[X]) / r[X], (-p[Y] - c[Y]) / r[Y]);
Point v(1, 0);
- _start_angle = angle_between(v, sp);
- double sweep_angle = angle_between(sp, ep);
- if (!_sweep && sweep_angle > 0) sweep_angle -= 2*M_PI;
- if (_sweep && sweep_angle < 0) sweep_angle += 2*M_PI;
- _end_angle = _start_angle;
- _end_angle += sweep_angle;
+ _angles.setInitial(angle_between(v, sp));
+ _angles.setFinal(angle_between(v, ep));
+
+ /*double sweep_angle = angle_between(sp, ep);
+ if (!sweep() && sweep_angle > 0) sweep_angle -= 2*M_PI;
+ if (sweep() && sweep_angle < 0) sweep_angle += 2*M_PI;*/
}
D2<SBasis> EllipticalArc::toSBasis() const
{
+ if (isChord()) {
+ return chord().toSBasis();
+ }
+
D2<SBasis> arc;
// the interval of parametrization has to be [0,1]
- Coord et = initialAngle().radians() + ( _sweep ? sweepAngle() : -sweepAngle() );
- Linear param(initialAngle(), et);
- Coord cos_rot_angle, sin_rot_angle;
- sincos(_rot_angle, sin_rot_angle, cos_rot_angle);
+ Coord et = initialAngle().radians() + sweepAngle();
+ Linear param(initialAngle().radians(), et);
+ Coord cosrot, sinrot;
+ sincos(rotationAngle(), sinrot, cosrot);
// order = 4 seems to be enough to get a perfect looking elliptical arc
SBasis arc_x = ray(X) * cos(param,4);
SBasis arc_y = ray(Y) * sin(param,4);
- arc[0] = arc_x * cos_rot_angle - arc_y * sin_rot_angle + Linear(center(X),center(X));
- arc[1] = arc_x * sin_rot_angle + arc_y * cos_rot_angle + Linear(center(Y),center(Y));
+ arc[0] = arc_x * cosrot - arc_y * sinrot + Linear(center(X), center(X));
+ arc[1] = arc_x * sinrot + arc_y * cosrot + Linear(center(Y), center(Y));
// ensure that endpoints remain exact
for ( int d = 0 ; d < 2 ; d++ ) {
@@ -887,22 +814,96 @@ D2<SBasis> EllipticalArc::toSBasis() const
return arc;
}
+// All operations that do not contain skew can be evaulated
+// without passing through the implicit form of the ellipse,
+// which preserves precision.
-Curve *EllipticalArc::transformed(Affine const& m) const
+void EllipticalArc::operator*=(Translate const &tr)
{
- Ellipse e(center(X), center(Y), ray(X), ray(Y), _rot_angle);
- Ellipse et = e.transformed(m);
- Point inner_point = pointAt(0.5);
- return et.arc( initialPoint() * m,
- inner_point * m,
- finalPoint() * m,
- isSVGCompliant() );
+ _initial_point *= tr;
+ _final_point *= tr;
+ _ellipse *= tr;
+}
+
+void EllipticalArc::operator*=(Scale const &s)
+{
+ _initial_point *= s;
+ _final_point *= s;
+ _ellipse *= s;
+}
+
+void EllipticalArc::operator*=(Rotate const &r)
+{
+ _initial_point *= r;
+ _final_point *= r;
+ _ellipse *= r;
+}
+
+void EllipticalArc::operator*=(Zoom const &z)
+{
+ _initial_point *= z;
+ _final_point *= z;
+ _ellipse *= z;
+}
+
+void EllipticalArc::operator*=(Affine const& m)
+{
+ if (isChord()) {
+ _initial_point *= m;
+ _final_point *= m;
+ _ellipse.setCenter(middle_point(_initial_point, _final_point));
+ _ellipse.setRays(0, 0);
+ _ellipse.setRotationAngle(0);
+ return;
+ }
+
+ _initial_point *= m;
+ _final_point *= m;
+ _ellipse *= m;
+ if (m.det() < 0) {
+ _angles.setSweep(!sweep());
+ }
+
+ // ellipse transformation does not preserve its functional form,
+ // i.e. e.pointAt(0.5)*m and (e*m).pointAt(0.5) can be different.
+ // We need to recompute start / end angles.
+ _angles.setInitial(_ellipse.timeAt(_initial_point));
+ _angles.setFinal(_ellipse.timeAt(_final_point));
+}
+
+bool EllipticalArc::operator==(Curve const &c) const
+{
+ EllipticalArc const *other = dynamic_cast<EllipticalArc const *>(&c);
+ if (!other) return false;
+ if (_initial_point != other->_initial_point) return false;
+ if (_final_point != other->_final_point) return false;
+ // TODO: all arcs with ellipse rays which are too small
+ // and fall back to a line should probably be equal
+ if (rays() != other->rays()) return false;
+ if (rotationAngle() != other->rotationAngle()) return false;
+ if (_large_arc != other->_large_arc) return false;
+ if (sweep() != other->sweep()) return false;
+ return true;
+}
+
+void EllipticalArc::feed(PathSink &sink, bool moveto_initial) const
+{
+ if (moveto_initial) {
+ sink.moveTo(_initial_point);
+ }
+ sink.arcTo(ray(X), ray(Y), rotationAngle(), _large_arc, sweep(), _final_point);
}
-Coord EllipticalArc::map_to_01(Coord angle) const
+std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea)
{
- return map_circular_arc_on_unit_interval(angle, initialAngle(),
- finalAngle(), _sweep);
+ out << "EllipticalArc("
+ << ea.initialPoint() << ", "
+ << format_coord_nice(ea.ray(X)) << ", " << format_coord_nice(ea.ray(Y)) << ", "
+ << format_coord_nice(ea.rotationAngle()) << ", "
+ << "large_arc=" << (ea.largeArc() ? "true" : "false") << ", "
+ << "sweep=" << (ea.sweep() ? "true" : "false") << ", "
+ << ea.finalPoint() << ")";
+ return out;
}
} // end namespace Geom
diff --git a/src/2geom/elliptical-arc.h b/src/2geom/elliptical-arc.h
index 5527aa6bc..fbb290dca 100644
--- a/src/2geom/elliptical-arc.h
+++ b/src/2geom/elliptical-arc.h
@@ -34,90 +34,110 @@
* the specific language governing rights and limitations.
*/
-#ifndef _2GEOM_ELLIPTICAL_ARC_H_
-#define _2GEOM_ELLIPTICAL_ARC_H_
+#ifndef LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
+#define LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
#include <algorithm>
+#include <2geom/affine.h>
#include <2geom/angle.h>
#include <2geom/bezier-curve.h>
#include <2geom/curve.h>
-#include <2geom/affine.h>
+#include <2geom/ellipse.h>
#include <2geom/sbasis-curve.h> // for non-native methods
#include <2geom/utils.h>
namespace Geom
{
-class EllipticalArc : public Curve, public AngleInterval
+class EllipticalArc : public Curve
{
public:
- /** @brief Creates an arc with all variables set to zero, and both flags to true. */
+ /** @brief Creates an arc with all variables set to zero. */
EllipticalArc()
- : AngleInterval(0, 0, true)
- , _initial_point(0,0)
+ : _initial_point(0,0)
, _final_point(0,0)
- , _rays(0,0)
- , _center(0,0)
- , _rot_angle(0)
- , _large_arc(true)
+ , _large_arc(false)
{}
/** @brief Create a new elliptical arc.
* @param ip Initial point of the arc
- * @param rx First ray of the ellipse
- * @param ry Second ray of the ellipse
+ * @param r Rays of the ellipse as a point
* @param rot Angle of rotation of the X axis of the ellipse in radians
* @param large If true, the large arc is chosen (always >= 180 degrees), otherwise
* the smaller arc is chosen
* @param sweep If true, the clockwise arc is chosen, otherwise the counter-clockwise
* arc is chosen
* @param fp Final point of the arc */
- EllipticalArc( Point ip, Coord rx, Coord ry,
+ EllipticalArc( Point const &ip, Point const &r,
Coord rot_angle, bool large_arc, bool sweep,
- Point fp
+ Point const &fp
)
- : AngleInterval(0,0,sweep)
- , _initial_point(ip)
+ : _initial_point(ip)
, _final_point(fp)
- , _rays(rx, ry)
- , _rot_angle(rot_angle)
+ , _ellipse(0, 0, r[X], r[Y], rot_angle)
+ , _angles(0, 0, sweep)
, _large_arc(large_arc)
{
- _updateCenterAndAngles(false);
+ _updateCenterAndAngles();
}
- // methods new to EllipticalArc go here
+ /// Create a new elliptical arc, giving the ellipse's rays as separate coordinates.
+ EllipticalArc( Point const &ip, Coord rx, Coord ry,
+ Coord rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ : _initial_point(ip)
+ , _final_point(fp)
+ , _ellipse(0, 0, rx, ry, rot_angle)
+ , _angles(0, 0, sweep)
+ , _large_arc(large_arc)
+ {
+ _updateCenterAndAngles();
+ }
- /// @name Retrieve and modify parameters
+ /// @name Retrieve basic information
/// @{
- /** @brief Get the interval of angles the arc contains
- * @return The interval between the final and initial angles of the arc */
- Interval angleInterval() const { return Interval(initialAngle(), finalAngle()); }
+
/** @brief Get a coordinate of the elliptical arc's center.
* @param d The dimension to retrieve
* @return The selected coordinate of the center */
- /** @brief Get the defining ellipse's rotation
- * @return Angle between the +X ray of the ellipse and the +X axis */
- Angle rotationAngle() const {
- return _rot_angle;
- }
+ Coord center(Dim2 d) const { return _ellipse.center(d); }
+
+ /** @brief Get the arc's center
+ * @return The arc's center, situated on the intersection of the ellipse's rays */
+ Point center() const { return _ellipse.center(); }
+
/** @brief Get one of the ellipse's rays
* @param d Dimension to retrieve
* @return The selected ray of the ellipse */
- Coord ray(Dim2 d) const { return _rays[d]; }
+ Coord ray(Dim2 d) const { return _ellipse.ray(d); }
+
/** @brief Get both rays as a point
* @return Point with X equal to the X ray and Y to Y ray */
- Point rays() const { return _rays; }
+ Point rays() const { return _ellipse.rays(); }
+
+ /** @brief Get the defining ellipse's rotation
+ * @return Angle between the +X ray of the ellipse and the +X axis */
+ Angle rotationAngle() const {
+ return _ellipse.rotationAngle();
+ }
+
/** @brief Whether the arc is larger than half an ellipse.
* @return True if the arc is larger than \f$\pi\f$, false otherwise */
bool largeArc() const { return _large_arc; }
+
/** @brief Whether the arc turns clockwise
* @return True if the arc makes a clockwise turn when going from initial to final
* point, false otherwise */
- bool sweep() const { return _sweep; }
- /** @brief Get the line segment connecting the arc's endpoints.
- * @return A linear segment with initial and final point correspoding to those of the arc. */
- LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
- /** @brief Change the arc's parameters. */
+ bool sweep() const { return _angles.sweep(); }
+
+ Angle initialAngle() const { return _angles.initialAngle(); }
+ Angle finalAngle() const { return _angles.finalAngle(); }
+ /// @}
+
+ /// @name Modify parameters
+ /// @{
+
+ /// Change all of the arc's parameters.
void set( Point const &ip, double rx, double ry,
double rot_angle, bool large_arc, bool sweep,
Point const &fp
@@ -125,89 +145,127 @@ public:
{
_initial_point = ip;
_final_point = fp;
- _rays[X] = rx;
- _rays[Y] = ry;
- _rot_angle = Angle(rot_angle);
+ _ellipse.setRays(rx, ry);
+ _ellipse.setRotationAngle(rot_angle);
+ _angles.setSweep(sweep);
_large_arc = large_arc;
- _sweep = sweep;
- _updateCenterAndAngles(isSVGCompliant());
+ _updateCenterAndAngles();
}
+
+ /// Change all of the arc's parameters.
+ void set( Point const &ip, Point const &r,
+ Angle rot_angle, bool large_arc, bool sweep,
+ Point const &fp
+ )
+ {
+ _initial_point = ip;
+ _final_point = fp;
+ _ellipse.setRays(r);
+ _ellipse.setRotationAngle(rot_angle);
+ _angles.setSweep(sweep);
+ _large_arc = large_arc;
+ _updateCenterAndAngles();
+ }
+
/** @brief Change the initial and final point in one operation.
* This method exists because modifying any of the endpoints causes rather costly
* recalculations of the center and extreme angles.
* @param ip New initial point
* @param fp New final point */
- void setExtremes(Point const &ip, Point const &fp) {
+ void setEndpoints(Point const &ip, Point const &fp) {
_initial_point = ip;
_final_point = fp;
- _updateCenterAndAngles(isSVGCompliant());
+ _updateCenterAndAngles();
}
/// @}
- /// @name Access computed parameters of the arc
- /// @{
- Coord center(Dim2 d) const { return _center[d]; }
- /** @brief Get the arc's center
- * @return The arc's center, situated on the intersection of the ellipse's rays */
- Point center() const { return _center; }
- /** @brief Get the extent of the arc
- * @return The angle between the initial and final point, in arc's angular coordinates */
- Coord sweepAngle() const {
- return extent();
- }
- /// @}
-
- /// @name Angular evaluation
+ /// @name Evaluate the arc as a function
/// @{
/** Check whether the arc contains the given angle
* @param t The angle to check
* @return True if the arc contains the angle, false otherwise */
- bool containsAngle(Coord angle) const;
+ bool containsAngle(Angle angle) const { return _angles.contains(angle); }
+
/** @brief Evaluate the arc at the specified angular coordinate
* @param t Angle
* @return Point corresponding to the given angle */
Point pointAtAngle(Coord t) const;
+
/** @brief Evaluate one of the arc's coordinates at the specified angle
* @param t Angle
* @param d The dimension to retrieve
* @return Selected coordinate of the arc at the specified angle */
Coord valueAtAngle(Coord t, Dim2 d) const;
- /** @brief Retrieve the unit circle transform.
+
+ /// Compute the curve time value corresponding to the given angular value.
+ Coord timeAtAngle(Angle a) const { return _angles.timeAtAngle(a); }
+
+ /// Compute the angular domain value corresponding to the given time value.
+ Angle angleAt(Coord t) const { return _angles.angleAt(t); }
+
+ /** @brief Compute the amount by which the angle parameter changes going from start to end.
+ * This has range \f$(-2\pi, 2\pi)\f$ and thus cannot be represented as instance
+ * of the class Angle. Add this to the initial angle to obtain the final angle. */
+ Coord sweepAngle() const { return _angles.sweepAngle(); }
+
+ /** @brief Get the elliptical angle spanned by the arc.
+ * This is basically the absolute value of sweepAngle(). */
+ Coord angularExtent() const { return _angles.extent(); }
+
+ /// Get the angular interval of the arc.
+ AngleInterval angularInterval() const { return _angles; }
+
+ /// Evaluate the arc in the curve domain, i.e. \f$[0, 1]\$.
+ virtual Point pointAt(Coord t) const;
+
+ /// Evaluate a single coordinate on the arc in the curve domain.
+ virtual Coord valueAt(Coord t, Dim2 d) const;
+
+ /** @brief Compute a transform that maps the unit circle to the arc's ellipse.
* Each ellipse can be interpreted as a translated, scaled and rotate unit circle.
* This function returns the transform that maps the unit circle to the arc's ellipse.
* @return Transform from unit circle to the arc's ellipse */
- Affine unitCircleTransform() const;
+ Affine unitCircleTransform() const {
+ Affine result = _ellipse.unitCircleTransform();
+ return result;
+ }
+
+ /** @brief Compute a transform that maps the arc's ellipse to the unit circle. */
+ Affine inverseUnitCircleTransform() const {
+ Affine result = _ellipse.inverseUnitCircleTransform();
+ return result;
+ }
/// @}
- /** @brief Check whether the arc adheres to SVG 1.1 implementation guidelines */
- virtual bool isSVGCompliant() const { return false; }
-
- std::pair<EllipticalArc, EllipticalArc> subdivide(Coord t) const {
- EllipticalArc* arc1 = static_cast<EllipticalArc*>(portion(0, t));
- EllipticalArc* arc2 = static_cast<EllipticalArc*>(portion(t, 1));
- assert( arc1 != NULL && arc2 != NULL);
- std::pair<EllipticalArc, EllipticalArc> arc_pair(*arc1, *arc2);
- delete arc1;
- delete arc2;
- return arc_pair;
+ /// @name Deal with degenerate ellipses.
+ /// @{
+ /** @brief Check whether both rays are nonzero.
+ * If they are not, the arc is represented as a line segment instead. */
+ bool isChord() const {
+ return ray(X) == 0 || ray(Y) == 0;
}
+ /** @brief Get the line segment connecting the arc's endpoints.
+ * @return A linear segment with initial and final point correspoding to those of the arc. */
+ LineSegment chord() const { return LineSegment(_initial_point, _final_point); }
+ /// @}
+
// implementation of overloads goes here
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Point initialPoint() const { return _initial_point; }
virtual Point finalPoint() const { return _final_point; }
virtual Curve* duplicate() const { return new EllipticalArc(*this); }
virtual void setInitial(Point const &p) {
_initial_point = p;
- _updateCenterAndAngles(isSVGCompliant());
+ _updateCenterAndAngles();
}
virtual void setFinal(Point const &p) {
_final_point = p;
- _updateCenterAndAngles(isSVGCompliant());
+ _updateCenterAndAngles();
}
virtual bool isDegenerate() const {
- return ( are_near(ray(X), 0) || are_near(ray(Y), 0) );
+ return _initial_point == _final_point;
}
+ virtual bool isLineSegment() const { return isChord(); }
virtual Rect boundsFast() const {
return boundsExact();
}
@@ -218,56 +276,56 @@ public:
}
virtual std::vector<double> roots(double v, Dim2 d) const;
#ifdef HAVE_GSL
- virtual std::vector<double> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const;
-#endif
- virtual double nearestPoint( Point const& p, double from = 0, double to = 1 ) const {
+ virtual std::vector<double> allNearestTimes( Point const& p, double from = 0, double to = 1 ) const;
+ virtual double nearestTime( Point const& p, double from = 0, double to = 1 ) const {
if ( are_near(ray(X), ray(Y)) && are_near(center(), p) ) {
return from;
}
- return allNearestPoints(p, from, to).front();
+ return allNearestTimes(p, from, to).front();
}
+#endif
+ virtual std::vector<CurveIntersection> intersect(Curve const &other, Coord eps=EPSILON) const;
virtual int degreesOfFreedom() const { return 7; }
virtual Curve *derivative() const;
- virtual Curve *transformed(Affine const &m) const;
- virtual Curve &operator*=(Translate const &m) {
- _initial_point += m.vector();
- _final_point += m.vector();
- _center += m.vector();
- return *this;
- }
+ using Curve::operator*=;
+ virtual void operator*=(Translate const &tr);
+ virtual void operator*=(Scale const &s);
+ virtual void operator*=(Rotate const &r);
+ virtual void operator*=(Zoom const &z);
+ virtual void operator*=(Affine const &m);
- /**
- * The size of the returned vector equals n+1.
- */
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const;
-
virtual D2<SBasis> toSBasis() const;
- virtual double valueAt(Coord t, Dim2 d) const {
- return valueAtAngle(angleAt(t), d);
- }
- virtual Point pointAt(Coord t) const {
- return pointAtAngle(angleAt(t));
- }
- virtual Curve* portion(double f, double t) const;
- virtual Curve* reverse() const;
-#endif
+ virtual Curve *portion(double f, double t) const;
+ virtual Curve *reverse() const;
+ virtual bool operator==(Curve const &c) const;
+ virtual void feed(PathSink &sink, bool moveto_initial) const;
-protected:
- void _updateCenterAndAngles(bool svg);
+private:
+ void _updateCenterAndAngles();
+ void _filterIntersections(std::vector<ShapeIntersection> &xs, bool is_first) const;
Point _initial_point, _final_point;
- Point _rays, _center;
- Angle _rot_angle;
+ Ellipse _ellipse;
+ AngleInterval _angles;
bool _large_arc;
-
-private:
- Coord map_to_01(Coord angle) const;
}; // end class EllipticalArc
+
+// implemented in elliptical-arc-from-sbasis.cpp
+/** @brief Fit an elliptical arc to an SBasis fragment.
+ * @relates EllipticalArc */
+bool arc_from_sbasis(EllipticalArc &ea, D2<SBasis> const &in,
+ double tolerance = EPSILON, unsigned num_samples = 20);
+
+/** @brief Debug output for elliptical arcs.
+ * @relates EllipticalArc */
+std::ostream &operator<<(std::ostream &out, EllipticalArc const &ea);
+
} // end namespace Geom
-#endif // _2GEOM_ELLIPTICAL_ARC_H_
+#endif // LIB2GEOM_SEEN_ELLIPTICAL_ARC_H
/*
Local Variables:
diff --git a/src/2geom/exception.h b/src/2geom/exception.h
index 12736d639..ab6f82142 100644
--- a/src/2geom/exception.h
+++ b/src/2geom/exception.h
@@ -38,8 +38,8 @@
*
*/
-#ifndef LIB2GEOM_EXCEPTION_HEADER
-#define LIB2GEOM_EXCEPTION_HEADER
+#ifndef LIB2GEOM_SEEN_EXCEPTION_H
+#define LIB2GEOM_SEEN_EXCEPTION_H
#include <exception>
#include <sstream>
diff --git a/src/2geom/forward.h b/src/2geom/forward.h
index 70cac1f7d..cf9c75988 100644
--- a/src/2geom/forward.h
+++ b/src/2geom/forward.h
@@ -32,14 +32,12 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_FORWARD_H
-#define SEEN_GEOM_FORWARD_H
-
-#include <vector> // include this dependency so PathVector can be defined more explicitly
+#ifndef LIB2GEOM_SEEN_FORWARD_H
+#define LIB2GEOM_SEEN_FORWARD_H
namespace Geom {
-// basic types
+// primitives
typedef double Coord;
typedef int IntCoord;
class Point;
@@ -63,6 +61,12 @@ typedef GenericOptRect<IntCoord> OptIntRect;
class Linear;
class Bezier;
class SBasis;
+class Poly;
+
+// shapes
+class Circle;
+class Ellipse;
+class ConvexHull;
// curves
class Curve;
@@ -75,11 +79,13 @@ typedef BezierCurveN<1> LineSegment;
typedef BezierCurveN<2> QuadraticBezier;
typedef BezierCurveN<3> CubicBezier;
class EllipticalArc;
-class SVGEllipticalArc;
// paths and path sequences
class Path;
-typedef std::vector<Path> PathVector;
+class PathVector;
+struct PathTime;
+class PathInterval;
+struct PathVectorTime;
// errors
class Exception;
@@ -103,11 +109,6 @@ class Zoom;
template <typename> class D2;
template <typename> class Piecewise;
-class Shape;
-class Region;
-class Hat;
-class Tri;
-
// misc
class SVGPathSink;
template <typename> class SVGPathGenerator;
diff --git a/src/2geom/generic-interval.h b/src/2geom/generic-interval.h
index f6d4718de..716390e57 100644
--- a/src/2geom/generic-interval.h
+++ b/src/2geom/generic-interval.h
@@ -90,13 +90,24 @@ public:
}
/// @}
- /// @name Inspect endpoints.
+ /// @name Inspect contained values.
/// @{
C min() const { return _b[0]; }
C max() const { return _b[1]; }
C extent() const { return max() - min(); }
C middle() const { return (max() + min()) / 2; }
bool isSingular() const { return min() == max(); }
+ C operator[](unsigned i) const { assert(i < 2); return _b[i]; }
+ C clamp(C val) const {
+ if (val < min()) return min();
+ if (val > max()) return max();
+ return val;
+ }
+ /// Return the closer end of the interval.
+ C nearestEnd(C val) const {
+ C dmin = std::abs(val - min()), dmax = std::abs(val - max());
+ return dmin <= dmax ? min() : max();
+ }
/// @}
/// @name Test coordinates and other intervals for inclusion.
@@ -138,6 +149,16 @@ public:
_b[1] = val;
}
}
+ /// Set both ends of the interval simultaneously
+ void setEnds(C a, C b) {
+ if (a <= b) {
+ _b[0] = a;
+ _b[1] = b;
+ } else {
+ _b[0] = b;
+ _b[1] = a;
+ }
+ }
/** @brief Extend the interval to include the given number. */
void expandTo(C val) {
if(val < _b[0]) _b[0] = val;
@@ -271,6 +292,8 @@ public:
/** @brief Check whether this interval is empty. */
bool isEmpty() { return !*this; };
+ /// Alias of isEmpty() for STL similarity.
+ bool empty() { return !*this; }
/** @brief Union with another interval, gracefully handling empty ones. */
void unionWith(GenericOptInterval<C> const &a) {
@@ -317,14 +340,14 @@ inline GenericOptInterval<C> operator&(GenericInterval<C> const &a, GenericInter
return GenericOptInterval<C>(a) & GenericOptInterval<C>(b);
}
-//#ifdef _GLIBCXX_IOSTREAM
+#ifdef _GLIBCXX_IOSTREAM
template <typename C>
inline std::ostream &operator<< (std::ostream &os,
Geom::GenericInterval<C> const &I) {
os << "Interval("<<I.min() << ", "<<I.max() << ")";
return os;
}
-//#endif
+#endif
} // namespace Geom
#endif // !LIB2GEOM_SEEN_GENERIC_INTERVAL_H
diff --git a/src/2geom/generic-rect.h b/src/2geom/generic-rect.h
index 9ad0e60b0..f7d722757 100644
--- a/src/2geom/generic-rect.h
+++ b/src/2geom/generic-rect.h
@@ -64,6 +64,10 @@ class GenericRect
protected:
CInterval f[2];
public:
+ typedef CInterval D1Value;
+ typedef CInterval &D1Reference;
+ typedef CInterval const &D1ConstReference;
+
/// @name Create rectangles.
/// @{
/** @brief Create a rectangle that contains only the point at (0,0). */
@@ -180,12 +184,34 @@ public:
/** @brief Compute rectangle's area. */
C area() const { return f[X].extent() * f[Y].extent(); }
/** @brief Check whether the rectangle has zero area. */
- bool hasZeroArea() const { return (area() == 0); }
+ bool hasZeroArea() const { return f[X].isSingular() || f[Y].isSingular(); }
/** @brief Get the larger extent (width or height) of the rectangle. */
C maxExtent() const { return std::max(f[X].extent(), f[Y].extent()); }
/** @brief Get the smaller extent (width or height) of the rectangle. */
C minExtent() const { return std::min(f[X].extent(), f[Y].extent()); }
+
+ /** @brief Clamp point to the rectangle. */
+ CPoint clamp(CPoint const &p) const {
+ CPoint result(f[X].clamp(p[X]), f[Y].clamp(p[Y]));
+ return result;
+ }
+ /** @brief Get the nearest point on the edge of the rectangle. */
+ CPoint nearestEdgePoint(CPoint const &p) const {
+ CPoint result = p;
+ if (!contains(p)) {
+ result = clamp(p);
+ } else {
+ C cx = f[X].nearestEnd(p[X]);
+ C cy = f[Y].nearestEnd(p[Y]);
+ if (std::abs(cx - p[X]) <= std::abs(cy - p[Y])) {
+ result[X] = cx;
+ } else {
+ result[Y] = cy;
+ }
+ }
+ return result;
+ }
/// @}
/// @name Test other rectangles and points for inclusion.
@@ -328,6 +354,10 @@ class GenericOptRect
typedef typename CoordTraits<C>::OptRectType OptCRect;
typedef boost::optional<CRect> Base;
public:
+ typedef CInterval D1Value;
+ typedef CInterval &D1Reference;
+ typedef CInterval const &D1ConstReference;
+
/// @name Create potentially empty rectangles.
/// @{
GenericOptRect() : Base() {}
@@ -483,13 +513,13 @@ inline bool GenericRect<C>::contains(OptCRect const &r) const {
return !r || contains(*r);
}
-//#ifdef _GLIBCXX_IOSTREAM
+#ifdef _GLIBCXX_IOSTREAM
template <typename C>
inline std::ostream &operator<<(std::ostream &out, GenericRect<C> const &r) {
out << "X: " << r[X] << " Y: " << r[Y];
return out;
}
-//#endif
+#endif
} // end namespace Geom
diff --git a/src/2geom/geom.cpp b/src/2geom/geom.cpp
index d3cf0ca73..bf9b0a721 100644
--- a/src/2geom/geom.cpp
+++ b/src/2geom/geom.cpp
@@ -10,8 +10,17 @@
#include <algorithm>
#include <2geom/rect.h>
+using std::swap;
+
namespace Geom {
+enum IntersectorKind {
+ intersects = 0,
+ parallel,
+ coincident,
+ no_intersection
+};
+
/**
* Finds the intersection of the two (infinite) lines
* defined by the points p such that dot(n0, p) == d0 and dot(n1, p) == d1.
@@ -305,7 +314,7 @@ rect_line_intersect(Geom::Point const &c0, Geom::Point const &c1,
Point dir1 (results[1] - results[0]);
Point dir2 (p1 - p0);
if (dot(dir1, dir2) < 0) {
- std::swap(results[0], results[1]);
+ swap(results[0], results[1]);
}
}
@@ -363,7 +372,7 @@ int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &a
Geom::Point centroid_tmp(0,0);
double atmp = 0;
for (unsigned i = n-1, j = 0; j < n; i = j, j++) {
- const double ai = -cross(p[j], p[i]);
+ const double ai = cross(p[j], p[i]);
atmp += ai;
centroid_tmp += (p[j] + p[i])*ai; // first moment.
}
diff --git a/src/2geom/geom.h b/src/2geom/geom.h
index 5aeded23d..6ba812254 100644
--- a/src/2geom/geom.h
+++ b/src/2geom/geom.h
@@ -44,52 +44,10 @@
namespace Geom {
-enum IntersectorKind {
- intersects = 0,
- parallel,
- coincident,
- no_intersection
-};
-
-int
-intersector_ccw(const Geom::Point& p0, const Geom::Point& p1,
- const Geom::Point& p2);
-
-/* intersectors */
-
-#if 0
-// Use the new routines provided in line.h
-
-IntersectorKind
-line_intersection(Geom::Point const &n0, double const d0,
- Geom::Point const &n1, double const d1,
- Geom::Point &result);
-
-IntersectorKind
-segment_intersect(Geom::Point const &p00, Geom::Point const &p01,
- Geom::Point const &p10, Geom::Point const &p11,
- Geom::Point &result);
-
-IntersectorKind
-line_twopoint_intersect(Geom::Point const &p00, Geom::Point const &p01,
- Geom::Point const &p10, Geom::Point const &p11,
- Geom::Point &result);
-#endif
-
-#if 0
-std::vector<Geom::Point>
-rect_line_intersect(Geom::Point const &E, Geom::Point const &F,
- Geom::Point const &p0, Geom::Point const &p1);
-#endif
-
boost::optional<Geom::LineSegment>
rect_line_intersect(Geom::Rect &r,
Geom::LineSegment ls);
-boost::optional<Geom::LineSegment>
-rect_line_intersect(Geom::Rect &r,
- Geom::Line l);
-
int centroid(std::vector<Geom::Point> const &p, Geom::Point& centroid, double &area);
}
diff --git a/src/2geom/hvlinesegment.h b/src/2geom/hvlinesegment.h
deleted file mode 100644
index d2b9d6310..000000000
--- a/src/2geom/hvlinesegment.h
+++ /dev/null
@@ -1,283 +0,0 @@
-ï»ż/**
- * \file
- * \brief Horizontal and vertical line segment
- *//*
- * Authors:
- * Marco Cecchetti <mrcekets at gmail.com>
- * Krzysztof KosiƄski <tweenk.pl@gmail.com>
- * Copyright 2008-2011 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
-
-#ifndef LIB2GEOM_SEEN_HVLINESEGMENT_H
-#define LIB2GEOM_SEEN_HVLINESEGMENT_H
-
-#include <2geom/bezier-curve.h>
-
-namespace Geom
-{
-
-template <Dim2 axis>
-class AxisLineSegment : public LineSegment
-{
-public:
- static const Dim2 other_axis = static_cast<Dim2>((axis + 1) % 2);
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual void setInitial(Point const &p) {
- Point f = finalPoint();
- f[axis] = p[axis];
- LineSegment::setInitial(p);
- LineSegment::setFinal(f);
- }
- virtual void setFinal(Point const &p) {
- Point i = initialPoint();
- i[axis] = p[axis];
- LineSegment::setFinal(p);
- LineSegment::setInitial(i);
- }
- virtual Rect boundsFast() const { return boundsExact(); }
- virtual Rect boundsExact() const { Rect r(initialPoint(), finalPoint()); return r; }
- virtual int degreesOfFreedom() const { return 3; }
- virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
- std::vector<Coord> result;
- if (d == axis) {
- if ( v >= initialPoint()[axis] && v <= finalPoint()[axis] ) {
- Coord t = 0;
- if (!isDegenerate())
- t = (v - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
- result.push_back(t);
- }
- } else {
- if (v == initialPoint()[other_axis]) {
- if (!isDegenerate())
- THROW_INFINITESOLUTIONS(0);
- result.push_back(0);
- }
- }
- return result;
- }
- virtual Coord nearestPoint( Point const &p, Coord from = 0, Coord to = 1 ) const {
- if ( from > to ) std::swap(from, to);
- Coord xfrom = valueAt(from, axis);
- Coord xto = valueAt(to, axis);
- if ( xfrom > xto ) {
- std::swap(xfrom, xto);
- std::swap(from, to);
- }
- if ( p[axis] > xfrom && p[axis] < xto ) {
- return (p[axis] - initialPoint()[axis]) / (finalPoint()[axis] - initialPoint()[axis]);
- }
- else if ( p[X] <= xfrom )
- return from;
- else
- return to;
- }
- virtual Point pointAt(Coord t) const {
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("AxisLineSegment: Time value out of range");
- Point ret = initialPoint() + t * (finalPoint() - initialPoint());
- return ret;
- }
- virtual Coord valueAt(Coord t, Dim2 d) const {
- if ( t < 0 || t > 1 )
- THROW_RANGEERROR("AxisLineSegment: Time value out of range");
- if (d != axis) return initialPoint()[other_axis];
- return initialPoint()[axis] + t * (finalPoint()[axis] - initialPoint()[axis]);
- }
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
- std::vector<Point> result;
- result.push_back(pointAt(t));
- if (n > 0) {
- Point der = finalPoint() - initialPoint();
- result.push_back( der );
- }
- if (n > 1) {
- /* higher order derivatives are zero,
- * so the other n-1 vector elements are (0,0) */
- result.insert( result.end(), n-1, Point(0, 0) );
- }
- return result;
- }
-#endif
-protected:
- AxisLineSegment(Point const &p0, Point const &p1) : LineSegment(p0, p1) {}
- AxisLineSegment() {}
-};
-
-class HLineSegment : public AxisLineSegment<X>
-{
-public:
- HLineSegment() {}
- HLineSegment(Coord x0, Coord x1, Coord y)
- : AxisLineSegment<X>(Point(x0, y), Point(x1, y))
- {}
-
- HLineSegment(Point const& p, Coord len)
- : AxisLineSegment<X>(p, Point(p[X] + len, p[Y]))
- {}
-
- HLineSegment(Point const& p0, Point const& p1)
- : AxisLineSegment<X>(p0, p1)
- {
- if ( p0[Y] != p1[Y] ) {
- THROW_RANGEERROR("HLineSegment::HLineSegment passed points should "
- "have the same Y value");
- }
- }
-
- Coord getY() { return initialPoint()[Y]; }
- void setInitialX(Coord x) {
- LineSegment::setInitial(Point(x, initialPoint()[Y]));
- }
- void setFinalX(Coord x) {
- LineSegment::setFinal(Point(x, initialPoint()[Y]));
- }
- void setY(Coord y) {
- LineSegment::setInitial( Point(initialPoint()[X], y) );
- LineSegment::setFinal( Point(finalPoint()[X], y) );
- }
- std::pair<HLineSegment, HLineSegment> subdivide(Coord t) const {
- std::pair<HLineSegment, HLineSegment> result;
- Point p = pointAt(t);
- result.first.setInitial(initialPoint());
- result.first.setFinal(p);
- result.second.setInitial(p);
- result.second.setFinal(finalPoint());
- return result;
- }
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve* duplicate() const { return new HLineSegment(*this); }
- virtual Curve *portion(Coord f, Coord t) const {
- Point ip = pointAt(f);
- Point ep = pointAt(t);
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- }
- virtual Curve *reverse() const {
- Point ip = initialPoint();
- return new HLineSegment(finalPoint()[X], ip[X], ip[Y]);
- }
- virtual Curve *transformed(Affine const & m) const {
- Point ip = initialPoint() * m;
- Point ep = finalPoint() * m;
- // cannot afford to lose precision here since it can lead to discontinuous paths
- if (m.isZoom(0)) {
- return new HLineSegment(ip[X], ep[X], ip[Y]);
- } else {
- return new LineSegment(ip, ep);
- }
- }
- virtual Curve *derivative() const {
- Coord x = finalPoint()[X] - initialPoint()[X];
- return new HLineSegment(x, x, 0);
- }
-#endif
-}; // end class HLineSegment
-
-
-class VLineSegment : public AxisLineSegment<Y>
-{
-public:
- VLineSegment() {}
-
- VLineSegment(Coord x, Coord y0, Coord y1)
- : AxisLineSegment<Y>(Point(x, y0), Point(x, y1))
- {}
-
- VLineSegment(Point const& _p, Coord _length)
- : AxisLineSegment<Y>(_p, Point(_p[X], _p[Y] + _length))
- {}
-
- VLineSegment(Point const& _p0, Point const& _p1)
- : AxisLineSegment<Y>(_p0, _p1)
- {
- if ( _p0[X] != _p1[X] ) {
- THROW_RANGEERROR("VLineSegment::VLineSegment passed points should "
- "have the same X value");
- }
- }
-
- Coord getX() { return initialPoint()[X]; }
- void setInitialY(Coord _y) {
- LineSegment::setInitial( Point(initialPoint()[X], _y) );
- }
- void setFinalY(Coord _y) {
- LineSegment::setFinal( Point(finalPoint()[Y], _y) );
- }
- void setX(Coord _x) {
- LineSegment::setInitial( Point(_x, initialPoint()[Y]) );
- LineSegment::setFinal( Point(_x, finalPoint()[Y]) );
- }
- std::pair<VLineSegment, VLineSegment> subdivide(Coord t) const {
- std::pair<VLineSegment, VLineSegment> result;
- Point p = pointAt(t);
- result.first.setInitial(initialPoint());
- result.first.setFinal(p);
- result.second.setInitial(p);
- result.second.setFinal(finalPoint());
- return result;
- }
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve *duplicate() const { return new VLineSegment(*this); }
- virtual Curve *portion(Coord f, Coord t) const {
- Point ip = pointAt(f);
- Coord epy = valueAt(t, Y);
- return new VLineSegment(ip[X], ip[Y], epy);
- }
- virtual Curve *reverse() const {
- Point ip = initialPoint();
- return new VLineSegment(ip[X], finalPoint()[Y], ip[Y]);
- }
- virtual Curve *transformed(Affine const & m) const {
- Point ip = initialPoint() * m;
- Point ep = finalPoint() * m;
- if (m.isZoom()) {
- return new VLineSegment(ip[X], ip[Y], ep[Y]);
- } else {
- return new LineSegment(ip, ep);
- }
- }
- virtual Curve* derivative() const {
- Coord y = finalPoint()[Y] - initialPoint()[Y];
- return new VLineSegment(0, y, y);
- }
-#endif
-}; // end class VLineSegment
-
-} // end namespace Geom
-
-#endif // LIB2GEOM_SEEN_HVLINESEGMENT_H
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/int-point.h b/src/2geom/int-point.h
index 1a16ecb7a..7c737b1d7 100644
--- a/src/2geom/int-point.h
+++ b/src/2geom/int-point.h
@@ -120,30 +120,53 @@ public:
}
/// @}
- /** @brief Lexicographical ordering functor. */
- template <Dim2 d> struct LexOrder;
+ /** @brief Lexicographical ordering functor.
+ * @param d The more significant dimension */
+ template <Dim2 d> struct LexLess;
+ /** @brief Lexicographical ordering functor.
+ * @param d The more significant dimension */
+ template <Dim2 d> struct LexGreater;
/** @brief Lexicographical ordering functor with runtime dimension. */
- class LexOrderRt {
- public:
- LexOrderRt(Dim2 d) : dim(d) {}
- inline bool operator()(IntPoint const &a, IntPoint const &b);
+ struct LexLessRt {
+ LexLessRt(Dim2 d) : dim(d) {}
+ inline bool operator()(IntPoint const &a, IntPoint const &b) const;
+ private:
+ Dim2 dim;
+ };
+ /** @brief Lexicographical ordering functor with runtime dimension. */
+ struct LexGreaterRt {
+ LexGreaterRt(Dim2 d) : dim(d) {}
+ inline bool operator()(IntPoint const &a, IntPoint const &b) const;
private:
Dim2 dim;
};
};
-template<> struct IntPoint::LexOrder<X> {
- bool operator()(IntPoint const &a, IntPoint const &b) {
+template<> struct IntPoint::LexLess<X> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
}
};
-template<> struct IntPoint::LexOrder<Y> {
- bool operator()(IntPoint const &a, IntPoint const &b) {
+template<> struct IntPoint::LexLess<Y> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
}
};
-inline bool IntPoint::LexOrderRt::operator()(IntPoint const &a, IntPoint const &b) {
- return dim ? IntPoint::LexOrder<Y>()(a, b) : IntPoint::LexOrder<X>()(a, b);
+template<> struct IntPoint::LexGreater<X> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
+ return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+ }
+};
+template<> struct IntPoint::LexGreater<Y> {
+ bool operator()(IntPoint const &a, IntPoint const &b) const {
+ return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+ }
+};
+inline bool IntPoint::LexLessRt::operator()(IntPoint const &a, IntPoint const &b) const {
+ return dim ? IntPoint::LexLess<Y>()(a, b) : IntPoint::LexLess<X>()(a, b);
+}
+inline bool IntPoint::LexGreaterRt::operator()(IntPoint const &a, IntPoint const &b) const {
+ return dim ? IntPoint::LexGreater<Y>()(a, b) : IntPoint::LexGreater<X>()(a, b);
}
} // namespace Geom
diff --git a/src/2geom/intersection-graph.cpp b/src/2geom/intersection-graph.cpp
new file mode 100644
index 000000000..e18561f67
--- /dev/null
+++ b/src/2geom/intersection-graph.cpp
@@ -0,0 +1,394 @@
+/**
+ * \file
+ * \brief Intersection graph for Boolean operations
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <2geom/intersection-graph.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+#include <iostream>
+#include <iterator>
+
+namespace Geom {
+
+struct PathIntersectionGraph::IntersectionVertexLess {
+ bool operator()(IntersectionVertex const &a, IntersectionVertex const &b) const {
+ return a.pos < b.pos;
+ }
+};
+
+/** @class PathIntersectionGraph
+ * @brief Intermediate data for computing Boolean operations on paths.
+ *
+ * This class implements the Greiner-Hormann clipping algorithm,
+ * with improvements inspired by Foster and Overfelt as well as some
+ * original contributions.
+ *
+ * @ingroup Paths
+ */
+
+PathIntersectionGraph::PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision)
+ : _a(a)
+ , _b(b)
+{
+ if (a.empty() || b.empty()) return;
+
+ // all paths must be closed, otherwise we will miss some intersections
+ for (std::size_t i = 0; i < a.size(); ++i) {
+ _a[i].close();
+ }
+ for (std::size_t i = 0; i < b.size(); ++i) {
+ _b[i].close();
+ }
+
+ std::vector<PVIntersection> pxs = _a.intersect(_b, precision);
+ // NOTE: this early return means that the path data structures will not be created
+ // if there are no intersections at all!
+ if (pxs.empty()) return;
+
+ // prepare intersection lists for each path component
+ for (std::size_t i = 0; i < _a.size(); ++i) {
+ _apaths.push_back(new PathData());
+ }
+ for (std::size_t i = 0; i < _b.size(); ++i) {
+ _bpaths.push_back(new PathData());
+ }
+
+ for (std::size_t i = 0; i < pxs.size(); ++i) {
+ IntersectionVertex *xa, *xb;
+ xa = new IntersectionVertex();
+ xb = new IntersectionVertex();
+ xa->processed = xb->processed = false;
+ xa->pos = pxs[i].first;
+ xb->pos = pxs[i].second;
+ xa->p = xb->p = pxs[i].point();
+ xa->neighbor = xb;
+ xb->neighbor = xa;
+ _xs.push_back(xa);
+ _xs.push_back(xb);
+ _apaths[xa->pos.path_index].xlist.push_back(*xa);
+ _bpaths[xb->pos.path_index].xlist.push_back(*xb);
+ }
+
+ for (std::size_t i = 0; i < _apaths.size(); ++i) {
+ _apaths[i].xlist.sort(IntersectionVertexLess());
+ }
+ for (std::size_t i = 0; i < _bpaths.size(); ++i) {
+ _bpaths[i].xlist.sort(IntersectionVertexLess());
+ }
+
+ typedef IntersectionList::iterator Iter;
+
+ // determine in/out/on flags using winding
+ for (unsigned npv = 0; npv < 2; ++npv) {
+ boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths;
+ boost::ptr_vector<PathData> &ols = npv ? _apaths : _bpaths;
+ PathVector const &pv = npv ? b : a;
+ PathVector const &other = npv ? a : b;
+
+ for (unsigned li = 0; li < ls.size(); ++li) {
+ IntersectionList &xl = ls[li].xlist;
+ for (Iter i = xl.begin(); i != xl.end(); ++i) {
+ Iter n = boost::next(i);
+ if (n == xl.end()) {
+ n = xl.begin();
+ }
+ std::size_t pi = i->pos.path_index;
+
+ PathInterval ival = forward_interval(i->pos, n->pos, pv[pi].size());
+ PathTime mid = ival.inside(precision);
+
+ // TODO check for degenerate cases
+ int w = other.winding(pv[pi].pointAt(mid));
+ if (w % 2) {
+ i->next = POINT_INSIDE;
+ n->previous = POINT_INSIDE;
+ } else {
+ i->next = POINT_OUTSIDE;
+ n->previous = POINT_OUTSIDE;
+ }
+ }
+
+ // remove intersections that don't change between in/out
+ // and assign exit / entry flags
+ for (Iter i = xl.begin(); i != xl.end();) {
+ if (i->previous == i->next) {
+ IntersectionList &oxl = ols[i->neighbor->pos.path_index].xlist;
+ oxl.erase(oxl.iterator_to(*i->neighbor));
+ xl.erase(i++);
+ if (i->next == POINT_INSIDE) {
+ ++ls[li].removed_in;
+ } else {
+ ++ls[li].removed_out;
+ }
+ } else {
+ i->entry = ((i->next == POINT_INSIDE) && (i->previous == POINT_OUTSIDE));
+ ++i;
+ }
+ }
+ }
+ }
+}
+
+PathVector PathIntersectionGraph::getUnion()
+{
+ PathVector result = _getResult(false, false);
+ _handleNonintersectingPaths(result, 0, false);
+ _handleNonintersectingPaths(result, 1, false);
+ return result;
+}
+
+PathVector PathIntersectionGraph::getIntersection()
+{
+ PathVector result = _getResult(true, true);
+ _handleNonintersectingPaths(result, 0, true);
+ _handleNonintersectingPaths(result, 1, true);
+ return result;
+}
+
+PathVector PathIntersectionGraph::getAminusB()
+{
+ PathVector result = _getResult(false, true);
+ _handleNonintersectingPaths(result, 0, false);
+ _handleNonintersectingPaths(result, 1, true);
+ return result;
+}
+
+PathVector PathIntersectionGraph::getBminusA()
+{
+ PathVector result = _getResult(true, false);
+ _handleNonintersectingPaths(result, 1, false);
+ _handleNonintersectingPaths(result, 0, true);
+ return result;
+}
+
+PathVector PathIntersectionGraph::getXOR()
+{
+ PathVector r1 = getAminusB();
+ PathVector r2 = getBminusA();
+ std::copy(r2.begin(), r2.end(), std::back_inserter(r1));
+ return r1;
+}
+
+std::size_t PathIntersectionGraph::size() const
+{
+ std::size_t result = 0;
+ for (std::size_t i = 0; i < _apaths.size(); ++i) {
+ result += _apaths[i].xlist.size();
+ }
+ return result;
+}
+
+std::vector<Point> PathIntersectionGraph::intersectionPoints() const
+{
+ std::vector<Point> result;
+
+ typedef IntersectionList::const_iterator Iter;
+ for (std::size_t i = 0; i < _apaths.size(); ++i) {
+ for (Iter j = _apaths[i].xlist.begin(); j != _apaths[i].xlist.end(); ++j) {
+ result.push_back(j->p);
+ }
+ }
+ return result;
+}
+
+PathVector PathIntersectionGraph::_getResult(bool enter_a, bool enter_b)
+{
+ typedef IntersectionList::iterator Iter;
+ PathVector result;
+ if (_xs.empty()) return result;
+
+ // reset processed status
+ for (unsigned npv = 0; npv < 2; ++npv) {
+ boost::ptr_vector<PathData> &ls = npv ? _bpaths : _apaths;
+ for (std::size_t li = 0; li < ls.size(); ++li) {
+ for (Iter k = ls[li].xlist.begin(); k != ls[li].xlist.end(); ++k) {
+ k->processed = false;
+ }
+ }
+ }
+
+ unsigned n_processed = 0;
+
+ while (true) {
+ PathVector const *cur = &_a, *other = &_b;
+ boost::ptr_vector<PathData> *lscur = &_apaths, *lsother = &_bpaths;
+
+ // find unprocessed intersection
+ Iter i;
+ if (!_findUnprocessed(i)) break;
+
+ result.push_back(Path(i->p));
+ result.back().setStitching(true);
+
+ while (!i->processed) {
+ Iter prev = i;
+ std::size_t pi = i->pos.path_index;
+ // determine which direction to go
+ // union: always go outside
+ // intersection: always go inside
+ // a minus b: go inside in b, outside in a
+ // b minus a: go inside in a, outside in b
+ bool reverse = false;
+ if (cur == &_a) {
+ reverse = i->entry ^ enter_a;
+ } else {
+ reverse = i->entry ^ enter_b;
+ }
+
+ // get next intersection
+ if (reverse) {
+ if (i == (*lscur)[pi].xlist.begin()) {
+ i = (*lscur)[pi].xlist.end();
+ }
+ --i;
+ } else {
+ ++i;
+ if (i == (*lscur)[pi].xlist.end()) {
+ i = (*lscur)[pi].xlist.begin();
+ }
+ }
+
+ // append portion of path
+ PathInterval ival = PathInterval::from_direction(
+ prev->pos.asPathTime(), i->pos.asPathTime(),
+ reverse, (*cur)[pi].size());
+
+ (*cur)[pi].appendPortionTo(result.back(), ival, prev->p, i->p);
+
+ // mark both vertices as processed
+ prev->processed = true;
+ i->processed = true;
+ n_processed += 2;
+
+ // switch to the other path
+ i = (*lsother)[i->neighbor->pos.path_index].xlist.iterator_to(*i->neighbor);
+ std::swap(lscur, lsother);
+ std::swap(cur, other);
+ }
+ result.back().close(true);
+
+ assert(!result.back().empty());
+ }
+
+ assert(n_processed == size() * 2);
+
+ return result;
+}
+
+void PathIntersectionGraph::_handleNonintersectingPaths(PathVector &result, int which, bool inside)
+{
+ /* Every component that has any intersections will be processed by _getResult.
+ * Here we take care of paths that don't have any intersections. They are either
+ * completely inside or completely outside the other pathvector. We test this by
+ * evaluating the winding rule at the initial point. If inside is true and
+ * the path is inside, we add it to the result.
+ */
+ boost::ptr_vector<PathData> const &ls = which ? _bpaths : _apaths;
+ PathVector const &cur = which ? _b : _a;
+ PathVector const &other = which ? _a : _b;
+
+ for (std::size_t i = 0; i < cur.size(); ++i) {
+ // the path data vector might have been left empty if there were no intersections at all
+ bool has_path_data = !ls.empty();
+ // Skip if the path has intersections
+ if (has_path_data && !ls[i].xlist.empty()) continue;
+ bool path_inside = false;
+
+ // If the path had any intersections removed, use the result of that,
+ // since one of those might have been at the initial point.
+ // Also, it saves time.
+ if (has_path_data && ls[i].removed_in != 0) {
+ path_inside = true;
+ } else if (has_path_data && ls[i].removed_out != 0) {
+ path_inside = false;
+ } else {
+ int w = other.winding(cur[i].initialPoint());
+ path_inside = w % 2 != 0;
+ }
+
+ if (path_inside == inside) {
+ result.push_back(cur[i]);
+ }
+ }
+}
+
+bool PathIntersectionGraph::_findUnprocessed(IntersectionList::iterator &result)
+{
+ typedef IntersectionList::iterator Iter;
+
+ Iter it, last;
+
+ for (std::size_t k = 0; k < _apaths.size(); ++k) {
+ it = _apaths[k].xlist.begin();
+ last = _apaths[k].xlist.end();
+ for (; it != last; ++it) {
+ if (!it->_hook.is_linked()) {
+ // this intersection was removed since it did not change inside/outside status
+ continue;
+ }
+ if (!it->processed) {
+ result = it;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig)
+{
+ os << "Intersection graph:\n"
+ << pig._xs.size()/2 << " total intersections\n"
+ << pig.size() << " considered intersections\n";
+ for (std::size_t i = 0; i < pig._apaths.size(); ++i) {
+ typedef PathIntersectionGraph::IntersectionList::const_iterator Iter;
+ for (Iter j = pig._apaths[i].xlist.begin(); j != pig._apaths[i].xlist.end(); ++j) {
+ os << j->pos << " - " << j->neighbor->pos << " @ " << j->p << "\n";
+ }
+ }
+ return os;
+}
+
+} // namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/intersection-graph.h b/src/2geom/intersection-graph.h
new file mode 100644
index 000000000..bd9aaee81
--- /dev/null
+++ b/src/2geom/intersection-graph.h
@@ -0,0 +1,128 @@
+/**
+ * \file
+ * \brief Path intersection graph
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+#define SEEN_LIB2GEOM_INTERSECTION_GRAPH_H
+
+#include <set>
+#include <vector>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/intrusive/list.hpp>
+#include <2geom/forward.h>
+#include <2geom/pathvector.h>
+
+namespace Geom {
+
+class PathIntersectionGraph
+{
+ // this is called PathIntersectionGraph so that we can also have a class for polygons,
+ // e.g. PolygonIntersectionGraph, which is going to be significantly faster
+public:
+ PathIntersectionGraph(PathVector const &a, PathVector const &b, Coord precision = EPSILON);
+
+ PathVector getUnion();
+ PathVector getIntersection();
+ PathVector getAminusB();
+ PathVector getBminusA();
+ PathVector getXOR();
+
+ /// Returns the number of intersections used when computing Boolean operations.
+ std::size_t size() const;
+ std::vector<Point> intersectionPoints() const;
+
+private:
+ enum InOutFlag {
+ POINT_INSIDE,
+ POINT_OUTSIDE
+ };
+
+ struct IntersectionVertex {
+ boost::intrusive::list_member_hook<> _hook;
+ PathVectorTime pos;
+ Point p; // guarantees that endpoints are exact
+ IntersectionVertex *neighbor;
+ bool entry; // going in +t direction enters the other path
+ InOutFlag previous;
+ InOutFlag next;
+ bool processed; // TODO: use intrusive unprocessed list instead
+ };
+
+ typedef boost::intrusive::list
+ < IntersectionVertex
+ , boost::intrusive::member_hook
+ < IntersectionVertex
+ , boost::intrusive::list_member_hook<>
+ , &IntersectionVertex::_hook
+ >
+ > IntersectionList;
+
+ struct PathData {
+ IntersectionList xlist;
+ unsigned removed_in;
+ unsigned removed_out;
+
+ PathData()
+ : removed_in(0)
+ , removed_out(0)
+ {}
+ };
+
+ struct IntersectionVertexLess;
+
+ PathVector _getResult(bool enter_a, bool enter_b);
+ void _handleNonintersectingPaths(PathVector &result, int which, bool inside);
+ bool _findUnprocessed(IntersectionList::iterator &result);
+
+ PathVector _a, _b;
+ boost::ptr_vector<IntersectionVertex> _xs;
+ boost::ptr_vector<PathData> _apaths;
+ boost::ptr_vector<PathData> _bpaths;
+
+ friend std::ostream &operator<<(std::ostream &, PathIntersectionGraph const &);
+};
+
+std::ostream &operator<<(std::ostream &os, PathIntersectionGraph const &pig);
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_PATH_GRAPH_H
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/intersection.h b/src/2geom/intersection.h
new file mode 100644
index 000000000..bbce19947
--- /dev/null
+++ b/src/2geom/intersection.h
@@ -0,0 +1,147 @@
+/**
+ * \file
+ * \brief Intersection utilities
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef SEEN_LIB2GEOM_INTERSECTION_H
+#define SEEN_LIB2GEOM_INTERSECTION_H
+
+#include <2geom/coord.h>
+#include <2geom/point.h>
+
+namespace Geom {
+
+
+/** @brief Intersection between two shapes.
+ */
+template <typename TimeA = Coord, typename TimeB = TimeA>
+class Intersection
+ : boost::totally_ordered< Intersection<TimeA, TimeB> >
+{
+public:
+ /** @brief Construct from shape references and time values.
+ * By default, the intersection point will be halfway between the evaluated
+ * points on the two shapes. */
+ template <typename TA, typename TB>
+ Intersection(TA const &sa, TB const &sb, TimeA const &ta, TimeB const &tb)
+ : first(ta)
+ , second(tb)
+ , _point(lerp(0.5, sa.pointAt(ta), sb.pointAt(tb)))
+ {}
+
+ /// Additionally report the intersection point.
+ Intersection(TimeA const &ta, TimeB const &tb, Point const &p)
+ : first(ta)
+ , second(tb)
+ , _point(p)
+ {}
+
+ /// Intersection point, as calculated by the intersection algorithm.
+ Point point() const {
+ return _point;
+ }
+ /// Implicit conversion to Point.
+ operator Point() const {
+ return _point;
+ }
+
+ friend inline void swap(Intersection &a, Intersection &b) {
+ using std::swap;
+ swap(a.first, b.first);
+ swap(a.second, b.second);
+ swap(a._point, b._point);
+ }
+
+ bool operator==(Intersection const &other) const {
+ if (first != other.first) return false;
+ if (second != other.second) return false;
+ return true;
+ }
+ bool operator<(Intersection const &other) const {
+ if (first < other.first) return true;
+ if (first == other.first && second < other.second) return true;
+ return false;
+ }
+
+public:
+ /// First shape and time value.
+ TimeA first;
+ /// Second shape and time value.
+ TimeB second;
+private:
+ // Recalculation of the intersection point from the time values is in many cases
+ // less precise than the value obtained directly from the intersection algorithm,
+ // so we need to store it.
+ Point _point;
+};
+
+
+// TODO: move into new header?
+template <typename T>
+struct ShapeTraits {
+ typedef Coord TimeType;
+ typedef Interval IntervalType;
+ typedef T AffineClosureType;
+ typedef Intersection<> IntersectionType;
+};
+
+template <typename A, typename B> inline
+std::vector< Intersection<A, B> > transpose(std::vector< Intersection<B, A> > const &in) {
+ std::vector< Intersection<A, B> > result;
+ for (std::size_t i = 0; i < in.size(); ++i) {
+ result.push_back(Intersection<A, B>(in[i].second, in[i].first, in[i].point()));
+ }
+ return result;
+}
+
+template <typename T> inline
+void transpose_in_place(std::vector< Intersection<T, T> > &xs) {
+ for (std::size_t i = 0; i < xs.size(); ++i) {
+ std::swap(xs[i].first, xs[i].second);
+ }
+}
+
+typedef Intersection<> ShapeIntersection;
+
+
+} // namespace Geom
+
+#endif // SEEN_LIB2GEOM_INTERSECTION_H
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/interval.h b/src/2geom/interval.h
index 1714435be..fd446a1c6 100644
--- a/src/2geom/interval.h
+++ b/src/2geom/interval.h
@@ -91,19 +91,28 @@ public:
}
/// @}
- /// @name Inspect endpoints.
+ /// @name Inspect contained values.
/// @{
- /** @brief Access endpoints by value.
- * @deprecated Use min() and max() instead */
- Coord operator[](unsigned i) const { return _b[i]; }
- /** @brief Access endpoints by reference.
- * @deprecated Use min() and max() instead
- * @todo Remove Interval index operator, which can be used to break the invariant */
- Coord& operator[](unsigned i) { return _b[i]; }
-
+ /// Check whether both endpoints are finite.
bool isFinite() const {
return IS_FINITE(min()) && IS_FINITE(max());
}
+ /** @brief Map the interval [0,1] onto this one.
+ * This method simply performs 1D linear interpolation between endpoints. */
+ Coord valueAt(Coord t) {
+ return lerp(t, min(), max());
+ }
+ /** @brief Compute a time value that maps to the given value.
+ * The supplied value does not need to be in the interval for this method to work. */
+ Coord timeAt(Coord v) {
+ return (v - min()) / extent();
+ }
+ /// Find closest time in [0,1] that maps to the given value. */
+ Coord nearestTime(Coord v) {
+ if (v <= min()) return 0;
+ if (v >= max()) return 1;
+ return timeAt(v);
+ }
/// @}
/// @name Test coordinates and other intervals for inclusion.
@@ -114,7 +123,16 @@ public:
/** @brief Check whether the interior of the interval includes the given interval.
* Interior means all numbers in the interval except its ends. */
bool interiorContains(Interval const &val) const { return min() < val.min() && val.max() < max(); }
- /** @brief Check whether the interiors of the intervals have any common elements. A single point in common is not considered an intersection. */
+ /// Check whether the number is contained in the union of the interior and the lower boundary.
+ bool lowerContains(Coord val) { return min() <= val && val < max(); }
+ /// Check whether the given interval is contained in the union of the interior and the lower boundary.
+ bool lowerContains(Interval const &val) const { return min() <= val.min() && val.max() < max(); }
+ /// Check whether the number is contained in the union of the interior and the upper boundary.
+ bool upperContains(Coord val) { return min() < val && val <= max(); }
+ /// Check whether the given interval is contained in the union of the interior and the upper boundary.
+ bool upperContains(Interval const &val) const { return min() < val.min() && val.max() <= max(); }
+ /** @brief Check whether the interiors of the intervals have any common elements.
+ * A single point in common is not considered an intersection. */
bool interiorIntersects(Interval const &val) const {
return std::max(min(), val.min()) < std::min(max(), val.max());
}
@@ -125,16 +143,18 @@ public:
// IMPL: ScalableConcept
/** @brief Scale an interval */
Interval &operator*=(Coord s) {
+ using std::swap;
_b[0] *= s;
_b[1] *= s;
- if(s < 0) std::swap(_b[0], _b[1]);
+ if(s < 0) swap(_b[0], _b[1]);
return *this;
}
/** @brief Scale an interval by the inverse of the specified value */
Interval &operator/=(Coord s) {
+ using std::swap;
_b[0] /= s;
_b[1] /= s;
- if(s < 0) std::swap(_b[0], _b[1]);
+ if(s < 0) swap(_b[0], _b[1]);
return *this;
}
/** @brief Multiply two intervals.
diff --git a/src/2geom/line.cpp b/src/2geom/line.cpp
index 097365245..bada8ef38 100644
--- a/src/2geom/line.cpp
+++ b/src/2geom/line.cpp
@@ -28,11 +28,9 @@
* the specific language governing rights and limitations.
*/
-
-#include <2geom/line.h>
-
#include <algorithm>
-
+#include <2geom/line.h>
+#include <2geom/math-utils.h>
namespace Geom
{
@@ -41,12 +39,18 @@ namespace Geom
* @class Line
* @brief Infinite line on a plane.
*
- * Every line in 2Geom has a special point on it, called the origin. The direction of the line
- * is stored as a unit vector (versor). This way a line can be interpreted as a function
- * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the origin point,
- * positive values to the points in the direction of the unit vector, and negative values
- * to points in the opposite direction.
- *
+ * A line is specified as two points through which it passes. Lines can be interpreted as functions
+ * \f$ f: (-\infty, \infty) \to \mathbb{R}^2\f$. Zero corresponds to the first (origin) point,
+ * one corresponds to the second (final) point. All other points are computed as a linear
+ * interpolation between those two: \f$p = (1-t) a + t b\f$. Many such functions have the same
+ * image and therefore represent the same lines; for example, adding \f$b-a\f$ to both points
+ * yields the same line.
+ *
+ * 2Geom can represent the same line in many ways by design: using a different representation
+ * would lead to precision loss. For example, a line from (1e30, 1e30) to (10,0) would actually
+ * evaluate to (0,0) at time 1 if it was stored as origin and normalized versor,
+ * or origin and angle.
+ *
* @ingroup Primitives
*/
@@ -54,36 +58,69 @@ namespace Geom
* A line is a set of points that satisfies the line equation
* \f$Ax + By + C = 0\f$. This function changes the line so that its points
* satisfy the line equation with the given coefficients. */
-void Line::setCoefficients (double a, double b, double c) {
+void Line::setCoefficients (Coord a, Coord b, Coord c)
+{
+ // degenerate case
if (a == 0 && b == 0) {
if (c != 0) {
- THROW_LOGICALERROR("the passed coefficients gives the empty set");
+ THROW_LOGICALERROR("the passed coefficients give the empty set");
}
- m_versor = Point(0,0);
- m_origin = Point(0,0);
- } else {
- double l = hypot(a,b);
- a /= l;
- b /= l;
- c /= l;
- Point N(a, b);
- m_versor = N.ccw();
- m_origin = -c * N;
+ _initial = Point(0,0);
+ _final = Point(0,0);
+ return;
}
+
+ // The way final / initial points are set based on coefficients is somewhat unusual.
+ // This is done to make sure that calling coefficients() will give back
+ // (almost) the same values.
+
+ // vertical case
+ if (a == 0) {
+ // b must be nonzero
+ _initial = Point(-b/2, -c / b);
+ _final = _initial;
+ _final[X] = b/2;
+ return;
+ }
+
+ // horizontal case
+ if (b == 0) {
+ _initial = Point(-c / a, a/2);
+ _final = _initial;
+ _final[Y] = -a/2;
+ return;
+ }
+
+ // This gives reasonable results regardless of the magnitudes of a, b and c.
+ _initial = Point(-b/2,a/2);
+ _final = Point(b/2,-a/2);
+
+ Point offset(-c/(2*a), -c/(2*b));
+
+ _initial += offset;
+ _final += offset;
+}
+
+void Line::coefficients(Coord &a, Coord &b, Coord &c) const
+{
+ Point v = versor().cw();
+ a = v[X];
+ b = v[Y];
+ c = cross(_initial, _final);
}
-/** @brief Get the line equation coefficients of this line.
+/** @brief Get the implicit line equation coefficients.
+ * Note that conversion to implicit form always causes loss of
+ * precision when dealing with lines that start far from the origin
+ * and end very close to it. It is recommended to normalize the line
+ * before converting it to implicit form.
* @return Vector with three values corresponding to the A, B and C
* coefficients of the line equation for this line. */
-std::vector<double> Line::coefficients() const {
- std::vector<double> coeff;
- coeff.reserve(3);
- Point N = versor().cw();
- coeff.push_back (N[X]);
- coeff.push_back (N[Y]);
- double d = - dot (N, origin());
- coeff.push_back (d);
- return coeff;
+std::vector<Coord> Line::coefficients() const
+{
+ std::vector<Coord> c(3);
+ coefficients(c[0], c[1], c[2]);
+ return c;
}
/** @brief Find intersection with an axis-aligned line.
@@ -91,87 +128,213 @@ std::vector<double> Line::coefficients() const {
* @param d Which axis the coordinate is on. X means a vertical line, Y means a horizontal line.
* @return Time values at which this line intersects the query line. */
std::vector<Coord> Line::roots(Coord v, Dim2 d) const {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Line::roots, dimension argument out of range");
std::vector<Coord> result;
- if ( m_versor[d] != 0 )
- {
- result.push_back( (v - m_origin[d]) / m_versor[d] );
+ Coord r = root(v, d);
+ if (IS_FINITE(r)) {
+ result.push_back(r);
}
- // TODO: else ?
return result;
}
+Coord Line::root(Coord v, Dim2 d) const
+{
+ assert(d == X || d == Y);
+ Point vs = versor();
+ if (vs[d] != 0) {
+ return (v - _initial[d]) / vs[d];
+ } else {
+ return nan("");
+ }
+}
+
+boost::optional<LineSegment> Line::clip(Rect const &r) const
+{
+ Point v = versor();
+ // handle horizontal and vertical lines first,
+ // since the root-based code below will break for them
+ for (unsigned i = 0; i < 2; ++i) {
+ Dim2 d = (Dim2) i;
+ Dim2 o = other_dimension(d);
+ if (v[d] != 0) continue;
+ if (r[d].contains(_initial[d])) {
+ Point a, b;
+ a[o] = r[o].min();
+ b[o] = r[o].max();
+ a[d] = b[d] = _initial[d];
+ if (v[o] > 0) {
+ return LineSegment(a, b);
+ } else {
+ return LineSegment(b, a);
+ }
+ } else {
+ return boost::none;
+ }
+ }
+
+ Interval xpart(root(r[X].min(), X), root(r[X].max(), X));
+ Interval ypart(root(r[Y].min(), Y), root(r[Y].max(), Y));
+ if (!xpart.isFinite() || !ypart.isFinite()) {
+ return boost::none;
+ }
+
+ OptInterval common = xpart & ypart;
+ if (common) {
+ Point p1 = pointAt(common->min()), p2 = pointAt(common->max());
+ LineSegment result(r.clamp(p1), r.clamp(p2));
+ return result;
+ } else {
+ return boost::none;
+ }
+
+ /* old implementation using coefficients:
+
+ if (fabs(b) > fabs(a)) {
+ p0 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p0[Y] < r[Y].min())
+ p0 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p0[Y] > r[Y].max())
+ p0 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ p1 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ if (p1[Y] < r[Y].min())
+ p1 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p1[Y] > r[Y].max())
+ p1 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ } else {
+ p0 = Point((-c - b*r[Y].min())/a, r[Y].min());
+ if (p0[X] < r[X].min())
+ p0 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p0[X] > r[X].max())
+ p0 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ p1 = Point((-c - b*r[Y].max())/a, r[Y].max());
+ if (p1[X] < r[X].min())
+ p1 = Point(r[X].min(), (-c - a*r[X].min())/b);
+ if (p1[X] > r[X].max())
+ p1 = Point(r[X].max(), (-c - a*r[X].max())/b);
+ }
+ return LineSegment(p0, p1); */
+}
+
/** @brief Get a time value corresponding to a point.
* @param p Point on the line. If the point is not on the line,
* the returned value will be meaningless.
* @return Time value t such that \f$f(t) = p\f$.
* @see timeAtProjection */
-Coord Line::timeAt(Point const& _point) const {
- Coord t;
- if ( m_versor[X] != 0 ) {
- t = (_point[X] - m_origin[X]) / m_versor[X];
+Coord Line::timeAt(Point const &p) const
+{
+ Point v = versor();
+ // degenerate case
+ if (v[X] == 0 && v[Y] == 0) {
+ return 0;
}
- else if ( m_versor[Y] != 0 ) {
- t = (_point[Y] - m_origin[Y]) / m_versor[Y];
+
+ // use the coordinate that will give better precision
+ if (fabs(v[X]) > fabs(v[Y])) {
+ return (p[X] - _initial[X]) / v[X];
+ } else {
+ return (p[Y] - _initial[Y]) / v[Y];
}
- else { // degenerate case
- t = 0;
+}
+
+std::vector<ShapeIntersection> Line::intersect(Line const &other) const
+{
+ std::vector<ShapeIntersection> result;
+
+ Point v1 = versor();
+ Point v2 = other.versor();
+ Coord cp = cross(v1, v2);
+ if (cp == 0) return result;
+
+ Point odiff = other.initialPoint() - initialPoint();
+ Coord t1 = cross(odiff, v2) / cp;
+ Coord t2 = cross(odiff, v1) / cp;
+ result.push_back(ShapeIntersection(*this, other, t1, t2));
+ return result;
+}
+
+std::vector<ShapeIntersection> Line::intersect(Ray const &r) const
+{
+ Line other(r);
+ std::vector<ShapeIntersection> result = intersect(other);
+ filter_ray_intersections(result, false, true);
+ return result;
+}
+
+std::vector<ShapeIntersection> Line::intersect(LineSegment const &ls) const
+{
+ Line other(ls);
+ std::vector<ShapeIntersection> result = intersect(other);
+ filter_line_segment_intersections(result, false, true);
+ return result;
+}
+
+
+
+void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a, bool b)
+{
+ Interval unit(0, 1);
+ std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend();
+ while (i != last) {
+ if ((a && !unit.contains(i->first)) || (b && !unit.contains(i->second))) {
+ xs.erase((++i).base());
+ } else {
+ ++i;
+ }
+ }
+}
+
+void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a, bool b)
+{
+ Interval unit(0, 1);
+ std::vector<ShapeIntersection>::reverse_iterator i = xs.rbegin(), last = xs.rend();
+ while (i != last) {
+ if ((a && i->first < 0) || (b && i->second < 0)) {
+ xs.erase((++i).base());
+ } else {
+ ++i;
+ }
}
- return t;
}
namespace detail
{
inline
-OptCrossing intersection_impl(Point const& V1, Point const O1,
- Point const& V2, Point const O2 )
+OptCrossing intersection_impl(Point const &v1, Point const &o1,
+ Point const &v2, Point const &o2)
{
- double detV1V2 = V1[X] * V2[Y] - V2[X] * V1[Y];
- if (are_near(detV1V2, 0)) return OptCrossing();
+ Coord cp = cross(v1, v2);
+ if (cp == 0) return OptCrossing();
- Point B = O2 - O1;
- double detBV2 = B[X] * V2[Y] - V2[X] * B[Y];
- double detV1B = B[X] * V1[Y] - V1[X] * B[Y];
- double inv_detV1V2 = 1 / detV1V2;
+ Point odiff = o2 - o1;
Crossing c;
- c.ta = detBV2 * inv_detV1V2;
- c.tb = detV1B * inv_detV1V2;
-// std::cerr << "ta = " << c.ta << std::endl;
-// std::cerr << "tb = " << c.tb << std::endl;
- return OptCrossing(c);
+ c.ta = cross(odiff, v2) / cp;
+ c.tb = cross(odiff, v1) / cp;
+ return c;
}
OptCrossing intersection_impl(Ray const& r1, Line const& l2, unsigned int i)
{
+ using std::swap;
+
OptCrossing crossing =
intersection_impl(r1.versor(), r1.origin(),
l2.versor(), l2.origin() );
- if (crossing)
- {
- if (crossing->ta < 0)
- {
+ if (crossing) {
+ if (crossing->ta < 0) {
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap(crossing->ta, crossing->tb);
+ } else {
+ if (i != 0) {
+ swap(crossing->ta, crossing->tb);
}
return crossing;
}
}
- if (are_near(r1.origin(), l2))
- {
+ if (are_near(r1.origin(), l2)) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
return OptCrossing();
}
}
@@ -181,34 +344,29 @@ OptCrossing intersection_impl( LineSegment const& ls1,
Line const& l2,
unsigned int i )
{
+ using std::swap;
+
OptCrossing crossing =
intersection_impl(ls1.finalPoint() - ls1.initialPoint(),
ls1.initialPoint(),
l2.versor(),
l2.origin() );
- if (crossing)
- {
+ if (crossing) {
if ( crossing->getTime(0) < 0
|| crossing->getTime(0) > 1 )
{
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap((*crossing).ta, (*crossing).tb);
+ } else {
+ if (i != 0) {
+ swap((*crossing).ta, (*crossing).tb);
}
return crossing;
}
}
- if (are_near(ls1.initialPoint(), l2))
- {
+ if (are_near(ls1.initialPoint(), l2)) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
return OptCrossing();
}
}
@@ -218,6 +376,8 @@ OptCrossing intersection_impl( LineSegment const& ls1,
Ray const& r2,
unsigned int i )
{
+ using std::swap;
+
Point direction = ls1.finalPoint() - ls1.initialPoint();
OptCrossing crossing =
intersection_impl( direction,
@@ -225,57 +385,40 @@ OptCrossing intersection_impl( LineSegment const& ls1,
r2.versor(),
r2.origin() );
- if (crossing)
- {
+ if (crossing) {
if ( (crossing->getTime(0) < 0)
|| (crossing->getTime(0) > 1)
|| (crossing->getTime(1) < 0) )
{
return OptCrossing();
- }
- else
- {
- if (i != 0)
- {
- std::swap(crossing->ta, crossing->tb);
+ } else {
+ if (i != 0) {
+ swap(crossing->ta, crossing->tb);
}
return crossing;
}
}
- if ( are_near(r2.origin(), ls1) )
- {
+ if ( are_near(r2.origin(), ls1) ) {
bool eqvs = (dot(direction, r2.versor()) > 0);
- if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs )
- {
+ if ( are_near(ls1.initialPoint(), r2.origin()) && !eqvs) {
crossing->ta = crossing->tb = 0;
return crossing;
- }
- else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs )
- {
- if (i == 0)
- {
+ } else if ( are_near(ls1.finalPoint(), r2.origin()) && eqvs) {
+ if (i == 0) {
crossing->ta = 1;
crossing->tb = 0;
- }
- else
- {
+ } else {
crossing->ta = 0;
crossing->tb = 1;
}
return crossing;
- }
- else
- {
+ } else {
THROW_INFINITESOLUTIONS();
}
- }
- else if ( are_near(ls1.initialPoint(), r2) )
- {
+ } else if ( are_near(ls1.initialPoint(), r2) ) {
THROW_INFINITESOLUTIONS();
- }
- else
- {
+ } else {
OptCrossing no_crossing;
return no_crossing;
}
@@ -287,24 +430,16 @@ OptCrossing intersection_impl( LineSegment const& ls1,
OptCrossing intersection(Line const& l1, Line const& l2)
{
- OptCrossing crossing =
- detail::intersection_impl( l1.versor(), l1.origin(),
- l2.versor(), l2.origin() );
- if (crossing)
- {
- return crossing;
- }
- if (are_near(l1.origin(), l2))
- {
+ OptCrossing c = detail::intersection_impl(
+ l1.versor(), l1.origin(),
+ l2.versor(), l2.origin());
+
+ if (!c && distance(l1.origin(), l2) == 0) {
THROW_INFINITESOLUTIONS();
}
- else
- {
- return crossing;
- }
+ return c;
}
-
OptCrossing intersection(Ray const& r1, Ray const& r2)
{
OptCrossing crossing =
@@ -416,64 +551,6 @@ OptCrossing intersection( LineSegment const& ls1, LineSegment const& ls2 )
}
}
-
-boost::optional<LineSegment> clip (Line const& l, Rect const& r)
-{
- typedef boost::optional<LineSegment> opt_linesegment;
- LineSegment result;
- //size_t index = 0;
- std::vector<Point> points;
- LineSegment ls (r.corner(0), r.corner(1));
- try
- {
- OptCrossing oc = intersection (ls, l);
- if (oc)
- {
- points.push_back (l.pointAt (oc->tb));
- }
- }
- catch (InfiniteSolutions const &e)
- {
- return opt_linesegment(ls);
- }
-
- for (size_t i = 2; i < 5; ++i)
- {
- ls.setInitial (ls[1]);
- ls.setFinal (r.corner(i));
- try
- {
- OptCrossing oc = intersection (ls, l);
- if (oc)
- {
- points.push_back (l.pointAt (oc->tb));
- if (points.size() > 1)
- {
- size_t sz = points.size();
- if (!are_near (points[sz - 2], points[sz - 1], 1e-10))
- {
- result.setInitial (points[sz - 2]);
- result.setFinal (points[sz - 1]);
- return opt_linesegment(result);
- }
- }
- }
- }
- catch (InfiniteSolutions const &e)
- {
- return opt_linesegment(ls);
- }
- }
- if ( !points.empty() )
- {
- result.setInitial (points[0]);
- result.setFinal (points[0]);
- return opt_linesegment(result);
- }
- return opt_linesegment();
-}
-
-
Line make_angle_bisector_line(Line const& l1, Line const& l2)
{
OptCrossing crossing;
diff --git a/src/2geom/line.h b/src/2geom/line.h
index cbd68fa08..7d4766e12 100644
--- a/src/2geom/line.h
+++ b/src/2geom/line.h
@@ -35,7 +35,6 @@
#define LIB2GEOM_SEEN_LINE_H
#include <cmath>
-#include <iostream>
#include <boost/optional.hpp>
#include <2geom/bezier-curve.h> // for LineSegment
#include <2geom/rect.h>
@@ -43,36 +42,46 @@
#include <2geom/exception.h>
#include <2geom/ray.h>
#include <2geom/angle.h>
+#include <2geom/intersection.h>
namespace Geom
{
-class Line {
+// class docs in cpp file
+class Line
+ : boost::equality_comparable< Line >
+{
private:
- Point m_origin;
- Point m_versor;
+ Point _initial;
+ Point _final;
public:
/// @name Creating lines.
/// @{
- /** @brief Create a default horizontal line. */
+ /** @brief Create a default horizontal line.
+ * Creates a line with unit speed going in +X direction. */
Line()
- : m_origin(0,0), m_versor(1,0)
+ : _initial(0,0), _final(1,0)
{}
/** @brief Create a line with the specified inclination.
- * @param _origin One of the points on the line
+ * @param origin One of the points on the line
* @param angle Angle of the line in mathematical convention */
- Line(Point const& _origin, Coord angle )
- : m_origin(_origin)
+ Line(Point const &origin, Coord angle)
+ : _initial(origin)
{
- sincos(angle, m_versor[Y], m_versor[X]);
+ Point v;
+ sincos(angle, v[Y], v[X]);
+ _final = _initial + v;
}
/** @brief Create a line going through two points.
- * @param A First point
- * @param B Second point */
- Line(Point const& A, Point const& B) {
- setPoints(A, B);
- }
+ * The first point will be at time 0, while the second one
+ * will be at time 1.
+ * @param a Initial point
+ * @param b First point */
+ Line(Point const &a, Point const &b)
+ : _initial(a)
+ , _final(b)
+ {}
/** @brief Create a line based on the coefficients of its equation.
@see Line::setCoefficients() */
@@ -80,30 +89,30 @@ public:
setCoefficients(a, b, c);
}
- /** @brief Create a line by extending a line segment. */
- explicit Line(LineSegment const& _segment) {
- setPoints(_segment.initialPoint(), _segment.finalPoint());
- }
+ /// Create a line by extending a line segment.
+ explicit Line(LineSegment const &seg)
+ : _initial(seg.initialPoint())
+ , _final(seg.finalPoint())
+ {}
- /** @brief Create a line by extending a ray. */
- explicit Line(Ray const& _ray)
- : m_origin(_ray.origin()), m_versor(_ray.versor())
+ /// Create a line by extending a ray.
+ explicit Line(Ray const &r)
+ : _initial(r.origin())
+ , _final(r.origin() + r.versor())
{}
- // huh?
- static Line from_normal_distance(Point n, double c) {
- Point P = n * c / dot(n,n);
- Line l(P, P+rot90(n));
+ /// Create a line normal to a vector at a specified distance from origin.
+ static Line from_normal_distance(Point const &n, Coord c) {
+ Point start = c * n.normalized();
+ Line l(start, start + rot90(n));
return l;
}
/** @brief Create a line from origin and unit vector.
* Note that each line direction has two possible unit vectors.
* @param o Point through which the line will pass
* @param v Unit vector of the line's direction */
- static Line from_origin_and_versor(Point o, Point v) {
- Line l;
- l.m_origin = o;
- l.m_versor = v;
+ static Line from_origin_and_versor(Point const &o, Point const &v) {
+ Line l(o, o + v);
return l;
}
@@ -114,63 +123,113 @@ public:
/// @name Retrieve and set the line's parameters.
/// @{
- /** @brief Get the line's origin point. */
- Point origin() const { return m_origin; }
- /** @brief Get the line's direction unit vector. */
- Point versor() const { return m_versor; }
- // return the angle described by rotating the X-axis in cw direction
- // until it overlaps the line
- // the returned value is in the interval [0, PI[
+
+ /// Get the line's origin point.
+ Point origin() const { return _initial; }
+ /** @brief Get the line's direction vector.
+ * Note that the retrieved vector is not normalized to unit length. */
+ Point versor() const { return _final - _initial; }
+ /// Angle the line makes with the X axis, in mathematical convention.
Coord angle() const {
- double a = std::atan2(m_versor[Y], m_versor[X]);
+ Point d = _final - _initial;
+ double a = std::atan2(d[Y], d[X]);
if (a < 0) a += M_PI;
if (a == M_PI) a = 0;
return a;
}
- void setOrigin(Point const& _point) {
- m_origin = _point;
+ /** @brief Set the point at zero time.
+ * The orientation remains unchanged, modulo numeric errors during addition. */
+ void setOrigin(Point const &p) {
+ Point d = p - _initial;
+ _initial = p;
+ _final += d;
}
- void setVersor(Point const& _versor) {
- m_versor = _versor;
+ /** @brief Set the speed of the line.
+ * Origin remains unchanged. */
+ void setVersor(Point const &v) {
+ _final = _initial + v;
}
- void setAngle(Coord _angle) {
- sincos(_angle, m_versor[Y], m_versor[X]);
+ /** @brief Set the angle the line makes with the X axis.
+ * Origin remains unchanged. */
+ void setAngle(Coord angle) {
+ Point v;
+ sincos(angle, v[Y], v[X]);
+ v *= distance(_initial, _final);
+ _final = _initial + v;
}
- /** @brief Set a line based on two points it should pass through. */
- void setPoints(Point const& A, Point const& B) {
- m_origin = A;
- if ( are_near(A, B) )
- m_versor = Point(0,0);
- else
- m_versor = B - A;
- m_versor.normalize();
+ /// Set a line based on two points it should pass through.
+ void setPoints(Point const &a, Point const &b) {
+ _initial = a;
+ _final = b;
}
- void setCoefficients (double a, double b, double c);
+ /** @brief Set the coefficients of the line equation.
+ * The line equation is: \f$ax + by = c\f$. Points that satisfy the equation
+ * are on the line. */
+ void setCoefficients(double a, double b, double c);
+
+ /** @brief Get the coefficients of the line equation as a vector.
+ * @return STL vector @a v such that @a v[0] contains \f$a\f$, @a v[1] contains \f$b\f$,
+ * and @a v[2] contains \f$c\f$. */
std::vector<double> coefficients() const;
- /** @brief Check if the line has any points.
+ /// Get the coefficients of the line equation by reference.
+ void coefficients(Coord &a, Coord &b, Coord &c) const;
+
+ /** @brief Check if the line has more than one point.
* A degenerate line can be created if the line is created from a line equation
* that has no solutions.
- * @return True if the line has no points */
+ * @return True if the line has no points or exactly one point */
bool isDegenerate() const {
- return ( m_versor[X] == 0 && m_versor[Y] == 0 );
+ return _initial == _final;
+ }
+ /// Check if the line is horizontal (y is constant).
+ bool isHorizontal() const {
+ return _initial[Y] == _final[Y];
+ }
+ /// Check if the line is vertical (x is constant).
+ bool isVertical() const {
+ return _initial[X] == _final[X];
+ }
+
+ /** @brief Reparametrize the line so that it has unit speed.
+ * Note that the direction of the line may also change. */
+ void normalize() {
+ // this helps with the nasty case of a line that starts somewhere far
+ // and ends very close to the origin
+ if (L2sq(_final) < L2sq(_initial)) {
+ std::swap(_initial, _final);
+ }
+ Point v = _final - _initial;
+ v.normalize();
+ _final = _initial + v;
+ }
+ /** @brief Return a new line reparametrized for unit speed. */
+ Line normalized() const {
+ Point v = _final - _initial;
+ v.normalize();
+ Line ret(_initial, _initial + v);
+ return ret;
}
/// @}
/// @name Evaluate the line as a function.
///@{
+ Point initialPoint() const {
+ return _initial;
+ }
+ Point finalPoint() const {
+ return _final;
+ }
Point pointAt(Coord t) const {
- return m_origin + m_versor * t;
+ return lerp(t, _initial, _final);;
}
Coord valueAt(Coord t, Dim2 d) const {
- if (d < 0 || d > 1)
- THROW_RANGEERROR("Line::valueAt, dimension argument out of range");
- return m_origin[d] + m_versor[d] * t;
+ return lerp(t, _initial[d], _final[d]);
}
Coord timeAt(Point const &p) const;
@@ -180,27 +239,29 @@ public:
* @return Time value corresponding to a point closest to @c p. */
Coord timeAtProjection(Point const& p) const {
if ( isDegenerate() ) return 0;
- return dot( p - m_origin, m_versor );
+ Point v = versor();
+ return dot(p - _initial, v) / dot(v, v);
}
/** @brief Find a point on the line closest to the query point.
* This is an alias for timeAtProjection(). */
- Coord nearestPoint(Point const& _point) const {
- return timeAtProjection(_point);
+ Coord nearestTime(Point const &p) const {
+ return timeAtProjection(p);
}
std::vector<Coord> roots(Coord v, Dim2 d) const;
+ Coord root(Coord v, Dim2 d) const;
/// @}
/// @name Create other objects based on this line.
/// @{
- /** @brief Create a line containing the same points, but with negated time values.
- * @return Line \f$g\f$ such that \f$g(t) = f(-t)\f$ */
- Line reverse() const
- {
- Line result;
- result.setOrigin(m_origin);
- result.setVersor(-m_versor);
+ void reverse() {
+ std::swap(_final, _initial);
+ }
+ /** @brief Create a line containing the same points, but in opposite direction.
+ * @return Line \f$g\f$ such that \f$g(t) = f(1-t)\f$ */
+ Line reversed() const {
+ Line result(_final, _initial);
return result;
}
@@ -219,6 +280,9 @@ public:
return LineSegment(pointAt(f), pointAt(t));
}
+ /// Return the portion of the line that is inside the given rectangle
+ boost::optional<LineSegment> clip(Rect const &r) const;
+
/** @brief Create a ray starting at the specified time value.
* The created ray will go in the direction of the line's versor (in the direction
* of increasing time values).
@@ -227,7 +291,7 @@ public:
Ray ray(Coord t) {
Ray result;
result.setOrigin(pointAt(t));
- result.setVersor(m_versor);
+ result.setVersor(versor());
return result;
}
@@ -235,88 +299,148 @@ public:
* The new line will always be degenerate. Its origin will be equal to this
* line's versor. */
Line derivative() const {
- Line result;
- result.setOrigin(m_versor);
- result.setVersor(Point(0,0));
+ Point v = versor();
+ Line result(v, v);
return result;
}
- /** @brief Create a line transformed by an affine transformation. */
+ /// Create a line transformed by an affine transformation.
Line transformed(Affine const& m) const {
- return Line(m_origin * m, (m_origin + m_versor) * m);
+ Line l(_initial * m, _final * m);
+ return l;
}
- /** @brief Get a vector normal to the line.
+ /** @brief Get a unit vector normal to the line.
* If Y grows upwards, then this is the left normal. If Y grows downwards,
* then this is the right normal. */
Point normal() const {
- return rot90(m_versor);
+ return rot90(versor()).normalized();
}
// what does this do?
Point normalAndDist(double & dist) const {
Point n = normal();
- dist = -dot(n, m_origin);
+ dist = -dot(n, _initial);
return n;
}
- friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line);
-/// @}
-}; // end class Line
+ /// Compute an affine matrix representing a reflection about the line.
+ Affine reflection() const {
+ Point v = versor().normalized();
+ Coord x2 = v[X]*v[X], y2 = v[Y]*v[Y], xy = v[X]*v[Y];
+ Affine m(x2-y2, 2.*xy,
+ 2.*xy, y2-x2,
+ _initial[X], _initial[Y]);
+ m = Translate(-_initial) * m;
+ return m;
+ }
-/** @brief Output operator for lines.
- * Prints out representation (point + versor)
- */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::Line &in_line) {
- out_file << "X: " << in_line.m_origin[X] << " Y: " << in_line.m_origin[Y]
- << " dX: " << in_line.m_versor[X] << " dY: " << in_line.m_versor[Y];
- return out_file;
-}
+ /** @brief Compute an affine which transforms all points on the line to zero X or Y coordinate.
+ * This operation is useful in reducing intersection problems to root-finding problems.
+ * There are many affines which do this transformation. This function returns one that
+ * preserves angles, areas and distances - a rotation combined with a translation, and
+ * additionaly moves the initial point of the line to (0,0). This way it works without
+ * problems even for lines perpendicular to the target, though may in some cases have
+ * lower precision than e.g. a shear transform.
+ * @param d Which coordinate of points on the line should be zero after the transformation */
+ Affine rotationToZero(Dim2 d) const {
+ Point v = versor();
+ if (d == X) {
+ std::swap(v[X], v[Y]);
+ } else {
+ v[Y] = -v[Y];
+ }
+ Affine m = Translate(-_initial) * Rotate(v);
+ return m;
+ }
+ /** @brief Compute a rotation affine which transforms the line to one of the axes.
+ * @param d Which line should be the axis */
+ Affine rotationToAxis(Dim2 d) const {
+ Affine m = rotationToZero(other_dimension(d));
+ return m;
+ }
+ /// @}
-inline
-double distance(Point const& _point, Line const& _line)
-{
- if ( _line.isDegenerate() )
- {
- return ::Geom::distance( _point, _line.origin() );
+ std::vector<ShapeIntersection> intersect(Line const &other) const;
+ std::vector<ShapeIntersection> intersect(Ray const &r) const;
+ std::vector<ShapeIntersection> intersect(LineSegment const &ls) const;
+
+ template <typename T>
+ Line &operator*=(T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ _initial *= tr;
+ _final *= tr;
+ return *this;
}
- else
- {
- return fabs( dot(_point - _line.origin(), _line.versor().ccw()) );
+
+ bool operator==(Line const &other) const {
+ if (distance(pointAt(nearestTime(other._initial)), other._initial) != 0) return false;
+ if (distance(pointAt(nearestTime(other._final)), other._final) != 0) return false;
+ return true;
}
-}
+ template <typename T>
+ friend Line operator*(Line const &l, T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ Line result(l);
+ result *= tr;
+ return result;
+ }
+}; // end class Line
+
+/** @brief Removes intersections outside of the unit interval.
+ * A helper used to implement line segment intersections.
+ * @param xs Line intersections
+ * @param a Whether the first time value has to be in the unit interval
+ * @param b Whether the second time value has to be in the unit interval
+ * @return Appropriately filtered intersections */
+void filter_line_segment_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+void filter_ray_intersections(std::vector<ShapeIntersection> &xs, bool a=false, bool b=true);
+
+/// @brief Compute distance from point to line.
+/// @relates Line
inline
-bool are_near(Point const& _point, Line const& _line, double eps = EPSILON)
+double distance(Point const &p, Line const &line)
{
- return are_near(distance(_point, _line), 0, eps);
+ if (line.isDegenerate()) {
+ return ::Geom::distance(p, line.initialPoint());
+ } else {
+ Coord t = line.nearestTime(p);
+ return ::Geom::distance(line.pointAt(t), p);
+ }
}
inline
-bool are_parallel(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_near(Point const &p, Line const &line, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor(), eps)
- || are_near(l1.versor(), -l2.versor(), eps) );
+ return are_near(distance(p, line), 0, eps);
}
inline
-bool are_same(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_parallel(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
+ return are_near(cross(l1.versor(), l2.versor()), 0, eps);
}
+/** @brief Test whether two lines are approximately the same.
+ * This tests for being parallel and the origin of one line being close to the other,
+ * so it tests whether the images of the lines are similar, not whether the same time values
+ * correspond to similar points. For example a line from (1,1) to (2,2) and a line from
+ * (-1,-1) to (0,0) will the the same, because their images match, even though there is
+ * no time value for which the lines give similar points.
+ * @relates Line */
inline
-bool are_orthogonal(Line const& l1, Line const& l2, double eps = EPSILON)
+bool are_same(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return ( are_near(l1.versor(), l2.versor().cw(), eps)
- || are_near(l1.versor(), l2.versor().ccw(), eps) );
+ return are_parallel(l1, l2, eps) && are_near(l1.origin(), l2, eps);
}
+/// Test whether two lines are perpendicular.
+/// @relates Line
inline
-bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
- double eps = EPSILON)
+bool are_orthogonal(Line const &l1, Line const &l2, double eps = EPSILON)
{
- return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+ return are_near(dot(l1.versor(), l2.versor()), 0, eps);
}
// evaluate the angle between l1 and l2 rotating l1 in cw direction
@@ -332,36 +456,34 @@ double angle_between(Line const& l1, Line const& l2)
}
inline
-double distance(Point const& _point, LineSegment const& _segment)
+double distance(Point const &p, LineSegment const &seg)
{
- double t = _segment.nearestPoint(_point);
- return L2(_point - _segment.pointAt(t));
+ double t = seg.nearestTime(p);
+ return distance(p, seg.pointAt(t));
}
inline
-bool are_near(Point const& _point, LineSegment const& _segment,
- double eps = EPSILON)
+bool are_near(Point const &p, LineSegment const &seg, double eps = EPSILON)
{
- return are_near(distance(_point, _segment), 0, eps);
+ return are_near(distance(p, seg), 0, eps);
}
// build a line passing by _point and orthogonal to _line
inline
-Line make_orthogonal_line(Point const& _point, Line const& _line)
+Line make_orthogonal_line(Point const &p, Line const &line)
{
- Line l;
- l.setOrigin(_point);
- l.setVersor(_line.versor().cw());
+ Point d = line.versor().cw();
+ Line l(p, p + d);
return l;
}
// build a line passing by _point and parallel to _line
inline
-Line make_parallel_line(Point const& _point, Line const& _line)
+Line make_parallel_line(Point const &p, Line const &line)
{
- Line l(_line);
- l.setOrigin(_point);
- return l;
+ Line result(line);
+ result.setOrigin(p);
+ return result;
}
// build a line passing by the middle point of _segment and orthogonal to it.
@@ -373,31 +495,31 @@ Line make_bisector_line(LineSegment const& _segment)
// build the bisector line of the angle between ray(O,A) and ray(O,B)
inline
-Line make_angle_bisector_line(Point const& A, Point const& O, Point const& B)
+Line make_angle_bisector_line(Point const &A, Point const &O, Point const &B)
{
- Point M = middle_point(A,B);
- if (are_near(O,M)) {
- Line l(A,B);
- M += (make_orthogonal_line(O,l)).versor();
- }
- return Line(O,M);
+ AngleInterval ival(Angle(A-O), Angle(B-O));
+ Angle bisect = ival.angleAt(0.5);
+ return Line(O, bisect);
}
// prj(P) = rot(v, Point( rot(-v, P-O)[X], 0 )) + O
inline
-Point projection(Point const& _point, Line const& _line)
+Point projection(Point const &p, Line const &line)
{
- return _line.pointAt( _line.nearestPoint(_point) );
+ return line.pointAt(line.nearestTime(p));
}
inline
-LineSegment projection(LineSegment const& _segment, Line const& _line)
+LineSegment projection(LineSegment const &seg, Line const &line)
{
- return _line.segment( _line.nearestPoint(_segment.initialPoint()),
- _line.nearestPoint(_segment.finalPoint()) );
+ return line.segment(line.nearestTime(seg.initialPoint()),
+ line.nearestTime(seg.finalPoint()));
}
-boost::optional<LineSegment> clip (Line const& l, Rect const& r);
+inline
+boost::optional<LineSegment> clip(Line const &l, Rect const &r) {
+ return l.clip(r);
+}
namespace detail
diff --git a/src/2geom/linear.h b/src/2geom/linear.h
index 8c154364e..b0306fb9f 100644
--- a/src/2geom/linear.h
+++ b/src/2geom/linear.h
@@ -1,7 +1,7 @@
/**
* \file
* \brief Linear fragment function class
- *
+ *//*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* Michael Sloan <mgsloan@gmail.com>
@@ -32,12 +32,12 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_LINEAR_H
-#define SEEN_LINEAR_H
+#ifndef LIB2GEOM_SEEN_LINEAR_H
+#define LIB2GEOM_SEEN_LINEAR_H
+
#include <2geom/interval.h>
#include <2geom/math-utils.h>
-
//#define USE_SBASIS_OF
#ifdef USE_SBASIS_OF
@@ -46,48 +46,60 @@
#else
-namespace Geom{
-
-inline double lerp(double t, double a, double b) { return a*(1-t) + b*t; }
+namespace Geom {
class SBasis;
-class Linear{
+/**
+ * @brief Function that interpolates linearly between two values.
+ * @ingroup Fragments
+ */
+class Linear {
public:
double a[2];
Linear() {a[0]=0; a[1]=0;}
Linear(double aa, double b) {a[0] = aa; a[1] = b;}
Linear(double aa) {a[0] = aa; a[1] = aa;}
- double operator[](const int i) const {
- assert(i >= 0);
+ double operator[](unsigned i) const {
assert(i < 2);
return a[i];
}
- double& operator[](const int i) {
- assert(i >= 0);
+ double &operator[](unsigned i) {
assert(i < 2);
return a[i];
}
//IMPL: FragmentConcept
typedef double output_type;
- inline bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
- inline bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); }
- inline bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); }
-
- inline double at0() const { return a[0]; }
- inline double at1() const { return a[1]; }
-
- inline double valueAt(double t) const { return lerp(t, a[0], a[1]); }
- inline double operator()(double t) const { return valueAt(t); }
+ bool isZero(double eps=EPSILON) const { return are_near(a[0], 0., eps) && are_near(a[1], 0., eps); }
+ bool isConstant(double eps=EPSILON) const { return are_near(a[0], a[1], eps); }
+ bool isFinite() const { return IS_FINITE(a[0]) && IS_FINITE(a[1]); }
+
+ Coord at0() const { return a[0]; }
+ Coord &at0() { return a[0]; }
+ Coord at1() const { return a[1]; }
+ Coord &at1() { return a[1]; }
+
+ double valueAt(double t) const { return lerp(t, a[0], a[1]); }
+ double operator()(double t) const { return valueAt(t); }
+
+ // not very useful, but required for ShapeConcept
+ std::vector<Coord> valueAndDerivatives(Coord t, unsigned n) {
+ std::vector<Coord> result(n+1, 0.0);
+ result[0] = valueAt(t);
+ if (n >= 1) {
+ result[1] = a[1] - a[0];
+ }
+ return result;
+ }
//defined in sbasis.h
inline SBasis toSBasis() const;
- inline OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
- inline OptInterval bounds_fast() const { return bounds_exact(); }
- inline OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
+ OptInterval bounds_exact() const { return Interval(a[0], a[1]); }
+ OptInterval bounds_fast() const { return bounds_exact(); }
+ OptInterval bounds_local(double u, double v) const { return Interval(valueAt(u), valueAt(v)); }
double tri() const {
return a[1] - a[0];
@@ -98,6 +110,10 @@ public:
};
inline Linear reverse(Linear const &a) { return Linear(a[1], a[0]); }
+inline Linear portion(Linear const &a, Coord from, Coord to) {
+ Linear result(a.valueAt(from), a.valueAt(to));
+ return result;
+}
//IMPL: AddableConcept
inline Linear operator+(Linear const & a, Linear const & b) {
@@ -158,7 +174,7 @@ inline Linear operator/=(Linear & a, double b) {
}
#endif
-#endif //SEEN_LINEAR_H
+#endif //LIB2GEOM_SEEN_LINEAR_H
/*
Local Variables:
diff --git a/src/2geom/nearest-point.cpp b/src/2geom/nearest-time.cpp
index c5dfc133c..0b21e51a2 100644
--- a/src/2geom/nearest-point.cpp
+++ b/src/2geom/nearest-time.cpp
@@ -1,9 +1,8 @@
-/*
- * nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>>
- *
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
* Authors:
- *
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -32,27 +31,79 @@
*/
-#include <2geom/nearest-point.h>
+#include <2geom/nearest-time.h>
#include <algorithm>
-
namespace Geom
{
+Coord nearest_time(Point const &p, D2<Bezier> const &input, Coord from, Coord to)
+{
+ Interval domain(from, to);
+ bool partial = false;
+
+ if (domain.min() < 0 || domain.max() > 1) {
+ THROW_RANGEERROR("[from,to] interval out of bounds");
+ }
+
+ if (input.isConstant(0)) return from;
+
+ D2<Bezier> bez;
+ if (domain.min() != 0 || domain.max() != 1) {
+ bez = portion(input, domain) - p;
+ partial = true;
+ } else {
+ bez = input - p;
+ }
+
+ // find extrema of the function x(t)^2 + y(t)^2
+ // use the fact that (f^2)' = 2 f f'
+ // this reduces the order of the distance function by 1
+ D2<Bezier> deriv = derivative(bez);
+ std::vector<Coord> ts = (multiply(bez[X], deriv[X]) + multiply(bez[Y], deriv[Y])).roots();
+
+ Coord t = -1, mind = infinity();
+ for (unsigned i = 0; i < ts.size(); ++i) {
+ Coord droot = L2sq(bez.valueAt(ts[i]));
+ if (droot < mind) {
+ mind = droot;
+ t = ts[i];
+ }
+ }
+
+ // also check endpoints
+ Coord dinitial = L2sq(bez.at0());
+ Coord dfinal = L2sq(bez.at1());
+
+ if (dinitial < mind) {
+ mind = dinitial;
+ t = 0;
+ }
+ if (dfinal < mind) {
+ mind = dfinal;
+ t = 1;
+ }
+
+ if (partial) {
+ t = domain.valueAt(t);
+ }
+ return t;
+}
+
////////////////////////////////////////////////////////////////////////////////
// D2<SBasis> versions
/*
- * Return the parameter t of a nearest point on the portion of the curve "c",
+ * Return the parameter t of the nearest time value on the portion of the curve "c",
* related to the interval [from, to], to the point "p".
* The needed curve derivative "dc" is passed as parameter.
- * The function return the first nearest point to "p" that is found.
+ * The function return the first nearest time value to "p" that is found.
*/
-double nearest_point( Point const& p,
- D2<SBasis> const& c,
- D2<SBasis> const& dc,
- double from, double to )
+double nearest_time(Point const& p,
+ D2<SBasis> const& c,
+ D2<SBasis> const& dc,
+ double from, double to )
{
if ( from > to ) std::swap(from, to);
if ( from < 0 || to > 1 )
@@ -88,21 +139,20 @@ double nearest_point( Point const& p,
*/
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c,
- D2<SBasis> const& dc,
- double from, double to )
+all_nearest_times(Point const &p,
+ D2<SBasis> const &c,
+ D2<SBasis> const &dc,
+ double from, double to)
{
- std::swap(from, to);
- if ( from > to ) std::swap(from, to);
- if ( from < 0 || to > 1 )
- {
+ if (from > to) {
+ std::swap(from, to);
+ }
+ if (from < 0 || to > 1) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
std::vector<double> result;
- if (c.isConstant())
- {
+ if (c.isConstant()) {
result.push_back(from);
return result;
}
@@ -115,24 +165,19 @@ all_nearest_points( Point const& p,
candidates.push_back(to);
std::vector<double> distsq;
distsq.reserve(candidates.size());
- for ( unsigned int i = 0; i < candidates.size(); ++i )
- {
- distsq.push_back( L2sq(c(candidates[i]) - p) );
+ for (unsigned i = 0; i < candidates.size(); ++i) {
+ distsq.push_back(L2sq(c(candidates[i]) - p));
}
- unsigned int closest = 0;
+ unsigned closest = 0;
double dsq = distsq[0];
- for ( unsigned int i = 1; i < candidates.size(); ++i )
- {
- if ( dsq > distsq[i] )
- {
+ for (unsigned i = 1; i < candidates.size(); ++i) {
+ if (dsq > distsq[i]) {
closest = i;
dsq = distsq[i];
}
}
- for ( unsigned int i = 0; i < candidates.size(); ++i )
- {
- if( distsq[closest] == distsq[i] )
- {
+ for (unsigned i = 0; i < candidates.size(); ++i) {
+ if (distsq[closest] == distsq[i]) {
result.push_back(candidates[i]);
}
}
@@ -144,39 +189,37 @@ all_nearest_points( Point const& p,
// Piecewise< D2<SBasis> > versions
-double nearest_point( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to )
+double nearest_time(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to)
{
- if ( from > to ) std::swap(from, to);
- if ( from < c.cuts[0] || to > c.cuts[c.size()] )
- {
+ if (from > to) std::swap(from, to);
+ if (from < c.cuts[0] || to > c.cuts[c.size()]) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
- unsigned int si = c.segN(from);
- unsigned int ei = c.segN(to);
- if ( si == ei )
- {
- double nearest=
- nearest_point(p, c[si], c.segT(from, si), c.segT(to, si));
+ unsigned si = c.segN(from);
+ unsigned ei = c.segN(to);
+ if (si == ei) {
+ double nearest =
+ nearest_time(p, c[si], c.segT(from, si), c.segT(to, si));
return c.mapToDomain(nearest, si);
}
+
double t;
- double nearest = nearest_point(p, c[si], c.segT(from, si));
+ double nearest = nearest_time(p, c[si], c.segT(from, si));
unsigned int ni = si;
double dsq;
double mindistsq = distanceSq(p, c[si](nearest));
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
+ Rect bb;
+ for (unsigned i = si + 1; i < ei; ++i) {
bb = *bounds_fast(c[i]);
dsq = distanceSq(p, bb);
if ( mindistsq <= dsq ) continue;
- t = nearest_point(p, c[i]);
+
+ t = nearest_time(p, c[i]);
dsq = distanceSq(p, c[i](t));
- if ( mindistsq > dsq )
- {
+ if (mindistsq > dsq) {
nearest = t;
ni = i;
mindistsq = dsq;
@@ -184,12 +227,10 @@ double nearest_point( Point const& p,
}
bb = *bounds_fast(c[ei]);
dsq = distanceSq(p, bb);
- if ( mindistsq > dsq )
- {
- t = nearest_point(p, c[ei], 0, c.segT(to, ei));
+ if (mindistsq > dsq) {
+ t = nearest_time(p, c[ei], 0, c.segT(to, ei));
dsq = distanceSq(p, c[ei](t));
- if ( mindistsq > dsq )
- {
+ if (mindistsq > dsq) {
nearest = t;
ni = ei;
}
@@ -198,22 +239,23 @@ double nearest_point( Point const& p,
}
std::vector<double>
-all_nearest_points( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to )
+all_nearest_times(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to)
{
- if ( from > to ) std::swap(from, to);
- if ( from < c.cuts[0] || to > c.cuts[c.size()] )
- {
+ if (from > to) {
+ std::swap(from, to);
+ }
+ if (from < c.cuts[0] || to > c.cuts[c.size()]) {
THROW_RANGEERROR("[from,to] interval out of bounds");
}
- unsigned int si = c.segN(from);
- unsigned int ei = c.segN(to);
+ unsigned si = c.segN(from);
+ unsigned ei = c.segN(to);
if ( si == ei )
{
std::vector<double> all_nearest =
- all_nearest_points(p, c[si], c.segT(from, si), c.segT(to, si));
+ all_nearest_times(p, c[si], c.segT(from, si), c.segT(to, si));
for ( unsigned int i = 0; i < all_nearest.size(); ++i )
{
all_nearest[i] = c.mapToDomain(all_nearest[i], si);
@@ -222,18 +264,18 @@ all_nearest_points( Point const& p,
}
std::vector<double> all_t;
std::vector< std::vector<double> > all_np;
- all_np.push_back( all_nearest_points(p, c[si], c.segT(from, si)) );
- std::vector<unsigned int> ni;
+ all_np.push_back( all_nearest_times(p, c[si], c.segT(from, si)) );
+ std::vector<unsigned> ni;
ni.push_back(si);
double dsq;
double mindistsq = distanceSq( p, c[si](all_np.front().front()) );
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
+ Rect bb;
+
+ for (unsigned i = si + 1; i < ei; ++i) {
bb = *bounds_fast(c[i]);
dsq = distanceSq(p, bb);
if ( mindistsq < dsq ) continue;
- all_t = all_nearest_points(p, c[i]);
+ all_t = all_nearest_times(p, c[i]);
dsq = distanceSq( p, c[i](all_t.front()) );
if ( mindistsq > dsq )
{
@@ -251,29 +293,22 @@ all_nearest_points( Point const& p,
}
bb = *bounds_fast(c[ei]);
dsq = distanceSq(p, bb);
- if ( mindistsq >= dsq )
- {
- all_t = all_nearest_points(p, c[ei], 0, c.segT(to, ei));
+ if (mindistsq >= dsq) {
+ all_t = all_nearest_times(p, c[ei], 0, c.segT(to, ei));
dsq = distanceSq( p, c[ei](all_t.front()) );
- if ( mindistsq > dsq )
- {
- for ( unsigned int i = 0; i < all_t.size(); ++i )
- {
+ if (mindistsq > dsq) {
+ for (unsigned int i = 0; i < all_t.size(); ++i) {
all_t[i] = c.mapToDomain(all_t[i], ei);
}
return all_t;
- }
- else if ( mindistsq == dsq )
- {
+ } else if (mindistsq == dsq) {
all_np.push_back(all_t);
ni.push_back(ei);
}
}
std::vector<double> all_nearest;
- for ( unsigned int i = 0; i < all_np.size(); ++i )
- {
- for ( unsigned int j = 0; j < all_np[i].size(); ++j )
- {
+ for (unsigned i = 0; i < all_np.size(); ++i) {
+ for (unsigned int j = 0; j < all_np[i].size(); ++j) {
all_nearest.push_back( c.mapToDomain(all_np[i][j], ni[i]) );
}
}
diff --git a/src/2geom/nearest-point.h b/src/2geom/nearest-time.h
index 19485242c..007cd27ba 100644
--- a/src/2geom/nearest-point.h
+++ b/src/2geom/nearest-time.h
@@ -1,10 +1,8 @@
-/**
- * \file
- * \brief nearest point routines for D2<SBasis> and Piecewise<D2<SBasis>>
- *
+/** @file
+ * @brief Nearest time routines for D2<SBasis> and Piecewise<D2<SBasis>>
+ *//*
* Authors:
- *
- * Marco Cecchetti <mrcekets at gmail.com>
+ * Marco Cecchetti <mrcekets at gmail.com>
*
* Copyright 2007-2008 authors
*
@@ -33,8 +31,8 @@
*/
-#ifndef _NEAREST_POINT_H_
-#define _NEAREST_POINT_H_
+#ifndef LIB2GEOM_SEEN_NEAREST_TIME_H
+#define LIB2GEOM_SEEN_NEAREST_TIME_H
#include <vector>
@@ -42,7 +40,7 @@
#include <2geom/d2.h>
#include <2geom/piecewise.h>
#include <2geom/exception.h>
-
+#include <2geom/bezier.h>
namespace Geom
@@ -53,81 +51,91 @@ namespace Geom
* return the point on L nearest to p. Note that the returned value
* is with respect to the _normalized_ direction of v!
*/
-inline double nearest_point(Point const &p, Point const &A, Point const &v)
+inline double nearest_time(Point const &p, Point const &A, Point const &v)
{
Point d(p - A);
return d[0] * v[0] + d[1] * v[1];
}
+Coord nearest_time(Point const &p, D2<Bezier> const &bez, Coord from = 0, Coord to = 1);
+
////////////////////////////////////////////////////////////////////////////////
// D2<SBasis> versions
/*
* Return the parameter t of a nearest point on the portion of the curve "c",
* related to the interval [from, to], to the point "p".
- * The needed curve derivative "dc" is passed as parameter.
+ * The needed curve derivative "deriv" is passed as parameter.
* The function return the first nearest point to "p" that is found.
*/
-double nearest_point( Point const& p,
- D2<SBasis> const& c, D2<SBasis> const& dc,
- double from = 0, double to = 1 );
+double nearest_time(Point const &p,
+ D2<SBasis> const &c, D2<SBasis> const &deriv,
+ double from = 0, double to = 1);
inline
-double nearest_point( Point const& p,
- D2<SBasis> const& c,
- double from = 0, double to = 1 )
+double nearest_time(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1 )
{
- return nearest_point(p, c, Geom::derivative(c), from, to);
+ return nearest_time(p, c, Geom::derivative(c), from, to);
}
/*
- * Return the parameters t of all the nearest points on the portion of
+ * Return the parameters t of all the nearest times on the portion of
* the curve "c", related to the interval [from, to], to the point "p".
* The needed curve derivative "dc" is passed as parameter.
*/
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c, D2<SBasis> const& dc,
- double from = 0, double to = 1 );
+all_nearest_times(Point const& p,
+ D2<SBasis> const& c, D2<SBasis> const& dc,
+ double from = 0, double to = 1 );
inline
std::vector<double>
-all_nearest_points( Point const& p,
- D2<SBasis> const& c,
- double from = 0, double to = 1 )
+all_nearest_times(Point const &p,
+ D2<SBasis> const &c,
+ double from = 0, double to = 1)
{
- return all_nearest_points(p, c, Geom::derivative(c), from, to);
+ return all_nearest_times(p, c, Geom::derivative(c), from, to);
}
////////////////////////////////////////////////////////////////////////////////
// Piecewise< D2<SBasis> > versions
-double nearest_point( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to );
+double nearest_time(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
inline
-double nearest_point( Point const& p, Piecewise< D2<SBasis> > const& c )
+double nearest_time(Point const& p, Piecewise< D2<SBasis> > const &c)
{
- return nearest_point(p, c, c.cuts[0], c.cuts[c.size()]);
+ return nearest_time(p, c, c.cuts[0], c.cuts[c.size()]);
}
std::vector<double>
-all_nearest_points( Point const& p,
- Piecewise< D2<SBasis> > const& c,
- double from, double to );
+all_nearest_times(Point const &p,
+ Piecewise< D2<SBasis> > const &c,
+ double from, double to);
inline
std::vector<double>
-all_nearest_points( Point const& p, Piecewise< D2<SBasis> > const& c )
+all_nearest_times( Point const& p, Piecewise< D2<SBasis> > const& c )
{
- return all_nearest_points(p, c, c.cuts[0], c.cuts[c.size()]);
+ return all_nearest_times(p, c, c.cuts[0], c.cuts[c.size()]);
}
} // end namespace Geom
-
-
-#endif /*_NEAREST_POINT_H_*/
+#endif // LIB2GEOM_SEEN_NEAREST_TIME_H
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/numeric/fitting-model.h b/src/2geom/numeric/fitting-model.h
index a44c1ddac..fb96d1d2a 100644
--- a/src/2geom/numeric/fitting-model.h
+++ b/src/2geom/numeric/fitting-model.h
@@ -39,7 +39,7 @@
#include <2geom/sbasis.h>
#include <2geom/bezier.h>
#include <2geom/bezier-curve.h>
-#include <2geom/poly.h>
+#include <2geom/polynomial.h>
#include <2geom/ellipse.h>
#include <2geom/circle.h>
#include <2geom/utils.h>
@@ -298,7 +298,7 @@ class LFMEllipse
public:
void instance(Ellipse & e, ConstVectorView const& coeff) const
{
- e.set(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
+ e.setCoefficients(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]);
}
};
@@ -333,7 +333,7 @@ class LFMCircle
public:
void instance(Circle & c, ConstVectorView const& coeff) const
{
- c.set(1, coeff[0], coeff[1], coeff[2]);
+ c.setCoefficients(1, coeff[0], coeff[1], coeff[2]);
}
};
diff --git a/src/2geom/numeric/matrix.h b/src/2geom/numeric/matrix.h
index a130bd748..ddca35cd6 100644
--- a/src/2geom/numeric/matrix.h
+++ b/src/2geom/numeric/matrix.h
@@ -432,16 +432,17 @@ class Matrix: public detail::MatrixImpl
inline
void swap(Matrix & m1, Matrix & m2)
{
- assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() );
- std::swap(m1.m_matrix, m2.m_matrix);
+ assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+ using std::swap;
+ swap(m1.m_matrix, m2.m_matrix);
}
-inline
-void swap_any(Matrix & m1, Matrix & m2)
+inline void swap_any(Matrix &m1, Matrix &m2)
{
- std::swap(m1.m_matrix, m2.m_matrix);
- std::swap(m1.m_rows, m2.m_rows);
- std::swap(m1.m_columns, m2.m_columns);
+ using std::swap;
+ swap(m1.m_matrix, m2.m_matrix);
+ swap(m1.m_rows, m2.m_rows);
+ swap(m1.m_columns, m2.m_columns);
}
@@ -569,8 +570,9 @@ class MatrixView : public detail::MatrixImpl
inline
void swap_view(MatrixView & m1, MatrixView & m2)
{
- assert( m1.rows() == m2.rows() && m1.columns() == m2.columns() );
- std::swap(m1.m_matrix_view, m2.m_matrix_view);
+ assert(m1.rows() == m2.rows() && m1.columns() == m2.columns());
+ using std::swap;
+ swap(m1.m_matrix_view, m2.m_matrix_view);
}
Vector operator*( detail::BaseMatrixImpl const& A,
diff --git a/src/2geom/numeric/symmetric-matrix-fs-operation.h b/src/2geom/numeric/symmetric-matrix-fs-operation.h
index 37ece56ae..c5aaa724b 100644
--- a/src/2geom/numeric/symmetric-matrix-fs-operation.h
+++ b/src/2geom/numeric/symmetric-matrix-fs-operation.h
@@ -43,12 +43,7 @@
namespace Geom { namespace NL {
template <size_t N>
-inline
-SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & /*S*/)
-{
- THROW_NOTIMPLEMENTED();
- return SymmetricMatrix<N>();
-}
+SymmetricMatrix<N> adj(const ConstBaseSymmetricMatrix<N> & S);
template <>
inline
diff --git a/src/2geom/numeric/symmetric-matrix-fs-trace.h b/src/2geom/numeric/symmetric-matrix-fs-trace.h
index dbabecf6e..eff3dd24d 100644
--- a/src/2geom/numeric/symmetric-matrix-fs-trace.h
+++ b/src/2geom/numeric/symmetric-matrix-fs-trace.h
@@ -73,13 +73,7 @@ bool abs_less (double x, double y)
template <size_t K, size_t N>
struct trace
{
- static
- double evaluate (const ConstBaseSymmetricMatrix<N> & /*S*/)
- {
- THROW_NOTIMPLEMENTED();
- return K;
- }
-
+ static double evaluate(const ConstBaseSymmetricMatrix<N> &S);
};
template <size_t N>
diff --git a/src/2geom/numeric/symmetric-matrix-fs.h b/src/2geom/numeric/symmetric-matrix-fs.h
index c1de27afd..2fadd6915 100644
--- a/src/2geom/numeric/symmetric-matrix-fs.h
+++ b/src/2geom/numeric/symmetric-matrix-fs.h
@@ -339,7 +339,10 @@ class BaseSymmetricMatrix : public ConstBaseSymmetricMatrix<N>
{
}
- using base_type::operator();
+ double operator() (size_t i, size_t j) const
+ {
+ return m_data[base_type::get_index(i,j)];
+ }
double& operator() (size_t i, size_t j)
{
diff --git a/src/2geom/numeric/vector.h b/src/2geom/numeric/vector.h
index 6ab898f29..f28289f0f 100644
--- a/src/2geom/numeric/vector.h
+++ b/src/2geom/numeric/vector.h
@@ -348,15 +348,17 @@ class Vector : public detail::VectorImpl
inline
void swap(Vector & v1, Vector & v2)
{
- assert( v1.size() == v2.size() );
- std::swap(v1.m_vector, v2.m_vector);
+ assert(v1.size() == v2.size());
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
}
inline
void swap_any(Vector & v1, Vector & v2)
{
- std::swap(v1.m_vector, v2.m_vector);
- std::swap(v1.m_size, v2.m_size);
+ using std::swap;
+ swap(v1.m_vector, v2.m_vector);
+ swap(v1.m_size, v2.m_size);
}
@@ -552,7 +554,8 @@ inline
void swap_view(VectorView & v1, VectorView & v2)
{
assert( v1.size() == v2.size() );
- std::swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
+ using std::swap;
+ swap(v1.m_vector_view, v2.m_vector_view); // not swap m_vector too
}
inline
diff --git a/src/2geom/ord.h b/src/2geom/ord.h
index 0add83da4..e190a4a1e 100644
--- a/src/2geom/ord.h
+++ b/src/2geom/ord.h
@@ -1,6 +1,5 @@
-/**
- * \file
- * \brief Comparator template
+/** @file
+ * @brief Comparator template
*//*
* Authors:
* ? <?@?.?>
@@ -32,8 +31,8 @@
*
*/
-#ifndef __2GEOM_ORD__
-#define __2GEOM_ORD__
+#ifndef LIB2GEOM_SEEN_ORD_H
+#define LIB2GEOM_SEEN_ORD_H
namespace {
diff --git a/src/2geom/path-intersection.cpp b/src/2geom/path-intersection.cpp
index 63a29423d..07e38ba9e 100644
--- a/src/2geom/path-intersection.cpp
+++ b/src/2geom/path-intersection.cpp
@@ -12,98 +12,9 @@
namespace Geom {
-/**
- * This function computes the winding of the path, given a reference point.
- * Positive values correspond to counter-clockwise in the mathematical coordinate system,
- * and clockwise in screen coordinates. This particular implementation casts a ray in
- * the positive x direction. It iterates the path, checking for intersection with the
- * bounding boxes. If an intersection is found, the initial/final Y value of the curve is
- * used to derive a delta on the winding value. If the point is within the bounding box,
- * the curve specific winding function is called.
- */
-int winding(Path const &path, Point p) {
- //start on a segment which is not a horizontal line with y = p[y]
- Path::const_iterator start;
- for(Path::const_iterator iter = path.begin(); ; ++iter) {
- if(iter == path.end_closed()) { return 0; }
- if(iter->initialPoint()[Y]!=p[Y]) { start = iter; break; }
- if(iter->finalPoint()[Y]!=p[Y]) { start = iter; break; }
- if(iter->boundsFast().height()!=0.){ start = iter; break; }
- }
- int wind = 0;
- unsigned cnt = 0;
- bool starting = true;
- for (Path::const_iterator iter = start; iter != start || starting
- ; ++iter, iter = (iter == path.end_closed()) ? path.begin() : iter )
- {
- cnt++;
- if(cnt > path.size()) return wind; //some bug makes this required
- starting = false;
- Rect bounds = (iter->boundsFast());
- Coord x = p[X], y = p[Y];
-
- if(x > bounds.right() || !bounds[Y].contains(y)) continue; //ray doesn't intersect box
-
- Point final = iter->finalPoint();
- Point initial = iter->initialPoint();
- Cmp final_to_ray = cmp(final[Y], y);
- Cmp initial_to_ray = cmp(initial[Y], y);
-
- // if y is included, these will have opposite values, giving order.
- Cmp c = cmp(final_to_ray, initial_to_ray);
- if(x < bounds.left()) {
- // ray goes through bbox
- // winding delta determined by position of endpoints
- if(final_to_ray != EQUAL_TO) {
- wind += int(c); // GT = counter-clockwise = 1; LT = clockwise = -1; EQ = not-included = 0
- //std::cout << int(c) << " ";
- goto cont;
- }
- } else {
- //inside bbox, use custom per-curve winding thingie
- int delt = iter->winding(p);
- wind += delt;
- //std::cout << "n" << delt << " ";
- }
- //Handling the special case of an endpoint on the ray:
- if(final[Y] == y) {
- //Traverse segments until it breaks away from y
- //99.9% of the time this will happen the first go
- Path::const_iterator next = iter;
- ++next;
- for(; ; ++next) {
- if(next == path.end_closed()) next = path.begin();
- Rect bnds = (next->boundsFast());
- //TODO: X considerations
- if(bnds.height() > 0) {
- //It has diverged
- if(bnds.contains(p)) {
- const double fudge = 0.01;
- if(cmp(y, next->valueAt(fudge, Y)) == initial_to_ray) {
- wind += int(c);
- //std::cout << "!!!!!" << int(c) << " ";
- }
- iter = next; // No increment, as the rest of the thing hasn't been counted.
- } else {
- Coord ny = next->initialPoint()[Y];
- if(cmp(y, ny) == initial_to_ray) {
- //Is a continuation through the ray, so counts windingwise
- wind += int(c);
- //std::cout << "!!!!!" << int(c) << " ";
- }
- iter = ++next;
- }
- goto cont;
- }
- if(next==start) return wind;
- }
- //Looks like it looped, which means everything's flat
- return 0;
- }
-
- cont:(void)0;
- }
- return wind;
+/// Compute winding number of the path at the specified point
+int winding(Path const &path, Point const &p) {
+ return path.winding(p);
}
/**
@@ -162,7 +73,7 @@ void append(T &a, T const &b) {
* indicates if the time values are within their proper range on the line segments.
*/
bool
-linear_intersect(Point A0, Point A1, Point B0, Point B1,
+linear_intersect(Point const &A0, Point const &A1, Point const &B0, Point const &B1,
double &tA, double &tB, double &det) {
bool both_lines_non_zero = (!are_near(A0, A1)) && (!are_near(B0, B1));
@@ -521,7 +432,7 @@ std::vector<double> path_mono_splits(Path const &p) {
* Applies path_mono_splits to multiple paths, and returns the results such that
* time-set i corresponds to Path i.
*/
-std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps) {
+std::vector<std::vector<double> > paths_mono_splits(PathVector const &ps) {
std::vector<std::vector<double> > ret;
for(unsigned i = 0; i < ps.size(); i++)
ret.push_back(path_mono_splits(ps[i]));
@@ -533,7 +444,7 @@ std::vector<std::vector<double> > paths_mono_splits(std::vector<Path> const &ps)
* Each entry i corresponds to path i of the input. The number of rects in each entry is guaranteed to be the
* number of splits for that path, subtracted by one.
*/
-std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::vector<std::vector<double> > splits) {
+std::vector<std::vector<Rect> > split_bounds(PathVector const &p, std::vector<std::vector<double> > splits) {
std::vector<std::vector<Rect> > ret;
for(unsigned i = 0; i < p.size(); i++) {
std::vector<Rect> res;
@@ -553,7 +464,7 @@ std::vector<std::vector<Rect> > split_bounds(std::vector<Path> const &p, std::ve
* This function does two sweeps, one on the bounds of each path, and after that cull, one on the curves within.
* This leads to a certain amount of code complexity, however, most of that is factored into the above functions
*/
-CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path> const &b) {
+CrossingSet MonoCrosser::crossings(PathVector const &a, PathVector const &b) {
if(b.empty()) return CrossingSet(a.size(), Crossings());
CrossingSet results(a.size() + b.size(), Crossings());
if(a.empty()) return results;
@@ -596,7 +507,7 @@ CrossingSet MonoCrosser::crossings(std::vector<Path> const &a, std::vector<Path>
/* This function is similar codewise to the MonoCrosser, the main difference is that it deals with
* only one set of paths and includes self intersection
-CrossingSet crossings_among(std::vector<Path> const &p) {
+CrossingSet crossings_among(PathVector const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
@@ -780,7 +691,7 @@ void flip_crossings(Crossings &crs) {
crs[i] = Crossing(crs[i].tb, crs[i].ta, crs[i].b, crs[i].a, !crs[i].dir);
}
-CrossingSet crossings_among(std::vector<Path> const &p) {
+CrossingSet crossings_among(PathVector const &p) {
CrossingSet results(p.size(), Crossings());
if(p.empty()) return results;
diff --git a/src/2geom/path-intersection.h b/src/2geom/path-intersection.h
index 512c31167..f06eeaf94 100644
--- a/src/2geom/path-intersection.h
+++ b/src/2geom/path-intersection.h
@@ -32,21 +32,19 @@
*
*/
-#ifndef __GEOM_PATH_INTERSECTION_H
-#define __GEOM_PATH_INTERSECTION_H
-
-#include <2geom/path.h>
+#ifndef LIB2GEOM_SEEN_PATH_INTERSECTION_H
+#define LIB2GEOM_SEEN_PATH_INTERSECTION_H
#include <2geom/crossing.h>
-
-#include <2geom/sweep.h>
+#include <2geom/path.h>
+#include <2geom/sweep-bounds.h>
namespace Geom {
-int winding(Path const &path, Point p);
+int winding(Path const &path, Point const &p);
bool path_direction(Path const &p);
-inline bool contains(Path const & p, Point i, bool evenodd = true) {
+inline bool contains(Path const & p, Point const &i, bool evenodd = true) {
return (evenodd ? winding(p, i) % 2 : winding(p, i)) != 0;
}
@@ -74,19 +72,19 @@ Crossings mono_intersect(Curve const & A, Interval const &Ad,
struct SimpleCrosser : public Crosser<Path> {
Crossings crossings(Curve const &a, Curve const &b);
Crossings crossings(Path const &a, Path const &b) { return curve_sweep<SimpleCrosser>(a, b); }
- CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b) { return Crosser<Path>::crossings(a, b); }
+ CrossingSet crossings(PathVector const &a, PathVector const &b) { return Crosser<Path>::crossings(a, b); }
};
struct MonoCrosser : public Crosser<Path> {
- Crossings crossings(Path const &a, Path const &b) { return crossings(std::vector<Path>(1,a), std::vector<Path>(1,b))[0]; }
- CrossingSet crossings(std::vector<Path> const &a, std::vector<Path> const &b);
+ Crossings crossings(Path const &a, Path const &b) { return crossings(PathVector(a), PathVector(b))[0]; }
+ CrossingSet crossings(PathVector const &a, PathVector const &b);
};
typedef SimpleCrosser DefaultCrosser;
std::vector<double> path_mono_splits(Path const &p);
-CrossingSet crossings_among(std::vector<Path> const & p);
+CrossingSet crossings_among(PathVector const & p);
Crossings self_crossings(Path const & a);
inline Crossings crossings(Curve const & a, Curve const & b) {
@@ -99,7 +97,7 @@ inline Crossings crossings(Path const & a, Path const & b) {
return c.crossings(a, b);
}
-inline CrossingSet crossings(std::vector<Path> const & a, std::vector<Path> const & b) {
+inline CrossingSet crossings(PathVector const & a, PathVector const & b) {
DefaultCrosser c = DefaultCrosser();
return c.crossings(a, b);
}
diff --git a/src/2geom/path-sink.cpp b/src/2geom/path-sink.cpp
index 6acd9508c..3b8d407f8 100644
--- a/src/2geom/path-sink.cpp
+++ b/src/2geom/path-sink.cpp
@@ -31,80 +31,65 @@
#include <2geom/sbasis-to-bezier.h>
#include <2geom/path-sink.h>
#include <2geom/exception.h>
+#include <2geom/circle.h>
+#include <2geom/ellipse.h>
namespace Geom {
-void output(Curve const &curve, PathSink &sink) {
- std::vector<Point> pts;
- sbasis_to_bezier(pts, curve.toSBasis(), 2); //TODO: use something better!
- sink.curveTo(pts[0], pts[1], pts[2]);
+void PathSink::feed(Curve const &c, bool moveto_initial)
+{
+ c.feed(*this, moveto_initial);
}
-void output(HLineSegment const &curve, PathSink &sink) {
- sink.hlineTo(curve.finalPoint()[X]);
-}
-
-void output(VLineSegment const &curve, PathSink &sink) {
- sink.vlineTo(curve.finalPoint()[Y]);
-}
-
-void output(LineSegment const &curve, PathSink &sink) {
- sink.lineTo(curve[1]);
-}
-
-void output(CubicBezier const &curve, PathSink &sink) {
- sink.curveTo(curve[1], curve[2], curve[3]);
-}
-
-void output(QuadraticBezier const &curve, PathSink &sink) {
- sink.quadTo(curve[1], curve[2]);
-}
-
-void output(SVGEllipticalArc const &curve, PathSink &sink) {
- sink.arcTo( curve.ray(X), curve.ray(Y), curve.rotationAngle(),
- curve.largeArc(), curve.sweep(),
- curve.finalPoint() );
-}
-
-template <typename T>
-bool output_as(Curve const &curve, PathSink &sink) {
- T const *t = dynamic_cast<T const *>(&curve);
- if (t) {
- output(*t, sink);
- return true;
- } else {
- return false;
- }
-}
-
-void PathSink::path(Path const &path) {
+void PathSink::feed(Path const &path) {
flush();
moveTo(path.front().initialPoint());
- Path::const_iterator iter;
- for (iter = path.begin(); iter != path.end(); ++iter) {
- output_as<HLineSegment>(*iter, *this) ||
- output_as<VLineSegment>(*iter, *this) ||
- output_as<LineSegment>(*iter, *this) ||
- output_as<CubicBezier>(*iter, *this) ||
- output_as<QuadraticBezier>(*iter, *this) ||
- output_as<SVGEllipticalArc>(*iter, *this) ||
- output_as<Curve>(*iter, *this);
+ // never output the closing segment to the sink
+ Path::const_iterator iter = path.begin(), last = path.end_open();
+ for (; iter != last; ++iter) {
+ iter->feed(*this, false);
}
-
if (path.closed()) {
closePath();
}
flush();
}
-void PathSink::pathvector(PathVector const &pv) {
- flush();
+void PathSink::feed(PathVector const &pv) {
for (PathVector::const_iterator i = pv.begin(); i != pv.end(); ++i) {
- path(*i);
+ feed(*i);
}
}
+void PathSink::feed(Rect const &r) {
+ moveTo(r.corner(0));
+ lineTo(r.corner(1));
+ lineTo(r.corner(2));
+ lineTo(r.corner(3));
+ closePath();
+}
+
+void PathSink::feed(Circle const &e) {
+ Coord r = e.radius();
+ Point c = e.center();
+ Point a = c + Point(0, c[Y] + r);
+ Point b = c + Point(0, c[Y] - r);
+
+ moveTo(a);
+ arcTo(r, r, 0, false, false, b);
+ arcTo(r, r, 0, false, false, a);
+ closePath();
+}
+
+void PathSink::feed(Ellipse const &e) {
+ Point s = e.pointAt(0);
+ moveTo(s);
+ arcTo(e.ray(X), e.ray(Y), e.rotationAngle(), false, false, e.pointAt(M_PI));
+ arcTo(e.ray(X), e.ray(Y), e.rotationAngle(), false, false, s);
+ closePath();
+}
+
}
/*
diff --git a/src/2geom/path-sink.h b/src/2geom/path-sink.h
index 949369b80..17ede18a4 100644
--- a/src/2geom/path-sink.h
+++ b/src/2geom/path-sink.h
@@ -29,9 +29,10 @@
*
*/
-#ifndef SEEN_SVG_PATH_H
-#define SEEN_SVG_PATH_H
+#ifndef LIB2GEOM_SEEN_PATH_SINK_H
+#define LIB2GEOM_SEEN_PATH_SINK_H
+#include <2geom/forward.h>
#include <2geom/pathvector.h>
#include <2geom/curves.h>
#include <iterator>
@@ -44,58 +45,73 @@ namespace Geom {
* PathSink provides an interface that allows one to easily write
* code which processes path data, for instance when converting
* between path formats used by different graphics libraries.
+ * It is also useful for writing algorithms which must do something
+ * for each curve in the path.
*
* To store a path in a new format, implement the virtual methods
- * for segments in a derived class and call path() or pathvector().
+ * for segments in a derived class and call feed().
+ *
+ * @ingroup Paths
*/
class PathSink {
public:
- /** Move to a different point without creating a segment.
+ /** @brief Move to a different point without creating a segment.
* Usually starts a new subpath. */
virtual void moveTo(Point const &p) = 0;
- /// Output a horizontal line segment. Only the X coordinate of the final point is given.
- virtual void hlineTo(Coord v) = 0;
- /// Output a vertical line segment. Only the Y coordinate of the final point is given.
- virtual void vlineTo(Coord v) = 0;
/// Output a line segment.
virtual void lineTo(Point const &p) = 0;
/// Output a quadratic Bezier segment.
virtual void curveTo(Point const &c0, Point const &c1, Point const &p) = 0;
/// Output a cubic Bezier segment.
virtual void quadTo(Point const &c, Point const &p) = 0;
- /** Output an elliptical arc segment.
+ /** @brief Output an elliptical arc segment.
* See the EllipticalArc class for the documentation of parameters. */
- virtual void arcTo(double rx, double ry, double angle,
+ virtual void arcTo(Coord rx, Coord ry, Coord angle,
bool large_arc, bool sweep, Point const &p) = 0;
/// Close the current path with a line segment.
virtual void closePath() = 0;
- /** Flush any internal state of the generator.
- *
+ /** @brief Flush any internal state of the generator.
* This call should implicitly finish the current subpath.
* Calling this method should be idempotent, because the default
- * implementations of path() and pathvector() will be call it
+ * implementations of path() and pathvector() will call it
* multiple times in a row. */
virtual void flush() = 0;
+ // Get the current point, e.g. where the initial point of the next segment will be.
+ //virtual Point currentPoint() const = 0;
- /** Undo the last segment.
+ /** @brief Undo the last segment.
* This method is optional.
* @return true true if a segment was erased, false otherwise. */
virtual bool backspace() { return false; }
// these have a default implementation
- /** Output a subpath.
- * Calls the appropriate segment methods according to the contents
- * of the passed subpath. You can override this function. */
- virtual void path(Path const &p);
- /** Output a path.
+ virtual void feed(Curve const &c, bool moveto_initial = true);
+ /** @brief Output a subpath.
* Calls the appropriate segment methods according to the contents
- * of the passed path. You can override this function. */
- virtual void pathvector(PathVector const &v);
+ * of the passed subpath. You can override this function.
+ * NOTE: if you override only some of the feed() functions,
+ * always write this in the derived class:
+ * @code
+ using PathSink::feed;
+ @endcode
+ * Otherwise the remaining methods will be hidden. */
+ virtual void feed(Path const &p);
+ /** @brief Output a path.
+ * Calls feed() on each path in the vector. You can override this function. */
+ virtual void feed(PathVector const &v);
+ /// Output an axis-aligned rectangle, using moveTo, lineTo and closePath.
+ virtual void feed(Rect const &);
+ /// Output a circle as two elliptical arcs.
+ virtual void feed(Circle const &e);
+ /// Output an ellipse as two elliptical arcs.
+ virtual void feed(Ellipse const &e);
virtual ~PathSink() {}
};
+/** @brief Store paths to an output iterator
+ * @ingroup Paths */
template <typename OutputIterator>
class PathIteratorSink : public PathSink {
public:
@@ -110,22 +126,6 @@ public:
}
//TODO: what if _in_path = false?
- void hlineTo(Coord v) {
- // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
- if (!_in_path) {
- moveTo(_start_p);
- }
- _path.template appendNew<HLineSegment>(Point(v, _path.finalPoint()[Y]));
- }
-
- void vlineTo(Coord v) {
- // check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
- if (!_in_path) {
- moveTo(_start_p);
- }
- _path.template appendNew<VLineSegment>(Point(_path.finalPoint()[X], v));
- }
-
void lineTo(Point const &p) {
// check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
if (!_in_path) {
@@ -150,15 +150,15 @@ public:
_path.template appendNew<CubicBezier>(c0, c1, p);
}
- void arcTo(double rx, double ry, double angle,
+ void arcTo(Coord rx, Coord ry, Coord angle,
bool large_arc, bool sweep, Point const &p)
{
// check for implicit moveto, like in: "M 1,1 L 2,2 z l 2,2 z"
if (!_in_path) {
moveTo(_start_p);
}
- _path.template appendNew<SVGEllipticalArc>(rx, ry, angle,
- large_arc, sweep, p);
+ _path.template appendNew<EllipticalArc>(rx, ry, angle,
+ large_arc, sweep, p);
}
bool backspace()
@@ -170,12 +170,12 @@ public:
return false;
}
- void append(Path const &other, Path::Stitching stitching = Path::NO_STITCHING)
+ void append(Path const &other)
{
if (!_in_path) {
moveTo(other.initialPoint());
}
- _path.append(other, stitching);
+ _path.append(other);
}
void closePath() {
@@ -188,11 +188,15 @@ public:
_in_path = false;
*_out++ = _path;
_path.clear();
- _path.close(false);
}
}
+
+ void setStitching(bool s) {
+ _path.setStitching(s);
+ }
- void path(Path const &other)
+ using PathSink::feed;
+ void feed(Path const &other)
{
flush();
*_out++ = other;
@@ -205,14 +209,23 @@ protected:
Point _start_p;
};
-typedef std::back_insert_iterator<std::vector<Path> > iter;
+typedef std::back_insert_iterator<PathVector> SubpathInserter;
-class PathBuilder : public PathIteratorSink<iter> {
+/** @brief Store paths to a PathVector
+ * @ingroup Paths */
+class PathBuilder : public PathIteratorSink<SubpathInserter> {
private:
- std::vector<Path> _pathset;
+ PathVector _pathset;
public:
- PathBuilder() : PathIteratorSink<iter>(iter(_pathset)) {}
- std::vector<Path> const &peek() const {return _pathset;}
+ /// Create a builder that outputs to an internal pathvector.
+ PathBuilder() : PathIteratorSink<SubpathInserter>(SubpathInserter(_pathset)) {}
+ /// Create a builder that outputs to pathvector given by reference.
+ PathBuilder(PathVector &pv) : PathIteratorSink<SubpathInserter>(SubpathInserter(pv)) {}
+
+ /// Retrieve the path
+ PathVector const &peek() const {return _pathset;}
+ /// Clear the stored path vector
+ void clear() { _pathset.clear(); }
};
}
diff --git a/src/2geom/path.cpp b/src/2geom/path.cpp
index 3558af3b3..836e65013 100644
--- a/src/2geom/path.cpp
+++ b/src/2geom/path.cpp
@@ -1,11 +1,12 @@
-/*
- * Path - Series of continuous curves
- *
+/** @file
+ * @brief Path - a sequence of contiguous curves (implementation file)
+ *//*
* Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2007-2008 authors
+ * Copyright 2007-2014 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -31,414 +32,1023 @@
* the specific language governing rights and limitations.
*/
-
-
-
#include <2geom/path.h>
+#include <2geom/pathvector.h>
#include <2geom/transforms.h>
+#include <2geom/circle.h>
+#include <2geom/ellipse.h>
+#include <2geom/convex-hull.h>
+#include <2geom/svg-path-writer.h>
+#include <2geom/sweeper.h>
#include <algorithm>
+#include <limits>
using std::swap;
using namespace Geom::PathInternal;
-namespace Geom
+namespace Geom {
+
+// this represents an empty interval
+PathInterval::PathInterval()
+ : _from(0, 0.0)
+ , _to(0, 0.0)
+ , _path_size(1)
+ , _cross_start(false)
+ , _reverse(false)
+{}
+
+PathInterval::PathInterval(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size)
+ : _from(from)
+ , _to(to)
+ , _path_size(path_size)
+ , _cross_start(cross_start)
+ , _reverse(cross_start ? to >= from : to < from)
+{
+ if (_reverse) {
+ _to.normalizeForward(_path_size);
+ if (_from != _to) {
+ _from.normalizeBackward(_path_size);
+ }
+ } else {
+ _from.normalizeForward(_path_size);
+ if (_from != _to) {
+ _to.normalizeBackward(_path_size);
+ }
+ }
+
+ if (_from == _to) {
+ _reverse = false;
+ _cross_start = false;
+ }
+}
+
+bool PathInterval::contains(PathTime const &pos) const {
+ if (_cross_start) {
+ if (_reverse) {
+ return pos >= _to || _from >= pos;
+ } else {
+ return pos >= _from || _to >= pos;
+ }
+ } else {
+ if (_reverse) {
+ return _to <= pos && pos <= _from;
+ } else {
+ return _from <= pos && pos <= _to;
+ }
+ }
+}
+
+PathTime PathInterval::inside(Coord min_dist) const
+{
+ // If there is some node further than min_dist (in time coord) from the ends,
+ // return that node. Otherwise, return the middle.
+ PathTime result(0, 0.0);
+
+ if (!_cross_start && _from.curve_index == _to.curve_index) {
+ PathTime result(_from.curve_index, lerp(0.5, _from.t, _to.t));
+ return result;
+ }
+ // If _cross_start, then we can be sure that at least one node is in the domain.
+ // If dcurve == 0, it actually means that all curves are included in the domain
+
+ if (_reverse) {
+ size_type dcurve = (_path_size + _from.curve_index - _to.curve_index) % _path_size;
+ bool from_close = _from.t < min_dist;
+ bool to_close = _to.t > 1 - min_dist;
+
+ if (dcurve == 0) {
+ dcurve = _path_size;
+ }
+
+ if (dcurve == 1) {
+ if (from_close || to_close) {
+ result.curve_index = _from.curve_index;
+ Coord tmid = _from.t - ((1 - _to.t) + _from.t) * 0.5;
+ if (tmid < 0) {
+ result.curve_index = (_path_size + result.curve_index - 1) % _path_size;
+ tmid += 1;
+ }
+ result.t = tmid;
+ return result;
+ }
+
+ result.curve_index = _from.curve_index;
+ return result;
+ }
+
+ result.curve_index = (_to.curve_index + 1) % _path_size;
+ if (to_close) {
+ if (dcurve == 2) {
+ result.t = 0.5;
+ } else {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ }
+ }
+ return result;
+ } else {
+ size_type dcurve = (_path_size + _to.curve_index - _from.curve_index) % _path_size;
+ bool from_close = _from.t > 1 - min_dist;
+ bool to_close = _to.t < min_dist;
+
+ if (dcurve == 0) {
+ dcurve = _path_size;
+ }
+
+ if (dcurve == 1) {
+ if (from_close || to_close) {
+ result.curve_index = _from.curve_index;
+ Coord tmid = ((1 - _from.t) + _to.t) * 0.5 + _from.t;
+ if (tmid >= 1) {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ tmid -= 1;
+ }
+ result.t = tmid;
+ return result;
+ }
+
+ result.curve_index = _to.curve_index;
+ return result;
+ }
+
+ result.curve_index = (_from.curve_index + 1) % _path_size;
+ if (from_close) {
+ if (dcurve == 2) {
+ result.t = 0.5;
+ } else {
+ result.curve_index = (result.curve_index + 1) % _path_size;
+ }
+ }
+ return result;
+ }
+
+ result.curve_index = _reverse ? _from.curve_index : _to.curve_index;
+ return result;
+}
+
+PathInterval PathInterval::from_direction(PathTime const &from, PathTime const &to, bool reversed, size_type path_size)
{
+ PathInterval result;
+ result._from = from;
+ result._to = to;
+ result._path_size = path_size;
-OptRect Path::boundsFast() const {
- OptRect bounds;
- if (empty()) return bounds;
- bounds = front().boundsFast();
- const_iterator iter = begin();
- // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
- if ( iter != end() ) {
- for ( ++iter; iter != end() ; ++iter ) {
- bounds.unionWith(iter->boundsFast());
+ if (reversed) {
+ result._to.normalizeForward(path_size);
+ if (result._from != result._to) {
+ result._from.normalizeBackward(path_size);
+ }
+ } else {
+ result._from.normalizeForward(path_size);
+ if (result._from != result._to) {
+ result._to.normalizeBackward(path_size);
+ }
+ }
+
+ if (result._from == result._to) {
+ result._reverse = false;
+ result._cross_start = false;
+ } else {
+ result._reverse = reversed;
+ if (reversed) {
+ result._cross_start = from < to;
+ } else {
+ result._cross_start = to < from;
+ }
}
- }
- return bounds;
+ return result;
}
-OptRect Path::boundsExact() const {
- OptRect bounds;
- if (empty()) return bounds;
- bounds = front().boundsExact();
- const_iterator iter = begin();
- // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
- if ( iter != end() ) {
- for ( ++iter; iter != end() ; ++iter ) {
- bounds.unionWith(iter->boundsExact());
+
+Path::Path(Rect const &r)
+ : _curves(new Sequence())
+ , _closing_seg(new ClosingSegment(r.corner(3), r.corner(0)))
+ , _closed(true)
+ , _exception_on_stitch(true)
+{
+ for (unsigned i = 0; i < 3; ++i) {
+ _curves->push_back(new LineSegment(r.corner(i), r.corner(i+1)));
}
- }
- return bounds;
+ _curves->push_back(_closing_seg);
}
-template<typename iter>
+Path::Path(ConvexHull const &ch)
+ : _curves(new Sequence())
+ , _closing_seg(new ClosingSegment(Point(), Point()))
+ , _closed(true)
+ , _exception_on_stitch(true)
+{
+ if (ch.empty()) {
+ _curves->push_back(_closing_seg);
+ return;
+ }
+
+ _closing_seg->setInitial(ch.back());
+ _closing_seg->setFinal(ch.front());
+
+ Point last = ch.front();
+
+ for (std::size_t i = 1; i < ch.size(); ++i) {
+ _curves->push_back(new LineSegment(last, ch[i]));
+ last = ch[i];
+ }
+
+ _curves->push_back(_closing_seg);
+ _closed = true;
+}
+
+Path::Path(Circle const &c)
+ : _curves(new Sequence())
+ , _closing_seg(NULL)
+ , _closed(true)
+ , _exception_on_stitch(true)
+{
+ Point p1 = c.pointAt(0);
+ Point p2 = c.pointAt(M_PI);
+ _curves->push_back(new EllipticalArc(p1, c.radius(), c.radius(), 0, false, true, p2));
+ _curves->push_back(new EllipticalArc(p2, c.radius(), c.radius(), 0, false, true, p1));
+ _closing_seg = new ClosingSegment(p1, p1);
+ _curves->push_back(_closing_seg);
+}
+
+Path::Path(Ellipse const &e)
+ : _curves(new Sequence())
+ , _closing_seg(NULL)
+ , _closed(true)
+ , _exception_on_stitch(true)
+{
+ Point p1 = e.pointAt(0);
+ Point p2 = e.pointAt(M_PI);
+ _curves->push_back(new EllipticalArc(p1, e.rays(), e.rotationAngle(), false, true, p2));
+ _curves->push_back(new EllipticalArc(p2, e.rays(), e.rotationAngle(), false, true, p1));
+ _closing_seg = new ClosingSegment(p1, p1);
+ _curves->push_back(_closing_seg);
+}
+
+void Path::close(bool c)
+{
+ if (c == _closed) return;
+ if (c && _curves->size() >= 2) {
+ // when closing, if last segment is linear and ends at initial point,
+ // replace it with the closing segment
+ Sequence::iterator last = _curves->end() - 2;
+ if (last->isLineSegment() && last->finalPoint() == initialPoint()) {
+ _closing_seg->setInitial(last->initialPoint());
+ _curves->erase(last);
+ }
+ }
+ _closed = c;
+}
+
+void Path::clear()
+{
+ _unshare();
+ _curves->pop_back().release();
+ _curves->clear();
+ _closing_seg->setInitial(Point(0, 0));
+ _closing_seg->setFinal(Point(0, 0));
+ _curves->push_back(_closing_seg);
+ _closed = false;
+}
+
+OptRect Path::boundsFast() const
+{
+ OptRect bounds;
+ if (empty())
+ return bounds;
+ bounds = front().boundsFast();
+ const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
+ if (iter != end()) {
+ for (++iter; iter != end(); ++iter) {
+ bounds.unionWith(iter->boundsFast());
+ }
+ }
+ return bounds;
+}
+
+OptRect Path::boundsExact() const
+{
+ OptRect bounds;
+ if (empty())
+ return bounds;
+ bounds = front().boundsExact();
+ const_iterator iter = begin();
+ // the closing path segment can be ignored, because it will always lie within the bbox of the rest of the path
+ if (iter != end()) {
+ for (++iter; iter != end(); ++iter) {
+ bounds.unionWith(iter->boundsExact());
+ }
+ }
+ return bounds;
+}
+
+Piecewise<D2<SBasis> > Path::toPwSb() const
+{
+ Piecewise<D2<SBasis> > ret;
+ ret.push_cut(0);
+ unsigned i = 1;
+ bool degenerate = true;
+ // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
+ for (const_iterator it = begin(); it != end_default(); ++it) {
+ if (!it->isDegenerate()) {
+ ret.push(it->toSBasis(), i++);
+ degenerate = false;
+ }
+ }
+ if (degenerate) {
+ // if path only contains degenerate curves, no second cut is added
+ // so we need to create at least one segment manually
+ ret = Piecewise<D2<SBasis> >(initialPoint());
+ }
+ return ret;
+}
+
+template <typename iter>
iter inc(iter const &x, unsigned n) {
- iter ret = x;
- for(unsigned i = 0; i < n; i++)
- ret++;
- return ret;
-}
-
-Path &Path::operator*=(Affine const &m) {
- unshare();
- Sequence::iterator last = get_curves().end() - 1;
- Sequence::iterator it;
- Point prev;
- for (it = get_curves().begin() ; it != last ; ++it) {
- *it = boost::shared_ptr<Curve>((*it)->transformed(m));
- if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) {
- THROW_CONTINUITYERROR();
- }
- prev = (*it)->finalPoint();
- }
- for ( int i = 0 ; i < 2 ; ++i ) {
- final_->setPoint(i, (*final_)[i] * m);
- }
- if (get_curves().size() > 1) {
- if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) {
- THROW_CONTINUITYERROR();
- }
- }
- return *this;
-}
-
-Path &Path::operator*=(Translate const &m) {
-/* Somehow there is something wrong here, LPE Construct grid fails with this code
- unshare();
- Sequence::iterator last = get_curves().end() - 1;
- Sequence::iterator it;
- Point prev;
- for (it = get_curves().begin() ; it != last ; ++it) {
- // *(const_cast<Curve*>(&**it)) *= m;
- const_cast<Curve*>(it->get())->operator*=(m);
- if ( it != get_curves().begin() && (*it)->initialPoint() != prev ) {
- THROW_CONTINUITYERROR();
- }
- prev = (*it)->finalPoint();
- }
- for ( int i = 0 ; i < 2 ; ++i ) {
- final_->setPoint(i, (*final_)[i] + m.vector());
- }
- if (get_curves().size() > 1) {
- if ( front().initialPoint() != initialPoint() || back().finalPoint() != finalPoint() ) {
- THROW_CONTINUITYERROR();
- }
- }
- return *this;
-*/
- return this->operator*=(static_cast<Affine>(m));
-}
-
-std::vector<double>
-Path::allNearestPoints(Point const& _point, double from, double to) const
-{
- using std::swap;
-
- if ( from > to ) swap(from, to);
- const Path& _path = *this;
- unsigned int sz = _path.size();
- if ( _path.closed() ) ++sz;
- if ( from < 0 || to > sz )
- {
- THROW_RANGEERROR("[from,to] interval out of bounds");
- }
- double sif, st = modf(from, &sif);
- double eif, et = modf(to, &eif);
- unsigned int si = static_cast<unsigned int>(sif);
- unsigned int ei = static_cast<unsigned int>(eif);
- if ( si == sz )
- {
- --si;
- st = 1;
- }
- if ( ei == sz )
- {
- --ei;
- et = 1;
- }
- if ( si == ei )
- {
- std::vector<double> all_nearest =
- _path[si].allNearestPoints(_point, st, et);
- for ( unsigned int i = 0; i < all_nearest.size(); ++i )
- {
- all_nearest[i] = si + all_nearest[i];
- }
- return all_nearest;
- }
- std::vector<double> all_t;
- std::vector< std::vector<double> > all_np;
- all_np.push_back( _path[si].allNearestPoints(_point, st) );
- std::vector<unsigned int> ni;
- ni.push_back(si);
- double dsq;
- double mindistsq
- = distanceSq( _point, _path[si].pointAt( all_np.front().front() ) );
- Rect bb(Geom::Point(0,0),Geom::Point(0,0));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
- bb = (_path[i].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq < dsq ) continue;
- all_t = _path[i].allNearestPoints(_point);
- dsq = distanceSq( _point, _path[i].pointAt( all_t.front() ) );
- if ( mindistsq > dsq )
- {
- all_np.clear();
- all_np.push_back(all_t);
- ni.clear();
- ni.push_back(i);
- mindistsq = dsq;
- }
- else if ( mindistsq == dsq )
- {
- all_np.push_back(all_t);
- ni.push_back(i);
- }
- }
- bb = (_path[ei].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq >= dsq )
- {
- all_t = _path[ei].allNearestPoints(_point, 0, et);
- dsq = distanceSq( _point, _path[ei].pointAt( all_t.front() ) );
- if ( mindistsq > dsq )
- {
- for ( unsigned int i = 0; i < all_t.size(); ++i )
- {
- all_t[i] = ei + all_t[i];
- }
- return all_t;
- }
- else if ( mindistsq == dsq )
- {
- all_np.push_back(all_t);
- ni.push_back(ei);
- }
- }
- std::vector<double> all_nearest;
- for ( unsigned int i = 0; i < all_np.size(); ++i )
- {
- for ( unsigned int j = 0; j < all_np[i].size(); ++j )
- {
- all_nearest.push_back( ni[i] + all_np[i][j] );
- }
- }
- all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()),
- all_nearest.end());
- return all_nearest;
-}
-
-std::vector<double>
-Path::nearestPointPerCurve(Point const& _point) const
-{
- //return a single nearest point for each curve in this path
- std::vector<double> np;
- for (const_iterator it = begin() ; it != end_default() ; ++it)
- //for (std::vector<Path>::const_iterator it = _path.begin(); it != _path.end(), ++it){
- {
- np.push_back(it->nearestPoint(_point));
- }
- return np;
-}
-
-double Path::nearestPoint(Point const &_point, double from, double to, double *distance_squared) const
-{
- using std::swap;
-
- if ( from > to ) swap(from, to);
- const Path& _path = *this;
- unsigned int sz = _path.size();
- if ( _path.closed() ) ++sz;
- if ( from < 0 || to > sz )
- {
- THROW_RANGEERROR("[from,to] interval out of bounds");
- }
- double sif, st = modf(from, &sif);
- double eif, et = modf(to, &eif);
- unsigned int si = static_cast<unsigned int>(sif);
- unsigned int ei = static_cast<unsigned int>(eif);
- if(sz == 0) {// naked moveto
- if (distance_squared != NULL)
- *distance_squared = distanceSq(_point, _path.initialPoint());
- return 0;
- }
- if ( si == sz )
- {
- --si;
- st = 1;
- }
- if ( ei == sz )
- {
- --ei;
- et = 1;
- }
- if ( si == ei )
- {
- double nearest = _path[si].nearestPoint(_point, st, et);
- if (distance_squared != NULL)
- *distance_squared = distanceSq(_point, _path[si].pointAt(nearest));
- return si + nearest;
- }
-
- double t;
- double nearest = _path[si].nearestPoint(_point, st);
- unsigned int ni = si;
- double dsq;
- double mindistsq = distanceSq(_point, _path[si].pointAt(nearest));
- for ( unsigned int i = si + 1; i < ei; ++i )
- {
- Rect bb = (_path[i].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq <= dsq ) continue;
- t = _path[i].nearestPoint(_point);
- dsq = distanceSq(_point, _path[i].pointAt(t));
- if ( mindistsq > dsq )
- {
- nearest = t;
- ni = i;
- mindistsq = dsq;
- }
- }
- Rect bb = (_path[ei].boundsFast());
- dsq = distanceSq(_point, bb);
- if ( mindistsq > dsq )
- {
- t = _path[ei].nearestPoint(_point, 0, et);
- dsq = distanceSq(_point, _path[ei].pointAt(t));
- if ( mindistsq > dsq )
- {
- nearest = t;
- ni = ei;
- mindistsq = dsq;
- }
- }
-
- if (distance_squared != NULL)
- *distance_squared = mindistsq;
-
- return ni + nearest;
-}
-
-void Path::appendPortionTo(Path &ret, double from, double to) const {
- if (!(from >= 0 && to >= 0)) {
- THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
- }
- if(to == 0) to = size()+0.999999;
- if(from == to) { return; }
- double fi, ti;
- double ff = modf(from, &fi), tf = modf(to, &ti);
- if(tf == 0) { ti--; tf = 1; }
- const_iterator fromi = inc(begin(), (unsigned)fi);
- if(fi == ti && from < to) {
- Curve *v = fromi->portion(ff, tf);
- ret.append(*v, STITCH_DISCONTINUOUS);
- delete v;
- return;
- }
- const_iterator toi = inc(begin(), (unsigned)ti);
- if(ff != 1.) {
- Curve *fromv = fromi->portion(ff, 1.);
- //fromv->setInitial(ret.finalPoint());
- ret.append(*fromv, STITCH_DISCONTINUOUS);
- delete fromv;
- }
- if(from >= to) {
- const_iterator ender = end();
- if(ender->initialPoint() == ender->finalPoint()) ++ender;
- ret.insert(ret.end(), ++fromi, ender, STITCH_DISCONTINUOUS);
- ret.insert(ret.end(), begin(), toi, STITCH_DISCONTINUOUS);
- } else {
- ret.insert(ret.end(), ++fromi, toi, STITCH_DISCONTINUOUS);
- }
- Curve *tov = toi->portion(0., tf);
- ret.append(*tov, STITCH_DISCONTINUOUS);
- delete tov;
-}
-
-void Path::do_update(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last)
-{
- // note: modifies the contents of [first,last)
- check_continuity(first_replaced, last_replaced, first, last);
- if ( ( last - first ) == ( last_replaced - first_replaced ) ) {
- std::copy(first, last, first_replaced);
- } else {
- // this approach depends on std::vector's behavior WRT iterator stability
- get_curves().erase(first_replaced, last_replaced);
- get_curves().insert(first_replaced, first, last);
- }
-
- if ( get_curves().front().get() != final_ ) {
- final_->setPoint(0, back().finalPoint());
- final_->setPoint(1, front().initialPoint());
- }
-}
-
-void Path::do_append(Curve *c) {
- if ( get_curves().front().get() == final_ ) {
- final_->setPoint(1, c->initialPoint());
- } else {
- if (c->initialPoint() != finalPoint()) {
- THROW_CONTINUITYERROR();
- }
- }
- get_curves().insert(get_curves().end()-1, boost::shared_ptr<Curve>(c));
- final_->setPoint(0, c->finalPoint());
-}
-
-void Path::stitch(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence &source)
-{
- if (!source.empty()) {
- if ( first_replaced != get_curves().begin() ) {
- if ( (*first_replaced)->initialPoint() != source.front()->initialPoint() ) {
- Curve *stitch = new StitchSegment((*first_replaced)->initialPoint(),
- source.front()->initialPoint());
- source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
- }
- }
- if ( last_replaced != (get_curves().end()-1) ) {
- if ( (*last_replaced)->finalPoint() != source.back()->finalPoint() ) {
- Curve *stitch = new StitchSegment(source.back()->finalPoint(),
- (*last_replaced)->finalPoint());
- source.insert(source.end(), boost::shared_ptr<Curve>(stitch));
- }
- }
- } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
- if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
- Curve *stitch = new StitchSegment((*(last_replaced-1))->finalPoint(),
- (*first_replaced)->initialPoint());
- source.insert(source.begin(), boost::shared_ptr<Curve>(stitch));
- }
- }
-}
-
-void Path::check_continuity(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last)
-{
- if ( first != last ) {
- if ( first_replaced != get_curves().begin() ) {
- if ( (*first_replaced)->initialPoint() != (*first)->initialPoint() ) {
- THROW_CONTINUITYERROR();
- }
+ iter ret = x;
+ for (unsigned i = 0; i < n; i++)
+ ret++;
+ return ret;
+}
+
+bool Path::operator==(Path const &other) const
+{
+ if (this == &other)
+ return true;
+ if (_closed != other._closed)
+ return false;
+ return *_curves == *other._curves;
+}
+
+void Path::start(Point const &p) {
+ if (_curves->size() > 1) {
+ clear();
+ }
+ _closing_seg->setInitial(p);
+ _closing_seg->setFinal(p);
+}
+
+Interval Path::timeRange() const
+{
+ Interval ret(0, size_default());
+ return ret;
+}
+
+Curve const &Path::curveAt(Coord t, Coord *rest) const
+{
+ PathTime pos = _factorTime(t);
+ if (rest) {
+ *rest = pos.t;
}
- if ( last_replaced != (get_curves().end()-1) ) {
- if ( (*(last_replaced-1))->finalPoint() != (*(last-1))->finalPoint() ) {
+ return at(pos.curve_index);
+}
+
+Point Path::pointAt(Coord t) const
+{
+ return pointAt(_factorTime(t));
+}
+
+Coord Path::valueAt(Coord t, Dim2 d) const
+{
+ return valueAt(_factorTime(t), d);
+}
+
+Curve const &Path::curveAt(PathTime const &pos) const
+{
+ return at(pos.curve_index);
+}
+Point Path::pointAt(PathTime const &pos) const
+{
+ return at(pos.curve_index).pointAt(pos.t);
+}
+Coord Path::valueAt(PathTime const &pos, Dim2 d) const
+{
+ return at(pos.curve_index).valueAt(pos.t, d);
+}
+
+std::vector<PathTime> Path::roots(Coord v, Dim2 d) const
+{
+ std::vector<PathTime> res;
+ for (unsigned i = 0; i <= size(); i++) {
+ std::vector<Coord> temp = (*this)[i].roots(v, d);
+ for (unsigned j = 0; j < temp.size(); j++)
+ res.push_back(PathTime(i, temp[j]));
+ }
+ return res;
+}
+
+
+// The class below implements sweepline optimization for curve intersection in paths.
+// Instead of O(N^2), this takes O(N + X), where X is the number of overlaps
+// between the bounding boxes of curves.
+namespace {
+
+struct CurveSweepTraits {
+ struct Bound {
+ Rect r;
+ std::size_t index;
+ int which;
+ };
+ typedef std::less<Coord> Compare;
+ inline static Coord entry_value(Bound const &b) { return b.r[X].min(); }
+ inline static Coord exit_value(Bound const &b) { return b.r[X].max(); }
+};
+
+class CurveSweeper
+ : public Sweeper<Curve const *, CurveSweepTraits>
+{
+public:
+ CurveSweeper(Path const &a, Path const &b, std::vector<PathIntersection> &result, Coord prec)
+ : _result(result)
+ , _precision(prec)
+ {
+ for (std::size_t i = 0; i < a.size(); ++i) {
+ Bound bound;
+ bound.r = a[i].boundsFast();
+ bound.index = i;
+ bound.which = 0;
+ insert(bound, &a[i]);
+ }
+ for (std::size_t i = 0; i < b.size(); ++i) {
+ Bound bound;
+ bound.r = b[i].boundsFast();
+ bound.index = i;
+ bound.which = 1;
+ insert(bound, &b[i]);
+ }
+ }
+
+protected:
+ void _enter(Record const &record) {
+ int which = record.bound.which;
+
+ for (RecordList::iterator i = _active_items.begin(); i != _active_items.end(); ++i) {
+ // do not intersect in the same path
+ if (i->bound.which == which) continue;
+ // do not intersect if boxes do not overlap in Y
+ if (!record.bound.r[Y].intersects(i->bound.r[Y])) continue;
+
+ std::vector<CurveIntersection> cx;
+ int ia = record.bound.index;
+ int ib = i->bound.index;
+
+ if (which == 0) {
+ cx = record.item->intersect(*i->item, _precision);
+ } else {
+ cx = i->item->intersect(*record.item, _precision);
+ std::swap(ia, ib);
+ }
+
+ for (std::size_t ci = 0; ci < cx.size(); ++ci) {
+ PathTime a(ia, cx[ci].first), b(ib, cx[ci].second);
+ PathIntersection px(a, b, cx[ci].point());
+ _result.push_back(px);
+ }
+ }
+ }
+
+private:
+ std::vector<PathIntersection> &_result;
+ Coord _precision;
+};
+
+} // end anonymous namespace
+
+std::vector<PathIntersection> Path::intersect(Path const &other, Coord precision) const
+{
+ std::vector<PathIntersection> result;
+
+ CurveSweeper sweeper(*this, other, result, precision);
+ sweeper.process();
+
+ // preprocessing to remove duplicate intersections at endpoints
+ for (std::size_t i = 0; i < result.size(); ++i) {
+ result[i].first.normalizeForward(size());
+ result[i].second.normalizeForward(other.size());
+ }
+ std::sort(result.begin(), result.end());
+ result.erase(std::unique(result.begin(), result.end()), result.end());
+
+ return result;
+}
+
+int Path::winding(Point const &p) const {
+ int wind = 0;
+
+ /* To handle all the edge cases, we consider the maximum Y edge of the bounding box
+ * as not included in box. This way paths that contain linear horizontal
+ * segments will be treated correctly. */
+ for (const_iterator i = begin(); i != end_closed(); ++i) {
+ Rect bounds = i->boundsFast();
+
+ if (bounds.height() == 0) continue;
+ if (p[X] > bounds.right() || !bounds[Y].lowerContains(p[Y])) {
+ // Ray doesn't intersect bbox, so we ignore this segment
+ continue;
+ }
+
+ if (p[X] < bounds.left()) {
+ /* Ray intersects the curve's bbox, but the point is outside it.
+ * The winding contribution is exactly the same as that
+ * of a linear segment with the same initial and final points. */
+ Point ip = i->initialPoint();
+ Point fp = i->finalPoint();
+ Rect eqbox(ip, fp);
+
+ if (eqbox[Y].lowerContains(p[Y])) {
+ /* The ray intersects the equivalent linear segment.
+ * Determine winding contribution based on its derivative. */
+ if (ip[Y] < fp[Y]) {
+ wind += 1;
+ } else if (ip[Y] > fp[Y]) {
+ wind -= 1;
+ } else {
+ // should never happen, because bounds.height() was not zero
+ assert(false);
+ }
+ }
+ } else {
+ // point is inside bbox
+ wind += i->winding(p);
+ }
+ }
+ return wind;
+}
+
+std::vector<double> Path::allNearestTimes(Point const &_point, double from, double to) const
+{
+ // TODO from and to are not used anywhere.
+ // rewrite this to simplify.
+ using std::swap;
+
+ if (from > to)
+ swap(from, to);
+ const Path &_path = *this;
+ unsigned int sz = _path.size();
+ if (_path.closed())
+ ++sz;
+ if (from < 0 || to > sz) {
+ THROW_RANGEERROR("[from,to] interval out of bounds");
+ }
+ double sif, st = modf(from, &sif);
+ double eif, et = modf(to, &eif);
+ unsigned int si = static_cast<unsigned int>(sif);
+ unsigned int ei = static_cast<unsigned int>(eif);
+ if (si == sz) {
+ --si;
+ st = 1;
+ }
+ if (ei == sz) {
+ --ei;
+ et = 1;
+ }
+ if (si == ei) {
+ std::vector<double> all_nearest = _path[si].allNearestTimes(_point, st, et);
+ for (unsigned int i = 0; i < all_nearest.size(); ++i) {
+ all_nearest[i] = si + all_nearest[i];
+ }
+ return all_nearest;
+ }
+ std::vector<double> all_t;
+ std::vector<std::vector<double> > all_np;
+ all_np.push_back(_path[si].allNearestTimes(_point, st));
+ std::vector<unsigned int> ni;
+ ni.push_back(si);
+ double dsq;
+ double mindistsq = distanceSq(_point, _path[si].pointAt(all_np.front().front()));
+ Rect bb(Geom::Point(0, 0), Geom::Point(0, 0));
+ for (unsigned int i = si + 1; i < ei; ++i) {
+ bb = (_path[i].boundsFast());
+ dsq = distanceSq(_point, bb);
+ if (mindistsq < dsq)
+ continue;
+ all_t = _path[i].allNearestTimes(_point);
+ dsq = distanceSq(_point, _path[i].pointAt(all_t.front()));
+ if (mindistsq > dsq) {
+ all_np.clear();
+ all_np.push_back(all_t);
+ ni.clear();
+ ni.push_back(i);
+ mindistsq = dsq;
+ } else if (mindistsq == dsq) {
+ all_np.push_back(all_t);
+ ni.push_back(i);
+ }
+ }
+ bb = (_path[ei].boundsFast());
+ dsq = distanceSq(_point, bb);
+ if (mindistsq >= dsq) {
+ all_t = _path[ei].allNearestTimes(_point, 0, et);
+ dsq = distanceSq(_point, _path[ei].pointAt(all_t.front()));
+ if (mindistsq > dsq) {
+ for (unsigned int i = 0; i < all_t.size(); ++i) {
+ all_t[i] = ei + all_t[i];
+ }
+ return all_t;
+ } else if (mindistsq == dsq) {
+ all_np.push_back(all_t);
+ ni.push_back(ei);
+ }
+ }
+ std::vector<double> all_nearest;
+ for (unsigned int i = 0; i < all_np.size(); ++i) {
+ for (unsigned int j = 0; j < all_np[i].size(); ++j) {
+ all_nearest.push_back(ni[i] + all_np[i][j]);
+ }
+ }
+ all_nearest.erase(std::unique(all_nearest.begin(), all_nearest.end()), all_nearest.end());
+ return all_nearest;
+}
+
+std::vector<Coord> Path::nearestTimePerCurve(Point const &p) const
+{
+ // return a single nearest time for each curve in this path
+ std::vector<Coord> np;
+ for (const_iterator it = begin(); it != end_default(); ++it) {
+ np.push_back(it->nearestTime(p));
+ }
+ return np;
+}
+
+PathTime Path::nearestTime(Point const &p, Coord *dist) const
+{
+ Coord mindist = std::numeric_limits<Coord>::max();
+ PathTime ret;
+
+ if (_curves->size() == 1) {
+ // naked moveto
+ ret.curve_index = 0;
+ ret.t = 0;
+ if (dist) {
+ *dist = distance(_closing_seg->initialPoint(), p);
+ }
+ return ret;
+ }
+
+ for (size_type i = 0; i < size_default(); ++i) {
+ Curve const &c = at(i);
+ if (distance(p, c.boundsFast()) >= mindist) continue;
+
+ Coord t = c.nearestTime(p);
+ Coord d = distance(c.pointAt(t), p);
+ if (d < mindist) {
+ mindist = d;
+ ret.curve_index = i;
+ ret.t = t;
+ }
+ }
+ if (dist) {
+ *dist = mindist;
+ }
+
+ return ret;
+}
+
+void Path::appendPortionTo(Path &ret, double from, double to) const
+{
+ if (!(from >= 0 && to >= 0)) {
+ THROW_RANGEERROR("from and to must be >=0 in Path::appendPortionTo");
+ }
+ if (to == 0)
+ to = size() + 0.999999;
+ if (from == to) {
+ return;
+ }
+ double fi, ti;
+ double ff = modf(from, &fi), tf = modf(to, &ti);
+ if (tf == 0) {
+ ti--;
+ tf = 1;
+ }
+ const_iterator fromi = inc(begin(), (unsigned)fi);
+ if (fi == ti && from < to) {
+ ret.append(fromi->portion(ff, tf));
+ return;
+ }
+ const_iterator toi = inc(begin(), (unsigned)ti);
+ if (ff != 1.) {
+ // fromv->setInitial(ret.finalPoint());
+ ret.append(fromi->portion(ff, 1.));
+ }
+ if (from >= to) {
+ const_iterator ender = end();
+ if (ender->initialPoint() == ender->finalPoint())
+ ++ender;
+ ret.insert(ret.end(), ++fromi, ender);
+ ret.insert(ret.end(), begin(), toi);
+ } else {
+ ret.insert(ret.end(), ++fromi, toi);
+ }
+ ret.append(toi->portion(0., tf));
+}
+
+void Path::appendPortionTo(Path &target, PathInterval const &ival,
+ boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const
+{
+ assert(ival.pathSize() == size_closed());
+
+ if (ival.isDegenerate()) {
+ Point stitch_to = p_from ? *p_from : pointAt(ival.from());
+ target.stitchTo(stitch_to);
+ return;
+ }
+
+ PathTime const &from = ival.from(), &to = ival.to();
+
+ bool reverse = ival.reverse();
+ int di = reverse ? -1 : 1;
+ size_type s = size_closed();
+
+ if (!ival.crossesStart() && from.curve_index == to.curve_index) {
+ Curve *c = (*this)[from.curve_index].portion(from.t, to.t);
+ if (p_from) {
+ c->setInitial(*p_from);
+ }
+ if (p_to) {
+ c->setFinal(*p_to);
+ }
+ target.append(c);
+ } else {
+ Curve *c_first = (*this)[from.curve_index].portion(from.t, reverse ? 0 : 1);
+ if (p_from) {
+ c_first->setInitial(*p_from);
+ }
+ target.append(c_first);
+
+ for (size_type i = (from.curve_index + s + di) % s; i != to.curve_index;
+ i = (i + s + di) % s)
+ {
+ if (reverse) {
+ target.append((*this)[i].reverse());
+ } else {
+ target.append((*this)[i].duplicate());
+ }
+ }
+
+ Curve *c_last = (*this)[to.curve_index].portion(reverse ? 1 : 0, to.t);
+ if (p_to) {
+ c_last->setFinal(*p_to);
+ }
+ target.append(c_last);
+ }
+}
+
+Path Path::reversed() const
+{
+ typedef std::reverse_iterator<Sequence::const_iterator> RIter;
+
+ Path ret(finalPoint());
+ if (empty()) return ret;
+
+ ret._curves->pop_back(); // this also deletes the closing segment from ret
+
+ RIter iter(_includesClosingSegment() ? _curves->end() : _curves->end() - 1);
+ RIter rend(_curves->begin());
+
+ if (_closed) {
+ // when the path is closed, there are two cases:
+ if (front().isLineSegment()) {
+ // 1. initial segment is linear: it becomes the new closing segment.
+ rend = RIter(_curves->begin() + 1);
+ ret._closing_seg = new ClosingSegment(front().finalPoint(), front().initialPoint());
+ } else {
+ // 2. initial segment is not linear: the closing segment becomes degenerate.
+ // However, skip it if it's already degenerate.
+ Point fp = finalPoint();
+ ret._closing_seg = new ClosingSegment(fp, fp);
+ }
+ } else {
+ // when the path is open, we reverse all real curves, and add a reversed closing segment.
+ ret._closing_seg = static_cast<ClosingSegment *>(_closing_seg->reverse());
+ }
+
+ for (; iter != rend; ++iter) {
+ ret._curves->push_back(iter->reverse());
+ }
+ ret._curves->push_back(ret._closing_seg);
+ ret._closed = _closed;
+ return ret;
+}
+
+
+void Path::insert(iterator pos, Curve const &curve)
+{
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+ Sequence source;
+ source.push_back(curve.duplicate());
+ do_update(seq_pos, seq_pos, source);
+}
+
+void Path::erase(iterator pos)
+{
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+
+ Sequence stitched;
+ do_update(seq_pos, seq_pos + 1, stitched);
+}
+
+void Path::erase(iterator first, iterator last)
+{
+ _unshare();
+ Sequence::iterator seq_first = seq_iter(first);
+ Sequence::iterator seq_last = seq_iter(last);
+
+ Sequence stitched;
+ do_update(seq_first, seq_last, stitched);
+}
+
+void Path::stitchTo(Point const &p)
+{
+ if (!empty() && _closing_seg->initialPoint() != p) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ _unshare();
+ do_append(new StitchSegment(_closing_seg->initialPoint(), p));
+ }
+}
+
+void Path::replace(iterator replaced, Curve const &curve)
+{
+ replace(replaced, replaced + 1, curve);
+}
+
+void Path::replace(iterator first_replaced, iterator last_replaced, Curve const &curve)
+{
+ _unshare();
+ Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
+ Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
+ Sequence source(1);
+ source.push_back(curve.duplicate());
+
+ do_update(seq_first_replaced, seq_last_replaced, source);
+}
+
+void Path::replace(iterator replaced, Path const &path)
+{
+ replace(replaced, path.begin(), path.end());
+}
+
+void Path::replace(iterator first, iterator last, Path const &path)
+{
+ replace(first, last, path.begin(), path.end());
+}
+
+void Path::snapEnds(Coord precision)
+{
+ if (!_closed) return;
+ if (_curves->size() > 1 && are_near(_closing_seg->length(precision), 0, precision)) {
+ _unshare();
+ _closing_seg->setInitial(_closing_seg->finalPoint());
+ (_curves->end() - 1)->setFinal(_closing_seg->finalPoint());
+ }
+}
+
+// replace curves between first and last with contents of source,
+//
+void Path::do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source)
+{
+ // TODO: handle cases where first > last in closed paths?
+ bool last_beyond_closing_segment = (last == _curves->end());
+
+ // special case:
+ // if do_update replaces the closing segment, we have to regenerate it
+ if (source.empty()) {
+ if (first == last) return; // nothing to do
+
+ // only removing some segments
+ if ((!_closed && first == _curves->begin()) || (!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ // just adjust the closing segment
+ // do nothing
+ } else if (first->initialPoint() != (last - 1)->finalPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.push_back(new StitchSegment(first->initialPoint(), (last - 1)->finalPoint()));
+ }
+ } else {
+ // replacing
+ if (first == _curves->begin() && last == _curves->end()) {
+ // special case: replacing everything should work the same in open and closed curves
+ _curves->erase(_curves->begin(), _curves->end() - 1);
+ _closing_seg->setFinal(source.front().initialPoint());
+ _closing_seg->setInitial(source.back().finalPoint());
+ _curves->transfer(_curves->begin(), source.begin(), source.end(), source);
+ return;
+ }
+
+ // stitch in front
+ if (!_closed && first == _curves->begin()) {
+ // not necessary to stitch in front
+ } else if (first->initialPoint() != source.front().initialPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.insert(source.begin(), new StitchSegment(first->initialPoint(), source.front().initialPoint()));
+ }
+
+ // stitch at the end
+ if ((!_closed && last == _curves->end() - 1) || last_beyond_closing_segment) {
+ // repurpose the closing segment as the stitch segment
+ // do nothing
+ } else if (source.back().finalPoint() != (last - 1)->finalPoint()) {
+ if (_exception_on_stitch) {
+ THROW_CONTINUITYERROR();
+ }
+ source.push_back(new StitchSegment(source.back().finalPoint(), (last - 1)->finalPoint()));
+ }
+ }
+
+ // do not erase the closing segment, adjust it instead
+ if (last_beyond_closing_segment) {
+ --last;
+ }
+ _curves->erase(first, last);
+ _curves->transfer(first, source.begin(), source.end(), source);
+
+ // adjust closing segment
+ if (size_open() == 0) {
+ _closing_seg->setFinal(_closing_seg->initialPoint());
+ } else {
+ _closing_seg->setInitial(back_open().finalPoint());
+ _closing_seg->setFinal(front().initialPoint());
+ }
+
+ checkContinuity();
+}
+
+void Path::do_append(Curve *c)
+{
+ if (&_curves->front() == _closing_seg) {
+ _closing_seg->setFinal(c->initialPoint());
+ } else {
+ // if we can't freely move the closing segment, we check whether
+ // the new curve connects with the last non-closing curve
+ if (c->initialPoint() != _closing_seg->initialPoint()) {
+ THROW_CONTINUITYERROR();
+ }
+ if (_closed && c->isLineSegment() &&
+ c->finalPoint() == _closing_seg->finalPoint())
+ {
+ // appending a curve that matches the closing segment has no effect
+ delete c;
+ return;
+ }
+ }
+ _curves->insert(_curves->end() - 1, c);
+ _closing_seg->setInitial(c->finalPoint());
+}
+
+void Path::checkContinuity() const
+{
+ Sequence::const_iterator i = _curves->begin(), j = _curves->begin();
+ ++j;
+ for (; j != _curves->end(); ++i, ++j) {
+ if (i->finalPoint() != j->initialPoint()) {
+ THROW_CONTINUITYERROR();
+ }
+ }
+ if (_curves->front().initialPoint() != _curves->back().finalPoint()) {
THROW_CONTINUITYERROR();
- }
}
- } else if ( first_replaced != last_replaced && first_replaced != get_curves().begin() && last_replaced != get_curves().end()-1) {
- if ( (*first_replaced)->initialPoint() != (*(last_replaced-1))->finalPoint() ) {
- THROW_CONTINUITYERROR();
+}
+
+// breaks time value into integral and fractional part
+PathTime Path::_factorTime(Coord t) const
+{
+ size_type sz = size_default();
+ if (t < 0 || t > sz) {
+ THROW_RANGEERROR("parameter t out of bounds");
+ }
+
+ PathTime ret;
+ Coord k;
+ ret.t = modf(t, &k);
+ ret.curve_index = k;
+ if (ret.curve_index == sz) {
+ --ret.curve_index;
+ ret.t = 1;
}
- }
+ return ret;
+}
+
+Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths)
+{
+ Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
+ for (unsigned i = 1; i < paths.size(); i++) {
+ ret.concat(paths[i].toPwSb());
+ }
+ return ret;
+}
+
+std::ostream &operator<<(std::ostream &out, Path const &path)
+{
+ SVGPathWriter pw;
+ pw.feed(path);
+ out << pw.str();
+ return out;
}
} // end namespace Geom
diff --git a/src/2geom/path.h b/src/2geom/path.h
index 28d2a25e4..a2d1e751e 100644
--- a/src/2geom/path.h
+++ b/src/2geom/path.h
@@ -1,43 +1,47 @@
-/**
- * \file
- * \brief Path - Series of continuous curves
+/** @file
+ * @brief Path - a sequence of contiguous curves
*//*
- * Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
- *
- * Copyright 2007-2008 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Marco Cecchetti <mrcekets at gmail.com>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
#ifndef LIB2GEOM_SEEN_PATH_H
#define LIB2GEOM_SEEN_PATH_H
#include <iterator>
#include <algorithm>
+#include <iostream>
+#include <boost/operators.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/shared_ptr.hpp>
+#include <2geom/intersection.h>
#include <2geom/curve.h>
#include <2geom/bezier-curve.h>
#include <2geom/transforms.h>
@@ -45,656 +49,800 @@
namespace Geom {
class Path;
+class ConvexHull;
namespace PathInternal {
-typedef std::vector<boost::shared_ptr<Curve const> > Sequence;
+typedef boost::ptr_vector<Curve> Sequence;
+
+template <typename P>
+class BaseIterator
+ : public boost::random_access_iterator_helper
+ < BaseIterator<P>
+ , Curve const
+ , std::ptrdiff_t
+ , Curve const *
+ , Curve const &
+ >
+{
+ protected:
+ BaseIterator(P &p, unsigned i) : path(&p), index(i) {}
+ // default copy, default assign
+ typedef BaseIterator<P> Self;
-template <typename C, typename P>
-class BaseIterator {
-protected:
- BaseIterator() : path(NULL), index(0) {}
- BaseIterator(P *p, unsigned i) : path(p), index(i) {}
- // default copy, default assign
+ public:
+ BaseIterator() : path(NULL), index(0) {}
-public:
- bool operator==(BaseIterator const &other) {
- return path == other.path && index == other.index;
- }
- bool operator!=(BaseIterator const &other) {
- return path != other.path || index != other.index;
- }
-
- Curve const &operator*() const { return (*path)[index]; }
- Curve const *operator->() const { return &(*path)[index]; }
- boost::shared_ptr<Curve const> get_ref() const {
- return path->get_ref_at_index(index);
- }
-
- C &operator++() {
- ++index;
- return static_cast<C &>(*this);
- }
- C operator++(int) {
- C old(static_cast<C &>(*this));
- ++(*this);
- return old;
- }
-
- C &operator--() {
- --index;
- return static_cast<C &>(*this);
- }
- C operator--(int) {
- C old(static_cast<C &>(*this));
- --(*this);
- return old;
- }
+ bool operator<(BaseIterator const &other) const {
+ return path == other.path && index < other.index;
+ }
+ bool operator==(BaseIterator const &other) const {
+ return path == other.path && index == other.index;
+ }
+ Curve const &operator*() const {
+ return (*path)[index];
+ }
-private:
- P *path;
- unsigned index;
+ Self &operator++() {
+ ++index;
+ return *this;
+ }
+ Self &operator--() {
+ --index;
+ return *this;
+ }
+ Self &operator+=(std::ptrdiff_t d) {
+ index += d;
+ return *this;
+ }
+ Self &operator-=(std::ptrdiff_t d) {
+ index -= d;
+ return *this;
+ }
- friend class ::Geom::Path;
+ private:
+ P *path;
+ unsigned index;
+
+ friend class ::Geom::Path;
};
-class ConstIterator : public BaseIterator<ConstIterator, Path const> {
-public:
- typedef BaseIterator<ConstIterator, Path const> Base;
+}
- ConstIterator() : Base() {}
- // default copy, default assign
+/** @brief Generalized time value in the path.
+ *
+ * This class exists because when mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, either use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on, or use curveAt() to first obtain the curve, then
+ * call the method again to obtain a high precision result.
+ *
+ * @ingroup Paths */
+struct PathTime
+ : boost::totally_ordered<PathTime>
+{
+ typedef PathInternal::Sequence::size_type size_type;
-private:
- ConstIterator(Path const &p, unsigned i) : Base(&p, i) {}
- friend class ::Geom::Path;
+ Coord t; ///< Time value in the curve
+ size_type curve_index; ///< Index of the curve in the path
+
+ PathTime() : t(0), curve_index(0) {}
+ PathTime(size_type idx, Coord tval) : t(tval), curve_index(idx) {}
+
+ bool operator<(PathTime const &other) const {
+ if (curve_index < other.curve_index) return true;
+ if (curve_index == other.curve_index) {
+ return t < other.t;
+ }
+ return false;
+ }
+ bool operator==(PathTime const &other) const {
+ return curve_index == other.curve_index && t == other.t;
+ }
+ /// Convert times at or beyond 1 to 0 on the next curve.
+ void normalizeForward(size_type path_size) {
+ if (t >= 1) {
+ curve_index = (curve_index + 1) % path_size;
+ t = 0;
+ }
+ }
+ /// Convert times at or before 0 to 1 on the previous curve.
+ void normalizeBackward(size_type path_size) {
+ if (t <= 0) {
+ curve_index = (curve_index - 1) % path_size;
+ t = 1;
+ }
+ }
+
+ Coord asFlatTime() const { return curve_index + t; }
};
-class Iterator : public BaseIterator<Iterator, Path> {
-public:
- typedef BaseIterator<Iterator, Path> Base;
+inline std::ostream &operator<<(std::ostream &os, PathTime const &pos) {
+ os << pos.curve_index << ": " << format_coord_nice(pos.t);
+ return os;
+}
- Iterator() : Base() {}
- // default copy, default assign
- operator ConstIterator const &() const {
- return reinterpret_cast<ConstIterator const &>(*this);
- }
+/** @brief Contiguous subset of the path's parameter domain.
+ * This is a directed interval, which allows one to specify any contiguous subset
+ * of the path's domain, including subsets that wrap around the initial point
+ * of the path.
+ * @ingroup Paths */
+class PathInterval {
+public:
+ typedef PathInternal::Sequence::size_type size_type;
+
+ /** @brief Default interval.
+ * Default-constructed PathInterval includes only the initial point of the initial segment. */
+ PathInterval();
+
+ /** @brief Construct an interval in the path's parameter domain.
+ * @param from Initial time
+ * @param to Final time
+ * @param cross_start If true, the interval will proceed from the initial to final
+ * time through the initial point of the path, wrapping around the closing segment;
+ * otherwise it will not wrap around the closing segment.
+ * @param path_size Size of the path to which this interval applies, required
+ * to clean up degenerate cases */
+ PathInterval(PathTime const &from, PathTime const &to, bool cross_start, size_type path_size);
+
+ /// Get the time value of the initial point.
+ PathTime const &initialTime() const { return _from; }
+ /// Get the time value of the final point.
+ PathTime const &finalTime() const { return _to; }
+
+ PathTime const &from() const { return _from; }
+ PathTime const &to() const { return _to; }
+
+ /// Check whether the interval has only one point.
+ bool isDegenerate() const { return _from == _to; }
+ /// True if the interval goes in the direction of decreasing time values.
+ bool reverse() const { return _reverse; }
+ /// True if the interior of the interval contains the initial point of the path.
+ bool crossesStart() const { return _cross_start; }
+
+ /// Test a path time for inclusion.
+ bool contains(PathTime const &pos) const;
+
+ /// Get a time at least @a min_dist away in parameter space from the ends.
+ /// If no such time exists, the middle point is returned.
+ PathTime inside(Coord min_dist = EPSILON) const;
+
+ /// Select one of two intervals with given endpoints by parameter direction.
+ static PathInterval from_direction(PathTime const &from, PathTime const &to,
+ bool reversed, size_type path_size);
+
+ /// Select one of two intervals with given endpoints by whether it includes the initial point.
+ static PathInterval from_start_crossing(PathTime const &from, PathTime const &to,
+ bool cross_start, size_type path_size) {
+ PathInterval result(from, to, cross_start, path_size);
+ return result;
+ }
+
+ size_type pathSize() const { return _path_size; }
private:
- Iterator(Path &p, unsigned i) : Base(&p, i) {}
- friend class ::Geom::Path;
+ PathTime _from, _to;
+ size_type _path_size;
+ bool _cross_start, _reverse;
};
+/// Create an interval in the direction of increasing time value.
+/// @relates PathInterval
+inline PathInterval forward_interval(PathTime const &from, PathTime const &to,
+ PathInterval::size_type path_size)
+{
+ PathInterval result = PathInterval::from_direction(from, to, false, path_size);
+ return result;
+}
+
+/// Create an interval in the direction of decreasing time value.
+/// @relates PathInterval
+inline PathInterval backward_interval(PathTime const &from, PathTime const &to,
+ PathInterval::size_type path_size)
+{
+ PathInterval result = PathInterval::from_direction(from, to, true, path_size);
+ return result;
}
-/*
- * Open and closed paths: all paths, whether open or closed, store a final
- * segment which connects the initial and final endpoints of the "real"
- * path data. While similar to the "z" in an SVG path, it exists for
- * both open and closed paths, and is not considered part of the "normal"
- * path data, which is always covered by the range [begin(), end_open()).
- * Conversely, the range [begin(), end_closed()) always contains the "extra"
- * closing segment.
+/// Output an interval in the path's domain.
+/// @relates PathInterval
+inline std::ostream &operator<<(std::ostream &os, PathInterval const &ival) {
+ os << "PathInterval[";
+ if (ival.crossesStart()) {
+ os << ival.from() << " -> 0: 0.0 -> " << ival.to();
+ } else {
+ os << ival.from() << " -> " << ival.to();
+ }
+ os << "]";
+ return os;
+}
+
+typedef Intersection<PathTime> PathIntersection;
+
+template <>
+struct ShapeTraits<Path> {
+ typedef PathTime TimeType;
+ typedef PathInterval IntervalType;
+ typedef Path AffineClosureType;
+ typedef PathIntersection IntersectionType;
+};
+
+/** @brief Sequence of contiguous curves, aka spline.
+ *
+ * Path represents a sequence of contiguous curves, also known as a spline.
+ * It corresponds to a "subpath" in SVG terminology. It can represent both
+ * open and closed subpaths. The final point of each curve is exactly
+ * equal to the initial point of the next curve.
+ *
+ * The path always contains a linear closing segment that connects
+ * the final point of the last "real" curve to the initial point of the
+ * first curve. This way the curves form a closed loop even for open paths.
+ * If the closing segment has nonzero length and the path is closed, it is
+ * considered a normal part of the path data. There are three distinct sets
+ * of end iterators one can use to iterate over the segments:
+ *
+ * - Iterating between @a begin() and @a end() will iterate over segments
+ * which are part of the path.
+ * - Iterating between @a begin() and @a end_closed()
+ * will always iterate over a closed loop of segments.
+ * - Iterating between @a begin() and @a end_open() will always skip
+ * the final linear closing segment.
+ *
+ * If the final point of the last "real" segment coincides exactly with the initial
+ * point of the first segment, the closing segment will be absent from both
+ * [begin(), end_open()) and [begin(), end_closed()).
+ *
+ * Normally, an exception will be thrown when you try to insert a curve
+ * that makes the path non-continuous. If you are working with unsanitized
+ * curve data, you can call setStitching(true), which will insert line segments
+ * to make the path continuous.
+ *
+ * Internally, Path uses copy-on-write data. This is done for two reasons: first,
+ * copying a Curve requires calling a virtual function, so it's a little more expensive
+ * that normal copying; and second, it reduces the memory cost of copying the path.
+ * Therefore you can return Path and PathVector from functions without worrying
+ * about temporary copies.
+ *
+ * Note that this class cannot represent arbitrary shapes, which may contain holes.
+ * To do that, use PathVector, which is more generic.
*
- * The only difference between a closed and an open path is whether
- * end_default() returns end_closed() or end_open(). The idea behind this
- * is to let any path be stroked using [begin(), end_default()), and filled
- * using [begin(), end_closed()), without requiring a separate "filled" version
- * of the path to use for filling.
+ * It's not very convenient to create a Path directly. To construct paths more easily,
+ * use PathBuilder.
*
- * \invariant : curves_ always contains at least one segment. The last segment
- * is always of type ClosingSegment. All constructors take care of this.
- (curves_.size() > 0) && dynamic_cast<ClosingSegment>(curves_.back())
- */
-class Path {
+ * @ingroup Paths */
+class Path
+ : boost::equality_comparable< Path >
+{
public:
- typedef PathInternal::Sequence Sequence;
- typedef PathInternal::Iterator iterator;
- typedef PathInternal::ConstIterator const_iterator;
- typedef Sequence::size_type size_type;
- typedef Sequence::difference_type difference_type;
+ typedef PathInternal::Sequence Sequence;
+ typedef PathInternal::BaseIterator<Path> iterator;
+ typedef PathInternal::BaseIterator<Path const> const_iterator;
+ typedef Sequence::size_type size_type;
+ typedef Sequence::difference_type difference_type;
+
+ class ClosingSegment : public LineSegment {
+ public:
+ ClosingSegment() : LineSegment() {}
+ ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+ virtual Curve *duplicate() const { return new ClosingSegment(*this); }
+ virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
+ };
+
+ class StitchSegment : public LineSegment {
+ public:
+ StitchSegment() : LineSegment() {}
+ StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
+ virtual Curve *duplicate() const { return new StitchSegment(*this); }
+ virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
+ };
+
+ // Path(Path const &other) - use default copy constructor
+
+ /// Construct an empty path starting at the specified point.
+ explicit Path(Point const &p = Point())
+ : _curves(new Sequence())
+ , _closing_seg(new ClosingSegment(p, p))
+ , _closed(false)
+ , _exception_on_stitch(true)
+ {
+ _curves->push_back(_closing_seg);
+ }
- class ClosingSegment : public LineSegment {
- public:
- ClosingSegment() : LineSegment() {}
- ClosingSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
- virtual Curve *duplicate() const { return new ClosingSegment(*this); }
- virtual Curve *reverse() const { return new ClosingSegment((*this)[1], (*this)[0]); }
- };
-
- enum Stitching {
- NO_STITCHING=0,
- STITCH_DISCONTINUOUS
- };
-
- class StitchSegment : public LineSegment {
- public:
- StitchSegment() : LineSegment() {}
- StitchSegment(Point const &p1, Point const &p2) : LineSegment(p1, p2) {}
- virtual Curve *duplicate() const { return new StitchSegment(*this); }
- virtual Curve *reverse() const { return new StitchSegment((*this)[1], (*this)[0]); }
- };
-
- // Path(Path const &other) - use default copy constructor
-
- explicit Path(Point p=Point())
- : curves_(boost::shared_ptr<Sequence>(new Sequence(1, boost::shared_ptr<Curve>()))),
- final_(new ClosingSegment(p, p)),
- closed_(false)
- {
- get_curves().back() = boost::shared_ptr<Curve>(final_);
- }
-
- Path(const_iterator const &first,
- const_iterator const &last,
- bool closed=false)
- : curves_(boost::shared_ptr<Sequence>(new Sequence(seq_iter(first),
- seq_iter(last)))),
- closed_(closed)
- {
- if (!get_curves().empty()) {
- final_ = new ClosingSegment(get_curves().back()->finalPoint(),
- get_curves().front()->initialPoint());
- } else {
- final_ = new ClosingSegment();
- }
- get_curves().push_back(boost::shared_ptr<Curve>(final_));
- }
-
- virtual ~Path() {}
-
- // Path &operator=(Path const &other) - use default assignment operator
-
- /// \todo Add noexcept specifiers for C++11
- void swap(Path &other) {
- using std::swap;
- swap(other.curves_, curves_);
- swap(other.final_, final_);
- swap(other.closed_, closed_);
- }
- friend inline void swap(Path &a, Path &b) { a.swap(b); }
-
- Curve const &operator[](unsigned i) const { return *get_curves()[i]; }
- Curve const &at_index(unsigned i) const { return *get_curves()[i]; }
- boost::shared_ptr<Curve const> get_ref_at_index(unsigned i) {
- return get_curves()[i];
- }
-
- Curve const &front() const { return *get_curves()[0]; }
- Curve const &back() const { return back_open(); }
- Curve const &back_open() const {
- if (empty()) { THROW_RANGEERROR("Path contains not enough segments"); }
- return *get_curves()[get_curves().size()-2];
- }
- Curve const &back_closed() const { return *get_curves()[get_curves().size()-1]; }
- Curve const &back_default() const {
- return ( closed_ ? back_closed() : back_open() );
- }
-
- const_iterator begin() const { return const_iterator(*this, 0); }
- const_iterator end() const { return const_iterator(*this, size()); }
- iterator begin() { return iterator(*this, 0); }
- iterator end() { return iterator(*this, size()); }
-
- const_iterator end_open() const { return const_iterator(*this, size()); }
- const_iterator end_closed() const { return const_iterator(*this, size()+1); }
- const_iterator end_default() const {
- return ( closed_ ? end_closed() : end_open() );
- }
-
- size_type size_open() const { return get_curves().size()-1; }
- size_type size_closed() const { return get_curves().size(); }
- size_type size_default() const {
- return ( closed_ ? size_closed() : size_open() );
- }
- size_type size() const { return size_open(); }
-
- size_type max_size() const { return get_curves().max_size()-1; }
-
- bool empty() const { return (get_curves().size() == 1); }
- bool closed() const { return closed_; }
- void close(bool closed=true) { closed_ = closed; }
-
- OptRect boundsFast() const;
- OptRect boundsExact() const;
-
- Piecewise<D2<SBasis> > toPwSb() const {
- Piecewise<D2<SBasis> > ret;
- ret.push_cut(0);
- unsigned i = 1;
- bool degenerate = true;
- // pw<d2<>> is always open. so if path is closed, add closing segment as well to pwd2.
- for(const_iterator it = begin(); it != end_default(); ++it) {
- if (!it->isDegenerate()) {
- ret.push(it->toSBasis(), i++);
- degenerate = false;
- }
- }
- if (degenerate) {
- // if path only contains degenerate curves, no second cut is added
- // so we need to create at least one segment manually
- ret = Piecewise<D2<SBasis> >(initialPoint());
- }
- return ret;
- }
-
- bool operator==(Path const &other) const {
- if (this == &other) return true;
- if (closed_ != other.closed_) return false;
- return get_curves() == other.get_curves();
- }
- bool operator!=(Path const &other) const {
- return !( *this == other );
- }
-
- Path operator*(Affine const &m) const {
- Path ret(*this);
- ret *= m;
- return ret;
- }
- Path operator*(Translate const &m) const { // specialization over Affine, for faster computation
- Path ret(*this);
- ret *= m;
- return ret;
- }
-
- Path &operator*=(Affine const &m);
- Path &operator*=(Translate const &m); // specialization over Affine, for faster computation
-
- Point pointAt(double t) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- if ( t < 0 || t > sz )
- {
- THROW_RANGEERROR("parameter t out of bounds");
- }
- if ( empty() ) return initialPoint(); // naked moveto
- double k, lt = modf(t, &k);
- unsigned int i = static_cast<unsigned int>(k);
- if ( i == sz )
- {
- --i;
- lt = 1;
- }
- return (*this)[i].pointAt(lt);
- }
-
- double valueAt(double t, Dim2 d) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- if ( t < 0 || t > sz )
- {
- THROW_RANGEERROR("parameter t out of bounds");
- }
- if ( empty() ) return initialPoint()[d]; // naked moveto
- double k, lt = modf(t, &k);
- unsigned int i = static_cast<unsigned int>(k);
- if ( i == sz )
- {
- --i;
- lt = 1;
- }
- return (*this)[i].valueAt(lt, d);
- }
-
-
- Point operator() (double t) const
- {
- return pointAt(t);
- }
-
- std::vector<double> roots(double v, Dim2 d) const {
- std::vector<double> res;
- for(unsigned i = 0; i <= size(); i++) {
- std::vector<double> temp = (*this)[i].roots(v, d);
- for(unsigned j = 0; j < temp.size(); j++)
- res.push_back(temp[j] + i);
- }
- return res;
- }
-
- std::vector<double>
- allNearestPoints(Point const& _point, double from, double to) const;
-
- std::vector<double>
- allNearestPoints(Point const& _point) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- return allNearestPoints(_point, 0, sz);
- }
-
- std::vector<double>
- nearestPointPerCurve(Point const& _point) const;
-
- double nearestPoint(Point const& _point, double from, double to, double *distance_squared = NULL) const;
-
- double nearestPoint(Point const& _point, double *distance_squared = NULL) const
- {
- unsigned int sz = size();
- if ( closed() ) ++sz;
- return nearestPoint(_point, 0, sz, distance_squared);
- }
-
- void appendPortionTo(Path &p, double f, double t) const;
-
- Path portion(double f, double t) const {
- Path ret;
- ret.close(false);
- appendPortionTo(ret, f, t);
- return ret;
- }
- Path portion(Interval i) const { return portion(i.min(), i.max()); }
-
- Path reverse() const {
- Path ret(*this);
- ret.unshare();
- for ( Sequence::iterator iter = ret.get_curves().begin() ;
- iter != ret.get_curves().end()-1 ; ++iter )
+ /// Construct a path containing a range of curves.
+ template <typename Iter>
+ Path(Iter first, Iter last, bool closed = false, bool stitch = false)
+ : _curves(new Sequence())
+ , _closed(closed)
+ , _exception_on_stitch(!stitch)
{
- *iter = boost::shared_ptr<Curve>((*iter)->reverse());
- }
- std::reverse(ret.get_curves().begin(), ret.get_curves().end()-1);
- ret.final_ = static_cast<ClosingSegment *>(ret.final_->reverse());
- ret.get_curves().back() = boost::shared_ptr<Curve>(ret.final_);
- return ret;
- }
-
- void insert(iterator const &pos,
- Curve const &curve, Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_pos, seq_pos, source);
- do_update(seq_pos, seq_pos, source.begin(), source.end());
- }
-
- void insert(iterator const &pos,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_pos, seq_pos, source);
- do_update(seq_pos, seq_pos, source.begin(), source.end());
- }
-
- void clear() {
- unshare();
- do_update(get_curves().begin(), get_curves().end()-1,
- get_curves().begin(), get_curves().begin());
- }
-
- void erase(iterator const &pos, Stitching stitching=NO_STITCHING) {
- unshare();
- Sequence::iterator seq_pos(seq_iter(pos));
- if (stitching) {
- Sequence stitched;
- stitch(seq_pos, seq_pos+1, stitched);
- do_update(seq_pos, seq_pos+1, stitched.begin(), stitched.end());
- } else {
- do_update(seq_pos, seq_pos+1, get_curves().begin(), get_curves().begin());
- }
- }
-
- void erase(iterator const &first,
- iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first=seq_iter(first);
- Sequence::iterator seq_last=seq_iter(last);
- if (stitching) {
- Sequence stitched;
- stitch(seq_first, seq_last, stitched);
- do_update(seq_first, seq_last, stitched.begin(), stitched.end());
- } else {
- do_update(seq_first, seq_last,
- get_curves().begin(), get_curves().begin());
- }
- }
-
- // erase last segment of path
- void erase_last() {
- erase(iterator(*this, size()-1));
- }
-
- void replace(iterator const &replaced,
- Curve const &curve,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_replaced(seq_iter(replaced));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_replaced, seq_replaced+1, source);
- do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
- }
-
- void replace(iterator const &first_replaced,
- iterator const &last_replaced,
- Curve const &curve, Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
- Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
- Sequence source(1, boost::shared_ptr<Curve>(curve.duplicate()));
- if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
- do_update(seq_first_replaced, seq_last_replaced,
- source.begin(), source.end());
- }
-
- void replace(iterator const &replaced,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_replaced(seq_iter(replaced));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_replaced, seq_replaced+1, source);
- do_update(seq_replaced, seq_replaced+1, source.begin(), source.end());
- }
-
- void replace(iterator const &first_replaced,
- iterator const &last_replaced,
- const_iterator const &first,
- const_iterator const &last,
- Stitching stitching=NO_STITCHING)
- {
- unshare();
- Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
- Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
- Sequence source(seq_iter(first), seq_iter(last));
- if (stitching) stitch(seq_first_replaced, seq_last_replaced, source);
- do_update(seq_first_replaced, seq_last_replaced,
- source.begin(), source.end());
- }
-
- void start(Point p) {
- clear();
- final_->setPoint(0, p);
- final_->setPoint(1, p);
- }
-
- Point initialPoint() const { return (*final_)[1]; }
- Point finalPoint() const { return (*final_)[0]; }
-
- void setInitial(Point const& p)
- {
- if ( empty() ) return;
- unshare();
- boost::shared_ptr<Curve> head(front().duplicate());
- head->setInitial(p);
- Sequence::iterator replaced = get_curves().begin();
- Sequence source(1, head);
- do_update(replaced, replaced + 1, source.begin(), source.end());
- }
-
- void setFinal(Point const& p)
- {
- if ( empty() ) return;
- unshare();
- boost::shared_ptr<Curve> tail(back().duplicate());
- tail->setFinal(p);
- Sequence::iterator replaced = get_curves().end() - 2;
- Sequence source(1, tail);
- do_update(replaced, replaced + 1, source.begin(), source.end());
- }
-
- void append(Curve const &curve, Stitching stitching=NO_STITCHING) {
- unshare();
- if (stitching) stitchTo(curve.initialPoint());
- do_append(curve.duplicate());
- }
- void append(D2<SBasis> const &curve, Stitching stitching=NO_STITCHING) {
- unshare();
- if (stitching) stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
- do_append(new SBasisCurve(curve));
- }
- void append(Path const &other, Stitching stitching=NO_STITCHING) {
- insert(end(), other.begin(), other.end(), stitching);
- }
-
- void stitchTo(Point const &p) {
- if (!empty() && finalPoint() != p) {
- unshare();
- do_append(new StitchSegment(finalPoint(), p));
- }
- }
-
-
- /**
- * It is important to note that the coordinates passed to appendNew should be finite!
- * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
- */
-
- template <typename CurveType, typename A>
- void appendNew(A a) {
- unshare();
- do_append(new CurveType(finalPoint(), a));
- }
-
- template <typename CurveType, typename A, typename B>
- void appendNew(A a, B b) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b));
- }
-
- template <typename CurveType, typename A, typename B, typename C>
- void appendNew(A a, B b, C c) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D>
- void appendNew(A a, B b, C c, D d) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E>
- void appendNew(A a, B b, C c, D d, E e) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F>
- void appendNew(A a, B b, C c, D d, E e, F f) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G>
- void appendNew(A a, B b, C c, D d, E e, F f, G g) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G, typename H>
- void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
- }
-
- template <typename CurveType, typename A, typename B, typename C,
- typename D, typename E, typename F,
- typename G, typename H, typename I>
- void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
- unshare();
- do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
- }
+ for (Iter i = first; i != last; ++i) {
+ _curves->push_back(i->duplicate());
+ }
+ if (!_curves->empty()) {
+ _closing_seg = new ClosingSegment(_curves->back().finalPoint(),
+ _curves->front().initialPoint());
+ } else {
+ _closing_seg = new ClosingSegment();
+ }
+ _curves->push_back(_closing_seg);
+ }
+
+ /// Create a path from a rectangle.
+ explicit Path(Rect const &r);
+ /// Create a path from a convex hull.
+ explicit Path(ConvexHull const &);
+ /// Create a path from a circle, using two elliptical arcs.
+ explicit Path(Circle const &c);
+ /// Create a path from an ellipse, using two elliptical arcs.
+ explicit Path(Ellipse const &e);
+
+ virtual ~Path() {}
+
+ // Path &operator=(Path const &other) - use default assignment operator
+
+ /** @brief Swap contents with another path
+ * @todo Add noexcept specifiers for C++11 */
+ void swap(Path &other) throw() {
+ using std::swap;
+ swap(other._curves, _curves);
+ swap(other._closing_seg, _closing_seg);
+ swap(other._closed, _closed);
+ }
+ /** @brief Swap contents of two paths.
+ * @relates Path */
+ friend inline void swap(Path &a, Path &b) throw() { a.swap(b); }
+
+ /** @brief Access a curve by index */
+ Curve const &operator[](size_type i) const { return (*_curves)[i]; }
+ /** @brief Access a curve by index */
+ Curve const &at(size_type i) const { return _curves->at(i); }
+
+ /** @brief Access the first curve in the path.
+ * Since the curve always contains at least a degenerate closing segment,
+ * it is always safe to use this method. */
+ Curve const &front() const { return _curves->front(); }
+ /// Alias for front().
+ Curve const &initialCurve() const { return _curves->front(); }
+ /** @brief Access the last curve in the path. */
+ Curve const &back() const { return back_default(); }
+ Curve const &back_open() const {
+ if (empty()) return _curves->back();
+ return (*_curves)[_curves->size() - 2];
+ }
+ Curve const &back_closed() const {
+ return _closing_seg->isDegenerate()
+ ? (*_curves)[_curves->size() - 2]
+ : (*_curves)[_curves->size() - 1];
+ }
+ Curve const &back_default() const {
+ return _includesClosingSegment()
+ ? back_closed()
+ : back_open();
+ }
+ Curve const &finalCurve() const { return back_default(); }
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return end_default(); }
+ const_iterator end_default() const { return const_iterator(*this, size_default()); }
+ const_iterator end_open() const { return const_iterator(*this, size_open()); }
+ const_iterator end_closed() const { return const_iterator(*this, size_closed()); }
+ iterator begin() { return iterator(*this, 0); }
+ iterator end() { return end_default(); }
+ iterator end_default() { return iterator(*this, size_default()); }
+ iterator end_open() { return iterator(*this, size_open()); }
+ iterator end_closed() { return iterator(*this, size_closed()); }
+
+ /// Size without the closing segment, even if the path is closed.
+ size_type size_open() const { return _curves->size() - 1; }
+
+ /** @brief Size with the closing segment, if it makes a difference.
+ * If the closing segment is degenerate, i.e. its initial and final points
+ * are exactly equal, then it is not included in this size. */
+ size_type size_closed() const {
+ return _closing_seg->isDegenerate() ? _curves->size() - 1 : _curves->size();
+ }
+
+ /// Natural size of the path.
+ size_type size_default() const {
+ return _includesClosingSegment() ? size_closed() : size_open();
+ }
+ /// Natural size of the path.
+ size_type size() const { return size_default(); }
+
+ size_type max_size() const { return _curves->max_size() - 1; }
+
+ /** @brief Check whether path is empty.
+ * The path is empty if it contains only the closing segment, which according
+ * to the continuity invariant must be degenerate. Note that unlike standard
+ * containers, two empty paths are not necessarily identical, because the
+ * degenerate closing segment may be at a different point, affecting the operation
+ * of methods such as appendNew(). */
+ bool empty() const { return (_curves->size() == 1); }
+
+ /// Check whether the path is closed.
+ bool closed() const { return _closed; }
+
+ /** @brief Set whether the path is closed.
+ * When closing a path where the last segment can be represented as a closing
+ * segment, the last segment will be removed. When opening a path, the closing
+ * segment will be erased. This means that closing and then opening a path
+ * will not always give back the original path. */
+ void close(bool closed = true);
+
+ /** @brief Remove all curves from the path.
+ * The initial and final points of the closing segment are set to (0,0).
+ * The stitching flag remains unchanged. */
+ void clear();
+
+ /** @brief Get the approximate bounding box.
+ * The rectangle returned by this method will contain all the curves, but it's not
+ * guaranteed to be the smallest possible one */
+ OptRect boundsFast() const;
+
+ /** @brief Get a tight-fitting bounding box.
+ * This will return the smallest possible axis-aligned rectangle containing
+ * all the curves in the path. */
+ OptRect boundsExact() const;
+
+ Piecewise<D2<SBasis> > toPwSb() const;
+
+ /// Test paths for exact equality.
+ bool operator==(Path const &other) const;
+
+ /// Apply a transform to each curve.
+ template <typename T>
+ Path &operator*=(T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ _unshare();
+ for (std::size_t i = 0; i < _curves->size(); ++i) {
+ (*_curves)[i] *= tr;
+ }
+ return *this;
+ }
+
+ template <typename T>
+ friend Path operator*(Path const &path, T const &tr) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
+ Path result(path);
+ result *= tr;
+ return result;
+ }
+
+ /** @brief Get the allowed range of time values.
+ * @return Values for which pointAt() and valueAt() yield valid results. */
+ Interval timeRange() const;
+
+ /** Get the curve at the specified time value.
+ * @param t Time value
+ * @param rest Optional storage for the corresponding time value in the curve */
+ Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+
+ /// Get the closing segment of the path.
+ LineSegment const &closingSegment() const { return *_closing_seg; }
+
+ /** @brief Get the point at the specified time value.
+ * Note that this method has reduced precision with respect to calling pointAt()
+ * directly on the curve. If you want high precision results, use the version
+ * that takes a PathTime parameter.
+ *
+ * Allowed time values range from zero to the number of curves; you can retrieve
+ * the allowed range of values with timeRange(). */
+ Point pointAt(Coord t) const;
+
+ /// Get one coordinate (X or Y) at the specified time value.
+ Coord valueAt(Coord t, Dim2 d) const;
+
+ /// Get the curve at the specified path time.
+ Curve const &curveAt(PathTime const &pos) const;
+ /// Get the point at the specified path time.
+ Point pointAt(PathTime const &pos) const;
+ /// Get one coordinate at the specified path time.
+ Coord valueAt(PathTime const &pos, Dim2 d) const;
+
+ Point operator()(Coord t) const { return pointAt(t); }
+
+ /// Compute intersections with axis-aligned line.
+ std::vector<PathTime> roots(Coord v, Dim2 d) const;
+
+ /// Compute intersections with another path.
+ std::vector<PathIntersection> intersect(Path const &other, Coord precision = EPSILON) const;
+
+ /** @brief Determine the winding number at the specified point.
+ *
+ * The winding number is the number of full turns made by a ray that connects the passed
+ * point and the path's value (i.e. the result of the pointAt() method) as the time increases
+ * from 0 to the maximum valid value. Positive numbers indicate turns in the direction
+ * of increasing angles.
+ *
+ * Winding numbers are often used as the definition of what is considered "inside"
+ * the shape. Typically points with either nonzero winding or odd winding are
+ * considered to be inside the path. */
+ int winding(Point const &p) const;
+
+ std::vector<Coord> allNearestTimes(Point const &p, Coord from, Coord to) const;
+ std::vector<Coord> allNearestTimes(Point const &p) const {
+ return allNearestTimes(p, 0, size_default());
+ }
+
+ PathTime nearestTime(Point const &p, Coord *dist = NULL) const;
+ std::vector<Coord> nearestTimePerCurve(Point const &p) const;
+
+ void appendPortionTo(Path &p, Coord f, Coord t) const;
+
+ /** @brief Append a subset of this path to another path.
+ * An extra stitching segment will be inserted if the start point of the portion
+ * and the final point of the target path do not match exactly.
+ * The closing segment of the target path will be modified. */
+ void appendPortionTo(Path &p, PathTime const &from, PathTime const &to, bool cross_start = false) const {
+ PathInterval ival(from, to, cross_start, size_closed());
+ appendPortionTo(p, ival, boost::none, boost::none);
+ }
+
+ /** @brief Append a subset of this path to another path.
+ * This version allows you to explicitly pass a PathInterval. */
+ void appendPortionTo(Path &p, PathInterval const &ival) const {
+ appendPortionTo(p, ival, boost::none, boost::none);
+ }
+
+ /** @brief Append a subset of this path to another path, specifying endpoints.
+ * This method is for use in situations where endpoints of the portion segments
+ * have to be set exactly, for instance when computing Boolean operations. */
+ void appendPortionTo(Path &p, PathInterval const &ival,
+ boost::optional<Point> const &p_from, boost::optional<Point> const &p_to) const;
+
+ Path portion(Coord f, Coord t) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, f, t);
+ return ret;
+ }
+
+ Path portion(Interval const &i) const { return portion(i.min(), i.max()); }
+
+ /** @brief Get a subset of the current path with full precision.
+ * When @a from is larger (later in the path) than @a to, the returned portion
+ * will be reversed. If @a cross_start is true, the portion will be reversed
+ * and will cross the initial point of the path. Therefore, when @a from is larger
+ * than @a to and @a cross_start is true, the returned portion will not be reversed,
+ * but will "wrap around" the end of the path. */
+ Path portion(PathTime const &from, PathTime const &to, bool cross_start = false) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, from, to, cross_start);
+ return ret;
+ }
+
+ /** @brief Get a subset of the current path with full precision.
+ * This version allows you to explicitly pass a PathInterval. */
+ Path portion(PathInterval const &ival) const {
+ Path ret;
+ ret.close(false);
+ appendPortionTo(ret, ival);
+ return ret;
+ }
+
+ /** @brief Obtain a reversed version of the current path.
+ * The final point of the current path will become the initial point
+ * of the reversed path, unless it is closed and has a non-degenerate
+ * closing segment. In that case, the new initial point will be the final point
+ * of the last "real" segment. */
+ Path reversed() const;
+
+ void insert(iterator pos, Curve const &curve);
+
+ template <typename Iter>
+ void insert(iterator pos, Iter first, Iter last) {
+ _unshare();
+ Sequence::iterator seq_pos(seq_iter(pos));
+ Sequence source;
+ for (; first != last; ++first) {
+ source.push_back(first->duplicate());
+ }
+ do_update(seq_pos, seq_pos, source);
+ }
+
+ void erase(iterator pos);
+ void erase(iterator first, iterator last);
+
+ // erase last segment of path
+ void erase_last() { erase(iterator(*this, size() - 1)); }
+
+ void start(Point const &p);
+
+ /** @brief Get the first point in the path. */
+ Point initialPoint() const { return (*_closing_seg)[1]; }
+
+ /** @brief Get the last point in the path.
+ * If the path is closed, this is always the same as the initial point. */
+ Point finalPoint() const { return (*_closing_seg)[_closed ? 1 : 0]; }
+
+ void setInitial(Point const &p) {
+ _unshare();
+ _closed = false;
+ _curves->front().setInitial(p);
+ _closing_seg->setFinal(p);
+ }
+ void setFinal(Point const &p) {
+ _unshare();
+ _closed = false;
+ (*_curves)[size_open() - 1].setFinal(p);
+ _closing_seg->setInitial(p);
+ }
+
+ /** @brief Add a new curve to the end of the path.
+ * This inserts the new curve right before the closing segment.
+ * The path takes ownership of the passed pointer, which should not be freed. */
+ void append(Curve *curve) {
+ _unshare();
+ stitchTo(curve->initialPoint());
+ do_append(curve);
+ }
+
+ void append(Curve const &curve) {
+ _unshare();
+ stitchTo(curve.initialPoint());
+ do_append(curve.duplicate());
+ }
+ void append(D2<SBasis> const &curve) {
+ _unshare();
+ stitchTo(Point(curve[X][0][0], curve[Y][0][0]));
+ do_append(new SBasisCurve(curve));
+ }
+ void append(Path const &other) {
+ replace(end_open(), other.begin(), other.end());
+ }
+
+ void replace(iterator replaced, Curve const &curve);
+ void replace(iterator first, iterator last, Curve const &curve);
+ void replace(iterator replaced, Path const &path);
+ void replace(iterator first, iterator last, Path const &path);
+
+ template <typename Iter>
+ void replace(iterator replaced, Iter first, Iter last) {
+ replace(replaced, replaced + 1, first, last);
+ }
+
+ template <typename Iter>
+ void replace(iterator first_replaced, iterator last_replaced, Iter first, Iter last) {
+ _unshare();
+ Sequence::iterator seq_first_replaced(seq_iter(first_replaced));
+ Sequence::iterator seq_last_replaced(seq_iter(last_replaced));
+ Sequence source;
+ for (; first != last; ++first) {
+ source.push_back(first->duplicate());
+ }
+ do_update(seq_first_replaced, seq_last_replaced, source);
+ }
+
+ /** @brief Append a new curve to the path.
+ *
+ * This family of methods will automaticaly use the current final point of the path
+ * as the first argument of the new curve's constructor. To call this method,
+ * you'll need to write e.g.:
+ * @code
+ path.template appendNew<CubicBezier>(control1, control2, end_point);
+ @endcode
+ * It is important to note that the coordinates passed to appendNew should be finite!
+ * If one of the coordinates is infinite, 2geom will throw a ContinuityError exception.
+ */
+ template <typename CurveType, typename A>
+ void appendNew(A a) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a));
+ }
+
+ template <typename CurveType, typename A, typename B>
+ void appendNew(A a, B b) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C>
+ void appendNew(A a, B b, C c) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D>
+ void appendNew(A a, B b, C c, D d) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E>
+ void appendNew(A a, B b, C c, D d, E e) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F>
+ void appendNew(A a, B b, C c, D d, E e, F f) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
+ typename H>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h));
+ }
+
+ template <typename CurveType, typename A, typename B, typename C, typename D, typename E, typename F, typename G,
+ typename H, typename I>
+ void appendNew(A a, B b, C c, D d, E e, F f, G g, H h, I i) {
+ _unshare();
+ do_append(new CurveType(finalPoint(), a, b, c, d, e, f, g, h, i));
+ }
+
+ /** @brief Reduce the closing segment to a point if it's shorter than precision.
+ * Do this by moving the final point. */
+ void snapEnds(Coord precision = EPSILON);
+
+ /// Append a stitching segment ending at the specified point.
+ void stitchTo(Point const &p);
+
+ /** @brief Verify the continuity invariant.
+ * If the path is not contiguous, this will throw a CountinuityError. */
+ void checkContinuity() const;
+
+ /** @brief Enable or disable the throwing of exceptions when stitching discontinuities.
+ * Normally stitching will cause exceptions, but when you are working with unsanitized
+ * curve data, you can disable these exceptions. */
+ void setStitching(bool x) {
+ _exception_on_stitch = !x;
+ }
private:
- static Sequence::iterator seq_iter(iterator const &iter) {
- return iter.path->get_curves().begin() + iter.index;
- }
- static Sequence::const_iterator seq_iter(const_iterator const &iter) {
- return iter.path->get_curves().begin() + iter.index;
- }
-
- Sequence &get_curves() { return *curves_; }
- Sequence const &get_curves() const { return *curves_; }
-
- void unshare() {
- if (!curves_.unique()) {
- curves_ = boost::shared_ptr<Sequence>(new Sequence(*curves_));
- }
- if (!get_curves().back().unique()) {
- final_ = static_cast<ClosingSegment *>(final_->duplicate());
- get_curves().back() = boost::shared_ptr<Curve>(final_);
- }
- }
-
- void stitch(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence &sequence);
-
- void do_update(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last);
-
- // n.b. takes ownership of curve object
- void do_append(Curve *curve);
-
- void check_continuity(Sequence::iterator first_replaced,
- Sequence::iterator last_replaced,
- Sequence::iterator first,
- Sequence::iterator last);
-
- boost::shared_ptr<Sequence> curves_;
- ClosingSegment *final_;
- bool closed_;
-}; // end class Path
-
-inline static Piecewise<D2<SBasis> > paths_to_pw(std::vector<Path> paths) {
- Piecewise<D2<SBasis> > ret = paths[0].toPwSb();
- for(unsigned i = 1; i < paths.size(); i++) {
- ret.concat(paths[i].toPwSb());
- }
- return ret;
-}
+ static Sequence::iterator seq_iter(iterator const &iter) {
+ return iter.path->_curves->begin() + iter.index;
+ }
+ static Sequence::const_iterator seq_iter(const_iterator const &iter) {
+ return iter.path->_curves->begin() + iter.index;
+ }
-inline
-Coord nearest_point(Point const& p, Path const& c)
-{
- return c.nearestPoint(p);
+ // whether the closing segment is part of the path
+ bool _includesClosingSegment() const {
+ return _closed && !_closing_seg->isDegenerate();
+ }
+ void _unshare() {
+ if (!_curves.unique()) {
+ _curves.reset(new Sequence(*_curves));
+ _closing_seg = static_cast<ClosingSegment*>(&_curves->back());
+ }
+ }
+ PathTime _factorTime(Coord t) const;
+
+ void stitch(Sequence::iterator first_replaced, Sequence::iterator last_replaced, Sequence &sequence);
+ void do_update(Sequence::iterator first, Sequence::iterator last, Sequence &source);
+
+ // n.b. takes ownership of curve object
+ void do_append(Curve *curve);
+
+ boost::shared_ptr<Sequence> _curves;
+ ClosingSegment *_closing_seg;
+ bool _closed;
+ bool _exception_on_stitch;
+}; // end class Path
+
+Piecewise<D2<SBasis> > paths_to_pw(PathVector const &paths);
+
+inline Coord nearest_time(Point const &p, Path const &c) {
+ PathTime pt = c.nearestTime(p);
+ return pt.curve_index + pt.t;
}
-} // end namespace Geom
+std::ostream &operator<<(std::ostream &out, Path const &path);
+
+} // end namespace Geom
#endif // LIB2GEOM_SEEN_PATH_H
diff --git a/src/2geom/pathvector.cpp b/src/2geom/pathvector.cpp
index fc0ad75c4..bff201c71 100644
--- a/src/2geom/pathvector.cpp
+++ b/src/2geom/pathvector.cpp
@@ -1,12 +1,11 @@
-/*
- * PathVector - std::vector containing Geom::Path
- * This file provides a set of operations that can be performed on PathVector,
- * e.g. an affine transform.
- *
+/** @file
+ * @brief PathVector - a sequence of subpaths
+ *//*
* Authors:
- * Johan Engelen <goejendaagh@zonnet.nl>
+ * Johan Engelen <goejendaagh@zonnet.nl>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -32,113 +31,205 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_PATHVECTOR_CPP
-#define SEEN_GEOM_PATHVECTOR_CPP
-
-#include <2geom/pathvector.h>
-
-#include <2geom/path.h>
#include <2geom/affine.h>
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+#include <2geom/svg-path-writer.h>
namespace Geom {
-// TODO: see which of these functions can be inlined for optimization
+//PathVector &PathVector::operator+=(PathVector const &other);
+
+PathVector::size_type PathVector::curveCount() const
+{
+ size_type n = 0;
+ for (const_iterator it = begin(); it != end(); ++it) {
+ n += it->size_default();
+ }
+ return n;
+}
+
+void PathVector::reverse(bool reverse_paths)
+{
+ if (reverse_paths) {
+ std::reverse(begin(), end());
+ }
+ for (iterator i = begin(); i != end(); ++i) {
+ *i = i->reversed();
+ }
+}
-/**
- * Reverses all Paths and the order of paths in the vector as well
- **/
-PathVector reverse_paths_and_order (PathVector const & path_in)
+PathVector PathVector::reversed(bool reverse_paths) const
{
- PathVector path_out;
- for (PathVector::const_reverse_iterator it = path_in.rbegin(); it != path_in.rend(); ++it) {
- path_out.push_back( (*it).reverse() );
+ PathVector ret;
+ for (const_iterator i = begin(); i != end(); ++i) {
+ ret.push_back(i->reversed());
+ }
+ if (reverse_paths) {
+ std::reverse(ret.begin(), ret.end());
}
- return path_out;
+ return ret;
}
-OptRect bounds_fast( PathVector const& pv )
+Path &PathVector::pathAt(Coord t, Coord *rest)
+{
+ return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(t, rest));
+}
+Path const &PathVector::pathAt(Coord t, Coord *rest) const
+{
+ PathVectorTime pos = _factorTime(t);
+ if (rest) {
+ *rest = Coord(pos.curve_index) + pos.t;
+ }
+ return at(pos.path_index);
+}
+Curve const &PathVector::curveAt(Coord t, Coord *rest) const
+{
+ PathVectorTime pos = _factorTime(t);
+ if (rest) {
+ *rest = pos.t;
+ }
+ return at(pos.path_index).at(pos.curve_index);
+}
+Coord PathVector::valueAt(Coord t, Dim2 d) const
+{
+ PathVectorTime pos = _factorTime(t);
+ return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d);
+}
+Point PathVector::pointAt(Coord t) const
+{
+ PathVectorTime pos = _factorTime(t);
+ return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+}
+
+OptRect PathVector::boundsFast() const
{
- typedef PathVector::const_iterator const_iterator;
-
OptRect bound;
- if (pv.empty()) return bound;
-
- bound = (pv.begin())->boundsFast();
- for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it)
- {
+ if (empty()) return bound;
+
+ bound = front().boundsFast();
+ for (const_iterator it = ++begin(); it != end(); ++it) {
bound.unionWith(it->boundsFast());
}
return bound;
}
-OptRect bounds_exact( PathVector const& pv )
+OptRect PathVector::boundsExact() const
{
- typedef PathVector::const_iterator const_iterator;
-
OptRect bound;
- if (pv.empty()) return bound;
-
- bound = (pv.begin())->boundsExact();
- for (const_iterator it = ++(pv.begin()); it != pv.end(); ++it)
- {
+ if (empty()) return bound;
+
+ bound = front().boundsExact();
+ for (const_iterator it = ++begin(); it != end(); ++it) {
bound.unionWith(it->boundsExact());
}
return bound;
}
-/* Note: undefined for empty pathvectors or pathvectors with empty paths.
- * */
-boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared)
+void PathVector::snapEnds(Coord precision)
{
- boost::optional<PathVectorPosition> retval;
+ for (std::size_t i = 0; i < size(); ++i) {
+ (*this)[i].snapEnds(precision);
+ }
+}
- double mindsq = infinity();
- unsigned int i = 0;
- for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) {
- double dsq;
- double t = pit->nearestPoint(_point, &dsq);
- //std::cout << t << "," << dsq << std::endl;
- if (dsq < mindsq) {
- mindsq = dsq;
- retval = PathVectorPosition(i, t);
+std::vector<PVIntersection> PathVector::intersect(PathVector const &other, Coord precision) const
+{
+ typedef PathVectorTime PVPos;
+ std::vector<PVIntersection> result;
+ for (std::size_t i = 0; i < size(); ++i) {
+ for (std::size_t j = 0; j < other.size(); ++j) {
+ std::vector<PathIntersection> xs = (*this)[i].intersect(other[j], precision);
+ for (std::size_t k = 0; k < xs.size(); ++k) {
+ PVIntersection pvx(PVPos(i, xs[k].first), PVPos(j, xs[k].second), xs[k].point());
+ result.push_back(pvx);
+ }
}
+ }
+ return result;
+}
- ++i;
+int PathVector::winding(Point const &p) const
+{
+ int wind = 0;
+ for (const_iterator i = begin(); i != end(); ++i) {
+ wind += i->winding(p);
}
+ return wind;
+}
- if (distance_squared) {
- *distance_squared = mindsq;
+boost::optional<PathVectorTime> PathVector::nearestTime(Point const &p, Coord *dist) const
+{
+ boost::optional<PathVectorTime> retval;
+
+ Coord mindist = infinity();
+ for (size_type i = 0; i < size(); ++i) {
+ Coord d;
+ PathTime pos = (*this)[i].nearestTime(p, &d);
+ if (d < mindist) {
+ mindist = d;
+ retval = PathVectorTime(i, pos.curve_index, pos.t);
+ }
+ }
+
+ if (dist) {
+ *dist = mindist;
}
return retval;
}
-std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared)
+std::vector<PathVectorTime> PathVector::allNearestTimes(Point const &p, Coord *dist) const
{
- std::vector<PathVectorPosition> retval;
-
- double mindsq = infinity();
- unsigned int i = 0;
- for (Geom::PathVector::const_iterator pit = path_in.begin(); pit != path_in.end(); ++pit) {
- double dsq;
- double t = pit->nearestPoint(_point, &dsq);
- if (dsq < mindsq) {
- mindsq = dsq;
- retval.push_back(PathVectorPosition(i, t));
+ std::vector<PathVectorTime> retval;
+
+ Coord mindist = infinity();
+ for (size_type i = 0; i < size(); ++i) {
+ Coord d;
+ PathTime pos = (*this)[i].nearestTime(p, &d);
+ if (d < mindist) {
+ mindist = d;
+ retval.clear();
+ }
+ if (d <= mindist) {
+ retval.push_back(PathVectorTime(i, pos.curve_index, pos.t));
}
-
- ++i;
}
- if (distance_squared) {
- *distance_squared = mindsq;
+ if (dist) {
+ *dist = mindist;
}
return retval;
+}
+PathVectorTime PathVector::_factorTime(Coord t) const
+{
+ PathVectorTime ret;
+ Coord rest = 0;
+ ret.t = modf(t, &rest);
+ ret.curve_index = rest;
+ for (; ret.path_index < size(); ++ret.path_index) {
+ unsigned s = _data.at(ret.path_index).size_default();
+ if (s > ret.curve_index) break;
+ // special case for the last point
+ if (s == ret.curve_index && ret.path_index + 1 == size()) {
+ --ret.curve_index;
+ ret.t = 1;
+ break;
+ }
+ ret.curve_index -= s;
+ }
+ return ret;
}
-} // namespace Geom
+std::ostream &operator<<(std::ostream &out, PathVector const &pv)
+{
+ SVGPathWriter wr;
+ wr.feed(pv);
+ out << wr.str();
+ return out;
+}
-#endif // SEEN_GEOM_PATHVECTOR_CPP
+} // namespace Geom
/*
Local Variables:
diff --git a/src/2geom/pathvector.h b/src/2geom/pathvector.h
index e875e915f..108f2aa05 100644
--- a/src/2geom/pathvector.h
+++ b/src/2geom/pathvector.h
@@ -1,13 +1,11 @@
-/**
- * \file
- * \brief PathVector - std::vector containing Geom::Path.
- * This file provides a set of operations that can be performed on PathVector,
- * e.g. an affine transform.
+/** @file
+ * @brief PathVector - a sequence of subpaths
*//*
* Authors:
- * Johan Engelen <goejendaagh@zonnet.nl>
+ * Johan Engelen <j.b.c.engelen@alumnus.utwente.nl>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright 2008 authors
+ * Copyright 2008-2014 authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -36,93 +34,254 @@
#ifndef LIB2GEOM_SEEN_PATHVECTOR_H
#define LIB2GEOM_SEEN_PATHVECTOR_H
+#include <boost/concept/requires.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/range/algorithm/equal.hpp>
#include <2geom/forward.h>
#include <2geom/path.h>
#include <2geom/transforms.h>
namespace Geom {
-typedef std::vector<Geom::Path> PathVector;
+/** @brief Generalized time value in the path vector.
+ *
+ * This class exists because mapping the range of multiple curves onto the same interval
+ * as the curve index, we lose some precision. For instance, a path with 16 curves will
+ * have 4 bits less precision than a path with 1 curve. If you need high precision results
+ * in long paths, use this class and related methods instead of the standard methods
+ * pointAt(), nearestTime() and so on.
+ *
+ * @ingroup Paths */
+struct PathVectorTime
+ : public PathTime
+ , boost::totally_ordered<PathVectorTime>
+{
+ size_type path_index; ///< Index of the path in the vector
-/* general path transformation: */
-inline
-void operator*= (PathVector & path_in, Affine const &m) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= m;
- }
-}
-inline
-PathVector operator*(PathVector const & path_in, Affine const &m) {
- PathVector ret(path_in);
- ret *= m;
- return ret;
-}
+ PathVectorTime() : PathTime(0, 0), path_index(0) {}
+ PathVectorTime(size_type _i, size_type _c, Coord _t)
+ : PathTime(_c, _t), path_index(_i) {}
+ PathVectorTime(size_type _i, PathTime const &pos)
+ : PathTime(pos), path_index(_i) {}
-/* specific path transformations: Translation:
- * This makes it possible to make optimized implementations for Translate transforms */
-inline
-void operator*= (PathVector & path_in, Translate const &m) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= m;
+ bool operator<(PathVectorTime const &other) const {
+ if (path_index < other.path_index) return true;
+ if (path_index == other.path_index) {
+ return static_cast<PathTime const &>(*this) < static_cast<PathTime const &>(other);
+ }
+ return false;
+ }
+ bool operator==(PathVectorTime const &other) const {
+ return path_index == other.path_index
+ && static_cast<PathTime const &>(*this) == static_cast<PathTime const &>(other);
}
-}
-inline
-PathVector operator*(PathVector const & path_in, Translate const &m) {
- PathVector ret(path_in);
- ret *= m;
- return ret;
-}
-/* user friendly approach to Translate transforms: just add an offset Point to the whole path */
-inline
-void operator+=(PathVector &path_in, Point const &p) {
- for(PathVector::iterator it = path_in.begin(); it != path_in.end(); ++it) {
- (*it) *= Translate(p);
+ PathTime const &asPathTime() const {
+ return *static_cast<PathTime const *>(this);
}
-}
-inline
-PathVector operator+(PathVector const &path_in, Point const &p) {
- PathVector ret(path_in);
- ret *= Translate(p);
- return ret;
-}
+};
-inline
-Geom::Point initialPoint(PathVector const &path_in)
-{
- return path_in.front().initialPoint();
+inline std::ostream &operator<<(std::ostream &os, PathVectorTime const &pvt) {
+ os << pvt.path_index << ": " << pvt.asPathTime();
+ return os;
}
-inline
-Geom::Point finalPoint(PathVector const &path_in)
+typedef Intersection<PathVectorTime> PathVectorIntersection;
+typedef PathVectorIntersection PVIntersection; ///< Alias to save typing
+
+template <>
+struct ShapeTraits<PathVector> {
+ typedef PathVectorTime TimeType;
+ //typedef PathVectorInterval IntervalType;
+ typedef PathVector AffineClosureType;
+ typedef PathVectorIntersection IntersectionType;
+};
+
+/** @brief Sequence of subpaths.
+ *
+ * This class corresponds to the SVG notion of a path:
+ * a sequence of any number of open or closed contiguous subpaths.
+ * Unlike Path, this class is closed under boolean operations.
+ *
+ * If you want to represent an arbitrary shape, this is the best class to use.
+ * Shapes with a boundary that is composed of only a single contiguous
+ * component can be represented with Path instead.
+ *
+ * @ingroup Paths
+ */
+class PathVector
+ : MultipliableNoncommutative< PathVector, Affine
+ , MultipliableNoncommutative< PathVector, Translate
+ , MultipliableNoncommutative< PathVector, Scale
+ , MultipliableNoncommutative< PathVector, Rotate
+ , MultipliableNoncommutative< PathVector, HShear
+ , MultipliableNoncommutative< PathVector, VShear
+ , MultipliableNoncommutative< PathVector, Zoom
+ , boost::equality_comparable< PathVector
+ > > > > > > > >
{
- return path_in.back().finalPoint();
-}
+ typedef std::vector<Path> Sequence;
+public:
+ typedef PathVectorTime Position;
+ typedef Sequence::iterator iterator;
+ typedef Sequence::const_iterator const_iterator;
+ typedef Sequence::size_type size_type;
+ typedef Path value_type;
+ typedef Path &reference;
+ typedef Path const &const_reference;
+ typedef Path *pointer;
+ typedef std::ptrdiff_t difference_type;
-PathVector reverse_paths_and_order (PathVector const & path_in);
-
-OptRect bounds_fast( PathVector const & pv );
-OptRect bounds_exact( PathVector const & pv );
-
-struct PathVectorPosition {
- // pathvector[path_nr].pointAt(t) is the position
- unsigned int path_nr;
- double t;
- PathVectorPosition() :
- path_nr(0),
- t(0)
- {}
- PathVectorPosition(unsigned int path_nr,
- double t) : path_nr(path_nr), t(t) {}
+ PathVector() {}
+ PathVector(Path const &p)
+ : _data(1, p)
+ {}
+ template <typename InputIter>
+ PathVector(InputIter first, InputIter last)
+ : _data(first, last)
+ {}
+
+ /// Check whether the vector contains any paths.
+ bool empty() const { return _data.empty(); }
+ /// Get the number of paths in the vector.
+ size_type size() const { return _data.size(); }
+ /// Get the total number of curves in the vector.
+ size_type curveCount() const;
+
+ iterator begin() { return _data.begin(); }
+ iterator end() { return _data.end(); }
+ const_iterator begin() const { return _data.begin(); }
+ const_iterator end() const { return _data.end(); }
+ Path &operator[](size_type index) {
+ return _data[index];
+ }
+ Path const &operator[](size_type index) const {
+ return _data[index];
+ }
+ Path &at(size_type index) {
+ return _data.at(index);
+ }
+ Path const &at(size_type index) const {
+ return _data.at(index);
+ }
+ Path &front() { return _data.front(); }
+ Path const &front() const { return _data.front(); }
+ Path &back() { return _data.back(); }
+ Path const &back() const { return _data.back(); }
+ /// Append a path at the end.
+ void push_back(Path const &path) {
+ _data.push_back(path);
+ }
+ /// Remove the last path.
+ void pop_back() {
+ _data.pop_back();
+ }
+ iterator insert(iterator pos, Path const &p) {
+ return _data.insert(pos, p);
+ }
+ template <typename InputIter>
+ void insert(iterator out, InputIter first, InputIter last) {
+ _data.insert(out, first, last);
+ }
+ /// Remove a path from the vector.
+ iterator erase(iterator i) {
+ return _data.erase(i);
+ }
+ /// Remove a range of paths from the vector.
+ iterator erase(iterator first, iterator last) {
+ return _data.erase(first, last);
+ }
+ /// Remove all paths from the vector.
+ void clear() { _data.clear(); }
+ /** @brief Change the number of paths.
+ * If the vector size increases, it is passed with paths that contain only
+ * a degenerate closing segment at (0,0). */
+ void resize(size_type n) { _data.resize(n); }
+ /** @brief Reverse the direction of paths in the vector.
+ * @param reverse_paths If this is true, the order of paths is reversed as well;
+ * otherwise each path is reversed, but their order in the
+ * PathVector stays the same */
+ void reverse(bool reverse_paths = true);
+ /** @brief Get a new vector with reversed direction of paths.
+ * @param reverse_paths If this is true, the order of paths is reversed as well;
+ * otherwise each path is reversed, but their order in the
+ * PathVector stays the same */
+ PathVector reversed(bool reverse_paths = true) const;
+
+ /// Get the range of allowed time values.
+ Interval timeRange() const {
+ Interval ret(0, curveCount()); return ret;
+ }
+ /** @brief Get the first point in the first path of the vector.
+ * This method will throw an exception if the vector doesn't contain any paths. */
+ Point initialPoint() const {
+ return _data.front().initialPoint();
+ }
+ /** @brief Get the last point in the last path of the vector.
+ * This method will throw an exception if the vector doesn't contain any paths. */
+ Point finalPoint() const {
+ return _data.back().finalPoint();
+ }
+ Path &pathAt(Coord t, Coord *rest = NULL);
+ Path const &pathAt(Coord t, Coord *rest = NULL) const;
+ Curve const &curveAt(Coord t, Coord *rest = NULL) const;
+ Coord valueAt(Coord t, Dim2 d) const;
+ Point pointAt(Coord t) const;
+
+ Path &pathAt(PathVectorTime const &pos) {
+ return const_cast<Path &>(static_cast<PathVector const*>(this)->pathAt(pos));
+ }
+ Path const &pathAt(PathVectorTime const &pos) const {
+ return at(pos.path_index);
+ }
+ Curve const &curveAt(PathVectorTime const &pos) const {
+ return at(pos.path_index).at(pos.curve_index);
+ }
+ Point pointAt(PathVectorTime const &pos) const {
+ return at(pos.path_index).at(pos.curve_index).pointAt(pos.t);
+ }
+ Coord valueAt(PathVectorTime const &pos, Dim2 d) const {
+ return at(pos.path_index).at(pos.curve_index).valueAt(pos.t, d);
+ }
+
+ OptRect boundsFast() const;
+ OptRect boundsExact() const;
+
+ template <typename T>
+ BOOST_CONCEPT_REQUIRES(((TransformConcept<T>)), (PathVector &))
+ operator*=(T const &t) {
+ if (empty()) return *this;
+ for (iterator i = begin(); i != end(); ++i) {
+ *i *= t;
+ }
+ return *this;
+ }
+
+ bool operator==(PathVector const &other) const {
+ return boost::range::equal(_data, other._data);
+ }
+
+ void snapEnds(Coord precision = EPSILON);
+
+ std::vector<PVIntersection> intersect(PathVector const &other, Coord precision = EPSILON) const;
+
+ /** @brief Determine the winding number at the specified point.
+ * This is simply the sum of winding numbers for constituent paths. */
+ int winding(Point const &p) const;
+
+ boost::optional<PathVectorTime> nearestTime(Point const &p, Coord *dist = NULL) const;
+ std::vector<PathVectorTime> allNearestTimes(Point const &p, Coord *dist = NULL) const;
+
+private:
+ PathVectorTime _factorTime(Coord t) const;
+
+ Sequence _data;
};
-boost::optional<PathVectorPosition> nearestPoint(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
-std::vector<PathVectorPosition> allNearestPoints(PathVector const & path_in, Point const& _point, double *distance_squared = NULL);
+inline OptRect bounds_fast(PathVector const &pv) { return pv.boundsFast(); }
+inline OptRect bounds_exact(PathVector const &pv) { return pv.boundsExact(); }
-inline
-Point pointAt(PathVector const & path_in, PathVectorPosition const &pvp) {
- return path_in[pvp.path_nr].pointAt(pvp.t);
-}
+std::ostream &operator<<(std::ostream &out, PathVector const &pv);
} // end namespace Geom
diff --git a/src/2geom/piecewise.h b/src/2geom/piecewise.h
index ab8417254..a5a65a9fe 100644
--- a/src/2geom/piecewise.h
+++ b/src/2geom/piecewise.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Piecewise function class
- *
+/** @file
+ * @brief Piecewise function class
+ *//*
* Copyright 2007 Michael Sloan <mgsloan@gmail.com>
*
* This library is free software; you can redistribute it and/or
@@ -29,8 +28,8 @@
*
*/
-#ifndef SEEN_GEOM_PW_SB_H
-#define SEEN_GEOM_PW_SB_H
+#ifndef LIB2GEOM_SEEN_PIECEWISE_H
+#define LIB2GEOM_SEEN_PIECEWISE_H
#include <vector>
#include <map>
@@ -43,12 +42,14 @@
namespace Geom {
/**
- * %Piecewise function class.
+ * @brief Function defined as discrete pieces.
+ *
* The Piecewise class manages a sequence of elements of a type as segments and
* the ’cuts’ between them. These cuts are time values which separate the pieces.
* This function representation allows for more interesting functions, as it provides
* a viable output for operations such as inversion, which may require multiple
* SBasis to properly invert the original.
+ *
* As for technical details, while the actual SBasis segments begin on the ïŹrst
* cut and end on the last, the function is deïŹned throughout all inputs by ex-
* tending the ïŹrst and last segments. The exact switching between segments is
@@ -63,6 +64,8 @@ namespace Geom {
* s_n,& c_n <= t
* \end{array}\right.
* \f]
+ *
+ * @ingroup Fragments
*/
template <typename T>
class Piecewise {
@@ -933,7 +936,7 @@ Piecewise<T> lerp(double t, Piecewise<T> const &a, Piecewise<T> b) {
}
}
-#endif //SEEN_GEOM_PW_SB_H
+#endif //LIB2GEOM_SEEN_PIECEWISE_H
/*
Local Variables:
mode:c++
diff --git a/src/2geom/point-ops.h b/src/2geom/point-ops.h
deleted file mode 100644
index 6f5eab56b..000000000
--- a/src/2geom/point-ops.h
+++ /dev/null
@@ -1,25 +0,0 @@
-//[[[cog
-import operators
-
-setContext("Point", "Matrix", "Point")
-make({'*':'*='}, {'/':'/='})
-apsnd({'*':'/'}, "b.inverse()")
-
-setContext("Point", "double", "Point")
-make({'*=':'*'}, {'/=':'/'}, {'*':'*'}, {'*':'/'})
-
-setContext("Point", "Point", "bool")
-make({'==':'!='})
-
-setContext("Point", "Point", "Point")
-make({'+=':'+', '-=':'-'})
-]]]
-
-**************
-GENERATED CODE
-**************
-If you wish to modify, move function out of generation region and remove the
-cause of its generation.
-*/
-
-//[[[end]]]
diff --git a/src/2geom/point.cpp b/src/2geom/point.cpp
index b0b00b5da..bcdbab429 100644
--- a/src/2geom/point.cpp
+++ b/src/2geom/point.cpp
@@ -35,6 +35,7 @@
#include <assert.h>
#include <math.h>
+#include <2geom/coord.h>
#include <2geom/point.h>
#include <2geom/transforms.h>
@@ -146,8 +147,8 @@ bool is_zero(Point const &p) {
/** @brief True if the point has a length near 1. The are_near() function is used.
* @relates Point */
-bool is_unit_vector(Point const &p) {
- return are_near(L2(p), 1.0);
+bool is_unit_vector(Point const &p, Coord eps) {
+ return are_near(L2(p), 1.0, eps);
}
/** @brief Return the angle between the point and the +X axis.
* @return Angle in \f$(-\pi, \pi]\f$.
@@ -161,7 +162,7 @@ Coord atan2(Point const &p) {
* @return Angle in \f$(-\pi, \pi]\f$.
* @relates Point */
Coord angle_between(Point const &a, Point const &b) {
- return std::atan2(cross(b,a), dot(b,a));
+ return std::atan2(cross(a,b), dot(a,b));
}
/** @brief Create a normalized version of a point.
@@ -230,6 +231,13 @@ Point constrain_angle(Point const &A, Point const &B, unsigned int n, Point cons
return A + dir * Rotate(k * 2.0 * M_PI / (double)n) * L2(diff);
}
+std::ostream &operator<<(std::ostream &out, const Geom::Point &p)
+{
+ out << "(" << format_coord_nice(p[X]) << ", "
+ << format_coord_nice(p[Y]) << ")";
+ return out;
+}
+
} // end namespace Geom
/*
diff --git a/src/2geom/point.h b/src/2geom/point.h
index 23dcfd54f..f2659d351 100644
--- a/src/2geom/point.h
+++ b/src/2geom/point.h
@@ -1,6 +1,5 @@
-/**
- * \file
- * \brief Cartesian point / 2D vector and related operations
+/** @file
+ * @brief Cartesian point / 2D vector and related operations
*//*
* Authors:
* Michael G. Sloan <mgsloan@gmail.com>
@@ -33,8 +32,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_Geom_POINT_H
-#define SEEN_Geom_POINT_H
+#ifndef LIB2GEOM_SEEN_POINT_H
+#define LIB2GEOM_SEEN_POINT_H
#include "config.h"
#include <iostream>
@@ -59,10 +58,14 @@ class Point
, MultipliableNoncommutative< Point, HShear
, MultipliableNoncommutative< Point, VShear
, MultipliableNoncommutative< Point, Zoom
- > > > > > > > > > > // this uses chaining so it looks weird, but works
+ > > > > > > > > > > // base class chaining, see documentation for Boost.Operator
{
Coord _pt[2];
public:
+ typedef Coord D1Value;
+ typedef Coord &D1Reference;
+ typedef Coord const &D1ConstReference;
+
/// @name Create points
/// @{
/** Construct a point on the origin. */
@@ -116,6 +119,11 @@ public:
* @return Length of the vector from origin to this point */
Coord length() const { return hypot(_pt[0], _pt[1]); }
void normalize();
+ Point normalized() const {
+ Point ret(*this);
+ ret.normalize();
+ return ret;
+ }
/** @brief Return a point like this point but rotated -90 degrees.
* If the y axis grows downwards and the x axis grows to the
@@ -223,40 +231,73 @@ public:
}
/// @}
- /** @brief Lexicographical ordering functor. */
- template <Dim2 d> struct LexOrder;
+ /** @brief Lexicographical ordering functor.
+ * @param d The dimension with higher significance */
+ template <Dim2 DIM> struct LexLess;
+ template <Dim2 DIM> struct LexGreater;
+ //template <Dim2 DIM, typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder;
/** @brief Lexicographical ordering functor with runtime dimension. */
- class LexOrderRt {
- public:
- LexOrderRt(Dim2 d) : dim(d) {}
- inline bool operator()(Point const &a, Point const &b);
+ struct LexLessRt {
+ LexLessRt(Dim2 d) : dim(d) {}
+ inline bool operator()(Point const &a, Point const &b) const;
private:
Dim2 dim;
};
-
- friend inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt);
+ struct LexGreaterRt {
+ LexGreaterRt(Dim2 d) : dim(d) {}
+ inline bool operator()(Point const &a, Point const &b) const;
+ private:
+ Dim2 dim;
+ };
+ //template <typename First = std::less<Coord>, typename Second = std::less<Coord> > LexOrder
};
/** @brief Output operator for points.
* Prints out the coordinates.
* @relates Point */
-inline std::ostream &operator<< (std::ostream &out_file, const Geom::Point &in_pnt) {
- out_file << "X: " << in_pnt[X] << " Y: " << in_pnt[Y];
- return out_file;
-}
-
-template<> struct Point::LexOrder<X> {
- bool operator()(Point const &a, Point const &b) {
+std::ostream &operator<<(std::ostream &out, const Geom::Point &p);
+
+template<> struct Point::LexLess<X> {
+ typedef std::less<Coord> Primary;
+ typedef std::less<Coord> Secondary;
+ typedef std::less<Coord> XOrder;
+ typedef std::less<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
return a[X] < b[X] || (a[X] == b[X] && a[Y] < b[Y]);
}
};
-template<> struct Point::LexOrder<Y> {
- bool operator()(Point const &a, Point const &b) {
+template<> struct Point::LexLess<Y> {
+ typedef std::less<Coord> Primary;
+ typedef std::less<Coord> Secondary;
+ typedef std::less<Coord> XOrder;
+ typedef std::less<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
return a[Y] < b[Y] || (a[Y] == b[Y] && a[X] < b[X]);
}
};
-inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) {
- return dim ? Point::LexOrder<Y>()(a, b) : Point::LexOrder<X>()(a, b);
+template<> struct Point::LexGreater<X> {
+ typedef std::greater<Coord> Primary;
+ typedef std::greater<Coord> Secondary;
+ typedef std::greater<Coord> XOrder;
+ typedef std::greater<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
+ return a[X] > b[X] || (a[X] == b[X] && a[Y] > b[Y]);
+ }
+};
+template<> struct Point::LexGreater<Y> {
+ typedef std::greater<Coord> Primary;
+ typedef std::greater<Coord> Secondary;
+ typedef std::greater<Coord> XOrder;
+ typedef std::greater<Coord> YOrder;
+ bool operator()(Point const &a, Point const &b) const {
+ return a[Y] > b[Y] || (a[Y] == b[Y] && a[X] > b[X]);
+ }
+};
+inline bool Point::LexLessRt::operator()(Point const &a, Point const &b) const {
+ return dim ? Point::LexLess<Y>()(a, b) : Point::LexLess<X>()(a, b);
+}
+inline bool Point::LexGreaterRt::operator()(Point const &a, Point const &b) const {
+ return dim ? Point::LexGreater<Y>()(a, b) : Point::LexGreater<X>()(a, b);
}
/** @brief Compute the second (Euclidean) norm of @a p.
@@ -265,8 +306,7 @@ inline bool Point::LexOrderRt::operator()(Point const &a, Point const &b) {
* in a <code>double</code>.
* @return \f$\sqrt{p_X^2 + p_Y^2}\f$
* @relates Point */
-inline Coord L2(Point const &p)
-{
+inline Coord L2(Point const &p) {
return p.length();
}
@@ -274,28 +314,10 @@ inline Coord L2(Point const &p)
* Warning: this can overflow where L2 won't.
* @return \f$p_X^2 + p_Y^2\f$
* @relates Point */
-inline Coord L2sq(Point const &p)
-{
+inline Coord L2sq(Point const &p) {
return p[0]*p[0] + p[1]*p[1];
}
-//IMPL: NearConcept
-/** @brief Nearness predicate for points.
- * True if neither coordinate of @a a is further than @a eps from the corresponding
- * coordinate of @a b.
- * @relates Point */
-inline bool are_near(Point const &a, Point const &b, double const eps=EPSILON)
-{
- return ( are_near(a[X],b[X],eps) && are_near(a[Y],b[Y],eps) );
-}
-
-/** @brief Return a point halfway between the specified ones.
- * @relates Point */
-inline Point middle_point(Point const& P1, Point const& P2)
-{
- return (P1 + P2) / 2;
-}
-
/** @brief Returns p * Geom::rotate_degrees(90), but more efficient.
*
* Angle direction in 2Geom: If you use the traditional mathematics convention that y
@@ -305,8 +327,7 @@ inline Point middle_point(Point const& P1, Point const& P2)
*
* There is no function to rotate by -90 degrees: use -rot90(p) instead.
* @relates Point */
-inline Point rot90(Point const &p)
-{
+inline Point rot90(Point const &p) {
return Point(-p[Y], p[X]);
}
@@ -317,9 +338,14 @@ inline Point rot90(Point const &p)
* @return Point on a line between a and b. The ratio of its distance from a
* and the distance between a and b will be equal to t.
* @relates Point */
-inline Point lerp(double const t, Point const &a, Point const &b)
-{
- return (a * (1 - t) + b * t);
+inline Point lerp(Coord t, Point const &a, Point const &b) {
+ return (1 - t) * a + t * b;
+}
+
+/** @brief Return a point halfway between the specified ones.
+ * @relates Point */
+inline Point middle_point(Point const &p1, Point const &p2) {
+ return lerp(0.5, p1, p2);
}
/** @brief Compute the dot product of a and b.
@@ -328,45 +354,60 @@ inline Point lerp(double const t, Point const &a, Point const &b)
* and the sign depends on whether they point in the same direction (+) or opposite ones (-).
* @return \f$a \cdot b = a_X b_X + a_Y b_Y\f$.
* @relates Point */
-inline Coord dot(Point const &a, Point const &b)
-{
- return a[0] * b[0] + a[1] * b[1];
+inline Coord dot(Point const &a, Point const &b) {
+ return a[X] * b[X] + a[Y] * b[Y];
}
/** @brief Compute the 2D cross product.
- * Defined as dot(a, b.cw()). This means it will be zero for parallel vectors,
- * and its absolute value highest for perpendicular vectors.
+ * This is also known as "perp dot product". It will be zero for parallel vectors,
+ * and the absolute value will be highest for perpendicular vectors.
+ * @return \f$a \times b = a_X b_Y - a_Y b_X\f$.
* @relates Point*/
inline Coord cross(Point const &a, Point const &b)
{
- return dot(a, b.cw());
+ // equivalent implementation:
+ // return dot(a, b.ccw());
+ return a[X] * b[Y] - a[Y] * b[X];
}
-/** @brief Compute the (Euclidean) distance between points.
- * @relates Point */
-inline Coord distance (Point const &a, Point const &b)
-{
- return L2(a - b);
+/// Compute the (Euclidean) distance between points.
+/// @relates Point
+inline Coord distance (Point const &a, Point const &b) {
+ return (a - b).length();
}
-/** @brief Compute the square of the distance between points.
- * @relates Point */
-inline Coord distanceSq (Point const &a, Point const &b)
-{
+/// Compute the square of the distance between points.
+/// @relates Point
+inline Coord distanceSq (Point const &a, Point const &b) {
return L2sq(a - b);
}
+//IMPL: NearConcept
+/// Test whether two points are no further apart than some threshold.
+/// @relates Point
+inline bool are_near(Point const &a, Point const &b, double eps = EPSILON) {
+ return are_near(distance(a, b), 0, eps);
+}
+
+/// Test whether three points lie approximately on the same line.
+/// @relates Point
+inline bool are_collinear(Point const& p1, Point const& p2, Point const& p3,
+ double eps = EPSILON)
+{
+ return are_near( cross(p3, p2) - cross(p3, p1) + cross(p2, p1), 0, eps);
+}
+
Point unit_vector(Point const &a);
Coord L1(Point const &p);
Coord LInfty(Point const &p);
bool is_zero(Point const &p);
-bool is_unit_vector(Point const &p);
+bool is_unit_vector(Point const &p, Coord eps = EPSILON);
double atan2(Point const &p);
double angle_between(Point const &a, Point const &b);
Point abs(Point const &b);
Point constrain_angle(Point const &A, Point const &B, unsigned int n = 4, Geom::Point const &dir = Geom::Point(1,0));
-} /* namespace Geom */
+} // end namespace Geom
// This is required to fix a bug in GCC 4.3.3 (and probably others) that causes the compiler
// to try to instantiate the iterator_traits template and fail. Probably it thinks that Point
@@ -375,7 +416,7 @@ namespace std {
template <> class iterator_traits<Geom::Point> {};
}
-#endif /* !SEEN_Geom_POINT_H */
+#endif // LIB2GEOM_SEEN_POINT_H
/*
Local Variables:
diff --git a/src/2geom/poly.cpp b/src/2geom/polynomial.cpp
index de0229172..ca2389f80 100644
--- a/src/2geom/poly.cpp
+++ b/src/2geom/polynomial.cpp
@@ -1,4 +1,41 @@
-#include <2geom/poly.h>
+/**
+ * \file
+ * \brief Polynomial in canonical (monomial) basis
+ *//*
+ * Authors:
+ * MenTaLguY <mental@rydia.net>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2007-2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <algorithm>
+#include <2geom/polynomial.h>
+#include <2geom/math-utils.h>
+#include <math.h>
#ifdef HAVE_GSL
#include <gsl/gsl_poly.h>
@@ -183,6 +220,100 @@ Poly gcd(Poly const &a, Poly const &b, const double /*tol*/) {
+
+std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c)
+{
+ std::vector<Coord> result;
+
+ if (a == 0) {
+ // linear equation
+ if (b == 0) return result;
+ result.push_back(-c/b);
+ return result;
+ }
+
+ Coord delta = b*b - 4*a*c;
+
+ if (delta == 0) {
+ // one root
+ result.push_back(-b / (2*a));
+ } else if (delta > 0) {
+ // two roots
+ Coord delta_sqrt = sqrt(delta);
+
+ // Use different formulas depending on sign of b to preserve
+ // numerical stability. See e.g.:
+ // http://people.csail.mit.edu/bkph/articles/Quadratics.pdf
+ Coord t = -0.5 * (b + sgn(b) * delta_sqrt);
+ result.push_back(t / a);
+ result.push_back(c / t);
+ }
+ // no roots otherwise
+
+ std::sort(result.begin(), result.end());
+ return result;
+}
+
+
+std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d)
+{
+ // based on:
+ // http://mathworld.wolfram.com/CubicFormula.html
+
+ if (a == 0) {
+ return solve_quadratic(b, c, d);
+ }
+ if (d == 0) {
+ // divide by x
+ std::vector<Coord> result = solve_quadratic(a, b, c);
+ result.push_back(0);
+ std::sort(result.begin(), result.end());
+ return result;
+ }
+
+ std::vector<Coord> result;
+
+ // 1. divide everything by a to bring to canonical form
+ b /= a;
+ c /= a;
+ d /= a;
+
+ // 2. eliminate x^2 term: x^3 + 3Qx - 2R = 0
+ Coord Q = (3*c - b*b) / 9;
+ Coord R = (-27 * d + b * (9*c - 2*b*b)) / 54;
+
+ // 3. compute polynomial discriminant
+ Coord D = Q*Q*Q + R*R;
+ Coord term1 = b/3;
+
+ if (D > 0) {
+ // only one real root
+ Coord S = cbrt(R + sqrt(D));
+ Coord T = cbrt(R - sqrt(D));
+ result.push_back(-b/3 + S + T);
+ } else if (D == 0) {
+ // 3 real roots, 2 of which are equal
+ Coord rroot = cbrt(R);
+ result.reserve(3);
+ result.push_back(-term1 + 2*rroot);
+ result.push_back(-term1 - rroot);
+ result.push_back(-term1 - rroot);
+ } else {
+ // 3 distinct real roots
+ assert(Q < 0);
+ Coord theta = acos(R / sqrt(-Q*Q*Q));
+ Coord rroot = 2 * sqrt(-Q);
+ result.reserve(3);
+ result.push_back(-term1 + rroot * cos(theta / 3));
+ result.push_back(-term1 + rroot * cos((theta + 2*M_PI) / 3));
+ result.push_back(-term1 + rroot * cos((theta + 4*M_PI) / 3));
+ }
+
+ std::sort(result.begin(), result.end());
+ return result;
+}
+
+
/*Poly divide_out_root(Poly const & p, double x) {
assert(1);
}*/
diff --git a/src/2geom/poly.h b/src/2geom/polynomial.h
index 7d93d0a85..5ab2aa4c8 100644
--- a/src/2geom/poly.h
+++ b/src/2geom/polynomial.h
@@ -3,9 +3,10 @@
* \brief Polynomial in canonical (monomial) basis
*//*
* Authors:
- * ? <?@?.?>
+ * MenTaLguY <mental@rydia.net>
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
*
- * Copyright ?-? authors
+ * Copyright 2007-2015 Authors
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
@@ -29,7 +30,6 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
- *
*/
#ifndef LIB2GEOM_SEEN_POLY_H
@@ -39,10 +39,13 @@
#include <iostream>
#include <algorithm>
#include <complex>
+#include <2geom/coord.h>
#include <2geom/utils.h>
namespace Geom {
+/** @brief Polynomial in canonical (monomial) basis.
+ * @ingroup Fragments */
class Poly : public std::vector<double>{
public:
// coeff; // sum x^i*coeff[i]
@@ -214,6 +217,18 @@ std::vector<double> solve_reals(const Poly & p);
#endif
double polish_root(Poly const & p, double guess, double tol);
+
+/** @brief Analytically solve quadratic equation.
+ * The equation is given in the standard form: ax^2 + bx + c = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_quadratic(Coord a, Coord b, Coord c);
+
+/** @brief Analytically solve cubic equation.
+ * The equation is given in the standard form: ax^3 + bx^2 + cx + d = 0.
+ * Only real roots are returned. */
+std::vector<Coord> solve_cubic(Coord a, Coord b, Coord c, Coord d);
+
+
inline std::ostream &operator<< (std::ostream &out_file, const Poly &in_poly) {
if(in_poly.size() == 0)
out_file << "0";
diff --git a/src/2geom/quadtree.cpp b/src/2geom/quadtree.cpp
deleted file mode 100644
index 98030c424..000000000
--- a/src/2geom/quadtree.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-#include <2geom/quadtree.h>
-
-namespace Geom{
-Quad* QuadTree::search(Rect const &r) {
- return search(r[0].min(), r[1].min(),
- r[0].max(), r[1].max());
-}
-
-void QuadTree::insert(Rect const &r, int shape) {
- insert(r[0].min(), r[1].min(),
- r[0].max(), r[1].max(), shape);
-}
-
-
-Quad* QuadTree::search(double x0, double y0, double x1, double y1) {
- Quad *q = root;
-
- double bxx0 = bx1, bxx1 = bx1;
- double byy0 = by1, byy1 = by1;
- while(q) {
- double cx = (bxx0 + bxx1)/2;
- double cy = (byy0 + byy1)/2;
- unsigned i = 0;
- if(x0 >= cx) {
- i += 1;
- bxx0 = cx; // zoom in a quad
- } else if(x1 <= cx) {
- bxx1 = cx;
- } else
- break;
- if(y0 >= cy) {
- i += 2;
- byy0 = cy;
- } else if(y1 <= cy) {
- byy1 = cy;
- } else
- break;
-
- assert(i < 4);
- Quad *qq = q->children[i];
- if(qq == 0) break; // last non-null
- q = qq;
- }
- return q;
-}
-
-
-/*
-Comments by Vangelis (use with caution :P )
-
-Insert Rect (x0, y0), (x1, y1) in the QuadTree Q.
-
-===================================================================================
-* QuadTree Q has: Quadtree's Quad root R, QuadTree's bounding box B.
-
-* Each Quad has a Quad::data where we store the id of the Rect that belong to
-this Quad. (In reality we'll store a pointer to the shape).
-
-* Each Quad has 4 Quad children: 0, 1, 2, 3. Each child Quad represents one of the following quarters
-of the bounding box B:
-
-+---------------------+
-| | |
-| NW=0 | NE=1 |
-| | |
-| | |
-+---------------------+
-| | |
-| SW=2 | SE=3 |
-| | |
-| | |
-+---------------------+
-
-Each Quad can further be divided in 4 Quads as above and so on. Below there is an example
-
- Root
- / || \
- / / \ \
- 0 1 2 3
- /\
- / | | \
- 0 1 2 3
-
-+---------------------+
-| | 1-0 | 1-1|
-| 0 | | |
-| |-----|----|
-| | 1-2 | 1-3|
-| | | |
-+---------------------+
-| | |
-| | |
-| 2 | 3 |
-| | |
-+---------------------+
-
-
-
-===================================================================================
-Insert Rect (x0, y0), (x1, y1) in the QuadTree Q. Algorithm:
-1) check if Rect is bigger than QuadTree's bounding box
-2) find in which Quad we should add the Rect:
-
-
-
------------------------------------------------------------------------------------
-How we find in which Quad we should add the Rect R:
-
-Q = Quadtree's Quad root
-B = QuadTree's bounding box B
-WHILE (Q) {
- IF ( Rect cannot fit in one unique quarter of B ){
- Q = current Quad ;
- BREAK;
- }
- IF ( Rect can fit in the quarter I ) {
- IF (Q.children[I] doesn't exist) {
- create the Quad Q.children[I];
- }
- B = bounding box of the Quad Q.children[I] ;
- Q = Q.children[I] ;
- CHECK(R, B) ;
- }
-}
-add Rect R to Q ;
-
-
-*/
-
-void QuadTree::insert(double x0, double y0, double x1, double y1, int shape) {
- // loop until a quad would break the box.
-
- // empty root => empty QuadTree. Create initial bounding box (0,0), (1,1)
- if(root == NULL) {
- root = new Quad;
-
- bx0 = 0;
- bx1 = 1;
- by0 = 0;
- by1 = 1;
- }
- Quad *q = root;
-
- //A temp bounding box. Same as root's bounting box (ie of the whole QuadTree)
- double bxx0 = bx0, bxx1 = bx1;
- double byy0 = by0, byy1 = by1;
-
- while((bxx0 > x0) ||
- (bxx1 < x1) ||
- (byy0 > y0) ||
- (byy1 < y1)) {
- // QuadTree has small size, can't accomodate new rect. Double the size:
- unsigned i = 0;
-
- if(bxx0 > x0) {
- bxx0 = 2*bxx0 - bxx1;
- i += 1;
- } else {
- bxx1 = 2*bxx1 - bxx0;
- }
- if(byy0 > y0) {
- byy0 = 2*byy0 - byy1;
- i += 2;
- } else {
- byy1 = 2*byy1 - byy0;
- }
- q = new Quad;
- //check if root is empty (no rects, no quad children)
- if( clean_root() ){
- root = q;
- }
- else{
- q->children[i] = root;
- root = q;
- }
- bx0 = bxx0;
- bx1 = bxx1;
- by0 = byy0;
- by1 = byy1;
- }
-
- while(true) {
- // Find the center of the temp bounding box
- double cx = (bxx0 + bxx1)/2;
- double cy = (byy0 + byy1)/2;
- unsigned i = 0;
- assert(x0 >= bxx0);
- assert(x1 <= bxx1);
- assert(y0 >= byy0);
- assert(y1 <= byy1);
-
- if(x0 >= cx) {
- i += 1;
- bxx0 = cx; // zoom in a quad
- } else if(x1 <= cx) {
- bxx1 = cx;
- } else{
- // rect does not fit in one unique quarter (in X axis) of the temp bounding box
- break;
- }
- if(y0 >= cy) {
- i += 2;
- byy0 = cy;
- } else if(y1 <= cy) {
- byy1 = cy;
- } else{
- // rect does not fit in one unique quarter (in Y axis) of the temp bounding box
- break;
- }
-
- // check if rect's bounding box has size 1x1. This means that rect is defined by 2 points
- // that are in the same place.
- if( ( fabs(bxx0 - bxx1) < 1.0 ) && ( fabs(byy0 - byy1) < 1.0 )){
- bxx0 = floor(bxx0);
- bxx1 = floor(bxx1);
- byy0 = floor(byy0);
- byy1 = floor(byy1);
- break;
- }
-
- /*
- 1 rect does fit in one unique quarter of the temp bounding box. And we have found which.
- 2 temp bounding box = bounding box of this quarter.
- 3 "Go in" this quarter (create if doesn't exist)
- */
- assert(i < 4);
- Quad *qq = q->children[i];
- if(qq == NULL) {
- qq = new Quad;
- q->children[i] = qq;
- }
- q = qq;
- }
- q->data.push_back(shape);
-}
-
-
-void QuadTree::erase(Quad *q, int shape) {
- for(Quad::iterator i = q->data.begin(); i != q->data.end(); ++i) {
- if(*i == shape) {
- q->data.erase(i);
- if(q->data.empty()) {
-
- }
- }
- }
- return;
-}
-
-/*
-Returns:
-false: if root isn't empty
-true: if root is empty it cleans root
-*/
-bool QuadTree::clean_root() {
- assert(root);
-
- // false if root *has* rects assigned to it.
- bool all_clean = root->data.empty();
-
- // if root has children we get false
- for(unsigned i = 0; i < 4; i++)
- {
- if(root->children[i])
- {
- all_clean = false;
- }
- }
-
- if(all_clean)
- {
- delete root;
- root=0;
- return true;
- }
- return false;
-}
-
-};
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/quadtree.h b/src/2geom/quadtree.h
deleted file mode 100644
index 949a9b898..000000000
--- a/src/2geom/quadtree.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * \file
- * \brief Quad tree data structure
- *//*
- * Authors:
- * ? <?@?.?>
- *
- * Copyright ?-? authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#include <vector>
-#include <cassert>
-
-#include <2geom/d2.h>
-
-namespace Geom{
-
-class Quad{
-public:
- Quad* children[4];
- std::vector<int> data;
- Quad() {
- for(int i = 0; i < 4; i++)
- children[i] = 0;
- }
- typedef std::vector<int>::iterator iterator;
- Rect bounds(unsigned i, double x, double y, double d) {
- double dd = d/2;
- switch(i % 4) {
- case 0:
- return Rect(Interval(x, x+dd), Interval(y, y+dd));
- case 1:
- return Rect(Interval(x+dd, x+d), Interval(y, y+dd));
- case 2:
- return Rect(Interval(x, x+dd), Interval(y+dd, y+d));
- case 3:
- return Rect(Interval(x+dd, x+d), Interval(y+dd, y+d));
- default:
- /* just to suppress warning message
- * this case should be never reached */
- assert(false);
- }
- }
-};
-
-class QuadTree{
-public:
- Quad* root;
- double scale;
- double bx0, bx1;
- double by0, by1;
-
- QuadTree() : root(0), scale(1) {}
-
- Quad* search(double x0, double y0, double x1, double y1);
- void insert(double x0, double y0, double x1, double y1, int shape);
- Quad* search(Geom::Rect const &r);
- void insert(Geom::Rect const &r, int shape);
- void erase(Quad *q, int shape);
-private:
- bool clean_root();
-};
-
-};
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/ray.h b/src/2geom/ray.h
index 2fda06ff5..a336e3132 100644
--- a/src/2geom/ray.h
+++ b/src/2geom/ray.h
@@ -98,7 +98,7 @@ public:
}
return result;
}
- Coord nearestPoint(Point const& point) const {
+ Coord nearestTime(Point const& point) const {
if ( isDegenerate() ) return 0;
double t = dot(point - _origin, _versor);
if (t < 0) t = 0;
@@ -123,7 +123,7 @@ public:
inline
double distance(Point const& _point, Ray const& _ray) {
- double t = _ray.nearestPoint(_point);
+ double t = _ray.nearestTime(_point);
return ::Geom::distance(_point, _ray.pointAt(t));
}
diff --git a/src/2geom/rect.cpp b/src/2geom/rect.cpp
index 0cb842d29..383e72c8e 100644
--- a/src/2geom/rect.cpp
+++ b/src/2geom/rect.cpp
@@ -84,6 +84,17 @@ Coord distance(Point const &p, Rect const &rect)
return hypot(dx, dy);
}
+Coord distanceSq(Point const &p, OptRect const &rect)
+{
+ if (!rect) return std::numeric_limits<Coord>::max();
+ return distanceSq(p, *rect);
+}
+Coord distance(Point const &p, OptRect const &rect)
+{
+ if (!rect) return std::numeric_limits<Coord>::max();
+ return distance(p, *rect);
+}
+
} // namespace Geom
/*
diff --git a/src/2geom/rect.h b/src/2geom/rect.h
index 2516bcfa6..51daf6b5a 100644
--- a/src/2geom/rect.h
+++ b/src/2geom/rect.h
@@ -75,6 +75,10 @@ public:
* @param eps Maximum value of the area to consider empty
* @return True if rectangle has an area smaller than tolerance, false otherwise */
bool hasZeroArea(Coord eps = EPSILON) const { return (area() <= eps); }
+ /// Check whether the rectangle has finite area
+ bool isFinite() const { return (*this)[X].isFinite() && (*this)[Y].isFinite(); }
+ /// Calculate the diameter of the smallest circle that would contain the rectangle.
+ Coord diameter() const { return distance(corner(0), corner(2)); }
/// @}
/// @name Test other rectangles and points for inclusion.
@@ -153,6 +157,10 @@ public:
Coord distanceSq(Point const &p, Rect const &rect);
Coord distance(Point const &p, Rect const &rect);
+/// Minimum square of distance to rectangle, or infinity if empty.
+Coord distanceSq(Point const &p, OptRect const &rect);
+/// Minimum distance to rectangle, or infinity if empty.
+Coord distance(Point const &p, OptRect const &rect);
inline bool Rect::interiorContains(OptRect const &r) const {
return !r || interiorContains(static_cast<Rect const &>(*r));
diff --git a/src/2geom/recursive-bezier-intersection.cpp b/src/2geom/recursive-bezier-intersection.cpp
index 548065196..e86192f54 100644
--- a/src/2geom/recursive-bezier-intersection.cpp
+++ b/src/2geom/recursive-bezier-intersection.cpp
@@ -13,7 +13,6 @@
unsigned intersect_steps = 0;
using std::vector;
-using std::swap;
namespace Geom {
@@ -33,7 +32,7 @@ public:
minax = p[0][X]; // These are the most likely to be extremal
maxax = p.back()[X];
if( minax > maxax )
- swap(minax, maxax);
+ std::swap(minax, maxax);
for(unsigned i = 1; i < p.size()-1; i++) {
if( p[i][X] < minax )
minax = p[i][X];
@@ -44,7 +43,7 @@ public:
minay = p[0][Y]; // These are the most likely to be extremal
maxay = p.back()[Y];
if( minay > maxay )
- swap(minay, maxay);
+ std::swap(minay, maxay);
for(unsigned i = 1; i < p.size()-1; i++) {
if( p[i][Y] < minay )
minay = p[i][Y];
diff --git a/src/2geom/region.cpp b/src/2geom/region.cpp
deleted file mode 100644
index 8cfb1c68c..000000000
--- a/src/2geom/region.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-#include <2geom/region.h>
-#include <2geom/utils.h>
-
-#include <2geom/shape.h>
-
-namespace Geom {
-
-Region Region::operator*(Affine const &m) const {
- Region r((m.flips() ? boundary.reverse() : boundary) * m, fill);
- if(box && m.isZoom()) r.box = (*box) * m;
- return r;
-}
-
-bool Region::invariants() const {
- return self_crossings(boundary).empty();
-}
-
-unsigned outer_index(Regions const &ps) {
- if(ps.size() <= 1 || ps[0].contains(ps[1])) {
- return 0;
- } else {
- /* Since we've already shown that chunks[0] is not outside
- it can be used as an exemplar inner. */
- Point exemplar = Path(ps[0]).initialPoint();
- for(unsigned i = 1; i < ps.size(); i++) {
- if(ps[i].contains(exemplar)) {
- return i;
- }
- }
- }
- return ps.size();
-}
-
-}
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/region.h b/src/2geom/region.h
deleted file mode 100644
index 06a4f63e9..000000000
--- a/src/2geom/region.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * \file
- * \brief Uncrossed path for boolean algorithms
- *//*
- * Authors:
- * ? <?@?.?>
- *
- * Copyright ?-? authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef __2GEOM_REGION_H
-#define __2GEOM_REGION_H
-
-#include <2geom/path.h>
-#include <2geom/path-intersection.h>
-
-namespace Geom {
-
-class Shape;
-
-class Region {
- friend Crossings crossings(Region const &a, Region const &b);
- friend class Shape;
- friend Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs);
-
- Path boundary;
- mutable OptRect box;
- bool fill;
- public:
- Region() : fill(true) {}
- explicit Region(Path const &p) : boundary(p) { fill = path_direction(p); }
- Region(Path const &p, bool dir) : boundary(p), fill(dir) {}
- Region(Path const &p, OptRect const &b) : boundary(p), box(b) { fill = path_direction(p); }
- Region(Path const &p, OptRect const &b, bool dir) : boundary(p), box(b), fill(dir) {}
-
- unsigned size() const { return boundary.size(); }
-
- bool isFill() const { return fill; }
- Region asFill() const { if(fill) return Region(*this); else return inverse(); }
- Region asHole() const { if(fill) return inverse(); else return Region(*this); }
-
- operator Path() const { return boundary; }
- Rect boundsFast() const {
- if(!box) box = boundary.boundsFast(); /// \todo this doesn't look right at all...
- return *box;
- }
-
- bool contains(Point const &p) const {
- if(box && !box->contains(p)) return false;
- return Geom::contains(boundary, p);
- }
- bool contains(Region const &other) const { return contains(other.boundary.initialPoint()); }
-
- bool includes(Point const &p) const {
- return logical_xor(!fill, contains(p));
- }
-
- Region inverse() const { return Region(boundary.reverse(), box, !fill); }
-
- Region operator*(Affine const &m) const;
-
- bool invariants() const;
-};
-
-typedef std::vector<Region> Regions;
-
-unsigned outer_index(Regions const &ps);
-
-//assumes they're already sanitized somewhat
-inline Regions regions_from_paths(std::vector<Path> const &ps) {
- Regions res;
- for(unsigned i = 0; i < ps.size(); i++)
- res.push_back(Region(ps[i]));
- return res;
-}
-
-inline std::vector<Path> paths_from_regions(Regions const &rs) {
- std::vector<Path> res;
- for(unsigned i = 0; i < rs.size(); i++)
- res.push_back(rs[i]);
- return res;
-}
-
-Regions sanitize_path(Path const &p);
-
-Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const &cr);
-Regions region_boolean(bool rev, Region const & a, Region const & b, Crossings const & cr_a, Crossings const & cr_b);
-
-inline Regions region_boolean(bool rev, Region const & a, Region const & b) {
- return region_boolean(rev, a, b, crossings(a, b));
-}
-
-}
-
-#endif
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sbasis-2d.cpp b/src/2geom/sbasis-2d.cpp
index aa5018e9e..53b09cd35 100644
--- a/src/2geom/sbasis-2d.cpp
+++ b/src/2geom/sbasis-2d.cpp
@@ -169,7 +169,7 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B){
std::vector<D2<SBasis> > candidates = cubics_fitting_curvature(A,B,V0,V1,D2fVV0,D2fVV1);
if (candidates.empty()) {
- return D2<SBasis>(Linear(A[X],B[X]),Linear(A[Y],B[Y]));
+ return D2<SBasis>(SBasis(Linear(A[X],B[X])), SBasis(Linear(A[Y],B[Y])));
}
//TODO: I'm sure std algorithm could do that for me...
double error = -1;
diff --git a/src/2geom/sbasis-2d.h b/src/2geom/sbasis-2d.h
index 00429e259..c7d9b000a 100644
--- a/src/2geom/sbasis-2d.h
+++ b/src/2geom/sbasis-2d.h
@@ -33,8 +33,8 @@
*
*/
-#ifndef SEEN_SBASIS_2D_H
-#define SEEN_SBASIS_2D_H
+#ifndef LIB2GEOM_SEEN_SBASIS_2D_H
+#define LIB2GEOM_SEEN_SBASIS_2D_H
#include <vector>
#include <cassert>
#include <algorithm>
@@ -356,8 +356,9 @@ sb2dsolve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B, unsigne
D2<SBasis>
sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
-};
+} // end namespace Geom
+#endif
/*
Local Variables:
mode:c++
@@ -368,4 +369,3 @@ sb2d_cubic_solve(SBasis2d const &f, Geom::Point const &A, Geom::Point const &B);
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-#endif
diff --git a/src/2geom/sbasis-curve.h b/src/2geom/sbasis-curve.h
index a5c3f2ca7..affe7edc0 100644
--- a/src/2geom/sbasis-curve.h
+++ b/src/2geom/sbasis-curve.h
@@ -37,7 +37,7 @@
#define LIB2GEOM_SEEN_SBASIS_CURVE_H
#include <2geom/curve.h>
-#include <2geom/nearest-point.h>
+#include <2geom/nearest-time.h>
#include <2geom/sbasis-geometric.h>
#include <2geom/transforms.h>
@@ -84,11 +84,11 @@ public:
explicit SBasisCurve(D2<SBasis> const &sb) : inner(sb) {}
explicit SBasisCurve(Curve const &other) : inner(other.toSBasis()) {}
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
virtual Curve *duplicate() const { return new SBasisCurve(*this); }
virtual Point initialPoint() const { return inner.at0(); }
virtual Point finalPoint() const { return inner.at1(); }
- virtual bool isDegenerate() const { return inner.isConstant(); }
+ virtual bool isDegenerate() const { return inner.isConstant(0); }
+ virtual bool isLineSegment() const { return inner[X].size() == 1; }
virtual Point pointAt(Coord t) const { return inner.valueAt(t); }
virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned n) const {
return inner.valueAndDerivatives(t, n);
@@ -106,33 +106,34 @@ public:
return bounds_local(inner, i, deg);
}
virtual std::vector<Coord> roots(Coord v, Dim2 d) const { return Geom::roots(inner[d] - v); }
- virtual Coord nearestPoint( Point const& p, Coord from = 0, Coord to = 1 ) const {
- return nearest_point(p, inner, from, to);
+ virtual Coord nearestTime( Point const& p, Coord from = 0, Coord to = 1 ) const {
+ return nearest_time(p, inner, from, to);
}
- virtual std::vector<Coord> allNearestPoints( Point const& p, Coord from = 0,
+ virtual std::vector<Coord> allNearestTimes( Point const& p, Coord from = 0,
Coord to = 1 ) const
{
- return all_nearest_points(p, inner, from, to);
+ return all_nearest_times(p, inner, from, to);
}
virtual Coord length(Coord tolerance) const { return ::Geom::length(inner, tolerance); }
virtual Curve *portion(Coord f, Coord t) const {
return new SBasisCurve(Geom::portion(inner, f, t));
}
- virtual Curve *transformed(Affine const &m) const { return new SBasisCurve(inner * m); }
- virtual Curve &operator*=(Translate const &m) {
- inner += m.vector();
- return *this;
- };
+ using Curve::operator*=;
+ virtual void operator*=(Affine const &m) { inner = inner * m; }
virtual Curve *derivative() const {
return new SBasisCurve(Geom::derivative(inner));
}
virtual D2<SBasis> toSBasis() const { return inner; }
+ virtual bool operator==(Curve const &c) const {
+ SBasisCurve const *other = dynamic_cast<SBasisCurve const *>(&c);
+ if (!other) return false;
+ return inner == other->inner;
+ }
virtual int degreesOfFreedom() const {
return inner[0].degreesOfFreedom() + inner[1].degreesOfFreedom();
}
-#endif
};
} // end namespace Geom
diff --git a/src/2geom/sbasis-geometric.h b/src/2geom/sbasis-geometric.h
index 43c624fb4..7f1e8aaba 100644
--- a/src/2geom/sbasis-geometric.h
+++ b/src/2geom/sbasis-geometric.h
@@ -1,15 +1,6 @@
-#ifndef _SBASIS_GEOMETRIC
-#define _SBASIS_GEOMETRIC
-#include <2geom/d2.h>
-#include <2geom/piecewise.h>
-#include <vector>
-
/**
* \file
* \brief two-dimensional geometric operators.
- *
- * Copyright 2007, JFBarraud
- * Copyright 2007, njh
*
* These operators are built on a more 'polynomially robust'
* transformation to map a function that takes a [0,1] parameter to a
@@ -21,9 +12,42 @@
* from the various tangent directions at each end (angular jet). As
* a result, the final path has a convergence behaviour derived from
* that of the sin and cos series. -- njh
+ *//*
+ * Copyright 2007, JFBarraud
+ * Copyright 2007, njh
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
*/
-namespace Geom{
+#ifndef LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+#define LIB2GEOM_SEEN_SBASIS_GEOMETRIC_H
+
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+#include <vector>
+
+namespace Geom {
Piecewise<D2<SBasis> >
cutAtRoots(Piecewise<D2<SBasis> > const &M, double tol=1e-4);
diff --git a/src/2geom/sbasis-math.cpp b/src/2geom/sbasis-math.cpp
index 97cdf45ce..896eb18a7 100644
--- a/src/2geom/sbasis-math.cpp
+++ b/src/2geom/sbasis-math.cpp
@@ -297,11 +297,11 @@ Piecewise<SBasis> reciprocalOnDomain(Interval range, double tol){
if (a<=tol){
reciprocal_fn.push_cut(0);
int i0=(int) floor(std::log(tol)/std::log(R));
- a=pow(R,i0);
+ a = std::pow(R,i0);
reciprocal_fn.push(Linear(1/a),a);
}else{
int i0=(int) floor(std::log(a)/std::log(R));
- a=pow(R,i0);
+ a = std::pow(R,i0);
reciprocal_fn.cuts.push_back(a);
}
diff --git a/src/2geom/sbasis-math.h b/src/2geom/sbasis-math.h
index e6d40a3de..e191dae62 100644
--- a/src/2geom/sbasis-math.h
+++ b/src/2geom/sbasis-math.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief some std functions to work with (pw)s-basis
- *
+/** @file
+ * @brief some std functions to work with (pw)s-basis
+ *//*
* Authors:
* Jean-Francois Barraud
*
@@ -36,8 +35,8 @@
//TODO: in all these functions, compute 'order' according to 'tol'.
//TODO: use template to define the pw version automatically from the sb version?
-#ifndef SEEN_GEOM_SB_CALCULS_H
-#define SEEN_GEOM_SB_CALCULS_H
+#ifndef LIB2GEOM_SEEN_SBASIS_MATH_H
+#define LIB2GEOM_SEEN_SBASIS_MATH_H
#include <2geom/sbasis.h>
diff --git a/src/2geom/sbasis-poly.h b/src/2geom/sbasis-poly.h
index e0bef9333..d18bc369e 100644
--- a/src/2geom/sbasis-poly.h
+++ b/src/2geom/sbasis-poly.h
@@ -1,13 +1,6 @@
-#ifndef _SBASIS_TO_POLY
-#define _SBASIS_TO_POLY
-
-#include <2geom/poly.h>
-#include <2geom/sbasis.h>
-
-/**
- * \file
- * \brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
- *
+/** @file
+ * @brief Conversion between SBasis and Poly. Not recommended for general use due to instability.
+ *//*
* Authors:
* ? <?@?.?>
*
@@ -35,9 +28,14 @@
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
- *
*/
+#ifndef LIB2GEOM_SEEN_SBASIS_POLY_H
+#define LIB2GEOM_SEEN_SBASIS_POLY_H
+
+#include <2geom/polynomial.h>
+#include <2geom/sbasis.h>
+
namespace Geom{
SBasis poly_to_sbasis(Poly const & p);
@@ -45,6 +43,7 @@ Poly sbasis_to_poly(SBasis const & s);
};
+#endif
/*
Local Variables:
mode:c++
@@ -55,5 +54,3 @@ Poly sbasis_to_poly(SBasis const & s);
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-
-#endif
diff --git a/src/2geom/sbasis-roots.cpp b/src/2geom/sbasis-roots.cpp
index acf4e1abc..57bef4c0f 100644
--- a/src/2geom/sbasis-roots.cpp
+++ b/src/2geom/sbasis-roots.cpp
@@ -1,7 +1,38 @@
-/** root finding for sbasis functions.
- * Copyright 2006 N Hurst
- * Copyright 2007 JF Barraud
+/**
+ * @file
+ * @brief Root finding for sbasis functions.
+ *//*
+ * Authors:
+ * Nathan Hurst <njh@njhurst.com>
+ * JF Barraud
+ * Copyright 2006-2007 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
*
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ */
+
+ /*
* It is more efficient to find roots of f(t) = c_0, c_1, ... all at once, rather than iterating.
*
* Todo/think about:
@@ -98,20 +129,20 @@ OptInterval bounds_fast(const SBasis &sb, int order) {
double b=sb[j][1];
double v, t = 0;
- v = res[0];
+ v = res.min();
if (v<0) t = ((b-a)/v+1)*0.5;
if (v>=0 || t<0 || t>1) {
- res[0] = std::min(a,b);
- }else{
- res[0]=lerp(t, a+v*t, b);
+ res.setMin(std::min(a,b));
+ } else {
+ res.setMin(lerp(t, a+v*t, b));
}
- v = res[1];
+ v = res.max();
if (v>0) t = ((b-a)/v+1)*0.5;
if (v<=0 || t<0 || t>1) {
- res[1] = std::max(a,b);
+ res.setMax(std::max(a,b));
}else{
- res[1]=lerp(t, a+v*t, b);
+ res.setMax(lerp(t, a+v*t, b));
}
}
if (order>0) res*=std::pow(.25,order);
@@ -576,6 +607,7 @@ std::vector<double> roots1(SBasis const & s, Interval const ivl) {
/** Find all t s.t s(t) = 0
\param a sbasis function
+ \see Bezier::roots
\returns vector of zeros (roots)
*/
diff --git a/src/2geom/sbasis-to-bezier.cpp b/src/2geom/sbasis-to-bezier.cpp
index 98c400197..09fbb03ef 100644
--- a/src/2geom/sbasis-to-bezier.cpp
+++ b/src/2geom/sbasis-to-bezier.cpp
@@ -37,7 +37,7 @@
#include <2geom/choose.h>
#include <2geom/path-sink.h>
#include <2geom/exception.h>
-#include <2geom/convex-cover.h>
+#include <2geom/convex-hull.h>
#include <iostream>
@@ -153,6 +153,15 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
bz[n] = sb[0][1];
}
+void sbasis_to_bezier(D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz)
+{
+ if (sz == 0) {
+ sz = std::max(sb[X].size(), sb[Y].size())*2;
+ }
+ sbasis_to_bezier(bz[X], sb[X], sz);
+ sbasis_to_bezier(bz[Y], sb[Y], sz);
+}
+
/** Changes the basis of p to be Bernstein.
\param p the D2 Symmetric basis polynomial
\returns the D2 Bernstein basis polynomial
@@ -161,24 +170,9 @@ void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
*/
void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz)
{
- Bezier bzx, bzy;
- if(sz == 0) {
- sz = std::max(sb[X].size(), sb[Y].size())*2;
- }
- sbasis_to_bezier(bzx, sb[X], sz);
- sbasis_to_bezier(bzy, sb[Y], sz);
- assert(bzx.size() == bzy.size());
- size_t n = (bzx.size() >= bzy.size()) ? bzx.size() : bzy.size();
-
- bz.resize(n, Point(0,0));
- for (size_t i = 0; i < bzx.size(); ++i)
- {
- bz[i][X] = bzx[i];
- }
- for (size_t i = 0; i < bzy.size(); ++i)
- {
- bz[i][Y] = bzy[i];
- }
+ D2<Bezier> bez;
+ sbasis_to_bezier(bez, sb, sz);
+ bz = bezier_points(bez);
}
/** Changes the basis of p to be Bernstein.
@@ -238,15 +232,13 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
// is midpoint in hull: if not, the solution will be ill-conditioned, LP Bug 1428683
- if (!bezhull.contains_point(Geom::Point(midx, midy)))
+ if (!bezhull.contains(Geom::Point(midx, midy)))
return;
// calculate Bezier control arms
midx = 8*midx - 4*bz[0][X] - 4*bz[3][X]; // re-define relative to center
midy = 8*midy - 4*bz[0][Y] - 4*bz[3][Y];
- if ((std::abs(midx) < EPSILON) && (std::abs(midy) < EPSILON))
- return;
if ((std::abs(xprime[0]) < EPSILON) && (std::abs(yprime[0]) < EPSILON)
&& ((std::abs(xprime[1]) > EPSILON) || (std::abs(yprime[1]) > EPSILON))) { // degenerate handle at 0 : use distance of closest approach
@@ -270,11 +262,11 @@ void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
double test2 = (bz[2][Y] - bz[0][Y])*(bz[3][X] - bz[0][X]) - (bz[2][X] - bz[0][X])*(bz[3][Y] - bz[0][Y]);
if (test1*test2 < 0) // reject anti-symmetric case, LP Bug 1428267 & Bug 1428683
return;
- denom = xprime[1]*yprime[0] - yprime[1]*xprime[0];
+ denom = 3.0*(xprime[1]*yprime[0] - yprime[1]*xprime[0]);
for (int i = 0; i < 2; ++i) {
numer = xprime[1 - i]*midy - yprime[1 - i]*midx;
- delx[i] = xprime[i]*numer/denom/3;
- dely[i] = yprime[i]*numer/denom/3;
+ delx[i] = xprime[i]*numer/denom;
+ dely[i] = yprime[i]*numer/denom;
}
} else if ((xprime[0]*xprime[1] < 0) || (yprime[0]*yprime[1] < 0)) { // symmetric case : use distance of closest approach
numer = midx*xprime[0] + midy*yprime[0];
@@ -520,7 +512,7 @@ path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
TODO: some of this logic should be lifted into svg-path
*/
-std::vector<Geom::Path>
+PathVector
path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) {
Geom::PathBuilder pb;
if(B.size() == 0) return pb.peek();
diff --git a/src/2geom/sbasis-to-bezier.h b/src/2geom/sbasis-to-bezier.h
index 07511f4a4..eadb47bff 100644
--- a/src/2geom/sbasis-to-bezier.h
+++ b/src/2geom/sbasis-to-bezier.h
@@ -32,11 +32,11 @@
*
*/
-#ifndef _SBASIS_TO_BEZIER
-#define _SBASIS_TO_BEZIER
+#ifndef LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
+#define LIB2GEOM_SEEN_SBASIS_TO_BEZIER_H
#include <2geom/d2.h>
-#include <2geom/path.h>
+#include <2geom/pathvector.h>
#include <vector>
@@ -44,7 +44,8 @@ namespace Geom {
class PathBuilder;
-void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz = 0);
+void sbasis_to_bezier (Bezier &bz, SBasis const &sb, size_t sz = 0);
+void sbasis_to_bezier (D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz = 0);
void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz = 0);
void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb);
void bezier_to_sbasis (SBasis & sb, Bezier const& bz);
@@ -65,7 +66,7 @@ sbasis_to_bezier(D2<SBasis> const &B, unsigned q = 0);
#endif
-std::vector<Path> path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false);
+PathVector path_from_piecewise(Piecewise<D2<SBasis> > const &B, double tol, bool only_cubicbeziers = false);
Path path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers = false);
inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol)
@@ -73,10 +74,7 @@ inline Path cubicbezierpath_from_sbasis(D2<SBasis> const &B, double tol)
} // end namespace Geom
-
-
#endif
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/sbasis.cpp b/src/2geom/sbasis.cpp
index b56e03c74..4f1df621e 100644
--- a/src/2geom/sbasis.cpp
+++ b/src/2geom/sbasis.cpp
@@ -466,6 +466,15 @@ SBasis compose(SBasis const &a, SBasis const &b, unsigned k) {
return r;
}
+SBasis portion(const SBasis &t, double from, double to) {
+ double fv = t.valueAt(from);
+ double tv = t.valueAt(to);
+ SBasis ret = compose(t, Linear(from, to));
+ ret.at0() = fv;
+ ret.at1() = tv;
+ return ret;
+}
+
/*
Inversion algorithm. The notation is certainly very misleading. The
pseudocode should say:
@@ -632,7 +641,6 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order, double
//TODO: handle det~0!!
if (fabs(det)<zero){
- det = zero;
a=b=0;
}else{
a=( q01*r10-q10*r01)/det;
diff --git a/src/2geom/sbasis.h b/src/2geom/sbasis.h
index ca864ac7c..787e8b722 100644
--- a/src/2geom/sbasis.h
+++ b/src/2geom/sbasis.h
@@ -1,7 +1,6 @@
-/**
- * \file
- * \brief Defines S-power basis function class
- *
+/** @file
+ * @brief Polynomial in symmetric power basis (S-basis)
+ *//*
* Authors:
* Nathan Hurst <njh@mail.csse.monash.edu.au>
* Michael Sloan <mgsloan@gmail.com>
@@ -32,8 +31,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_SBASIS_H
-#define SEEN_SBASIS_H
+#ifndef LIB2GEOM_SEEN_SBASIS_H
+#define LIB2GEOM_SEEN_SBASIS_H
#include <vector>
#include <cassert>
#include <iostream>
@@ -61,13 +60,13 @@ class SBasis : public SBasisN<1>;
};
#else
-namespace Geom{
+namespace Geom {
/**
-* \brief S-power basis function class
-*
-* An empty SBasis is identically 0. */
-class SBasis{
+ * @brief Polynomial in symmetric power basis
+ * @ingroup Fragments
+ */
+class SBasis {
std::vector<Linear> d;
void push_back(Linear const&l) { d.push_back(l); }
@@ -101,11 +100,17 @@ public:
SBasis() {}
- explicit SBasis(double a) {
- push_back(Linear(a,a));
+ explicit SBasis(double a)
+ : d(1)
+ {
+ d[0][0] = a;
+ d[0][1] = a;
}
- explicit SBasis(double a, double b) {
- push_back(Linear(a,b));
+ explicit SBasis(double a, double b)
+ : d(1)
+ {
+ d[0][0] = a;
+ d[0][1] = b;
}
SBasis(SBasis const & a) :
d(a.d)
@@ -117,10 +122,69 @@ public:
push_back(bo);
}
SBasis(Linear* bo) {
- push_back(*bo);
+ if (bo) {
+ push_back(*bo);
+ }
}
explicit SBasis(size_t n, Linear const&l) : d(n, l) {}
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3)
+ : d(2)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[1][1] = c2;
+ d[0][1] = c3;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5)
+ : d(3)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[2][1] = c3;
+ d[1][1] = c4;
+ d[0][1] = c5;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+ Coord c6, Coord c7)
+ : d(4)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[3][0] = c3;
+ d[3][1] = c4;
+ d[2][1] = c5;
+ d[1][1] = c6;
+ d[0][1] = c7;
+ }
+ SBasis(Coord c0, Coord c1, Coord c2, Coord c3, Coord c4, Coord c5,
+ Coord c6, Coord c7, Coord c8, Coord c9)
+ : d(5)
+ {
+ d[0][0] = c0;
+ d[1][0] = c1;
+ d[2][0] = c2;
+ d[3][0] = c3;
+ d[4][0] = c4;
+ d[4][1] = c5;
+ d[3][1] = c6;
+ d[2][1] = c7;
+ d[1][1] = c8;
+ d[0][1] = c9;
+ }
+
+ // construct from a sequence of coefficients
+ template <typename Iter>
+ SBasis(Iter first, Iter last) {
+ assert(std::distance(first, last) % 2 == 0);
+ for (; first != last; ++first) {
+ --last;
+ push_back(Linear(*first, *last));
+ }
+ }
+
//IMPL: FragmentConcept
typedef double output_type;
inline bool isZero(double eps=EPSILON) const {
@@ -140,12 +204,10 @@ public:
}
bool isFinite() const;
- inline double at0() const {
- if(empty()) return 0; else return (*this)[0][0];
- }
- inline double at1() const{
- if(empty()) return 0; else return (*this)[0][1];
- }
+ inline Coord at0() const { return (*this)[0][0]; }
+ inline Coord &at0() { return (*this)[0][0]; }
+ inline Coord at1() const { return (*this)[0][1]; }
+ inline Coord &at1() { return (*this)[0][1]; }
int degreesOfFreedom() const { return size()*2;}
@@ -348,8 +410,8 @@ SBasis compose_inverse(SBasis const &f, SBasis const &g, unsigned order=2, doubl
\return sbasis
\relates SBasis
*/
-inline SBasis portion(const SBasis &t, double from, double to) { return compose(t, Linear(from, to)); }
-inline SBasis portion(const SBasis &t, Interval ivl) { return compose(t, Linear(ivl.min(), ivl.max())); }
+SBasis portion(const SBasis &t, double from, double to);
+inline SBasis portion(const SBasis &t, Interval const &ivl) { return portion(t, ivl.min(), ivl.max()); }
// compute f(g)
inline SBasis
@@ -364,7 +426,10 @@ inline std::ostream &operator<< (std::ostream &out_file, const Linear &bo) {
inline std::ostream &operator<< (std::ostream &out_file, const SBasis & p) {
for(unsigned i = 0; i < p.size(); i++) {
- out_file << p[i] << "s^" << i << " + ";
+ if (i != 0) {
+ out_file << " + ";
+ }
+ out_file << p[i] << "s^" << i;
}
return out_file;
}
diff --git a/src/2geom/shape.cpp b/src/2geom/shape.cpp
deleted file mode 100644
index e9f5e55dc..000000000
--- a/src/2geom/shape.cpp
+++ /dev/null
@@ -1,689 +0,0 @@
-/**
- * \brief Shapes are special paths on which boolops can be performed
- *
- * Authors:
- * Michael G. Sloan <mgsloan@gmail.com>
- * Nathan Hurst <njh@mail.csse.monash.edu.au>
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright 2007-2009 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#include <2geom/shape.h>
-
-#include <2geom/utils.h>
-#include <2geom/sweep.h>
-#include <2geom/ord.h>
-
-#include <iostream>
-#include <algorithm>
-#include <cstdlib>
-
-//#define SHAPE_DEBUG // turns on debug outputting to cout.
-
-namespace Geom {
-
-// A little sugar for appending a list to another
-template<typename T>
-void append(T &a, T const &b) {
- a.insert(a.end(), b.begin(), b.end());
-}
-
-//Orders a list of indices according to their containment within eachother.
-struct ContainmentOrder {
- std::vector<Region> const *rs;
- explicit ContainmentOrder(std::vector<Region> const *r) : rs(r) {}
- bool operator()(unsigned a, unsigned b) const { return (*rs)[b].contains((*rs)[a]); }
-};
-
-//Returns the list of regions containing a particular point. Useful in tandem with ContainmentOrder
-std::vector<unsigned> Shape::containment_list(Point p) const {
- std::vector<Rect> pnt;
- pnt.push_back(Rect(p, p));
- std::vector<std::vector<unsigned> > cull = sweep_bounds(pnt, bounds(*this));
- std::vector<unsigned> containers;
- if(cull[0].size() == 0) return containers;
- for(unsigned i = 0; i < cull[0].size(); i++)
- if(content[cull[0][i]].contains(p)) containers.push_back(cull[0][i]);
- return containers;
-}
-
-/* Used within shape_boolean and related functions, as the name describes, finds the
- * first false within the list of lists of booleans.
- */
-void first_false(std::vector<std::vector<bool> > visited, unsigned &i, unsigned &j) {
- for(i = 0, j = 0; i < visited.size(); i++) {
- std::vector<bool>::iterator unvisited = std::find(visited[i].begin(), visited[i].end(), false);
- if(unvisited != visited[i].end()) {
- j = unvisited - visited[i].begin();
- break;
- }
- }
-}
-
-// Finds a crossing in a list of them, given the sorting index.
-unsigned find_crossing(Crossings const &cr, Crossing x, unsigned i) {
- return std::lower_bound(cr.begin(), cr.end(), x, CrossingOrder(i)) - cr.begin();
-}
-
-/* This function handles boolean ops on shapes. The first parameter is a bool
- * which determines its behavior in each combination of cases. For proper
- * fill information and noncrossing behavior, the fill data of the regions
- * must be correct. The boolean parameter determines whether the operation
- * is a union or a subtraction. Reversed paths represent inverse regions,
- * where everything is included in the fill except for the insides.
- *
- * Here is a chart of the behavior under various circumstances:
- *
- * rev = false (union)
- * A
- * F H
- * F A+B -> F A-B -> H
- *B
- * H B-A -> H AxB -> H
- *
- * rev = true (intersect)
- * A
- * F H
- * F AxB -> F B-A -> F
- *B
- * H A-B -> F A+B -> H
- *
- * F/H = Fill outer / Hole outer
- * A/B specify operands
- * + = union, - = subtraction, x = intersection
- * -> read as "produces"
- *
- * This is the main function of boolops, yet its operation isn't very complicated.
- * It traverses the crossings, and uses the crossing direction to decide whether
- * the next segment should be taken from A or from B. The second half of the
- * function deals with figuring out what to do with bits that have no intersection.
- */
-Shape shape_boolean(bool rev, Shape const & a, Shape const & b, CrossingSet const & crs) {
- const Regions ac = a.content, bc = b.content;
-
- //Keep track of which crossings we've hit.
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size(), false));
-
- //Traverse the crossings, creating chunks
- Regions chunks;
- while(true) {
- unsigned i, j;
- first_false(visited, i, j);
- if(i == visited.size()) break;
-
- Path res;
- do {
- Crossing cur = crs[i][j];
- visited[i][j] = true;
-
- //get indices of the dual:
- unsigned io = cur.getOther(i), jo = find_crossing(crs[io], cur, io);
- if(jo < visited[io].size()) visited[io][jo] = true;
-
- //main driving logic
- if(logical_xor(cur.dir, rev)) {
- if(i >= ac.size()) { i = io; j = jo; }
- j++;
- if(j >= crs[i].size()) j = 0;
- Crossing next = crs[i][j];
- ac[next.a].boundary.appendPortionTo(res, cur.ta, next.ta);
- } else {
- if(i < ac.size()) { i = io; j = jo; }
- j++;
- if(j >= crs[i].size()) j = 0;
- Crossing next = crs[i][j];
- bc[next.b - ac.size()].boundary.appendPortionTo(res, cur.tb, next.tb);
- }
- } while (!visited[i][j]);
- if(res.size() > 0) chunks.push_back(Region(res));
- }
-
- //If true, then we are on the 'subtraction diagonal'
- bool const on_sub = logical_xor(a.fill, b.fill);
- //If true, outer paths are filled
- bool const res_fill = rev ? (on_sub || (a.fill && b.fill)) : (a.fill && b.fill);
-
- //Handle unintersecting portions
- for(unsigned i = 0; i < crs.size(); i++) {
- if(crs[i].size() == 0) {
- bool env;
- bool on_a = i < ac.size();
- Region const & r(on_a ? ac[i] : bc[i - ac.size()]);
- Shape const & other(on_a ? b : a);
-
- std::vector<unsigned> containers = other.containment_list(r.boundary.initialPoint());
- if(containers.empty()) {
- //not included in any container, the environment fill is the opposite of the outer fill
- env = !res_fill;
- if(on_sub && logical_xor(other.fill, res_fill)) env = !env; //If on the subtractor, invert the environment fill
- } else {
- //environment fill is the same as the inner-most container
- std::vector<unsigned>::iterator cit = std::min_element(containers.begin(), containers.end(), ContainmentOrder(&other.content));
- env = other[*cit].isFill();
- }
- if(!logical_xor(rev, env)) chunks.push_back(r); //When unioning, environment must be hole for inclusion, when intersecting, it must be filled
- }
- }
-
- return Shape(chunks, res_fill);
-}
-
-// Just a convenience wrapper for shape_boolean, which handles the crossings
-Shape shape_boolean(bool rev, Shape const & a, Shape const & b) {
- CrossingSet crs = crossings_between(a, b);
-
- return shape_boolean(rev, a, b, crs);
-}
-
-
-// Some utility functions for boolop:
-
-std::vector<double> region_sizes(Shape const &a) {
- std::vector<double> ret;
- for(unsigned i = 0; i < a.size(); i++) {
- ret.push_back(double(a[i].size()));
- }
- return ret;
-}
-
-Shape shape_boolean_ra(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) {
- return shape_boolean(rev, a.inverse(), b, reverse_ta(crs, a.size(), region_sizes(a)));
-}
-
-Shape shape_boolean_rb(bool rev, Shape const &a, Shape const &b, CrossingSet const &crs) {
- return shape_boolean(rev, a, b.inverse(), reverse_tb(crs, a.size(), region_sizes(b)));
-}
-
-/* This is a function based on shape_boolean which allows boolean operations
- * to be specified as a logic table. This logic table is 4 bit-flags, which
- * correspond to the elements of the 'truth table' for a particular operation.
- * These flags are defined with the enums starting with BOOLOP_ .
- *
- * NOTE: currently doesn't work, as the CrossingSet reversal functions crash
- */
-Shape boolop(Shape const &a, Shape const &b, unsigned flags, CrossingSet const &crs) {
- THROW_NOTIMPLEMENTED();
- flags &= 15;
- if(flags <= BOOLOP_UNION) {
- switch(flags) {
- case BOOLOP_INTERSECT: return shape_boolean(true, a, b, crs);
- case BOOLOP_SUBTRACT_A_B: return shape_boolean_rb(true, a, b, crs);
- case BOOLOP_IDENTITY_A: return a;
- case BOOLOP_SUBTRACT_B_A: return shape_boolean_ra(true, a, b, crs);
- case BOOLOP_IDENTITY_B: return b;
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean_rb(true, a, b, crs);
- append(res.content, shape_boolean_ra(true, a, b, crs).content);
- return res;
- }
- case BOOLOP_UNION: return shape_boolean(false, a, b);
- }
- } else {
- flags = ~flags & 15;
- switch(flags - BOOLOP_NEITHER) {
- case BOOLOP_SUBTRACT_A_B: return shape_boolean_ra(false, a, b, crs);
- case BOOLOP_SUBTRACT_B_A: return shape_boolean_rb(false, a, b, crs);
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean_ra(false, a, b, CrossingSet(crs));
- append(res.content, shape_boolean_rb(false, a, b, CrossingSet(crs)).content);
- return res;
- }
- }
- return boolop(a, b, flags, crs).inverse();
- }
- return Shape();
-}
-
-/* This version of the boolop function doesn't require a set of crossings, as
- * it computes them for you. This is more efficient in some cases, as the
- * shape can be inverted before finding crossings. In the special case of
- * exclusion it uses the other version of boolop.
- */
-Shape boolop(Shape const &a, Shape const &b, unsigned flags) {
- flags &= 15;
- if(flags <= BOOLOP_UNION) {
- switch(flags) {
- case BOOLOP_INTERSECT: return shape_boolean(true, a, b);
- case BOOLOP_SUBTRACT_A_B: return shape_boolean(true, a, b.inverse());
- case BOOLOP_IDENTITY_A: return a;
- case BOOLOP_SUBTRACT_B_A: return shape_boolean(true, b, a.inverse());
- case BOOLOP_IDENTITY_B: return b;
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean(true, a, b.inverse());
- append(res.content, shape_boolean(true, b, a.inverse()).content);
- return res;
- } //return boolop(a, b, flags, crossings_between(a, b));
- case BOOLOP_UNION: return shape_boolean(false, a, b);
- }
- } else {
- flags = ~flags & 15;
- switch(flags) {
- case BOOLOP_SUBTRACT_A_B: return shape_boolean(false, b, a.inverse());
- case BOOLOP_SUBTRACT_B_A: return shape_boolean(false, a, b.inverse());
- case BOOLOP_EXCLUSION: {
- Shape res = shape_boolean(false, a, b.inverse());
- append(res.content, shape_boolean(false, b, a.inverse()).content);
- return res;
- } //return boolop(a, b, flags, crossings_between(a, b));
- }
- return boolop(a, b, flags).inverse();
- }
- return Shape();
-}
-
-int paths_winding(std::vector<Path> const &ps, Point p) {
- int ret = 0;
- for(unsigned i = 0; i < ps.size(); i++)
- ret += winding(ps[i], p);
- return ret;
-}
-
-void add_to_shape(Shape &s, Path const &p, bool fill) {
- if(fill)
- s.content.push_back(Region(p).asFill());
- else
- s.content.push_back(Region(p).asHole());
-}
-
-int inner_winding(Path const & p, std::vector<Path> const &ps) {
- Point pnt = p.initialPoint();
- return paths_winding(ps, pnt) - winding(p, pnt) + 1;
-}
-
-double fudgerize(double d, bool rev) {
- double ret = rev ? d - 0.01 : d + 0.01;
- if(ret < 0) ret = 0;
- return ret;
-}
-
-unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) {
- unsigned ex_jx = jx;
- unsigned oix = crs[ix][jx].getOther(ix);
- double otime = crs[ix][jx].getTime(oix);
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(fudgerize(otime, rev)) - cross_point,
- prev = -along;
- bool ex_dir = rev;
- for(unsigned k = jx; k < crs[ix].size(); k++) {
- unsigned koix = crs[ix][k].getOther(ix);
- if(koix == oix) {
- if(!are_near(otime, crs[ix][k].getTime(oix))) break;
- for(unsigned dir = 0; dir < 2; dir++) {
- Point val = ps[ix].pointAt(fudgerize(crs[ix][k].getTime(ix), dir)) - cross_point;
- Cmp to_prev = cmp(cross(val, prev), 0);
- Cmp from_along = cmp(cross(along, val), 0);
- Cmp c = cmp(from_along, to_prev);
- if(c == EQUAL_TO && from_along == LESS_THAN) {
- ex_jx = k;
- prev = val;
- ex_dir = dir;
- }
- }
- }
- }
- rev = ex_dir;
- return ex_jx;
-}
-
-unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs) {
- Crossing cur = Crossing(t, t, ix, ix, false);
- if(jx < crs.size()) {
- double ct = crs[jx].getTime(ix);
- if(t == ct) {
- cur = crs[jx];
- if(cur.a == cur.b) {
- if(jx+1 <= crs.size() && crs[jx+1].getOther(ix) == ix) return jx+1;
- if(jx > 0 && crs[jx-1].getOther(ix) == ix) return jx-1;
- }
- }
- }
- if(!dir) {
- jx = std::upper_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin();
- } else {
- jx = std::lower_bound(crs.begin(), crs.end(), cur, CrossingOrder(ix)) - crs.begin();
- if(jx == 0) jx = crs.size() - 1; else jx--;
- jx = std::lower_bound(crs.begin(), crs.end(), crs[jx], CrossingOrder(ix)) - crs.begin();
- }
- if(jx >= crs.size()) jx = 0;
- return jx;
-}
-
-void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs) {
- Crossing cur = crs[i][j];
- i = cur.getOther(i);
-#ifdef SHAPE_DEBUG
- std::cout << i << "\n";
-#endif
- if(crs[i].empty())
- j = 0;
- else
- j = std::lower_bound(crs[i].begin(), crs[i].end(), cur, CrossingOrder(i)) - crs[i].begin();
-}
-
-//locate a crossing on the outside, by casting a ray through the middle of the bbox
-void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs) {
- Rect bounds = *(ps[ix].boundsFast());
- double ry = bounds[Y].middle();
- double max_val = bounds.left(), max_t = 0;
- ix = ps.size();
- for(unsigned i = 0; i < ps.size(); i++) {
- if(!crs[i].empty()) {
- std::vector<double> rts = ps[i].roots(ry, Y);
- for(unsigned j = 0; j < rts.size(); j++) {
- double val = ps[i].valueAt(rts[j], X);
- if(val > max_val) {
- ix = i;
- max_val = val;
- max_t = rts[j];
- }
- }
- }
- }
- if(ix != ps.size()) {
- dir = ps[ix].valueAt(max_t + 0.01, Y) >
- ps[ix].valueAt(max_t - 0.01, Y);
- jx = crossing_along(max_t, ix, jx, dir, crs[ix]);
- }
-}
-
-std::vector<Path> inner_sanitize(std::vector<Path> const & ps) {
- CrossingSet crs(crossings_among(ps));
-
- Regions chunks;
-
- std::vector<bool> used_path(ps.size(), false);
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size(), false));
-
- std::vector<Path> result_paths;
-
- while(true) {
- unsigned ix = 0, jx = 0;
- bool dir = false;
-
- //find an outer crossing by trying various paths and checking if the crossings are used
- for(; ix < crs.size(); ix++) {
- //TODO: optimize so it doesn't unecessarily check stuff
- bool cont = true;
- for(unsigned j = 0; j < crs[ix].size(); j++) {
- if(!visited[ix][j]) { cont = false; break; }
- }
- if(cont) continue;
- unsigned rix = ix, rjx = jx;
- outer_crossing(rix, rjx, dir, ps, crs);
- if(rix >= crs.size() || visited[rix][rjx]) continue;
- ix = rix; jx = rjx;
- break;
- }
- if(ix == crs.size()) break;
- crossing_dual(ix, jx, crs);
-
- dir = !dir;
-
- Path res;
- do {
- visited[ix][jx] = true;
- //unsigned nix = ix, njx = jx;
- //crossing_dual(nix, njx, crs);
- //visited[nix][njx] = true;
- unsigned fix = ix, fjx = jx;
-
- bool new_dir = dir;
-
- jx = crossing_along(crs[ix][jx].getTime(ix), ix, jx, dir, crs[ix]);
- if(crs[ix][jx].a != crs[ix][jx].b) crossing_dual(ix, jx, crs); else new_dir = !new_dir;
- jx = pick_coincident(ix, jx, new_dir, ps, crs);
-
- //unsigned nix = ix, njx = jx;
- //crossing_dual(nix, njx, crs);
-
- Crossing from = crs[fix][fjx],
- to = crs[ix][jx];
- if(dir) {
- // backwards
-#ifdef SHAPE_DEBUG
- std::cout << "r" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n";
-#endif
- Path p = ps[ix].portion(from.getTime(ix), to.getTime(ix)).reverse();
- for(unsigned i = 0; i < p.size(); i++)
- res.append(p[i], Path::STITCH_DISCONTINUOUS);
- } else {
- // forwards
-#ifdef SHAPE_DEBUG
- std::cout << "f" << ix << "[" << from.getTime(ix) << ", " << to.getTime(ix) << "]\n";
-#endif
- ps[ix].appendPortionTo(res, from.getTime(ix), to.getTime(ix));
- }
- dir = new_dir;
- } while(!visited[ix][jx]);
-#ifdef SHAPE_DEBUG
- std::cout << "added " << res.size() << "\n";
-#endif
- result_paths.push_back(res);
- }
- for(unsigned i = 0; i < crs.size(); i++) {
- if(crs[i].empty() && !used_path[i])
- result_paths.push_back(ps[i]);
- }
- return result_paths;
-}
-
-Shape sanitize(std::vector<Path> const & ps) {
- std::vector<Path> res;
- for(unsigned i = 0; i < ps.size(); i++) {
- append(res, inner_sanitize(std::vector<Path>(1, ps[i])));
- }
- return stopgap_cleaner(res);
-}
-
-/* WIP sanitizer:
-unsigned pick_coincident(unsigned ix, unsigned jx, bool pref, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs) {
- unsigned ex_jx = jx;
- unsigned oix = crs[ix][jx].getOther(ix);
- double otime = crs[ix][jx].getTime(oix);
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(otime + (rev ? -0.01 : 0.01)) - cross_point,
- prev = -along;
- bool ex_dir = rev;
- for(unsigned k = jx; k < crs[ix].size(); k++) {
- unsigned koix = crs[ix][k].getOther(ix);
- if(koix == oix) {
- if(!are_near(otime, crs[ix][k].getTime(oix))) break;
- for(unsigned dir = 0; dir < 2; dir++) {
- Point val = ps[ix].pointAt(crs[ix][k].getTime(ix) + (dir ? -0.01 : 0.01)) - cross_point;
- Cmp to_prev = cmp(cross(val, prev), 0);
- Cmp from_along = cmp(cross(along, val), 0);
- Cmp c = cmp(from_along, to_prev);
- if(c == EQUAL_TO && (from_along == LESS_THAN) == pref) {
- ex_jx = k;
- prev = val;
- ex_dir = dir;
- }
- }
- }
- }
- rev = ex_dir;
- return ex_jx;
-}
-
-unsigned corner_index(unsigned &i) {
- div_t div_res = div(i, 4);
- i = div_res.quot;
- return div_res.rem;
-}
-
-bool corner_direction(unsigned ix, unsigned jc, unsigned corner, CrossingSet const &crs) {
- if(crs[ix][jc].a == ix) return corner > 1; else return corner %2 == 1;
-}
-
-Shape sanitize(std::vector<Path> const & ps) {
- CrossingSet crs = crossings_among(ps);
-
- //Keep track of which CORNERS we've hit.
- // FF FR RF RR, first is A dir, second B dir
- std::vector<std::vector<bool> > visited;
- for(unsigned i = 0; i < crs.size(); i++)
- visited.push_back(std::vector<bool>(crs[i].size()*4, false));
-
- Regions chunks;
- while(true) {
- unsigned i, j;
- first_false(visited, i, j);
- unsigned corner = corner_index(j);
-
- if(i == visited.size()) break;
-
- bool dir = corner_direction(i, j, corner, crs);
-
- //Figure out whether we hug the path cw or ccw, based on the orientation of the initial corner:
- unsigned oix = crs[i][j].getOther(i);
- double otime = crs[i][j].getTime(oix);
- bool odir = (oix == crs[i][j].a) ? corner > 1 : corner % 2 == 1;
- Point cross_point = ps[oix].pointAt(otime),
- along = ps[oix].pointAt(otime + (odir ? -0.01 : 0.01)) - cross_point,
- val = ps[i].pointAt(crs[i][j].getTime(i) + (dir ? -0.01 : 0.01)) - cross_point;
-
- Cmp from_along = cmp(cross(along, val), 0);
- bool cw = from_along == LESS_THAN;
- std::cout << "cw = " << cw << "\n";
- Path res;
- do {
- Crossing cur = crs[i][j];
- visited[i][j*4+corner] = true;
-
- unsigned fix = i, fjx = j;
- crossing_dual(i, j, crs);
- visited[i][j*4+corner] = true;
- i = fix; j = fjx;
-
- j = crossing_along(crs[i][j].getTime(i), i, j, dir, crs[i]);
-
- crossing_dual(i, j, crs);
-
- bool new_dir = dir;
- pick_coincident(i, j, cw, new_dir, ps, crs);
-
- Crossing from = crs[fix][fjx],
- to = crs[i][j];
- if(dir) {
- // backwards
- std::cout << "r" << i << "[" << to.getTime(i) << ", " << from.getTime(i) << "]\n";
- Path p = ps[i].portion(to.getTime(i) + 0.001, from.getTime(i)).reverse();
- for(unsigned k = 0; k < p.size(); k++)
- res.append(p[k]);
- } else {
- // forwards
- std::cout << "f" << i << "[" << from.getTime(i) << ", " << to.getTime(i) << "]\n";
- ps[i].appendPortionTo(res, from.getTime(i) + 0.001, to.getTime(i));
- }
- if(i == to.a)
- corner = (new_dir ? 2 : 0) + (dir ? 1 : 0);
- else
- corner = (new_dir ? 1 : 0) + (dir ? 2 : 0);
- dir = new_dir;
- } while(!visited[i][j*4+corner]);
- chunks.push_back(Region(res));
-// if(use) {
-// chunks.push_back(Region(res, true));
-// }
- }
- return Shape(chunks);
-// return ret;
-} */
-
-/* This transforms a shape by a matrix. In the case that the matrix flips
- * the shape, it reverses the paths in order to preserve the fill.
- */
-Shape Shape::operator*(Affine const &m) const {
- Shape ret;
- for(unsigned i = 0; i < size(); i++)
- ret.content.push_back(content[i] * m);
- ret.fill = fill;
- return ret;
-}
-
-// Inverse is a boolean not, and simply reverses all the paths & fill flags
-Shape Shape::inverse() const {
- Shape ret;
- for(unsigned i = 0; i < size(); i++)
- ret.content.push_back(content[i].inverse());
- ret.fill = !fill;
- return ret;
-}
-
-bool Shape::contains(Point const &p) const {
- std::vector<unsigned> containers = containment_list(p);
- if(containers.empty()) return !isFill();
- unsigned ix = *min_element(containers.begin(), containers.end(), ContainmentOrder(&content));
- return content[ix].isFill();
-}
-
-Shape stopgap_cleaner(std::vector<Path> const &ps) {
- if(ps.empty()) return Shape(false);
- Shape ret;
- for(unsigned i = 0; i < ps.size(); i++)
- add_to_shape(ret, ps[i], inner_winding(ps[i], ps) % 2 != 0);
- return ret;
-}
-
-bool Shape::inside_invariants() const { //semi-slow & easy to violate
- for(unsigned i = 0; i < size(); i++)
- if( logical_xor(content[i].isFill(), contains(content[i].boundary.initialPoint())) ) return false;
- return true;
-}
-bool Shape::region_invariants() const { //semi-slow
- for(unsigned i = 0; i < size(); i++)
- if(!content[i].invariants()) return false;
- return true;
-}
-bool Shape::cross_invariants() const { //slow
- CrossingSet crs; // = crossings_among(paths_from_regions(content));
- for(unsigned i = 0; i < crs.size(); i++)
- if(!crs[i].empty()) return false;
- return true;
-}
-
-bool Shape::invariants() const {
- return inside_invariants() && region_invariants() && cross_invariants();
-}
-
-}
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/shape.h b/src/2geom/shape.h
deleted file mode 100644
index 0a7ee9709..000000000
--- a/src/2geom/shape.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/**
- * \brief Shapes are special paths on which boolops can be performed
- *
- * Authors:
- * Michael G. Sloan <mgsloan@gmail.com>
- * Nathan Hurst <njh@mail.csse.monash.edu.au>
- * MenTaLguY <mental@rydia.net>
- *
- * Copyright 2007-2009 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- *
- */
-
-#ifndef __2GEOM_SHAPE_H
-#define __2GEOM_SHAPE_H
-
-#include <vector>
-#include <set>
-
-#include <2geom/region.h>
-
-//TODO: BBOX optimizations
-
-namespace Geom {
-
-enum {
- BOOLOP_JUST_A = 1,
- BOOLOP_JUST_B = 2,
- BOOLOP_BOTH = 4,
- BOOLOP_NEITHER = 8
-};
-
-enum {
- BOOLOP_NULL = 0,
- BOOLOP_INTERSECT = BOOLOP_BOTH,
- BOOLOP_SUBTRACT_A_B = BOOLOP_JUST_B,
- BOOLOP_IDENTITY_A = BOOLOP_JUST_A | BOOLOP_BOTH,
- BOOLOP_SUBTRACT_B_A = BOOLOP_JUST_A,
- BOOLOP_IDENTITY_B = BOOLOP_JUST_B | BOOLOP_BOTH,
- BOOLOP_EXCLUSION = BOOLOP_JUST_A | BOOLOP_JUST_B,
- BOOLOP_UNION = BOOLOP_JUST_A | BOOLOP_JUST_B | BOOLOP_BOTH
-};
-
-class Shape {
- Regions content;
- mutable bool fill;
- //friend Shape shape_region_boolean(bool rev, Shape const & a, Region const & b);
- friend CrossingSet crossings_between(Shape const &a, Shape const &b);
- friend Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &);
- friend Shape boolop(Shape const &a, Shape const &b, unsigned);
- friend Shape boolop(Shape const &a, Shape const &b, unsigned, CrossingSet const &);
- friend void add_to_shape(Shape &s, Path const &p, bool);
- public:
- Shape() : fill(true) {}
- explicit Shape(Region const & r) {
- content = Regions(1, r);
- fill = r.fill;
- }
- explicit Shape(Regions const & r) : content(r) { update_fill(); }
- explicit Shape(bool f) : fill(f) {}
- Shape(Regions const & r, bool f) : content(r), fill(f) {}
-
- Regions getContent() const { return content; }
- bool isFill() const { return fill; }
-
- unsigned size() const { return content.size(); }
- const Region &operator[](unsigned ix) const { return content[ix]; }
-
- Shape inverse() const;
- Shape operator*(Affine const &m) const;
-
- bool contains(Point const &p) const;
-
- bool inside_invariants() const; //semi-slow & easy to violate : checks that the insides are inside, the outsides are outside
- bool region_invariants() const; //semi-slow : checks for self crossing
- bool cross_invariants() const; //slow : checks that everything is disjoint
- bool invariants() const; //vera slow (combo, checks the above)
-
- private:
- std::vector<unsigned> containment_list(Point p) const;
- void update_fill() const {
- unsigned ix = outer_index(content);
- if(ix < size())
- fill = content[ix].fill;
- else if(size() > 0)
- fill = content.front().fill;
- else
- fill = true;
- }
-};
-
-inline CrossingSet crossings_between(Shape const &a, Shape const &b) { return crossings(paths_from_regions(a.content), paths_from_regions(b.content)); }
-
-Shape shape_boolean(bool rev, Shape const &, Shape const &, CrossingSet const &);
-Shape shape_boolean(bool rev, Shape const &, Shape const &);
-
-//unsigned pick_coincident(unsigned ix, unsigned jx, bool &rev, std::vector<Path> const &ps, CrossingSet const &crs);
-//void outer_crossing(unsigned &ix, unsigned &jx, bool & dir, std::vector<Path> const & ps, CrossingSet const & crs);
-void crossing_dual(unsigned &i, unsigned &j, CrossingSet const & crs);
-unsigned crossing_along(double t, unsigned ix, unsigned jx, bool dir, Crossings const & crs);
-
-Shape boolop(Shape const &, Shape const &, unsigned flags);
-Shape boolop(Shape const &, Shape const &, unsigned flags, CrossingSet &);
-
-Shape sanitize(std::vector<Path> const &ps);
-
-Shape stopgap_cleaner(std::vector<Path> const &ps);
-
-inline std::vector<Path> desanitize(Shape const & s) {
- return paths_from_regions(s.getContent());
-}
-
-}
-
-#endif
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/solve-bezier-one-d.cpp b/src/2geom/solve-bezier-one-d.cpp
index 3d87d4926..3a25e9808 100644
--- a/src/2geom/solve-bezier-one-d.cpp
+++ b/src/2geom/solve-bezier-one-d.cpp
@@ -27,7 +27,7 @@ static int SGN(t x) { return (x > 0 ? 1 : (x < 0 ? -1 : 0)); }
**/
class Bernsteins{
public:
- static const size_t MAX_DEPTH = 22;
+ static const size_t MAX_DEPTH = 53;
size_t degree, N;
std::vector<double> &solutions;
//std::vector<double> bc;
@@ -38,17 +38,9 @@ public:
{
}
- void subdivide(double const *V,
- double t,
- double *Left,
- double *Right);
-
unsigned
control_poly_flat_enough(double const *V);
- double horner(const double *b, double t);
-
-
void
find_bernstein_roots(double const *w, /* The control points */
unsigned depth, /* The depth of the recursion */
@@ -134,7 +126,7 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */
r = (fs*t - ft*s) / (fs - ft);
if (fabs(t-s) < e * fabs(t+s)) break;
- double fr = horner(w, r);
+ double fr = bernstein_value_at(r, w, degree);
if (fr * ft > 0)
{
@@ -191,24 +183,6 @@ void Bernsteins::find_bernstein_roots(double const *w, /* The control points */
delete[] LR;
}
-
-// suggested by Sederberg.
-double Bernsteins::horner(const double *b, double t)
-{
- double u, tn, tmp;
- u = 1.0 - t;
- tn = 1.0;
- tmp = b[0] * u;
- for(size_t i = 1; i < degree; ++i)
- {
- tn *= t;
- tmp = (tmp + tn*bc[i]*b[i]) * u;
- }
- return (tmp + tn*t*b[degree]);
-}
-
-
-
#if 0
/*
* control_poly_flat_enough :
diff --git a/src/2geom/solve-bezier-parametric.cpp b/src/2geom/solve-bezier-parametric.cpp
index 9b0feaee4..2fb3f41da 100644
--- a/src/2geom/solve-bezier-parametric.cpp
+++ b/src/2geom/solve-bezier-parametric.cpp
@@ -1,8 +1,9 @@
-#include <2geom/solver.h>
+#include <2geom/bezier.h>
#include <2geom/point.h>
+#include <2geom/solver.h>
#include <algorithm>
-namespace Geom{
+namespace Geom {
/*** Find the zeros of the parametric function in 2d defined by two beziers X(t), Y(t). The code subdivides until it happy with the linearity of the bezier. This requires an n^2 subdivision for each step, even when there is only one solution.
*
@@ -14,13 +15,6 @@ namespace Geom{
/*
* Forward declarations
*/
-static Geom::Point
-Bezier(Geom::Point const *V,
- unsigned degree,
- double t,
- Geom::Point *Left,
- Geom::Point *Right);
-
unsigned
crossing_count(Geom::Point const *V, unsigned degree);
static unsigned
@@ -74,7 +68,7 @@ find_parametric_bezier_roots(Geom::Point const *w, /* The control points */
// Right[degree+1]; /* control polygons */
std::vector<Geom::Point> Left( degree+1 ), Right(degree+1);
- Bezier(w, degree, 0.5, Left.data(), Right.data());
+ casteljau_subdivision(0.5, w, Left.data(), Right.data(), degree);
total_subs ++;
find_parametric_bezier_roots(Left.data(), degree, solutions, depth+1);
find_parametric_bezier_roots(Right.data(), degree, solutions, depth+1);
@@ -181,43 +175,6 @@ compute_x_intercept(Geom::Point const *V, /* Control points */
return (A[Geom::X]*V[0][Geom::Y] - A[Geom::Y]*V[0][Geom::X]) / -A[Geom::Y];
}
-
-/*
- * Bezier :
- * Evaluate a Bezier curve at a particular parameter value
- * Fill in control points for resulting sub-curves.
- *
- */
-static Geom::Point
-Bezier(Geom::Point const *V, /* Control pts */
- unsigned degree, /* Degree of bezier curve */
- double t, /* Parameter value */
- Geom::Point *Left, /* RETURN left half ctl pts */
- Geom::Point *Right) /* RETURN right half ctl pts */
-{
- //Geom::Point Vtemp[degree+1][degree+1];
- std::vector<std::vector<Geom::Point> > Vtemp(degree+1);
- for ( size_t i = 0; i < degree + 1; ++i )
- Vtemp.reserve(degree+1);
-
- /* Copy control points */
- std::copy(V, V+degree+1, Vtemp[0].begin());
-
- /* Triangle computation */
- for (unsigned i = 1; i <= degree; i++) {
- for (unsigned j = 0; j <= degree - i; j++) {
- Vtemp[i][j] = lerp(t, Vtemp[i-1][j], Vtemp[i-1][j+1]);
- }
- }
-
- for (unsigned j = 0; j <= degree; j++)
- Left[j] = Vtemp[j][0];
- for (unsigned j = 0; j <= degree; j++)
- Right[j] = Vtemp[degree-j][j];
-
- return (Vtemp[degree][0]);
-}
-
};
/*
diff --git a/src/2geom/solve-bezier.cpp b/src/2geom/solve-bezier.cpp
index adf3c9ac0..4732169cb 100644
--- a/src/2geom/solve-bezier.cpp
+++ b/src/2geom/solve-bezier.cpp
@@ -37,12 +37,10 @@ public:
double *Left,
double *Right);
- double secant(Bezier bz);
+ double secant(Bezier const &bz);
- double horner(Bezier bz, double t);
-
- void find_bernstein_roots(Bezier bz, unsigned depth,
+ void find_bernstein_roots(Bezier const &bz, unsigned depth,
double left_t, double right_t);
};
@@ -55,27 +53,7 @@ inline std::ostream &operator<< (std::ostream &out_file, const std::vector<T> &
return out_file << "]";
}
-Bezier subRight(Bezier bz, double t) {
- unsigned order = bz.order();
- unsigned N = order+1;
- std::valarray<Coord> row(N);
- for (unsigned i = 0; i < N; i++)
- row[i] = bz[i];
-
- // Triangle computation
- const double omt = (1-t);
- Bezier Right = bz;
- Right[order] = row[order];
- for (unsigned i = 1; i < N; i++) {
- for (unsigned j = 0; j < N - i; j++) {
- row[j] = omt*row[j] + t*row[j+1];
- }
- Right[order-i] = row[order-i];
- }
- return Right;
-}
-
-void convex_hull_marching(Bezier src_bz, Bezier bz,
+void convex_hull_marching(Bezier const &src_bz, Bezier bz,
std::vector<double> &solutions,
double left_t,
double right_t)
@@ -111,7 +89,7 @@ void convex_hull_marching(Bezier src_bz, Bezier bz,
<< " = " << bz(left_bound) << std::endl;
double new_left_t = left_bound * (right_t - left_t) + left_t;
std::cout << "new_left_t = " << new_left_t << std::endl;
- Bezier bzr = subRight(src_bz, new_left_t);
+ Bezier bzr = portion(src_bz, new_left_t, 1);
while(bzr.order() > 0 && bzr[0] == 0) {
std::cout << "deflate\n";
bzr = bzr.deflate();
@@ -171,7 +149,7 @@ Bezier::find_bezier_roots(std::vector<double> &solutions,
//std::cout << solutions << std::endl;
}
-void Bernsteins::find_bernstein_roots(Bezier bz,
+void Bernsteins::find_bernstein_roots(Bezier const &bz,
unsigned depth,
double left_t,
double right_t)
@@ -194,6 +172,11 @@ void Bernsteins::find_bernstein_roots(Bezier bz,
old_sign = sign;
}
}
+ // if last control point is zero, that counts as crossing too
+ if (SGN(bz[bz.size()-1]) == 0) {
+ ++n_crossings;
+ }
+
//std::cout << "n_crossings = " << n_crossings << std::endl;
if (n_crossings == 0) return; // no solutions here
@@ -271,23 +254,7 @@ void Bernsteins::find_bernstein_roots(Bezier bz,
}
}
-
-// suggested by Sederberg.
-double Bernsteins::horner(Bezier bz, double t)
-{
- double u, tn, tmp;
- u = 1.0 - t;
- tn = 1.0;
- tmp = bz.at0() * u;
- for(size_t i = 1; i < bz.degree(); ++i)
- {
- tn *= t;
- tmp = (tmp + tn*choose<double>(bz.order(), (unsigned)i)*bz[i]) * u;
- }
- return (tmp + tn*t*bz.at1());
-}
-
-double Bernsteins::secant(Bezier bz) {
+double Bernsteins::secant(Bezier const &bz) {
double s = 0, t = 1;
double e = 1e-14;
int side = 0;
@@ -303,7 +270,7 @@ double Bernsteins::secant(Bezier bz) {
return r;
}
- double fr = horner(bz, r);
+ double fr = bz.valueAt(r);
if (fr * ft > 0)
{
diff --git a/src/2geom/solver.h b/src/2geom/solver.h
index 793939b2a..5b082cb83 100644
--- a/src/2geom/solver.h
+++ b/src/2geom/solver.h
@@ -32,16 +32,14 @@
*
*/
-#ifndef _SOLVE_SBASIS_H
-#define _SOLVE_SBASIS_H
+#ifndef LIB2GEOM_SEEN_SOLVER_H
+#define LIB2GEOM_SEEN_SOLVER_H
+
#include <2geom/point.h>
#include <2geom/sbasis.h>
-
-
#include <vector>
-
-namespace Geom{
+namespace Geom {
class Point;
class Bezier;
@@ -78,7 +76,6 @@ find_bernstein_roots(std::vector<double> &solutions, /* RETURN candidate t-value
double left_t, double right_t);
#endif
-
/*
Local Variables:
mode:c++
diff --git a/src/2geom/svg-elliptical-arc.h b/src/2geom/svg-elliptical-arc.h
deleted file mode 100644
index ba0a18257..000000000
--- a/src/2geom/svg-elliptical-arc.h
+++ /dev/null
@@ -1,282 +0,0 @@
-/**
- * \file
- * \brief SVG 1.1-compliant elliptical arc curve
- *//*
- * Authors:
- * MenTaLguY <mental@rydia.net>
- * Marco Cecchetti <mrcekets at gmail.com>
- * Krzysztof KosiƄski <tweenk.pl@gmail.com>
- *
- * Copyright 2007-2009 Authors
- *
- * This library is free software; you can redistribute it and/or
- * modify it either under the terms of the GNU Lesser General Public
- * License version 2.1 as published by the Free Software Foundation
- * (the "LGPL") or, at your option, under the terms of the Mozilla
- * Public License Version 1.1 (the "MPL"). If you do not alter this
- * notice, a recipient may use your version of this file under either
- * the MPL or the LGPL.
- *
- * You should have received a copy of the LGPL along with this library
- * in the file COPYING-LGPL-2.1; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * You should have received a copy of the MPL along with this library
- * in the file COPYING-MPL-1.1
- *
- * The contents of this file are subject to the Mozilla Public License
- * Version 1.1 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
- * OF ANY KIND, either express or implied. See the LGPL or the MPL for
- * the specific language governing rights and limitations.
- */
-
-
-#ifndef LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_H
-#define LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_H
-
-#include <2geom/curve.h>
-#include <2geom/angle.h>
-#include <2geom/utils.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/elliptical-arc.h>
-#include <2geom/sbasis-curve.h> // for non-native methods
-#include <2geom/numeric/vector.h>
-#include <2geom/numeric/fitting-tool.h>
-#include <2geom/numeric/fitting-model.h>
-#include <algorithm>
-
-namespace Geom {
-
-class SVGEllipticalArc : public EllipticalArc {
-public:
- SVGEllipticalArc()
- : EllipticalArc()
- {}
- SVGEllipticalArc( Point ip, double rx, double ry,
- double rot_angle, bool large_arc, bool sweep,
- Point fp
- )
- : EllipticalArc()
- {
- _initial_point = ip;
- _final_point = fp;
- _rays[X] = rx; _rays[Y] = ry;
- _rot_angle = rot_angle;
- _large_arc = large_arc;
- _sweep = sweep;
- _updateCenterAndAngles(true);
- }
-
-#ifndef DOXYGEN_SHOULD_SKIP_THIS
- virtual Curve *duplicate() const {
- return new SVGEllipticalArc(*this);
- }
- virtual Coord valueAt(Coord t, Dim2 d) const {
- if (isDegenerate()) return chord().valueAt(t, d);
- return EllipticalArc::valueAt(t, d);
- }
- virtual Point pointAt(Coord t) const {
- if (isDegenerate()) return chord().pointAt(t);
- return EllipticalArc::pointAt(t);
- }
- virtual std::vector<Point> pointAndDerivatives(Coord t, unsigned int n) const {
- if (isDegenerate()) return chord().pointAndDerivatives(t, n);
- return EllipticalArc::pointAndDerivatives(t, n);
- }
- virtual Rect boundsExact() const {
- if (isDegenerate()) return chord().boundsExact();
- return EllipticalArc::boundsExact();
- }
- virtual OptRect boundsLocal(OptInterval const &i, unsigned int deg) const {
- if (isDegenerate()) return chord().boundsLocal(i, deg);
- return EllipticalArc::boundsLocal(i, deg);
- }
-
- virtual Curve *derivative() const {
- if (isDegenerate()) return chord().derivative();
- return EllipticalArc::derivative();
- }
-
- virtual std::vector<Coord> roots(Coord v, Dim2 d) const {
- if (isDegenerate()) return chord().roots(v, d);
- return EllipticalArc::roots(v, d);
- }
-#ifdef HAVE_GSL
- virtual std::vector<Coord> allNearestPoints( Point const& p, double from = 0, double to = 1 ) const {
- if (isDegenerate()) {
- std::vector<Coord> result;
- result.push_back(chord().nearestPoint(p, from, to));
- return result;
- }
- return EllipticalArc::allNearestPoints(p, from, to);
- }
-#endif
- virtual D2<SBasis> toSBasis() const {
- if (isDegenerate()) return chord().toSBasis();
- return EllipticalArc::toSBasis();
- }
- virtual bool isSVGCompliant() const { return true; }
- // TODO move SVG-specific behavior here.
-//protected:
- //virtual void _updateCenterAndAngles();
-#endif
-}; // end class SVGEllipticalArc
-
-/*
- * useful for testing and debugging
- */
-template< class charT >
-inline
-std::basic_ostream<charT> &
-operator<< (std::basic_ostream<charT> & os, const SVGEllipticalArc & ea)
-{
- os << "{ cx: " << ea.center(X) << ", cy: " << ea.center(Y)
- << ", rx: " << ea.ray(X) << ", ry: " << ea.ray(Y)
- << ", rot angle: " << decimal_round(rad_to_deg(ea.rotationAngle()),2)
- << ", start angle: " << decimal_round(rad_to_deg(ea.initialAngle()),2)
- << ", end angle: " << decimal_round(rad_to_deg(ea.finalAngle()),2)
- << " }";
-
- return os;
-}
-
-
-
-
-// forward declation
-namespace detail
-{
- struct ellipse_equation;
-}
-
-// TODO this needs to be rewritten and moved to EllipticalArc header
-/*
- * make_elliptical_arc
- *
- * convert a parametric polynomial curve given in symmetric power basis form
- * into an SVGEllipticalArc type; in order to be successfull the input curve
- * has to look like an actual elliptical arc even if a certain tolerance
- * is allowed through an ad-hoc parameter.
- * The conversion is performed through an interpolation on a certain amount of
- * sample points computed on the input curve;
- * the interpolation computes the coefficients of the general implicit equation
- * of an ellipse (A*X^2 + B*XY + C*Y^2 + D*X + E*Y + F = 0), then from the
- * implicit equation we compute the parametric form.
- *
- */
-class make_elliptical_arc
-{
- public:
- typedef D2<SBasis> curve_type;
-
- /*
- * constructor
- *
- * it doesn't execute the conversion but set the input and output parameters
- *
- * _ea: the output SVGEllipticalArc that will be generated;
- * _curve: the input curve to be converted;
- * _total_samples: the amount of sample points to be taken
- * on the input curve for performing the conversion
- * _tolerance: how much likelihood is required between the input curve
- * and the generated elliptical arc; the smaller it is the
- * the tolerance the higher it is the likelihood.
- */
- make_elliptical_arc( EllipticalArc& _ea,
- curve_type const& _curve,
- unsigned int _total_samples,
- double _tolerance );
-
- private:
- bool bound_exceeded( unsigned int k, detail::ellipse_equation const & ee,
- double e1x, double e1y, double e2 );
-
- bool check_bound(double A, double B, double C, double D, double E, double F);
-
- void fit();
-
- bool make_elliptiarc();
-
- void print_bound_error(unsigned int k)
- {
- std::cerr
- << "tolerance error" << std::endl
- << "at point: " << k << std::endl
- << "error value: "<< dist_err << std::endl
- << "bound: " << dist_bound << std::endl
- << "angle error: " << angle_err
- << " (" << angle_tol << ")" << std::endl;
- }
-
- public:
- /*
- * perform the actual conversion
- * return true if the conversion is successfull, false on the contrary
- */
- bool operator()()
- {
- // initialize the reference
- const NL::Vector & coeff = fitter.result();
- fit();
- if ( !check_bound(1, coeff[0], coeff[1], coeff[2], coeff[3], coeff[4]) )
- return false;
- if ( !(make_elliptiarc()) ) return false;
- return true;
- }
-
- /*
- * you can set a boolean parameter to tell the conversion routine
- * if the output elliptical arc has to be svg compliant or not;
- * the default value is true
- */
- bool svg_compliant_flag() const
- {
- return svg_compliant;
- }
-
- void svg_compliant_flag(bool _svg_compliant)
- {
- svg_compliant = _svg_compliant;
- }
-
- private:
- EllipticalArc& ea; // output elliptical arc
- const curve_type & curve; // input curve
- Piecewise<D2<SBasis> > dcurve; // derivative of the input curve
- NL::LFMEllipse model; // model used for fitting
- // perform the actual fitting task
- NL::least_squeares_fitter<NL::LFMEllipse> fitter;
- // tolerance: the user-defined tolerance parameter;
- // tol_at_extr: the tolerance at end-points automatically computed
- // on the value of "tolerance", and usually more strict;
- // tol_at_center: tolerance at the center of the ellipse
- // angle_tol: tolerance for the angle btw the input curve tangent
- // versor and the ellipse normal versor at the sample points
- double tolerance, tol_at_extr, tol_at_center, angle_tol;
- Point initial_point, final_point; // initial and final end-points
- unsigned int N; // total samples
- unsigned int last; // N-1
- double partitions; // N-1
- std::vector<Point> p; // sample points
- double dist_err, dist_bound, angle_err;
- bool svg_compliant;
-};
-
-} // end namespace Geom
-
-#endif // LIB2GEOM_SEEN_SVG_ELLIPTICAL_ARC_H
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
-
diff --git a/src/2geom/svg-path-parser.cpp b/src/2geom/svg-path-parser.cpp
index 932f95829..b6e6da869 100644
--- a/src/2geom/svg-path-parser.cpp
+++ b/src/2geom/svg-path-parser.cpp
@@ -1,3 +1,5 @@
+
+#line 1 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
/**
* \file
* \brief parse SVG path specifications
@@ -30,7 +32,7 @@
*
*/
-
+#include <cstdio>
#include <cmath>
#include <vector>
#include <glib.h>
@@ -41,229 +43,108 @@
namespace Geom {
-namespace {
-
-class Parser {
-public:
- Parser(PathSink &sink) : _absolute(false), _sink(sink) {}
-
- void parse(char const *str) throw(SVGPathParseError);
-
-private:
- bool _absolute;
- Point _current;
- Point _initial;
- Point _cubic_tangent;
- Point _quad_tangent;
- std::vector<double> _params;
- PathSink &_sink;
-
- void _reset() {
- _absolute = false;
- _current = _initial = Point(0, 0);
- _quad_tangent = _cubic_tangent = Point(0, 0);
- _params.clear();
- }
-
- void _push(double value) {
- _params.push_back(value);
- }
-
- double _pop() {
- double value = _params.back();
- _params.pop_back();
- return value;
- }
-
- bool _pop_flag() {
- return _pop() != 0.0;
- }
-
- double _pop_coord(Geom::Dim2 axis) {
- if (_absolute) {
- return _pop();
- } else {
- return _pop() + _current[axis];
- }
- }
-
- Point _pop_point() {
- double y = _pop_coord(Geom::Y);
- double x = _pop_coord(Geom::X);
- return Point(x, y);
- }
-
- void _moveTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = _initial = p;
- _sink.moveTo(p);
- }
-
- void _hlineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.hlineTo(p[Geom::X]);
- }
-
- void _vlineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.vlineTo(p[Geom::Y]);
- }
-
- void _lineTo(Point p) {
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.lineTo(p);
- }
-
- void _curveTo(Point c0, Point c1, Point p) {
- _quad_tangent = _current = p;
- _cubic_tangent = p + ( p - c1 );
- _sink.curveTo(c0, c1, p);
- }
-
- void _quadTo(Point c, Point p) {
- _cubic_tangent = _current = p;
- _quad_tangent = p + ( p - c );
- _sink.quadTo(c, p);
- }
-
- void _arcTo(double rx, double ry, double angle,
- bool large_arc, bool sweep, Point p)
- {
- if (are_near(_current, p)) {
- return;
- }
- _quad_tangent = _cubic_tangent = _current = p;
- _sink.arcTo(rx, ry, angle, large_arc, sweep, p);
- }
-
- void _closePath() {
- _quad_tangent = _cubic_tangent = _current = _initial;
- _sink.closePath();
- }
-};
-
+#line 48 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
static const char _svg_path_actions[] = {
0, 1, 0, 1, 1, 1, 2, 1,
- 3, 1, 4, 1, 5, 1, 15, 1,
- 16, 2, 1, 0, 2, 1, 6, 2,
- 1, 7, 2, 1, 8, 2, 1, 9,
- 2, 1, 10, 2, 1, 11, 2, 1,
- 12, 2, 1, 13, 2, 1, 14, 2,
- 4, 0, 2, 5, 0, 2, 15, 16,
- 3, 1, 6, 0, 3, 1, 6, 16,
- 3, 1, 7, 0, 3, 1, 7, 16,
- 3, 1, 8, 0, 3, 1, 8, 16,
- 3, 1, 9, 0, 3, 1, 9, 16,
- 3, 1, 10, 0, 3, 1, 10, 16,
- 3, 1, 11, 0, 3, 1, 11, 16,
- 3, 1, 12, 0, 3, 1, 12, 16,
- 3, 1, 13, 0, 3, 1, 13, 16,
- 3, 1, 14, 0, 3, 1, 14, 16
-
+ 3, 1, 4, 1, 5, 1, 15, 2,
+ 1, 0, 2, 1, 6, 2, 1, 7,
+ 2, 1, 8, 2, 1, 9, 2, 1,
+ 10, 2, 1, 11, 2, 1, 12, 2,
+ 1, 13, 2, 1, 14, 2, 2, 0,
+ 2, 3, 0, 2, 4, 0, 2, 5,
+ 0, 3, 1, 6, 0, 3, 1, 7,
+ 0, 3, 1, 8, 0, 3, 1, 9,
+ 0, 3, 1, 10, 0, 3, 1, 11,
+ 0, 3, 1, 12, 0, 3, 1, 13,
+ 0, 3, 1, 14, 0
};
static const short _svg_path_key_offsets[] = {
- 0, 0, 7, 16, 25, 28, 30, 42,
- 52, 55, 57, 90, 121, 124, 126, 138,
- 148, 151, 153, 186, 195, 207, 216, 249,
- 256, 263, 265, 275, 283, 290, 292, 304,
- 314, 317, 319, 328, 335, 341, 346, 353,
- 359, 364, 374, 377, 379, 391, 401, 404,
- 406, 437, 466, 476, 480, 482, 490, 499,
- 508, 511, 513, 525, 535, 538, 540, 552,
- 562, 565, 567, 579, 589, 592, 594, 606,
- 616, 619, 621, 633, 643, 646, 648, 681,
- 712, 724, 733, 745, 754, 766, 775, 787,
- 796, 808, 817, 850, 854, 856, 887, 896,
- 905, 908, 910, 943, 974, 1007, 1011, 1013,
- 1044, 1053, 1062, 1071, 1074, 1076, 1088, 1098,
- 1101, 1103, 1115, 1125, 1128, 1130, 1142, 1152,
- 1155, 1157, 1190, 1221, 1233, 1242, 1254, 1263,
- 1275, 1284, 1317, 1321, 1323, 1354, 1363, 1372,
- 1375, 1377, 1389, 1399, 1402, 1404, 1416, 1426,
- 1429, 1431, 1443, 1453, 1456, 1458, 1491, 1522,
- 1534, 1543, 1555, 1564, 1576, 1585, 1618, 1622,
- 1624, 1655, 1664, 1673, 1676, 1678, 1690, 1700,
- 1703, 1705, 1738, 1769, 1781, 1790, 1823, 1827,
- 1829, 1860, 1869, 1878, 1881, 1883, 1916, 1947,
- 1980, 1984, 1986, 2017, 2042, 2067, 2074, 2083,
- 2092, 2101, 2110, 2122, 2131, 2164, 2168, 2170,
- 2201, 2210, 2219, 2228, 2237, 2241, 2243, 2253,
- 2257, 2259, 2269, 2273, 2275, 2285, 2289, 2291,
- 2301, 2305, 2307, 2317, 2321, 2323, 2333, 2337,
- 2339, 2349, 2353, 2355, 2365, 2369, 2371, 2381,
- 2385, 2387, 2397, 2401, 2403, 2413, 2417, 2419,
- 2429, 2433, 2435, 2445, 2449, 2451, 2480, 2511,
- 2520, 2524, 2526, 2536, 2548, 2557, 2562, 2567,
- 2571, 2573, 2580, 2590, 2599, 2603, 2605, 2615,
- 2627, 2631, 2633, 2664, 2668, 2670, 2680
+ 0, 0, 9, 18, 21, 23, 35, 45,
+ 48, 50, 53, 55, 67, 77, 80, 82,
+ 91, 103, 112, 119, 126, 128, 138, 146,
+ 153, 155, 167, 177, 180, 182, 191, 198,
+ 204, 211, 218, 224, 234, 244, 247, 249,
+ 261, 271, 274, 276, 286, 290, 292, 300,
+ 309, 318, 321, 323, 335, 345, 348, 350,
+ 362, 372, 375, 377, 389, 399, 402, 404,
+ 416, 426, 429, 431, 443, 453, 456, 458,
+ 470, 479, 491, 500, 512, 521, 533, 542,
+ 554, 563, 567, 569, 578, 587, 590, 592,
+ 596, 598, 607, 616, 625, 628, 630, 642,
+ 652, 655, 657, 669, 679, 682, 684, 696,
+ 706, 709, 711, 723, 732, 744, 753, 765,
+ 774, 778, 780, 789, 798, 801, 803, 815,
+ 825, 828, 830, 842, 852, 855, 857, 869,
+ 879, 882, 884, 896, 905, 917, 926, 938,
+ 947, 951, 953, 962, 971, 974, 976, 988,
+ 998, 1001, 1003, 1015, 1024, 1028, 1030, 1039,
+ 1048, 1051, 1053, 1057, 1059, 1066, 1075, 1084,
+ 1093, 1102, 1114, 1123, 1127, 1129, 1138, 1147,
+ 1156, 1165, 1169, 1171, 1181, 1185, 1187, 1197,
+ 1201, 1203, 1213, 1217, 1219, 1229, 1233, 1235,
+ 1245, 1249, 1251, 1261, 1265, 1267, 1277, 1281,
+ 1283, 1293, 1297, 1299, 1309, 1313, 1315, 1325,
+ 1329, 1331, 1341, 1345, 1347, 1357, 1361, 1363,
+ 1373, 1377, 1379, 1388, 1392, 1394, 1404, 1416,
+ 1425, 1435, 1442, 1446, 1448, 1455, 1465, 1474,
+ 1478, 1480, 1490, 1502, 1506, 1508, 1512, 1514,
+ 1524, 1530, 1562, 1592, 1624, 1656, 1686, 1714,
+ 1746, 1776, 1808, 1838, 1870, 1900, 1932, 1962,
+ 1994, 2024, 2056, 2086, 2118, 2148, 2180, 2210,
+ 2242, 2272, 2304, 2334, 2366, 2396, 2428, 2458,
+ 2482, 2506, 2538, 2568, 2596, 2626
};
static const char _svg_path_trans_keys[] = {
- 0, 13, 32, 77, 109, 9, 10, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 46, 48, 57, 48, 57, 13, 32,
- 44, 46, 69, 101, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 9, 10,
- 43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 46, 48, 57, 48, 57, 13, 32,
- 44, 46, 69, 101, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 9, 10,
- 43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 46, 48, 57, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
+ 48, 57, 46, 48, 57, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 46, 9,
+ 10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 43, 45, 46, 9, 10, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 13, 32, 46, 9, 10, 48, 57,
- 13, 32, 46, 9, 10, 48, 57, 48,
+ 13, 32, 46, 9, 10, 48, 57, 13,
+ 32, 46, 9, 10, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 48, 57, 13, 32, 46, 9, 10, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 69, 101, 9, 10, 48, 57, 13,
+ 32, 44, 48, 49, 9, 10, 13, 32,
+ 48, 49, 9, 10, 13, 32, 44, 48,
+ 49, 9, 10, 13, 32, 44, 48, 49,
+ 9, 10, 13, 32, 48, 49, 9, 10,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
- 10, 48, 57, 13, 32, 44, 46, 9,
- 10, 48, 57, 13, 32, 46, 9, 10,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
48, 57, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 9, 10, 43, 45,
- 48, 57, 46, 48, 57, 48, 57, 13,
- 32, 44, 69, 101, 9, 10, 48, 57,
- 13, 32, 44, 48, 49, 9, 10, 13,
- 32, 48, 49, 9, 10, 13, 32, 44,
- 9, 10, 13, 32, 44, 48, 49, 9,
- 10, 13, 32, 48, 49, 9, 10, 13,
- 32, 44, 9, 10, 13, 32, 44, 46,
- 9, 10, 43, 45, 48, 57, 46, 48,
+ 69, 101, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 9, 10, 48, 57, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 46, 48,
57, 48, 57, 13, 32, 44, 46, 69,
101, 9, 10, 43, 45, 48, 57, 13,
32, 44, 46, 9, 10, 43, 45, 48,
- 57, 46, 48, 57, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 48, 57, 43, 45, 48, 57,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
- 48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 46, 48, 57, 48,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
@@ -276,102 +157,29 @@ static const char _svg_path_trans_keys[] = {
10, 43, 45, 48, 57, 46, 48, 57,
48, 57, 13, 32, 44, 46, 69, 101,
9, 10, 43, 45, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 46, 48, 57, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
- 57, 13, 32, 44, 46, 9, 10, 43,
- 45, 48, 57, 46, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 104, 108, 109, 113, 115, 116,
- 118, 122, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 69, 101, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
- 57, 13, 32, 43, 45, 46, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
43, 45, 46, 9, 10, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 43, 45, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 13, 32, 43, 45, 46, 9, 10, 48,
- 57, 46, 48, 57, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 0,
- 13, 32, 44, 46, 65, 67, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 43, 45, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 43,
- 45, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 43,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
45, 46, 9, 10, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 46,
- 48, 57, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 9, 10, 43, 45,
- 48, 57, 46, 48, 57, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 44, 46, 9,
- 10, 43, 45, 48, 57, 46, 48, 57,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 46, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 69, 72, 76, 77,
- 81, 83, 84, 86, 90, 97, 99, 101,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 43, 45, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 72, 76, 77,
- 81, 83, 84, 86, 90, 97, 99, 104,
- 108, 109, 113, 115, 116, 118, 122, 9,
- 10, 43, 45, 48, 57, 13, 32, 44,
- 46, 69, 101, 9, 10, 43, 45, 48,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 43, 45, 48, 57, 48,
57, 13, 32, 43, 45, 46, 9, 10,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 0, 13, 32, 44,
- 46, 65, 67, 69, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 101, 104,
- 108, 109, 113, 115, 116, 118, 122, 9,
- 10, 43, 45, 48, 57, 43, 45, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 13, 32, 43, 45, 46,
- 9, 10, 48, 57, 46, 48, 57, 48,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 46, 48, 57, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 46, 48, 57, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 44,
46, 9, 10, 43, 45, 48, 57, 46,
@@ -380,85 +188,49 @@ static const char _svg_path_trans_keys[] = {
13, 32, 44, 46, 9, 10, 43, 45,
48, 57, 46, 48, 57, 48, 57, 13,
32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 13, 32, 44, 46, 9,
- 10, 43, 45, 48, 57, 46, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 69, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 101, 104, 108, 109,
- 113, 115, 116, 118, 122, 9, 10, 43,
- 45, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 72, 76, 77, 81, 83, 84,
- 86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 13, 32, 44, 46, 69, 101,
- 9, 10, 43, 45, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
45, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 44, 46,
69, 101, 9, 10, 43, 45, 48, 57,
13, 32, 43, 45, 46, 9, 10, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 43, 45, 48, 57, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 43, 45,
+ 48, 57, 48, 57, 13, 32, 43, 45,
+ 46, 9, 10, 48, 57, 13, 32, 43,
+ 45, 46, 9, 10, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 44, 46, 9, 10, 43, 45, 48,
57, 46, 48, 57, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 44, 46, 9, 10,
43, 45, 48, 57, 46, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
- 69, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 101, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 43, 45,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
+ 57, 13, 32, 44, 46, 69, 101, 9,
+ 10, 43, 45, 48, 57, 13, 32, 44,
+ 46, 9, 10, 43, 45, 48, 57, 46,
+ 48, 57, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
57, 13, 32, 44, 46, 69, 101, 9,
10, 43, 45, 48, 57, 13, 32, 43,
- 45, 46, 9, 10, 48, 57, 0, 13,
- 32, 44, 46, 65, 67, 69, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 101, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 43, 45, 48, 57, 43,
- 45, 48, 57, 48, 57, 0, 13, 32,
- 44, 46, 65, 67, 72, 76, 77, 81,
- 83, 84, 86, 90, 97, 99, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 43,
- 45, 46, 9, 10, 48, 57, 46, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 0, 13, 32, 44,
- 46, 65, 67, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 104, 108, 109,
- 113, 115, 116, 118, 122, 9, 10, 43,
- 45, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 43, 45, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
- 57, 0, 13, 32, 65, 67, 72, 76,
- 77, 81, 83, 84, 86, 90, 97, 99,
- 104, 108, 109, 113, 115, 116, 118, 122,
- 9, 10, 0, 13, 32, 65, 67, 72,
- 76, 77, 81, 83, 84, 86, 90, 97,
- 99, 104, 108, 109, 113, 115, 116, 118,
- 122, 9, 10, 13, 32, 46, 9, 10,
+ 45, 46, 9, 10, 48, 57, 13, 32,
+ 44, 46, 69, 101, 9, 10, 43, 45,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 43, 45, 48, 57, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 13, 32, 43, 45, 46, 9,
+ 10, 48, 57, 46, 48, 57, 48, 57,
+ 13, 32, 44, 46, 69, 101, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 9, 10, 43, 45, 48, 57, 46, 48,
+ 57, 48, 57, 13, 32, 44, 46, 69,
+ 101, 9, 10, 43, 45, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 43, 45, 48, 57, 48, 57, 13, 32,
+ 43, 45, 46, 9, 10, 48, 57, 13,
+ 32, 43, 45, 46, 9, 10, 48, 57,
+ 46, 48, 57, 48, 57, 43, 45, 48,
+ 57, 48, 57, 13, 32, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
9, 10, 48, 57, 13, 32, 43, 45,
@@ -466,15 +238,7 @@ static const char _svg_path_trans_keys[] = {
45, 46, 9, 10, 48, 57, 13, 32,
44, 46, 69, 101, 9, 10, 43, 45,
48, 57, 13, 32, 43, 45, 46, 9,
- 10, 48, 57, 0, 13, 32, 44, 46,
- 65, 67, 69, 72, 76, 77, 81, 83,
- 84, 86, 90, 97, 99, 101, 104, 108,
- 109, 113, 115, 116, 118, 122, 9, 10,
- 43, 45, 48, 57, 43, 45, 48, 57,
- 48, 57, 0, 13, 32, 44, 46, 65,
- 67, 72, 76, 77, 81, 83, 84, 86,
- 90, 97, 99, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 43, 45, 48,
+ 10, 48, 57, 43, 45, 48, 57, 48,
57, 13, 32, 43, 45, 46, 9, 10,
48, 57, 13, 32, 43, 45, 46, 9,
10, 48, 57, 13, 32, 43, 45, 46,
@@ -506,661 +270,980 @@ static const char _svg_path_trans_keys[] = {
10, 43, 45, 48, 57, 43, 45, 48,
57, 48, 57, 13, 32, 44, 46, 9,
10, 43, 45, 48, 57, 43, 45, 48,
- 57, 48, 57, 0, 13, 32, 44, 46,
+ 57, 48, 57, 13, 32, 43, 45, 46,
+ 9, 10, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 69, 101, 9, 10, 43, 45, 48, 57,
+ 13, 32, 43, 45, 46, 9, 10, 48,
+ 57, 13, 32, 44, 46, 9, 10, 43,
+ 45, 48, 57, 13, 32, 44, 48, 49,
+ 9, 10, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 9, 10, 48, 57, 13,
+ 32, 44, 46, 69, 101, 9, 10, 48,
+ 57, 13, 32, 43, 45, 46, 9, 10,
+ 48, 57, 43, 45, 48, 57, 48, 57,
+ 13, 32, 44, 46, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 69, 101,
+ 9, 10, 43, 45, 48, 57, 43, 45,
+ 48, 57, 48, 57, 43, 45, 48, 57,
+ 48, 57, 13, 32, 44, 46, 9, 10,
+ 43, 45, 48, 57, 13, 32, 77, 109,
+ 9, 10, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
65, 67, 72, 76, 77, 81, 83, 84,
86, 90, 97, 99, 104, 108, 109, 113,
- 115, 116, 118, 122, 9, 10, 48, 57,
- 0, 13, 32, 44, 46, 65, 67, 69,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
- 97, 99, 101, 104, 108, 109, 113, 115,
- 116, 118, 122, 9, 10, 48, 57, 13,
- 32, 43, 45, 46, 9, 10, 48, 57,
- 43, 45, 48, 57, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
- 13, 32, 44, 46, 69, 101, 9, 10,
- 43, 45, 48, 57, 13, 32, 43, 45,
- 46, 9, 10, 48, 57, 13, 32, 44,
- 9, 10, 13, 32, 44, 9, 10, 43,
- 45, 48, 57, 48, 57, 13, 32, 44,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 72, 76, 77, 81, 83, 84,
+ 86, 90, 97, 99, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 69, 72,
+ 76, 77, 81, 83, 84, 86, 90, 97,
+ 99, 101, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 69, 72, 76, 77,
+ 81, 83, 84, 86, 90, 97, 99, 101,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 43, 45, 48, 57, 13, 32,
+ 44, 46, 65, 67, 72, 76, 77, 81,
+ 83, 84, 86, 90, 97, 99, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 43, 45, 48, 57, 13, 32, 44, 46,
+ 65, 67, 72, 76, 77, 81, 83, 84,
+ 86, 90, 97, 99, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 13, 32, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
+ 9, 10, 13, 32, 44, 46, 65, 67,
+ 69, 72, 76, 77, 81, 83, 84, 86,
+ 90, 97, 99, 101, 104, 108, 109, 113,
+ 115, 116, 118, 122, 9, 10, 43, 45,
+ 48, 57, 13, 32, 44, 46, 65, 67,
+ 72, 76, 77, 81, 83, 84, 86, 90,
+ 97, 99, 104, 108, 109, 113, 115, 116,
+ 118, 122, 9, 10, 43, 45, 48, 57,
+ 13, 32, 44, 46, 65, 67, 72, 76,
+ 77, 81, 83, 84, 86, 90, 97, 99,
+ 104, 108, 109, 113, 115, 116, 118, 122,
9, 10, 48, 57, 13, 32, 44, 46,
- 69, 101, 9, 10, 48, 57, 13, 32,
- 43, 45, 46, 9, 10, 48, 57, 43,
- 45, 48, 57, 48, 57, 13, 32, 44,
- 46, 9, 10, 43, 45, 48, 57, 13,
- 32, 44, 46, 69, 101, 9, 10, 43,
- 45, 48, 57, 43, 45, 48, 57, 48,
- 57, 0, 13, 32, 44, 46, 65, 67,
+ 65, 67, 69, 72, 76, 77, 81, 83,
+ 84, 86, 90, 97, 99, 101, 104, 108,
+ 109, 113, 115, 116, 118, 122, 9, 10,
+ 48, 57, 13, 32, 44, 46, 65, 67,
72, 76, 77, 81, 83, 84, 86, 90,
97, 99, 104, 108, 109, 113, 115, 116,
118, 122, 9, 10, 43, 45, 48, 57,
- 43, 45, 48, 57, 48, 57, 13, 32,
- 44, 46, 9, 10, 43, 45, 48, 57,
0
};
static const char _svg_path_single_lengths[] = {
+ 0, 5, 5, 1, 0, 6, 4, 1,
+ 0, 1, 0, 6, 4, 1, 0, 5,
+ 6, 5, 3, 3, 0, 6, 4, 3,
+ 0, 6, 4, 1, 0, 5, 5, 4,
+ 5, 5, 4, 4, 4, 1, 0, 6,
+ 4, 1, 0, 6, 2, 0, 4, 5,
+ 5, 1, 0, 6, 4, 1, 0, 6,
+ 4, 1, 0, 6, 4, 1, 0, 6,
+ 4, 1, 0, 6, 4, 1, 0, 6,
+ 5, 6, 5, 6, 5, 6, 5, 6,
+ 5, 2, 0, 5, 5, 1, 0, 2,
0, 5, 5, 5, 1, 0, 6, 4,
- 1, 0, 27, 25, 1, 0, 6, 4,
- 1, 0, 27, 5, 6, 5, 27, 3,
- 3, 0, 6, 4, 3, 0, 6, 4,
- 1, 0, 5, 5, 4, 3, 5, 4,
- 3, 4, 1, 0, 6, 4, 1, 0,
- 27, 25, 6, 2, 0, 4, 5, 5,
1, 0, 6, 4, 1, 0, 6, 4,
+ 1, 0, 6, 5, 6, 5, 6, 5,
+ 2, 0, 5, 5, 1, 0, 6, 4,
1, 0, 6, 4, 1, 0, 6, 4,
- 1, 0, 6, 4, 1, 0, 27, 25,
- 6, 5, 6, 5, 6, 5, 6, 5,
- 6, 5, 27, 2, 0, 25, 5, 5,
- 1, 0, 27, 25, 27, 2, 0, 25,
- 5, 5, 5, 1, 0, 6, 4, 1,
- 0, 6, 4, 1, 0, 6, 4, 1,
- 0, 27, 25, 6, 5, 6, 5, 6,
- 5, 27, 2, 0, 25, 5, 5, 1,
- 0, 6, 4, 1, 0, 6, 4, 1,
- 0, 6, 4, 1, 0, 27, 25, 6,
- 5, 6, 5, 6, 5, 27, 2, 0,
- 25, 5, 5, 1, 0, 6, 4, 1,
- 0, 27, 25, 6, 5, 27, 2, 0,
- 25, 5, 5, 1, 0, 27, 25, 27,
- 2, 0, 25, 23, 23, 3, 5, 5,
- 5, 5, 6, 5, 27, 2, 0, 25,
- 5, 5, 5, 5, 2, 0, 4, 2,
+ 1, 0, 6, 5, 6, 5, 6, 5,
+ 2, 0, 5, 5, 1, 0, 6, 4,
+ 1, 0, 6, 5, 2, 0, 5, 5,
+ 1, 0, 2, 0, 3, 5, 5, 5,
+ 5, 6, 5, 2, 0, 5, 5, 5,
+ 5, 2, 0, 4, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
2, 0, 4, 2, 0, 4, 2, 0,
4, 2, 0, 4, 2, 0, 4, 2,
0, 4, 2, 0, 4, 2, 0, 4,
- 2, 0, 4, 2, 0, 25, 27, 5,
- 2, 0, 4, 6, 5, 3, 3, 2,
- 0, 3, 6, 5, 2, 0, 4, 6,
- 2, 0, 25, 2, 0, 4, 0
+ 2, 0, 5, 2, 0, 4, 6, 5,
+ 4, 5, 2, 0, 3, 6, 5, 2,
+ 0, 4, 6, 2, 0, 2, 0, 4,
+ 4, 26, 24, 26, 26, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 26,
+ 24, 26, 24, 26, 24, 26, 24, 22,
+ 22, 26, 24, 24, 26, 24
};
static const char _svg_path_range_lengths[] = {
- 0, 1, 2, 2, 1, 1, 3, 3,
+ 0, 2, 2, 1, 1, 3, 3, 1,
+ 1, 1, 1, 3, 3, 1, 1, 2,
+ 3, 2, 2, 2, 1, 2, 2, 2,
+ 1, 3, 3, 1, 1, 2, 1, 1,
+ 1, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 2, 1, 1, 2, 2,
+ 2, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 3, 3, 1, 1, 3,
+ 3, 1, 1, 3, 3, 1, 1, 3,
+ 2, 3, 2, 3, 2, 3, 2, 3,
+ 2, 1, 1, 2, 2, 1, 1, 1,
+ 1, 2, 2, 2, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
1, 1, 3, 2, 3, 2, 3, 2,
- 2, 1, 2, 2, 2, 1, 3, 3,
- 1, 1, 2, 1, 1, 1, 1, 1,
- 1, 3, 1, 1, 3, 3, 1, 1,
- 2, 2, 2, 1, 1, 2, 2, 2,
+ 1, 1, 2, 2, 1, 1, 3, 3,
1, 1, 3, 3, 1, 1, 3, 3,
- 1, 1, 3, 3, 1, 1, 3, 3,
- 1, 1, 3, 3, 1, 1, 3, 3,
- 3, 2, 3, 2, 3, 2, 3, 2,
- 3, 2, 3, 1, 1, 3, 2, 2,
- 1, 1, 3, 3, 3, 1, 1, 3,
- 2, 2, 2, 1, 1, 3, 3, 1,
- 1, 3, 3, 1, 1, 3, 3, 1,
- 1, 3, 3, 3, 2, 3, 2, 3,
- 2, 3, 1, 1, 3, 2, 2, 1,
- 1, 3, 3, 1, 1, 3, 3, 1,
- 1, 3, 3, 1, 1, 3, 3, 3,
- 2, 3, 2, 3, 2, 3, 1, 1,
- 3, 2, 2, 1, 1, 3, 3, 1,
- 1, 3, 3, 3, 2, 3, 1, 1,
- 3, 2, 2, 1, 1, 3, 3, 3,
- 1, 1, 3, 1, 1, 2, 2, 2,
- 2, 2, 3, 2, 3, 1, 1, 3,
- 2, 2, 2, 2, 1, 1, 3, 1,
+ 1, 1, 3, 2, 3, 2, 3, 2,
+ 1, 1, 2, 2, 1, 1, 3, 3,
+ 1, 1, 3, 2, 1, 1, 2, 2,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 2, 1, 1, 2, 2, 2,
+ 2, 1, 1, 3, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
1, 1, 3, 1, 1, 3, 1, 1,
3, 1, 1, 3, 1, 1, 3, 1,
1, 3, 1, 1, 3, 1, 1, 3,
- 1, 1, 3, 1, 1, 2, 2, 2,
- 1, 1, 3, 3, 2, 1, 1, 1,
- 1, 2, 2, 2, 1, 1, 3, 3,
- 1, 1, 3, 1, 1, 3, 0
+ 1, 1, 2, 1, 1, 3, 3, 2,
+ 3, 1, 1, 1, 2, 2, 2, 1,
+ 1, 3, 3, 1, 1, 1, 1, 3,
+ 1, 3, 3, 3, 3, 2, 2, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 1,
+ 1, 3, 3, 2, 2, 3
};
static const short _svg_path_index_offsets[] = {
- 0, 0, 7, 15, 23, 26, 28, 38,
- 46, 49, 51, 82, 111, 114, 116, 126,
- 134, 137, 139, 170, 178, 188, 196, 227,
- 233, 239, 241, 250, 257, 263, 265, 275,
- 283, 286, 288, 296, 303, 309, 314, 321,
- 327, 332, 340, 343, 345, 355, 363, 366,
- 368, 398, 426, 435, 439, 441, 448, 456,
- 464, 467, 469, 479, 487, 490, 492, 502,
- 510, 513, 515, 525, 533, 536, 538, 548,
- 556, 559, 561, 571, 579, 582, 584, 615,
- 644, 654, 662, 672, 680, 690, 698, 708,
- 716, 726, 734, 765, 769, 771, 800, 808,
- 816, 819, 821, 852, 881, 912, 916, 918,
- 947, 955, 963, 971, 974, 976, 986, 994,
- 997, 999, 1009, 1017, 1020, 1022, 1032, 1040,
- 1043, 1045, 1076, 1105, 1115, 1123, 1133, 1141,
- 1151, 1159, 1190, 1194, 1196, 1225, 1233, 1241,
- 1244, 1246, 1256, 1264, 1267, 1269, 1279, 1287,
- 1290, 1292, 1302, 1310, 1313, 1315, 1346, 1375,
- 1385, 1393, 1403, 1411, 1421, 1429, 1460, 1464,
- 1466, 1495, 1503, 1511, 1514, 1516, 1526, 1534,
- 1537, 1539, 1570, 1599, 1609, 1617, 1648, 1652,
- 1654, 1683, 1691, 1699, 1702, 1704, 1735, 1764,
- 1795, 1799, 1801, 1830, 1855, 1880, 1886, 1894,
- 1902, 1910, 1918, 1928, 1936, 1967, 1971, 1973,
- 2002, 2010, 2018, 2026, 2034, 2038, 2040, 2048,
- 2052, 2054, 2062, 2066, 2068, 2076, 2080, 2082,
- 2090, 2094, 2096, 2104, 2108, 2110, 2118, 2122,
- 2124, 2132, 2136, 2138, 2146, 2150, 2152, 2160,
- 2164, 2166, 2174, 2178, 2180, 2188, 2192, 2194,
- 2202, 2206, 2208, 2216, 2220, 2222, 2250, 2280,
- 2288, 2292, 2294, 2302, 2312, 2320, 2325, 2330,
- 2334, 2336, 2342, 2351, 2359, 2363, 2365, 2373,
- 2383, 2387, 2389, 2418, 2422, 2424, 2432
+ 0, 0, 8, 16, 19, 21, 31, 39,
+ 42, 44, 47, 49, 59, 67, 70, 72,
+ 80, 90, 98, 104, 110, 112, 121, 128,
+ 134, 136, 146, 154, 157, 159, 167, 174,
+ 180, 187, 194, 200, 208, 216, 219, 221,
+ 231, 239, 242, 244, 253, 257, 259, 266,
+ 274, 282, 285, 287, 297, 305, 308, 310,
+ 320, 328, 331, 333, 343, 351, 354, 356,
+ 366, 374, 377, 379, 389, 397, 400, 402,
+ 412, 420, 430, 438, 448, 456, 466, 474,
+ 484, 492, 496, 498, 506, 514, 517, 519,
+ 523, 525, 533, 541, 549, 552, 554, 564,
+ 572, 575, 577, 587, 595, 598, 600, 610,
+ 618, 621, 623, 633, 641, 651, 659, 669,
+ 677, 681, 683, 691, 699, 702, 704, 714,
+ 722, 725, 727, 737, 745, 748, 750, 760,
+ 768, 771, 773, 783, 791, 801, 809, 819,
+ 827, 831, 833, 841, 849, 852, 854, 864,
+ 872, 875, 877, 887, 895, 899, 901, 909,
+ 917, 920, 922, 926, 928, 934, 942, 950,
+ 958, 966, 976, 984, 988, 990, 998, 1006,
+ 1014, 1022, 1026, 1028, 1036, 1040, 1042, 1050,
+ 1054, 1056, 1064, 1068, 1070, 1078, 1082, 1084,
+ 1092, 1096, 1098, 1106, 1110, 1112, 1120, 1124,
+ 1126, 1134, 1138, 1140, 1148, 1152, 1154, 1162,
+ 1166, 1168, 1176, 1180, 1182, 1190, 1194, 1196,
+ 1204, 1208, 1210, 1218, 1222, 1224, 1232, 1242,
+ 1250, 1258, 1265, 1269, 1271, 1277, 1286, 1294,
+ 1298, 1300, 1308, 1318, 1322, 1324, 1328, 1330,
+ 1338, 1344, 1374, 1402, 1432, 1462, 1491, 1518,
+ 1548, 1576, 1606, 1634, 1664, 1692, 1722, 1750,
+ 1780, 1808, 1838, 1866, 1896, 1924, 1954, 1982,
+ 2012, 2040, 2070, 2098, 2128, 2156, 2186, 2214,
+ 2238, 2262, 2292, 2320, 2347, 2376
};
static const short _svg_path_indicies[] = {
- 0, 2, 2, 3, 4, 2, 1, 5,
- 5, 6, 6, 7, 5, 8, 1, 9,
- 9, 10, 10, 11, 9, 12, 1, 13,
- 14, 1, 15, 1, 16, 16, 18, 19,
- 20, 20, 16, 17, 15, 1, 21, 21,
- 23, 24, 21, 22, 25, 1, 26, 27,
- 1, 28, 1, 29, 30, 30, 32, 33,
- 34, 35, 36, 37, 38, 39, 40, 41,
- 42, 43, 44, 45, 46, 36, 47, 48,
- 49, 50, 51, 52, 53, 44, 30, 31,
- 28, 1, 0, 54, 54, 56, 57, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 54, 55, 58, 1, 76,
- 77, 1, 78, 1, 79, 79, 81, 82,
- 83, 83, 79, 80, 78, 1, 84, 84,
- 86, 87, 84, 85, 88, 1, 89, 90,
- 1, 91, 1, 92, 93, 93, 95, 96,
- 97, 98, 99, 100, 101, 102, 103, 104,
- 105, 106, 107, 108, 109, 99, 110, 111,
- 112, 113, 114, 115, 116, 107, 93, 94,
- 91, 1, 56, 56, 55, 55, 57, 56,
- 58, 1, 79, 79, 81, 78, 83, 83,
- 79, 80, 77, 1, 86, 86, 85, 85,
- 87, 86, 88, 1, 92, 93, 93, 95,
- 91, 97, 98, 99, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 99, 110,
- 111, 112, 113, 114, 115, 116, 107, 93,
- 94, 90, 1, 117, 117, 118, 117, 119,
- 1, 120, 120, 121, 120, 122, 1, 123,
- 1, 124, 124, 125, 126, 127, 127, 124,
- 123, 1, 128, 128, 129, 130, 128, 131,
- 1, 129, 129, 130, 129, 131, 1, 132,
- 1, 133, 133, 135, 136, 137, 137, 133,
- 134, 132, 1, 138, 138, 140, 141, 138,
- 139, 142, 1, 143, 144, 1, 145, 1,
- 146, 146, 147, 148, 148, 146, 145, 1,
- 149, 149, 150, 151, 152, 149, 1, 150,
- 150, 151, 152, 150, 1, 153, 153, 154,
- 153, 1, 155, 155, 156, 157, 158, 155,
- 1, 156, 156, 157, 158, 156, 1, 159,
- 159, 160, 159, 1, 161, 161, 163, 164,
- 161, 162, 165, 1, 166, 167, 1, 168,
- 1, 169, 169, 171, 172, 173, 173, 169,
- 170, 168, 1, 174, 174, 176, 177, 174,
- 175, 178, 1, 179, 180, 1, 181, 1,
- 182, 183, 183, 184, 185, 186, 187, 188,
- 189, 190, 191, 192, 193, 194, 195, 196,
- 197, 198, 188, 199, 200, 201, 202, 203,
- 204, 205, 196, 183, 181, 1, 0, 206,
- 206, 120, 121, 59, 60, 61, 62, 3,
- 63, 64, 65, 66, 67, 68, 69, 70,
- 71, 4, 72, 73, 74, 75, 67, 206,
- 122, 1, 124, 124, 125, 123, 127, 127,
- 124, 207, 1, 208, 208, 209, 1, 209,
- 1, 124, 124, 125, 126, 124, 209, 1,
- 210, 210, 211, 211, 212, 210, 213, 1,
- 214, 214, 215, 215, 216, 214, 217, 1,
- 218, 219, 1, 220, 1, 221, 221, 223,
- 224, 225, 225, 221, 222, 220, 1, 226,
- 226, 228, 229, 226, 227, 230, 1, 231,
- 232, 1, 233, 1, 234, 234, 236, 237,
- 238, 238, 234, 235, 233, 1, 239, 239,
- 241, 242, 239, 240, 243, 1, 244, 245,
- 1, 246, 1, 247, 247, 249, 250, 251,
- 251, 247, 248, 246, 1, 252, 252, 254,
- 255, 252, 253, 256, 1, 257, 258, 1,
- 259, 1, 260, 260, 262, 263, 264, 264,
- 260, 261, 259, 1, 265, 265, 267, 268,
- 265, 266, 269, 1, 270, 271, 1, 272,
- 1, 273, 273, 275, 276, 277, 277, 273,
- 274, 272, 1, 278, 278, 280, 281, 278,
- 279, 282, 1, 283, 284, 1, 285, 1,
- 286, 287, 287, 289, 290, 291, 292, 293,
- 294, 295, 296, 297, 298, 299, 300, 301,
- 302, 303, 293, 304, 305, 306, 307, 308,
- 309, 310, 301, 287, 288, 285, 1, 0,
- 311, 311, 214, 216, 59, 60, 61, 62,
- 3, 63, 64, 65, 66, 67, 68, 69,
- 70, 71, 4, 72, 73, 74, 75, 67,
- 311, 215, 217, 1, 221, 221, 223, 220,
- 225, 225, 221, 222, 219, 1, 228, 228,
- 227, 227, 229, 228, 230, 1, 234, 234,
- 236, 233, 238, 238, 234, 235, 232, 1,
- 241, 241, 240, 240, 242, 241, 243, 1,
- 247, 247, 249, 246, 251, 251, 247, 248,
- 245, 1, 254, 254, 253, 253, 255, 254,
- 256, 1, 260, 260, 262, 259, 264, 264,
- 260, 261, 258, 1, 267, 267, 266, 266,
- 268, 267, 269, 1, 273, 273, 275, 272,
- 277, 277, 273, 274, 271, 1, 280, 280,
- 279, 279, 281, 280, 282, 1, 286, 287,
- 287, 289, 285, 291, 292, 293, 294, 295,
- 296, 297, 298, 299, 300, 301, 302, 303,
- 293, 304, 305, 306, 307, 308, 309, 310,
- 301, 287, 288, 284, 1, 312, 312, 313,
- 1, 313, 1, 286, 287, 287, 289, 290,
- 291, 292, 294, 295, 296, 297, 298, 299,
- 300, 301, 302, 303, 304, 305, 306, 307,
- 308, 309, 310, 301, 287, 288, 313, 1,
- 314, 314, 315, 315, 316, 314, 317, 1,
- 318, 318, 319, 319, 320, 318, 321, 1,
- 322, 323, 1, 324, 1, 325, 326, 326,
- 328, 329, 330, 331, 332, 333, 334, 335,
- 336, 337, 338, 339, 340, 341, 342, 332,
- 343, 344, 345, 346, 347, 348, 349, 340,
- 326, 327, 324, 1, 0, 350, 350, 318,
- 320, 59, 60, 61, 62, 3, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 4,
- 72, 73, 74, 75, 67, 350, 319, 321,
- 1, 325, 326, 326, 328, 324, 330, 331,
- 332, 333, 334, 335, 336, 337, 338, 339,
- 340, 341, 342, 332, 343, 344, 345, 346,
- 347, 348, 349, 340, 326, 327, 323, 1,
- 351, 351, 352, 1, 352, 1, 325, 326,
- 326, 328, 329, 330, 331, 333, 334, 335,
- 336, 337, 338, 339, 340, 341, 342, 343,
- 344, 345, 346, 347, 348, 349, 340, 326,
- 327, 352, 1, 353, 353, 354, 354, 355,
- 353, 356, 1, 357, 357, 358, 358, 359,
- 357, 360, 1, 361, 361, 362, 362, 363,
- 361, 364, 1, 365, 366, 1, 367, 1,
- 368, 368, 370, 371, 372, 372, 368, 369,
- 367, 1, 373, 373, 375, 376, 373, 374,
- 377, 1, 378, 379, 1, 380, 1, 381,
- 381, 383, 384, 385, 385, 381, 382, 380,
- 1, 386, 386, 388, 389, 386, 387, 390,
- 1, 391, 392, 1, 393, 1, 394, 394,
- 396, 397, 398, 398, 394, 395, 393, 1,
- 399, 399, 401, 402, 399, 400, 403, 1,
- 404, 405, 1, 406, 1, 407, 408, 408,
- 410, 411, 412, 413, 414, 415, 416, 417,
- 418, 419, 420, 421, 422, 423, 424, 414,
- 425, 426, 427, 428, 429, 430, 431, 422,
- 408, 409, 406, 1, 0, 432, 432, 361,
- 363, 59, 60, 61, 62, 3, 63, 64,
- 65, 66, 67, 68, 69, 70, 71, 4,
- 72, 73, 74, 75, 67, 432, 362, 364,
- 1, 368, 368, 370, 367, 372, 372, 368,
- 369, 366, 1, 375, 375, 374, 374, 376,
- 375, 377, 1, 381, 381, 383, 380, 385,
- 385, 381, 382, 379, 1, 388, 388, 387,
- 387, 389, 388, 390, 1, 394, 394, 396,
- 393, 398, 398, 394, 395, 392, 1, 401,
- 401, 400, 400, 402, 401, 403, 1, 407,
- 408, 408, 410, 406, 412, 413, 414, 415,
- 416, 417, 418, 419, 420, 421, 422, 423,
- 424, 414, 425, 426, 427, 428, 429, 430,
- 431, 422, 408, 409, 405, 1, 433, 433,
- 434, 1, 434, 1, 407, 408, 408, 410,
- 411, 412, 413, 415, 416, 417, 418, 419,
- 420, 421, 422, 423, 424, 425, 426, 427,
- 428, 429, 430, 431, 422, 408, 409, 434,
- 1, 435, 435, 436, 436, 437, 435, 438,
- 1, 439, 439, 440, 440, 441, 439, 442,
- 1, 443, 444, 1, 445, 1, 446, 446,
- 448, 449, 450, 450, 446, 447, 445, 1,
- 451, 451, 453, 454, 451, 452, 455, 1,
- 456, 457, 1, 458, 1, 459, 459, 461,
- 462, 463, 463, 459, 460, 458, 1, 464,
- 464, 466, 467, 464, 465, 468, 1, 469,
- 470, 1, 471, 1, 472, 472, 474, 475,
- 476, 476, 472, 473, 471, 1, 477, 477,
- 479, 480, 477, 478, 481, 1, 482, 483,
- 1, 484, 1, 485, 486, 486, 488, 489,
- 490, 491, 492, 493, 494, 495, 496, 497,
- 498, 499, 500, 501, 502, 492, 503, 504,
- 505, 506, 507, 508, 509, 500, 486, 487,
- 484, 1, 0, 510, 510, 439, 441, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 510, 440, 442, 1, 446,
- 446, 448, 445, 450, 450, 446, 447, 444,
- 1, 453, 453, 452, 452, 454, 453, 455,
- 1, 459, 459, 461, 458, 463, 463, 459,
- 460, 457, 1, 466, 466, 465, 465, 467,
- 466, 468, 1, 472, 472, 474, 471, 476,
- 476, 472, 473, 470, 1, 479, 479, 478,
- 478, 480, 479, 481, 1, 485, 486, 486,
- 488, 484, 490, 491, 492, 493, 494, 495,
- 496, 497, 498, 499, 500, 501, 502, 492,
- 503, 504, 505, 506, 507, 508, 509, 500,
- 486, 487, 483, 1, 511, 511, 512, 1,
- 512, 1, 485, 486, 486, 488, 489, 490,
- 491, 493, 494, 495, 496, 497, 498, 499,
- 500, 501, 502, 503, 504, 505, 506, 507,
- 508, 509, 500, 486, 487, 512, 1, 513,
- 513, 514, 514, 515, 513, 516, 1, 517,
- 517, 518, 518, 519, 517, 520, 1, 521,
- 522, 1, 523, 1, 524, 524, 526, 527,
- 528, 528, 524, 525, 523, 1, 529, 529,
- 531, 532, 529, 530, 533, 1, 534, 535,
- 1, 536, 1, 537, 538, 538, 540, 541,
+ 0, 0, 2, 2, 3, 0, 4, 1,
+ 5, 5, 6, 6, 7, 5, 8, 1,
+ 9, 10, 1, 11, 1, 12, 12, 14,
+ 15, 16, 16, 12, 13, 11, 1, 17,
+ 17, 19, 20, 17, 18, 21, 1, 22,
+ 23, 1, 24, 1, 25, 26, 1, 27,
+ 1, 28, 28, 30, 31, 32, 32, 28,
+ 29, 27, 1, 33, 33, 35, 36, 33,
+ 34, 37, 1, 38, 39, 1, 40, 1,
+ 41, 41, 42, 42, 43, 41, 44, 1,
+ 28, 28, 30, 27, 32, 32, 28, 29,
+ 26, 1, 35, 35, 34, 34, 36, 35,
+ 37, 1, 45, 45, 46, 45, 47, 1,
+ 48, 48, 49, 48, 50, 1, 51, 1,
+ 52, 52, 53, 54, 55, 55, 52, 51,
+ 1, 56, 56, 57, 58, 56, 59, 1,
+ 57, 57, 58, 57, 59, 1, 60, 1,
+ 61, 61, 63, 64, 65, 65, 61, 62,
+ 60, 1, 66, 66, 68, 69, 66, 67,
+ 70, 1, 71, 72, 1, 73, 1, 74,
+ 74, 75, 76, 76, 74, 73, 1, 77,
+ 77, 78, 79, 80, 77, 1, 78, 78,
+ 79, 80, 78, 1, 81, 81, 82, 83,
+ 84, 81, 1, 85, 85, 86, 87, 88,
+ 85, 1, 86, 86, 87, 88, 86, 1,
+ 89, 89, 91, 92, 89, 90, 93, 1,
+ 94, 94, 96, 97, 94, 95, 98, 1,
+ 99, 100, 1, 101, 1, 102, 102, 104,
+ 105, 106, 106, 102, 103, 101, 1, 107,
+ 107, 109, 110, 107, 108, 111, 1, 112,
+ 113, 1, 114, 1, 52, 52, 53, 51,
+ 55, 55, 52, 115, 1, 116, 116, 117,
+ 1, 117, 1, 52, 52, 53, 54, 52,
+ 117, 1, 118, 118, 119, 119, 120, 118,
+ 121, 1, 122, 122, 123, 123, 124, 122,
+ 125, 1, 126, 127, 1, 128, 1, 129,
+ 129, 131, 132, 133, 133, 129, 130, 128,
+ 1, 134, 134, 136, 137, 134, 135, 138,
+ 1, 139, 140, 1, 141, 1, 142, 142,
+ 144, 145, 146, 146, 142, 143, 141, 1,
+ 147, 147, 149, 150, 147, 148, 151, 1,
+ 152, 153, 1, 154, 1, 155, 155, 157,
+ 158, 159, 159, 155, 156, 154, 1, 160,
+ 160, 162, 163, 160, 161, 164, 1, 165,
+ 166, 1, 167, 1, 168, 168, 170, 171,
+ 172, 172, 168, 169, 167, 1, 173, 173,
+ 175, 176, 173, 174, 177, 1, 178, 179,
+ 1, 180, 1, 181, 181, 183, 184, 185,
+ 185, 181, 182, 180, 1, 186, 186, 188,
+ 189, 186, 187, 190, 1, 191, 192, 1,
+ 193, 1, 129, 129, 131, 128, 133, 133,
+ 129, 130, 127, 1, 136, 136, 135, 135,
+ 137, 136, 138, 1, 142, 142, 144, 141,
+ 146, 146, 142, 143, 140, 1, 149, 149,
+ 148, 148, 150, 149, 151, 1, 155, 155,
+ 157, 154, 159, 159, 155, 156, 153, 1,
+ 162, 162, 161, 161, 163, 162, 164, 1,
+ 168, 168, 170, 167, 172, 172, 168, 169,
+ 166, 1, 175, 175, 174, 174, 176, 175,
+ 177, 1, 181, 181, 183, 180, 185, 185,
+ 181, 182, 179, 1, 188, 188, 187, 187,
+ 189, 188, 190, 1, 194, 194, 195, 1,
+ 195, 1, 196, 196, 197, 197, 198, 196,
+ 199, 1, 200, 200, 201, 201, 202, 200,
+ 203, 1, 204, 205, 1, 206, 1, 207,
+ 207, 208, 1, 208, 1, 209, 209, 210,
+ 210, 211, 209, 212, 1, 213, 213, 214,
+ 214, 215, 213, 216, 1, 217, 217, 218,
+ 218, 219, 217, 220, 1, 221, 222, 1,
+ 223, 1, 224, 224, 226, 227, 228, 228,
+ 224, 225, 223, 1, 229, 229, 231, 232,
+ 229, 230, 233, 1, 234, 235, 1, 236,
+ 1, 237, 237, 239, 240, 241, 241, 237,
+ 238, 236, 1, 242, 242, 244, 245, 242,
+ 243, 246, 1, 247, 248, 1, 249, 1,
+ 250, 250, 252, 253, 254, 254, 250, 251,
+ 249, 1, 255, 255, 257, 258, 255, 256,
+ 259, 1, 260, 261, 1, 262, 1, 224,
+ 224, 226, 223, 228, 228, 224, 225, 222,
+ 1, 231, 231, 230, 230, 232, 231, 233,
+ 1, 237, 237, 239, 236, 241, 241, 237,
+ 238, 235, 1, 244, 244, 243, 243, 245,
+ 244, 246, 1, 250, 250, 252, 249, 254,
+ 254, 250, 251, 248, 1, 257, 257, 256,
+ 256, 258, 257, 259, 1, 263, 263, 264,
+ 1, 264, 1, 265, 265, 266, 266, 267,
+ 265, 268, 1, 269, 269, 270, 270, 271,
+ 269, 272, 1, 273, 274, 1, 275, 1,
+ 276, 276, 278, 279, 280, 280, 276, 277,
+ 275, 1, 281, 281, 283, 284, 281, 282,
+ 285, 1, 286, 287, 1, 288, 1, 289,
+ 289, 291, 292, 293, 293, 289, 290, 288,
+ 1, 294, 294, 296, 297, 294, 295, 298,
+ 1, 299, 300, 1, 301, 1, 302, 302,
+ 304, 305, 306, 306, 302, 303, 301, 1,
+ 307, 307, 309, 310, 307, 308, 311, 1,
+ 312, 313, 1, 314, 1, 276, 276, 278,
+ 275, 280, 280, 276, 277, 274, 1, 283,
+ 283, 282, 282, 284, 283, 285, 1, 289,
+ 289, 291, 288, 293, 293, 289, 290, 287,
+ 1, 296, 296, 295, 295, 297, 296, 298,
+ 1, 302, 302, 304, 301, 306, 306, 302,
+ 303, 300, 1, 309, 309, 308, 308, 310,
+ 309, 311, 1, 315, 315, 316, 1, 316,
+ 1, 317, 317, 318, 318, 319, 317, 320,
+ 1, 321, 321, 322, 322, 323, 321, 324,
+ 1, 325, 326, 1, 327, 1, 328, 328,
+ 330, 331, 332, 332, 328, 329, 327, 1,
+ 333, 333, 335, 336, 333, 334, 337, 1,
+ 338, 339, 1, 340, 1, 328, 328, 330,
+ 327, 332, 332, 328, 329, 326, 1, 335,
+ 335, 334, 334, 336, 335, 337, 1, 341,
+ 341, 342, 1, 342, 1, 343, 343, 344,
+ 344, 345, 343, 346, 1, 347, 347, 348,
+ 348, 349, 347, 350, 1, 351, 352, 1,
+ 353, 1, 354, 354, 355, 1, 355, 1,
+ 356, 356, 357, 356, 358, 1, 359, 359,
+ 360, 360, 361, 359, 362, 1, 363, 363,
+ 364, 364, 365, 363, 366, 1, 367, 367,
+ 368, 368, 369, 367, 370, 1, 371, 371,
+ 372, 372, 373, 371, 374, 1, 12, 12,
+ 14, 11, 16, 16, 12, 13, 10, 1,
+ 19, 19, 18, 18, 20, 19, 21, 1,
+ 375, 375, 376, 1, 376, 1, 377, 377,
+ 378, 378, 379, 377, 380, 1, 381, 381,
+ 382, 382, 383, 381, 384, 1, 385, 385,
+ 386, 386, 387, 385, 388, 1, 389, 389,
+ 390, 390, 391, 389, 392, 1, 393, 393,
+ 394, 1, 394, 1, 12, 12, 14, 15,
+ 12, 13, 394, 1, 395, 395, 396, 1,
+ 396, 1, 328, 328, 330, 331, 328, 329,
+ 396, 1, 397, 397, 398, 1, 398, 1,
+ 302, 302, 304, 305, 302, 303, 398, 1,
+ 399, 399, 400, 1, 400, 1, 289, 289,
+ 291, 292, 289, 290, 400, 1, 401, 401,
+ 402, 1, 402, 1, 276, 276, 278, 279,
+ 276, 277, 402, 1, 403, 403, 404, 1,
+ 404, 1, 250, 250, 252, 253, 250, 251,
+ 404, 1, 405, 405, 406, 1, 406, 1,
+ 237, 237, 239, 240, 237, 238, 406, 1,
+ 407, 407, 408, 1, 408, 1, 224, 224,
+ 226, 227, 224, 225, 408, 1, 409, 409,
+ 410, 1, 410, 1, 181, 181, 183, 184,
+ 181, 182, 410, 1, 411, 411, 412, 1,
+ 412, 1, 168, 168, 170, 171, 168, 169,
+ 412, 1, 413, 413, 414, 1, 414, 1,
+ 155, 155, 157, 158, 155, 156, 414, 1,
+ 415, 415, 416, 1, 416, 1, 142, 142,
+ 144, 145, 142, 143, 416, 1, 417, 417,
+ 418, 1, 418, 1, 129, 129, 131, 132,
+ 129, 130, 418, 1, 419, 419, 420, 1,
+ 420, 1, 109, 109, 108, 108, 110, 109,
+ 111, 1, 421, 421, 422, 1, 422, 1,
+ 102, 102, 104, 105, 102, 103, 422, 1,
+ 102, 102, 104, 101, 106, 106, 102, 103,
+ 100, 1, 96, 96, 95, 95, 97, 96,
+ 98, 1, 423, 423, 425, 426, 423, 424,
+ 427, 1, 428, 428, 429, 430, 431, 428,
+ 1, 432, 432, 433, 1, 433, 1, 74,
+ 74, 75, 74, 433, 1, 74, 74, 75,
+ 73, 76, 76, 74, 72, 1, 68, 68,
+ 67, 67, 69, 68, 70, 1, 434, 434,
+ 435, 1, 435, 1, 61, 61, 63, 64,
+ 61, 62, 435, 1, 61, 61, 63, 60,
+ 65, 65, 61, 62, 436, 1, 437, 437,
+ 438, 1, 438, 1, 439, 439, 440, 1,
+ 440, 1, 28, 28, 30, 31, 28, 29,
+ 440, 1, 441, 441, 442, 443, 441, 1,
+ 444, 444, 446, 447, 448, 449, 450, 451,
+ 452, 453, 454, 455, 456, 457, 458, 459,
+ 460, 450, 461, 462, 463, 464, 465, 466,
+ 467, 458, 444, 445, 24, 1, 468, 468,
+ 41, 43, 469, 470, 471, 472, 442, 473,
+ 474, 475, 476, 477, 478, 479, 480, 481,
+ 443, 482, 483, 484, 485, 477, 468, 42,
+ 44, 1, 486, 486, 488, 489, 490, 491,
+ 492, 493, 494, 495, 496, 497, 498, 499,
+ 500, 501, 502, 492, 503, 504, 505, 506,
+ 507, 508, 509, 500, 486, 487, 40, 1,
+ 486, 486, 488, 40, 490, 491, 492, 493,
+ 494, 495, 496, 497, 498, 499, 500, 501,
+ 502, 492, 503, 504, 505, 506, 507, 508,
+ 509, 500, 486, 487, 39, 1, 510, 510,
+ 511, 512, 513, 514, 515, 516, 517, 518,
+ 519, 520, 521, 522, 523, 524, 525, 515,
+ 526, 527, 528, 529, 530, 531, 532, 523,
+ 510, 114, 1, 533, 533, 48, 49, 469,
+ 470, 471, 472, 442, 473, 474, 475, 476,
+ 477, 478, 479, 480, 481, 443, 482, 483,
+ 484, 485, 477, 533, 50, 1, 534, 534,
+ 536, 537, 538, 539, 540, 541, 542, 543,
+ 544, 545, 546, 547, 548, 549, 550, 540,
+ 551, 552, 553, 554, 555, 556, 557, 548,
+ 534, 535, 193, 1, 558, 558, 122, 124,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 558, 123, 125, 1,
+ 534, 534, 536, 193, 538, 539, 540, 541,
542, 543, 544, 545, 546, 547, 548, 549,
- 550, 551, 552, 553, 554, 544, 555, 556,
- 557, 558, 559, 560, 561, 552, 538, 539,
- 536, 1, 0, 562, 562, 517, 519, 59,
- 60, 61, 62, 3, 63, 64, 65, 66,
- 67, 68, 69, 70, 71, 4, 72, 73,
- 74, 75, 67, 562, 518, 520, 1, 524,
- 524, 526, 523, 528, 528, 524, 525, 522,
- 1, 531, 531, 530, 530, 532, 531, 533,
- 1, 537, 538, 538, 540, 536, 542, 543,
- 544, 545, 546, 547, 548, 549, 550, 551,
- 552, 553, 554, 544, 555, 556, 557, 558,
- 559, 560, 561, 552, 538, 539, 535, 1,
- 563, 563, 564, 1, 564, 1, 537, 538,
- 538, 540, 541, 542, 543, 545, 546, 547,
- 548, 549, 550, 551, 552, 553, 554, 555,
- 556, 557, 558, 559, 560, 561, 552, 538,
- 539, 564, 1, 565, 565, 566, 566, 567,
- 565, 568, 1, 569, 569, 570, 570, 571,
- 569, 572, 1, 573, 574, 1, 575, 1,
- 576, 577, 577, 579, 580, 581, 582, 583,
- 584, 585, 586, 587, 588, 589, 590, 591,
- 592, 593, 583, 594, 595, 596, 597, 598,
- 599, 600, 591, 577, 578, 575, 1, 0,
- 601, 601, 569, 571, 59, 60, 61, 62,
- 3, 63, 64, 65, 66, 67, 68, 69,
- 70, 71, 4, 72, 73, 74, 75, 67,
- 601, 570, 572, 1, 576, 577, 577, 579,
- 575, 581, 582, 583, 584, 585, 586, 587,
- 588, 589, 590, 591, 592, 593, 583, 594,
- 595, 596, 597, 598, 599, 600, 591, 577,
- 578, 574, 1, 602, 602, 603, 1, 603,
- 1, 576, 577, 577, 579, 580, 581, 582,
- 584, 585, 586, 587, 588, 589, 590, 591,
+ 550, 540, 551, 552, 553, 554, 555, 556,
+ 557, 548, 534, 535, 192, 1, 534, 534,
+ 536, 537, 538, 539, 541, 542, 543, 544,
+ 545, 546, 547, 548, 549, 550, 551, 552,
+ 553, 554, 555, 556, 557, 548, 534, 535,
+ 195, 1, 559, 559, 561, 562, 563, 564,
+ 565, 566, 567, 568, 569, 570, 571, 572,
+ 573, 574, 575, 565, 576, 577, 578, 579,
+ 580, 581, 582, 573, 559, 560, 206, 1,
+ 583, 583, 200, 202, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 583, 201, 203, 1, 559, 559, 561, 206,
+ 563, 564, 565, 566, 567, 568, 569, 570,
+ 571, 572, 573, 574, 575, 565, 576, 577,
+ 578, 579, 580, 581, 582, 573, 559, 560,
+ 205, 1, 559, 559, 561, 562, 563, 564,
+ 566, 567, 568, 569, 570, 571, 572, 573,
+ 574, 575, 576, 577, 578, 579, 580, 581,
+ 582, 573, 559, 560, 208, 1, 584, 584,
+ 586, 587, 588, 589, 590, 591, 592, 593,
+ 594, 595, 596, 597, 598, 599, 600, 590,
+ 601, 602, 603, 604, 605, 606, 607, 598,
+ 584, 585, 262, 1, 608, 608, 217, 219,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 608, 218, 220, 1,
+ 584, 584, 586, 262, 588, 589, 590, 591,
592, 593, 594, 595, 596, 597, 598, 599,
- 600, 591, 577, 578, 603, 1, 604, 605,
- 605, 606, 607, 608, 609, 610, 611, 612,
+ 600, 590, 601, 602, 603, 604, 605, 606,
+ 607, 598, 584, 585, 261, 1, 584, 584,
+ 586, 587, 588, 589, 591, 592, 593, 594,
+ 595, 596, 597, 598, 599, 600, 601, 602,
+ 603, 604, 605, 606, 607, 598, 584, 585,
+ 264, 1, 609, 609, 611, 612, 613, 614,
+ 615, 616, 617, 618, 619, 620, 621, 622,
+ 623, 624, 625, 615, 626, 627, 628, 629,
+ 630, 631, 632, 623, 609, 610, 314, 1,
+ 633, 633, 269, 271, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 633, 270, 272, 1, 609, 609, 611, 314,
613, 614, 615, 616, 617, 618, 619, 620,
- 621, 622, 623, 624, 615, 605, 1, 0,
- 625, 625, 59, 60, 61, 62, 3, 63,
- 64, 65, 66, 67, 68, 69, 70, 71,
- 4, 72, 73, 74, 75, 67, 625, 1,
- 626, 626, 627, 626, 628, 1, 629, 629,
- 630, 630, 631, 629, 632, 1, 633, 633,
- 634, 634, 635, 633, 636, 1, 637, 637,
- 638, 638, 639, 637, 640, 1, 641, 641,
- 642, 642, 643, 641, 644, 1, 16, 16,
- 18, 15, 20, 20, 16, 17, 14, 1,
- 23, 23, 22, 22, 24, 23, 25, 1,
- 29, 30, 30, 32, 28, 34, 35, 36,
- 37, 38, 39, 40, 41, 42, 43, 44,
- 45, 46, 36, 47, 48, 49, 50, 51,
- 52, 53, 44, 30, 31, 27, 1, 645,
- 645, 646, 1, 646, 1, 29, 30, 30,
- 32, 33, 34, 35, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48,
- 49, 50, 51, 52, 53, 44, 30, 31,
- 646, 1, 647, 647, 648, 648, 649, 647,
- 650, 1, 651, 651, 652, 652, 653, 651,
- 654, 1, 655, 655, 656, 656, 657, 655,
- 658, 1, 659, 659, 660, 660, 661, 659,
- 662, 1, 663, 663, 664, 1, 664, 1,
- 16, 16, 18, 19, 16, 17, 664, 1,
- 665, 665, 666, 1, 666, 1, 524, 524,
- 526, 527, 524, 525, 666, 1, 667, 667,
- 668, 1, 668, 1, 472, 472, 474, 475,
- 472, 473, 668, 1, 669, 669, 670, 1,
- 670, 1, 459, 459, 461, 462, 459, 460,
- 670, 1, 671, 671, 672, 1, 672, 1,
- 446, 446, 448, 449, 446, 447, 672, 1,
- 673, 673, 674, 1, 674, 1, 394, 394,
- 396, 397, 394, 395, 674, 1, 675, 675,
- 676, 1, 676, 1, 381, 381, 383, 384,
- 381, 382, 676, 1, 677, 677, 678, 1,
- 678, 1, 368, 368, 370, 371, 368, 369,
- 678, 1, 679, 679, 680, 1, 680, 1,
- 273, 273, 275, 276, 273, 274, 680, 1,
- 681, 681, 682, 1, 682, 1, 260, 260,
- 262, 263, 260, 261, 682, 1, 683, 683,
- 684, 1, 684, 1, 247, 247, 249, 250,
- 247, 248, 684, 1, 685, 685, 686, 1,
- 686, 1, 234, 234, 236, 237, 234, 235,
- 686, 1, 687, 687, 688, 1, 688, 1,
- 221, 221, 223, 224, 221, 222, 688, 1,
- 689, 689, 690, 1, 690, 1, 182, 183,
- 183, 184, 185, 186, 187, 189, 190, 191,
- 192, 193, 194, 195, 196, 197, 198, 199,
- 200, 201, 202, 203, 204, 205, 196, 183,
- 690, 1, 182, 183, 183, 184, 181, 186,
- 187, 188, 189, 190, 191, 192, 193, 194,
- 195, 196, 197, 198, 188, 199, 200, 201,
- 202, 203, 204, 205, 196, 183, 180, 1,
- 176, 176, 175, 175, 177, 176, 178, 1,
- 691, 691, 692, 1, 692, 1, 169, 169,
- 171, 172, 169, 170, 692, 1, 169, 169,
- 171, 168, 173, 173, 169, 170, 167, 1,
- 163, 163, 162, 162, 164, 163, 165, 1,
- 693, 693, 694, 693, 1, 695, 695, 696,
- 695, 1, 697, 697, 698, 1, 698, 1,
- 146, 146, 147, 146, 698, 1, 146, 146,
- 147, 145, 148, 148, 146, 144, 1, 140,
- 140, 139, 139, 141, 140, 142, 1, 699,
- 699, 700, 1, 700, 1, 133, 133, 135,
- 136, 133, 134, 700, 1, 133, 133, 135,
- 132, 137, 137, 133, 134, 701, 1, 702,
- 702, 703, 1, 703, 1, 92, 93, 93,
- 95, 96, 97, 98, 100, 101, 102, 103,
- 104, 105, 106, 107, 108, 109, 110, 111,
- 112, 113, 114, 115, 116, 107, 93, 94,
- 703, 1, 704, 704, 705, 1, 705, 1,
- 79, 79, 81, 82, 79, 80, 705, 1,
- 1, 0
+ 621, 622, 623, 624, 625, 615, 626, 627,
+ 628, 629, 630, 631, 632, 623, 609, 610,
+ 313, 1, 609, 609, 611, 612, 613, 614,
+ 616, 617, 618, 619, 620, 621, 622, 623,
+ 624, 625, 626, 627, 628, 629, 630, 631,
+ 632, 623, 609, 610, 316, 1, 634, 634,
+ 636, 637, 638, 639, 640, 641, 642, 643,
+ 644, 645, 646, 647, 648, 649, 650, 640,
+ 651, 652, 653, 654, 655, 656, 657, 648,
+ 634, 635, 340, 1, 658, 658, 321, 323,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 658, 322, 324, 1,
+ 634, 634, 636, 340, 638, 639, 640, 641,
+ 642, 643, 644, 645, 646, 647, 648, 649,
+ 650, 640, 651, 652, 653, 654, 655, 656,
+ 657, 648, 634, 635, 339, 1, 634, 634,
+ 636, 637, 638, 639, 641, 642, 643, 644,
+ 645, 646, 647, 648, 649, 650, 651, 652,
+ 653, 654, 655, 656, 657, 648, 634, 635,
+ 342, 1, 659, 659, 661, 662, 663, 664,
+ 665, 666, 667, 668, 669, 670, 671, 672,
+ 673, 674, 675, 665, 676, 677, 678, 679,
+ 680, 681, 682, 673, 659, 660, 353, 1,
+ 683, 683, 347, 349, 469, 470, 471, 472,
+ 442, 473, 474, 475, 476, 477, 478, 479,
+ 480, 481, 443, 482, 483, 484, 485, 477,
+ 683, 348, 350, 1, 659, 659, 661, 353,
+ 663, 664, 665, 666, 667, 668, 669, 670,
+ 671, 672, 673, 674, 675, 665, 676, 677,
+ 678, 679, 680, 681, 682, 673, 659, 660,
+ 352, 1, 659, 659, 661, 662, 663, 664,
+ 666, 667, 668, 669, 670, 671, 672, 673,
+ 674, 675, 676, 677, 678, 679, 680, 681,
+ 682, 673, 659, 660, 355, 1, 684, 684,
+ 685, 686, 687, 688, 689, 690, 691, 692,
+ 693, 694, 695, 696, 697, 698, 699, 700,
+ 701, 702, 703, 694, 684, 1, 704, 704,
+ 469, 470, 471, 472, 442, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 443, 482,
+ 483, 484, 485, 477, 704, 1, 444, 444,
+ 446, 24, 448, 449, 450, 451, 452, 453,
+ 454, 455, 456, 457, 458, 459, 460, 450,
+ 461, 462, 463, 464, 465, 466, 467, 458,
+ 444, 445, 23, 1, 444, 444, 446, 447,
+ 448, 449, 451, 452, 453, 454, 455, 456,
+ 457, 458, 459, 460, 461, 462, 463, 464,
+ 465, 466, 467, 458, 444, 445, 376, 1,
+ 510, 510, 511, 512, 513, 514, 516, 517,
+ 518, 519, 520, 521, 522, 523, 524, 525,
+ 526, 527, 528, 529, 530, 531, 532, 523,
+ 510, 420, 1, 510, 510, 511, 114, 513,
+ 514, 515, 516, 517, 518, 519, 520, 521,
+ 522, 523, 524, 525, 515, 526, 527, 528,
+ 529, 530, 531, 532, 523, 510, 113, 1,
+ 486, 486, 488, 489, 490, 491, 493, 494,
+ 495, 496, 497, 498, 499, 500, 501, 502,
+ 503, 504, 505, 506, 507, 508, 509, 500,
+ 486, 487, 438, 1, 0
};
static const short _svg_path_trans_targs[] = {
- 270, 0, 1, 2, 193, 3, 4, 5,
- 194, 3, 4, 5, 194, 5, 194, 6,
- 7, 8, 195, 9, 204, 7, 8, 195,
- 9, 196, 9, 196, 10, 270, 11, 12,
- 19, 13, 23, 54, 197, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 11, 12,
- 19, 13, 20, 23, 54, 94, 104, 105,
- 133, 161, 177, 187, 189, 190, 191, 192,
- 200, 201, 202, 203, 13, 20, 14, 15,
- 16, 21, 17, 267, 15, 16, 21, 17,
- 22, 17, 22, 18, 270, 11, 12, 19,
- 13, 23, 54, 264, 94, 104, 2, 105,
- 133, 161, 177, 187, 189, 190, 191, 192,
- 193, 200, 201, 202, 203, 24, 25, 50,
- 24, 25, 50, 26, 27, 28, 29, 51,
- 27, 28, 29, 263, 30, 31, 32, 259,
- 33, 260, 31, 32, 259, 33, 258, 33,
- 258, 34, 35, 36, 255, 35, 36, 37,
- 254, 38, 39, 38, 39, 40, 253, 41,
- 252, 41, 42, 252, 43, 251, 43, 251,
- 44, 45, 46, 247, 47, 248, 45, 46,
- 247, 47, 246, 47, 246, 48, 270, 49,
- 24, 25, 23, 54, 243, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 49, 50,
- 52, 53, 55, 56, 57, 80, 55, 56,
- 57, 80, 57, 80, 58, 59, 60, 81,
- 61, 240, 59, 60, 81, 61, 82, 61,
- 82, 62, 63, 64, 83, 65, 237, 63,
- 64, 83, 65, 84, 65, 84, 66, 67,
- 68, 85, 69, 234, 67, 68, 85, 69,
- 86, 69, 86, 70, 71, 72, 87, 73,
- 231, 71, 72, 87, 73, 88, 73, 88,
- 74, 75, 76, 89, 77, 228, 75, 76,
- 89, 77, 90, 77, 90, 78, 270, 79,
- 56, 55, 57, 23, 54, 91, 94, 104,
- 2, 105, 133, 161, 177, 187, 189, 190,
- 191, 192, 193, 200, 201, 202, 203, 79,
- 92, 93, 95, 96, 97, 100, 95, 96,
- 97, 100, 97, 100, 98, 270, 99, 96,
- 95, 97, 23, 54, 101, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 99, 102,
- 103, 19, 12, 13, 20, 106, 107, 108,
- 123, 106, 107, 108, 123, 108, 123, 109,
- 110, 111, 124, 112, 225, 110, 111, 124,
- 112, 125, 112, 125, 113, 114, 115, 126,
- 116, 222, 114, 115, 126, 116, 127, 116,
- 127, 117, 118, 119, 128, 120, 219, 118,
- 119, 128, 120, 129, 120, 129, 121, 270,
- 122, 107, 106, 108, 23, 54, 130, 94,
- 104, 2, 105, 133, 161, 177, 187, 189,
- 190, 191, 192, 193, 200, 201, 202, 203,
- 122, 131, 132, 134, 135, 136, 151, 134,
- 135, 136, 151, 136, 151, 137, 138, 139,
- 152, 140, 216, 138, 139, 152, 140, 153,
- 140, 153, 141, 142, 143, 154, 144, 213,
- 142, 143, 154, 144, 155, 144, 155, 145,
- 146, 147, 156, 148, 210, 146, 147, 156,
- 148, 157, 148, 157, 149, 270, 150, 135,
- 134, 136, 23, 54, 158, 94, 104, 2,
- 105, 133, 161, 177, 187, 189, 190, 191,
- 192, 193, 200, 201, 202, 203, 150, 159,
- 160, 162, 163, 164, 171, 162, 163, 164,
- 171, 164, 171, 165, 166, 167, 172, 168,
- 207, 166, 167, 172, 168, 173, 168, 173,
- 169, 270, 170, 163, 162, 164, 23, 54,
- 174, 94, 104, 2, 105, 133, 161, 177,
- 187, 189, 190, 191, 192, 193, 200, 201,
- 202, 203, 170, 175, 176, 178, 179, 180,
- 183, 178, 179, 180, 183, 180, 183, 181,
- 270, 182, 179, 178, 180, 23, 54, 184,
- 94, 104, 2, 105, 133, 161, 177, 187,
- 189, 190, 191, 192, 193, 200, 201, 202,
- 203, 182, 185, 186, 270, 188, 23, 54,
- 94, 104, 2, 105, 133, 161, 177, 187,
- 189, 190, 191, 192, 193, 200, 201, 202,
- 203, 188, 24, 25, 50, 55, 56, 57,
- 80, 95, 96, 97, 100, 19, 12, 13,
- 20, 3, 4, 5, 194, 198, 199, 106,
- 107, 108, 123, 134, 135, 136, 151, 162,
- 163, 164, 171, 178, 179, 180, 183, 205,
- 206, 208, 209, 211, 212, 214, 215, 217,
- 218, 220, 221, 223, 224, 226, 227, 229,
- 230, 232, 233, 235, 236, 238, 239, 241,
- 242, 244, 245, 249, 250, 41, 252, 38,
- 39, 256, 257, 261, 262, 263, 265, 266,
- 268, 269
+ 2, 0, 3, 4, 161, 2, 3, 4,
+ 161, 4, 161, 5, 6, 7, 162, 8,
+ 169, 6, 7, 162, 8, 265, 8, 265,
+ 233, 10, 16, 11, 12, 13, 17, 14,
+ 229, 12, 13, 17, 14, 236, 14, 236,
+ 235, 15, 9, 10, 16, 19, 20, 43,
+ 19, 20, 43, 21, 22, 23, 24, 44,
+ 22, 23, 24, 226, 25, 26, 27, 222,
+ 28, 223, 26, 27, 222, 28, 221, 28,
+ 221, 29, 30, 31, 218, 30, 31, 32,
+ 217, 33, 34, 35, 216, 33, 34, 35,
+ 216, 36, 37, 215, 38, 214, 36, 37,
+ 215, 38, 214, 38, 214, 39, 40, 41,
+ 210, 42, 211, 40, 41, 210, 42, 268,
+ 42, 268, 237, 43, 45, 46, 48, 49,
+ 50, 71, 48, 49, 50, 71, 50, 71,
+ 51, 52, 53, 72, 54, 205, 52, 53,
+ 72, 54, 73, 54, 73, 55, 56, 57,
+ 74, 58, 202, 56, 57, 74, 58, 75,
+ 58, 75, 59, 60, 61, 76, 62, 199,
+ 60, 61, 76, 62, 77, 62, 77, 63,
+ 64, 65, 78, 66, 196, 64, 65, 78,
+ 66, 79, 66, 79, 67, 68, 69, 80,
+ 70, 193, 68, 69, 80, 70, 241, 70,
+ 241, 239, 82, 242, 84, 85, 86, 245,
+ 84, 85, 86, 245, 86, 245, 243, 88,
+ 246, 15, 9, 10, 16, 91, 92, 93,
+ 106, 91, 92, 93, 106, 93, 106, 94,
+ 95, 96, 107, 97, 190, 95, 96, 107,
+ 97, 108, 97, 108, 98, 99, 100, 109,
+ 101, 187, 99, 100, 109, 101, 110, 101,
+ 110, 102, 103, 104, 111, 105, 184, 103,
+ 104, 111, 105, 249, 105, 249, 247, 113,
+ 250, 115, 116, 117, 130, 115, 116, 117,
+ 130, 117, 130, 118, 119, 120, 131, 121,
+ 181, 119, 120, 131, 121, 132, 121, 132,
+ 122, 123, 124, 133, 125, 178, 123, 124,
+ 133, 125, 134, 125, 134, 126, 127, 128,
+ 135, 129, 175, 127, 128, 135, 129, 253,
+ 129, 253, 251, 137, 254, 139, 140, 141,
+ 146, 139, 140, 141, 146, 141, 146, 142,
+ 143, 144, 147, 145, 172, 143, 144, 147,
+ 145, 257, 145, 257, 255, 149, 258, 151,
+ 152, 153, 261, 151, 152, 153, 261, 153,
+ 261, 259, 155, 262, 19, 20, 43, 48,
+ 49, 50, 71, 84, 85, 86, 245, 15,
+ 9, 10, 16, 2, 3, 4, 161, 164,
+ 266, 91, 92, 93, 106, 115, 116, 117,
+ 130, 139, 140, 141, 146, 151, 152, 153,
+ 261, 170, 171, 173, 174, 176, 177, 179,
+ 180, 182, 183, 185, 186, 188, 189, 191,
+ 192, 194, 195, 197, 198, 200, 201, 203,
+ 204, 206, 207, 209, 267, 212, 213, 36,
+ 37, 215, 38, 214, 33, 34, 35, 216,
+ 219, 220, 224, 225, 226, 228, 269, 230,
+ 231, 232, 1, 160, 234, 9, 15, 10,
+ 18, 47, 163, 83, 89, 1, 90, 114,
+ 138, 150, 263, 156, 157, 158, 159, 160,
+ 165, 166, 167, 168, 234, 18, 47, 83,
+ 89, 90, 114, 138, 150, 263, 156, 157,
+ 158, 159, 165, 166, 167, 168, 234, 9,
+ 15, 10, 18, 47, 227, 83, 89, 1,
+ 90, 114, 138, 150, 263, 156, 157, 158,
+ 159, 160, 165, 166, 167, 168, 238, 19,
+ 20, 18, 47, 208, 83, 89, 1, 90,
+ 114, 138, 150, 263, 156, 157, 158, 159,
+ 160, 165, 166, 167, 168, 238, 240, 49,
+ 48, 50, 18, 47, 81, 83, 89, 1,
+ 90, 114, 138, 150, 263, 156, 157, 158,
+ 159, 160, 165, 166, 167, 168, 240, 244,
+ 85, 84, 86, 18, 47, 87, 83, 89,
+ 1, 90, 114, 138, 150, 263, 156, 157,
+ 158, 159, 160, 165, 166, 167, 168, 244,
+ 248, 92, 91, 93, 18, 47, 112, 83,
+ 89, 1, 90, 114, 138, 150, 263, 156,
+ 157, 158, 159, 160, 165, 166, 167, 168,
+ 248, 252, 116, 115, 117, 18, 47, 136,
+ 83, 89, 1, 90, 114, 138, 150, 263,
+ 156, 157, 158, 159, 160, 165, 166, 167,
+ 168, 252, 256, 140, 139, 141, 18, 47,
+ 148, 83, 89, 1, 90, 114, 138, 150,
+ 263, 156, 157, 158, 159, 160, 165, 166,
+ 167, 168, 256, 260, 152, 151, 153, 18,
+ 47, 154, 83, 89, 1, 90, 114, 138,
+ 150, 263, 156, 157, 158, 159, 160, 165,
+ 166, 167, 168, 260, 264, 18, 47, 83,
+ 89, 1, 90, 114, 138, 150, 263, 156,
+ 157, 158, 159, 160, 165, 166, 167, 168,
+ 264
};
static const char _svg_path_trans_actions[] = {
- 15, 0, 0, 0, 0, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 60, 20, 56,
- 20, 56, 20, 20, 0, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 0, 1,
- 0, 1, 1, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 3,
- 17, 3, 17, 0, 0, 1, 0, 1,
- 1, 0, 0, 0, 68, 23, 64, 23,
- 64, 23, 23, 0, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 23, 23, 23,
- 23, 23, 23, 23, 23, 9, 47, 47,
- 0, 1, 1, 0, 3, 3, 17, 0,
- 0, 0, 1, 1, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 3, 0, 0, 0, 0,
- 0, 7, 7, 0, 0, 0, 0, 7,
- 7, 0, 1, 0, 1, 1, 0, 0,
- 0, 3, 17, 3, 17, 0, 0, 1,
- 0, 1, 1, 0, 0, 0, 124, 44,
- 44, 120, 44, 44, 0, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 44, 44,
- 44, 44, 44, 44, 44, 44, 0, 0,
- 0, 0, 9, 47, 47, 47, 0, 1,
- 1, 1, 0, 0, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 17, 3, 17, 0, 0,
- 1, 0, 1, 1, 0, 0, 0, 3,
- 17, 3, 17, 0, 0, 1, 0, 1,
- 1, 0, 0, 0, 3, 17, 3, 17,
+ 9, 0, 51, 51, 51, 0, 1, 1,
+ 1, 0, 0, 0, 3, 15, 3, 15,
+ 0, 0, 1, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0, 3, 15, 3, 15,
0, 0, 1, 0, 1, 1, 0, 0,
- 0, 3, 17, 3, 17, 0, 0, 1,
- 0, 1, 1, 0, 0, 0, 92, 32,
- 88, 32, 88, 32, 32, 0, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 32,
- 32, 32, 32, 32, 32, 32, 32, 0,
- 0, 0, 9, 47, 47, 47, 0, 1,
- 1, 1, 0, 0, 0, 76, 26, 72,
- 26, 72, 26, 26, 0, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 0, 0,
- 0, 9, 47, 47, 47, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 3, 17, 3,
- 17, 0, 0, 1, 0, 1, 1, 0,
- 0, 0, 3, 17, 3, 17, 0, 0,
- 1, 0, 1, 1, 0, 0, 0, 108,
- 38, 104, 38, 104, 38, 38, 0, 38,
- 38, 38, 38, 38, 38, 38, 38, 38,
- 38, 38, 38, 38, 38, 38, 38, 38,
- 0, 0, 0, 9, 47, 47, 47, 0,
- 1, 1, 1, 0, 0, 0, 3, 17,
- 3, 17, 0, 0, 1, 0, 1, 1,
- 0, 0, 0, 3, 17, 3, 17, 0,
+ 0, 0, 1, 1, 1, 9, 51, 51,
+ 0, 1, 1, 0, 3, 3, 15, 0,
+ 0, 0, 1, 1, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 3, 3, 0, 0, 0, 0,
+ 0, 7, 7, 7, 7, 0, 0, 0,
+ 0, 7, 48, 7, 48, 48, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 9, 51,
+ 51, 51, 0, 1, 1, 1, 0, 0,
+ 0, 3, 15, 3, 15, 0, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 3, 15, 3, 15, 0,
0, 1, 0, 1, 1, 0, 0, 0,
- 3, 17, 3, 17, 0, 0, 1, 0,
- 1, 1, 0, 0, 0, 100, 35, 96,
- 35, 96, 35, 35, 0, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 0, 0,
- 0, 9, 47, 47, 47, 0, 1, 1,
- 1, 0, 0, 0, 3, 17, 3, 17,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 0, 0, 9, 51, 51, 51,
+ 0, 1, 1, 1, 0, 0, 0, 0,
+ 0, 9, 51, 51, 51, 9, 51, 51,
+ 51, 0, 1, 1, 1, 0, 0, 0,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 3, 15, 3,
+ 15, 0, 0, 1, 0, 1, 1, 0,
+ 0, 0, 3, 15, 3, 15, 0, 0,
+ 1, 0, 1, 1, 0, 0, 0, 0,
+ 0, 9, 51, 51, 51, 0, 1, 1,
+ 1, 0, 0, 0, 3, 15, 3, 15,
0, 0, 1, 0, 1, 1, 0, 0,
- 0, 116, 41, 112, 41, 112, 41, 41,
- 0, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 0, 0, 0, 9, 47, 47,
- 47, 0, 1, 1, 1, 0, 0, 0,
- 84, 29, 80, 29, 80, 29, 29, 0,
- 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 29, 29, 29, 29, 29, 29, 29,
- 29, 0, 0, 0, 53, 13, 13, 13,
+ 0, 3, 15, 3, 15, 0, 0, 1,
+ 0, 1, 1, 0, 0, 0, 3, 15,
+ 3, 15, 0, 0, 1, 0, 1, 1,
+ 0, 0, 0, 0, 0, 9, 51, 51,
+ 51, 0, 1, 1, 1, 0, 0, 0,
+ 3, 15, 3, 15, 0, 0, 1, 0,
+ 1, 1, 0, 0, 0, 0, 0, 9,
+ 51, 51, 51, 0, 1, 1, 1, 0,
+ 0, 0, 0, 0, 11, 54, 54, 11,
+ 54, 54, 54, 11, 54, 54, 54, 11,
+ 54, 54, 54, 11, 54, 54, 54, 0,
+ 0, 11, 54, 54, 54, 11, 54, 54,
+ 54, 11, 54, 54, 54, 11, 54, 54,
+ 54, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 5,
+ 45, 5, 45, 45, 5, 5, 5, 5,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 18, 57, 18, 57,
+ 18, 18, 0, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 21, 61,
+ 21, 61, 21, 21, 0, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 21, 21, 21, 21, 21, 21, 42, 42,
+ 89, 42, 42, 0, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 0, 30, 73,
+ 30, 73, 30, 30, 0, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 30, 30, 30, 30, 30, 30, 0, 24,
+ 65, 24, 65, 24, 24, 0, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 0,
+ 36, 81, 36, 81, 36, 36, 0, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 36, 36, 36, 36, 36, 36, 36, 36,
+ 0, 33, 77, 33, 77, 33, 33, 0,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 33, 33, 33, 33, 33, 33, 33,
+ 33, 0, 39, 85, 39, 85, 39, 39,
+ 0, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 39,
+ 39, 39, 0, 27, 69, 27, 69, 27,
+ 27, 0, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 27, 27, 27, 0, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13,
- 13, 0, 11, 50, 50, 11, 50, 50,
- 50, 11, 50, 50, 50, 11, 50, 50,
- 50, 11, 50, 50, 50, 0, 0, 11,
- 50, 50, 50, 11, 50, 50, 50, 11,
- 50, 50, 50, 11, 50, 50, 50, 0,
+ 0
+};
+
+static const char _svg_path_eof_actions[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 5, 5, 5,
- 5, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 18, 0, 21, 21, 42, 0, 30,
+ 0, 30, 30, 24, 0, 24, 24, 36,
+ 0, 36, 36, 33, 0, 33, 33, 39,
+ 0, 39, 39, 27, 0, 27, 27, 13,
+ 0, 18, 18, 42, 42, 21
};
-static const int svg_path_start = 1;
-static const int svg_path_first_final = 270;
+static const int svg_path_start = 232;
+static const int svg_path_first_final = 232;
-//static const int svg_path_en_main = 1;
+static const int svg_path_en_main = 232;
-void Parser::parse(char const *str)
-throw(SVGPathParseError)
+#line 47 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+
+
+SVGPathParser::SVGPathParser(PathSink &sink)
+ : _absolute(false)
+ , _sink(sink)
+ , _z_snap_threshold(0)
+ , _curve(NULL)
{
- char const *p = str;
- char const *start = NULL;
- int cs;
+ reset();
+}
+
+SVGPathParser::~SVGPathParser()
+{
+ delete _curve;
+}
- _reset();
+void SVGPathParser::reset() {
+ _absolute = false;
+ _current = _initial = Point(0, 0);
+ _quad_tangent = _cubic_tangent = Point(0, 0);
+ _params.clear();
+ delete _curve;
+ _curve = NULL;
+
+#line 1105 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
{
cs = svg_path_start;
}
+#line 73 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+
+}
+
+void SVGPathParser::parse(char const *str, int len)
+{
+ if (len < 0) {
+ len = std::strlen(str);
+ }
+ _parse(str, str + len, true);
+}
+
+void SVGPathParser::parse(std::string const &s)
+{
+ _parse(s.c_str(), s.c_str() + s.size(), true);
+}
+
+void SVGPathParser::feed(char const *str, int len)
+{
+ if (len < 0) {
+ len = std::strlen(str);
+ }
+ _parse(str, str + len, false);
+}
+
+void SVGPathParser::feed(std::string const &s)
+{
+ _parse(s.c_str(), s.c_str() + s.size(), false);
+}
+
+void SVGPathParser::finish()
+{
+ char const *empty = "";
+ _parse(empty, empty, true);
+}
+
+void SVGPathParser::_push(Coord value)
+{
+ _params.push_back(value);
+}
+
+Coord SVGPathParser::_pop()
+{
+ Coord value = _params.back();
+ _params.pop_back();
+ return value;
+}
+
+bool SVGPathParser::_pop_flag()
+{
+ return _pop() != 0.0;
+}
+
+Coord SVGPathParser::_pop_coord(Dim2 axis)
+{
+ if (_absolute) {
+ return _pop();
+ } else {
+ return _pop() + _current[axis];
+ }
+}
+
+Point SVGPathParser::_pop_point()
+{
+ Coord y = _pop_coord(Y);
+ Coord x = _pop_coord(X);
+ return Point(x, y);
+}
+
+void SVGPathParser::_moveTo(Point const &p)
+{
+ _pushCurve(NULL); // flush
+ _sink.moveTo(p);
+ _quad_tangent = _cubic_tangent = _current = _initial = p;
+}
+
+void SVGPathParser::_lineTo(Point const &p)
+{
+ _pushCurve(new LineSegment(_current, p));
+ _quad_tangent = _cubic_tangent = _current = p;
+}
+
+void SVGPathParser::_curveTo(Point const &c0, Point const &c1, Point const &p)
+{
+ _pushCurve(new CubicBezier(_current, c0, c1, p));
+ _quad_tangent = _current = p;
+ _cubic_tangent = p + ( p - c1 );
+}
+
+void SVGPathParser::_quadTo(Point const &c, Point const &p)
+{
+ _pushCurve(new QuadraticBezier(_current, c, p));
+ _cubic_tangent = _current = p;
+ _quad_tangent = p + ( p - c );
+}
+
+void SVGPathParser::_arcTo(Coord rx, Coord ry, Coord angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ if (_current == p) {
+ return; // ignore invalid (ambiguous) arc segments where start and end point are the same (per SVG spec)
+ }
+
+ _pushCurve(new EllipticalArc(_current, rx, ry, angle, large_arc, sweep, p));
+ _quad_tangent = _cubic_tangent = _current = p;
+}
+
+void SVGPathParser::_closePath()
+{
+ if (_curve && (!_absolute || !_moveto_was_absolute) &&
+ are_near(_initial, _current, _z_snap_threshold))
+ {
+ _curve->setFinal(_initial);
+ }
+
+ _pushCurve(NULL); // flush
+ _sink.closePath();
+ _quad_tangent = _cubic_tangent = _current = _initial;
+}
+
+void SVGPathParser::_pushCurve(Curve *c)
+{
+ if (_curve) {
+ _sink.feed(*_curve, false);
+ delete _curve;
+ }
+ _curve = c;
+}
+void SVGPathParser::_parse(char const *str, char const *strend, bool finish)
+{
+ char const *p = str;
+ char const *pe = strend;
+ char const *eof = finish ? pe : NULL;
+ char const *start = NULL;
+
+
+#line 1247 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
{
int _klen;
unsigned int _trans;
@@ -1168,6 +1251,8 @@ throw(SVGPathParseError)
unsigned int _nacts;
const char *_keys;
+ if ( p == pe )
+ goto _test_eof;
if ( cs == 0 )
goto _out;
_resume:
@@ -1233,63 +1318,76 @@ _match:
switch ( *_acts++ )
{
case 0:
+#line 209 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
start = p;
}
break;
case 1:
-
+#line 213 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
- char const *end=p;
- std::string buf(start, end);
- _push(g_ascii_strtod(buf.c_str(), NULL));
- start = NULL;
+ if (start) {
+ std::string buf(start, p);
+ _push(g_ascii_strtod(buf.c_str(), NULL));
+ start = NULL;
+ } else {
+ std::string buf(str, p);
+ _push(g_ascii_strtod((_number_part + buf).c_str(), NULL));
+ _number_part.clear();
+ }
}
break;
case 2:
-
+#line 225 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(1.0);
}
break;
case 3:
-
+#line 229 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_push(0.0);
}
break;
case 4:
-
+#line 233 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = true;
}
break;
case 5:
+#line 237 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_absolute = false;
}
break;
case 6:
+#line 241 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
+ _moveto_was_absolute = _absolute;
_moveTo(_pop_point());
}
break;
case 7:
+#line 246 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_lineTo(_pop_point());
}
break;
case 8:
+#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
- _hlineTo(Point(_pop_coord(X), _current[Y]));
+ _lineTo(Point(_pop_coord(X), _current[Y]));
}
break;
case 9:
+#line 254 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
- _vlineTo(Point(_current[X], _pop_coord(Y)));
+ _lineTo(Point(_current[X], _pop_coord(Y)));
}
break;
case 10:
+#line 258 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1298,6 +1396,7 @@ _match:
}
break;
case 11:
+#line 265 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c1 = _pop_point();
@@ -1305,6 +1404,7 @@ _match:
}
break;
case 12:
+#line 271 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
Point c = _pop_point();
@@ -1312,12 +1412,14 @@ _match:
}
break;
case 13:
+#line 277 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point p = _pop_point();
_quadTo(_quad_tangent, p);
}
break;
case 14:
+#line 282 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
Point point = _pop_point();
bool sweep = _pop_flag();
@@ -1330,42 +1432,169 @@ _match:
}
break;
case 15:
+#line 293 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
{
_closePath();
}
break;
- case 16:
- {{p++; goto _out; }}
- break;
+#line 1441 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
}
}
_again:
if ( cs == 0 )
goto _out;
- p += 1;
- goto _resume;
+ if ( ++p != pe )
+ goto _resume;
+ _test_eof: {}
+ if ( p == eof )
+ {
+ const char *__acts = _svg_path_actions + _svg_path_eof_actions[cs];
+ unsigned int __nacts = (unsigned int) *__acts++;
+ while ( __nacts-- > 0 ) {
+ switch ( *__acts++ ) {
+ case 1:
+#line 213 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ if (start) {
+ std::string buf(start, p);
+ _push(g_ascii_strtod(buf.c_str(), NULL));
+ start = NULL;
+ } else {
+ std::string buf(str, p);
+ _push(g_ascii_strtod((_number_part + buf).c_str(), NULL));
+ _number_part.clear();
+ }
+ }
+ break;
+ case 6:
+#line 241 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ _moveto_was_absolute = _absolute;
+ _moveTo(_pop_point());
+ }
+ break;
+ case 7:
+#line 246 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(_pop_point());
+ }
+ break;
+ case 8:
+#line 250 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(Point(_pop_coord(X), _current[Y]));
+ }
+ break;
+ case 9:
+#line 254 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ _lineTo(Point(_current[X], _pop_coord(Y)));
+ }
+ break;
+ case 10:
+#line 258 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c1 = _pop_point();
+ Point c0 = _pop_point();
+ _curveTo(c0, c1, p);
+ }
+ break;
+ case 11:
+#line 265 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c1 = _pop_point();
+ _curveTo(_cubic_tangent, c1, p);
+ }
+ break;
+ case 12:
+#line 271 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ Point c = _pop_point();
+ _quadTo(c, p);
+ }
+ break;
+ case 13:
+#line 277 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ Point p = _pop_point();
+ _quadTo(_quad_tangent, p);
+ }
+ break;
+ case 14:
+#line 282 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ Point point = _pop_point();
+ bool sweep = _pop_flag();
+ bool large_arc = _pop_flag();
+ double angle = deg_to_rad(_pop());
+ double ry = _pop();
+ double rx = _pop();
+
+ _arcTo(rx, ry, angle, large_arc, sweep, point);
+ }
+ break;
+ case 15:
+#line 293 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
+ {
+ _closePath();
+ }
+ break;
+#line 1547 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.cpp"
+ }
+ }
+ }
+
_out: {}
}
+#line 435 "/home/tweenk/src/lib2geom/src/2geom/svg-path-parser.rl"
- if ( cs < svg_path_first_final ) {
- throw SVGPathParseError();
+
+ if (finish) {
+ if (cs < svg_path_first_final) {
+ throw SVGPathParseError();
+ }
+ } else if (start != NULL) {
+ _number_part = std::string(start, pe);
}
-}
+ if (finish) {
+ _pushCurve(NULL);
+ _sink.flush();
+ reset();
+ }
}
void parse_svg_path(char const *str, PathSink &sink)
-throw(SVGPathParseError)
{
- Parser parser(sink);
+ SVGPathParser parser(sink);
parser.parse(str);
- sink.flush();
}
+void parse_svg_path_file(FILE *fi, PathSink &sink)
+{
+ static const size_t BUFFER_SIZE = 4096;
+ char buffer[BUFFER_SIZE];
+ size_t bytes_read;
+ SVGPathParser parser(sink);
+
+ while (true) {
+ bytes_read = fread(buffer, 1, BUFFER_SIZE, fi);
+ if (bytes_read < BUFFER_SIZE) {
+ parser.parse(buffer, bytes_read);
+ break;
+ } else {
+ parser.feed(buffer, bytes_read);
+ }
+ }
}
+} // namespace Geom
+
/*
Local Variables:
mode:c++
diff --git a/src/2geom/svg-path-parser.h b/src/2geom/svg-path-parser.h
index 163fbe5c4..e25316cf8 100644
--- a/src/2geom/svg-path-parser.h
+++ b/src/2geom/svg-path-parser.h
@@ -30,48 +30,161 @@
*
*/
-#ifndef SEEN_SVG_PATH_PARSER_H
-#define SEEN_SVG_PATH_PARSER_H
+#ifndef LIB2GEOM_SEEN_SVG_PATH_PARSER_H
+#define LIB2GEOM_SEEN_SVG_PATH_PARSER_H
-#include <vector>
+#include <iostream>
#include <iterator>
#include <stdexcept>
+#include <vector>
+#include <cstdio>
#include <2geom/exception.h>
#include <2geom/point.h>
#include <2geom/path-sink.h>
+#include <2geom/forward.h>
namespace Geom {
-void parse_svg_path(char const *str, PathSink &sink) throw(SVGPathParseError);
+/** @brief Read SVG path data and feed it to a PathSink
+ *
+ * This class provides an interface to an SVG path data parser written in Ragel.
+ * It supports parsing the path data either at once or block-by-block.
+ * Use the parse() functions to parse complete data and the feed() and finish()
+ * functions to parse partial data.
+ *
+ * The parser will call the appropriate methods on the PathSink supplied
+ * at construction. To store the path in memory as a PathVector, pass
+ * a PathBuilder. You can also use one of the freestanding helper functions
+ * if you don't need to parse data block-by-block.
+ *
+ * @ingroup Paths
+ */
+class SVGPathParser {
+public:
+ SVGPathParser(PathSink &sink);
+ ~SVGPathParser();
+
+ /** @brief Reset internal state.
+ * Discards the internal state associated with partially parsed data,
+ * letting you start from scratch. Note that any partial data written
+ * to the path sink is not affected - you need to clear it yourself. */
+ void reset();
+
+ /** @brief Parse a C-style string.
+ * The path sink is flushed and the internal state is reset after this call.
+ * Note that the state is not reset before this method, so you can use it to
+ * process the last block of partial data.
+ * @param str String to parse
+ * @param len Length of string or -1 if null-terminated */
+ void parse(char const *str, int len = -1);
+ /** @brief Parse an STL string. */
+ void parse(std::string const &s);
+
+ /** @brief Parse a part of path data stored in a C-style string.
+ * This method does not reset internal state, so it can be called multiple
+ * times to parse successive blocks of a longer SVG path data string.
+ * To finish parsing, call finish() after the final block or call parse()
+ * with the last block of data.
+ * @param str String to parse
+ * @param len Length of string or -1 if null-terminated */
+ void feed(char const *str, int len = -1);
+ /** @brief Parse a part of path data stored in an STL string. */
+ void feed(std::string const &s);
+
+ /** @brief Finalize parsing.
+ * After the last block of data was submitted with feed(), call this method
+ * to finalize parsing, flush the path sink and reset internal state.
+ * You should not call this after parse(). */
+ void finish();
-inline std::vector<Path> parse_svg_path(char const *str) throw(SVGPathParseError) {
- typedef std::vector<Path> Subpaths;
- typedef std::back_insert_iterator<Subpaths> Inserter;
-
- Subpaths subpaths;
- Inserter iter(subpaths);
- PathIteratorSink<Inserter> generator(iter);
+ /** @brief Set the threshold for considering the closing segment degenerate.
+ * When the current point was reached by a relative command, is closer
+ * to the initial point of the path than the specified threshold
+ * and a 'z' is encountered, the last segment will be adjusted instead so that
+ * the closing segment has exactly zero length. This is useful when reading
+ * SVG 1.1 paths that have non-linear final segments written in relative
+ * coordinates, which always suffer from some loss of precision. SVG 2
+ * allows alternate placement of 'z' which does not have this problem. */
+ void setZSnapThreshold(Coord threshold) { _z_snap_threshold = threshold; }
+ Coord zSnapThreshold() const { return _z_snap_threshold; }
+
+private:
+ bool _absolute;
+ bool _moveto_was_absolute;
+ Point _current;
+ Point _initial;
+ Point _cubic_tangent;
+ Point _quad_tangent;
+ std::vector<Coord> _params;
+ PathSink &_sink;
+ Coord _z_snap_threshold;
+ Curve *_curve;
+
+ int cs;
+ std::string _number_part;
+
+ void _push(Coord value);
+ Coord _pop();
+ bool _pop_flag();
+ Coord _pop_coord(Geom::Dim2 axis);
+ Point _pop_point();
+ void _moveTo(Point const &p);
+ void _lineTo(Point const &p);
+ void _curveTo(Point const &c0, Point const &c1, Point const &p);
+ void _quadTo(Point const &c, Point const &p);
+ void _arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p);
+ void _closePath();
+ void _pushCurve(Curve *c);
+
+ void _parse(char const *str, char const *strend, bool finish);
+};
+
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+void parse_svg_path(char const *str, PathSink &sink);
+/** @brief Feed SVG path data to the specified sink
+ * @ingroup Paths */
+inline void parse_svg_path(std::string const &str, PathSink &sink) {
+ parse_svg_path(str.c_str(), sink);
+}
+/** Feed SVG path data from a C stream to the specified sink
+ * @ingroup Paths */
+void parse_svg_path_file(FILE *fi, PathSink &sink);
+
+/** @brief Create path vector from SVG path data stored in a C string
+ * @ingroup Paths */
+inline PathVector parse_svg_path(char const *str) {
+ PathVector ret;
+ SubpathInserter iter(ret);
+ PathIteratorSink<SubpathInserter> generator(iter);
parse_svg_path(str, generator);
- return subpaths;
+ return ret;
}
-inline std::vector<Path> read_svgd_f(FILE * fi) throw(SVGPathParseError) {
- /// @bug The 10kB length limit should be removed
- char input[1024 * 10];
- fgets(input, 1024 * 10, fi);
- return parse_svg_path(input);
+/** @brief Create path vector from a C stream with SVG path data
+ * @ingroup Paths */
+inline PathVector read_svgd_f(FILE * fi) {
+ PathVector ret;
+ SubpathInserter iter(ret);
+ PathIteratorSink<SubpathInserter> generator(iter);
+
+ parse_svg_path_file(fi, generator);
+ return ret;
}
-inline std::vector<Path> read_svgd(char const * name) throw(SVGPathParseError) {
- FILE* fi = fopen(name, "r");
+/** @brief Create path vector from SVG path data stored in a file
+ * @ingroup Paths */
+inline PathVector read_svgd(char const *filename) {
+ FILE* fi = fopen(filename, "r");
if(fi == NULL) throw(std::runtime_error("Error opening file"));
- std::vector<Path> out = read_svgd_f(fi);
+ PathVector out = read_svgd_f(fi);
fclose(fi);
return out;
}
-}
+} // end namespace Geom
#endif
/*
diff --git a/src/2geom/svg-path-writer.cpp b/src/2geom/svg-path-writer.cpp
new file mode 100644
index 000000000..1c40ba64e
--- /dev/null
+++ b/src/2geom/svg-path-writer.cpp
@@ -0,0 +1,296 @@
+/** @file
+ * @brief Path sink which writes an SVG-compatible command string
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <cmath>
+#include <iomanip>
+#include <2geom/coord.h>
+#include <2geom/svg-path-writer.h>
+#include <glib.h>
+
+namespace Geom {
+
+static inline bool is_digit(char c) {
+ return c >= '0' && c <= '9';
+}
+
+SVGPathWriter::SVGPathWriter()
+ : _epsilon(0)
+ , _precision(-1)
+ , _optimize(false)
+ , _use_shorthands(true)
+ , _command(0)
+{
+ // always use C locale for number formatting
+ _ns.imbue(std::locale::classic());
+ _ns.unsetf(std::ios::floatfield);
+}
+
+void SVGPathWriter::moveTo(Point const &p)
+{
+ _setCommand('M');
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _subpath_start = _quad_tangent = _cubic_tangent = p;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::lineTo(Point const &p)
+{
+ // The weird setting of _current is to avoid drift with many almost-aligned segments
+ // The additional conditions ensure that the smaller dimension is rounded to zero
+ bool written = false;
+ if (_use_shorthands) {
+ Point r = _current - p;
+ if (are_near(p[X], _current[X], _epsilon) && std::abs(r[X]) < std::abs(r[Y])) {
+ // emit vlineto
+ _setCommand('V');
+ _current_pars.push_back(p[Y]);
+ _current[Y] = p[Y];
+ written = true;
+ } else if (are_near(p[Y], _current[Y], _epsilon) && std::abs(r[Y]) < std::abs(r[X])) {
+ // emit hlineto
+ _setCommand('H');
+ _current_pars.push_back(p[X]);
+ _current[X] = p[X];
+ written = true;
+ }
+ }
+
+ if (!written) {
+ // emit normal lineto
+ if (_command != 'M' && _command != 'L') {
+ _setCommand('L');
+ }
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+ _current = p;
+ }
+
+ _cubic_tangent = _quad_tangent = _current;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::quadTo(Point const &c, Point const &p)
+{
+ bool shorthand = _use_shorthands && are_near(c, _quad_tangent, _epsilon);
+
+ _setCommand(shorthand ? 'T' : 'Q');
+ if (!shorthand) {
+ _current_pars.push_back(c[X]);
+ _current_pars.push_back(c[Y]);
+ }
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _cubic_tangent = p;
+ _quad_tangent = p + (p - c);
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::curveTo(Point const &p1, Point const &p2, Point const &p3)
+{
+ bool shorthand = _use_shorthands && are_near(p1, _cubic_tangent, _epsilon);
+
+ _setCommand(shorthand ? 'S' : 'C');
+ if (!shorthand) {
+ _current_pars.push_back(p1[X]);
+ _current_pars.push_back(p1[Y]);
+ }
+ _current_pars.push_back(p2[X]);
+ _current_pars.push_back(p2[Y]);
+ _current_pars.push_back(p3[X]);
+ _current_pars.push_back(p3[Y]);
+
+ _current = _quad_tangent = p3;
+ _cubic_tangent = p3 + (p3 - p2);
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p)
+{
+ _setCommand('A');
+ _current_pars.push_back(rx);
+ _current_pars.push_back(ry);
+ _current_pars.push_back(rad_to_deg(angle));
+ _current_pars.push_back(large_arc ? 1. : 0.);
+ _current_pars.push_back(sweep ? 1. : 0.);
+ _current_pars.push_back(p[X]);
+ _current_pars.push_back(p[Y]);
+
+ _current = _quad_tangent = _cubic_tangent = p;
+ if (!_optimize) {
+ flush();
+ }
+}
+
+void SVGPathWriter::closePath()
+{
+ flush();
+ if (_optimize) {
+ _s << "z";
+ } else {
+ _s << " z";
+ }
+ _current = _quad_tangent = _cubic_tangent = _subpath_start;
+}
+
+void SVGPathWriter::flush()
+{
+ if (_command == 0 || _current_pars.empty()) return;
+
+ if (_optimize) {
+ _s << _command;
+ } else {
+ if (_s.tellp() != 0) {
+ _s << ' ';
+ }
+ _s << _command;
+ }
+
+ char lastchar = _command;
+ bool contained_dot = false;
+
+ for (unsigned i = 0; i < _current_pars.size(); ++i) {
+ // TODO: optimize the use of absolute / relative coords
+ std::string cs = _formatCoord(_current_pars[i]);
+
+ // Separator handling logic.
+ // Floating point values can end with a digit or dot
+ // and start with a digit, a plus or minus sign, or a dot.
+ // The following cases require a separator:
+ // * digit-digit
+ // * digit-dot (only if the previous number didn't contain a dot)
+ // * dot-digit
+ if (_optimize) {
+ // C++11: change to front()
+ char firstchar = cs[0];
+ if (is_digit(lastchar)) {
+ if (is_digit(firstchar)) {
+ _s << " ";
+ } else if (firstchar == '.' && !contained_dot) {
+ _s << " ";
+ }
+ } else if (lastchar == '.' && is_digit(firstchar)) {
+ _s << " ";
+ }
+ _s << cs;
+
+ // C++11: change to back()
+ lastchar = cs[cs.length()-1];
+ contained_dot = cs.find('.') != std::string::npos;
+ } else {
+ _s << " " << cs;
+ }
+ }
+ _current_pars.clear();
+ _command = 0;
+}
+
+void SVGPathWriter::clear()
+{
+ _s.clear();
+ _s.str("");
+ _ns.clear();
+ _ns.str("");
+ _command = 0;
+ _current_pars.clear();
+ _current = Point(0,0);
+ _subpath_start = Point(0,0);
+}
+
+void SVGPathWriter::setPrecision(int prec)
+{
+ _precision = prec;
+ if (prec < 0) {
+ _epsilon = 0;
+ } else {
+ _epsilon = std::pow(10., -prec);
+ _ns << std::setprecision(_precision);
+ }
+}
+
+void SVGPathWriter::_setCommand(char cmd)
+{
+ if (_command != 0 && _command != cmd) {
+ flush();
+ }
+ _command = cmd;
+}
+
+std::string SVGPathWriter::_formatCoord(Coord par)
+{
+ std::string ret;
+ if (_precision < 0) {
+ ret = format_coord_shortest(par);
+ } else {
+ _ns << par;
+ ret = _ns.str();
+ _ns.clear();
+ _ns.str("");
+ }
+ return ret;
+}
+
+
+std::string write_svg_path(PathVector const &pv, int prec, bool optimize, bool shorthands)
+{
+ SVGPathWriter writer;
+ writer.setPrecision(prec);
+ writer.setOptimize(optimize);
+ writer.setUseShorthands(shorthands);
+
+ writer.feed(pv);
+ return writer.str();
+}
+
+} // namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/svg-path-writer.h b/src/2geom/svg-path-writer.h
new file mode 100644
index 000000000..e639541ab
--- /dev/null
+++ b/src/2geom/svg-path-writer.h
@@ -0,0 +1,122 @@
+/** @file
+ * @brief Path sink which writes an SVG-compatible command string
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2014 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+#define LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+
+#include <2geom/path-sink.h>
+#include <sstream>
+
+namespace Geom {
+
+/** @brief Serialize paths to SVG path data strings.
+ * You can access the generated string by calling the str() method.
+ * @ingroup Paths
+ */
+class SVGPathWriter
+ : public PathSink
+{
+public:
+ SVGPathWriter();
+ ~SVGPathWriter() {}
+
+ void moveTo(Point const &p);
+ void lineTo(Point const &p);
+ void quadTo(Point const &c, Point const &p);
+ void curveTo(Point const &c0, Point const &c1, Point const &p);
+ void arcTo(double rx, double ry, double angle,
+ bool large_arc, bool sweep, Point const &p);
+ void closePath();
+ void flush();
+
+ /// Clear any path data written so far.
+ void clear();
+
+ /** @brief Set output precision.
+ * When the parameter is negative, the path writer enters a verbatim mode
+ * which preserves all values exactly. */
+ void setPrecision(int prec);
+
+ /** @brief Enable or disable length optimization.
+ *
+ * When set to true, the path writer will optimize the generated path data
+ * for minimum length. However, this will make the data less readable,
+ * because spaces between commands and coordinates will be omitted where
+ * unnecessary for correct parsing.
+ *
+ * When set to false, the string will be a straightforward, partially redundant
+ * representation of the passed commands, optimized for readability.
+ * Commands and coordinates will always be separated by spaces and the command
+ * symbol will not be omitted for multiple consecutive commands of the same type.
+ *
+ * Length optimization is turned off by default. */
+ void setOptimize(bool opt) { _optimize = opt; }
+
+ /** @brief Enable or disable the use of V, H, T and S commands where possible.
+ * Shorthands are turned on by default. */
+ void setUseShorthands(bool use) { _use_shorthands = use; }
+
+ /// Retrieve the generated path data string.
+ std::string str() const { return _s.str(); }
+
+private:
+ void _setCommand(char cmd);
+ std::string _formatCoord(Coord par);
+
+ std::ostringstream _s, _ns;
+ std::vector<Coord> _current_pars;
+ Point _subpath_start;
+ Point _current;
+ Point _quad_tangent;
+ Point _cubic_tangent;
+ Coord _epsilon;
+ int _precision;
+ bool _optimize;
+ bool _use_shorthands;
+ char _command;
+};
+
+std::string write_svg_path(PathVector const &pv, int prec = -1, bool optimize = false, bool shorthands = true);
+
+} // namespace Geom
+
+#endif // LIB2GEOM_SEEN_SVG_PATH_WRITER_H
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/2geom/sweep.cpp b/src/2geom/sweep-bounds.cpp
index f25894282..48f168b97 100644
--- a/src/2geom/sweep.cpp
+++ b/src/2geom/sweep-bounds.cpp
@@ -1,9 +1,27 @@
-#include <2geom/sweep.h>
+#include <2geom/sweep-bounds.h>
#include <algorithm>
namespace Geom {
+struct Event {
+ double x;
+ unsigned ix;
+ bool closing;
+ Event(double pos, unsigned i, bool c) : x(pos), ix(i), closing(c) {}
+// Lexicographic ordering by x then closing
+ bool operator<(Event const &other) const {
+ if(x < other.x) return true;
+ if(x > other.x) return false;
+ return closing < other.closing;
+ }
+ bool operator==(Event const &other) const {
+ return other.x == x && other.ix == ix && other.closing == closing;
+ }
+};
+
+std::vector<std::vector<unsigned> > fake_cull(unsigned a, unsigned b);
+
/**
* \brief Make a list of pairs of self intersections in a list of Rects.
*
@@ -19,8 +37,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> rs, Dim2 d) {
std::vector<std::vector<unsigned> > pairs(rs.size());
for(unsigned i = 0; i < rs.size(); i++) {
- events.push_back(Event(rs[i][d][0], i, false));
- events.push_back(Event(rs[i][d][1], i, true));
+ events.push_back(Event(rs[i][d].min(), i, false));
+ events.push_back(Event(rs[i][d].max(), i, true));
}
std::sort(events.begin(), events.end());
@@ -66,8 +84,8 @@ std::vector<std::vector<unsigned> > sweep_bounds(std::vector<Rect> a, std::vecto
events[n].reserve(sz*2);
for(unsigned i = 0; i < sz; i++) {
Rect r = n ? b[i] : a[i];
- events[n].push_back(Event(r[d][0], i, false));
- events[n].push_back(Event(r[d][1], i, true));
+ events[n].push_back(Event(r[d].min(), i, false));
+ events[n].push_back(Event(r[d].max(), i, true));
}
std::sort(events[n].begin(), events[n].end());
}
diff --git a/src/2geom/sweep.h b/src/2geom/sweep-bounds.h
index 91371e6fb..e0ebf2975 100644
--- a/src/2geom/sweep.h
+++ b/src/2geom/sweep-bounds.h
@@ -32,37 +32,20 @@
*
*/
-#ifndef __2GEOM_SWEEP_H__
-#define __2GEOM_SWEEP_H__
+#ifndef LIB2GEOM_SEEN_SWEEP_H
+#define LIB2GEOM_SEEN_SWEEP_H
#include <vector>
#include <2geom/d2.h>
namespace Geom {
-struct Event {
- double x;
- unsigned ix;
- bool closing;
- Event(double pos, unsigned i, bool c) : x(pos), ix(i), closing(c) {}
-// Lexicographic ordering by x then closing
- bool operator<(Event const &other) const {
- if(x < other.x) return true;
- if(x > other.x) return false;
- return closing < other.closing;
- }
- bool operator==(Event const &other) const {
- return other.x == x && other.ix == ix && other.closing == closing;
- }
-};
std::vector<std::vector<unsigned> >
sweep_bounds(std::vector<Rect>, Dim2 dim = X);
std::vector<std::vector<unsigned> >
sweep_bounds(std::vector<Rect>, std::vector<Rect>, Dim2 dim = X);
-std::vector<std::vector<unsigned> > fake_cull(unsigned a, unsigned b);
-
}
#endif
diff --git a/src/2geom/sweeper.h b/src/2geom/sweeper.h
new file mode 100644
index 000000000..8c4e182a6
--- /dev/null
+++ b/src/2geom/sweeper.h
@@ -0,0 +1,252 @@
+/** @file
+ * @brief Class for implementing sweepline algorithms
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright 2015 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_SWEEPER_H
+#define LIB2GEOM_SEEN_SWEEPER_H
+
+#include <2geom/coord.h>
+#include <algorithm>
+#include <vector>
+#include <boost/intrusive/list.hpp>
+#include <boost/range/algorithm/heap_algorithm.hpp>
+
+namespace Geom {
+
+/** @brief Sweep traits class for interval bounds.
+ * @relates Sweeper
+ * @ingroup Utilities */
+struct IntervalSweepTraits {
+ typedef Interval Bound;
+ typedef std::less<Coord> Compare;
+ inline static Coord entry_value(Bound const &b) { return b.min(); }
+ inline static Coord exit_value(Bound const &b) { return b.max(); }
+};
+
+/** @brief Sweep traits class for rectangle bounds.
+ * @tparam D Which axis to use for sweeping
+ * @ingroup Utilities */
+template <Dim2 D>
+struct RectSweepTraits {
+ typedef Rect Bound;
+ typedef std::less<Coord> Compare;
+ inline static Coord entry_value(Bound const &b) { return b[D].min(); }
+ inline static Coord exit_value(Bound const &b) { return b[D].max(); }
+};
+
+template <typename T>
+struct BoundsFast {
+ Rect operator()(T const &item) const {
+ return item.boundsFast();
+ }
+};
+
+/** @brief Generic sweepline algorithm.
+ *
+ * This class encapsulates an algorithm that sorts the objects according
+ * to their bounds, then moves an imaginary line (sweepline) over those
+ * bounds from left to right. Objects are added to the active list when
+ * the line starts intersecting their bounds, and removed when it completely
+ * passes over them.
+ *
+ * To use this, create a derived class and reimplement the _enter()
+ * and/or _leave() virtual functions, insert all the objects,
+ * and finally call process(). Inside _enter() and _leave(), the items that have
+ * their bounds intersected by the sweepline are available in a list called
+ * _active_items. This is an intrusive linked list, so you should access it using
+ * iterators. Do not add or remove items from it. You can specify the bound type
+ * and how it should be accessed by defining a custom SweepTraits class.
+ *
+ * Look in path.cpp for example usage.
+ *
+ * @tparam Item The type of items to sweep
+ * @tparam SweepTraits Traits class that defines the items' bounds,
+ * how to interpret them and how to sort the events
+ * @ingroup Utilities
+ */
+template <typename Item, typename SweepTraits = IntervalSweepTraits>
+class Sweeper {
+public:
+ /// Type of the item's boundary - usually this will be an Interval or Rect.
+ typedef typename SweepTraits::Bound Bound;
+
+ Sweeper() {}
+
+ /** @brief Insert a single item for sweeping.
+ * @param bound Boundary of the item, as defined in sweep traits
+ * @param item The item itself */
+ void insert(Bound const &bound, Item const &item) {
+ assert(!(typename SweepTraits::Compare()(
+ SweepTraits::exit_value(bound),
+ SweepTraits::entry_value(bound))));
+ _items.push_back(Record(bound, item));
+ }
+
+ /** @brief Insert a range of items using the supplied bounds functor.
+ * The bounds are computed from items using the supplied bounds functor.
+ * @param first Start of range
+ * @param last End of range (one-past-the-end iterator)
+ * @param f Bounds functor */
+ template <typename Iter, typename BoundFunc>
+ void insert(Iter first, Iter last, BoundFunc f = BoundFunc()) {
+ for (; first != last; ++first) {
+ Bound b = f(*first);
+ assert(!(typename SweepTraits::Compare()(
+ SweepTraits::exit_value(b),
+ SweepTraits::entry_value(b))));
+ _items.push_back(Record(b, *first));
+ }
+ }
+
+ /** @brief Process entry and exit events.
+ * This will iterate over all inserted items, calling the virtual protected
+ * functions _enter() and _leave() according to the order of the boundaries
+ * of each item. */
+ void process() {
+ if (_items.empty()) return;
+
+ typename SweepTraits::Compare cmp;
+
+ // we store the events in heaps, which is slightly more efficient
+ // than sorting them, since a heap requires linear time to construct
+ for (RecordIter i = _items.begin(); i != _items.end(); ++i) {
+ _entry_events.push_back(i);
+ _exit_events.push_back(i);
+ }
+ boost::make_heap(_entry_events, _entry_heap_compare);
+ boost::make_heap(_exit_events, _exit_heap_compare);
+ boost::pop_heap(_entry_events, _entry_heap_compare);
+ boost::pop_heap(_exit_events, _exit_heap_compare);
+
+ RecordIter next_entry = _entry_events.back();
+ RecordIter next_exit = _exit_events.back();
+ _entry_events.pop_back();
+ _exit_events.pop_back();
+
+ while (next_entry != _items.end() || next_exit != _items.end()) {
+ assert(next_exit != _items.end());
+
+ if (next_entry == _items.end() ||
+ cmp(SweepTraits::exit_value(next_exit->bound),
+ SweepTraits::entry_value(next_entry->bound)))
+ {
+ // exit event - remove record from active list
+ _leave(*next_exit);
+ _active_items.erase(_active_items.iterator_to(*next_exit));
+ if (!_exit_events.empty()) {
+ boost::pop_heap(_exit_events, _exit_heap_compare);
+ next_exit = _exit_events.back();
+ _exit_events.pop_back();
+ } else {
+ next_exit = _items.end();
+ // we should end the loop after this happens
+ }
+ } else {
+ // entry event - add record to active list
+ _enter(*next_entry);
+ _active_items.push_back(*next_entry);
+ if (!_entry_events.empty()) {
+ boost::pop_heap(_entry_events, _entry_heap_compare);
+ next_entry = _entry_events.back();
+ _entry_events.pop_back();
+ } else {
+ next_entry = _items.end();
+ }
+ }
+ }
+
+ assert(_active_items.empty());
+ }
+
+protected:
+ /// The item and its sweepline boundary.
+ struct Record {
+ boost::intrusive::list_member_hook<> _hook;
+ Bound bound;
+ Item item;
+
+ Record(Bound const &b, Item const &i)
+ : bound(b), item(i)
+ {}
+ };
+ typedef typename std::vector<Record>::iterator RecordIter;
+
+ typedef boost::intrusive::list
+ < Record
+ , boost::intrusive::member_hook
+ < Record
+ , boost::intrusive::list_member_hook<>
+ , &Record::_hook
+ >
+ > RecordList;
+
+ /** @brief Enter an item record.
+ * Override this to process an item as it is about to enter the active list.
+ * When called, the passed record will not be part of the active list. */
+ virtual void _enter(Record const &) {}
+ /** @brief Leave an item record.
+ * Override this to process an item as it is about to leave the active list.
+ * When called, the passed record will be part of the active list. */
+ virtual void _leave(Record const &) {}
+
+ /// The list of all item records undergoing sweeping.
+ std::vector<Record> _items;
+ /// The list of active item records.
+ RecordList _active_items;
+
+private:
+ inline static bool _entry_heap_compare(RecordIter a, RecordIter b) {
+ typename SweepTraits::Compare cmp;
+ return cmp(SweepTraits::entry_value(b->bound), SweepTraits::entry_value(a->bound));
+ }
+ inline static bool _exit_heap_compare(RecordIter a, RecordIter b) {
+ typename SweepTraits::Compare cmp;
+ return cmp(SweepTraits::exit_value(b->bound), SweepTraits::exit_value(a->bound));
+ }
+
+ std::vector<RecordIter> _entry_events;
+ std::vector<RecordIter> _exit_events;
+};
+
+} // namespace Geom
+
+#endif // !LIB2GEOM_SEEN_SWEEPER_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/toposweep.cpp b/src/2geom/toposweep.cpp
index 4da3f6922..3cb231142 100644
--- a/src/2geom/toposweep.cpp
+++ b/src/2geom/toposweep.cpp
@@ -189,7 +189,7 @@ bool SectionSorter::operator()(Section const &a, Section const &b) const {
}
}
- return Point::LexOrderRt(dim)(a.fp, b.fp);
+ return Point::LexLessRt(dim)(a.fp, b.fp);
}
// splits a section into pieces, as specified by an array of doubles, mutating the section to
@@ -203,7 +203,7 @@ std::vector<boost::shared_ptr<Section> > split_section(boost::shared_ptr<Section
s->t = cuts[1];
s->tp = s->curve.get(ps)(cuts[1]);
- assert(Point::LexOrderRt(d)(s->fp, s->tp));
+ assert(Point::LexLessRt(d)(s->fp, s->tp));
ret.reserve(cuts.size() - 2);
for(int i = cuts.size() - 1; i > 1; i--) ret.push_back(boost::shared_ptr<Section>(new Section(s->curve, cuts[i-1], cuts[i], ps, d)));
@@ -380,7 +380,7 @@ TopoGraph::TopoGraph(PathVector const &ps, Dim2 d, double t) : dim(d), tol(t) {
//find all sections to remove
for(int i = context.size() - 1; i >= 0; i--) {
boost::shared_ptr<Section> sec = context[i].section;
- if(Point::LexOrderRt(d)(lim, sec->tp)) {
+ if(Point::LexLessRt(d)(lim, sec->tp)) {
//sec->tp is less than or equal to lim
if(context[i].to_vert == -1) {
//we need to create a new vertex; add everything that enters it
@@ -639,6 +639,7 @@ void remove_area_whiskers(Areas &areas) {
Path area_to_path(PathVector const &ps, Area const &area) {
Path ret;
+ ret.setStitching(true);
if(area.size() == 0) return ret;
Point prev = area[0]->fp;
for(unsigned i = 0; i < area.size(); i++) {
@@ -646,17 +647,18 @@ Path area_to_path(PathVector const &ps, Area const &area) {
Curve *curv = area[i]->curve.get(ps).portion(
forward ? area[i]->f : area[i]->t,
forward ? area[i]->t : area[i]->f);
- ret.append(*curv, Path::STITCH_DISCONTINUOUS);
+ ret.append(*curv);
delete curv;
prev = forward ? area[i]->tp : area[i]->fp;
}
+ ret.setStitching(false);
return ret;
}
PathVector areas_to_paths(PathVector const &ps, Areas const &areas) {
- std::vector<Path> ret;
- ret.reserve(areas.size());
- for(unsigned i = 0; i < areas.size(); i++)
+ PathVector ret;
+ //ret.reserve(areas.size());
+ for(unsigned i = 0; i < areas.size(); ++i)
ret.push_back(area_to_path(ps, areas[i]));
return ret;
}
diff --git a/src/2geom/toposweep.h b/src/2geom/toposweep.h
index b6a55b154..7faf890e1 100644
--- a/src/2geom/toposweep.h
+++ b/src/2geom/toposweep.h
@@ -1,8 +1,7 @@
-
/**
* \file
* \brief TopoSweep - topology / graph representation of a PathVector, for boolean operations and related tasks
- *
+ *//*
* Authors:
* Michael Sloan <mgsloan at gmail.com>
* Nathan Hurst <njhurst at njhurst.com>
@@ -33,8 +32,8 @@
* the specific language governing rights and limitations.
*/
-#ifndef SEEN_GEOM_TOPOSWEEP_H
-#define SEEN_GEOM_TOPOSWEEP_H
+#ifndef LIB2GEOM_SEEN_TOPOSWEEP_H
+#define LIB2GEOM_SEEN_TOPOSWEEP_H
#include <2geom/coord.h>
#include <2geom/point.h>
@@ -69,10 +68,11 @@ struct Section {
Section(CurveIx cix, double fd, double td, Point fdp, Point tdp) : curve(cix), f(fd), t(td), fp(fdp), tp(tdp) { }
Section(CurveIx cix, double fd, double td, PathVector ps, Dim2 d) : curve(cix), f(fd), t(td) {
fp = curve.get(ps).pointAt(f), tp = curve.get(ps).pointAt(t);
- if (Point::LexOrderRt(d)(tp, fp)) {
+ if (Point::LexLessRt(d)(tp, fp)) {
//swap from and to, since tp is left or above fp
- std::swap(f, t);
- std::swap(fp, tp);
+ using std::swap;
+ swap(f, t);
+ swap(fp, tp);
}
}
Rect bbox() const { return Rect(fp, tp); }
@@ -167,7 +167,7 @@ struct SweepSorter {
Dim2 dim;
SweepSorter(Dim2 d) : dim(d) {}
bool operator()(const Section &a, const Section &b) const {
- return Point::LexOrderRt(dim)(a.fp, b.fp);
+ return Point::LexLessRt(dim)(a.fp, b.fp);
}
};
@@ -208,7 +208,7 @@ Areas filter_areas(PathVector const &ps, Areas const & areas, Z const &z) {
} // end namespace Geom
-#endif // SEEN_GEOM_TOPOSWEEP_H
+#endif // LIB2GEOM_SEEN_TOPOSWEEP_H
/*
Local Variables:
diff --git a/src/2geom/transforms.cpp b/src/2geom/transforms.cpp
index 091079d5a..41d395297 100644
--- a/src/2geom/transforms.cpp
+++ b/src/2geom/transforms.cpp
@@ -139,6 +139,24 @@ Affine &Affine::operator*=(Zoom const &z) {
return *this;
}
+Affine Rotate::around(Point const &p, Coord angle)
+{
+ Affine result = Translate(-p) * Rotate(angle) * Translate(p);
+ return result;
+}
+
+Affine reflection(Point const & vector, Point const & origin)
+{
+ Geom::Point vn = unit_vector(vector);
+ Coord cx2 = vn[X] * vn[X];
+ Coord cy2 = vn[Y] * vn[Y];
+ Coord c2xy = 2 * vn[X] * vn[Y];
+ Affine mirror ( cx2 - cy2, c2xy,
+ c2xy, cy2 - cx2,
+ 0, 0 );
+ return Translate(-origin) * mirror * Translate(origin);
+}
+
// this checks whether the requirements of TransformConcept are satisfied for all transforms.
// if you add a new transform type, include it here!
void check_transforms()
@@ -173,14 +191,6 @@ void check_transforms()
m = z * t; m = z * s; m = z * r; m = z * h; m = z * v; m = z * z;
}
-Affine reflection(Point const & vector, Point const & origin) {
- Geom::Point vec_norm = unit_vector(vector);
- Affine mirror ( vec_norm[X]*vec_norm[X] - vec_norm[Y]*vec_norm[Y], 2 * vec_norm[X] * vec_norm[Y] ,
- 2 * vec_norm[X] * vec_norm[Y], vec_norm[Y]*vec_norm[Y] - vec_norm[X]*vec_norm[X] ,
- 0 ,0 );
- return Translate(-origin) * mirror * Translate(origin);
-}
-
} // namespace Geom
/*
diff --git a/src/2geom/transforms.h b/src/2geom/transforms.h
index 7f5635747..de4e6871f 100644
--- a/src/2geom/transforms.h
+++ b/src/2geom/transforms.h
@@ -40,6 +40,7 @@
#include <2geom/forward.h>
#include <2geom/affine.h>
#include <2geom/angle.h>
+#include <boost/concept/assert.hpp>
namespace Geom {
@@ -95,6 +96,7 @@ public:
* @ingroup Transforms */
template <typename T>
T pow(T const &t, int n) {
+ BOOST_CONCEPT_ASSERT((TransformConcept<T>));
if (n == 0) return T::identity();
T result(T::identity());
T x(n < 0 ? t.inverse() : t);
@@ -199,6 +201,7 @@ public:
/** @brief Get the characteristic vector of the rotation.
* @return A vector that would be obtained by applying this transform to the X versor. */
Point vector() const { return vec; }
+ Coord angle() const { return atan2(vec); }
Coord operator[](Dim2 dim) const { return vec[dim]; }
Coord operator[](unsigned dim) const { return vec[dim]; }
Rotate &operator*=(Rotate const &o) { vec *= o; return *this; }
@@ -216,6 +219,7 @@ public:
Coord rad = (deg / 180.0) * M_PI;
return Rotate(rad);
}
+ static Affine around(Point const &p, Coord angle);
friend class Point;
};
@@ -343,6 +347,8 @@ inline Translate pow(Translate const &t, int n) {
/** @brief Reflects objects about line.
* The line, defined by a vector along the line and a point on it, acts as a mirror.
+ * @ingroup Transforms
+ * @see Line::reflection()
*/
Affine reflection(Point const & vector, Point const & origin);
diff --git a/src/2geom/utils.h b/src/2geom/utils.h
index fe955dd41..bc0ad74b8 100644
--- a/src/2geom/utils.h
+++ b/src/2geom/utils.h
@@ -30,11 +30,12 @@
*
*/
-#ifndef SEEN_LIB2GEOM_UTILS_H
-#define SEEN_LIB2GEOM_UTILS_H
+#ifndef LIB2GEOM_SEEN_UTILS_H
+#define LIB2GEOM_SEEN_UTILS_H
#include <cstddef>
#include <vector>
+#include <boost/operators.hpp>
namespace Geom {
@@ -59,9 +60,20 @@ struct MultipliableNoncommutative : B
}
};
+/** @brief Null output iterator
+ * Use this if you want to discard a result returned through an output iterator. */
+struct NullIterator
+ : public boost::output_iterator_helper<NullIterator>
+{
+ NullIterator() {}
+
+ template <typename T>
+ void operator=(T const &v) {}
+};
+
} // end namespace Geom
-#endif // SEEN_LIB2GEOM_UTILS_H
+#endif // LIB2GEOM_SEEN_UTILS_H
/*
Local Variables:
diff --git a/src/2geom/viewbox.cpp b/src/2geom/viewbox.cpp
new file mode 100644
index 000000000..69bd0c487
--- /dev/null
+++ b/src/2geom/viewbox.cpp
@@ -0,0 +1,133 @@
+/**
+ * \file
+ * \brief Convenience class for SVG viewBox handling
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#include <2geom/transforms.h>
+#include <2geom/viewbox.h>
+
+namespace Geom {
+
+/** Convert an align specification to coordinate fractions. */
+Point align_factors(Align g) {
+ Point p;
+ switch (g) {
+ case ALIGN_XMIN_YMIN:
+ p[X] = 0.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMID_YMIN:
+ p[X] = 0.5;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMAX_YMIN:
+ p[X] = 1.0;
+ p[Y] = 0.0;
+ break;
+ case ALIGN_XMIN_YMID:
+ p[X] = 0.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMID_YMID:
+ p[X] = 0.5;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMAX_YMID:
+ p[X] = 1.0;
+ p[Y] = 0.5;
+ break;
+ case ALIGN_XMIN_YMAX:
+ p[X] = 0.0;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMID_YMAX:
+ p[X] = 0.5;
+ p[Y] = 1.0;
+ break;
+ case ALIGN_XMAX_YMAX:
+ p[X] = 1.0;
+ p[Y] = 1.0;
+ break;
+ default:
+ break;
+ }
+ return p;
+}
+
+/** Obtain transformation from the viewbox to the specified viewport. */
+Affine ViewBox::transformTo(Geom::Rect const &viewport) const
+{
+ if (!_box) {
+ return Geom::Affine::identity();
+ }
+
+ // 1. translate viewbox to origin
+ Geom::Affine total = Translate(-_box->min());
+
+ // 2. compute scale
+ Geom::Point vdims = viewport.dimensions();
+ Geom::Point bdims = _box->dimensions();
+ Geom::Scale scale(vdims[X] / bdims[X], vdims[Y] / bdims[Y]);
+
+ if (_align == ALIGN_NONE) {
+ // apply non-uniform scale
+ // = Scale(_box->dimensions()).inverse() * Scale(viewport.dimensions())
+ total *= scale * Translate(viewport.min());
+ } else {
+ double uscale = 0;
+ if (_expansion == EXPANSION_MEET) {
+ uscale = std::min(scale[X], scale[Y]);
+ } else {
+ uscale = std::max(scale[X], scale[Y]);
+ }
+ scale = Scale(uscale);
+
+ // compute offset for align
+ Geom::Point offset = bdims * scale - vdims;
+ offset *= Scale(align_factors(_align));
+ total *= Translate(-offset);
+ }
+
+ return total;
+}
+
+} // namespace Geom
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/2geom/viewbox.h b/src/2geom/viewbox.h
new file mode 100644
index 000000000..81f59ee36
--- /dev/null
+++ b/src/2geom/viewbox.h
@@ -0,0 +1,102 @@
+/**
+ * \file
+ * \brief Convenience class for SVG viewBox handling
+ *//*
+ * Authors:
+ * Krzysztof KosiƄski <tweenk.pl@gmail.com>
+ *
+ * Copyright (C) 2013 Authors
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ */
+
+#ifndef LIB2GEOM_SEEN_VIEWBOX_H
+#define LIB2GEOM_SEEN_VIEWBOX_H
+
+#include <2geom/affine.h>
+#include <2geom/rect.h>
+
+namespace Geom {
+
+/** Values for the <align> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Align {
+ ALIGN_NONE,
+ ALIGN_XMIN_YMIN,
+ ALIGN_XMID_YMIN,
+ ALIGN_XMAX_YMIN,
+ ALIGN_XMIN_YMID,
+ ALIGN_XMID_YMID,
+ ALIGN_XMAX_YMID,
+ ALIGN_XMIN_YMAX,
+ ALIGN_XMID_YMAX,
+ ALIGN_XMAX_YMAX
+};
+
+/** Values for the <meetOrSlice> parameter of preserveAspectRatio.
+ * See: http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute */
+enum Expansion {
+ EXPANSION_MEET,
+ EXPANSION_SLICE
+};
+
+Point align_factors(Align align);
+
+class ViewBox {
+ OptRect _box;
+ Align _align;
+ Expansion _expansion;
+
+public:
+ explicit ViewBox(OptRect const &r = OptRect(), Align a = ALIGN_XMID_YMID, Expansion ex = EXPANSION_MEET)
+ : _box(r)
+ , _align(a)
+ , _expansion(ex)
+ {}
+
+ void setBox(OptRect const &r) { _box = r; }
+ void setAlign(Align a) { _align = a; }
+ void setExpansion(Expansion ex) { _expansion = ex; }
+ OptRect const &box() const { return _box; }
+ Align align() const { return _align; }
+ Expansion expansion() const { return _expansion; }
+
+ /** Obtain transformation from the viewbox to the specified viewport. */
+ Affine transformTo(Geom::Rect const &viewport) const;
+};
+
+} // namespace Geom
+
+#endif // !LIB2GEOM_SEEN_VIEWBOX_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
diff --git a/src/conn-avoid-ref.cpp b/src/conn-avoid-ref.cpp
index eb6694233..ec9aba793 100644
--- a/src/conn-avoid-ref.cpp
+++ b/src/conn-avoid-ref.cpp
@@ -19,7 +19,7 @@
#include "display/curve.h"
#include "2geom/line.h"
#include "2geom/crossing.h"
-#include "2geom/convex-cover.h"
+#include "2geom/convex-hull.h"
#include "helper/geom-curves.h"
#include "svg/stringstream.h"
#include "conn-avoid-ref.h"
@@ -297,7 +297,7 @@ static Avoid::Polygon avoid_item_poly(SPItem const *item)
Geom::Line prev_parallel_hull_edge;
prev_parallel_hull_edge.setOrigin(hull_edge.origin()+hull_edge.versor().ccw()*spacing);
prev_parallel_hull_edge.setVersor(hull_edge.versor());
- int hull_size = hull.boundary.size();
+ int hull_size = hull.size();
for (int i = 0; i < hull_size; ++i)
{
hull_edge.setPoints(hull[i], hull[i+1]);
diff --git a/src/desktop-events.cpp b/src/desktop-events.cpp
index e80e5f6c1..b685dacbf 100644
--- a/src/desktop-events.cpp
+++ b/src/desktop-events.cpp
@@ -354,7 +354,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data)
// the origin should still be constrained to the guide. So let's do
// that explicitly first:
Geom::Line line(guide->getPoint(), guide->angle());
- Geom::Coord t = line.nearestPoint(motion_dt);
+ Geom::Coord t = line.nearestTime(motion_dt);
motion_dt = line.pointAt(t);
if (!(event->motion.state & GDK_SHIFT_MASK)) {
m.guideConstrainedSnap(motion_dt, *guide);
@@ -435,7 +435,7 @@ gint sp_dt_guide_event(SPCanvasItem *item, GdkEvent *event, gpointer data)
// the origin should still be constrained to the guide. So let's
// do that explicitly first:
Geom::Line line(guide->getPoint(), guide->angle());
- Geom::Coord t = line.nearestPoint(event_dt);
+ Geom::Coord t = line.nearestTime(event_dt);
event_dt = line.pointAt(t);
if (!(event->button.state & GDK_SHIFT_MASK)) {
m.guideConstrainedSnap(event_dt, *guide);
diff --git a/src/display/cairo-utils.cpp b/src/display/cairo-utils.cpp
index e1f12b04b..59e190676 100644
--- a/src/display/cairo-utils.cpp
+++ b/src/display/cairo-utils.cpp
@@ -18,9 +18,7 @@
#include <glib/gstdio.h>
#include <glibmm/fileutils.h>
#include <2geom/pathvector.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/elliptical-arc.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include <2geom/affine.h>
#include <2geom/point.h>
#include <2geom/path.h>
@@ -531,7 +529,7 @@ feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & tran
case 2:
{
Geom::QuadraticBezier const *quadratic_bezier = static_cast<Geom::QuadraticBezier const*>(&c);
- std::vector<Geom::Point> points = quadratic_bezier->points();
+ std::vector<Geom::Point> points = quadratic_bezier->controlPoints();
points[0] *= trans;
points[1] *= trans;
points[2] *= trans;
@@ -554,7 +552,7 @@ feed_curve_to_cairo(cairo_t *cr, Geom::Curve const &c, Geom::Affine const & tran
case 3:
{
Geom::CubicBezier const *cubic_bezier = static_cast<Geom::CubicBezier const*>(&c);
- std::vector<Geom::Point> points = cubic_bezier->points();
+ std::vector<Geom::Point> points = cubic_bezier->controlPoints();
//points[0] *= trans; // don't do this one here for fun: it is only needed for optimized strokes
points[1] *= trans;
points[2] *= trans;
diff --git a/src/display/curve-test.h b/src/display/curve-test.h
index 3d698ca07..34137f3c8 100644
--- a/src/display/curve-test.h
+++ b/src/display/curve-test.h
@@ -25,8 +25,8 @@ public:
path2.close();
// Open path
path3.append(Geom::SVGEllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1)));
- path3.append(Geom::VLineSegment(Geom::Point(5,1),2), Geom::Path::STITCH_DISCONTINUOUS);
- path3.append(Geom::HLineSegment(Geom::Point(6,4),2), Geom::Path::STITCH_DISCONTINUOUS);
+ path3.append(Geom::LineSegment(Geom::Point(5,1),Geom::Point(5,2)));
+ path3.append(Geom::LineSegment(Geom::Point(6,4),Geom::Point(2,4)));
}
virtual ~CurveTest() {}
diff --git a/src/display/curve.cpp b/src/display/curve.cpp
index 54a62939d..d236d81cf 100644
--- a/src/display/curve.cpp
+++ b/src/display/curve.cpp
@@ -31,14 +31,12 @@
SPCurve::SPCurve()
: _refcount(1),
_pathv()
-{
-}
+{}
SPCurve::SPCurve(Geom::PathVector const& pathv)
: _refcount(1),
_pathv(pathv)
-{
-}
+{}
SPCurve *
SPCurve::new_from_rect(Geom::Rect const &rect, bool all_four_sides)
@@ -90,13 +88,7 @@ SPCurve::get_pathvector() const
size_t
SPCurve::get_segment_count() const
{
- size_t nr = 0;
- for(Geom::PathVector::const_iterator it = _pathv.begin(); it != _pathv.end(); ++it) {
- nr += (*it).size();
-
- if (it->closed()) nr += 1;
- }
- return nr;
+ return _pathv.curveCount();
}
/**
@@ -476,7 +468,7 @@ SPCurve::last_point() const
SPCurve *
SPCurve::create_reverse() const
{
- SPCurve *new_curve = new SPCurve(Geom::reverse_paths_and_order(_pathv));
+ SPCurve *new_curve = new SPCurve(_pathv.reversed());
return new_curve;
}
diff --git a/src/display/curve.h b/src/display/curve.h
index 5fad75b18..42b899210 100644
--- a/src/display/curve.h
+++ b/src/display/curve.h
@@ -13,7 +13,7 @@
#ifndef SEEN_DISPLAY_CURVE_H
#define SEEN_DISPLAY_CURVE_H
-#include <2geom/forward.h>
+#include <2geom/pathvector.h>
#include <cstddef>
#include <boost/optional.hpp>
diff --git a/src/display/drawing-image.cpp b/src/display/drawing-image.cpp
index 8fe337959..1594614ac 100644
--- a/src/display/drawing-image.cpp
+++ b/src/display/drawing-image.cpp
@@ -172,7 +172,7 @@ static double
distance_to_segment (Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2)
{
Geom::LineSegment l(a1, a2);
- Geom::Point np = l.pointAt(l.nearestPoint(p));
+ Geom::Point np = l.pointAt(l.nearestTime(p));
return Geom::distance(np, p);
}
diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h
index fb323cd78..f6f933aaf 100644
--- a/src/extension/implementation/implementation.h
+++ b/src/extension/implementation/implementation.h
@@ -15,14 +15,7 @@
#include <vector>
#include <sigc++/signal.h>
#include <glibmm/value.h>
-
-namespace Geom {
- class Affine;
- class OptRect;
- class Path;
- typedef std::vector<Path> PathVector;
- class Point;
-}
+#include <2geom/forward.h>
namespace Gtk {
class Widget;
diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp
index 40d0955e3..5b8aae655 100644
--- a/src/extension/internal/emf-print.cpp
+++ b/src/extension/internal/emf-print.cpp
@@ -860,7 +860,7 @@ Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect,
/* Get the ends of the LAST line segment.
Find minimum rotation to align rectangle with X,Y axes. (Very degenerate if it is rotated 45 degrees.) */
*angle = 10.0; /* must be > than the actual angle in radians. */
- for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open(); ++cit){
+ for(Geom::Path::iterator cit = pR.begin(); cit != pR.end_open(); ++cit){
P1_trail = cit->initialPoint();
P1 = cit->finalPoint();
v1 = unit_vector(P1 - P1_trail);
@@ -874,7 +874,7 @@ Geom::Path PrintEmf::pathv_to_rect(Geom::PathVector const &pathv, bool *is_rect,
double convert = 36000.0/ (2.0 * M_PI);
*angle = round(*angle * convert)/convert;
- for(Geom::Path::const_iterator cit = pR.begin(); cit != pR.end_open();++cit) {
+ for(Geom::Path::iterator cit = pR.begin(); cit != pR.end_open();++cit) {
P1_lead = cit->finalPoint();
v1 = unit_vector(P1 - P1_trail);
v2 = unit_vector(P1_lead - P1 );
@@ -924,7 +924,7 @@ int PrintEmf::vector_rect_alignment(double angle, Geom::Point vtest){
*/
Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int corner){
Geom::Point center(0,0);
- for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ for(Geom::Path::iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
center += cit->initialPoint()/4.0;
}
@@ -952,7 +952,7 @@ Geom::Point PrintEmf::get_pathrect_corner(Geom::Path pathRect, double angle, int
Geom::Point v1 = Geom::Point(1,0) * Geom::Rotate(-angle); // unit horizontal side (sign change because Y increases DOWN)
Geom::Point v2 = Geom::Point(0,1) * Geom::Rotate(-angle); // unit vertical side (sign change because Y increases DOWN)
Geom::Point P1;
- for(Geom::Path::const_iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
+ for(Geom::Path::iterator cit = pathRect.begin(); cit != pathRect.end_open(); ++cit) {
P1 = cit->initialPoint();
if ( ( LR == (dot(P1 - center,v1) > 0 ? 0 : 1) )
@@ -1494,11 +1494,11 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff
int curves = 0;
char *rec = NULL;
- for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
+ for (Geom::PathVector::iterator pit = pv.begin(); pit != pv.end(); ++pit) {
moves++;
nodes++;
- for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
+ for (Geom::Path::iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
nodes++;
if (is_straight_curve(*cit)) {
@@ -1519,7 +1519,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff
/**
* For all Subpaths in the <path>
*/
- for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
+ for (Geom::PathVector::iterator pit = pv.begin(); pit != pv.end(); ++pit) {
using Geom::X;
using Geom::Y;
@@ -1538,7 +1538,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff
/**
* For all segments in the subpath
*/
- for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
+ for (Geom::Path::iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
if (is_straight_curve(*cit)) {
//Geom::Point p0 = cit->initialPoint();
Geom::Point p1 = cit->finalPoint();
@@ -1557,7 +1557,7 @@ bool PrintEmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff
lpPoints[i].y = y1;
i = i + 1;
} else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
//Geom::Point p0 = points[0];
Geom::Point p1 = points[1];
Geom::Point p2 = points[2];
@@ -1846,7 +1846,7 @@ unsigned int PrintEmf::draw_pathv_to_EMF(Geom::PathVector const &pathv, const Ge
g_error("Fatal programming error in PrintEmf::print_pathv at U_EMRLINETO_set");
}
} else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
//Geom::Point p0 = points[0];
Geom::Point p1 = points[1];
Geom::Point p2 = points[2];
diff --git a/src/extension/internal/javafx-out.cpp b/src/extension/internal/javafx-out.cpp
index 19946022c..386bde1d6 100644
--- a/src/extension/internal/javafx-out.cpp
+++ b/src/extension/internal/javafx-out.cpp
@@ -35,8 +35,7 @@
#include <extension/system.h>
#include <2geom/pathvector.h>
#include <2geom/rect.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom.h"
#include "helper/geom-curves.h"
#include <io/sys.h>
@@ -531,9 +530,7 @@ bool JavaFXOutput::doCurve(SPItem *item, const String &id)
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
{
//### LINE
- if ( dynamic_cast<Geom::LineSegment const *> (&*cit) ||
- dynamic_cast<Geom::HLineSegment const *> (&*cit) ||
- dynamic_cast<Geom::VLineSegment const *> (&*cit) )
+ if ( dynamic_cast<Geom::LineSegment const *> (&*cit) )
{
Geom::Point p = cit->finalPoint();
out(" LineTo {\n");
@@ -545,7 +542,7 @@ bool JavaFXOutput::doCurve(SPItem *item, const String &id)
//### BEZIER
else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
{
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
Geom::Point p1 = points[1];
Geom::Point p2 = points[2];
Geom::Point p3 = points[3];
diff --git a/src/extension/internal/latex-pstricks.cpp b/src/extension/internal/latex-pstricks.cpp
index 6aaa1bca4..ae8f30a5c 100644
--- a/src/extension/internal/latex-pstricks.cpp
+++ b/src/extension/internal/latex-pstricks.cpp
@@ -17,8 +17,7 @@
#include <2geom/pathvector.h>
#include <2geom/sbasis-to-bezier.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include <errno.h>
#include <signal.h>
#include "util/units.h"
@@ -300,7 +299,7 @@ PrintLatex::print_2geomcurve(SVGOStringStream &os, Geom::Curve const &c)
os << "\\lineto(" << c.finalPoint()[X] << "," << c.finalPoint()[Y] << ")\n";
}
else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
- std::vector<Geom::Point> points = cubic_bezier->points();
+ std::vector<Geom::Point> points = cubic_bezier->controlPoints();
os << "\\curveto(" << points[1][X] << "," << points[1][Y] << ")("
<< points[2][X] << "," << points[2][Y] << ")("
<< points[3][X] << "," << points[3][Y] << ")\n";
diff --git a/src/extension/internal/metafile-print.cpp b/src/extension/internal/metafile-print.cpp
index 73d63f27d..2fb36be85 100644
--- a/src/extension/internal/metafile-print.cpp
+++ b/src/extension/internal/metafile-print.cpp
@@ -389,7 +389,7 @@ Geom::PathVector PrintMetafile::center_ellipse_as_SVG_PathV(Geom::Point ctr, dou
char text[256];
sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z", x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1);
- std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
+ Geom::PathVector outres = Geom::parse_svg_path(text);
return outres;
}
@@ -419,7 +419,7 @@ Geom::PathVector PrintMetafile::center_elliptical_ring_as_SVG_PathV(Geom::Point
sprintf(text, " M %f,%f A %f %f %f 0 1 %f %f A %f %f %f 0 1 %f %f z M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z",
x11, y11, rx1, ry1, degrot, x12, y12, rx1, ry1, degrot, x11, y11,
x21, y21, rx2, ry2, degrot, x22, y22, rx2, ry2, degrot, x21, y21);
- std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
+ Geom::PathVector outres = Geom::parse_svg_path(text);
return outres;
}
@@ -440,7 +440,7 @@ Geom::PathVector PrintMetafile::center_elliptical_hole_as_SVG_PathV(Geom::Point
char text[256];
sprintf(text, " M %f,%f A %f %f %f 0 0 %f %f A %f %f %f 0 0 %f %f z M 50000,50000 50000,-50000 -50000,-50000 -50000,50000 z",
x1, y1, rx, ry, F * 360. / (2.*M_PI), x2, y2, rx, ry, F * 360. / (2.*M_PI), x1, y1);
- std::vector<Geom::Path> outres = Geom::parse_svg_path(text);
+ Geom::PathVector outres = Geom::parse_svg_path(text);
return outres;
}
@@ -452,7 +452,7 @@ width vector to side edge
*/
Geom::PathVector PrintMetafile::rect_cutter(Geom::Point ctr, Geom::Point pos, Geom::Point neg, Geom::Point width)
{
- std::vector<Geom::Path> outres;
+ Geom::PathVector outres;
Geom::Path cutter;
cutter.start(ctr + pos - width);
cutter.appendNew<Geom::LineSegment>(ctr + pos + width);
diff --git a/src/extension/internal/odf.cpp b/src/extension/internal/odf.cpp
index 52fabcf3c..6904eb2fd 100644
--- a/src/extension/internal/odf.cpp
+++ b/src/extension/internal/odf.cpp
@@ -55,8 +55,7 @@
#include <style.h>
#include "display/curve.h"
#include <2geom/pathvector.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include <2geom/transforms.h>
#include <helper/geom.h>
#include "helper/geom-curves.h"
@@ -1299,7 +1298,7 @@ writePath(Writer &outs, Geom::PathVector const &pathv,
outs.printf("L %.3f %.3f ", destx, desty);
}
else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
for (unsigned i = 1; i <= 3; i++) {
if (fabs(points[i][X])<1.0) points[i][X] = 0.0; // Why is this needed? Shouldn't we just round all numbers then?
if (fabs(points[i][Y])<1.0) points[i][Y] = 0.0;
diff --git a/src/extension/internal/pov-out.cpp b/src/extension/internal/pov-out.cpp
index 3d149928f..bd2168b68 100644
--- a/src/extension/internal/pov-out.cpp
+++ b/src/extension/internal/pov-out.cpp
@@ -30,8 +30,7 @@
#include <extension/system.h>
#include <2geom/pathvector.h>
#include <2geom/rect.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom.h"
#include "helper/geom-curves.h"
#include <io/sys.h>
@@ -388,7 +387,7 @@ bool PovOutput::doCurve(SPItem *item, const String &id)
}
else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
{
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
Geom::Point p0 = points[0];
Geom::Point p1 = points[1];
Geom::Point p2 = points[2];
diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp
index 768909173..431053085 100644
--- a/src/extension/internal/wmf-print.cpp
+++ b/src/extension/internal/wmf-print.cpp
@@ -29,14 +29,13 @@
#endif
-#include "2geom/sbasis-to-bezier.h"
-#include "2geom/svg-elliptical-arc.h"
-
-#include "2geom/path.h"
-#include "2geom/pathvector.h"
-#include "2geom/rect.h"
-#include "2geom/bezier-curve.h"
-#include "2geom/hvlinesegment.h"
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/elliptical-arc.h>
+
+#include <2geom/path.h>
+#include <2geom/pathvector.h>
+#include <2geom/rect.h>
+#include <2geom/curves.h>
#include "helper/geom.h"
#include "helper/geom-curves.h"
#include "sp-item.h"
@@ -59,7 +58,7 @@
#include "display/cairo-utils.h"
#include "splivarot.h" // pieces for union on shapes
-#include "2geom/svg-path-parser.h" // to get from SVG text to Geom::Path
+#include <2geom/svg-path-parser.h> // to get from SVG text to Geom::Path
#include "display/canvas-bpath.h" // for SPWindRule
#include "display/cairo-utils.h" // for Inkscape::Pixbuf::PF_CAIRO
@@ -984,7 +983,7 @@ bool PrintWmf::print_simple_shape(Geom::PathVector const &pathv, const Geom::Aff
lpPoints[i].y = y1;
i = i + 1;
} else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*cit)) {
- std::vector<Geom::Point> points = cubic->points();
+ std::vector<Geom::Point> points = cubic->controlPoints();
//Geom::Point p0 = points[0];
Geom::Point p1 = points[1];
Geom::Point p2 = points[2];
diff --git a/src/gradient-chemistry.cpp b/src/gradient-chemistry.cpp
index d90972961..409e9f0e6 100644
--- a/src/gradient-chemistry.cpp
+++ b/src/gradient-chemistry.cpp
@@ -1068,7 +1068,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
// using X-coordinates only to determine the offset, assuming p has been snapped to the vector from begin to end.
Geom::Point begin(lg->x1.computed, lg->y1.computed);
Geom::Point end(lg->x2.computed, lg->y2.computed);
- double offset = Geom::LineSegment(begin, end).nearestPoint(p);
+ double offset = Geom::LineSegment(begin, end).nearestTime(p);
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (lg, false);
lg->ensureVector();
lg->vector.stops.at(point_i).offset = offset;
@@ -1163,7 +1163,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
{
Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
Geom::Point end = Geom::Point (rg->cx.computed + rg->r.computed, rg->cy.computed);
- double offset = Geom::LineSegment(start, end).nearestPoint(p);
+ double offset = Geom::LineSegment(start, end).nearestTime(p);
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (rg, false);
rg->ensureVector();
rg->vector.stops.at(point_i).offset = offset;
@@ -1180,7 +1180,7 @@ void sp_item_gradient_set_coords(SPItem *item, GrPointType point_type, guint poi
{
Geom::Point start = Geom::Point (rg->cx.computed, rg->cy.computed);
Geom::Point end = Geom::Point (rg->cx.computed, rg->cy.computed - rg->r.computed);
- double offset = Geom::LineSegment(start, end).nearestPoint(p);
+ double offset = Geom::LineSegment(start, end).nearestTime(p);
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(rg, false);
rg->ensureVector();
rg->vector.stops.at(point_i).offset = offset;
diff --git a/src/gradient-drag.cpp b/src/gradient-drag.cpp
index b22714959..b5a988729 100644
--- a/src/gradient-drag.cpp
+++ b/src/gradient-drag.cpp
@@ -378,7 +378,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
Geom::Point begin = getGradientCoords(item, POINT_LG_BEGIN, 0, fill_or_stroke);
Geom::Point end = getGradientCoords(item, POINT_LG_END, 0, fill_or_stroke);
Geom::LineSegment ls(begin, end);
- double offset = ls.nearestPoint(mouse_p);
+ double offset = ls.nearestTime(mouse_p);
Geom::Point nearest = ls.pointAt(offset);
double dist_screen = Geom::distance(mouse_p, nearest);
if ( dist_screen < tolerance ) {
@@ -391,7 +391,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
Geom::Point begin = getGradientCoords(item, POINT_RG_CENTER, 0, fill_or_stroke);
Geom::Point end = getGradientCoords(item, POINT_RG_R1, 0, fill_or_stroke);
Geom::LineSegment ls(begin, end);
- double offset = ls.nearestPoint(mouse_p);
+ double offset = ls.nearestTime(mouse_p);
Geom::Point nearest = ls.pointAt(offset);
double dist_screen = Geom::distance(mouse_p, nearest);
if ( dist_screen < tolerance ) {
@@ -403,7 +403,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
} else {
end = getGradientCoords(item, POINT_RG_R2, 0, fill_or_stroke);
ls = Geom::LineSegment(begin, end);
- offset = ls.nearestPoint(mouse_p);
+ offset = ls.nearestTime(mouse_p);
nearest = ls.pointAt(offset);
dist_screen = Geom::distance(mouse_p, nearest);
if ( dist_screen < tolerance ) {
@@ -442,7 +442,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
p[2] = patch.getPoint( 0, 2 ) * transform;
p[3] = patch.getPoint( 0, 3 ) * transform;
Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
- Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Coord coord = b.nearestTime( mouse_p );
Geom::Point nearest = b( coord );
double dist_screen = Geom::L2 ( mouse_p - nearest );
if ( dist_screen < closest ) {
@@ -460,7 +460,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
p[2] = patch.getPoint( 1, 2 ) * transform;
p[3] = patch.getPoint( 1, 3 ) * transform;
Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
- Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Coord coord = b.nearestTime( mouse_p );
Geom::Point nearest = b( coord );
double dist_screen = Geom::L2 ( mouse_p - nearest );
if ( dist_screen < closest ) {
@@ -478,7 +478,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
p[2] = patch.getPoint( 2, 2 ) * transform;
p[3] = patch.getPoint( 2, 3 ) * transform;
Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
- Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Coord coord = b.nearestTime( mouse_p );
Geom::Point nearest = b( coord );
double dist_screen = Geom::L2 ( mouse_p - nearest );
if ( dist_screen < closest ) {
@@ -496,7 +496,7 @@ SPStop *GrDrag::addStopNearPoint(SPItem *item, Geom::Point mouse_p, double toler
p[2] = patch.getPoint( 3, 2 ) * transform;
p[3] = patch.getPoint( 3, 3 ) * transform;
Geom::BezierCurveN<3> b( p[0], p[1], p[2], p[3] );
- Geom::Coord coord = b.nearestPoint( mouse_p );
+ Geom::Coord coord = b.nearestTime( mouse_p );
Geom::Point nearest = b( coord );
double dist_screen = Geom::L2 ( mouse_p - nearest );
if ( dist_screen < closest ) {
@@ -603,7 +603,7 @@ bool GrDrag::dropColor(SPItem */*item*/, gchar const *c, Geom::Point p)
for (GSList *l = lines; (l != NULL) && (!over_line); l = l->next) {
SPCtrlLine *line = (SPCtrlLine*) l->data;
Geom::LineSegment ls(line->s, line->e);
- Geom::Point nearest = ls.pointAt(ls.nearestPoint(p));
+ Geom::Point nearest = ls.pointAt(ls.nearestTime(p));
double dist_screen = Geom::L2(p - nearest) * desktop->current_zoom();
if (line->item && dist_screen < 5) {
SPStop *stop = addStopNearPoint(line->item, p, 5/desktop->current_zoom());
@@ -1018,10 +1018,10 @@ static void gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &
if (state & GDK_CONTROL_MASK) {
Geom::LineSegment ls(low_lim, high_lim);
- p = ls.pointAt(round(ls.nearestPoint(p) / snap_fraction) * snap_fraction);
+ p = ls.pointAt(round(ls.nearestTime(p) / snap_fraction) * snap_fraction);
} else {
Geom::LineSegment ls(low_lim, high_lim);
- p = ls.pointAt(ls.nearestPoint(p));
+ p = ls.pointAt(ls.nearestTime(p));
if (!(state & GDK_SHIFT_MASK)) {
Inkscape::Snapper::SnapConstraint cl(low_lim, high_lim - low_lim);
SPDesktop *desktop = dragger->parent->desktop;
@@ -2394,7 +2394,7 @@ void GrDrag::selected_move(double x, double y, bool write_repr, bool scale_radia
gr_midpoint_limits(dragger, server, &begin, &end, &low_lim, &high_lim, &moving);
Geom::LineSegment ls(low_lim, high_lim);
- Geom::Point p = ls.pointAt(ls.nearestPoint(dragger->point + Geom::Point(x,y)));
+ Geom::Point p = ls.pointAt(ls.nearestTime(dragger->point + Geom::Point(x,y)));
Geom::Point displacement = p - dragger->point;
for (GSList const* i = moving; i != NULL; i = i->next) {
diff --git a/src/helper/geom-curves.h b/src/helper/geom-curves.h
index 4586a346c..7357403f7 100644
--- a/src/helper/geom-curves.h
+++ b/src/helper/geom-curves.h
@@ -14,15 +14,13 @@
* Released under GNU GPL
*/
-#include <2geom/hvlinesegment.h>
#include <2geom/line.h>
#include <2geom/bezier-curve.h>
/// \todo un-inline this function
-inline bool is_straight_curve(Geom::Curve const & c) {
- if( dynamic_cast<Geom::LineSegment const*>(&c) ||
- dynamic_cast<Geom::HLineSegment const*>(&c) ||
- dynamic_cast<Geom::VLineSegment const*>(&c) )
+inline bool is_straight_curve(Geom::Curve const & c)
+{
+ if( dynamic_cast<Geom::LineSegment const*>(&c) )
{
return true;
}
@@ -31,7 +29,7 @@ inline bool is_straight_curve(Geom::Curve const & c) {
Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&c);
if (curve) {
Geom::Line line(curve->initialPoint(), curve->finalPoint());
- std::vector<Geom::Point> pts = curve->points();
+ std::vector<Geom::Point> pts = curve->controlPoints();
for (unsigned i = 1; i < pts.size() - 1; ++i) {
if (!are_near(pts[i], line))
return false;
diff --git a/src/helper/geom-pathstroke.cpp b/src/helper/geom-pathstroke.cpp
index 930737572..e1038b03a 100644
--- a/src/helper/geom-pathstroke.cpp
+++ b/src/helper/geom-pathstroke.cpp
@@ -10,89 +10,20 @@
#include <2geom/path-sink.h>
#include <2geom/point.h>
#include <2geom/bezier-curve.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/sbasis-to-bezier.h> // cubicbezierpath_from_sbasis
#include <2geom/path-intersection.h>
+#include <2geom/circle.h>
#include "helper/geom-pathstroke.h"
namespace Geom {
-// 2geom/circle-circle.cpp, no header
-int circle_circle_intersection(Point X0, double r0, Point X1, double r1, Point &p0, Point &p1);
-
-/**
- * Determine the intersection points between a circle C0 and a line defined
- * by two points, X0 and X1.
- *
- * Which intersection point is assigned to p0 or p1 is unspecified, and callers
- * should not depend on any particular intersection always being assigned to p0.
- *
- * Returns:
- * If the line and circle do not cross, 0 is returned.
- * If solution(s) exist, 2 is returned, and the results are written to p0 and p1.
- */
-static int circle_line_intersection(Circle C0, Point X0, Point X1, Point &p0, Point &p1)
-{
- /* equation of a circle: (x - h)^2 + (y - k)^2 = r^2 */
- Coord r = C0.ray();
- Coord h = C0.center()[X];
- Coord k = C0.center()[Y];
-
- Coord x0, y0;
- Coord x1, y1;
-
- if (are_near(X1[X], X0[X])) {
- /* slope is undefined (vertical line) */
- Coord c = X0[X];
- Coord det = r*r - (c-h)*(c-h);
-
- /* no intersection */
- if (det < 0)
- return 0;
-
- /* solve for y */
- y0 = k + std::sqrt(det);
- y1 = k - std::sqrt(det);
-
- // x == c (always)
- x0 = c;
- x1 = c;
- } else {
- /* equation of a line: y = mx + b */
- Coord m = (X1[Y] - X0[Y]) / (X1[X] - X0[X]);
- Coord b = X0[Y] - m*X0[X];
-
- /* obtain quadratic for x: */
- Coord A = m*m + 1;
- Coord B = 2*h - 2*b*m + 2*k*m;
- Coord C = b*b + h*h + k*k - r*r - 2*b*k;
-
- Coord det = B*B - 4*A*C;
-
- /* no intersection, circle and line do not cross */
- if (det < 0)
- return 0;
-
- /* solve quadratic */
- x0 = (B + std::sqrt(det)) / (2*A);
- x1 = (B - std::sqrt(det)) / (2*A);
-
- /* substitute the calculated x times to determine the y values */
- y0 = m*x0 + b;
- y1 = m*x1 + b;
- }
-
- p0 = Point(x0, y0);
- p1 = Point(x1, y1);
-
- return 2;
-}
static Point intersection_point(Point origin_a, Point vector_a, Point origin_b, Point vector_b)
{
- Coord denom = cross(vector_b, vector_a);
+ Coord denom = cross(vector_a, vector_b);
if (!are_near(denom,0.)) {
- Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom;
+ Coord t = (cross(vector_b, origin_a) + cross(origin_b, vector_b)) / denom;
return origin_a + vector_a*t;
}
return Point(infinity(), infinity());
@@ -160,7 +91,7 @@ void bevel_join(join_data jd)
void round_join(join_data jd)
{
- jd.res.appendNew<Geom::SVGEllipticalArc>(jd.width, jd.width, 0, false, jd.width <= 0, jd.outgoing.initialPoint());
+ jd.res.appendNew<Geom::EllipticalArc>(jd.width, jd.width, 0, false, jd.width <= 0, jd.outgoing.initialPoint());
jd.res.append(jd.outgoing);
}
@@ -226,18 +157,20 @@ void miter_join_internal(join_data jd, bool clip)
void miter_join(join_data jd) { miter_join_internal(jd, false); }
void miter_clip_join(join_data jd) { miter_join_internal(jd, true); }
-Geom::Point pick_solution(Geom::Point points[2], Geom::Point tang2, Geom::Point endPt)
+Geom::Point pick_solution(std::vector<Geom::ShapeIntersection> points, Geom::Point tang2, Geom::Point endPt)
{
+ assert(points.size() == 2);
Geom::Point sol;
- if ( dot(tang2,points[0]-endPt) > 0 ) {
+ if ( dot(tang2, points[0].point() - endPt) > 0 ) {
// points[0] is bad, choose points[1]
sol = points[1];
- } else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1]
+ } else if ( dot(tang2, points[1].point() - endPt) > 0 ) { // points[0] could be good, now check points[1]
// points[1] is bad, choose points[0]
sol = points[0];
} else {
// both points are good, choose nearest
- sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ? points[0] : points[1];
+ sol = ( distanceSq(endPt, points[0].point()) < distanceSq(endPt, points[1].point()) )
+ ? points[0].point() : points[1].point();
}
return sol;
}
@@ -261,9 +194,8 @@ void extrapolate_join(join_data jd)
bool inc_ls = !circle1.center().isFinite();
bool out_ls = !circle2.center().isFinite();
- Geom::Point points[2];
+ std::vector<Geom::ShapeIntersection> points;
- int solutions = 0;
Geom::EllipticalArc *arc1 = NULL;
Geom::EllipticalArc *arc2 = NULL;
Geom::Point sol;
@@ -272,33 +204,29 @@ void extrapolate_join(join_data jd)
if (!inc_ls && !out_ls) {
// Two circles
- solutions = Geom::circle_circle_intersection(circle1.center(), circle1.ray(),
- circle2.center(), circle2.ray(),
- points[0], points[1]);
- if (solutions == 2) {
+ points = circle1.intersect(circle2);
+ if (points.size() == 2) {
sol = pick_solution(points, tang2, endPt);
- arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol, true);
- arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+ arc1 = circle1.arc(startPt, 0.5*(startPt+sol), sol);
+ arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt);
}
} else if (inc_ls && !out_ls) {
// Line and circle
- solutions = Geom::circle_line_intersection(circle2, incoming.initialPoint(), incoming.finalPoint(), points[0], points[1]);
-
- if (solutions == 2) {
+ points = circle2.intersect(Line(incoming.initialPoint(), incoming.finalPoint()));
+ if (points.size() == 2) {
sol = pick_solution(points, tang2, endPt);
- arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+ arc2 = circle2.arc(sol, 0.5*(sol+endPt), endPt);
}
} else if (!inc_ls && out_ls) {
// Circle and line
- solutions = Geom::circle_line_intersection(circle1, outgoing.initialPoint(), outgoing.finalPoint(), points[0], points[1]);
-
- if (solutions == 2) {
+ points = circle1.intersect(Line(outgoing.initialPoint(), outgoing.finalPoint()));
+ if (points.size() == 2) {
sol = pick_solution(points, tang2, endPt);
- arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol, true);
+ arc1 = circle1.arc(startPt, 0.5*(sol+startPt), sol);
}
}
- if (solutions != 2)
+ if (points.size() != 2)
// no solutions available, fall back to miter
return miter_clip_join(jd);
@@ -342,25 +270,24 @@ void extrapolate_join(join_data jd)
Geom::Ray end_ray(center, sol);
Geom::Line limit_line(center, 0); // Angle set below
- if (Geom::cross(start_ray.versor(), end_ray.versor()) > 0) {
+ if (Geom::cross(start_ray.versor(), end_ray.versor()) < 0) {
limit_line.setAngle(start_ray.angle() - limit_angle);
} else {
limit_line.setAngle(start_ray.angle() + limit_angle);
}
- Geom::EllipticalArc *arc_center = circle_center.arc(point_on_path, 0.5*(point_on_path + sol), sol, true);
+ Geom::EllipticalArc *arc_center = circle_center.arc(point_on_path, 0.5*(point_on_path + sol), sol);
if (arc_center && arc_center->sweepAngle() > limit_angle) {
// We need to clip
clipped = true;
if (!inc_ls) {
// Incoming circular
- solutions = Geom::circle_line_intersection(circle1, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]);
-
- if (solutions == 2) {
+ points = circle1.intersect(limit_line);
+ if (points.size() == 2) {
p1 = pick_solution(points, tang2, endPt);
delete arc1;
- arc1 = circle1.arc(startPt, 0.5*(p1+startPt), p1, true);
+ arc1 = circle1.arc(startPt, 0.5*(p1+startPt), p1);
}
} else {
p1 = Geom::intersection_point(startPt, tang1, limit_line.pointAt(0), limit_line.versor());
@@ -368,12 +295,11 @@ void extrapolate_join(join_data jd)
if (!out_ls) {
// Outgoing circular
- solutions = Geom::circle_line_intersection(circle2, limit_line.pointAt(0), limit_line.pointAt(1), points[0], points[1]);
-
- if (solutions == 2) {
+ points = circle2.intersect(limit_line);
+ if (points.size() == 2) {
p2 = pick_solution(points, tang1, endPt);
delete arc2;
- arc2 = circle2.arc(p2, 0.5*(p2+endPt), endPt, true);
+ arc2 = circle2.arc(p2, 0.5*(p2+endPt), endPt);
}
} else {
p2 = Geom::intersection_point(endPt, tang2, limit_line.pointAt(0), limit_line.versor());
@@ -471,7 +397,7 @@ void outline_helper(Geom::Path &res, Geom::Path const& temp, Geom::Point in_tang
join_data jd(res, temp, in_tang, out_tang, miter, width);
- bool on_outside = (Geom::cross(in_tang, out_tang) < 0);
+ bool on_outside = (Geom::cross(in_tang, out_tang) > 0);
if (on_outside) {
join_func *jf;
@@ -531,12 +457,12 @@ void get_cubic_data(Geom::CubicBezier const& bez, double time, double& len, doub
if (Geom::are_near(l, 0)) {
return; // this isn't a segment...
}
- rad = 1e8;
+ rad = 1e8;
} else {
- rad = -l * (Geom::dot(der2, der2) / Geom::cross(der3, der2));
+ rad = -l * (Geom::dot(der2, der2) / Geom::cross(der2, der3));
}
} else {
- rad = -l * (Geom::dot(der1, der1) / Geom::cross(der2, der1));
+ rad = -l * (Geom::dot(der1, der1) / Geom::cross(der1, der2));
}
len = l;
}
@@ -583,7 +509,7 @@ void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, dou
// reached maximum recursive depth
// don't bother with any more correction
if (levels == 0) {
- p.append(c, Geom::Path::STITCH_DISCONTINUOUS);
+ p.append(c);
return;
}
@@ -615,7 +541,7 @@ void offset_quadratic(Geom::Path& p, Geom::QuadraticBezier const& bez, double wi
// cheat
// it's faster
// seriously
- std::vector<Geom::Point> points = bez.points();
+ std::vector<Geom::Point> points = bez.controlPoints();
Geom::Point b1 = points[0] + (2./3) * (points[1] - points[0]);
Geom::Point b2 = b1 + (1./3) * (points[2] - points[0]);
Geom::CubicBezier cub = Geom::CubicBezier(points[0], b1, b2, points[2]);
@@ -703,7 +629,7 @@ Geom::PathVector outline(Geom::Path const& input, double width, double miter, Li
Geom::PathBuilder res;
Geom::Path with_dir = half_outline(input, width/2., miter, join);
- Geom::Path against_dir = half_outline(input.reverse(), width/2., miter, join);
+ Geom::Path against_dir = half_outline(input.reversed(), width/2., miter, join);
res.moveTo(with_dir[0].initialPoint());
res.append(with_dir);
@@ -752,6 +678,9 @@ Geom::Path half_outline(Geom::Path const& input, double width, double miter, Lin
Geom::Path temp;
Geom::Point tang[2];
+ res.setStitching(true);
+ temp.setStitching(true);
+
res.start(start);
// Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
diff --git a/src/helper/geom.cpp b/src/helper/geom.cpp
index 77cba4736..ecb330b01 100644
--- a/src/helper/geom.cpp
+++ b/src/helper/geom.cpp
@@ -15,8 +15,7 @@
#include <typeinfo>
#include <2geom/pathvector.h>
#include <2geom/path.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include <2geom/transforms.h>
#include <2geom/rect.h>
#include <2geom/coord.h>
@@ -468,8 +467,8 @@ pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv )
for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
output.push_back( Geom::Path() );
+ output.back().setStitching(true);
output.back().start( pit->initialPoint() );
- output.back().close( pit->closed() );
for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
if (is_straight_curve(*cit)) {
@@ -483,10 +482,13 @@ pathv_to_linear_and_cubic_beziers( Geom::PathVector const &pathv )
} else {
// convert all other curve types to cubicbeziers
Geom::Path cubicbezier_path = Geom::cubicbezierpath_from_sbasis(cit->toSBasis(), 0.1);
+ cubicbezier_path.close(false);
output.back().append(cubicbezier_path);
}
}
}
+
+ output.back().close( pit->closed() );
}
return output;
@@ -520,8 +522,7 @@ pathv_to_linear( Geom::PathVector const &pathv, double /*maxdisp*/)
}
else { /* all others must be Bezier curves */
Geom::BezierCurve const *curve = dynamic_cast<Geom::BezierCurve const *>(&*cit);
- Geom::CubicBezier b((*curve)[0], (*curve)[1], (*curve)[2], (*curve)[3]);
- std::vector<Geom::Point> bzrpoints = b.points();
+ std::vector<Geom::Point> bzrpoints = curve->controlPoints();
Geom::Point A = bzrpoints[0];
Geom::Point B = bzrpoints[1];
Geom::Point C = bzrpoints[2];
@@ -579,7 +580,7 @@ pathv_to_cubicbezier( Geom::PathVector const &pathv)
pitCubic.appendNew<Geom::LineSegment>( pitCubic.initialPoint() );
pitCubic.close(true);
}
- for (Geom::Path::const_iterator cit = pitCubic.begin(); cit != pitCubic.end_open(); ++cit) {
+ for (Geom::Path::iterator cit = pitCubic.begin(); cit != pitCubic.end_open(); ++cit) {
if (is_straight_curve(*cit)) {
Geom::CubicBezier b(cit->initialPoint(), cit->pointAt(0.3334) + Geom::Point(cubicGap,cubicGap), cit->finalPoint(), cit->finalPoint());
output.back().append(b);
@@ -849,41 +850,6 @@ recursive_bezier4(const double x1, const double y1,
recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1);
}
-
-/**
- * rounds all corners of the rectangle 'outwards', i.e. x0 and y0 are floored, x1 and y1 are ceiled.
- */
-void round_rectangle_outwards(Geom::Rect & rect) {
- Geom::Interval ints[2];
- for (int i=0; i < 2; i++) {
- ints[i] = Geom::Interval(std::floor(rect[i][0]), std::ceil(rect[i][1]));
- }
- rect = Geom::Rect(ints[0], ints[1]);
-}
-
-
-namespace Geom {
-
-bool transform_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) {
- return
- Geom::are_near(m0[0], m1[0], epsilon) &&
- Geom::are_near(m0[1], m1[1], epsilon) &&
- Geom::are_near(m0[2], m1[2], epsilon) &&
- Geom::are_near(m0[3], m1[3], epsilon);
-}
-
-
-bool translate_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) {
- return Geom::are_near(m0[4], m1[4], epsilon) && Geom::are_near(m0[5], m1[5], epsilon);
-}
-
-
-bool matrix_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon) {
- return transform_equalp(m0, m1, epsilon) && translate_equalp(m0, m1, epsilon);
-}
-
-} //end namespace Geom
-
/*
Local Variables:
mode:c++
diff --git a/src/helper/geom.h b/src/helper/geom.h
index 3232d9fd5..d49e2070c 100644
--- a/src/helper/geom.h
+++ b/src/helper/geom.h
@@ -33,13 +33,6 @@ void recursive_bezier4(const double x1, const double y1, const double x2, const
std::vector<Geom::Point> &pointlist,
int level);
-void round_rectangle_outwards(Geom::Rect & rect);
-
-namespace Geom{
-bool transform_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon);
-bool translate_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon);
-bool matrix_equalp(Geom::Affine const &m0, Geom::Affine const &m1, Geom::Coord const epsilon);
-}
#endif // INKSCAPE_HELPER_GEOM_H
/*
diff --git a/src/libdepixelize/priv/splines-kopf2011.h b/src/libdepixelize/priv/splines-kopf2011.h
index c586f74b7..c3da58c05 100644
--- a/src/libdepixelize/priv/splines-kopf2011.h
+++ b/src/libdepixelize/priv/splines-kopf2011.h
@@ -93,7 +93,7 @@ template<class T>
void worker(const typename HomogeneousSplines<T>::Polygon &source,
Splines::Path &dest, bool optimize)
{
- dest.pathVector.reserve(source.holes.size() + 1);
+ //dest.pathVector.reserve(source.holes.size() + 1);
for ( int i = 0 ; i != 4 ; ++i )
dest.rgba[i] = source.rgba[i];
diff --git a/src/livarot/PathConversion.cpp b/src/livarot/PathConversion.cpp
index 42df898e6..30e21d546 100644
--- a/src/livarot/PathConversion.cpp
+++ b/src/livarot/PathConversion.cpp
@@ -716,7 +716,7 @@ static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
{
Geom::Point se = iE - iS;
Geom::Point ca(cos(angle), sin(angle));
- Geom::Point cse(dot(se, ca), cross(se, ca));
+ Geom::Point cse(dot(ca, se), cross(ca, se));
cse[0] /= rx;
cse[1] /= ry;
double const lensq = dot(cse,cse);
@@ -753,8 +753,8 @@ static void ArcAnglesAndCenter(Geom::Point const &iS, Geom::Point const &iE,
csd[1] *= ry;
ca[1] = -ca[1]; // because it's the inverse rotation
- dr[0] = dot(csd, ca);
- dr[1] = cross(csd, ca);
+ dr[0] = dot(ca, csd);
+ dr[1] = cross(ca, csd);
ca[1] = -ca[1];
diff --git a/src/livarot/PathCutting.cpp b/src/livarot/PathCutting.cpp
index 49b2c5a78..086b30557 100644
--- a/src/livarot/PathCutting.cpp
+++ b/src/livarot/PathCutting.cpp
@@ -308,7 +308,7 @@ Path::MakePathVector()
{
/* TODO: add testcase for this descr_arcto case */
PathDescrArcTo *nData = dynamic_cast<PathDescrArcTo *>(descr_cmd[i]);
- currentpath->appendNew<Geom::SVGEllipticalArc>( nData->rx, nData->ry, nData->angle*M_PI/180.0, nData->large, !nData->clockwise, nData->p );
+ currentpath->appendNew<Geom::EllipticalArc>( nData->rx, nData->ry, nData->angle*M_PI/180.0, nData->large, !nData->clockwise, nData->p );
lastP = nData->p;
}
break;
@@ -400,11 +400,11 @@ void Path::AddCurve(Geom::Curve const &c)
Geom::Point tme = 3 * ((*cubic_bezier)[3] - (*cubic_bezier)[2]);
CubicTo (tmp, tms, tme);
}
- else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc const *>(&c)) {
- ArcTo( svg_elliptical_arc->finalPoint(),
- svg_elliptical_arc->ray(Geom::X), svg_elliptical_arc->ray(Geom::Y),
- svg_elliptical_arc->rotationAngle()*180.0/M_PI, // convert from radians to degrees
- svg_elliptical_arc->largeArc(), !svg_elliptical_arc->sweep() );
+ else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(&c)) {
+ ArcTo( elliptical_arc->finalPoint(),
+ elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y),
+ elliptical_arc->rotationAngle()*180.0/M_PI, // convert from radians to degrees
+ elliptical_arc->largeArc(), !elliptical_arc->sweep() );
} else {
//this case handles sbasis as well as all other curve types
Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
@@ -434,17 +434,11 @@ void Path::LoadPath(Geom::Path const &path, Geom::Affine const &tr, bool doTran
MoveTo( pathtr.initialPoint() );
- for(Geom::Path::const_iterator cit = pathtr.begin(); cit != pathtr.end_open(); ++cit) {
+ for(Geom::Path::const_iterator cit = pathtr.begin(); cit != pathtr.end(); ++cit) {
AddCurve(*cit);
}
if (pathtr.closed()) {
- // check if closing segment is empty before adding it
- Geom::Curve const &crv = pathtr.back_closed();
- if ( !crv.isDegenerate() ) {
- AddCurve(crv);
- }
-
Close();
}
}
@@ -513,10 +507,10 @@ double Path::Surface()
for (std::vector<path_lineto>::const_iterator i = pts.begin(); i != pts.end(); ++i) {
if ( i->isMoveTo == polyline_moveto ) {
- surf += Geom::cross(lastM - lastP, lastM);
+ surf += Geom::cross(lastM, lastM - lastP);
lastP = lastM = i->p;
} else {
- surf += Geom::cross(i->p - lastP, i->p);
+ surf += Geom::cross(i->p, i->p - lastP);
lastP = i->p;
}
diff --git a/src/livarot/PathOutline.cpp b/src/livarot/PathOutline.cpp
index 211ee31e2..e146bb908 100644
--- a/src/livarot/PathOutline.cpp
+++ b/src/livarot/PathOutline.cpp
@@ -1108,7 +1108,7 @@ Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &
}
return;
}
- rad = -l * (dot(dder,dder)) / (cross(ddder,dder));
+ rad = -l * (dot(dder,dder)) / (cross(dder, ddder));
tgt = dder / l;
if (before) {
tgt = -tgt;
@@ -1117,7 +1117,7 @@ Path::TangentOnCubAt (double at, Geom::Point const &iS, PathDescrCubicTo const &
}
len = l;
- rad = -l * (dot(der,der)) / (cross(dder,der));
+ rad = -l * (dot(der,der)) / (cross(der, dder));
tgt = der / l;
}
@@ -1156,7 +1156,7 @@ Path::TangentOnBezAt (double at, Geom::Point const &iS,
return;
}
len = l;
- rad = -l * (dot(der,der)) / (cross(dder,der));
+ rad = -l * (dot(der,der)) / (cross(der, dder));
tgt = der / l;
}
@@ -1180,7 +1180,7 @@ Path::OutlineJoin (Path * dest, Geom::Point pos, Geom::Point stNor, Geom::Point
TurnInside ^= PrevPos == pos;
PrevPos = pos;
- const double angSi = cross (enNor,stNor);
+ const double angSi = cross (stNor, enNor);
const double angCo = dot (stNor, enNor);
if ((fabs(angSi) < .0000001) && angCo > 0) { // The join is straight -> nothing to do.
diff --git a/src/livarot/PathSimplify.cpp b/src/livarot/PathSimplify.cpp
index 7d9f3f57d..81ddcd049 100644
--- a/src/livarot/PathSimplify.cpp
+++ b/src/livarot/PathSimplify.cpp
@@ -128,7 +128,7 @@ static double DistanceToCubic(Geom::Point const &start, PathDescrCubicTo res, Ge
}
Geom::Point seg = res.p - start;
- nnle = Geom::cross(seg, sp);
+ nnle = Geom::cross(sp, seg);
nnle *= nnle;
nnle /= Geom::dot(seg, seg);
if ( nnle < nle ) {
diff --git a/src/livarot/PathStroke.cpp b/src/livarot/PathStroke.cpp
index 6ec7fa209..4cfeb887a 100644
--- a/src/livarot/PathStroke.cpp
+++ b/src/livarot/PathStroke.cpp
@@ -292,7 +292,7 @@ void Path::DoJoin (Shape *dest, double width, JoinType join, Geom::Point pos, Ge
{
Geom::Point pnor = prev.ccw();
Geom::Point nnor = next.ccw();
- double angSi = cross(next, prev);
+ double angSi = cross(prev, next);
/* FIXED: this special case caused bug 1028953 */
if (angSi > -0.0001 && angSi < 0.0001) {
@@ -416,7 +416,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
{
Geom::Point pnor=prev.ccw();
Geom::Point nnor=next.ccw();
- double angSi = cross (next, prev);
+ double angSi = cross(prev, next);
if (angSi > -0.0001 && angSi < 0.0001)
{
double angCo = dot (prev, next);
@@ -444,7 +444,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
/* Geom::Point biss;
biss.x=next.x-prev.x;
biss.y=next.y-prev.y;
- double c2=cross(biss,next);
+ double c2=cross(next, biss);
double l=width/c2;
double projn=l*(dot(biss,next));
double projp=-l*(dot(biss,prev));
@@ -503,7 +503,7 @@ Path::DoLeftJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
}
else
{
- double s2 = cross (biss, nnor);
+ double s2 = cross(nnor, biss);
double dec = (l - emiter) * c2 / s2;
const Geom::Point tbiss=biss.ccw();
@@ -560,7 +560,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
{
const Geom::Point pnor=prev.ccw();
const Geom::Point nnor=next.ccw();
- double angSi = cross (next,prev);
+ double angSi = cross(prev, next);
if (angSi > -0.0001 && angSi < 0.0001)
{
double angCo = dot (prev, next);
@@ -614,7 +614,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
}
else
{
- double s2 = cross (biss, nnor);
+ double s2 = cross(nnor, biss);
double dec = (l - emiter) * c2 / s2;
const Geom::Point tbiss=biss.ccw();
@@ -667,7 +667,7 @@ Path::DoRightJoin (Shape * dest, double width, JoinType join, Geom::Point pos,
/* Geom::Point biss;
biss=next.x-prev.x;
biss.y=next.y-prev.y;
- double c2=cross(next,biss);
+ double c2=cross(biss, next);
double l=width/c2;
double projn=l*(dot(biss,next));
double projp=-l*(dot(biss,prev));
@@ -719,7 +719,7 @@ void Path::RecRound(Shape *dest, int sNo, int eNo, // start and end index
sia = 1;
} else {
double coa = dot(nS, nE);
- sia = cross(nS, nE);
+ sia = cross(nE, nS);
ang = acos(coa);
if ( coa >= 1 ) {
ang = 0;
diff --git a/src/livarot/Shape.cpp b/src/livarot/Shape.cpp
index ac6c72342..0bb3390e8 100644
--- a/src/livarot/Shape.cpp
+++ b/src/livarot/Shape.cpp
@@ -1683,7 +1683,7 @@ Shape::CmpToVert (Geom::Point ax, Geom::Point bx,bool as,bool bs)
Geom::Point av, bv;
av = ax;
bv = bx;
- double si = cross (bv, av);
+ double si = cross(av, bv);
int tstSi = 0;
if (si > 0.000001) tstSi = 1;
if (si < -0.000001) tstSi = -1;
@@ -2104,7 +2104,7 @@ Shape::PtWinding (const Geom::Point px) const
}
Geom::Point const diff = px - ast;
- double const cote = cross(diff, adir);
+ double const cote = cross(adir, diff);
if (cote == 0) continue;
if (cote < 0) {
if (ast[0] > px[0]) lr += nWeight;
diff --git a/src/livarot/ShapeSweep.cpp b/src/livarot/ShapeSweep.cpp
index b04b36bfd..1e6273964 100644
--- a/src/livarot/ShapeSweep.cpp
+++ b/src/livarot/ShapeSweep.cpp
@@ -1738,7 +1738,7 @@ Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, doub
}
}
- double ang = cross (rdir, ldir);
+ double ang = cross (ldir, rdir);
// ang*=iL->src->eData[iL->bord].isqlength;
// ang*=iR->src->eData[iR->bord].isqlength;
if (ang <= 0) return false; // edges in opposite directions: <-left ... right ->
@@ -1776,12 +1776,12 @@ Shape::TesteIntersection (SweepTree * iL, SweepTree * iR, Geom::Point &atx, doub
double srDot, erDot;
sDiff = iL->src->pData[lSt].rx - iR->src->pData[rSt].rx;
eDiff = iL->src->pData[lEn].rx - iR->src->pData[rSt].rx;
- srDot = cross (sDiff,rdir);
- erDot = cross (eDiff,rdir);
+ srDot = cross(rdir, sDiff);
+ erDot = cross(rdir, eDiff);
sDiff = iR->src->pData[rSt].rx - iL->src->pData[lSt].rx;
eDiff = iR->src->pData[rEn].rx - iL->src->pData[lSt].rx;
- slDot = cross (sDiff,ldir);
- elDot = cross (eDiff,ldir);
+ slDot = cross(ldir, sDiff);
+ elDot = cross(ldir, eDiff);
if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
{
@@ -2089,7 +2089,7 @@ Shape::Winding (const Geom::Point px) const
}
diff = px - ast;
- double cote = cross (diff,adir);
+ double cote = cross(adir, diff);
if (cote == 0)
continue;
if (cote < 0)
@@ -2563,15 +2563,15 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb,
double srDot, erDot;
sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
- srDot = cross (sDiff,rdir );
- erDot = cross (eDiff,rdir );
+ srDot = cross(rdir, sDiff);
+ erDot = cross(rdir, eDiff);
if ((srDot >= 0 && erDot >= 0) || (srDot <= 0 && erDot <= 0))
return false;
sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
- slDot = cross (sDiff,ldir );
- elDot = cross (eDiff,ldir);
+ slDot = cross(ldir, sDiff);
+ elDot = cross(ldir, eDiff);
if ((slDot >= 0 && elDot >= 0) || (slDot <= 0 && elDot <= 0))
return false;
@@ -2615,8 +2615,8 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb,
double sDot, eDot;
sDiff = ils->pData[lSt].rx - irs->pData[rSt].rx;
eDiff = ils->pData[lEn].rx - irs->pData[rSt].rx;
- sDot = cross (sDiff,rdir );
- eDot = cross (eDiff,rdir);
+ sDot = cross(rdir, sDiff);
+ eDot = cross(rdir, eDiff);
atx =
(sDot * irs->pData[lEn].rx - eDot * irs->pData[lSt].rx) / (sDot -
@@ -2625,8 +2625,8 @@ Shape::TesteIntersection (Shape * ils, Shape * irs, int ilb, int irb,
sDiff = irs->pData[rSt].rx - ils->pData[lSt].rx;
eDiff = irs->pData[rEn].rx - ils->pData[lSt].rx;
- sDot = cross (sDiff,ldir );
- eDot = cross (eDiff,ldir );
+ sDot = cross(ldir, sDiff);
+ eDot = cross(ldir, eDiff);
atR = sDot / (sDot - eDot);
@@ -2669,7 +2669,7 @@ Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt,
diff = atx - ast;
- double e = IHalfRound ((cross (diff,adir)) * a->eData[no].isqlength);
+ double e = IHalfRound(cross(adir, diff) * a->eData[no].isqlength);
if (-3 < e && e < 3)
{
double rad = HalfRound (0.501); // when using single precision, 0.505 is better (0.5 would be the correct value,
@@ -2684,16 +2684,16 @@ Shape::TesteAdjacency (Shape * a, int no, const Geom::Point atx, int nPt,
diff4[1] = diff[1] + rad;
double di1, di2;
bool adjacent = false;
- di1 = cross (diff1,adir);
- di2 = cross (diff3,adir);
+ di1 = cross(adir, diff1);
+ di2 = cross(adir, diff3);
if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
{
adjacent = true;
}
else
{
- di1 = cross ( diff2,adir);
- di2 = cross (diff4,adir);
+ di1 = cross(adir, diff2);
+ di2 = cross(adir, diff4);
if ((di1 < 0 && di2 > 0) || (di1 > 0 && di2 < 0))
{
adjacent = true;
diff --git a/src/livarot/sweep-tree.cpp b/src/livarot/sweep-tree.cpp
index 7a016a2ee..1b9868f2e 100644
--- a/src/livarot/sweep-tree.cpp
+++ b/src/livarot/sweep-tree.cpp
@@ -117,9 +117,9 @@ SweepTree::Find(Geom::Point const &px, SweepTree *newOne, SweepTree *&insertL,
nNorm=nNorm.ccw();
if (sweepSens) {
- y = cross(nNorm, bNorm);
- } else {
y = cross(bNorm, nNorm);
+ } else {
+ y = cross(nNorm, bNorm);
}
if (y == 0) {
y = dot(bNorm, nNorm);
@@ -345,7 +345,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue,
SweepTree *insertL = NULL;
SweepTree *insertR = NULL;
- double ang = cross(nNorm, bNorm);
+ double ang = cross(bNorm, nNorm);
if (ang == 0)
{
insertL = insNode;
@@ -384,7 +384,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue,
{
bNorm = -bNorm;
}
- ang = cross(nNorm, bNorm);
+ ang = cross(bNorm, nNorm);
if (ang <= 0)
{
break;
@@ -426,7 +426,7 @@ SweepTree::InsertAt(SweepTreeList &list, SweepEventQueue &queue,
{
bNorm = -bNorm;
}
- ang = cross(nNorm, bNorm);
+ ang = cross(bNorm, nNorm);
if (ang > 0)
{
break;
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index dace45739..c2c2ce93c 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -14,8 +14,6 @@ ink_common_sources += \
live_effects/lpe-patternalongpath.h \
live_effects/lpe-bendpath.cpp \
live_effects/lpe-bendpath.h \
- live_effects/lpe-boolops.cpp \
- live_effects/lpe-boolops.h \
live_effects/lpe-dynastroke.cpp \
live_effects/lpe-dynastroke.h \
live_effects/lpe-extrude.cpp \
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 53097171b..332d81777 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -14,54 +14,53 @@
// include effects:
#include "live_effects/lpe-patternalongpath.h"
#include "live_effects/effect.h"
+#include "live_effects/lpe-angle_bisector.h"
+#include "live_effects/lpe-attach-path.h"
#include "live_effects/lpe-bendpath.h"
-#include "live_effects/lpe-sketch.h"
-#include "live_effects/lpe-vonkoch.h"
-#include "live_effects/lpe-knot.h"
-#include "live_effects/lpe-rough-hatches.h"
-#include "live_effects/lpe-dynastroke.h"
-#include "live_effects/lpe-test-doEffect-stack.h"
-#include "live_effects/lpe-gears.h"
-#include "live_effects/lpe-curvestitch.h"
+#include "live_effects/lpe-bounding-box.h"
+#include "live_effects/lpe-bspline.h"
+#include "live_effects/lpe-circle_3pts.h"
#include "live_effects/lpe-circle_with_radius.h"
-#include "live_effects/lpe-perspective_path.h"
-#include "live_effects/lpe-perspective-envelope.h"
-#include "live_effects/lpe-spiro.h"
-#include "live_effects/lpe-lattice.h"
-#include "live_effects/lpe-lattice2.h"
-#include "live_effects/lpe-roughen.h"
-#include "live_effects/lpe-show_handles.h"
-#include "live_effects/lpe-simplify.h"
-#include "live_effects/lpe-envelope.h"
+#include "live_effects/lpe-clone-original.h"
#include "live_effects/lpe-constructgrid.h"
-#include "live_effects/lpe-perp_bisector.h"
-#include "live_effects/lpe-tangent_to_curve.h"
-#include "live_effects/lpe-mirror_symmetry.h"
-#include "live_effects/lpe-circle_3pts.h"
-#include "live_effects/lpe-angle_bisector.h"
-#include "live_effects/lpe-parallel.h"
#include "live_effects/lpe-copy_rotate.h"
-#include "live_effects/lpe-offset.h"
-#include "live_effects/lpe-ruler.h"
-#include "live_effects/lpe-boolops.h"
+#include "live_effects/lpe-curvestitch.h"
+#include "live_effects/lpe-dynastroke.h"
+#include "live_effects/lpe-ellipse_5pts.h"
+#include "live_effects/lpe-envelope.h"
+#include "live_effects/lpe-extrude.h"
+#include "live_effects/lpe-fill-between-many.h"
+#include "live_effects/lpe-fill-between-strokes.h"
+#include "live_effects/lpe-fillet-chamfer.h"
+#include "live_effects/lpe-gears.h"
#include "live_effects/lpe-interpolate.h"
#include "live_effects/lpe-interpolate_points.h"
-#include "live_effects/lpe-text_label.h"
-#include "live_effects/lpe-path_length.h"
+#include "live_effects/lpe-jointype.h"
+#include "live_effects/lpe-knot.h"
+#include "live_effects/lpe-lattice2.h"
+#include "live_effects/lpe-lattice.h"
#include "live_effects/lpe-line_segment.h"
-#include "live_effects/lpe-recursiveskeleton.h"
-#include "live_effects/lpe-extrude.h"
+#include "live_effects/lpe-mirror_symmetry.h"
+#include "live_effects/lpe-offset.h"
+#include "live_effects/lpe-parallel.h"
+#include "live_effects/lpe-path_length.h"
+#include "live_effects/lpe-perp_bisector.h"
+#include "live_effects/lpe-perspective-envelope.h"
+#include "live_effects/lpe-perspective_path.h"
#include "live_effects/lpe-powerstroke.h"
-#include "live_effects/lpe-clone-original.h"
-#include "live_effects/lpe-bspline.h"
-#include "live_effects/lpe-attach-path.h"
-#include "live_effects/lpe-fill-between-strokes.h"
-#include "live_effects/lpe-fill-between-many.h"
-#include "live_effects/lpe-ellipse_5pts.h"
-#include "live_effects/lpe-bounding-box.h"
-#include "live_effects/lpe-jointype.h"
+#include "live_effects/lpe-recursiveskeleton.h"
+#include "live_effects/lpe-roughen.h"
+#include "live_effects/lpe-rough-hatches.h"
+#include "live_effects/lpe-ruler.h"
+#include "live_effects/lpe-show_handles.h"
+#include "live_effects/lpe-simplify.h"
+#include "live_effects/lpe-sketch.h"
+#include "live_effects/lpe-spiro.h"
+#include "live_effects/lpe-tangent_to_curve.h"
#include "live_effects/lpe-taperstroke.h"
-#include "live_effects/lpe-fillet-chamfer.h"
+#include "live_effects/lpe-test-doEffect-stack.h"
+#include "live_effects/lpe-text_label.h"
+#include "live_effects/lpe-vonkoch.h"
#include "xml/node-event-vector.h"
#include "sp-object.h"
@@ -98,8 +97,6 @@ const Util::EnumData<EffectType> LPETypeData[] = {
#ifdef LPE_ENABLE_TEST_EFFECTS
{DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
{ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
- // TRANSLATORS: boolean operations
- {BOOLOPS, N_("Boolops"), "boolops"},
{CIRCLE_WITH_RADIUS, N_("Circle (by center and radius)"), "circle_with_radius"},
{CIRCLE_3PTS, N_("Circle by 3 points"), "circle_3pts"},
{DYNASTROKE, N_("Dynamic stroke"), "dynastroke"},
@@ -243,9 +240,6 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case RULER:
neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
break;
- case BOOLOPS:
- neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
- break;
case INTERPOLATE:
neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
break;
@@ -520,24 +514,24 @@ Effect::acceptParamPath (SPPath const*/*param_path*/) {
void
Effect::doEffect (SPCurve * curve)
{
- std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
+ Geom::PathVector orig_pathv = curve->get_pathvector();
- std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
+ Geom::PathVector result_pathv = doEffect_path(orig_pathv);
curve->set_pathvector(result_pathv);
}
-std::vector<Geom::Path>
-Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+Effect::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
if ( !concatenate_before_pwd2 ) {
// default behavior
for (unsigned int i=0; i < path_in.size(); i++) {
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
- std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
+ Geom::PathVector path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
// add the output path vector to the already accumulated vector:
for (unsigned int j=0; j < path.size(); j++) {
path_out.push_back(path[j]);
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index a6b5a13ea..ea57ff243 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -129,9 +129,9 @@ protected:
// of what kind of input/output parameters he desires.
// the order in which they appear is the order in which they are
// called by this base class. (i.e. doEffect(SPCurve * curve) defaults to calling
- // doEffect(std::vector<Geom::Path> )
- virtual std::vector<Geom::Path>
- doEffect_path (std::vector<Geom::Path> const & path_in);
+ // doEffect(Geom::PathVector )
+ virtual Geom::PathVector
+ doEffect_path (Geom::PathVector const & path_in);
virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
@@ -162,7 +162,7 @@ protected:
double current_zoom;
std::vector<Geom::Point> selectedNodesPoints;
SPCurve * sp_curve;
- std::vector<Geom::Path> pathvector_before_effect;
+ Geom::PathVector pathvector_before_effect;
private:
bool provides_own_flash_paths; // if true, the standard flash path is suppressed
diff --git a/src/live_effects/lpe-angle_bisector.cpp b/src/live_effects/lpe-angle_bisector.cpp
index 1acf9605f..95a81c763 100644
--- a/src/live_effects/lpe-angle_bisector.cpp
+++ b/src/live_effects/lpe-angle_bisector.cpp
@@ -56,8 +56,8 @@ LPEAngleBisector::~LPEAngleBisector()
{
}
-std::vector<Geom::Path>
-LPEAngleBisector::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEAngleBisector::doEffect_path (Geom::PathVector const & path_in)
{
using namespace Geom;
@@ -73,7 +73,7 @@ LPEAngleBisector::doEffect_path (std::vector<Geom::Path> const & path_in)
Geom::Point D = ptA - dir * length_left;
Geom::Point E = ptA + dir * length_right;
- Piecewise<D2<SBasis> > output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(D[X], E[X]), Linear(D[Y], E[Y])));
+ Piecewise<D2<SBasis> > output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(D[X], E[X]), SBasis(D[Y], E[Y])));
return path_from_piecewise(output, LPE_CONVERSION_TOLERANCE);
}
@@ -103,7 +103,7 @@ KnotHolderEntityLeftEnd::knot_set(Geom::Point const &p, Geom::Point const &/*ori
Geom::Point const s = snap_knot_position(p, state);
- double lambda = Geom::nearest_point(s, lpe->ptA, lpe->dir);
+ double lambda = Geom::nearest_time(s, lpe->ptA, lpe->dir);
lpe->length_left.param_set_value(-lambda);
sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
@@ -116,7 +116,7 @@ KnotHolderEntityRightEnd::knot_set(Geom::Point const &p, Geom::Point const &/*or
Geom::Point const s = snap_knot_position(p, state);
- double lambda = Geom::nearest_point(s, lpe->ptA, lpe->dir);
+ double lambda = Geom::nearest_time(s, lpe->ptA, lpe->dir);
lpe->length_right.param_set_value(lambda);
sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
diff --git a/src/live_effects/lpe-angle_bisector.h b/src/live_effects/lpe-angle_bisector.h
index 95048751e..400cbf6b6 100644
--- a/src/live_effects/lpe-angle_bisector.h
+++ b/src/live_effects/lpe-angle_bisector.h
@@ -28,7 +28,7 @@ public:
LPEAngleBisector(LivePathEffectObject *lpeobject);
virtual ~LPEAngleBisector();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
friend class AB::KnotHolderEntityLeftEnd;
friend class AB::KnotHolderEntityRightEnd;
diff --git a/src/live_effects/lpe-attach-path.cpp b/src/live_effects/lpe-attach-path.cpp
index 768c66ee2..21459f322 100644
--- a/src/live_effects/lpe-attach-path.cpp
+++ b/src/live_effects/lpe-attach-path.cpp
@@ -64,7 +64,7 @@ void LPEAttachPath::resetDefaults(SPItem const * /*item*/)
void LPEAttachPath::doEffect (SPCurve * curve)
{
- std::vector<Geom::Path> this_pathv = curve->get_pathvector();
+ Geom::PathVector this_pathv = curve->get_pathvector();
if (sp_lpe_item && !this_pathv.empty()) {
Geom::Path p = Geom::Path(this_pathv.front().initialPoint());
@@ -73,7 +73,7 @@ void LPEAttachPath::doEffect (SPCurve * curve)
if (start_path.linksToPath()) {
- std::vector<Geom::Path> linked_pathv = start_path.get_pathvector();
+ Geom::PathVector linked_pathv = start_path.get_pathvector();
Geom::Affine linkedtransform = start_path.getObject()->getRelativeTransform(sp_lpe_item);
if ( !linked_pathv.empty() )
@@ -87,7 +87,7 @@ void LPEAttachPath::doEffect (SPCurve * curve)
Geom::Coord length = derivs[deriv_n].length();
if ( ! Geom::are_near(length, 0) ) {
if (set_start_end) {
- start_path_position.param_set_value(transformedpath.nearestPoint(start_path_curve_end.getOrigin()));
+ start_path_position.param_set_value(transformedpath.nearestTime(start_path_curve_end.getOrigin()).asFlatTime());
}
if (start_path_position > transformedpath.size()) {
@@ -95,7 +95,8 @@ void LPEAttachPath::doEffect (SPCurve * curve)
} else if (start_path_position < 0) {
start_path_position.param_set_value(0);
}
- const Geom::Curve *c = start_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)start_path_position);
+ Geom::Curve const *c = start_path_position >= transformedpath.size() ?
+ &transformedpath.back() : &transformedpath.at((int)start_path_position);
std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(start_path_position >= transformedpath.size() ? 1 : (start_path_position - (int)start_path_position), 3);
for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
@@ -126,7 +127,7 @@ void LPEAttachPath::doEffect (SPCurve * curve)
if (end_path.linksToPath()) {
- std::vector<Geom::Path> linked_pathv = end_path.get_pathvector();
+ Geom::PathVector linked_pathv = end_path.get_pathvector();
Geom::Affine linkedtransform = end_path.getObject()->getRelativeTransform(sp_lpe_item);
if ( !linked_pathv.empty() )
@@ -141,7 +142,7 @@ void LPEAttachPath::doEffect (SPCurve * curve)
Geom::Coord length = derivs[deriv_n].length();
if ( ! Geom::are_near(length, 0) ) {
if (set_end_end) {
- end_path_position.param_set_value(transformedpath.nearestPoint(end_path_curve_end.getOrigin()));
+ end_path_position.param_set_value(transformedpath.nearestTime(end_path_curve_end.getOrigin()).asFlatTime());
}
if (end_path_position > transformedpath.size()) {
@@ -149,7 +150,8 @@ void LPEAttachPath::doEffect (SPCurve * curve)
} else if (end_path_position < 0) {
end_path_position.param_set_value(0);
}
- const Geom::Curve *c = end_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)end_path_position);
+ const Geom::Curve *c = end_path_position >= transformedpath.size() ?
+ &transformedpath.back() : &transformedpath.at((int)end_path_position);
std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(end_path_position >= transformedpath.size() ? 1 : (end_path_position - (int)end_path_position), 3);
for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
diff --git a/src/live_effects/lpe-boolops.cpp b/src/live_effects/lpe-boolops.cpp
deleted file mode 100644
index 641cf5d50..000000000
--- a/src/live_effects/lpe-boolops.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-/** \file
- * LPE boolops implementation
- */
-/*
- * Authors:
- * Johan Engelen
- *
- * Copyright (C) Johan Engelen 2007-2008 <j.b.c.engelen@utwente.nl>
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "live_effects/lpe-boolops.h"
-
-#include <2geom/path.h>
-#include <2geom/shape.h>
-
-namespace Inkscape {
-namespace LivePathEffect {
-
-static const Util::EnumData<unsigned> BoolopTypeData[] = {
- {Geom::BOOLOP_NULL , N_("Null"), "null"},
- {Geom::BOOLOP_INTERSECT , N_("Intersect"), "intersect"},
- {Geom::BOOLOP_SUBTRACT_A_B , N_("Subtract A-B"), "subtract_a_b"},
- {Geom::BOOLOP_IDENTITY_A , N_("Identity A"), "identity_a"},
- {Geom::BOOLOP_SUBTRACT_B_A , N_("Subtract B-A"), "subtract_b_a"},
- {Geom::BOOLOP_IDENTITY_B , N_("Identity B"), "identity_b"},
- {Geom::BOOLOP_EXCLUSION , N_("Exclusion"), "Exclusion"},
- {Geom::BOOLOP_UNION , N_("Union"), "Union"}
-};
-static const Util::EnumDataConverter<unsigned> BoolopTypeConverter(BoolopTypeData, sizeof(BoolopTypeData)/sizeof(*BoolopTypeData));
-
-LPEBoolops::LPEBoolops(LivePathEffectObject *lpeobject) :
- Effect(lpeobject),
- bool_path(_("2nd path:"), _("Path to which the original path will be boolop'ed."), "path_2nd", &wr, this, "M0,0 L1,0"),
- boolop_type(_("Boolop type:"), _("Determines which kind of boolop will be performed."), "boolop_type", BoolopTypeConverter, &wr, this, Geom::BOOLOP_UNION)
-{
- show_orig_path = true;
-
- registerParameter( dynamic_cast<Parameter *>(&boolop_type) );
- registerParameter( dynamic_cast<Parameter *>(&bool_path) );
-}
-
-LPEBoolops::~LPEBoolops()
-{
-
-}
-
-
-Geom::PathVector
-LPEBoolops::doEffect_path (Geom::PathVector const & path_in)
-{
- std::vector<Geom::Path> path_out;
-
- Geom::Shape shape_in = Geom::sanitize(path_in);
-
- Geom::Shape shape_param = Geom::sanitize(bool_path.get_pathvector());
-
- Geom::Shape shape_out = Geom::boolop(shape_in, shape_param, boolop_type.get_value());
-
- path_out = Geom::desanitize(shape_out);
-
- return path_out;
-}
-
-
-} //namespace LivePathEffect
-} /* namespace Inkscape */
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-boolops.h b/src/live_effects/lpe-boolops.h
deleted file mode 100644
index 3c8dc85c4..000000000
--- a/src/live_effects/lpe-boolops.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef INKSCAPE_LPE_BOOLOPS_H
-#define INKSCAPE_LPE_BOOLOPS_H
-
-/** \file
- * LPE boolops implementation, see lpe-boolops.cpp.
- */
-
-/*
- * Authors:
- * Johan Engelen
- *
- * Copyright (C) Johan Engelen 2007-2008 <j.b.c.engelen@utwente.nl>
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include "live_effects/parameter/enum.h"
-#include "live_effects/effect.h"
-#include "live_effects/parameter/path.h"
-
-namespace Inkscape {
-namespace LivePathEffect {
-
-class LPEBoolops : public Effect {
-public:
- LPEBoolops(LivePathEffectObject *lpeobject);
- virtual ~LPEBoolops();
-
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
-
-private:
- PathParam bool_path;
- EnumParam<unsigned> boolop_type;
-
- LPEBoolops(const LPEBoolops&);
- LPEBoolops& operator=(const LPEBoolops&);
-};
-
-} //namespace LivePathEffect
-} //namespace Inkscape
-
-#endif // INKSCAPE_LPE_BOOLOPS_H
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-bounding-box.cpp b/src/live_effects/lpe-bounding-box.cpp
index bafd5e70e..43a60d482 100644
--- a/src/live_effects/lpe-bounding-box.cpp
+++ b/src/live_effects/lpe-bounding-box.cpp
@@ -45,7 +45,7 @@ void LPEBoundingBox::doEffect (SPCurve * curve)
p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
- std::vector<Geom::Path> out;
+ Geom::PathVector out;
out.push_back(p);
curve->set_pathvector(out);
}
diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp
index 99e2a55fd..c2a2d080e 100644
--- a/src/live_effects/lpe-bspline.cpp
+++ b/src/live_effects/lpe-bspline.cpp
@@ -124,12 +124,12 @@ void LPEBSpline::doEffect(SPCurve *curve)
if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER);
} else {
- point_at1 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment()));
+ point_at1 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[1], *in->first_segment()));
}
if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) {
point_at2 = sbasis_in.valueAt(DEFAULT_END_POWER);
} else {
- point_at2 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment()));
+ point_at2 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[2], *in->first_segment()));
}
} else {
point_at1 = in->first_segment()->initialPoint();
@@ -147,7 +147,7 @@ void LPEBSpline::doEffect(SPCurve *curve)
if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
next_point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER);
} else {
- next_point_at1 = sbasis_out.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment()));
+ next_point_at1 = sbasis_out.valueAt(Geom::nearest_time((*cubic)[1], *out->first_segment()));
}
} else {
next_point_at1 = out->first_segment()->initialPoint();
@@ -164,7 +164,7 @@ void LPEBSpline::doEffect(SPCurve *curve)
cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin());
if (cubic) {
line_helper->moveto(sbasis_start.valueAt(
- Geom::nearest_point((*cubic)[1], *start->first_segment())));
+ Geom::nearest_time((*cubic)[1], *start->first_segment())));
} else {
line_helper->moveto(start->first_segment()->initialPoint());
}
@@ -178,7 +178,7 @@ void LPEBSpline::doEffect(SPCurve *curve)
cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
if (cubic) {
line_helper->lineto(sbasis_end.valueAt(
- Geom::nearest_point((*cubic)[2], *end->first_segment())));
+ Geom::nearest_time((*cubic)[2], *end->first_segment())));
} else {
line_helper->lineto(end->first_segment()->finalPoint());
}
@@ -236,7 +236,7 @@ LPEBSpline::drawHandle(Geom::Point p, double helper_size)
Geom::Affine aff = Geom::Affine();
aff *= Geom::Scale(helper_size);
pathv *= aff;
- pathv += p - Geom::Point(0.5*helper_size, 0.5*helper_size);
+ pathv *= Geom::Translate(p - Geom::Point(0.5*helper_size, 0.5*helper_size));
hp.push_back(pathv[0]);
}
diff --git a/src/live_effects/lpe-circle_3pts.cpp b/src/live_effects/lpe-circle_3pts.cpp
index fb7496f58..dbb1f4b6b 100644
--- a/src/live_effects/lpe-circle_3pts.cpp
+++ b/src/live_effects/lpe-circle_3pts.cpp
@@ -17,6 +17,7 @@
// You might need to include other 2geom files. You can add them here:
#include <2geom/path.h>
#include <2geom/circle.h>
+#include <2geom/path-sink.h>
namespace Inkscape {
namespace LivePathEffect {
@@ -30,7 +31,7 @@ LPECircle3Pts::~LPECircle3Pts()
{
}
-static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, std::vector<Geom::Path> &path_out) {
+static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point const &C, Geom::PathVector &path_out) {
using namespace Geom;
Point D = (A + B)/2;
@@ -47,13 +48,13 @@ static void _circle3(Geom::Point const &A, Geom::Point const &B, Geom::Point con
double radius = L2(M - A);
Geom::Circle c(M, radius);
- c.getPath(path_out);
+ path_out = Geom::Path(c);
}
-std::vector<Geom::Path>
-LPECircle3Pts::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPECircle3Pts::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+ Geom::PathVector path_out = Geom::PathVector();
// we assume that the path has >= 3 nodes
Geom::Point A = path_in[0].initialPoint();
diff --git a/src/live_effects/lpe-circle_3pts.h b/src/live_effects/lpe-circle_3pts.h
index 2533fe251..07c2eddb0 100644
--- a/src/live_effects/lpe-circle_3pts.h
+++ b/src/live_effects/lpe-circle_3pts.h
@@ -27,7 +27,7 @@ public:
LPECircle3Pts(LivePathEffectObject *lpeobject);
virtual ~LPECircle3Pts();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
LPECircle3Pts(const LPECircle3Pts&);
diff --git a/src/live_effects/lpe-circle_with_radius.cpp b/src/live_effects/lpe-circle_with_radius.cpp
index 8a32cd230..8f2156044 100644
--- a/src/live_effects/lpe-circle_with_radius.cpp
+++ b/src/live_effects/lpe-circle_with_radius.cpp
@@ -15,11 +15,9 @@
#include "display/curve.h"
// You might need to include other 2geom files. You can add them here:
-#include <2geom/path.h>
-#include <2geom/sbasis.h>
-#include <2geom/bezier-to-sbasis.h>
-#include <2geom/d2.h>
+#include <2geom/pathvector.h>
#include <2geom/circle.h>
+#include <2geom/path-sink.h>
using namespace Geom;
@@ -40,10 +38,10 @@ LPECircleWithRadius::~LPECircleWithRadius()
}
-std::vector<Geom::Path>
-LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPECircleWithRadius::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+ Geom::PathVector path_out = Geom::PathVector();
Geom::Point center = path_in[0].initialPoint();
Geom::Point pt = path_in[0].finalPoint();
@@ -51,9 +49,7 @@ LPECircleWithRadius::doEffect_path (std::vector<Geom::Path> const & path_in)
double radius = Geom::L2(pt - center);
Geom::Circle c(center, radius);
- c.getPath(path_out);
-
- return path_out;
+ return Geom::Path(c);
}
/*
diff --git a/src/live_effects/lpe-circle_with_radius.h b/src/live_effects/lpe-circle_with_radius.h
index 10f652771..f5bf0c414 100644
--- a/src/live_effects/lpe-circle_with_radius.h
+++ b/src/live_effects/lpe-circle_with_radius.h
@@ -26,7 +26,7 @@ public:
virtual ~LPECircleWithRadius();
// Choose to implement one of the doEffect functions. You can delete or comment out the others.
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
// add the parameters for your effect here:
diff --git a/src/live_effects/lpe-clone-original.cpp b/src/live_effects/lpe-clone-original.cpp
index d7cd6e19c..7541c0be2 100644
--- a/src/live_effects/lpe-clone-original.cpp
+++ b/src/live_effects/lpe-clone-original.cpp
@@ -28,7 +28,7 @@ LPECloneOriginal::~LPECloneOriginal()
void LPECloneOriginal::doEffect (SPCurve * curve)
{
if ( linked_path.linksToPath() ) {
- std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
+ Geom::PathVector linked_pathv = linked_path.get_pathvector();
if ( !linked_pathv.empty() ) {
curve->set_pathvector(linked_pathv);
}
diff --git a/src/live_effects/lpe-constructgrid.cpp b/src/live_effects/lpe-constructgrid.cpp
index fbf9faf56..b1e0edaac 100644
--- a/src/live_effects/lpe-constructgrid.cpp
+++ b/src/live_effects/lpe-constructgrid.cpp
@@ -41,8 +41,8 @@ LPEConstructGrid::~LPEConstructGrid()
}
-std::vector<Geom::Path>
-LPEConstructGrid::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEConstructGrid::doEffect_path (Geom::PathVector const & path_in)
{
// Check that the path has at least 3 nodes (i.e. 2 segments), more nodes are ignored
if (path_in[0].size() >= 2) {
@@ -64,7 +64,7 @@ LPEConstructGrid::doEffect_path (std::vector<Geom::Path> const & path_in)
second_path.appendNew<LineSegment>( origin + second_p*nr_x );
// use the gridpaths and set them in the correct grid
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
path_out.push_back(first_path);
for (int ix = 0; ix < nr_x; ix++) {
path_out.push_back(path_out.back() * second_translation );
diff --git a/src/live_effects/lpe-constructgrid.h b/src/live_effects/lpe-constructgrid.h
index c7e695794..49c986742 100644
--- a/src/live_effects/lpe-constructgrid.h
+++ b/src/live_effects/lpe-constructgrid.h
@@ -25,7 +25,7 @@ public:
LPEConstructGrid(LivePathEffectObject *lpeobject);
virtual ~LPEConstructGrid();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
ScalarParam nr_x;
diff --git a/src/live_effects/lpe-curvestitch.cpp b/src/live_effects/lpe-curvestitch.cpp
index 854cc0734..609447f26 100644
--- a/src/live_effects/lpe-curvestitch.cpp
+++ b/src/live_effects/lpe-curvestitch.cpp
@@ -68,8 +68,8 @@ LPECurveStitch::~LPECurveStitch()
}
-std::vector<Geom::Path>
-LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPECurveStitch::doEffect_path (Geom::PathVector const & path_in)
{
if (path_in.size() >= 2) {
startpoint_edge_variation.resetRandomizer();
@@ -86,7 +86,7 @@ LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in)
gdouble scaling = bndsStroke->max() - bndsStroke->min();
Point stroke_origin(bndsStroke->min(), (bndsStrokeY->max()+bndsStrokeY->min())/2);
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
// do this for all permutations (ii,jj) if there are more than 2 paths? realllly cool!
for (unsigned ii = 0 ; ii < path_in.size() - 1; ii++)
@@ -127,7 +127,7 @@ LPECurveStitch::doEffect_path (std::vector<Geom::Path> const & path_in)
// add stuff to one big pw<d2<sbasis> > and then outside the loop convert to path?
// No: this way, the separate result paths are kept separate which might come in handy some time!
- std::vector<Geom::Path> result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE);
+ Geom::PathVector result = Geom::path_from_piecewise(pwd2_out, LPE_CONVERSION_TOLERANCE);
path_out.push_back(result[0]);
}
gdouble svA = startpoint_spacing_variation - startpoint_spacing_variation.get_value()/2;
@@ -162,7 +162,7 @@ LPECurveStitch::resetDefaults(SPItem const* item)
// calculate bounding box: (isn't there a simpler way?)
Piecewise<D2<SBasis> > pwd2;
- std::vector<Geom::Path> temppath = sp_svg_read_pathv( item->getRepr()->attribute("inkscape:original-d"));
+ Geom::PathVector temppath = sp_svg_read_pathv( item->getRepr()->attribute("inkscape:original-d"));
for (unsigned int i=0; i < temppath.size(); i++) {
pwd2.concat( temppath[i].toPwSb() );
}
diff --git a/src/live_effects/lpe-curvestitch.h b/src/live_effects/lpe-curvestitch.h
index 38a11b4af..c6ea66f6c 100644
--- a/src/live_effects/lpe-curvestitch.h
+++ b/src/live_effects/lpe-curvestitch.h
@@ -28,7 +28,7 @@ public:
LPECurveStitch(LivePathEffectObject *lpeobject);
virtual ~LPECurveStitch();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual void resetDefaults(SPItem const* item);
diff --git a/src/live_effects/lpe-ellipse_5pts.cpp b/src/live_effects/lpe-ellipse_5pts.cpp
index b0a5919fe..4c953bcda 100644
--- a/src/live_effects/lpe-ellipse_5pts.cpp
+++ b/src/live_effects/lpe-ellipse_5pts.cpp
@@ -67,10 +67,10 @@ static double _det5(double (*mat)[5])
return mat[4][4];
}
-std::vector<Geom::Path>
-LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEEllipse5Pts::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+ Geom::PathVector path_out = Geom::PathVector();
if (path_in[0].size() < 4) {
@@ -190,7 +190,7 @@ LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in)
p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3));
}
- Geom::Affine aff = Geom::Scale(el.ray(Geom::X), el.ray(Geom::Y)) * Geom::Rotate(el.rot_angle()) * Geom::Translate(el.center());
+ Geom::Affine aff = Geom::Scale(el.rays()) * Geom::Rotate(el.rotationAngle()) * Geom::Translate(el.center());
path_out.push_back(p * aff);
diff --git a/src/live_effects/lpe-ellipse_5pts.h b/src/live_effects/lpe-ellipse_5pts.h
index d3b1fccfa..691a693dc 100644
--- a/src/live_effects/lpe-ellipse_5pts.h
+++ b/src/live_effects/lpe-ellipse_5pts.h
@@ -26,7 +26,7 @@ public:
LPEEllipse5Pts(LivePathEffectObject *lpeobject);
virtual ~LPEEllipse5Pts();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
LPEEllipse5Pts(const LPEEllipse5Pts&);
diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp
index 7cf354044..3e0810cfc 100644
--- a/src/live_effects/lpe-fill-between-many.cpp
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -35,14 +35,14 @@ LPEFillBetweenMany::~LPEFillBetweenMany()
void LPEFillBetweenMany::doEffect (SPCurve * curve)
{
- std::vector<Geom::Path> res_pathv;
+ Geom::PathVector res_pathv;
SPItem * firstObj = NULL;
for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) {
SPObject *obj;
if ((*iter)->ref.isAttached() && (obj = (*iter)->ref.getObject()) && SP_IS_ITEM(obj) && !(*iter)->_pathvector.empty()) {
Geom::Path linked_path;
if ((*iter)->reversed) {
- linked_path = (*iter)->_pathvector.front().reverse();
+ linked_path = (*iter)->_pathvector.front().reversed();
} else {
linked_path = (*iter)->_pathvector.front();
}
diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp
index e72979ed0..89ea80545 100644
--- a/src/live_effects/lpe-fill-between-strokes.cpp
+++ b/src/live_effects/lpe-fill-between-strokes.cpp
@@ -39,17 +39,17 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
{
if (curve) {
if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) {
- std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
- std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
- std::vector<Geom::Path> result_linked_pathv;
- std::vector<Geom::Path> result_second_pathv;
+ Geom::PathVector linked_pathv = linked_path.get_pathvector();
+ Geom::PathVector second_pathv = second_path.get_pathvector();
+ Geom::PathVector result_linked_pathv;
+ Geom::PathVector result_second_pathv;
Geom::Affine second_transform = second_path.getObject()->getRelativeTransform(linked_path.getObject());
- for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
+ for (Geom::PathVector::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
{
result_linked_pathv.push_back((*iter));
}
- for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
+ for (Geom::PathVector::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
{
result_second_pathv.push_back((*iter) * second_transform);
}
@@ -58,7 +58,7 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
if (reverse_second.get_value())
{
result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint());
- result_linked_pathv.front().append(result_second_pathv.front().reverse());
+ result_linked_pathv.front().append(result_second_pathv.front().reversed());
}
else
{
@@ -75,10 +75,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
}
}
else if ( linked_path.linksToPath() && linked_path.getObject() ) {
- std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
- std::vector<Geom::Path> result_pathv;
+ Geom::PathVector linked_pathv = linked_path.get_pathvector();
+ Geom::PathVector result_pathv;
- for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
+ for (Geom::PathVector::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
{
result_pathv.push_back((*iter));
}
@@ -87,10 +87,10 @@ void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
}
}
else if ( second_path.linksToPath() && second_path.getObject() ) {
- std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
- std::vector<Geom::Path> result_pathv;
+ Geom::PathVector second_pathv = second_path.get_pathvector();
+ Geom::PathVector result_pathv;
- for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
+ for (Geom::PathVector::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
{
result_pathv.push_back((*iter));
}
diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp
index ca87c7be1..a9a96864a 100644
--- a/src/live_effects/lpe-fillet-chamfer.cpp
+++ b/src/live_effects/lpe-fillet-chamfer.cpp
@@ -16,7 +16,7 @@
#include "live_effects/lpe-fillet-chamfer.h"
#include <2geom/sbasis-to-bezier.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/line.h>
#include "desktop.h"
@@ -272,11 +272,11 @@ void LPEFilletChamfer::refreshKnots()
}
}
-void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power)
+void LPEFilletChamfer::doUpdateFillet(Geom::PathVector const &original_pathv, double power)
{
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
std::vector<Geom::Point> result;
- std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
+ Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
int counter = 0;
for (PathVector::const_iterator path_it = original_pathv_processed.begin();
path_it != original_pathv_processed.end(); ++path_it) {
@@ -320,11 +320,11 @@ void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pa
fillet_chamfer_values.param_set_and_write_new_value(result);
}
-void LPEFilletChamfer::doChangeType(std::vector<Geom::Path> const& original_pathv, int type)
+void LPEFilletChamfer::doChangeType(Geom::PathVector const &original_pathv, int type)
{
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
std::vector<Geom::Point> result;
- std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
+ Geom::PathVector original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
int counter = 0;
for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) {
int pathCounter = 0;
@@ -461,7 +461,7 @@ int LPEFilletChamfer::getKnotsNumber(SPCurve const *c)
{
int nKnots = c->nodes_in_path();
PathVector const pv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
- for (std::vector<Geom::Path>::const_iterator path_it = pv.begin();
+ for (Geom::PathVector::const_iterator path_it = pv.begin();
path_it != pv.end(); ++path_it) {
if (!(*path_it).closed()) {
nKnots--;
@@ -471,17 +471,17 @@ int LPEFilletChamfer::getKnotsNumber(SPCurve const *c)
}
void
-LPEFilletChamfer::adjustForNewPath(std::vector<Geom::Path> const &path_in)
+LPEFilletChamfer::adjustForNewPath(Geom::PathVector const &path_in)
{
if (!path_in.empty()) {
fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pathv_to_linear_and_cubic_beziers(path_in)[0].toPwSb());
}
}
-std::vector<Geom::Path>
-LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
+Geom::PathVector
+LPEFilletChamfer::doEffect_path(Geom::PathVector const &path_in)
{
- std::vector<Geom::Path> pathvector_out;
+ Geom::PathVector pathvector_out;
Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(pathv_to_linear_and_cubic_beziers(path_in));
pwd2_in = remove_short_cuts(pwd2_in, .01);
Piecewise<D2<SBasis> > der = derivative(pwd2_in);
@@ -490,7 +490,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
unsigned int counter = 0;
const double K = (4.0 / 3.0) * (sqrt(2.0) - 1.0);
- std::vector<Geom::Path> path_in_processed = pathv_to_linear_and_cubic_beziers(path_in);
+ Geom::PathVector path_in_processed = pathv_to_linear_and_cubic_beziers(path_in);
for (PathVector::const_iterator path_it = path_in_processed.begin();
path_it != path_in_processed.end(); ++path_it) {
if (path_it->empty())
@@ -543,7 +543,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
ray2.setPoints(endArcPoint, (*cubic2)[1]);
}
Point handle2 = endArcPoint - Point::polar(ray2.angle(),k2);
- bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0;
+ bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0;
double angle = angle_between(ray1, ray2, ccwToggle);
double handleAngle = ray1.angle() - angle;
if (ccwToggle) {
@@ -582,7 +582,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
Geom::Path path_chamfer;
path_chamfer.start(path_out.finalPoint());
if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- path_chamfer.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
} else {
path_chamfer.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
}
@@ -598,7 +598,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
path_chamfer.start(path_out.finalPoint());
if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
ccwToggle = ccwToggle?0:1;
- path_chamfer.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ path_chamfer.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
}else{
path_chamfer.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint);
}
@@ -611,13 +611,13 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
} else if (type == 2) {
if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
ccwToggle = ccwToggle?0:1;
- path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
}else{
path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint);
}
} else if (type == 1){
if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed) && method != FM_BEZIER )|| method == FM_ARC){
- path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ path_out.appendNew<EllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
} else {
path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
}
diff --git a/src/live_effects/lpe-fillet-chamfer.h b/src/live_effects/lpe-fillet-chamfer.h
index f8e03a399..290a37f92 100644
--- a/src/live_effects/lpe-fillet-chamfer.h
+++ b/src/live_effects/lpe-fillet-chamfer.h
@@ -37,11 +37,11 @@ public:
LPEFilletChamfer(LivePathEffectObject *lpeobject);
virtual ~LPEFilletChamfer();
- virtual std::vector<Geom::Path> doEffect_path(std::vector<Geom::Path> const &path_in);
+ virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in);
virtual void doOnApply(SPLPEItem const *lpeItem);
virtual void doBeforeEffect(SPLPEItem const *lpeItem);
- virtual void adjustForNewPath(std::vector<Geom::Path> const &path_in);
+ virtual void adjustForNewPath(Geom::PathVector const &path_in);
virtual Gtk::Widget* newWidget();
int getKnotsNumber(SPCurve const *c);
@@ -53,8 +53,8 @@ public:
void fillet();
void inverseFillet();
void updateFillet();
- void doUpdateFillet(std::vector<Geom::Path> const& original_pathv, double power);
- void doChangeType(std::vector<Geom::Path> const& original_pathv, int type);
+ void doUpdateFillet(Geom::PathVector const& original_pathv, double power);
+ void doChangeType(Geom::PathVector const& original_pathv, int type);
void refreshKnots();
FilletChamferPointArrayParam fillet_chamfer_values;
diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp
index fafe143b5..d4d695542 100644
--- a/src/live_effects/lpe-gears.cpp
+++ b/src/live_effects/lpe-gears.cpp
@@ -232,10 +232,10 @@ LPEGears::~LPEGears()
}
-std::vector<Geom::Path>
-LPEGears::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEGears::doEffect_path (Geom::PathVector const &path_in)
{
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
Geom::Path gearpath = path_in[0];
Geom::Path::iterator it(gearpath.begin());
diff --git a/src/live_effects/lpe-gears.h b/src/live_effects/lpe-gears.h
index bd5e4c4f9..5dd6dd239 100644
--- a/src/live_effects/lpe-gears.h
+++ b/src/live_effects/lpe-gears.h
@@ -22,7 +22,7 @@ public:
LPEGears(LivePathEffectObject *lpeobject);
virtual ~LPEGears();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path(Geom::PathVector const &path_in);
private:
ScalarParam teeth;
diff --git a/src/live_effects/lpe-interpolate.cpp b/src/live_effects/lpe-interpolate.cpp
index e41bc6f0e..b1ad07d23 100644
--- a/src/live_effects/lpe-interpolate.cpp
+++ b/src/live_effects/lpe-interpolate.cpp
@@ -61,7 +61,7 @@ LPEInterpolate::doEffect_path (Geom::PathVector const & path_in)
return path_in;
}
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_A = path_in[0].toPwSb();
Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_B = path_in[1].toPwSb();
diff --git a/src/live_effects/lpe-interpolate.h b/src/live_effects/lpe-interpolate.h
index 679001a81..ef8043e46 100644
--- a/src/live_effects/lpe-interpolate.h
+++ b/src/live_effects/lpe-interpolate.h
@@ -27,7 +27,7 @@ public:
LPEInterpolate(LivePathEffectObject *lpeobject);
virtual ~LPEInterpolate();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual void resetDefaults(SPItem const* item);
private:
diff --git a/src/live_effects/lpe-interpolate_points.h b/src/live_effects/lpe-interpolate_points.h
index 7a3364747..eb706a320 100644
--- a/src/live_effects/lpe-interpolate_points.h
+++ b/src/live_effects/lpe-interpolate_points.h
@@ -25,7 +25,7 @@ public:
LPEInterpolatePoints(LivePathEffectObject *lpeobject);
virtual ~LPEInterpolatePoints();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
EnumParam<unsigned> interpolator_type;
diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp
index c6a08d336..28f99eb94 100644
--- a/src/live_effects/lpe-jointype.cpp
+++ b/src/live_effects/lpe-jointype.cpp
@@ -20,7 +20,7 @@
#include "display/curve.h"
#include <2geom/path.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include "lpe-jointype.h"
@@ -151,7 +151,7 @@ void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem)
}
}
-std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in)
+Geom::PathVector LPEJoinType::doEffect_path(Geom::PathVector const & path_in)
{
Geom::PathVector ret;
for (size_t i = 0; i < path_in.size(); ++i) {
diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h
index bca0961c9..fddaeb619 100644
--- a/src/live_effects/lpe-jointype.h
+++ b/src/live_effects/lpe-jointype.h
@@ -24,7 +24,7 @@ public:
virtual void doOnApply(SPLPEItem const* lpeitem);
virtual void doOnRemove(SPLPEItem const* lpeitem);
- virtual std::vector <Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
LPEJoinType(const LPEJoinType&);
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp
index ec2f9c419..c3000fe0d 100644
--- a/src/live_effects/lpe-knot.cpp
+++ b/src/live_effects/lpe-knot.cpp
@@ -155,7 +155,7 @@ findShadowedTime(Geom::Path const &patha, std::vector<Geom::Point> const &pt_and
// -for each component, the time at which this crossing occurs + the order of this crossing along the component (when starting from 0).
namespace LPEKnotNS {//just in case...
-CrossingPoints::CrossingPoints(std::vector<Geom::Path> const &paths) : std::vector<CrossingPoint>(){
+CrossingPoints::CrossingPoints(Geom::PathVector const &paths) : std::vector<CrossingPoint>(){
// std::cout<<"\nCrossingPoints creation from path vector\n";
for( unsigned i=0; i<paths.size(); i++){
for( unsigned ii=0; ii < size_nondegenerate(paths[i]); ii++){
@@ -391,11 +391,11 @@ LPEKnot::updateSwitcher(){
}
}
-std::vector<Geom::Path>
-LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in)
+Geom::PathVector
+LPEKnot::doEffect_path (Geom::PathVector const &path_in)
{
using namespace Geom;
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
if (gpaths.size()==0){
return path_in;
@@ -433,7 +433,7 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in)
std::vector<Point> flag_j = gpaths[j][curveidx].pointAndDerivatives(t,1);
- int geom_sign = ( cross(flag_i[1],flag_j[1]) > 0 ? 1 : -1);
+ int geom_sign = ( cross(flag_i[1], flag_j[1]) < 0 ? 1 : -1);
bool i0_is_under = false;
if ( crossing_points[p].sign * geom_sign > 0 ){
@@ -486,8 +486,9 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in)
++beg_comp;
--end_comp;
Path first = gpaths[i0].portion(dom.back());
- //FIXME: STITCH_DISCONTINUOUS should not be necessary (?!?)
- first.append(gpaths[i0].portion(dom.front()), Path::STITCH_DISCONTINUOUS);
+ //FIXME: stitching should not be necessary (?!?)
+ first.setStitching(true);
+ first.append(gpaths[i0].portion(dom.front()));
path_out.push_back(first);
}
}
@@ -503,7 +504,7 @@ LPEKnot::doEffect_path (std::vector<Geom::Path> const &path_in)
//recursively collect gpaths and stroke widths (stolen from "sp-lpe_item.cpp").
static void
-collectPathsAndWidths (SPLPEItem const *lpeitem, std::vector<Geom::Path> &paths, std::vector<double> &stroke_widths){
+collectPathsAndWidths (SPLPEItem const *lpeitem, Geom::PathVector &paths, std::vector<double> &stroke_widths){
if (SP_IS_GROUP(lpeitem)) {
std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
for ( std::vector<SPItem*>::const_iterator iter = item_list.begin(); iter != item_list.end(); iter++) {
@@ -615,8 +616,7 @@ LPEKnot::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::Pat
svgd = "M 10,0 C 10,5.52 5.52,10 0,10 -5.52,10 -10,5.52 -10,0 c 0,-5.52 4.48,-10 10,-10 5.52,0 10,4.48 10,10 z";
}
PathVector pathv = sp_svg_read_pathv(svgd);
- pathv *= Affine(r,0,0,r,0,0);
- pathv += switcher;
+ pathv *= Affine(r,0,0,r,0,0) * Translate(switcher);
hp_vec.push_back(pathv);
}
diff --git a/src/live_effects/lpe-knot.h b/src/live_effects/lpe-knot.h
index 080f32de2..95bfaf6e1 100644
--- a/src/live_effects/lpe-knot.h
+++ b/src/live_effects/lpe-knot.h
@@ -42,8 +42,8 @@ struct CrossingPoint {
class CrossingPoints : public std::vector<CrossingPoint>{
public:
CrossingPoints() : std::vector<CrossingPoint>() {}
- CrossingPoints(Geom::CrossingSet const &cs, std::vector<Geom::Path> const &path);//for self crossings only!
- CrossingPoints(std::vector<Geom::Path> const &paths);
+ CrossingPoints(Geom::CrossingSet const &cs, Geom::PathVector const &path);//for self crossings only!
+ CrossingPoints(Geom::PathVector const &paths);
CrossingPoints(std::vector<double> const &input);
std::vector<double> to_vector();
CrossingPoint get(unsigned const i, unsigned const ni);
@@ -57,7 +57,7 @@ public:
virtual ~LPEKnot();
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & input_path);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & input_path);
/* the knotholder entity classes must be declared friends */
friend class KnotHolderEntityCrossingSwitcher;
@@ -65,7 +65,7 @@ public:
protected:
virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
- std::vector<Geom::Path> supplied_path; //for knotholder business
+ Geom::PathVector supplied_path; //for knotholder business
private:
void updateSwitcher();
@@ -79,7 +79,7 @@ private:
LPEKnotNS::CrossingPoints crossing_points;//topology representation of the knot.
- std::vector<Geom::Path> gpaths;//the collection of all the paths in the object or group.
+ Geom::PathVector gpaths;//the collection of all the paths in the object or group.
std::vector<double> gstroke_widths;//the collection of all the stroke widths in the object or group.
//UI: please, someone, help me to improve this!!
diff --git a/src/live_effects/lpe-lattice2.cpp b/src/live_effects/lpe-lattice2.cpp
index 02bda3af5..8c7f46cbd 100644
--- a/src/live_effects/lpe-lattice2.cpp
+++ b/src/live_effects/lpe-lattice2.cpp
@@ -291,7 +291,7 @@ LPELattice2::vertical(PointParam &param_one, PointParam &param_two, Geom::Line v
double Y = (A[Geom::Y] + B[Geom::Y])/2;
A[Geom::Y] = Y;
B[Geom::Y] = Y;
- Geom::Point nearest = vert.pointAt(vert.nearestPoint(A));
+ Geom::Point nearest = vert.pointAt(vert.nearestTime(A));
double distance_one = Geom::distance(A,nearest);
double distance_two = Geom::distance(B,nearest);
double distance_middle = (distance_one + distance_two)/2;
@@ -312,7 +312,7 @@ LPELattice2::horizontal(PointParam &param_one, PointParam &param_two, Geom::Line
double X = (A[Geom::X] + B[Geom::X])/2;
A[Geom::X] = X;
B[Geom::X] = X;
- Geom::Point nearest = horiz.pointAt(horiz.nearestPoint(A));
+ Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A));
double distance_one = Geom::distance(A,nearest);
double distance_two = Geom::distance(B,nearest);
double distance_middle = (distance_one + distance_two)/2;
diff --git a/src/live_effects/lpe-line_segment.cpp b/src/live_effects/lpe-line_segment.cpp
index 78d286143..dfd8aea8f 100644
--- a/src/live_effects/lpe-line_segment.cpp
+++ b/src/live_effects/lpe-line_segment.cpp
@@ -48,16 +48,16 @@ LPELineSegment::doBeforeEffect (SPLPEItem const* lpeitem)
Inkscape::UI::Tools::lpetool_get_limiting_bbox_corners(lpeitem->document, bboxA, bboxB);
}
-std::vector<Geom::Path>
-LPELineSegment::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPELineSegment::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> output;
+ Geom::PathVector output;
- A = initialPoint(path_in);
- B = finalPoint(path_in);
+ A = path_in.initialPoint();
+ B = path_in.finalPoint();
Geom::Rect dummyRect(bboxA, bboxB);
- boost::optional<Geom::LineSegment> intersection_segment = Geom::rect_line_intersect(dummyRect, Geom::Line(A, B));
+ boost::optional<Geom::LineSegment> intersection_segment = Geom::Line(A, B).clip(dummyRect);
if (!intersection_segment) {
g_print ("Possible error - no intersection with limiting bounding box.\n");
@@ -65,11 +65,11 @@ LPELineSegment::doEffect_path (std::vector<Geom::Path> const & path_in)
}
if (end_type == END_OPEN_INITIAL || end_type == END_OPEN_BOTH) {
- A = (*intersection_segment).initialPoint();
+ A = intersection_segment->initialPoint();
}
if (end_type == END_OPEN_FINAL || end_type == END_OPEN_BOTH) {
- B = (*intersection_segment).finalPoint();
+ B = intersection_segment->finalPoint();
}
Geom::Path path(A);
diff --git a/src/live_effects/lpe-line_segment.h b/src/live_effects/lpe-line_segment.h
index 1a57688b6..def828fe2 100644
--- a/src/live_effects/lpe-line_segment.h
+++ b/src/live_effects/lpe-line_segment.h
@@ -34,7 +34,7 @@ public:
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
//private:
EnumParam<EndType> end_type;
diff --git a/src/live_effects/lpe-mirror_symmetry.cpp b/src/live_effects/lpe-mirror_symmetry.cpp
index 0bb67a4a2..783053df2 100644
--- a/src/live_effects/lpe-mirror_symmetry.cpp
+++ b/src/live_effects/lpe-mirror_symmetry.cpp
@@ -64,24 +64,24 @@ LPEMirrorSymmetry::doOnApply (SPLPEItem const* lpeitem)
Point B(bbox.left(), bbox.top());
A *= t;
B *= t;
- Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(A[X], B[X]), Linear(A[Y], B[Y])));
+ Piecewise<D2<SBasis> > rline = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(A[X], B[X]), SBasis(A[Y], B[Y])));
reflection_line.set_new_value(rline, true);
}
-std::vector<Geom::Path>
-LPEMirrorSymmetry::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEMirrorSymmetry::doEffect_path (Geom::PathVector const & path_in)
{
// Don't allow empty path parameter:
if ( reflection_line.get_pathvector().empty() ) {
return path_in;
}
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
if (!discard_orig_path) {
path_out = path_in;
}
- std::vector<Geom::Path> mline(reflection_line.get_pathvector());
+ Geom::PathVector mline(reflection_line.get_pathvector());
Geom::Point A(mline.front().initialPoint());
Geom::Point B(mline.back().finalPoint());
diff --git a/src/live_effects/lpe-mirror_symmetry.h b/src/live_effects/lpe-mirror_symmetry.h
index a4a2b86c0..7a484a473 100644
--- a/src/live_effects/lpe-mirror_symmetry.h
+++ b/src/live_effects/lpe-mirror_symmetry.h
@@ -32,7 +32,7 @@ public:
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
BoolParam discard_orig_path;
diff --git a/src/live_effects/lpe-offset.cpp b/src/live_effects/lpe-offset.cpp
index ba7179476..dd7af92a2 100644
--- a/src/live_effects/lpe-offset.cpp
+++ b/src/live_effects/lpe-offset.cpp
@@ -20,7 +20,7 @@
#include <2geom/path.h>
#include <2geom/piecewise.h>
#include <2geom/sbasis-geometric.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/transforms.h>
namespace Inkscape {
@@ -52,7 +52,7 @@ static void append_half_circle(Geom::Piecewise<Geom::D2<Geom::SBasis> > &pwd2,
using namespace Geom;
double r = L2(dir);
- SVGEllipticalArc cap(center + dir, r, r, angle_between(Point(1,0), dir), false, false, center - dir);
+ EllipticalArc cap(center + dir, r, r, angle_between(Point(1,0), dir), false, false, center - dir);
Piecewise<D2<SBasis> > cap_pwd2(cap.toSBasis());
pwd2.continuousConcat(cap_pwd2);
}
@@ -72,7 +72,7 @@ LPEOffset::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_
Piecewise<D2<SBasis> > output;
- double t = nearest_point(offset_pt, pwd2_in);
+ double t = nearest_time(offset_pt, pwd2_in);
Point A = pwd2_in.valueAt(t);
double offset = L2(A - offset_pt);
diff --git a/src/live_effects/lpe-parallel.cpp b/src/live_effects/lpe-parallel.cpp
index aa7405607..23cd5e2e7 100644
--- a/src/live_effects/lpe-parallel.cpp
+++ b/src/live_effects/lpe-parallel.cpp
@@ -91,7 +91,7 @@ LPEParallel::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd
C = offset_pt - dir * length_left;
D = offset_pt + dir * length_right;
- output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y])));
+ output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y])));
return output + dir;
}
diff --git a/src/live_effects/lpe-perp_bisector.cpp b/src/live_effects/lpe-perp_bisector.cpp
index feed55b26..660318c57 100644
--- a/src/live_effects/lpe-perp_bisector.cpp
+++ b/src/live_effects/lpe-perp_bisector.cpp
@@ -67,7 +67,7 @@ KnotHolderEntityEnd::bisector_end_set(Geom::Point const &p, guint state, bool le
Geom::Point const s = snap_knot_position(p, state);
- double lambda = Geom::nearest_point(s, lpe->M, lpe->perp_dir);
+ double lambda = Geom::nearest_time(s, lpe->M, lpe->perp_dir);
if (left) {
lpe->C = lpe->M + lpe->perp_dir * lambda;
lpe->length_left.param_set_value(lambda);
@@ -146,7 +146,7 @@ LPEPerpBisector::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &
C = M + perp_dir * length_left;
D = M - perp_dir * length_right;
- output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y])));
+ output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y])));
return output;
}
diff --git a/src/live_effects/lpe-perspective-envelope.cpp b/src/live_effects/lpe-perspective-envelope.cpp
index 08100bb2d..72c1b0e99 100644
--- a/src/live_effects/lpe-perspective-envelope.cpp
+++ b/src/live_effects/lpe-perspective-envelope.cpp
@@ -323,7 +323,7 @@ LPEPerspectiveEnvelope::vertical(PointParam &param_one, PointParam &param_two, G
double Y = (A[Geom::Y] + B[Geom::Y])/2;
A[Geom::Y] = Y;
B[Geom::Y] = Y;
- Geom::Point nearest = vert.pointAt(vert.nearestPoint(A));
+ Geom::Point nearest = vert.pointAt(vert.nearestTime(A));
double distance_one = Geom::distance(A,nearest);
double distance_two = Geom::distance(B,nearest);
double distance_middle = (distance_one + distance_two)/2;
@@ -344,7 +344,7 @@ LPEPerspectiveEnvelope::horizontal(PointParam &param_one, PointParam &param_two,
double X = (A[Geom::X] + B[Geom::X])/2;
A[Geom::X] = X;
B[Geom::X] = X;
- Geom::Point nearest = horiz.pointAt(horiz.nearestPoint(A));
+ Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A));
double distance_one = Geom::distance(A,nearest);
double distance_two = Geom::distance(B,nearest);
double distance_middle = (distance_one + distance_two)/2;
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index 5d9d224e8..6ce912bcb 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -27,12 +27,13 @@
#include <2geom/sbasis-geometric.h>
#include <2geom/transforms.h>
#include <2geom/bezier-utils.h>
-#include <2geom/svg-elliptical-arc.h>
+#include <2geom/elliptical-arc.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/path-sink.h>
#include <2geom/path-intersection.h>
#include <2geom/crossing.h>
#include <2geom/ellipse.h>
+#include <2geom/circle.h>
#include <2geom/math-utils.h>
#include <math.h>
@@ -46,9 +47,9 @@ namespace Geom {
static boost::optional<Point> intersection_point( Point const & origin_a, Point const & vector_a,
Point const & origin_b, Point const & vector_b)
{
- Coord denom = cross(vector_b, vector_a);
+ Coord denom = cross(vector_a, vector_b);
if (!are_near(denom,0.)){
- Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom;
+ Coord t = (cross(origin_b, vector_a) + cross(origin_b, vector_b)) / denom;
return origin_a + t * vector_a;
}
return boost::none;
@@ -94,71 +95,6 @@ static Ellipse find_ellipse(Point P, Point Q, Point O)
}
/**
- * Refer to: Weisstein, Eric W. "Circle-Circle Intersection."
- From MathWorld--A Wolfram Web Resource.
- http://mathworld.wolfram.com/Circle-CircleIntersection.html
- *
- * @return 0 if no intersection
- * @return 1 if one circle is contained in the other
- * @return 2 if intersections are found (they are written to p0 and p1)
- */
-static int circle_circle_intersection(Circle const &circle0, Circle const &circle1,
- Point & p0, Point & p1)
-{
- Point X0 = circle0.center();
- double r0 = circle0.ray();
- Point X1 = circle1.center();
- double r1 = circle1.ray();
-
- /* dx and dy are the vertical and horizontal distances between
- * the circle centers.
- */
- Point D = X1 - X0;
-
- /* Determine the straight-line distance between the centers. */
- double d = L2(D);
-
- /* Check for solvability. */
- if (d > (r0 + r1))
- {
- /* no solution. circles do not intersect. */
- return 0;
- }
- if (d <= fabs(r0 - r1))
- {
- /* no solution. one circle is contained in the other */
- return 1;
- }
-
- /* 'point 2' is the point where the line through the circle
- * intersection points crosses the line between the circle
- * centers.
- */
-
- /* Determine the distance from point 0 to point 2. */
- double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
-
- /* Determine the coordinates of point 2. */
- Point p2 = X0 + D * (a/d);
-
- /* Determine the distance from point 2 to either of the
- * intersection points.
- */
- double h = std::sqrt((r0*r0) - (a*a));
-
- /* Now determine the offsets of the intersection points from
- * point 2.
- */
- Point r = (h/d)*rot90(D);
-
- /* Determine the absolute intersection points. */
- p0 = p2 + r;
- p1 = p2 - r;
-
- return 2;
-}
-
-/**
* Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t.
* Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt).
*/
@@ -313,7 +249,7 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem)
Geom::Path const &path = pathv.front();
Geom::Path::size_type const size = path.size_default();
if (!path.closed()) {
- points.push_back( Geom::Point(0.2,width) );
+ points.push_back( Geom::Point(0.2,width) );
}
points.push_back( Geom::Point(0.5*size,width) );
if (!path.closed()) {
@@ -362,7 +298,7 @@ void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem)
}
void
-LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in)
+LPEPowerStroke::adjustForNewPath(Geom::PathVector const & path_in)
{
if (!path_in.empty()) {
offset_points.recalculate_controlpoints_for_new_pwd2(path_in[0].toPwSb());
@@ -387,6 +323,8 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
return pb.peek().front();
}
+ pb.setStitching(true);
+
Geom::Point start = B[0].at0();
pb.moveTo(start);
build_from_sbasis(pb, B[0], tol, false);
@@ -446,7 +384,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
break;
}
- pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rot_angle(),
+ pb.arcTo( ellipse.ray(Geom::X), ellipse.ray(Geom::Y), ellipse.rotationAngle(),
false, width < 0, B[i].at0() );
break;
@@ -482,24 +420,24 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
// Extrapolate using the curvature at the end of the path segments to join
Geom::Circle circle1 = Geom::touching_circle(reverse(B[prev_i]), 0.0);
Geom::Circle circle2 = Geom::touching_circle(B[i], 0.0);
- Geom::Point points[2];
- int solutions = circle_circle_intersection(circle1, circle2, points[0], points[1]);
- if (solutions == 2) {
+ std::vector<Geom::ShapeIntersection> solutions;
+ solutions = circle1.intersect(circle2);
+ if (solutions.size() == 2) {
Geom::Point sol(0.,0.);
- if ( dot(tang2,points[0]-B[i].at0()) > 0 ) {
+ if ( dot(tang2, solutions[0].point() - B[i].at0()) > 0 ) {
// points[0] is bad, choose points[1]
- sol = points[1];
- } else if ( dot(tang2,points[1]-B[i].at0()) > 0 ) { // points[0] could be good, now check points[1]
+ sol = solutions[1].point();
+ } else if ( dot(tang2, solutions[1].point() - B[i].at0()) > 0 ) { // points[0] could be good, now check points[1]
// points[1] is bad, choose points[0]
- sol = points[0];
+ sol = solutions[0].point();
} else {
// both points are good, choose nearest
- sol = ( distanceSq(B[i].at0(), points[0]) < distanceSq(B[i].at0(), points[1]) ) ?
- points[0] : points[1];
+ sol = ( distanceSq(B[i].at0(), solutions[0].point()) < distanceSq(B[i].at0(), solutions[1].point()) ) ?
+ solutions[0].point() : solutions[1].point();
}
- Geom::EllipticalArc *arc0 = circle1.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol, true);
- Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0(), true);
+ Geom::EllipticalArc *arc0 = circle1.arc(B[prev_i].at1(), 0.5*(B[prev_i].at1()+sol), sol);
+ Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+B[i].at0()), B[i].at0());
if (arc0) {
build_from_sbasis(pb,arc0->toSBasis(), tol, false);
@@ -574,7 +512,7 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
Geom::Path spiro;
Spiro::spiro_run(controlpoints, 4, spiro);
- pb.append(spiro.portion(1,spiro.size_open()-1), Geom::Path::STITCH_DISCONTINUOUS);
+ pb.append(spiro.portion(1, spiro.size_open() - 1));
break;
}
case LINEJOIN_BEVEL:
@@ -593,15 +531,15 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
if (cross.size() != 1) {
// empty crossing or too many crossings: default to bevel
pb.lineTo(B[i].at0());
- pb.append(bzr2, Geom::Path::STITCH_DISCONTINUOUS);
+ pb.append(bzr2);
} else {
// :-) quick hack:
for (unsigned i=0; i < bzr1.size_open(); ++i) {
pb.backspace();
}
- pb.append( bzr1.portion(0, cross[0].ta), Geom::Path::STITCH_DISCONTINUOUS );
- pb.append( bzr2.portion(cross[0].tb, bzr2.size_open()), Geom::Path::STITCH_DISCONTINUOUS );
+ pb.append( bzr1.portion(0, cross[0].ta) );
+ pb.append( bzr2.portion(cross[0].tb, bzr2.size_open()) );
}
}
} else {
@@ -615,12 +553,12 @@ static Geom::Path path_from_piecewise_fix_cusps( Geom::Piecewise<Geom::D2<Geom::
}
-std::vector<Geom::Path>
-LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEPowerStroke::doEffect_path (Geom::PathVector const & path_in)
{
using namespace Geom;
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
if (path_in.empty()) {
return path_out;
}
@@ -737,12 +675,12 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
default:
{
double radius1 = 0.5 * distance(pwd2_out.lastValue(), mirrorpath.firstValue());
- fixed_path.appendNew<SVGEllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() );
+ fixed_path.appendNew<EllipticalArc>( radius1, radius1, M_PI/2., false, y.lastValue() < 0, mirrorpath.firstValue() );
break;
}
}
- fixed_path.append(fixed_mirrorpath, Geom::Path::STITCH_DISCONTINUOUS);
+ fixed_path.append(fixed_mirrorpath);
switch (start_linecap) {
case LINECAP_ZERO_WIDTH:
@@ -775,7 +713,7 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
default:
{
double radius2 = 0.5 * distance(pwd2_out.firstValue(), mirrorpath.lastValue());
- fixed_path.appendNew<SVGEllipticalArc>( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() );
+ fixed_path.appendNew<EllipticalArc>( radius2, radius2, M_PI/2., false, y.firstValue() < 0, pwd2_out.firstValue() );
break;
}
}
diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h
index a773434aa..135d39274 100644
--- a/src/live_effects/lpe-powerstroke.h
+++ b/src/live_effects/lpe-powerstroke.h
@@ -26,13 +26,13 @@ public:
virtual ~LPEPowerStroke();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual void doOnApply(SPLPEItem const* lpeitem);
virtual void doOnRemove(SPLPEItem const* lpeitem);
// methods called by path-manipulator upon edits
- void adjustForNewPath(std::vector<Geom::Path> const & path_in);
+ void adjustForNewPath(Geom::PathVector const & path_in);
PowerStrokePointArrayParam offset_points;
diff --git a/src/live_effects/lpe-rough-hatches.cpp b/src/live_effects/lpe-rough-hatches.cpp
index 455653e46..76421e0f0 100644
--- a/src/live_effects/lpe-rough-hatches.cpp
+++ b/src/live_effects/lpe-rough-hatches.cpp
@@ -529,7 +529,8 @@ LPERoughHatches::smoothSnake(std::vector<std::vector<Point> > const &linearSnake
}
if ( fat_output.get_value() ){
res_comp = res_comp_bot;
- res_comp.append(res_comp_top.reverse(),Geom::Path::STITCH_DISCONTINUOUS);
+ res_comp.setStitching(true);
+ res_comp.append(res_comp_top.reversed());
}
result.concat(res_comp.toPwSb());
}
diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp
index c5e002351..33ffd96d6 100644
--- a/src/live_effects/lpe-roughen.cpp
+++ b/src/live_effects/lpe-roughen.cpp
@@ -271,8 +271,8 @@ SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t)
}
if (cubic) {
std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
- std::vector<Geom::Point> seg1 = div.first.points(),
- seg2 = div.second.points();
+ std::vector<Geom::Point> seg1 = div.first.controlPoints(),
+ seg2 = div.second.controlPoints();
out->moveto(seg1[0]);
out->curveto(seg1[1] + point1, seg1[2] + point2, seg1[3] + point3);
out->curveto(seg2[1] + point_b1, seg2[2], seg2[3]);
diff --git a/src/live_effects/lpe-ruler.cpp b/src/live_effects/lpe-ruler.cpp
index fd611c78d..49b5faa2e 100644
--- a/src/live_effects/lpe-ruler.cpp
+++ b/src/live_effects/lpe-ruler.cpp
@@ -111,7 +111,7 @@ LPERuler::ruler_mark(Geom::Point const &A, Geom::Point const &n, MarkType const
break;
}
- Piecewise<D2<SBasis> > seg(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y])));
+ Piecewise<D2<SBasis> > seg(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y])));
return seg;
}
diff --git a/src/live_effects/lpe-show_handles.cpp b/src/live_effects/lpe-show_handles.cpp
index 2638f312e..0bc1c4f17 100644
--- a/src/live_effects/lpe-show_handles.cpp
+++ b/src/live_effects/lpe-show_handles.cpp
@@ -79,9 +79,9 @@ void LPEShowHandles::doBeforeEffect (SPLPEItem const* lpeitem)
stroke_width = item->style->stroke_width.computed;
}
-std::vector<Geom::Path> LPEShowHandles::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector LPEShowHandles::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(path_in);
if(original_path) {
for (unsigned int i=0; i < path_in.size(); i++) {
@@ -112,14 +112,14 @@ LPEShowHandles::generateHelperPath(Geom::PathVector result)
continue;
}
//Itreadores
- Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
- Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
+ Geom::Path::iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::iterator curve_it2 = ++(path_it->begin()); // outgoing curve
+ Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
if (path_it->closed()) {
// if the path is closed, maybe we have to stop a bit earlier because the
// closing line segment has zerolength.
- const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type
+ Geom::Curve const &closingline = path_it->back_closed(); // the closing line segment is always of type
// Geom::LineSegment.
if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
// closingline.isDegenerate() did not work, because it only checks for
@@ -165,9 +165,7 @@ LPEShowHandles::drawNode(Geom::Point p)
char const * svgd;
svgd = "M 0.05,0 A 0.05,0.05 0 0 1 0,0.05 0.05,0.05 0 0 1 -0.05,0 0.05,0.05 0 0 1 0,-0.05 0.05,0.05 0 0 1 0.05,0 Z M -0.5,-0.5 0.5,-0.5 0.5,0.5 -0.5,0.5 Z";
Geom::PathVector pathv = sp_svg_read_pathv(svgd);
- pathv *= Geom::Rotate::from_degrees(rotate_nodes);
- pathv *= Geom::Scale (diameter);
- pathv += p;
+ pathv *= Geom::Rotate::from_degrees(rotate_nodes) * Geom::Scale(diameter) * Geom::Translate(p);
outline_path.push_back(pathv[0]);
outline_path.push_back(pathv[1]);
}
@@ -181,8 +179,7 @@ LPEShowHandles::drawHandle(Geom::Point p)
char const * svgd;
svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z";
Geom::PathVector pathv = sp_svg_read_pathv(svgd);
- pathv *= Geom::Scale (diameter);
- pathv += p-Geom::Point(diameter * 0.35,diameter * 0.35);
+ pathv *= Geom::Scale (diameter) * Geom::Translate(p - Geom::Point(diameter * 0.35,diameter * 0.35));
outline_path.push_back(pathv[0]);
}
}
diff --git a/src/live_effects/lpe-show_handles.h b/src/live_effects/lpe-show_handles.h
index 77b28e77a..34390dd32 100644
--- a/src/live_effects/lpe-show_handles.h
+++ b/src/live_effects/lpe-show_handles.h
@@ -36,7 +36,7 @@ public:
protected:
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
private:
diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp
index 7fc20ede1..265192a17 100644
--- a/src/live_effects/lpe-simplify.cpp
+++ b/src/live_effects/lpe-simplify.cpp
@@ -164,14 +164,13 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
Geom::PathVector tmp_path;
Geom::CubicBezier const *cubic = NULL;
for (Geom::PathVector::iterator path_it = result.begin(); path_it != result.end(); ++path_it) {
- //Si estĂĄ vacĂ­o...
if (path_it->empty()) {
continue;
}
- //Itreadores
- Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
- Geom::Path::const_iterator curve_it2 = ++(path_it->begin());// outgoing curve
- Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
+
+ Geom::Path::iterator curve_it1 = path_it->begin(); // incoming curve
+ Geom::Path::iterator curve_it2 = ++(path_it->begin());// outgoing curve
+ Geom::Path::iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
SPCurve *nCurve = new SPCurve();
if (path_it->closed()) {
// if the path is closed, maybe we have to stop a bit earlier because the
@@ -264,8 +263,7 @@ LPESimplify::drawNode(Geom::Point p)
char const * svgd;
svgd = "M 0.55,0.5 A 0.05,0.05 0 0 1 0.5,0.55 0.05,0.05 0 0 1 0.45,0.5 0.05,0.05 0 0 1 0.5,0.45 0.05,0.05 0 0 1 0.55,0.5 Z M 0,0 1,0 1,1 0,1 Z";
Geom::PathVector pathv = sp_svg_read_pathv(svgd);
- pathv *= Geom::Affine(r,0,0,r,0,0);
- pathv += p - Geom::Point(0.5*r,0.5*r);
+ pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.5*r,0.5*r));
hp.push_back(pathv[0]);
hp.push_back(pathv[1]);
}
@@ -277,8 +275,7 @@ LPESimplify::drawHandle(Geom::Point p)
char const * svgd;
svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z";
Geom::PathVector pathv = sp_svg_read_pathv(svgd);
- pathv *= Geom::Affine(r,0,0,r,0,0);
- pathv += p - Geom::Point(0.35*r,0.35*r);
+ pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.35*r,0.35*r));
hp.push_back(pathv[0]);
}
diff --git a/src/live_effects/lpe-skeleton.cpp b/src/live_effects/lpe-skeleton.cpp
index c61e9773f..6e4afbe9b 100644
--- a/src/live_effects/lpe-skeleton.cpp
+++ b/src/live_effects/lpe-skeleton.cpp
@@ -62,10 +62,10 @@ LPESkeleton::doEffect (SPCurve * curve)
// spice this up to make the effect actually *do* something!
}
-std::vector<Geom::Path>
-LPESkeleton::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPESkeleton::doEffect_path (Geom::PathVector const & path_in)
{
- std::vector<Geom::Path> path_out;
+ Geom::PathVector path_out;
path_out = path_in; // spice this up to make the effect actually *do* something!
diff --git a/src/live_effects/lpe-skeleton.h b/src/live_effects/lpe-skeleton.h
index 124d1a4cb..3b45b6978 100644
--- a/src/live_effects/lpe-skeleton.h
+++ b/src/live_effects/lpe-skeleton.h
@@ -36,8 +36,8 @@ public:
// Choose to implement one of the doEffect functions. You can delete or comment out the others.
// virtual void doEffect (SPCurve * curve);
-// virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
- virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+// virtual Geom::PathVector doEffect_path (Geom::PathVector const &path_in);
+ virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in);
/* the knotholder entity classes (if any) can be declared friends */
//friend class Skeleton::KnotHolderEntityMyHandle;
diff --git a/src/live_effects/lpe-sketch.cpp b/src/live_effects/lpe-sketch.cpp
index 551dbe16a..82d343f6e 100644
--- a/src/live_effects/lpe-sketch.cpp
+++ b/src/live_effects/lpe-sketch.cpp
@@ -346,7 +346,7 @@ LPESketch::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_
//TODO: put this 4 as a parameter in the UI...
//TODO: what if with v=0?
double l = tgtlength*(1-tgtlength_rdm)/v_t.length();
- double r = std::pow(v_t.length(),3)/cross(a_t,v_t);
+ double r = std::pow(v_t.length(), 3) / cross(v_t, a_t);
r = sqrt((2*fabs(r)-tgtscale)*tgtscale)/v_t.length();
l=(r<l)?r:l;
//collect the tgt segment into output.
diff --git a/src/live_effects/lpe-spiro.cpp b/src/live_effects/lpe-spiro.cpp
index 8b4274ab2..eefd25c7d 100644
--- a/src/live_effects/lpe-spiro.cpp
+++ b/src/live_effects/lpe-spiro.cpp
@@ -10,8 +10,7 @@
#include <typeinfo>
#include <2geom/pathvector.h>
#include <2geom/affine.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom-nodetype.h"
#include "helper/geom-curves.h"
diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp
index bce4876af..978ab57fb 100644
--- a/src/live_effects/lpe-tangent_to_curve.cpp
+++ b/src/live_effects/lpe-tangent_to_curve.cpp
@@ -90,7 +90,7 @@ LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const
C = ptA - derivA * length_left;
D = ptA + derivA * length_right;
- output = Piecewise<D2<SBasis> >(D2<SBasis>(Linear(C[X], D[X]), Linear(C[Y], D[Y])));
+ output = Piecewise<D2<SBasis> >(D2<SBasis>(SBasis(C[X], D[X]), SBasis(C[Y], D[Y])));
return output;
}
@@ -135,7 +135,7 @@ KnotHolderEntityAttachPt::knot_set(Geom::Point const &p, Geom::Point const &/*or
}
Piecewise<D2<SBasis> > pwd2 = paths_to_pw( lpe->pathvector_before_effect );
- double t0 = nearest_point(s, pwd2);
+ double t0 = nearest_time(s, pwd2);
lpe->t_attach.param_set_value(t0);
// FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
@@ -149,7 +149,7 @@ KnotHolderEntityLeftEnd::knot_set(Geom::Point const &p, Geom::Point const &/*ori
Geom::Point const s = snap_knot_position(p, state);
- double lambda = Geom::nearest_point(s, lpe->ptA, lpe->derivA);
+ double lambda = Geom::nearest_time(s, lpe->ptA, lpe->derivA);
lpe->length_left.param_set_value(-lambda);
sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
@@ -162,7 +162,7 @@ KnotHolderEntityRightEnd::knot_set(Geom::Point const &p, Geom::Point const &/*or
Geom::Point const s = snap_knot_position(p, state);
- double lambda = Geom::nearest_point(s, lpe->ptA, lpe->derivA);
+ double lambda = Geom::nearest_time(s, lpe->ptA, lpe->derivA);
lpe->length_right.param_set_value(lambda);
sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index 5765febbd..ef616f802 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -14,7 +14,6 @@
#include "live_effects/lpe-taperstroke.h"
#include <2geom/path.h>
-#include <2geom/shape.h>
#include <2geom/path.h>
#include <2geom/circle.h>
#include <2geom/sbasis-to-bezier.h>
@@ -194,7 +193,7 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path
Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
{
Geom::Path first_cusp = return_at_first_cusp(path_in[0]);
- Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse());
+ Geom::Path last_cusp = return_at_first_cusp(path_in[0].reversed());
bool zeroStart = false; // [distance from start taper knot -> start of path] == 0
bool zeroEnd = false; // [distance from end taper knot -> end of path] == 0
@@ -318,7 +317,7 @@ Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
if (!metInMiddle) {
// append the inside outline of the path (against direction)
- throwaway_path = half_outline(pathv_out[1].reverse(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
+ throwaway_path = half_outline(pathv_out[1].reversed(), fabs(line_width)/2., miter_limit, static_cast<LineJoinType>(join_type.get_value()));
if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
@@ -483,7 +482,7 @@ void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point con
Geom::Path p_in = return_at_first_cusp(pathv[0]);
pwd2.concat(p_in.toPwSb());
- double t0 = nearest_point(s, pwd2);
+ double t0 = nearest_time(s, pwd2);
lpe->attach_start.param_set_value(t0);
// FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
@@ -508,10 +507,10 @@ void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const
return;
}
Geom::PathVector pathv = lpe->pathvector_before_effect;
- Geom::Path p_in = return_at_first_cusp(pathv[0].reverse());
+ Geom::Path p_in = return_at_first_cusp(pathv[0].reversed());
Piecewise<D2<SBasis> > pwd2 = p_in.toPwSb();
- double t0 = nearest_point(s, pwd2);
+ double t0 = nearest_time(s, pwd2);
lpe->attach_end.param_set_value(t0);
sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp
index c6787aae1..2bcd4c136 100644
--- a/src/live_effects/lpe-test-doEffect-stack.cpp
+++ b/src/live_effects/lpe-test-doEffect-stack.cpp
@@ -47,14 +47,14 @@ LPEdoEffectStackTest::doEffect (SPCurve * curve)
}
}
-std::vector<Geom::Path>
-LPEdoEffectStackTest::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEdoEffectStackTest::doEffect_path (Geom::PathVector const &path_in)
{
if (step >= 2) {
return Effect::doEffect_path(path_in);
} else {
// return here
- std::vector<Geom::Path> path_out = path_in;
+ Geom::PathVector path_out = path_in;
return path_out;
}
}
diff --git a/src/live_effects/lpe-test-doEffect-stack.h b/src/live_effects/lpe-test-doEffect-stack.h
index fa3ee09be..208a345c6 100644
--- a/src/live_effects/lpe-test-doEffect-stack.h
+++ b/src/live_effects/lpe-test-doEffect-stack.h
@@ -27,7 +27,7 @@ public:
virtual ~LPEdoEffectStackTest();
virtual void doEffect (SPCurve * curve);
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
private:
diff --git a/src/live_effects/lpe-vonkoch.cpp b/src/live_effects/lpe-vonkoch.cpp
index b52816c87..7b424e019 100644
--- a/src/live_effects/lpe-vonkoch.cpp
+++ b/src/live_effects/lpe-vonkoch.cpp
@@ -76,12 +76,12 @@ LPEVonKoch::~LPEVonKoch()
}
-std::vector<Geom::Path>
-LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in)
+Geom::PathVector
+LPEVonKoch::doEffect_path (Geom::PathVector const & path_in)
{
using namespace Geom;
- std::vector<Geom::Path> generating_path = generator.get_pathvector();
+ Geom::PathVector generating_path = generator.get_pathvector();
if (generating_path.empty()) {
return path_in;
@@ -148,15 +148,15 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in)
}
//Generate path:
- std::vector<Geom::Path> pathi = path_in;
- std::vector<Geom::Path> path_out = path_in;
+ Geom::PathVector pathi = path_in;
+ Geom::PathVector path_out = path_in;
for (unsigned i = 0; i<nbgenerations; i++){
if (drawall.get_value()){
path_out = path_in;
complexity = path_in_complexity;
}else{
- path_out = std::vector<Geom::Path>();
+ path_out = Geom::PathVector();
complexity = 0;
}
for (unsigned j = 0; j<transforms.size(); j++){
@@ -203,7 +203,7 @@ LPEVonKoch::doEffect_path (std::vector<Geom::Path> const & path_in)
hp_vec.push_back(refbox_as_vect);
}
//Draw the transformed boxes
- std::vector<Geom::Path> generating_path = generator.get_pathvector();
+ Geom::PathVector generating_path = generator.get_pathvector();
for (unsigned i=0;i<generating_path.size(); i++){
if (generating_path[i].size()==0){
//Ooops! this should not happen.
@@ -243,7 +243,7 @@ LPEVonKoch::doBeforeEffect (SPLPEItem const* lpeitem)
using namespace Geom;
original_bbox(lpeitem);
- std::vector<Geom::Path> paths = ref_path.get_pathvector();
+ Geom::PathVector paths = ref_path.get_pathvector();
Geom::Point A,B;
if (paths.empty()||paths.front().size()==0){
//FIXME: a path is used as ref instead of 2 points to work around path/point param incompatibility bug.
@@ -258,7 +258,7 @@ LPEVonKoch::doBeforeEffect (SPLPEItem const* lpeitem)
if (paths.size()!=1||paths.front().size()!=1){
Geom::Path tmp_path(A);
tmp_path.appendNew<LineSegment>(B);
- std::vector<Geom::Path> tmp_pathv;
+ Geom::PathVector tmp_pathv;
tmp_pathv.push_back(tmp_path);
ref_path.set_new_value(tmp_pathv,true);
}
@@ -282,7 +282,7 @@ LPEVonKoch::resetDefaults(SPItem const* item)
B[Geom::X] = boundingbox_X.max();
B[Geom::Y] = boundingbox_Y.middle();
- std::vector<Geom::Path> paths,refpaths;
+ Geom::PathVector paths,refpaths;
Geom::Path path = Geom::Path(A);
path.appendNew<Geom::LineSegment>(B);
@@ -298,7 +298,7 @@ LPEVonKoch::resetDefaults(SPItem const* item)
//refA[Geom::Y] = boundingbox_Y.middle();
//refB[Geom::X] = boundingbox_X.max();
//refB[Geom::Y] = boundingbox_Y.middle();
- //std::vector<Geom::Path> paths;
+ //Geom::PathVector paths;
//Geom::Path path = Geom::Path( (Point) refA);
//path.appendNew<Geom::LineSegment>( (Point) refB );
//paths.push_back(path * Affine(1./3,0,0,1./3, refA[X]*2./3, refA[Y]*2./3 + boundingbox_Y.extent()/2));
diff --git a/src/live_effects/lpe-vonkoch.h b/src/live_effects/lpe-vonkoch.h
index 7dff2be52..bffbebd54 100644
--- a/src/live_effects/lpe-vonkoch.h
+++ b/src/live_effects/lpe-vonkoch.h
@@ -49,7 +49,7 @@ public:
LPEVonKoch(LivePathEffectObject *lpeobject);
virtual ~LPEVonKoch();
- virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const & path_in);
virtual void resetDefaults(SPItem const* item);
diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp
index 43352507e..b089213fd 100644
--- a/src/live_effects/parameter/filletchamferpointarray.cpp
+++ b/src/live_effects/parameter/filletchamferpointarray.cpp
@@ -11,7 +11,6 @@
#include <2geom/piecewise.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/sbasis-geometric.h>
-#include <2geom/svg-elliptical-arc.h>
#include <2geom/line.h>
#include <2geom/path-intersection.h>
@@ -205,7 +204,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
//delete temp vector
std::vector<Point>().swap(tmp);
if (last_pathv.size() > counterPaths) {
- last_pathv[counterPaths] = last_pathv[counterPaths].reverse();
+ last_pathv[counterPaths] = last_pathv[counterPaths].reversed();
}
} else {
if (last_pathv.size() > counterPaths) {
@@ -223,7 +222,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
}
double xPos = 0;
if (_vector[1][X] > 0) {
- xPos = nearest_point(curve_it1->initialPoint(), pwd2_in);
+ xPos = nearest_time(curve_it1->initialPoint(), pwd2_in);
}
if (nodetype == NODE_CUSP) {
result.push_back(Point(xPos, 1));
@@ -234,7 +233,7 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
double xPos = _vector[counter - offset][X];
if (_vector.size() <= (unsigned)(counter - offset)) {
if (_vector[1][X] > 0) {
- xPos = nearest_point(curve_it1->initialPoint(), pwd2_in);
+ xPos = nearest_time(curve_it1->initialPoint(), pwd2_in);
} else {
xPos = 0;
}
@@ -396,8 +395,8 @@ void FilletChamferPointArrayParam::updateCanvasIndicators()
Geom::Affine aff = Geom::Affine();
aff *= Geom::Scale(helper_size);
aff *= Geom::Rotate(ray1.angle() - deg_to_rad(270));
+ aff *= Geom::Translate(last_pwd2[i].valueAt(Xvalue));
pathv *= aff;
- pathv += last_pwd2[i].valueAt(Xvalue);
hp.push_back(pathv[0]);
hp.push_back(pathv[1]);
i++;
@@ -413,7 +412,7 @@ void FilletChamferPointArrayParam::addCanvasIndicators(
double FilletChamferPointArrayParam::rad_to_len(int index, double rad)
{
double len = 0;
- std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1);
+ Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1);
std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
D2<SBasis> A = last_pwd2[last_index(index, subpaths)];
if(positions.second != 0){
@@ -431,7 +430,7 @@ double FilletChamferPointArrayParam::rad_to_len(int index, double rad)
Geom::Crossings cs = Geom::crossings(p0, p1);
if(cs.size() > 0){
Point cp =p0(cs[0].ta);
- double p0pt = nearest_point(cp, B);
+ double p0pt = nearest_time(cp, B);
len = time_to_len(index,p0pt);
} else {
if(rad < 0){
@@ -446,7 +445,7 @@ double FilletChamferPointArrayParam::len_to_rad(int index, double len)
double rad = 0;
double tmp_len = _vector[index][X];
_vector[index] = Geom::Point(len,_vector[index][Y]);
- std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1);
+ Geom::PathVector subpaths = path_from_piecewise(last_pwd2, 0.1);
std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
Piecewise<D2<SBasis> > u;
u.push_cut(0);
@@ -478,14 +477,14 @@ double FilletChamferPointArrayParam::len_to_rad(int index, double len)
if (cubic2) {
ray2.setPoints(endArcPoint, (*cubic2)[1]);
}
- bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0;
+ bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) > 0;
double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint));
double angleBetween = angle_between(ray1, ray2, ccwToggle);
rad = distanceArc/sin(angleBetween/2.0);
return rad * -1;
}
-std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vector<Geom::Path> subpaths, bool last)
+std::vector<double> FilletChamferPointArrayParam::get_times(int index, Geom::PathVector subpaths, bool last)
{
const double tolerance = 0.001;
const double gapHelper = 0.00001;
@@ -540,7 +539,7 @@ std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vect
return out;
}
-std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, std::vector<Geom::Path> subpaths)
+std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, Geom::PathVector subpaths)
{
int counter = -1;
std::size_t first = 0;
@@ -582,7 +581,7 @@ std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(
return out;
}
-int FilletChamferPointArrayParam::last_index(int index, std::vector<Geom::Path> subpaths)
+int FilletChamferPointArrayParam::last_index(int index, Geom::PathVector subpaths)
{
int counter = -1;
bool inSubpath = false;
@@ -708,9 +707,9 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_set(Point const &p,
return;
}
Piecewise<D2<SBasis> > const &pwd2 = _pparam->get_pwd2();
- double t = nearest_point(p, pwd2[_index]);
+ double t = nearest_time(p, pwd2[_index]);
Geom::Point const s = snap_knot_position(pwd2[_index].valueAt(t), state);
- t = nearest_point(s, pwd2[_index]);
+ t = nearest_time(s, pwd2[_index]);
if (t == 1) {
t = 0.9999;
}
@@ -802,7 +801,7 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state)
if(xModified < 0 && !_pparam->use_distance){
xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x());
}
- std::vector<Geom::Path> subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1);
+ Geom::PathVector subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1);
std::pair<std::size_t, std::size_t> positions = _pparam->get_positions(_index, subpaths);
D2<SBasis> A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)];
if(positions.second != 0){
diff --git a/src/live_effects/parameter/filletchamferpointarray.h b/src/live_effects/parameter/filletchamferpointarray.h
index 9bfd86b41..48cd26d2d 100644
--- a/src/live_effects/parameter/filletchamferpointarray.h
+++ b/src/live_effects/parameter/filletchamferpointarray.h
@@ -47,9 +47,9 @@ public:
virtual double len_to_rad(int index, double len);
virtual double len_to_time(int index, double len);
virtual double time_to_len(int index, double time);
- virtual std::pair<std::size_t, std::size_t> get_positions(int index, std::vector<Geom::Path> subpaths);
- virtual int last_index(int index, std::vector<Geom::Path> subpaths);
- std::vector<double> get_times(int index, std::vector<Geom::Path> subpaths, bool last);
+ virtual std::pair<std::size_t, std::size_t> get_positions(int index, Geom::PathVector subpaths);
+ virtual int last_index(int index, Geom::PathVector subpaths);
+ std::vector<double> get_times(int index, Geom::PathVector subpaths, bool last);
virtual void set_helper_size(int hs);
virtual void set_use_distance(bool use_knot_distance);
virtual void set_chamfer_steps(int value_chamfer_steps);
diff --git a/src/live_effects/parameter/originalpatharray.h b/src/live_effects/parameter/originalpatharray.h
index 6c792613f..296c0f7f7 100644
--- a/src/live_effects/parameter/originalpatharray.h
+++ b/src/live_effects/parameter/originalpatharray.h
@@ -40,7 +40,7 @@ public:
}
gchar *href;
URIReference ref;
- std::vector<Geom::Path> _pathvector;
+ Geom::PathVector _pathvector;
bool reversed;
sigc::connection linked_changed_connection;
diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp
index 32e82ff8c..e0369e662 100644
--- a/src/live_effects/parameter/path.cpp
+++ b/src/live_effects/parameter/path.cpp
@@ -77,7 +77,7 @@ PathParam::~PathParam()
g_free(defvalue);
}
-std::vector<Geom::Path> const &
+Geom::PathVector const &
PathParam::get_pathvector() const
{
return _pathvector;
@@ -255,7 +255,7 @@ PathParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
}
/*
- * See comments for set_new_value(std::vector<Geom::Path>).
+ * See comments for set_new_value(Geom::PathVector).
*/
void
PathParam::set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & newpath, bool write_to_svg)
@@ -291,7 +291,7 @@ PathParam::set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & newpa
* The new path data is not written to SVG. This method will emit the signal_path_changed signal.
*/
void
-PathParam::set_new_value (std::vector<Geom::Path> const &newpath, bool write_to_svg)
+PathParam::set_new_value (Geom::PathVector const &newpath, bool write_to_svg)
{
remove_link();
_pathvector = newpath;
diff --git a/src/live_effects/parameter/path.h b/src/live_effects/parameter/path.h
index 112a1ea13..d2dddbe97 100644
--- a/src/live_effects/parameter/path.h
+++ b/src/live_effects/parameter/path.h
@@ -31,7 +31,7 @@ public:
const gchar * default_value = "M0,0 L1,1");
virtual ~PathParam();
- std::vector<Geom::Path> const & get_pathvector() const;
+ Geom::PathVector const & get_pathvector() const;
Geom::Piecewise<Geom::D2<Geom::SBasis> > const & get_pwd2();
virtual Gtk::Widget * param_newWidget();
@@ -41,7 +41,7 @@ public:
virtual void param_set_default();
void param_set_and_write_default();
- void set_new_value (std::vector<Geom::Path> const &newpath, bool write_to_svg);
+ void set_new_value (Geom::PathVector const &newpath, bool write_to_svg);
void set_new_value (Geom::Piecewise<Geom::D2<Geom::SBasis> > const &newpath, bool write_to_svg);
virtual void param_editOncanvas(SPItem * item, SPDesktop * dt);
@@ -59,7 +59,7 @@ public:
void on_paste_button_click();
protected:
- std::vector<Geom::Path> _pathvector; // this is primary data storage, since it is closest to SVG.
+ Geom::PathVector _pathvector; // this is primary data storage, since it is closest to SVG.
Geom::Piecewise<Geom::D2<Geom::SBasis> > _pwd2; // secondary, hence the bool must_recalculate_pwd2
bool must_recalculate_pwd2; // set when _pathvector was updated, but _pwd2 not
diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp
index e0c2f4c68..c61e8f9cb 100644
--- a/src/live_effects/parameter/powerstrokepointarray.cpp
+++ b/src/live_effects/parameter/powerstrokepointarray.cpp
@@ -79,7 +79,7 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi
Geom::Point pt = _vector[i];
Geom::Point position = last_pwd2.valueAt(pt[Geom::X]) + pt[Geom::Y] * last_pwd2_normal.valueAt(pt[Geom::X]);
- double t = nearest_point(position, pwd2_in);
+ double t = nearest_time(position, pwd2_in);
double offset = dot(position - pwd2_in.valueAt(t), normal.valueAt(t));
_vector[i] = Geom::Point(t, offset);
}
@@ -161,7 +161,7 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_set(Geom::Point const &p, Geom:
Piecewise<D2<SBasis> > const & n = _pparam->get_pwd2_normal();
Geom::Point const s = snap_knot_position(p, state);
- double t = nearest_point(s, pwd2);
+ double t = nearest_time(s, pwd2);
double offset = dot(s - pwd2.valueAt(t), n.valueAt(t));
_pparam->_vector.at(_index) = Geom::Point(t, offset);
sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
diff --git a/src/main.cpp b/src/main.cpp
index d1ebdc3bb..26c25af02 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1542,7 +1542,7 @@ static int sp_do_export_png(SPDocument *doc)
}
if (sp_export_area_snap) {
- round_rectangle_outwards(area);
+ area = area.roundOutwards();
}
// default dpi
diff --git a/src/object-snapper.cpp b/src/object-snapper.cpp
index f9c189c58..634d56aa6 100644
--- a/src/object-snapper.cpp
+++ b/src/object-snapper.cpp
@@ -19,6 +19,7 @@
#include <2geom/rect.h>
#include <2geom/line.h>
#include <2geom/circle.h>
+#include <2geom/path-sink.h>
#include "document.h"
#include "sp-namedview.h"
#include "sp-image.h"
@@ -517,14 +518,14 @@ void Inkscape::ObjectSnapper::_snapPaths(IntermSnapResults &isr,
for(Geom::PathVector::iterator it_pv = (it_p->path_vector)->begin(); it_pv != (it_p->path_vector)->end(); ++it_pv) {
// Find a nearest point for each curve within this path
// n curves will return n time values with 0 <= t <= 1
- std::vector<double> anp = (*it_pv).nearestPointPerCurve(p_doc);
+ std::vector<double> anp = (*it_pv).nearestTimePerCurve(p_doc);
//std::cout << "#nearest points = " << anp.size() << " | p = " << p.getPoint() << std::endl;
// Now we will examine each of the nearest points, and determine whether it's within snapping range and if we should snap to it
std::vector<double>::const_iterator np = anp.begin();
unsigned int index = 0;
for (; np != anp.end(); ++np, index++) {
- Geom::Curve const *curve = &((*it_pv).at_index(index));
+ Geom::Curve const *curve = &(it_pv->at(index));
Geom::Point const sp_doc = curve->pointAt(*np);
//dt->snapindicator->set_new_debugging_point(sp_doc*dt->doc2dt());
bool c1 = true;
@@ -623,10 +624,13 @@ void Inkscape::ObjectSnapper::_snapPathsConstrained(IntermSnapResults &isr,
// PS: Because the paths we're about to snap to are all expressed relative to document coordinate system, we will have
// to convert the snapper coordinates from the desktop coordinates to document coordinates
- std::vector<Geom::Path> constraint_path;
+ Geom::PathVector constraint_path;
if (c.isCircular()) {
Geom::Circle constraint_circle(dt->dt2doc(c.getPoint()), c.getRadius());
- constraint_circle.getPath(constraint_path);
+ Geom::PathBuilder pb;
+ pb.feed(constraint_circle);
+ pb.flush();
+ constraint_path = pb.peek();
} else {
Geom::Path constraint_line;
constraint_line.start(p_min_on_cl);
diff --git a/src/sp-conn-end.cpp b/src/sp-conn-end.cpp
index 1e478d1c9..fa5a57529 100644
--- a/src/sp-conn-end.cpp
+++ b/src/sp-conn-end.cpp
@@ -110,7 +110,7 @@ static bool try_get_intersect_point_with_item(SPPath* conn, SPItem* item,
if (!at_start)
{
// connectors are actually a single path, so consider the first element from a Geom::PathVector
- conn_pv[0] = conn_pv[0].reverse();
+ conn_pv[0] = conn_pv[0].reversed();
}
// We start with the intersection point at the beginning of the path
diff --git a/src/sp-ellipse.cpp b/src/sp-ellipse.cpp
index 932a3a1b7..0675b7781 100644
--- a/src/sp-ellipse.cpp
+++ b/src/sp-ellipse.cpp
@@ -18,6 +18,7 @@
#include <glibmm/i18n.h>
#include <2geom/angle.h>
+#include <2geom/circle.h>
#include <2geom/ellipse.h>
#include <2geom/path-sink.h>
#include <2geom/pathvector.h>
diff --git a/src/sp-gradient-test.h b/src/sp-gradient-test.h
index 696072929..578d0c5c0 100644
--- a/src/sp-gradient-test.h
+++ b/src/sp-gradient-test.h
@@ -102,10 +102,10 @@ public:
Geom::Affine const g2d(sp_gradient_get_g2d_matrix(gr, Geom::identity(), unit_rect));
Geom::Affine const gs2d(sp_gradient_get_gs2d_matrix(gr, Geom::identity(), unit_rect));
TS_ASSERT_EQUALS( g2d, Geom::identity() );
- TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) );
+ TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) );
sp_gradient_set_gs2d_matrix(gr, Geom::identity(), unit_rect, gs2d);
- TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) );
+ TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) );
}
gr->gradientTransform = grXform;
@@ -116,10 +116,10 @@ public:
Geom::Affine const g2d(sp_gradient_get_g2d_matrix(gr, funny, unit_rect));
Geom::Affine const gs2d(sp_gradient_get_gs2d_matrix(gr, funny, unit_rect));
TS_ASSERT_EQUALS( g2d, funny );
- TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) );
+ TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) );
sp_gradient_set_gs2d_matrix(gr, funny, unit_rect, gs2d);
- TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) );
+ TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) );
}
gr->gradientTransform = grXform;
@@ -130,16 +130,16 @@ public:
TS_ASSERT_EQUALS( g2d, Geom::Affine(3, 0,
0, 4,
5, 6) * funny );
- TS_ASSERT( Geom::matrix_equalp(gs2d, gr->gradientTransform * g2d, 1e-12) );
+ TS_ASSERT( Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12) );
sp_gradient_set_gs2d_matrix(gr, funny, larger_rect, gs2d);
- TS_ASSERT( Geom::matrix_equalp(gr->gradientTransform, grXform, 1e-12) );
+ TS_ASSERT( Geom::are_near(gr->gradientTransform, grXform, 1e-12) );
SP_OBJECT(gr)->setKeyValue( SP_ATTR_GRADIENTUNITS, "userSpaceOnUse");
Geom::Affine const user_g2d(sp_gradient_get_g2d_matrix(gr, funny, larger_rect));
Geom::Affine const user_gs2d(sp_gradient_get_gs2d_matrix(gr, funny, larger_rect));
TS_ASSERT_EQUALS( user_g2d, funny );
- TS_ASSERT( Geom::matrix_equalp(user_gs2d, gr->gradientTransform * user_g2d, 1e-12) );
+ TS_ASSERT( Geom::are_near(user_gs2d, gr->gradientTransform * user_g2d, 1e-12) );
}
g_object_unref(gr);
}
diff --git a/src/sp-offset.cpp b/src/sp-offset.cpp
index e8aa09952..7c3d0bd03 100644
--- a/src/sp-offset.cpp
+++ b/src/sp-offset.cpp
@@ -890,7 +890,7 @@ sp_offset_distance_to_original (SPOffset * offset, Geom::Point px)
if (ab > 0 && ab < len * len)
{
// we're in the zone of influence of the segment
- double ndist = (cross(pxsx,nx)) / len;
+ double ndist = (cross(nx, pxsx)) / len;
if (arSet == false || fabs (ndist) < fabs (arDist))
{
diff --git a/src/sp-path.cpp b/src/sp-path.cpp
index 42883588b..cabed0467 100644
--- a/src/sp-path.cpp
+++ b/src/sp-path.cpp
@@ -28,8 +28,7 @@
#include "display/curve.h"
#include <2geom/pathvector.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom-curves.h"
#include "svg/svg.h"
diff --git a/src/sp-polygon.cpp b/src/sp-polygon.cpp
index af71280d5..ced485f12 100644
--- a/src/sp-polygon.cpp
+++ b/src/sp-polygon.cpp
@@ -16,8 +16,7 @@
#include "display/curve.h"
#include <glibmm/i18n.h>
#include <2geom/pathvector.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom-curves.h"
#include "svg/stringstream.h"
#include "xml/repr.h"
diff --git a/src/splivarot.cpp b/src/splivarot.cpp
index bec300936..726df6e20 100644
--- a/src/splivarot.cpp
+++ b/src/splivarot.cpp
@@ -47,6 +47,7 @@
#include "xml/repr.h"
#include "xml/repr-sorting.h"
#include <2geom/pathvector.h>
+#include <2geom/svg-path-writer.h>
#include "helper/geom.h"
#include "livarot/Path.h"
@@ -305,8 +306,9 @@ sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pat
delete originaux[0];
delete originaux[1];
- std::vector<Geom::Path> outres = Geom::parse_svg_path(res->svg_dump_path());
-
+ gchar *result_str = res->svg_dump_path();
+ Geom::PathVector outres = Geom::parse_svg_path(result_str);
+ g_free(result_str);
delete res;
return outres;
@@ -315,7 +317,7 @@ sp_pathvector_boolop(Geom::PathVector const &pathva, Geom::PathVector const &pat
/* Convert from a livarot path to a 2geom PathVector */
Geom::PathVector pathliv_to_pathvector(Path *pathliv){
- std::vector<Geom::Path> outres = Geom::parse_svg_path(pathliv->svg_dump_path());
+ Geom::PathVector outres = Geom::parse_svg_path(pathliv->svg_dump_path());
return outres;
}
@@ -445,8 +447,9 @@ sp_selected_path_boolop(Inkscape::Selection *selection, SPDesktop *desktop, bool
// reverse if needed
// note that the selection list keeps its order
if ( reverseOrderForOp ) {
- Path* swap=originaux[0];originaux[0]=originaux[1];originaux[1]=swap;
- FillRule swai=origWind[0]; origWind[0]=origWind[1]; origWind[1]=swai;
+ using std::swap;
+ swap(originaux[0], originaux[1]);
+ swap(origWind[0], origWind[1]);
}
// and work
@@ -2260,17 +2263,17 @@ Ancetre(Inkscape::XML::Node *a, Inkscape::XML::Node *who)
}
// derived from Path_for_item
-// there must be some other way to load dest directly from epathv, without going through pathv...
Path *
Path_for_pathvector(Geom::PathVector const &epathv)
{
- Geom::PathVector *pathv = new Geom::PathVector;
- std::copy(epathv.begin(), epathv.end(), std::back_inserter(*pathv));
-
+ /*std::cout << "converting to Livarot path" << std::endl;
+
+ Geom::SVGPathWriter wr;
+ wr.feed(epathv);
+ std::cout << wr.str() << std::endl;*/
+
Path *dest = new Path;
- dest->LoadPathVector(*pathv);
- delete pathv;
-
+ dest->LoadPathVector(epathv);
return dest;
}
@@ -2281,14 +2284,26 @@ Path_for_item(SPItem *item, bool doTransformation, bool transformFull)
if (curve == NULL)
return NULL;
-
+
Geom::PathVector *pathv = pathvector_for_curve(item, curve, doTransformation, transformFull, Geom::identity(), Geom::identity());
curve->unref();
-
+
+ /*std::cout << "converting to Livarot path" << std::endl;
+
+ Geom::SVGPathWriter wr;
+ if (pathv) {
+ wr.feed(*pathv);
+ }
+ std::cout << wr.str() << std::endl;*/
+
Path *dest = new Path;
dest->LoadPathVector(*pathv);
delete pathv;
-
+
+ /*gchar *str = dest->svg_dump_path();
+ std::cout << "After conversion:\n" << str << std::endl;
+ g_free(str);*/
+
return dest;
}
@@ -2335,7 +2350,7 @@ pathvector_for_curve(SPItem *item, SPCurve *curve, bool doTransformation, bool t
} else {
*dest *= extraPreAffine * extraPostAffine;
}
-
+
return dest;
}
diff --git a/src/svg/svg-path.cpp b/src/svg/svg-path.cpp
index 9ba3c0ebd..ba9e11452 100644
--- a/src/svg/svg-path.cpp
+++ b/src/svg/svg-path.cpp
@@ -43,16 +43,15 @@ Geom::PathVector sp_svg_read_pathv(char const * str)
if (!str)
return pathv; // return empty pathvector when str == NULL
-
- typedef std::back_insert_iterator<Geom::PathVector> Inserter;
- Inserter iter(pathv);
- Geom::PathIteratorSink<Inserter> generator(iter);
+ Geom::PathBuilder builder(pathv);
+ Geom::SVGPathParser parser(builder);
+ parser.setZSnapThreshold(Geom::EPSILON);
try {
- Geom::parse_svg_path(str, generator);
+ parser.parse(str);
}
catch (Geom::SVGPathParseError &e) {
- generator.flush();
+ builder.flush();
// This warning is extremely annoying when testing
//g_warning("Malformed SVG path, truncated path up to where error was found.\n Input path=\"%s\"\n Parsed path=\"%s\"", str, sp_svg_write_path(pathv));
}
@@ -61,10 +60,17 @@ Geom::PathVector sp_svg_read_pathv(char const * str)
}
static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve const * c) {
+ // TODO: this code needs to removed and replaced by appropriate path sink
if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) {
// don't serialize stitch segments
if (!dynamic_cast<Geom::Path::StitchSegment const *>(c)) {
- str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] );
+ if (line_segment->initialPoint()[Geom::X] == line_segment->finalPoint()[Geom::X]) {
+ str.verticalLineTo( line_segment->finalPoint()[Geom::Y] );
+ } else if (line_segment->initialPoint()[Geom::Y] == line_segment->finalPoint()[Geom::Y]) {
+ str.horizontalLineTo( line_segment->finalPoint()[Geom::X] );
+ } else {
+ str.lineTo( (*line_segment)[1][0], (*line_segment)[1][1] );
+ }
}
}
else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
@@ -76,17 +82,11 @@ static void sp_svg_write_curve(Inkscape::SVG::PathString & str, Geom::Curve cons
(*cubic_bezier)[2][0], (*cubic_bezier)[2][1],
(*cubic_bezier)[3][0], (*cubic_bezier)[3][1] );
}
- else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc const *>(c)) {
- str.arcTo( svg_elliptical_arc->ray(Geom::X), svg_elliptical_arc->ray(Geom::Y),
- Geom::rad_to_deg(svg_elliptical_arc->rotationAngle()),
- svg_elliptical_arc->largeArc(), svg_elliptical_arc->sweep(),
- svg_elliptical_arc->finalPoint() );
- }
- else if(Geom::HLineSegment const *hline_segment = dynamic_cast<Geom::HLineSegment const *>(c)) {
- str.horizontalLineTo( hline_segment->finalPoint()[0] );
- }
- else if(Geom::VLineSegment const *vline_segment = dynamic_cast<Geom::VLineSegment const *>(c)) {
- str.verticalLineTo( vline_segment->finalPoint()[1] );
+ else if(Geom::EllipticalArc const *elliptical_arc = dynamic_cast<Geom::EllipticalArc const *>(c)) {
+ str.arcTo( elliptical_arc->ray(Geom::X), elliptical_arc->ray(Geom::Y),
+ Geom::rad_to_deg(elliptical_arc->rotationAngle()),
+ elliptical_arc->largeArc(), elliptical_arc->sweep(),
+ elliptical_arc->finalPoint() );
} else {
//this case handles sbasis as well as all other curve types
Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c->toSBasis(), 0.1);
diff --git a/src/ui/tool/node.cpp b/src/ui/tool/node.cpp
index ef4439242..ca6f5abb1 100644
--- a/src/ui/tool/node.cpp
+++ b/src/ui/tool/node.cpp
@@ -356,8 +356,8 @@ void Handle::dragged(Geom::Point &new_pos, GdkEventMotion *event)
Geom::Line perp_line(parent_pos, parent_pos + Geom::rot90(origin - parent_pos));
Geom::Point snap_pos = parent_pos + Geom::constrain_angle(
Geom::Point(0,0), new_pos - parent_pos, snaps, Geom::Point(1,0));
- Geom::Point orig_pos = original_line.pointAt(original_line.nearestPoint(new_pos));
- Geom::Point perp_pos = perp_line.pointAt(perp_line.nearestPoint(new_pos));
+ Geom::Point orig_pos = original_line.pointAt(original_line.nearestTime(new_pos));
+ Geom::Point perp_pos = perp_line.pointAt(perp_line.nearestTime(new_pos));
Geom::Point result = snap_pos;
ctrl_constraint = Inkscape::Snapper::SnapConstraint(parent_pos, parent_pos - snap_pos);
@@ -1568,6 +1568,13 @@ NodeList::iterator NodeList::before(double t, double *fracpart)
return ret;
}
+NodeList::iterator NodeList::before(Geom::PathTime const &pvp)
+{
+ iterator ret = begin();
+ std::advance(ret, pvp.curve_index);
+ return ret;
+}
+
NodeList::iterator NodeList::insert(iterator pos, Node *x)
{
ListNode *ins = pos._node;
diff --git a/src/ui/tool/node.h b/src/ui/tool/node.h
index 101af4817..025c460e2 100644
--- a/src/ui/tool/node.h
+++ b/src/ui/tool/node.h
@@ -406,9 +406,13 @@ public:
void setClosed(bool c) { _closed = c; }
iterator before(double t, double *fracpart = NULL);
+ iterator before(Geom::PathTime const &pvp);
const_iterator before(double t, double *fracpart = NULL) const {
return const_iterator(before(t, fracpart)._node);
}
+ const_iterator before(Geom::PathTime const &pvp) const {
+ return const_iterator(before(pvp)._node);
+ }
// list operations
diff --git a/src/ui/tool/path-manipulator.cpp b/src/ui/tool/path-manipulator.cpp
index a772c07c2..848b10373 100644
--- a/src/ui/tool/path-manipulator.cpp
+++ b/src/ui/tool/path-manipulator.cpp
@@ -1018,7 +1018,7 @@ NodeList::iterator PathManipulator::subdivideSegment(NodeList::iterator first, d
Geom::CubicBezier temp(first->position(), first->front()->position(),
second->back()->position(), second->position());
std::pair<Geom::CubicBezier, Geom::CubicBezier> div = temp.subdivide(t);
- std::vector<Geom::Point> seg1 = div.first.points(), seg2 = div.second.points();
+ std::vector<Geom::Point> seg1 = div.first.controlPoints(), seg2 = div.second.controlPoints();
// set new handle positions
Node *n = new Node(_multi_path_manipulator._path_data.node_data, seg2[0]);
@@ -1163,22 +1163,22 @@ void PathManipulator::_createControlPointsFromGeometry()
pathv *= (_edit_transform * _i2d_transform);
// in this loop, we know that there are no zero-segment subpaths
- for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
+ for (Geom::PathVector::iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
// prepare new subpath
SubpathPtr subpath(new NodeList(_subpaths));
_subpaths.push_back(subpath);
Node *previous_node = new Node(_multi_path_manipulator._path_data.node_data, pit->initialPoint());
subpath->push_back(previous_node);
- Geom::Curve const &cseg = pit->back_closed();
- bool fuse_ends = pit->closed()
- && Geom::are_near(cseg.initialPoint(), cseg.finalPoint());
- for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_open(); ++cit) {
+
+ bool closed = pit->closed();
+
+ for (Geom::Path::iterator cit = pit->begin(); cit != pit->end(); ++cit) {
Geom::Point pos = cit->finalPoint();
Node *current_node;
// if the closing segment is degenerate and the path is closed, we need to move
// the handle of the first node instead of creating a new one
- if (fuse_ends && cit == --(pit->end_open())) {
+ if (closed && cit == --(pit->end())) {
current_node = subpath->begin().get_pointer();
} else {
/* regardless of segment type, create a new node at the end
@@ -1293,7 +1293,7 @@ double PathManipulator::_bsplineHandlePosition(Handle *h, Handle *h2)
line_inside_nodes->moveto(n->position());
line_inside_nodes->lineto(next_node->position());
if(!are_near(h->position(), n->position())){
- pos = Geom::nearest_point(Geom::Point(h->position()[X] - HANDLE_CUBIC_GAP, h->position()[Y] - HANDLE_CUBIC_GAP), *line_inside_nodes->first_segment());
+ pos = Geom::nearest_time(Geom::Point(h->position()[X] - HANDLE_CUBIC_GAP, h->position()[Y] - HANDLE_CUBIC_GAP), *line_inside_nodes->first_segment());
}
}
if (pos == NO_POWER && !h2){
@@ -1447,7 +1447,7 @@ void PathManipulator::_updateOutline()
Geom::PathVector arrows;
for (Geom::PathVector::iterator i = pv.begin(); i != pv.end(); ++i) {
Geom::Path &path = *i;
- for (Geom::Path::const_iterator j = path.begin(); j != path.end_default(); ++j) {
+ for (Geom::Path::iterator j = path.begin(); j != path.end_default(); ++j) {
Geom::Point at = j->pointAt(0.5);
Geom::Point ut = j->unitTangentAt(0.5);
// rotate the point
@@ -1667,21 +1667,22 @@ void PathManipulator::_commit(Glib::ustring const &annotation, gchar const *key)
* point of the path. */
Geom::Coord PathManipulator::_updateDragPoint(Geom::Point const &evp)
{
- Geom::Coord dist = 1e23;
+ Geom::Coord dist = HUGE_VAL;
Geom::Affine to_desktop = _edit_transform * _i2d_transform;
Geom::PathVector pv = _spcurve->get_pathvector();
- boost::optional<Geom::PathVectorPosition> pvp
- = Geom::nearestPoint(pv, _desktop->w2d(evp) * to_desktop.inverse());
+
+ boost::optional<Geom::PathVectorTime> pvp =
+ pv.nearestTime(_desktop->w2d(evp) * to_desktop.inverse());
if (!pvp) return dist;
- Geom::Point nearest_point = _desktop->d2w(pv.at(pvp->path_nr).pointAt(pvp->t) * to_desktop);
-
- double fracpart;
+ Geom::Point nearest_pt = _desktop->d2w(pv.pointAt(*pvp) * to_desktop);
+
+ double fracpart = pvp->t;
std::list<SubpathPtr>::iterator spi = _subpaths.begin();
- for (unsigned i = 0; i < pvp->path_nr; ++i, ++spi) {}
- NodeList::iterator first = (*spi)->before(pvp->t, &fracpart);
+ for (unsigned i = 0; i < pvp->path_index; ++i, ++spi) {}
+ NodeList::iterator first = (*spi)->before(pvp->asPathTime());
- dist = Geom::distance(evp, nearest_point);
+ dist = Geom::distance(evp, nearest_pt);
double stroke_tolerance = _getStrokeTolerance();
if (first && first.next() &&
@@ -1689,7 +1690,7 @@ Geom::Coord PathManipulator::_updateDragPoint(Geom::Point const &evp)
dist < stroke_tolerance)
{
_dragpoint->setVisible(true);
- _dragpoint->setPosition(_desktop->w2d(nearest_point));
+ _dragpoint->setPosition(_desktop->w2d(nearest_pt));
_dragpoint->setSize(2 * stroke_tolerance);
_dragpoint->setTimeValue(fracpart);
_dragpoint->setIterator(first);
diff --git a/src/ui/tools/calligraphic-tool.cpp b/src/ui/tools/calligraphic-tool.cpp
index 15e6527a3..28195eb75 100644
--- a/src/ui/tools/calligraphic-tool.cpp
+++ b/src/ui/tools/calligraphic-tool.cpp
@@ -140,8 +140,7 @@ void CalligraphicTool::setup() {
{
/* TODO: have a look at DropperTool::setup where the same is done.. generalize? */
- Geom::PathVector path;
- Geom::Circle(0, 0, 1).getPath(path);
+ Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1));
SPCurve *c = new SPCurve(path);
diff --git a/src/ui/tools/dropper-tool.cpp b/src/ui/tools/dropper-tool.cpp
index bda9d8e8a..c838c27d5 100644
--- a/src/ui/tools/dropper-tool.cpp
+++ b/src/ui/tools/dropper-tool.cpp
@@ -84,8 +84,7 @@ void DropperTool::setup() {
ToolBase::setup();
/* TODO: have a look at CalligraphicTool::setup where the same is done.. generalize? */
- Geom::PathVector path;
- Geom::Circle(0, 0, 1).getPath(path);
+ Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1));
SPCurve *c = new SPCurve(path);
diff --git a/src/ui/tools/eraser-tool.cpp b/src/ui/tools/eraser-tool.cpp
index 10f8c8694..e416fd7ef 100644
--- a/src/ui/tools/eraser-tool.cpp
+++ b/src/ui/tools/eraser-tool.cpp
@@ -789,6 +789,8 @@ add_cap(SPCurve *curve,
}
void EraserTool::accumulate() {
+ // construct a crude outline of the eraser's path.
+ // this desperately needs to be rewritten to use the path outliner...
if ( !this->cal1->is_empty() && !this->cal2->is_empty() ) {
this->accumulated->reset(); /* Is this required ?? */
SPCurve *rev_cal2 = this->cal2->create_reverse();
@@ -798,10 +800,10 @@ void EraserTool::accumulate() {
g_assert( ! this->cal1->first_path()->closed() );
g_assert( ! rev_cal2->first_path()->closed() );
- Geom::CubicBezier const * dc_cal1_firstseg = dynamic_cast<Geom::CubicBezier const *>( this->cal1->first_segment() );
- Geom::CubicBezier const * rev_cal2_firstseg = dynamic_cast<Geom::CubicBezier const *>( rev_cal2->first_segment() );
- Geom::CubicBezier const * dc_cal1_lastseg = dynamic_cast<Geom::CubicBezier const *>( this->cal1->last_segment() );
- Geom::CubicBezier const * rev_cal2_lastseg = dynamic_cast<Geom::CubicBezier const *>( rev_cal2->last_segment() );
+ Geom::BezierCurve const * dc_cal1_firstseg = dynamic_cast<Geom::BezierCurve const *>( this->cal1->first_segment() );
+ Geom::BezierCurve const * rev_cal2_firstseg = dynamic_cast<Geom::BezierCurve const *>( rev_cal2->first_segment() );
+ Geom::BezierCurve const * dc_cal1_lastseg = dynamic_cast<Geom::BezierCurve const *>( this->cal1->last_segment() );
+ Geom::BezierCurve const * rev_cal2_lastseg = dynamic_cast<Geom::BezierCurve const *>( rev_cal2->last_segment() );
g_assert( dc_cal1_firstseg );
g_assert( rev_cal2_firstseg );
@@ -810,11 +812,21 @@ void EraserTool::accumulate() {
this->accumulated->append(this->cal1, FALSE);
- add_cap(this->accumulated, (*dc_cal1_lastseg)[2], (*dc_cal1_lastseg)[3], (*rev_cal2_firstseg)[0], (*rev_cal2_firstseg)[1], this->cap_rounding);
+ add_cap(this->accumulated,
+ dc_cal1_lastseg->finalPoint() - dc_cal1_lastseg->unitTangentAt(1),
+ dc_cal1_lastseg->finalPoint(),
+ rev_cal2_firstseg->initialPoint(),
+ rev_cal2_firstseg->initialPoint() + rev_cal2_firstseg->unitTangentAt(0),
+ this->cap_rounding);
this->accumulated->append(rev_cal2, TRUE);
- add_cap(this->accumulated, (*rev_cal2_lastseg)[2], (*rev_cal2_lastseg)[3], (*dc_cal1_firstseg)[0], (*dc_cal1_firstseg)[1], this->cap_rounding);
+ add_cap(this->accumulated,
+ rev_cal2_lastseg->finalPoint() - rev_cal2_lastseg->unitTangentAt(1),
+ rev_cal2_lastseg->finalPoint(),
+ dc_cal1_firstseg->initialPoint(),
+ dc_cal1_firstseg->initialPoint() + dc_cal1_firstseg->unitTangentAt(0),
+ this->cap_rounding);
this->accumulated->closepath();
diff --git a/src/ui/tools/gradient-tool.cpp b/src/ui/tools/gradient-tool.cpp
index 526671515..603458983 100644
--- a/src/ui/tools/gradient-tool.cpp
+++ b/src/ui/tools/gradient-tool.cpp
@@ -207,7 +207,7 @@ sp_gradient_context_is_over_line (GradientTool *rc, SPItem *item, Geom::Point ev
SPCtrlLine* line = SP_CTRLLINE(item);
Geom::LineSegment ls(line->s, line->e);
- Geom::Point nearest = ls.pointAt(ls.nearestPoint(rc->mousepoint_doc));
+ Geom::Point nearest = ls.pointAt(ls.nearestTime(rc->mousepoint_doc));
double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance;
diff --git a/src/ui/tools/mesh-tool.cpp b/src/ui/tools/mesh-tool.cpp
index 0e68af601..813d6ae5b 100644
--- a/src/ui/tools/mesh-tool.cpp
+++ b/src/ui/tools/mesh-tool.cpp
@@ -275,7 +275,7 @@ sp_mesh_context_is_over_line (MeshTool *rc, SPItem *item, Geom::Point event_p)
SPCtrlCurve *curve = SP_CTRLCURVE(item);
Geom::BezierCurveN<3> b( curve->p0, curve->p1, curve->p2, curve->p3 );
- Geom::Coord coord = b.nearestPoint( rc->mousepoint_doc ); // Coord == double
+ Geom::Coord coord = b.nearestTime( rc->mousepoint_doc ); // Coord == double
Geom::Point nearest = b( coord );
double dist_screen = Geom::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
diff --git a/src/ui/tools/pen-tool.cpp b/src/ui/tools/pen-tool.cpp
index be6156fa2..38892517d 100644
--- a/src/ui/tools/pen-tool.cpp
+++ b/src/ui/tools/pen-tool.cpp
@@ -57,8 +57,7 @@
#include <typeinfo>
#include <2geom/pathvector.h>
#include <2geom/affine.h>
-#include <2geom/bezier-curve.h>
-#include <2geom/hvlinesegment.h>
+#include <2geom/curves.h>
#include "helper/geom-nodetype.h"
#include "helper/geom-curves.h"
@@ -71,7 +70,7 @@
#define INKSCAPE_LPE_BSPLINE_C
#include "live_effects/lpe-bspline.h"
-#include <2geom/nearest-point.h>
+#include <2geom/nearest-time.h>
#include "live_effects/effect.h"
@@ -1515,7 +1514,7 @@ void PenTool::_bsplineSpiroMotion(bool shift){
Geom::D2< Geom::SBasis > SBasisweight_power;
weight_power->moveto(tmp_curve ->last_segment()->finalPoint());
weight_power->lineto(tmp_curve ->last_segment()->initialPoint());
- float WP = Geom::nearest_point((*cubic)[2],*weight_power->first_segment());
+ float WP = Geom::nearest_time((*cubic)[2],*weight_power->first_segment());
weight_power->reset();
weight_power->moveto(this->red_curve->last_segment()->initialPoint());
weight_power->lineto(this->red_curve->last_segment()->finalPoint());
@@ -1737,7 +1736,7 @@ void PenTool::_bsplineSpiroBuild()
//from LPE BSPLINE:
void PenTool::_bsplineDoEffect(SPCurve * curve)
{
- const double NO_POWER = 0.0;
+ //const double NO_POWER = 0.0;
const double DEFAULT_START_POWER = 0.3334;
const double DEFAULT_END_POWER = 0.6667;
if (curve->get_segment_count() < 1) {
@@ -1791,12 +1790,12 @@ void PenTool::_bsplineDoEffect(SPCurve * curve)
if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER);
} else {
- point_at1 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment()));
+ point_at1 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[1], *in->first_segment()));
}
if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) {
point_at2 = sbasis_in.valueAt(DEFAULT_END_POWER);
} else {
- point_at2 = sbasis_in.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment()));
+ point_at2 = sbasis_in.valueAt(Geom::nearest_time((*cubic)[2], *in->first_segment()));
}
} else {
point_at1 = in->first_segment()->initialPoint();
@@ -1814,7 +1813,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve)
if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
next_point_at1 = sbasis_in.valueAt(DEFAULT_START_POWER);
} else {
- next_point_at1 = sbasis_out.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment()));
+ next_point_at1 = sbasis_out.valueAt(Geom::nearest_time((*cubic)[1], *out->first_segment()));
}
} else {
next_point_at1 = out->first_segment()->initialPoint();
@@ -1831,7 +1830,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve)
cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin());
if (cubic) {
line_helper->moveto(sbasis_start.valueAt(
- Geom::nearest_point((*cubic)[1], *start->first_segment())));
+ Geom::nearest_time((*cubic)[1], *start->first_segment())));
} else {
line_helper->moveto(start->first_segment()->initialPoint());
}
@@ -1845,7 +1844,7 @@ void PenTool::_bsplineDoEffect(SPCurve * curve)
cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
if (cubic) {
line_helper->lineto(sbasis_end.valueAt(
- Geom::nearest_point((*cubic)[2], *end->first_segment())));
+ Geom::nearest_time((*cubic)[2], *end->first_segment())));
} else {
line_helper->lineto(end->first_segment()->finalPoint());
}
diff --git a/src/ui/tools/spray-tool.cpp b/src/ui/tools/spray-tool.cpp
index 14595740d..e2be5ca4b 100644
--- a/src/ui/tools/spray-tool.cpp
+++ b/src/ui/tools/spray-tool.cpp
@@ -198,8 +198,7 @@ void SprayTool::setup() {
{
/* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
- Geom::PathVector path;
- Geom::Circle(0, 0, 1).getPath(path);
+ Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1));
SPCurve *c = new SPCurve(path);
diff --git a/src/ui/tools/tweak-tool.cpp b/src/ui/tools/tweak-tool.cpp
index 76b52f9be..94f7aa135 100644
--- a/src/ui/tools/tweak-tool.cpp
+++ b/src/ui/tools/tweak-tool.cpp
@@ -259,8 +259,7 @@ void TweakTool::setup() {
{
/* TODO: have a look at sp_dyna_draw_context_setup where the same is done.. generalize? at least make it an arcto! */
- Geom::PathVector path;
- Geom::Circle(0, 0, 1).getPath(path);
+ Geom::PathVector path = Geom::Path(Geom::Circle(0,0,1));
SPCurve *c = new SPCurve(path);