Util.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796
  1. //----------------------------------------------------------------------------
  2. // Anti-Grain Geometry - Version 2.4
  3. // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
  4. //
  5. // C# port by: Lars Brubaker
  6. // larsbrubaker@gmail.com
  7. // Copyright (C) 2007
  8. //
  9. // Permission to copy, use, modify, sell and distribute this software
  10. // is granted provided this copyright notice appears in all copies.
  11. // This software is provided "as is" without express or implied
  12. // warranty, and with no claim as to its suitability for any purpose.
  13. //
  14. //----------------------------------------------------------------------------
  15. // Contact: mcseem@antigrain.com
  16. // mcseemagg@yahoo.com
  17. // http://www.antigrain.com
  18. //----------------------------------------------------------------------------
  19. // #define USE_UNSAFE // no real code for this yet
  20. using MatterHackers.Agg.Image;
  21. using MatterHackers.Agg.RasterizerScanline;
  22. using MatterHackers.Agg.Transform;
  23. using MatterHackers.Agg.VertexSource;
  24. using System;
  25. using System.Collections.Generic;
  26. using System.Globalization;
  27. using System.IO;
  28. using System.Linq;
  29. using System.Text.RegularExpressions;
  30. using System.Threading;
  31. using static MatterHackers.Agg.ScanlineRasterizer;
  32. using static System.Net.WebRequestMethods;
  33. namespace MatterHackers.Agg
  34. {
  35. static public class Util
  36. {
  37. //----------------------------------------------------------filling_rule_e
  38. public enum filling_rule_e
  39. {
  40. fill_non_zero,
  41. fill_even_odd
  42. }
  43. public static double GetRatio(double startRatio, double endRatio, int index, int count)
  44. {
  45. return startRatio + (endRatio - startRatio) * index / count;
  46. }
  47. public static double GetRatio(double startRatio, double endRatio, double partialRatio)
  48. {
  49. return startRatio + (endRatio - startRatio) * partialRatio;
  50. }
  51. public static void memcpy(byte[] dest, int destIndex, byte[] source, int sourceIndex, int count)
  52. {
  53. #if USE_UNSAFE
  54. #else
  55. for (int i = 0; i < count; i++)
  56. {
  57. dest[destIndex + i] = source[sourceIndex + i];
  58. }
  59. #endif
  60. }
  61. public static string AddQueryPram(string url, string key, string value)
  62. {
  63. if (url.Contains("?"))
  64. {
  65. return url + "&" + key + "=" + value;
  66. }
  67. else
  68. {
  69. return url + "?" + key + "=" + value;
  70. }
  71. }
  72. public static double ParseDouble(string source, bool fastSimpleNumbers)
  73. {
  74. int startIndex = 0;
  75. return ParseDouble(source, ref startIndex, fastSimpleNumbers);
  76. }
  77. // private static Regex numberRegex = new Regex(@"[-+]?[0-9]*\.?[0-9]+");
  78. private static Regex numberRegex = new Regex(@"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?");
  79. private static double GetNextNumber(string source, ref int startIndex)
  80. {
  81. Match numberMatch = numberRegex.Match(source, startIndex);
  82. string returnString = numberMatch.Value;
  83. startIndex = numberMatch.Index + numberMatch.Length;
  84. double returnVal;
  85. double.TryParse(returnString, NumberStyles.Number, CultureInfo.InvariantCulture, out returnVal);
  86. return returnVal;
  87. }
  88. public static void CopyFilesWithProgress(IEnumerable<string> sourceFiles,
  89. string destinationPath,
  90. Action<double, string> reporter,
  91. CancellationTokenSource cancellationToken,
  92. string copyProgressMessage = "Copying")
  93. {
  94. var count = sourceFiles.Count();
  95. var i = 0;
  96. foreach (var file in sourceFiles)
  97. {
  98. if (cancellationToken.IsCancellationRequested)
  99. {
  100. break;
  101. }
  102. var destination = Path.Combine(destinationPath, Path.GetFileName(file));
  103. var completedRatio = (double)i / count;
  104. CopyFileWithProgress(file, destination, (ratio, message) => reporter?.Invoke(completedRatio + ratio / count, message), copyProgressMessage);
  105. i++;
  106. }
  107. reporter?.Invoke(1, copyProgressMessage);
  108. }
  109. public static void CopyFileWithProgress(string sourceFile, string destinationFile, Action<double, string> reporter, string copyProgressMessage = "Copying")
  110. {
  111. const int bufferSize = 1024 * 1024; // 1MB buffer
  112. byte[] buffer = new byte[bufferSize];
  113. int bytesRead;
  114. using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read))
  115. {
  116. long totalBytes = sourceStream.Length;
  117. using (FileStream destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write))
  118. {
  119. while ((bytesRead = sourceStream.Read(buffer, 0, bufferSize)) > 0)
  120. {
  121. destinationStream.Write(buffer, 0, bytesRead);
  122. reporter?.Invoke((double)destinationStream.Position / totalBytes, copyProgressMessage);
  123. }
  124. }
  125. }
  126. }
  127. public static double ParseDouble(string source, ref int startIndex, bool fastSimpleNumbers)
  128. {
  129. #if true
  130. if (fastSimpleNumbers)
  131. {
  132. return ParseDoubleFast(source, ref startIndex);
  133. }
  134. return GetNextNumber(source, ref startIndex);
  135. #else
  136. int startIndexNew = startIndex;
  137. double newNumber = agg_basics.ParseDoubleFast(source, ref startIndexNew);
  138. int startIndexOld = startIndex;
  139. double oldNumber = GetNextNumber(source, ref startIndexOld);
  140. if (Math.Abs(newNumber - oldNumber) > .0001
  141. || startIndexNew != startIndexOld)
  142. {
  143. int a = 0;
  144. }
  145. startIndex = startIndexNew;
  146. return newNumber;
  147. #endif
  148. }
  149. private static Regex fileNameNumberMatch = new Regex("\\(\\d+\\)\\s*$", RegexOptions.Compiled);
  150. private static Regex fileNameUnderscoreNumberMatch = new Regex("_\\d+\\s*$", RegexOptions.Compiled);
  151. public static string GetNonCollidingFileName(string pathAndFilename)
  152. {
  153. string extension = Path.GetExtension(pathAndFilename);
  154. // get the file name without the path
  155. string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(Path.GetFileName(pathAndFilename));
  156. var directory = Path.GetDirectoryName(pathAndFilename);
  157. var existingSimilarFilenames = Directory.GetFiles(directory, "*.*");
  158. // remove all the file extensions
  159. existingSimilarFilenames = existingSimilarFilenames.Select(f => Path.GetFileNameWithoutExtension(f)).ToArray();
  160. var validName = Util.GetNonCollidingName(fileNameWithoutExtension, new HashSet<string>(existingSimilarFilenames));
  161. // return the validName with path and extension
  162. return Path.Combine(directory, validName) + extension;
  163. }
  164. public static string GetNonCollidingName(string desiredName, HashSet<string> listToCheck, bool lookForParens = true)
  165. {
  166. return GetNonCollidingName(desiredName, (name) => !listToCheck.Contains(name), lookForParens);
  167. }
  168. public static string GetNonCollidingName(string desiredName, Func<string, bool> isUnique, bool lookForParens = true)
  169. {
  170. if (desiredName == null)
  171. {
  172. desiredName = "No Name";
  173. }
  174. if (isUnique(desiredName))
  175. {
  176. return desiredName;
  177. }
  178. else
  179. {
  180. if (lookForParens)
  181. {
  182. // Drop bracketed number sections from our source filename to ensure we don't generate something like "file (1) (1).amf"
  183. if (desiredName.Contains("("))
  184. {
  185. desiredName = fileNameNumberMatch.Replace(desiredName, "").Trim();
  186. }
  187. int nextNumberToTry = 1;
  188. string candidateName;
  189. do
  190. {
  191. candidateName = $"{desiredName} ({nextNumberToTry++})";
  192. } while (!isUnique(candidateName));
  193. return candidateName;
  194. }
  195. else
  196. {
  197. // Drop the number sections from our source filename to ensure we don't generate something like "file_1_1.amf"
  198. desiredName = fileNameUnderscoreNumberMatch.Replace(desiredName, "").Trim();
  199. int nextNumberToTry = 1;
  200. string candidateName;
  201. do
  202. {
  203. candidateName = $"{desiredName}_{nextNumberToTry++}";
  204. } while (!isUnique(candidateName));
  205. return candidateName;
  206. }
  207. }
  208. }
  209. private static double ParseDoubleFast(string source, ref int startIndex)
  210. {
  211. int length = source.Length;
  212. bool negative = false;
  213. long currentIntPart = 0;
  214. int fractionDigits = 0;
  215. long currentFractionPart = 0;
  216. bool foundNumber = false;
  217. // find the number start
  218. while (startIndex < length)
  219. {
  220. char next = source[startIndex];
  221. if (next == '.' || next == '-' || next == '+' || (next >= '0' && next <= '9'))
  222. {
  223. if (next == '.')
  224. {
  225. break;
  226. }
  227. if (next == '-')
  228. {
  229. negative = true;
  230. }
  231. else if (next == '+')
  232. {
  233. // this does nothing but lets us get to the else for numbers
  234. }
  235. else
  236. {
  237. currentIntPart = next - '0';
  238. foundNumber = true;
  239. }
  240. startIndex++;
  241. break;
  242. }
  243. startIndex++;
  244. }
  245. // accumulate the int part
  246. while (startIndex < length)
  247. {
  248. char next = source[startIndex];
  249. if (next >= '0' && next <= '9')
  250. {
  251. currentIntPart = (currentIntPart * 10) + next - '0';
  252. foundNumber = true;
  253. }
  254. else if (next == '.')
  255. {
  256. foundNumber = true;
  257. startIndex++;
  258. // parse out the fractional part
  259. while (startIndex < length)
  260. {
  261. char nextFraction = source[startIndex];
  262. if (nextFraction >= '0' && nextFraction <= '9')
  263. {
  264. fractionDigits++;
  265. currentFractionPart = (currentFractionPart * 10) + nextFraction - '0';
  266. }
  267. else // we are done
  268. {
  269. break;
  270. }
  271. startIndex++;
  272. }
  273. break;
  274. }
  275. else if (!foundNumber && next == ' ')
  276. {
  277. // happy to skip spaces
  278. }
  279. else // we are done
  280. {
  281. break;
  282. }
  283. startIndex++;
  284. }
  285. if (fractionDigits > 0)
  286. {
  287. double fractionNumber = currentIntPart + (currentFractionPart / Math.Pow(10.0, fractionDigits));
  288. if (negative)
  289. {
  290. return -fractionNumber;
  291. }
  292. return fractionNumber;
  293. }
  294. else
  295. {
  296. if (negative)
  297. {
  298. return -currentIntPart;
  299. }
  300. return currentIntPart;
  301. }
  302. }
  303. public static int Clamp(int value, int min, int max)
  304. {
  305. bool changed = false;
  306. return Clamp(value, min, max, ref changed);
  307. }
  308. public static int Clamp(int value, int min, int max, ref bool changed)
  309. {
  310. min = Math.Min(min, max);
  311. if (value < min)
  312. {
  313. value = min;
  314. changed = true;
  315. }
  316. if (value > max)
  317. {
  318. value = max;
  319. changed = true;
  320. }
  321. return value;
  322. }
  323. public static double Clamp(double value, double min, double max)
  324. {
  325. bool changed = false;
  326. return Clamp(value, min, max, ref changed);
  327. }
  328. public static double Clamp(double value, double min, double max, ref bool changed)
  329. {
  330. min = Math.Min(min, max);
  331. if (value < min)
  332. {
  333. value = min;
  334. changed = true;
  335. }
  336. if (value > max)
  337. {
  338. value = max;
  339. changed = true;
  340. }
  341. return value;
  342. }
  343. public static byte[] GetBytes(string str)
  344. {
  345. byte[] bytes = new byte[str.Length * sizeof(char)];
  346. System.Buffer.BlockCopy(str.ToCharArray(), 0, bytes, 0, bytes.Length);
  347. return bytes;
  348. }
  349. public static ulong GetLongHashCode(this string data, ulong hash = 14695981039346656037)
  350. {
  351. return ComputeHash(GetBytes(data), hash);
  352. }
  353. public static ulong GetLongHashCode(this int data, ulong hash = 14695981039346656037)
  354. {
  355. return ComputeHash(BitConverter.GetBytes(data), hash);
  356. }
  357. public static ulong GetLongHashCode(this double data, ulong hash = 14695981039346656037)
  358. {
  359. return ComputeHash(BitConverter.GetBytes(data), hash);
  360. }
  361. public static ulong GetLongHashCode(this ulong data, ulong hash = 14695981039346656037)
  362. {
  363. return ComputeHash(BitConverter.GetBytes(data), hash);
  364. }
  365. public static ulong GetLongHashCode(this long data, ulong hash = 14695981039346656037)
  366. {
  367. return ComputeHash(BitConverter.GetBytes(data), hash);
  368. }
  369. public static ulong GetLongHashCode(this byte[] data, ulong hash = 14695981039346656037)
  370. {
  371. return ComputeHash(data, hash);
  372. }
  373. // FNV-1a (64-bit) non-cryptographic hash function.
  374. // Adapted from: http://github.com/jakedouglas/fnv-java
  375. public static ulong ComputeHash(byte[] bytes, ulong hash = 14695981039346656037)
  376. {
  377. const ulong fnv64Prime = 0x100000001b3;
  378. for (var i = 0; i < bytes.Length; i++)
  379. {
  380. hash = hash ^ bytes[i];
  381. hash *= fnv64Prime;
  382. }
  383. return hash;
  384. }
  385. public static void memcpy(int[] dest, int destIndex, int[] source, int sourceIndex, int count)
  386. {
  387. for (int i = 0; i < count; i++)
  388. {
  389. dest[destIndex + i] = source[sourceIndex + i];
  390. }
  391. }
  392. public static void memcpy(float[] dest, int destIndex, float[] source, int sourceIndex, int count)
  393. {
  394. for (int i = 0; i < count; i++)
  395. {
  396. dest[destIndex++] = source[sourceIndex++];
  397. }
  398. }
  399. public static void memmove(byte[] dest, int destIndex, byte[] source, int sourceIndex, int count)
  400. {
  401. if (dest == source
  402. && destIndex == sourceIndex)
  403. {
  404. // there is nothing to do it is already the same
  405. return;
  406. }
  407. if (source != dest
  408. || destIndex < sourceIndex)
  409. {
  410. memcpy(dest, destIndex, source, sourceIndex, count);
  411. }
  412. else
  413. {
  414. for (int i = count - 1; i >= 0; i--)
  415. {
  416. dest[destIndex + i] = source[sourceIndex + i];
  417. }
  418. }
  419. }
  420. public static void memmove(int[] dest, int destIndex, int[] source, int sourceIndex, int count)
  421. {
  422. if (source != dest
  423. || destIndex < sourceIndex)
  424. {
  425. memcpy(dest, destIndex, source, sourceIndex, count);
  426. }
  427. else
  428. {
  429. throw new Exception("this code needs to be tested");
  430. /*
  431. for (int i = Count-1; i > 0; i--)
  432. {
  433. dest[destIndex + i] = source[sourceIndex + i];
  434. }
  435. */
  436. }
  437. }
  438. public static void memmove(float[] dest, int destIndex, float[] source, int sourceIndex, int count)
  439. {
  440. if (source != dest
  441. || destIndex < sourceIndex)
  442. {
  443. memcpy(dest, destIndex, source, sourceIndex, count);
  444. }
  445. else
  446. {
  447. throw new Exception("this code needs to be tested");
  448. /*
  449. for (int i = Count-1; i > 0; i--)
  450. {
  451. dest[destIndex + i] = source[sourceIndex + i];
  452. }
  453. */
  454. }
  455. }
  456. public static void memset(int[] dest, int destIndex, int val, int count)
  457. {
  458. for (int i = 0; i < count; i++)
  459. {
  460. dest[destIndex + i] = val;
  461. }
  462. }
  463. public static void memset(byte[] dest, int destIndex, byte byteVal, int count)
  464. {
  465. for (int i = 0; i < count; i++)
  466. {
  467. dest[destIndex + i] = byteVal;
  468. }
  469. }
  470. public static void MemClear(int[] dest, int destIndex, int count)
  471. {
  472. for (int i = 0; i < count; i++)
  473. {
  474. dest[destIndex + i] = 0;
  475. }
  476. }
  477. public static void MemClear(byte[] dest, int destIndex, int count)
  478. {
  479. for (int i = 0; i < count; i++)
  480. {
  481. dest[destIndex + i] = 0;
  482. }
  483. /*
  484. // dword align to dest
  485. while (((int)pDest & 3) != 0
  486. && Count > 0)
  487. {
  488. *pDest++ = 0;
  489. Count--;
  490. }
  491. int NumLongs = Count / 4;
  492. while (NumLongs-- > 0)
  493. {
  494. *((int*)pDest) = 0;
  495. pDest += 4;
  496. }
  497. switch (Count & 3)
  498. {
  499. case 3:
  500. pDest[2] = 0;
  501. goto case 2;
  502. case 2:
  503. pDest[1] = 0;
  504. goto case 1;
  505. case 1:
  506. pDest[0] = 0;
  507. break;
  508. }
  509. */
  510. }
  511. public static bool is_equal_eps(double v1, double v2, double epsilon)
  512. {
  513. return Math.Abs(v1 - v2) <= (double)epsilon;
  514. }
  515. //------------------------------------------------------------------deg2rad
  516. public static double deg2rad(double deg)
  517. {
  518. return deg * Math.PI / 180.0;
  519. }
  520. //------------------------------------------------------------------rad2deg
  521. public static double rad2deg(double rad)
  522. {
  523. return rad * 180.0 / Math.PI;
  524. }
  525. public static int iround(double v)
  526. {
  527. unchecked
  528. {
  529. return (int)((v < 0.0) ? v - 0.5 : v + 0.5);
  530. }
  531. }
  532. public static int iround(double v, int saturationLimit)
  533. {
  534. if (v < (double)-saturationLimit)
  535. {
  536. return -saturationLimit;
  537. }
  538. if (v > (double)saturationLimit)
  539. {
  540. return saturationLimit;
  541. }
  542. return iround(v);
  543. }
  544. public static int uround(double v)
  545. {
  546. return (int)(uint)(v + 0.5);
  547. }
  548. public static int ufloor(double v)
  549. {
  550. return (int)(uint)v;
  551. }
  552. public static int uceil(double v)
  553. {
  554. return (int)(uint)Math.Ceiling(v);
  555. }
  556. //----------------------------------------------------poly_subpixel_scale_e
  557. // These constants determine the subpixel accuracy, to be more precise,
  558. // the number of bits of the fractional part of the coordinates.
  559. // The possible coordinate capacity in bits can be calculated by formula:
  560. // sizeof(int) * 8 - poly_subpixel_shift, i.e, for 32-bit integers and
  561. // 8-bits fractional part the capacity is 24 bits.
  562. public enum poly_subpixel_scale_e
  563. {
  564. poly_subpixel_shift = 8, //----poly_subpixel_shift
  565. poly_subpixel_scale = 1 << poly_subpixel_shift, //----poly_subpixel_scale
  566. poly_subpixel_mask = poly_subpixel_scale - 1, //----poly_subpixel_mask
  567. }
  568. public static ImageBuffer TrasparentToColorGradientX(int width, int height, Color color, int distance)
  569. {
  570. var innerGradient = new gradient_x();
  571. var outerGradient = new gradient_clamp_adaptor(innerGradient); // gradient_repeat_adaptor/gradient_reflect_adaptor/gradient_clamp_adaptor
  572. var rect = new RoundedRect(new RectangleDouble(0, 0, width, height), 0);
  573. var ras = new ScanlineRasterizer();
  574. ras.add_path(rect);
  575. var imageBuffer = new ImageBuffer(width, height);
  576. imageBuffer.SetRecieveBlender(new BlenderPreMultBGRA());
  577. var scanlineRenderer = new ScanlineRenderer();
  578. scanlineRenderer.GenerateAndRender(
  579. ras,
  580. new scanline_unpacked_8(),
  581. imageBuffer,
  582. new span_allocator(),
  583. new span_gradient(
  584. new span_interpolator_linear(Affine.NewIdentity()),
  585. outerGradient,
  586. new GradientColors(Enumerable.Range(0, 256).Select(i => new Color(color, i))), // steps from full transparency color to solid color
  587. 0,
  588. distance));
  589. return imageBuffer;
  590. }
  591. private class GradientColors : IColorFunction
  592. {
  593. private List<Color> colors;
  594. public GradientColors(IEnumerable<Color> colors)
  595. {
  596. this.colors = new List<Color>(colors);
  597. }
  598. public Color this[int v] => colors[v];
  599. public int size() => colors.Count;
  600. }
  601. public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref int readValue, int startIndex = 0, string stopCheckingString = ";")
  602. {
  603. return GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref readValue, out _, startIndex, stopCheckingString);
  604. }
  605. public static string GetLineWithoutChecksum(string inLine)
  606. {
  607. if (inLine.StartsWith("N"))
  608. {
  609. int lineNumber = 0;
  610. if (Util.GetFirstNumberAfter("N", inLine, ref lineNumber, out int numberEnd))
  611. {
  612. var outLine = inLine.Substring(numberEnd).Trim();
  613. int checksumStart = outLine.IndexOf('*');
  614. if (checksumStart != -1)
  615. {
  616. return outLine.Substring(0, checksumStart);
  617. }
  618. }
  619. }
  620. return inLine;
  621. }
  622. public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref int readValue, out int numberEnd, int startIndex = 0, string stopCheckingString = ";")
  623. {
  624. double doubleValue = readValue;
  625. if (GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref doubleValue, out numberEnd, startIndex, stopCheckingString))
  626. {
  627. readValue = (int)doubleValue;
  628. return true;
  629. }
  630. return false;
  631. }
  632. public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref double readValue, int startIndex = 0, string stopCheckingString = ";")
  633. {
  634. return GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref readValue, out _, startIndex, stopCheckingString);
  635. }
  636. public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref double readValue, out int numberEnd, int startIndex = 0, string stopCheckingString = ";")
  637. {
  638. int stringPos = stringWithNumber.IndexOf(stringToCheckAfter, Math.Min(stringWithNumber.Length, startIndex));
  639. int stopPos = stringWithNumber.IndexOf(stopCheckingString);
  640. if (stringPos != -1
  641. && (stopPos == -1 || stringPos < stopPos || string.IsNullOrEmpty(stopCheckingString)))
  642. {
  643. stringPos += stringToCheckAfter.Length;
  644. readValue = Util.ParseDouble(stringWithNumber, ref stringPos, true);
  645. numberEnd = stringPos;
  646. return true;
  647. }
  648. numberEnd = -1;
  649. return false;
  650. }
  651. public static bool GetFirstStringAfter(string stringToCheckAfter, string fullStringToLookIn, string separatorString, ref string nextString, int startIndex = 0)
  652. {
  653. int stringPos = fullStringToLookIn.IndexOf(stringToCheckAfter, startIndex);
  654. if (stringPos != -1)
  655. {
  656. int separatorPos = fullStringToLookIn.IndexOf(separatorString, stringPos);
  657. if (separatorPos != -1)
  658. {
  659. nextString = fullStringToLookIn.Substring(stringPos + stringToCheckAfter.Length, separatorPos - (stringPos + stringToCheckAfter.Length));
  660. return true;
  661. }
  662. }
  663. return false;
  664. }
  665. public static string ReplaceNumberAfter(char charToReplaceAfter, string stringWithNumber, double numberToPutIn)
  666. {
  667. int charPos = stringWithNumber.IndexOf(charToReplaceAfter);
  668. if (charPos != -1)
  669. {
  670. int spacePos = stringWithNumber.IndexOf(" ", charPos);
  671. if (spacePos == -1)
  672. {
  673. string newString = string.Format("{0}{1:0.#####}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn);
  674. return newString;
  675. }
  676. else
  677. {
  678. string newString = string.Format("{0}{1:0.#####}{2}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn, stringWithNumber.Substring(spacePos));
  679. return newString;
  680. }
  681. }
  682. return stringWithNumber;
  683. }
  684. }
  685. }