2DToolpaths.pm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. package Slic3r::GUI::Plater::2DToolpaths;
  2. use strict;
  3. use warnings;
  4. use utf8;
  5. use List::Util qw();
  6. use Slic3r::Geometry qw();
  7. use Wx qw(:misc :sizer :slider);
  8. use Wx::Event qw(EVT_SLIDER);
  9. use base 'Wx::Panel';
  10. sub new {
  11. my $class = shift;
  12. my ($parent, $print) = @_;
  13. my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
  14. $self->{print} = $print;
  15. my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
  16. my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
  17. $sizer->Add($canvas, 1, wxALL | wxEXPAND, 10);
  18. my $slider = $self->{slider} = Wx::Slider->new(
  19. $self, -1,
  20. 0, # default
  21. 0, # min
  22. $print->total_layer_count-1, # max
  23. wxDefaultPosition,
  24. wxDefaultSize,
  25. wxVERTICAL | wxSL_INVERSE,
  26. );
  27. $sizer->Add($slider, 0, wxALL | wxEXPAND, 10);
  28. EVT_SLIDER($self, $slider, sub {
  29. $canvas->set_layer($slider->GetValue);
  30. });
  31. $self->SetSizer($sizer);
  32. $self->SetMinSize($self->GetSize);
  33. $sizer->SetSizeHints($self);
  34. $canvas->set_layer(0);
  35. return $self;
  36. }
  37. package Slic3r::GUI::Plater::2DToolpaths::Canvas;
  38. use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
  39. use OpenGL qw(:glconstants :glfunctions :glufunctions);
  40. use base qw(Wx::GLCanvas Class::Accessor);
  41. use Wx::GLCanvas qw(:all);
  42. use List::Util qw(min);
  43. use Slic3r::Geometry qw(scale unscale);
  44. __PACKAGE__->mk_accessors(qw(print layer_id init dirty bb));
  45. # make OpenGL::Array thread-safe
  46. {
  47. no warnings 'redefine';
  48. *OpenGL::Array::CLONE_SKIP = sub { 1 };
  49. }
  50. sub new {
  51. my ($class, $parent, $print) = @_;
  52. my $self = $class->SUPER::new($parent);
  53. $self->print($print);
  54. $self->bb($self->print->bounding_box);
  55. EVT_PAINT($self, sub {
  56. my $dc = Wx::PaintDC->new($self);
  57. $self->Render($dc);
  58. });
  59. EVT_SIZE($self, sub { $self->dirty(1) });
  60. EVT_IDLE($self, sub {
  61. return unless $self->dirty;
  62. return if !$self->IsShownOnScreen;
  63. $self->Resize( $self->GetSizeWH );
  64. $self->Refresh;
  65. });
  66. return $self;
  67. }
  68. sub set_layer {
  69. my ($self, $layer_id) = @_;
  70. $self->layer_id($layer_id);
  71. $self->dirty(1);
  72. }
  73. sub Render {
  74. my ($self, $dc) = @_;
  75. # prevent calling SetCurrent() when window is not shown yet
  76. return unless $self->IsShownOnScreen;
  77. return unless my $context = $self->GetContext;
  78. $self->SetCurrent($context);
  79. $self->InitGL;
  80. glMatrixMode(GL_PROJECTION);
  81. glLoadIdentity();
  82. my $bb = $self->bb;
  83. my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
  84. my ($x, $y) = $self->GetSizeWH;
  85. if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
  86. # adjust Y
  87. my $new_y = $y * ($x2 - $x1) / $x;
  88. $y1 = ($y2 + $y1)/2 - $new_y/2;
  89. $y2 = $y1 + $new_y;
  90. } else {
  91. my $new_x = $x * ($y2 - $y1) / $y;
  92. $x1 = ($x2 + $x1)/2 - $new_x/2;
  93. $x2 = $x1 + $new_x;
  94. }
  95. glOrtho($x1, $x2, $y1, $y2, 0, 1);
  96. glDisable(GL_DEPTH_TEST);
  97. glMatrixMode(GL_MODELVIEW);
  98. glLoadIdentity();
  99. glClearColor(1, 1, 1, 0);
  100. glClear(GL_COLOR_BUFFER_BIT);
  101. foreach my $object (@{$self->print->objects}) {
  102. my $layer = $object->get_layer($self->layer_id);
  103. foreach my $layerm (@{$layer->regions}) {
  104. glColor3f(0.7, 0, 0);
  105. $self->_draw_extrusionpath($object, $_) for @{$layerm->perimeters};
  106. glColor3f(0, 0, 0.7);
  107. $self->_draw_extrusionpath($object, $_) for map @$_, @{$layerm->fills};
  108. }
  109. }
  110. glFlush();
  111. $self->SwapBuffers;
  112. }
  113. sub _draw_extrusionpath {
  114. my ($self, $object, $path) = @_;
  115. my $polyline = $path->isa('Slic3r::ExtrusionLoop')
  116. ? $path->polygon->split_at_first_point
  117. : $path->polyline;
  118. glLineWidth(1);
  119. foreach my $copy (@{ $object->_shifted_copies }) {
  120. foreach my $line (@{$polyline->lines}) {
  121. $line->translate(@$copy);
  122. glBegin(GL_LINES);
  123. glVertex2f(@{$line->a});
  124. glVertex2f(@{$line->b});
  125. glEnd();
  126. }
  127. }
  128. }
  129. sub InitGL {
  130. my $self = shift;
  131. return if $self->init;
  132. return unless $self->GetContext;
  133. $self->init(1);
  134. }
  135. sub GetContext {
  136. my ($self) = @_;
  137. if (Wx::wxVERSION >= 2.009) {
  138. return $self->{context} ||= Wx::GLContext->new($self);
  139. } else {
  140. return $self->SUPER::GetContext;
  141. }
  142. }
  143. sub SetCurrent {
  144. my ($self, $context) = @_;
  145. if (Wx::wxVERSION >= 2.009) {
  146. return $self->SUPER::SetCurrent($context);
  147. } else {
  148. return $self->SUPER::SetCurrent;
  149. }
  150. }
  151. sub Resize {
  152. my ($self, $x, $y) = @_;
  153. return unless $self->GetContext;
  154. $self->dirty(0);
  155. $self->SetCurrent($self->GetContext);
  156. my ($x1, $y1, $x2, $y2) = (0, 0, $x, $y);
  157. if (0 && $x > $y) {
  158. $x2 = $y;
  159. $x1 = ($x - $y)/2;
  160. }
  161. if (0 && $y > $x) {
  162. $y2 = $x;
  163. $y1 = ($y - $x)/2;
  164. }
  165. glViewport($x1, $y1, $x2, $y2);
  166. }
  167. sub line {
  168. my (
  169. $x1, $y1, $x2, $y2, # coordinates of the line
  170. $w, # width/thickness of the line in pixel
  171. $Cr, $Cg, $Cb, # RGB color components
  172. $Br, $Bg, $Bb, # color of background when alphablend=false
  173. # Br=alpha of color when alphablend=true
  174. $alphablend, # use alpha blend or not
  175. ) = @_;
  176. my $t;
  177. my $R;
  178. my $f = $w - int($w);
  179. my $A;
  180. if ($alphablend) {
  181. $A = $Br;
  182. } else {
  183. $A = 1;
  184. }
  185. # determine parameters t,R
  186. if ($w >= 0 && $w < 1) {
  187. $t = 0.05; $R = 0.48 + 0.32 * $f;
  188. if (!$alphablend) {
  189. $Cr += 0.88 * (1-$f);
  190. $Cg += 0.88 * (1-$f);
  191. $Cb += 0.88 * (1-$f);
  192. $Cr = 1.0 if ($Cr > 1.0);
  193. $Cg = 1.0 if ($Cg > 1.0);
  194. $Cb = 1.0 if ($Cb > 1.0);
  195. } else {
  196. $A *= $f;
  197. }
  198. } elsif ($w >= 1.0 && $w < 2.0) {
  199. $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
  200. } elsif ($w >= 2.0 && $w < 3.0) {
  201. $t = 0.38 + $f*0.58; $R = 1.08;
  202. } elsif ($w >= 3.0 && $w < 4.0) {
  203. $t = 0.96 + $f*0.48; $R = 1.08;
  204. } elsif ($w >= 4.0 && $w < 5.0) {
  205. $t= 1.44 + $f*0.46; $R = 1.08;
  206. } elsif ($w >= 5.0 && $w < 6.0) {
  207. $t= 1.9 + $f*0.6; $R = 1.08;
  208. } elsif ($w >= 6.0) {
  209. my $ff = $w - 6.0;
  210. $t = 2.5 + $ff*0.50; $R = 1.08;
  211. }
  212. #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
  213. # determine angle of the line to horizontal
  214. my $tx = 0; my $ty = 0; # core thinkness of a line
  215. my $Rx = 0; my $Ry = 0; # fading edge of a line
  216. my $cx = 0; my $cy = 0; # cap of a line
  217. my $ALW = 0.01;
  218. my $dx = $x2 - $x1;
  219. my $dy = $y2 - $y1;
  220. if (abs($dx) < $ALW) {
  221. # vertical
  222. $tx = $t; $ty = 0;
  223. $Rx = $R; $Ry = 0;
  224. if ($w > 0.0 && $w < 1.0) {
  225. $tx *= 8;
  226. } elsif ($w == 1.0) {
  227. $tx *= 10;
  228. }
  229. } elsif (abs($dy) < $ALW) {
  230. #horizontal
  231. $tx = 0; $ty = $t;
  232. $Rx = 0; $Ry = $R;
  233. if ($w > 0.0 && $w < 1.0) {
  234. $ty *= 8;
  235. } elsif ($w == 1.0) {
  236. $ty *= 10;
  237. }
  238. } else {
  239. if ($w < 3) { # approximate to make things even faster
  240. my $m = $dy/$dx;
  241. # and calculate tx,ty,Rx,Ry
  242. if ($m > -0.4142 && $m <= 0.4142) {
  243. # -22.5 < $angle <= 22.5, approximate to 0 (degree)
  244. $tx = $t * 0.1; $ty = $t;
  245. $Rx = $R * 0.6; $Ry = $R;
  246. } elsif ($m > 0.4142 && $m <= 2.4142) {
  247. # 22.5 < $angle <= 67.5, approximate to 45 (degree)
  248. $tx = $t * -0.7071; $ty = $t * 0.7071;
  249. $Rx = $R * -0.7071; $Ry = $R * 0.7071;
  250. } elsif ($m > 2.4142 || $m <= -2.4142) {
  251. # 67.5 < $angle <= 112.5, approximate to 90 (degree)
  252. $tx = $t; $ty = $t*0.1;
  253. $Rx = $R; $Ry = $R*0.6;
  254. } elsif ($m > -2.4142 && $m < -0.4142) {
  255. # 112.5 < angle < 157.5, approximate to 135 (degree)
  256. $tx = $t * 0.7071; $ty = $t * 0.7071;
  257. $Rx = $R * 0.7071; $Ry = $R * 0.7071;
  258. } else {
  259. # error in determining angle
  260. printf("error in determining angle: m=%.4f\n", $m);
  261. }
  262. } else { # calculate to exact
  263. $dx= $y1 - $y2;
  264. $dy= $x2 - $x1;
  265. my $L = sqrt($dx*$dx + $dy*$dy);
  266. $dx /= $L;
  267. $dy /= $L;
  268. $cx = -0.6*$dy; $cy=0.6*$dx;
  269. $tx = $t*$dx; $ty = $t*$dy;
  270. $Rx = $R*$dx; $Ry = $R*$dy;
  271. }
  272. }
  273. # draw the line by triangle strip
  274. glBegin(GL_TRIANGLE_STRIP);
  275. if (!$alphablend) {
  276. glColor3f($Br, $Bg, $Bb);
  277. } else {
  278. glColor4f($Cr, $Cg, $Cb, 0);
  279. }
  280. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge
  281. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  282. if (!$alphablend) {
  283. glColor3f($Cr, $Cg, $Cb);
  284. } else {
  285. glColor4f($Cr, $Cg, $Cb, $A);
  286. }
  287. glVertex2f($x1 - $tx, $y1 - $ty); # core
  288. glVertex2f($x2 - $tx, $y2 - $ty);
  289. glVertex2f($x1 + $tx, $y1 + $ty);
  290. glVertex2f($x2 + $tx, $y2 + $ty);
  291. if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
  292. # printf("skipped one fading edge\n");
  293. } else {
  294. if (!$alphablend) {
  295. glColor3f($Br, $Bg, $Bb);
  296. } else {
  297. glColor4f($Cr, $Cg, $Cb, 0);
  298. }
  299. glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge
  300. glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
  301. }
  302. glEnd();
  303. # cap
  304. if ($w < 3) {
  305. # do not draw cap
  306. } else {
  307. # draw cap
  308. glBegin(GL_TRIANGLE_STRIP);
  309. if (!$alphablend) {
  310. glColor3f($Br, $Bg, $Bb);
  311. } else {
  312. glColor4f($Cr, $Cg, $Cb, 0);
  313. }
  314. glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
  315. glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
  316. glColor3f($Cr, $Cg, $Cb);
  317. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
  318. glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
  319. glEnd();
  320. glBegin(GL_TRIANGLE_STRIP);
  321. if (!$alphablend) {
  322. glColor3f($Br, $Bg, $Bb);
  323. } else {
  324. glColor4f($Cr, $Cg, $Cb, 0);
  325. }
  326. glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
  327. glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
  328. glColor3f($Cr, $Cg, $Cb);
  329. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  330. glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
  331. glEnd();
  332. }
  333. }
  334. package Slic3r::GUI::Plater::2DToolpaths::Dialog;
  335. use Wx qw(:dialog :id :misc :sizer);
  336. use Wx::Event qw(EVT_CLOSE);
  337. use base 'Wx::Dialog';
  338. sub new {
  339. my $class = shift;
  340. my ($parent, $print) = @_;
  341. my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
  342. my $sizer = Wx::BoxSizer->new(wxVERTICAL);
  343. $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
  344. $self->SetSizer($sizer);
  345. $self->SetMinSize($self->GetSize);
  346. # needed to actually free memory
  347. EVT_CLOSE($self, sub {
  348. $self->EndModal(wxID_OK);
  349. $self->Destroy;
  350. });
  351. return $self;
  352. }
  353. 1;