GCodeChecker.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. #include "GCodeChecker.h"
  2. #include <fstream>
  3. #include <math.h>
  4. #include <map>
  5. namespace BambuStudio {
  6. //BBS: only check wodth when dE is longer than this value
  7. const double CHECK_WIDTH_E_THRESHOLD = 0.0025;
  8. const double WIDTH_THRESHOLD = 0.012;
  9. const double RADIUS_THRESHOLD = 0.005;
  10. const double filament_diameter = 1.75;
  11. const double Pi = 3.14159265358979323846;
  12. const std::string Extrusion_Role_Tag = " FEATURE: ";
  13. const std::string Width_Tag = " LINE_WIDTH: ";
  14. const std::string Wipe_Start_Tag = " WIPE_START";
  15. const std::string Wipe_End_Tag = " WIPE_END";
  16. const std::string Layer_Change_Tag = " CHANGE_LAYER";
  17. const std::string Height_Tag = " LAYER_HEIGHT: ";
  18. GCodeCheckResult GCodeChecker::parse_file(const std::string& path)
  19. {
  20. std::ifstream file(path);
  21. if (file.fail()) {
  22. std::cout << "Failed to open file " << path << std::endl;
  23. return GCodeCheckResult::ParseFailed;
  24. }
  25. std::string line_raw;
  26. std::string line;
  27. while (std::getline(file, line_raw)) {
  28. const char *c = line_raw.c_str();
  29. c = skip_whitespaces(c);
  30. if (std::toupper(*c) == 'N')
  31. c = skip_word(c);
  32. c = skip_whitespaces(c);
  33. line = c;
  34. if (parse_line(line) != GCodeCheckResult::Success) {
  35. std::cout << "Failed to parse line " << line_raw << std::endl;
  36. return GCodeCheckResult::ParseFailed;
  37. }
  38. }
  39. if (m_layer_num == 0) {
  40. std::cout << "Invalid gcode file without layer change comment" << std::endl;
  41. return GCodeCheckResult::ParseFailed;
  42. }
  43. return GCodeCheckResult::Success;
  44. }
  45. bool GCodeChecker::include_chinese(const char* str)
  46. {
  47. char c;
  48. while(1)
  49. {
  50. c=*str++;
  51. if (is_end_of_line(c))
  52. break;
  53. if ((c & 0x80) && (*str & 0x80))
  54. return true;
  55. }
  56. return false;
  57. }
  58. GCodeCheckResult GCodeChecker::parse_line(const std::string& line)
  59. {
  60. // update start position
  61. m_start_position = m_end_position;
  62. GCodeCheckResult ret;
  63. const char *c = skip_whitespaces(line.c_str());
  64. if (include_chinese(c)) {
  65. //chinese is forbidden
  66. return GCodeCheckResult::ParseFailed;
  67. } if (is_end_of_line(*c)) {
  68. //BBS: skip empty line
  69. return GCodeCheckResult::Success;
  70. } else if (is_comment_line(*c)) {
  71. GCodeLine gcode_line;
  72. gcode_line.m_raw = c;
  73. ret = parse_comment(gcode_line);
  74. if (ret != GCodeCheckResult::Success)
  75. return ret;
  76. } else {
  77. GCodeLine gcode_line;
  78. gcode_line.m_raw = c;
  79. ret = parse_command(gcode_line);
  80. if (ret != GCodeCheckResult::Success)
  81. return ret;
  82. ret = check_line_width(gcode_line);
  83. if (ret != GCodeCheckResult::Success)
  84. return ret;
  85. }
  86. return GCodeCheckResult::Success;
  87. }
  88. GCodeCheckResult GCodeChecker::parse_comment(GCodeLine& line)
  89. {
  90. const char *c = line.m_raw.c_str();
  91. c++;
  92. std::string comment = c;
  93. // extrusion role tag
  94. if (starts_with(comment, Extrusion_Role_Tag)) {
  95. m_role = string_to_role(comment.substr(Extrusion_Role_Tag.length()));
  96. } else if (starts_with(comment, Wipe_Start_Tag)) {
  97. m_wiping = true;
  98. } else if (starts_with(comment, Wipe_End_Tag)) {
  99. m_wiping = false;
  100. } else if (starts_with(comment, Height_Tag)) {
  101. std::string str = comment.substr(Height_Tag.size());
  102. if (!parse_double_from_str(str, m_height)) {
  103. std::cout << "invalid height comment with invalid value!" << std::endl;
  104. return GCodeCheckResult::ParseFailed;
  105. }
  106. } else if (starts_with(comment, Width_Tag)) {
  107. std::string str = comment.substr(Width_Tag.size());
  108. if (!parse_double_from_str(str, m_width)) {
  109. std::cout << "invalid width comment with invalid value!" << std::endl;
  110. return GCodeCheckResult::ParseFailed;
  111. }
  112. } else if (starts_with(comment, Layer_Change_Tag)) {
  113. m_layer_num++;
  114. }
  115. return GCodeCheckResult::Success;
  116. }
  117. GCodeCheckResult GCodeChecker::parse_command(GCodeLine& gcode_line)
  118. {
  119. const std::string cmd = gcode_line.cmd();
  120. GCodeCheckResult ret = GCodeCheckResult::Success;
  121. switch (::toupper(cmd[0])) {
  122. case 'G':
  123. {
  124. switch (::atoi(&cmd[1]))
  125. {
  126. case 0:
  127. case 1: { ret = parse_G0_G1(gcode_line); break; } // Move
  128. case 2:
  129. case 3: { ret = parse_G2_G3(gcode_line); break; } // Move
  130. case 90: { ret = parse_G90(gcode_line); break; } // Set to Absolute Positioning
  131. case 91: { ret = parse_G91(gcode_line); break; } // Set to Relative Positioning
  132. case 92: { ret = parse_G92(gcode_line); break; } // Set Position
  133. default: { break; }
  134. }
  135. break;
  136. }
  137. case 'M':{
  138. switch (::atoi(&cmd[1]))
  139. {
  140. case 82: { ret = parse_M82(gcode_line); break; } // Set to Absolute extrusion
  141. case 83: { ret = parse_M83(gcode_line); break; } // Set to Relative extrusion
  142. default: { break; }
  143. }
  144. break;
  145. }
  146. case 'T':{
  147. break;
  148. }
  149. default: {
  150. //BBS: other g command? impossible! must be invalid
  151. ret = GCodeCheckResult::ParseFailed;
  152. break;
  153. }
  154. }
  155. return ret;
  156. }
  157. GCodeCheckResult GCodeChecker::parse_axis(GCodeLine& gcode_line)
  158. {
  159. const std::string cmd = gcode_line.m_raw;
  160. const char* c = cmd.c_str();
  161. c = skip_word(c);
  162. while (! is_end_of_gcode_line(*c)) {
  163. c = skip_whitespaces(c);
  164. if (is_end_of_gcode_line(*c))
  165. break;
  166. Axis axis = UNKNOWN_AXIS;
  167. switch (*c) {
  168. case 'X': axis = X; break;
  169. case 'Y': axis = Y; break;
  170. case 'Z': axis = Z; break;
  171. case 'E': axis = E; break;
  172. case 'F': axis = F; break;
  173. case 'I': axis = I; break;
  174. case 'J': axis = J; break;
  175. default:
  176. //BBS: invalid command which has invalid axis
  177. std::cout << "Invalid gcode because of invalid axis!" << std::endl;
  178. return GCodeCheckResult::ParseFailed;
  179. }
  180. char *pend = nullptr;
  181. double v = strtod(++c, &pend);
  182. if (pend != nullptr && is_end_of_word(*pend) && !isnan(v) && !isinf(v)) {
  183. gcode_line.m_axis[int(axis)] = v;
  184. if (gcode_line.m_mask & (1 << int(axis))) {
  185. //BBS: invalid command which has duplicated axis
  186. std::cout << "Invalid gcode because of duplicated axis!" << std::endl;
  187. return GCodeCheckResult::ParseFailed;
  188. } else {
  189. gcode_line.m_mask |= 1 << int(axis);
  190. }
  191. if (c == pend) {
  192. //BBS: invalid command which has invalid axis value
  193. std::cout << "Invalid gcode because of invalid axis value!" << std::endl;
  194. return GCodeCheckResult::ParseFailed;
  195. }
  196. c = pend;
  197. } else {
  198. //BBS: invalid command for invalid axis value
  199. std::cout << "Invalid gcode because of invalid axis value!" << std::endl;
  200. return GCodeCheckResult::ParseFailed;
  201. }
  202. }
  203. return GCodeCheckResult::Success;
  204. }
  205. GCodeCheckResult GCodeChecker::parse_G0_G1(GCodeLine& gcode_line)
  206. {
  207. if (parse_axis(gcode_line) != GCodeCheckResult::Success)
  208. return GCodeCheckResult::ParseFailed;
  209. //BBS: invalid G1 command which has no axis or invalid axis
  210. if ((!gcode_line.m_mask) ||
  211. gcode_line.has(I) ||
  212. gcode_line.has(J)) {
  213. std::cout << "Invalid G0_G1 gcode because of no axis or invalid axis!" << std::endl;
  214. return GCodeCheckResult::ParseFailed;
  215. }
  216. //BBS: invalid G1 command which has zero speed
  217. if (gcode_line.has(F) && gcode_line.get(F) == 0.0) {
  218. std::cout << "Invalid G0_G1 gcode because has F axis but 0 speed!" << std::endl;
  219. return GCodeCheckResult::ParseFailed;
  220. }
  221. return GCodeCheckResult::Success;
  222. }
  223. GCodeCheckResult GCodeChecker::parse_G2_G3(GCodeLine& gcode_line)
  224. {
  225. if (parse_axis(gcode_line) != GCodeCheckResult::Success)
  226. return GCodeCheckResult::ParseFailed;
  227. //BBS: invalid G2_G3 command which has no axis or Z axis
  228. if (!gcode_line.m_mask) {
  229. std::cout << "Invalid G2_G3 gcode because of no axis or has Z axis!" << std::endl;
  230. return GCodeCheckResult::ParseFailed;
  231. }
  232. //BBS: invalid G2_G3 command which has zero speed
  233. if (gcode_line.has(F) && gcode_line.get(F) == 0.0) {
  234. std::cout << "Invalid G2_G3 gcode because has F axis but 0 speed!" << std::endl;
  235. return GCodeCheckResult::ParseFailed;
  236. }
  237. //BBS: invalid G2_G3 command which has no I and J axis
  238. if (!gcode_line.has(I) &&
  239. !gcode_line.has(J)) {
  240. std::cout << "Invalid G2_G3 gcode because of no I and J axis at same time!" << std::endl;
  241. return GCodeCheckResult::ParseFailed;
  242. }
  243. //BBS: invalid G2_G3 command which has no X and Y axis at same time
  244. if (!gcode_line.has(X) &&
  245. !gcode_line.has(Y)) {
  246. if (!gcode_line.has(X) || !gcode_line.has(P) || (int)gcode_line.get(P) != 1) {
  247. std::cout << "Invalid G2_G3 gcode because of no X and Y axis at same time!" << std::endl;
  248. return GCodeCheckResult::ParseFailed;
  249. }
  250. }
  251. return GCodeCheckResult::Success;
  252. }
  253. GCodeCheckResult GCodeChecker::parse_G90(const GCodeLine& gcode_line)
  254. {
  255. const char* c = gcode_line.m_raw.c_str();
  256. //BBS: G90 is single command with no argument
  257. if (!is_single_gcode_word(c)) {
  258. std::cout << "Invalid G90 gcode with invalid end!" << std::endl;
  259. return GCodeCheckResult::ParseFailed;
  260. }
  261. m_global_positioning_type = EPositioningType::Absolute;
  262. return GCodeCheckResult::Success;
  263. }
  264. GCodeCheckResult GCodeChecker::parse_G91(const GCodeLine& gcode_line)
  265. {
  266. const char* c = gcode_line.m_raw.c_str();
  267. //BBS: G91 is single command with no argument
  268. if (!is_single_gcode_word(c)) {
  269. std::cout << "Invalid G91 gcode with invalid end!" << std::endl;
  270. return GCodeCheckResult::ParseFailed;
  271. }
  272. m_global_positioning_type = EPositioningType::Relative;
  273. return GCodeCheckResult::Success;
  274. }
  275. GCodeCheckResult GCodeChecker::parse_G92(GCodeLine& gcode_line)
  276. {
  277. if (parse_axis(gcode_line) != GCodeCheckResult::Success)
  278. return GCodeCheckResult::ParseFailed;
  279. //BBS: invalid G92 command which has no axis or invalid axis
  280. if (!gcode_line.m_mask ||
  281. gcode_line.has(F) ||
  282. gcode_line.has(I) ||
  283. gcode_line.has(J)) {
  284. std::cout << "Invalid G2_G3 gcode because of no axis or invalid axis!" << std::endl;
  285. return GCodeCheckResult::ParseFailed;
  286. }
  287. if (gcode_line.has(X))
  288. m_origin[X] = m_end_position[X] - gcode_line.get(X);
  289. if (gcode_line.has(Y))
  290. m_origin[Y] = m_end_position[Y] - gcode_line.get(Y);
  291. if (gcode_line.has(Z))
  292. m_origin[Z] = m_end_position[Z] - gcode_line.get(Z);
  293. if (gcode_line.has(E))
  294. m_end_position[E] = gcode_line.get(E);
  295. for (unsigned char a = X; a <= E; ++a) {
  296. m_origin[a] = m_end_position[a];
  297. }
  298. return GCodeCheckResult::Success;
  299. }
  300. GCodeCheckResult GCodeChecker::parse_M82(const GCodeLine& gcode_line)
  301. {
  302. const char* c = gcode_line.m_raw.c_str();
  303. //BBS: M82 is single command with no argument
  304. if (!is_single_gcode_word(c)) {
  305. std::cout << "Invalid M82 gcode with invalid end!" << std::endl;
  306. return GCodeCheckResult::ParseFailed;
  307. }
  308. m_e_local_positioning_type = EPositioningType::Absolute;
  309. return GCodeCheckResult::Success;
  310. }
  311. GCodeCheckResult GCodeChecker::parse_M83(const GCodeLine& gcode_line)
  312. {
  313. const char* c = gcode_line.m_raw.c_str();
  314. //BBS: M83 is single command with no argument
  315. if (!is_single_gcode_word(c)) {
  316. std::cout << "Invalid M83 gcode with invalid end!" << std::endl;
  317. return GCodeCheckResult::ParseFailed;
  318. }
  319. m_e_local_positioning_type = EPositioningType::Relative;
  320. return GCodeCheckResult::Success;
  321. }
  322. double GCodeChecker::calculate_G1_width(const std::array<double, 3>& source,
  323. const std::array<double, 3>& target,
  324. double e, double height, bool is_bridge) const
  325. {
  326. double volume = e * Pi * (filament_diameter/2.0f) * (filament_diameter/2.0f);
  327. std::array<double, 3> delta = { target[0] - source[0],
  328. target[1] - source[1],
  329. target[2] - source[2] };
  330. double length = sqrt(delta[0] * delta[0] + delta[1] * delta[1] + delta[2] * delta[2]);
  331. double mm3_per_mm = volume / length;
  332. return is_bridge? 2 * sqrt(mm3_per_mm/Pi) :
  333. (mm3_per_mm / height) + height * (1 - 0.25 * Pi);
  334. }
  335. double GCodeChecker::calculate_G2_G3_width(const std::array<double, 2>& source,
  336. const std::array<double, 2>& target,
  337. const std::array<double, 2>& center,
  338. bool is_ccw, double e, double height,
  339. bool is_bridge) const
  340. {
  341. std::array<double, 2> v1 = { source[0] - center[0], source[1] - center[1] };
  342. std::array<double, 2> v2 = { target[0] - center[0], target[1] - center[1] };
  343. double dot = v1[0] * v2[0] + v1[1] * v2[1];
  344. double cross = v1[0] * v2[1] - v1[1] * v2[0];
  345. double radian = atan2(cross, dot);
  346. radian = is_ccw ?
  347. (radian < 0 ? 2 * Pi + radian : radian) :
  348. (radian < 0 ? -radian : 2 * Pi - radian);
  349. double radius = sqrt(v1[0] * v1[0] + v1[1] * v1[1]);
  350. double length = radius * radian;
  351. double volume = e * Pi * (filament_diameter/2) * (filament_diameter/2);
  352. double mm3_per_mm = volume / length;
  353. return is_bridge? 2 * sqrt(mm3_per_mm/Pi) :
  354. (mm3_per_mm / height) + height * (1 - 0.25 * Pi);
  355. }
  356. GCodeCheckResult GCodeChecker::check_line_width(const GCodeLine& gcode_line)
  357. {
  358. //BBS: don't need to check extrusion before first layer
  359. if (m_layer_num <= 0) {
  360. return GCodeCheckResult::Success;
  361. }
  362. GCodeCheckResult ret = GCodeCheckResult::Success;
  363. //BBS: only need to handle G0 G1 G2 G3
  364. const std::string cmd = gcode_line.cmd();
  365. int cmd_id = ::atoi(&cmd[1]);
  366. if (::toupper(cmd[0]) == 'G')
  367. switch (::atoi(&cmd[1]))
  368. {
  369. case 0:
  370. case 1: { ret = check_G0_G1_width(gcode_line); break; }
  371. case 2:
  372. case 3: { ret = check_G2_G3_width(gcode_line); break; }
  373. default: { break; }
  374. }
  375. return ret;
  376. }
  377. GCodeCheckResult GCodeChecker::check_G0_G1_width(const GCodeLine& line)
  378. {
  379. auto absolute_position = [this](Axis axis, const GCodeLine& lineG1) {
  380. bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
  381. if (axis == E)
  382. is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
  383. if (lineG1.has(Axis(axis))) {
  384. double ret = lineG1.get(Axis(axis));
  385. return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
  386. } else
  387. return m_start_position[axis];
  388. };
  389. auto move_type = [this](const std::array<double, 4>& delta_pos) {
  390. EMoveType type = EMoveType::Noop;
  391. if (m_wiping)
  392. type = EMoveType::Wipe;
  393. else if (delta_pos[E] < 0.0f)
  394. type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
  395. else if (delta_pos[E] > 0.0f) {
  396. if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
  397. type = (delta_pos[Z] == 0.0f) ? EMoveType::Unretract : EMoveType::Travel;
  398. else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f)
  399. type = EMoveType::Extrude;
  400. }
  401. else if (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f)
  402. type = EMoveType::Travel;
  403. return type;
  404. };
  405. for (unsigned char a = X; a <= E; ++a) {
  406. m_end_position[a] = absolute_position((Axis)a, line);
  407. }
  408. // calculates movement deltas
  409. std::array<double, 4> delta_pos;
  410. for (unsigned char a = X; a <= E; ++a)
  411. delta_pos[a] = m_end_position[a] - m_start_position[a];
  412. // Todo: currently, for precision, there alwasy has possible to generate
  413. // such gcode because of decimal truncation
  414. /*if (line.has(Axis(E)) && delta_pos[E] == 0.0 && !m_wiping) {
  415. std::cout << "Invalid GCode because has E axis but 0 extrusion" << std::endl;
  416. return GCodeCheckResult::CheckFailed;
  417. }*/
  418. EMoveType type = move_type(delta_pos);
  419. if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f)
  420. type = EMoveType::Travel;
  421. //BBS: calculate line width and compare.
  422. //Don't need to check gap fill which has verious width
  423. if (type == EMoveType::Extrude &&
  424. m_role != erGapFill &&
  425. delta_pos[E] > CHECK_WIDTH_E_THRESHOLD) {
  426. std::array<double, 3> source = { m_start_position[X], m_start_position[Y], m_start_position[Z] };
  427. std::array<double, 3> target = { m_end_position[X], m_end_position[Y], m_end_position[Z] };
  428. bool is_bridge = m_role == erOverhangPerimeter || m_role == erBridgeInfill;
  429. double width_real = calculate_G1_width(source, target, delta_pos[E], m_height, is_bridge);
  430. if (fabs(width_real - m_width) > WIDTH_THRESHOLD) {
  431. std::cout << "Invalid G0_G1 because has abnormal line width." << std::endl;
  432. std::cout << "Width: " << m_width << " Width_real: " << width_real << std::endl;
  433. return GCodeCheckResult::CheckFailed;
  434. }
  435. }
  436. return GCodeCheckResult::Success;
  437. }
  438. GCodeCheckResult GCodeChecker::check_G2_G3_width(const GCodeLine& line)
  439. {
  440. auto absolute_position = [this](Axis axis, const GCodeLine& lineG2_3) {
  441. bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
  442. if (axis == E)
  443. is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
  444. if (lineG2_3.has(Axis(axis))) {
  445. double ret = lineG2_3.get(Axis(axis));
  446. if (axis == I)
  447. return m_start_position[X] + ret;
  448. else if (axis == J)
  449. return m_start_position[Y] + ret;
  450. else
  451. return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
  452. } else {
  453. if (axis == I)
  454. return m_start_position[X];
  455. else if (axis == J)
  456. return m_start_position[Y];
  457. else
  458. return m_start_position[axis];
  459. }
  460. };
  461. auto move_type = [this](const double& delta_E) {
  462. EMoveType type = EMoveType::Noop;
  463. if (m_wiping)
  464. type = EMoveType::Wipe;
  465. else if (delta_E < 0.0f || delta_E == 0.0f)
  466. type = EMoveType::Travel;
  467. else
  468. type = EMoveType::Extrude;
  469. return type;
  470. };
  471. for (unsigned char a = X; a <= E; ++a) {
  472. m_end_position[a] = absolute_position((Axis)a, line);
  473. }
  474. std::array<double, 2> source = { m_start_position[X], m_start_position[Y] };
  475. std::array<double, 2> target = { m_end_position[X], m_end_position[Y] };
  476. std::array<double, 2> center = { absolute_position(I, line),absolute_position(J, line) };
  477. const std::string& cmd = line.cmd();
  478. bool is_ccw = (::atoi(&cmd[1]) == 2) ? false : true;
  479. double delta_e = m_end_position[E] - m_start_position[E];
  480. EMoveType type = move_type(delta_e);
  481. //BBS: judge whether is normal arc by radius
  482. double radius1 = sqrt(pow((source[0] - center[0]), 2) + pow((source[1] - center[1]), 2));
  483. double radius2 = sqrt(pow((target[0] - center[0]), 2) + pow((target[1] - center[1]), 2));
  484. if (fabs(radius2 - radius1) > RADIUS_THRESHOLD) {
  485. std::cout << "Invalid G2_G3 because of abnormal radius." << std::endl;
  486. std::cout << "radius1: " << radius1 << " radius2: " << radius2 << std::endl;
  487. return GCodeCheckResult::CheckFailed;
  488. }
  489. //BBS: calculate line width and compare
  490. //Don't need to check gap fill which has verious width
  491. if (type == EMoveType::Extrude &&
  492. m_role != erGapFill &&
  493. delta_e > CHECK_WIDTH_E_THRESHOLD) {
  494. bool is_bridge = m_role == erOverhangPerimeter || m_role == erBridgeInfill;
  495. double width_real = calculate_G2_G3_width(source, target, center, is_ccw, delta_e, m_height, is_bridge);
  496. if (fabs(width_real - m_width) > WIDTH_THRESHOLD) {
  497. std::cout << "Invalid G2_G3 because has abnormal line width." << std::endl;
  498. std::cout << "Width: " << m_width << " Width_real: " << width_real << std::endl;
  499. return GCodeCheckResult::CheckFailed;
  500. }
  501. }
  502. return GCodeCheckResult::Success;
  503. }
  504. const std::map<std::string, ExtrusionRole> string_to_role_map = {
  505. { "Inner wall", erPerimeter },
  506. { "Outer wall", erExternalPerimeter },
  507. { "Overhang wall", erOverhangPerimeter },
  508. { "Sparse infill", erInternalInfill },
  509. { "Internal solid infill", erSolidInfill },
  510. { "Top surface", erTopSolidInfill },
  511. { "Bottom surface", erBottomSurface },
  512. { "Ironing", erIroning },
  513. { "Bridge", erBridgeInfill },
  514. { "Gap infill", erGapFill },
  515. { "Skirt", erSkirt },
  516. { "Brim", erBrim },
  517. { "Support", erSupportMaterial },
  518. { "Support interface", erSupportMaterialInterface },
  519. { "Support transition", erSupportTransition },
  520. { "Prime tower", erWipeTower },
  521. { "Custom", erCustom },
  522. { "Mixed", erMixed }
  523. };
  524. ExtrusionRole GCodeChecker::string_to_role(const std::string &role)
  525. {
  526. for (auto it = string_to_role_map.begin(); it != string_to_role_map.end(); it++) {
  527. if (role == it->first)
  528. return it->second;
  529. }
  530. return erNone;
  531. }
  532. }