1. === modified file 'src/helper/geom-pathstroke.cpp'
  2. --- src/helper/geom-pathstroke.cpp 2016-08-03 13:29:38 +0000
  3. +++ src/helper/geom-pathstroke.cpp 2016-11-28 20:42:57 +0000
  4. @@ -1,6 +1,7 @@
  5. /* Authors:
  6. * Liam P. White
  7. * Tavmjong Bah
  8. + * Alexander Brock
  9. *
  10. * Copyright (C) 2014-2015 Authors
  11. *
  12. @@ -746,33 +747,36 @@
  13. len = l;
  14. }
  15. -void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels)
  16. -{
  17. +double _offset_cubic_stable_sub(
  18. + Geom::CubicBezier const& bez,
  19. + Geom::CubicBezier& c,
  20. + const Geom::Point& start_normal,
  21. + const Geom::Point& end_normal,
  22. + const Geom::Point& start_new,
  23. + const Geom::Point& end_new,
  24. + const double start_rad,
  25. + const double end_rad,
  26. + const double start_len,
  27. + const double end_len,
  28. + const double width,
  29. + const double width_correction) {
  30. using Geom::X;
  31. using Geom::Y;
  32. - Geom::Point start_pos = bez.initialPoint();
  33. - Geom::Point end_pos = bez.finalPoint();
  34. -
  35. - Geom::Point start_normal = Geom::rot90(bez.unitTangentAt(0));
  36. - Geom::Point end_normal = -Geom::rot90(Geom::unitTangentAt(Geom::reverse(bez.toSBasis()), 0.));
  37. -
  38. - // offset the start and end control points out by the width
  39. - Geom::Point start_new = start_pos + start_normal*width;
  40. - Geom::Point end_new = end_pos + end_normal*width;
  41. -
  42. - // --------
  43. - double start_rad, end_rad;
  44. - double start_len, end_len; // tangent lengths
  45. - get_cubic_data(bez, 0, start_len, start_rad);
  46. - get_cubic_data(bez, 1, end_len, end_rad);
  47. -
  48. double start_off = 1, end_off = 1;
  49. // correction of the lengths of the tangent to the offset
  50. if (!Geom::are_near(start_rad, 0))
  51. - start_off += width / start_rad;
  52. + start_off += (width + width_correction) / start_rad;
  53. if (!Geom::are_near(end_rad, 0))
  54. - end_off += width / end_rad;
  55. + end_off += (width + width_correction) / end_rad;
  56. +
  57. + // We don't change the direction of the control points
  58. + if (start_off < 0) {
  59. + start_off = 0;
  60. + }
  61. + if (end_off < 0) {
  62. + end_off = 0;
  63. + }
  64. start_off *= start_len;
  65. end_off *= end_len;
  66. // --------
  67. @@ -783,23 +787,137 @@
  68. mid2_new = Geom::Point(end_new[X] - mid2_new[X]/3., end_new[Y] - mid2_new[Y]/3.);
  69. // create the estimate curve
  70. - Geom::CubicBezier c = Geom::CubicBezier(start_new, mid1_new, mid2_new, end_new);
  71. + c = Geom::CubicBezier(start_new, mid1_new, mid2_new, end_new);
  72. +
  73. + // check the tolerance for our estimate to be a parallel curve
  74. +
  75. + double worst_residual = 0;
  76. + for (size_t ii = 3; ii <= 7; ii+=2) {
  77. + const double t = static_cast<double>(ii) / 10;
  78. + const Geom::Point req = bez.pointAt(t);
  79. + const Geom::Point chk = c.pointAt(c.nearestTime(req));
  80. + const double current_residual = (chk-req).length() - std::abs(width);
  81. + if (std::abs(current_residual) > std::abs(worst_residual)) {
  82. + worst_residual = current_residual;
  83. + }
  84. + }
  85. + return worst_residual;
  86. +}
  87. +
  88. +void offset_cubic(Geom::Path& p, Geom::CubicBezier const& bez, double width, double tol, size_t levels)
  89. +{
  90. + using Geom::X;
  91. + using Geom::Y;
  92. +
  93. + const Geom::Point start_pos = bez.initialPoint();
  94. + const Geom::Point end_pos = bez.finalPoint();
  95. +
  96. + const Geom::Point start_normal = Geom::rot90(bez.unitTangentAt(0));
  97. + const Geom::Point end_normal = -Geom::rot90(Geom::unitTangentAt(Geom::reverse(bez.toSBasis()), 0.));
  98. +
  99. + // offset the start and end control points out by the width
  100. + const Geom::Point start_new = start_pos + start_normal*width;
  101. + const Geom::Point end_new = end_pos + end_normal*width;
  102. +
  103. + // --------
  104. + double start_rad, end_rad;
  105. + double start_len, end_len; // tangent lengths
  106. + get_cubic_data(bez, 0, start_len, start_rad);
  107. + get_cubic_data(bez, 1, end_len, end_rad);
  108. +
  109. +
  110. + Geom::CubicBezier c;
  111. +
  112. + double best_width_correction = 0;
  113. + double best_residual = _offset_cubic_stable_sub(
  114. + bez, c,
  115. + start_normal, end_normal,
  116. + start_new, end_new,
  117. + start_rad, end_rad,
  118. + start_len, end_len,
  119. + width, best_width_correction);
  120. + double stepsize = std::abs(width)/2;
  121. + bool seen_success = false;
  122. + double stepsize_threshold = 0;
  123. + // std::cout << "Residual from " << best_residual << " ";
  124. + size_t ii = 0;
  125. + for (; ii < 100 && stepsize > stepsize_threshold; ++ii) {
  126. + bool success = false;
  127. + const double width_correction = best_width_correction - (best_residual > 0 ? 1 : -1) * stepsize;
  128. + Geom::CubicBezier current_curve;
  129. + const double residual = _offset_cubic_stable_sub(
  130. + bez, current_curve,
  131. + start_normal, end_normal,
  132. + start_new, end_new,
  133. + start_rad, end_rad,
  134. + start_len, end_len,
  135. + width, width_correction);
  136. + if (std::abs(residual) < std::abs(best_residual)) {
  137. + best_residual = residual;
  138. + best_width_correction = width_correction;
  139. + c = current_curve;
  140. + success = true;
  141. + if (std::abs(best_residual) < tol/4) {
  142. + break;
  143. + }
  144. + }
  145. +
  146. + if (success) {
  147. + if (!seen_success) {
  148. + seen_success = true;
  149. + //std::cout << "Stepsize factor: " << std::abs(width) / stepsize << std::endl;
  150. + stepsize_threshold = stepsize / 1000;
  151. + }
  152. + }
  153. + else {
  154. + stepsize /= 2;
  155. + }
  156. + if (std::abs(best_width_correction) >= std::abs(width)/2) {
  157. + //break; // Seems to prevent some numerical instabilities, not clear if useful
  158. + }
  159. + }
  160. // reached maximum recursive depth
  161. // don't bother with any more correction
  162. if (levels == 0) {
  163. - p.append(c);
  164. + try {
  165. + p.append(c);
  166. + }
  167. + catch (...) {
  168. + if ((p.finalPoint() - c.initialPoint()).length() < 1e-6) {
  169. + c.setInitial(p.finalPoint());
  170. + }
  171. + else {
  172. + auto line = Geom::LineSegment(p.finalPoint(), c.initialPoint());
  173. + p.append(line);
  174. + }
  175. + p.append(c);
  176. + }
  177. +
  178. return;
  179. }
  180. - // check the tolerance for our estimate to be a parallel curve
  181. - Geom::Point chk = c.pointAt(.5);
  182. - Geom::Point req = bez.pointAt(.5) + Geom::rot90(bez.unitTangentAt(.5))*width; // required accuracy
  183. -
  184. - Geom::Point const diff = req - chk;
  185. - double const err = Geom::dot(diff, diff);
  186. -
  187. - if (err < tol) {
  188. + // We find the point on our new curve (c) for which the distance between
  189. + // (c) and (bez) differs the most from the desired distance (width).
  190. + double worst_err = std::abs(best_residual);
  191. + double worst_time = .5;
  192. + for (size_t ii = 1; ii <= 9; ++ii) {
  193. + const double t = static_cast<double>(ii) / 10;
  194. + const Geom::Point req = bez.pointAt(t);
  195. + // We use the exact solution with nearestTime because it is numerically
  196. + // much more stable than simply assuming that the point on (c) closest
  197. + // to bez.pointAt(t) is given by c.pointAt(t)
  198. + const Geom::Point chk = c.pointAt(c.nearestTime(req));
  199. +
  200. + Geom::Point const diff = req - chk;
  201. + const double err = std::abs(diff.length() - std::abs(width));
  202. + if (err > worst_err) {
  203. + worst_err = err;
  204. + worst_time = t;
  205. + }
  206. + }
  207. +
  208. + if (worst_err < tol) {
  209. if (Geom::are_near(start_new, p.finalPoint())) {
  210. p.setFinal(start_new); // if it isn't near, we throw
  211. }
  212. @@ -809,7 +927,7 @@
  213. return;
  214. } else {
  215. // split the curve in two
  216. - std::pair<Geom::CubicBezier, Geom::CubicBezier> s = bez.subdivide(.5);
  217. + std::pair<Geom::CubicBezier, Geom::CubicBezier> s = bez.subdivide(worst_time);
  218. offset_cubic(p, s.first, width, tol, levels - 1);
  219. offset_cubic(p, s.second, width, tol, levels - 1);
  220. }
  221. @@ -827,9 +945,8 @@
  222. offset_cubic(p, cub, width, tol, levels);
  223. }
  224. -void offset_curve(Geom::Path& res, Geom::Curve const* current, double width)
  225. +void offset_curve(Geom::Path& res, Geom::Curve const* current, double width, double tolerance)
  226. {
  227. - double const tolerance = 0.0025;
  228. size_t levels = 8;
  229. if (current->isDegenerate()) return; // don't do anything
  230. @@ -855,14 +972,14 @@
  231. default: {
  232. Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), tolerance);
  233. for (size_t i = 0; i < sbasis_path.size(); ++i)
  234. - offset_curve(res, &sbasis_path[i], width);
  235. + offset_curve(res, &sbasis_path[i], width, tolerance);
  236. break;
  237. }
  238. }
  239. } else {
  240. Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(current->toSBasis(), 0.1);
  241. for (size_t i = 0; i < sbasis_path.size(); ++i)
  242. - offset_curve(res, &sbasis_path[i], width);
  243. + offset_curve(res, &sbasis_path[i], width, tolerance);
  244. }
  245. }
  246. @@ -949,6 +1066,7 @@
  247. Geom::Path half_outline(Geom::Path const& input, double width, double miter, LineJoinType join)
  248. {
  249. + double const tolerance = 5.0 * (width/100); // Tolerance is 5%
  250. Geom::Path res;
  251. if (input.size() == 0) return res;
  252. @@ -963,12 +1081,13 @@
  253. res.start(start);
  254. // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
  255. - const size_t k = (input.back_closed().isDegenerate() && input.closed())
  256. - ?input.size_default()-1:input.size_default();
  257. + const Geom::Curve &closingline = input.back_closed();
  258. + const size_t k = (are_near(closingline.initialPoint(), closingline.finalPoint()) && input.closed() )
  259. + ?input.size_open():input.size_default();
  260. for (size_t u = 0; u < k; u += 2) {
  261. temp.clear();
  262. - offset_curve(temp, &input[u], width);
  263. + offset_curve(temp, &input[u], width, tolerance);
  264. // on the first run through, there isn't a join
  265. if (u == 0) {
  266. @@ -981,12 +1100,11 @@
  267. // odd number of paths
  268. if (u < k - 1) {
  269. temp.clear();
  270. - offset_curve(temp, &input[u+1], width);
  271. + offset_curve(temp, &input[u+1], width, tolerance);
  272. tangents(tang, input[u], input[u+1]);
  273. outline_join(res, temp, tang[0], tang[1], width, miter, join);
  274. }
  275. }
  276. -
  277. if (input.closed()) {
  278. Geom::Curve const &c1 = res.back();
  279. Geom::Curve const &c2 = res.front();
  280. @@ -998,7 +1116,6 @@
  281. outline_join(temp, temp2, tang[0], tang[1], width, miter, join);
  282. res.erase(res.begin());
  283. res.erase_last();
  284. - //
  285. res.append(temp);
  286. res.close();
  287. }
  288. @@ -1010,9 +1127,8 @@
  289. {
  290. if (res.size() == 0 || temp.size() == 0)
  291. return;
  292. -
  293. Geom::Curve const& outgoing = temp.front();
  294. - if (Geom::are_near(res.finalPoint(), outgoing.initialPoint())) {
  295. + if (Geom::are_near(res.finalPoint(), outgoing.initialPoint(), 0.01)) {
  296. // if the points are /that/ close, just ignore this one
  297. res.setFinal(temp.initialPoint());
  298. res.append(temp);
  1. More ...
 
 

27

 

815

Patch Fix end point issues on Geom::Pathstroke

-

PasteBin

Линии
312
Слова
1389
Размер:
11,4 КБ
Создан:
Изменения
2
Тип:
text/plain
General Public License v2 (GPLv2)
Пожалуйста, войдитев систему, чтобы оставить комментарии!