spline.dart 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. import 'package:fis_measure/interfaces/date_types/point.dart';
  2. class SplineUtils {
  3. static List<DPoint> create(
  4. List<DPoint>? sourcePoints, {
  5. double tension = 0.5,
  6. List<double>? tensions,
  7. bool isClosed = false,
  8. double tolerance = 1.0,
  9. bool closeByStraightLine = false,
  10. List<DPoint> samplePoints = const [],
  11. bool isSimpsonSpline = false,
  12. }) {
  13. List<DPoint> splinePoints = [];
  14. double length = 0;
  15. if (sourcePoints == null || sourcePoints.isEmpty) {
  16. return splinePoints;
  17. }
  18. splinePoints.add(sourcePoints[0]);
  19. if (sourcePoints.length == 1) {
  20. generateSamplePointsFromTraceLine(
  21. sourcePoints[0], sourcePoints[0], samplePoints);
  22. return splinePoints;
  23. }
  24. var pts = List<DPoint>.from(sourcePoints);
  25. if (pts.length > 2 &&
  26. (pts[pts.length - 1] - pts[pts.length - 2]).length < tolerance) {
  27. pts.removeAt(pts.length - 1);
  28. }
  29. _ModifiableInt segmentStart = _ModifiableInt(-1);
  30. if (pts.length == 2) {
  31. if (!isClosed) {
  32. length += _segment(splinePoints, pts[0], pts[0], pts[1], pts[1],
  33. tension, tension, tolerance, segmentStart);
  34. } else {
  35. length += _segment(splinePoints, pts[1], pts[0], pts[1], pts[0],
  36. tension, tension, tolerance, segmentStart);
  37. length += _segment(splinePoints, pts[0], pts[1], pts[0], pts[1],
  38. tension, tension, tolerance, segmentStart);
  39. }
  40. generateSamplePointsFromTraceLine(pts[0], pts[1], samplePoints);
  41. } else {
  42. // var startEndTension = ResourceManager.GetValue("Measurement", "Tension", 0.1);
  43. var startEndTension = 0.1; // TODO
  44. List<int> pointSegments = [];
  45. var useTensionCollection = tensions != null && tensions.isNotEmpty;
  46. for (var i = 0; i < pts.length; i++) {
  47. var t1 = useTensionCollection ? tensions[i % tensions.length] : tension;
  48. var t2 = useTensionCollection
  49. ? tensions[(i + 1) % tensions.length]
  50. : tension;
  51. if (isSimpsonSpline &&
  52. (i == 0 || i == pts.length - 2 || i == pts.length - 1)) {
  53. t1 = t2 = startEndTension;
  54. }
  55. if (i == 0) {
  56. length += _segment(
  57. splinePoints,
  58. isClosed ? pts[pts.length - 1] : pts[0],
  59. pts[0],
  60. pts[1],
  61. pts[2],
  62. t1,
  63. t2,
  64. tolerance,
  65. segmentStart,
  66. );
  67. if (segmentStart.value != -1) {
  68. pointSegments.add(segmentStart.value);
  69. }
  70. } else if (i == pts.length - 2) {
  71. length += _segment(
  72. splinePoints,
  73. pts[i - 1],
  74. pts[i],
  75. pts[i + 1],
  76. isClosed ? pts[0] : pts[i + 1],
  77. t1,
  78. t2,
  79. tolerance,
  80. segmentStart,
  81. );
  82. if (segmentStart.value != -1) {
  83. pointSegments.add(segmentStart.value);
  84. }
  85. } else if (i == pts.length - 1) {
  86. if (isClosed) {
  87. if (closeByStraightLine) {
  88. length += (pts[i] - pts[0]).length;
  89. } else {
  90. length += _segment(splinePoints, pts[i - 1], pts[i], pts[0],
  91. pts[1], t1, t2, tolerance, segmentStart);
  92. if (segmentStart.value != -1) {
  93. pointSegments.add(segmentStart.value);
  94. }
  95. }
  96. }
  97. } else {
  98. length += _segment(splinePoints, pts[i - 1], pts[i], pts[i + 1],
  99. pts[i + 2], t1, t2, tolerance, segmentStart);
  100. if (segmentStart.value != -1) {
  101. pointSegments.add(segmentStart.value);
  102. }
  103. }
  104. }
  105. if (samplePoints.isNotEmpty) {
  106. if (pointSegments.length <= samplePoints.length) {
  107. int samples = 0;
  108. int totalPoints = splinePoints.length;
  109. for (int i = 0; i < pointSegments.length; i++) {
  110. var nextStart = i == pointSegments.length - 1
  111. ? splinePoints.length
  112. : pointSegments[i + 1];
  113. var startIndex = pointSegments[i];
  114. var segCount = nextStart - startIndex;
  115. var totalSamples = samplePoints.length - samples;
  116. var interval = totalPoints / totalSamples;
  117. if (interval >= 1) {
  118. int count = (segCount / interval).round();
  119. if (count <= 1) {
  120. samplePoints[samples++] =
  121. splinePoints[startIndex + segCount - 1];
  122. if (samples == samplePoints.length) {
  123. return splinePoints;
  124. }
  125. } else {
  126. var step = segCount ~/ count;
  127. var seek = (segCount % count + step - 1).toInt();
  128. for (int index = seek; index < segCount; index += step) {
  129. samplePoints[samples++] = splinePoints[startIndex + index];
  130. if (samples == samplePoints.length) {
  131. return splinePoints;
  132. }
  133. }
  134. }
  135. } else {
  136. for (int index = 0; index < segCount; index++) {
  137. samplePoints[samples++] = splinePoints[startIndex + index];
  138. if (samples == samplePoints.length) {
  139. return splinePoints;
  140. }
  141. }
  142. }
  143. totalPoints -= segCount;
  144. }
  145. if (samples > 1 && samples < samplePoints.length) {
  146. List<DPoint> temp = samplePoints.take(samples).toList();
  147. int insertIndex = temp.length - 1;
  148. while (temp.length < samplePoints.length) {
  149. var vector = (temp[insertIndex] - temp[insertIndex - 1]) / 2;
  150. var point = temp[insertIndex - 1].addVector(vector);
  151. temp.insert(insertIndex, point);
  152. insertIndex--;
  153. if (insertIndex == 0) {
  154. insertIndex = temp.length - 1;
  155. }
  156. }
  157. for (int i = 0; i < samplePoints.length; i++) {
  158. samplePoints[i] = temp[i];
  159. }
  160. }
  161. } else {
  162. throw ArgumentError("Too many trace point segments.");
  163. }
  164. }
  165. }
  166. return splinePoints;
  167. }
  168. static void generateSamplePointsFromTraceLine(
  169. DPoint start,
  170. DPoint end,
  171. List<DPoint> samplePoints,
  172. ) {
  173. if (samplePoints.length > 1) {
  174. if (start.x == end.x && start.y == end.y) {
  175. for (int i = 0; i < samplePoints.length; i++) {
  176. samplePoints[i] = start;
  177. }
  178. } else {
  179. DPoint step = DPoint((end.x - start.x) / (samplePoints.length - 1),
  180. (end.y - start.y) / (samplePoints.length - 1));
  181. for (int i = 0; i < samplePoints.length; i++) {
  182. samplePoints[i] = DPoint(start.x + step.x * i, start.y + step.y * i);
  183. }
  184. }
  185. }
  186. }
  187. static double _segment(
  188. List<DPoint> points,
  189. DPoint pt0,
  190. DPoint pt1,
  191. DPoint pt2,
  192. DPoint pt3,
  193. double t1,
  194. double t2,
  195. double tolerance,
  196. _ModifiableInt segmentStart,
  197. ) {
  198. segmentStart.value = -1;
  199. double length = (pt1 - pt2).length;
  200. double sx1 = t1 * (pt2.x - pt0.x);
  201. double sy1 = t1 * (pt2.y - pt0.y);
  202. double sx2 = t2 * (pt3.x - pt1.x);
  203. double sy2 = t2 * (pt3.y - pt1.y);
  204. double ax = sx1 + sx2 + 2 * pt1.x - 2 * pt2.x;
  205. double ay = sy1 + sy2 + 2 * pt1.y - 2 * pt2.y;
  206. double bx = -2 * sx1 - sx2 - 3 * pt1.x + 3 * pt2.x;
  207. double by = -2 * sy1 - sy2 - 3 * pt1.y + 3 * pt2.y;
  208. double cx = sx1;
  209. double cy = sy1;
  210. double dx = pt1.x;
  211. double dy = pt1.y;
  212. int num = ((pt1.x - pt2.x).abs() + (pt1.y - pt2.y).abs()) ~/ tolerance;
  213. for (int i = 1; i < num; i++) {
  214. double t = i / (num - 1);
  215. DPoint pt = DPoint(
  216. ax * t * t * t + bx * t * t + cx * t + dx,
  217. ay * t * t * t + by * t * t + cy * t + dy,
  218. );
  219. if (i == 1) {
  220. length += (pt - pt1).length;
  221. } else {
  222. length += (pt - points[points.length - 1]).length;
  223. }
  224. if (segmentStart.value == -1) {
  225. segmentStart.value = points.length;
  226. }
  227. points.add(pt);
  228. }
  229. return length;
  230. }
  231. }
  232. class _ModifiableInt extends _ModifiableValue<int> {
  233. _ModifiableInt(super.value);
  234. _ModifiableInt operator +(int other) {
  235. value += other;
  236. return this;
  237. }
  238. _ModifiableInt operator -(int other) {
  239. value -= other;
  240. return this;
  241. }
  242. }
  243. class _ModifiableValue<T> {
  244. T value;
  245. _ModifiableValue(
  246. this.value,
  247. );
  248. }