2DToolpaths.pm 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. package Slic3r::GUI::Plater::2DToolpaths;
  2. use strict;
  3. use warnings;
  4. use utf8;
  5. use Slic3r::Print::State ':steps';
  6. use Wx qw(:misc :sizer :slider :statictext wxWHITE);
  7. use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
  8. use base qw(Wx::Panel Class::Accessor);
  9. __PACKAGE__->mk_accessors(qw(print enabled));
  10. sub new {
  11. my $class = shift;
  12. my ($parent, $print) = @_;
  13. my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
  14. $self->SetBackgroundColour(wxWHITE);
  15. # init GUI elements
  16. my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
  17. my $slider = $self->{slider} = Wx::Slider->new(
  18. $self, -1,
  19. 0, # default
  20. 0, # min
  21. # we set max to a bogus non-zero value because the MSW implementation of wxSlider
  22. # will skip drawing the slider if max <= min:
  23. 1, # max
  24. wxDefaultPosition,
  25. wxDefaultSize,
  26. wxVERTICAL | wxSL_INVERSE,
  27. );
  28. my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
  29. [40,-1], wxALIGN_CENTRE_HORIZONTAL);
  30. $z_label->SetFont($Slic3r::GUI::small_font);
  31. my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
  32. $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
  33. $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
  34. my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
  35. $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
  36. $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
  37. EVT_SLIDER($self, $slider, sub {
  38. $self->set_z($self->{layers_z}[$slider->GetValue])
  39. if $self->enabled;
  40. });
  41. EVT_KEY_DOWN($canvas, sub {
  42. my ($s, $event) = @_;
  43. my $key = $event->GetKeyCode;
  44. if ($key == 85 || $key == 315) {
  45. $slider->SetValue($slider->GetValue + 1);
  46. $self->set_z($self->{layers_z}[$slider->GetValue]);
  47. } elsif ($key == 68 || $key == 317) {
  48. $slider->SetValue($slider->GetValue - 1);
  49. $self->set_z($self->{layers_z}[$slider->GetValue]);
  50. }
  51. });
  52. $self->SetSizer($sizer);
  53. $self->SetMinSize($self->GetSize);
  54. $sizer->SetSizeHints($self);
  55. # init print
  56. $self->{print} = $print;
  57. $self->reload_print;
  58. return $self;
  59. }
  60. sub reload_print {
  61. my ($self) = @_;
  62. # we require that there's at least one object and the posSlice step
  63. # is performed on all of them (this ensures that _shifted_copies was
  64. # populated and we know the number of layers)
  65. if (!$self->print->object_step_done(STEP_SLICE)) {
  66. $self->enabled(0);
  67. $self->{slider}->Hide;
  68. $self->{canvas}->Refresh; # clears canvas
  69. return;
  70. }
  71. $self->{canvas}->bb($self->print->total_bounding_box);
  72. my %z = (); # z => 1
  73. foreach my $object (@{$self->{print}->objects}) {
  74. foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
  75. $z{$layer->print_z} = 1;
  76. }
  77. }
  78. $self->{layers_z} = [ sort { $a <=> $b } keys %z ];
  79. $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1);
  80. $self->enabled(1);
  81. $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}};
  82. $self->{slider}->Show;
  83. $self->Layout;
  84. }
  85. sub set_z {
  86. my ($self, $z) = @_;
  87. return if !$self->enabled;
  88. $self->{z_label}->SetLabel(sprintf '%.2f', $z);
  89. $self->{canvas}->set_z($z);
  90. }
  91. package Slic3r::GUI::Plater::2DToolpaths::Canvas;
  92. use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
  93. use OpenGL qw(:glconstants :glfunctions :glufunctions);
  94. use base qw(Wx::GLCanvas Class::Accessor);
  95. use Wx::GLCanvas qw(:all);
  96. use List::Util qw(min first);
  97. use Slic3r::Geometry qw(scale unscale epsilon);
  98. use Slic3r::Print::State ':steps';
  99. __PACKAGE__->mk_accessors(qw(print z layers color init bb));
  100. # make OpenGL::Array thread-safe
  101. {
  102. no warnings 'redefine';
  103. *OpenGL::Array::CLONE_SKIP = sub { 1 };
  104. }
  105. sub new {
  106. my ($class, $parent, $print) = @_;
  107. my $self = $class->SUPER::new($parent);
  108. $self->print($print);
  109. EVT_PAINT($self, sub {
  110. my $dc = Wx::PaintDC->new($self);
  111. $self->Render($dc);
  112. });
  113. EVT_SIZE($self, sub {
  114. return if !$self->IsShownOnScreen;
  115. $self->Resize( $self->GetSizeWH );
  116. $self->Refresh;
  117. });
  118. return $self;
  119. }
  120. sub set_z {
  121. my ($self, $z) = @_;
  122. my $print = $self->print;
  123. # can we have interlaced layers?
  124. my $interlaced = (defined first { $_->config->support_material } @{$print->objects})
  125. || (defined first { $_->config->infill_every_layers > 1 } @{$print->regions});
  126. my $max_layer_height = $print->max_allowed_layer_height;
  127. my @layers = ();
  128. foreach my $object (@{$print->objects}) {
  129. foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
  130. if ($interlaced) {
  131. push @layers, $layer
  132. if $z > ($layer->print_z - $max_layer_height - epsilon)
  133. && $z <= $layer->print_z + epsilon;
  134. } else {
  135. push @layers, $layer if abs($layer->print_z - $z) < epsilon;
  136. }
  137. }
  138. }
  139. $self->z($z);
  140. $self->layers([ @layers ]);
  141. $self->Refresh;
  142. }
  143. sub Render {
  144. my ($self, $dc) = @_;
  145. # prevent calling SetCurrent() when window is not shown yet
  146. return unless $self->IsShownOnScreen;
  147. return unless my $context = $self->GetContext;
  148. $self->SetCurrent($context);
  149. $self->InitGL;
  150. glClearColor(1, 1, 1, 0);
  151. glClear(GL_COLOR_BUFFER_BIT);
  152. if (!$self->GetParent->enabled || !$self->layers) {
  153. glFlush();
  154. $self->SwapBuffers;
  155. return;
  156. }
  157. glMatrixMode(GL_PROJECTION);
  158. glLoadIdentity();
  159. my $bb = $self->bb;
  160. my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
  161. my ($x, $y) = $self->GetSizeWH;
  162. if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
  163. # adjust Y
  164. my $new_y = $y * ($x2 - $x1) / $x;
  165. $y1 = ($y2 + $y1)/2 - $new_y/2;
  166. $y2 = $y1 + $new_y;
  167. } else {
  168. my $new_x = $x * ($y2 - $y1) / $y;
  169. $x1 = ($x2 + $x1)/2 - $new_x/2;
  170. $x2 = $x1 + $new_x;
  171. }
  172. glOrtho($x1, $x2, $y1, $y2, 0, 1);
  173. glDisable(GL_DEPTH_TEST);
  174. glMatrixMode(GL_MODELVIEW);
  175. glLoadIdentity();
  176. my $skirt_drawn = 0;
  177. my $brim_drawn = 0;
  178. foreach my $layer (@{$self->layers}) {
  179. my $object = $layer->object;
  180. my $print_z = $layer->print_z;
  181. # draw brim
  182. if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) {
  183. $self->color([0, 0, 0]);
  184. $self->_draw(undef, $print_z, $_) for @{$self->print->brim};
  185. $brim_drawn = 1;
  186. }
  187. if ($self->print->step_done(STEP_SKIRT)
  188. && ($self->print->config->skirt_height == -1 || $self->print->config->skirt_height > $layer->id)
  189. && !$skirt_drawn) {
  190. $self->color([0, 0, 0]);
  191. $self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
  192. $skirt_drawn = 1;
  193. }
  194. foreach my $layerm (@{$layer->regions}) {
  195. if ($object->step_done(STEP_PERIMETERS)) {
  196. $self->color([0.7, 0, 0]);
  197. $self->_draw($object, $print_z, $_) for @{$layerm->perimeters};
  198. }
  199. if ($object->step_done(STEP_INFILL)) {
  200. $self->color([0, 0, 0.7]);
  201. $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills};
  202. }
  203. }
  204. if ($object->step_done(STEP_SUPPORTMATERIAL)) {
  205. if ($layer->isa('Slic3r::Layer::Support')) {
  206. $self->color([0, 0, 0]);
  207. $self->_draw($object, $print_z, $_) for @{$layer->support_fills};
  208. $self->_draw($object, $print_z, $_) for @{$layer->support_interface_fills};
  209. }
  210. }
  211. }
  212. glFlush();
  213. $self->SwapBuffers;
  214. }
  215. sub _draw {
  216. my ($self, $object, $print_z, $path) = @_;
  217. my @paths = $path->isa('Slic3r::ExtrusionLoop')
  218. ? @$path
  219. : ($path);
  220. $self->_draw_path($object, $print_z, $_) for @paths;
  221. }
  222. sub _draw_path {
  223. my ($self, $object, $print_z, $path) = @_;
  224. return if $print_z - $path->height > $self->z - epsilon;
  225. if (abs($print_z - $self->z) < epsilon) {
  226. glColor3f(@{$self->color});
  227. } else {
  228. glColor3f(0.8, 0.8, 0.8);
  229. }
  230. glLineWidth(1);
  231. if (defined $object) {
  232. foreach my $copy (@{ $object->_shifted_copies }) {
  233. foreach my $line (@{$path->polyline->lines}) {
  234. $line->translate(@$copy);
  235. glBegin(GL_LINES);
  236. glVertex2f(@{$line->a});
  237. glVertex2f(@{$line->b});
  238. glEnd();
  239. }
  240. }
  241. } else {
  242. foreach my $line (@{$path->polyline->lines}) {
  243. glBegin(GL_LINES);
  244. glVertex2f(@{$line->a});
  245. glVertex2f(@{$line->b});
  246. glEnd();
  247. }
  248. }
  249. }
  250. sub InitGL {
  251. my $self = shift;
  252. return if $self->init;
  253. return unless $self->GetContext;
  254. $self->init(1);
  255. }
  256. sub GetContext {
  257. my ($self) = @_;
  258. if (Wx::wxVERSION >= 2.009) {
  259. return $self->{context} ||= Wx::GLContext->new($self);
  260. } else {
  261. return $self->SUPER::GetContext;
  262. }
  263. }
  264. sub SetCurrent {
  265. my ($self, $context) = @_;
  266. if (Wx::wxVERSION >= 2.009) {
  267. return $self->SUPER::SetCurrent($context);
  268. } else {
  269. return $self->SUPER::SetCurrent;
  270. }
  271. }
  272. sub Resize {
  273. my ($self, $x, $y) = @_;
  274. return unless $self->GetContext;
  275. $self->SetCurrent($self->GetContext);
  276. glViewport(0, 0, $x, $y);
  277. }
  278. sub line {
  279. my (
  280. $x1, $y1, $x2, $y2, # coordinates of the line
  281. $w, # width/thickness of the line in pixel
  282. $Cr, $Cg, $Cb, # RGB color components
  283. $Br, $Bg, $Bb, # color of background when alphablend=false
  284. # Br=alpha of color when alphablend=true
  285. $alphablend, # use alpha blend or not
  286. ) = @_;
  287. my $t;
  288. my $R;
  289. my $f = $w - int($w);
  290. my $A;
  291. if ($alphablend) {
  292. $A = $Br;
  293. } else {
  294. $A = 1;
  295. }
  296. # determine parameters t,R
  297. if ($w >= 0 && $w < 1) {
  298. $t = 0.05; $R = 0.48 + 0.32 * $f;
  299. if (!$alphablend) {
  300. $Cr += 0.88 * (1-$f);
  301. $Cg += 0.88 * (1-$f);
  302. $Cb += 0.88 * (1-$f);
  303. $Cr = 1.0 if ($Cr > 1.0);
  304. $Cg = 1.0 if ($Cg > 1.0);
  305. $Cb = 1.0 if ($Cb > 1.0);
  306. } else {
  307. $A *= $f;
  308. }
  309. } elsif ($w >= 1.0 && $w < 2.0) {
  310. $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
  311. } elsif ($w >= 2.0 && $w < 3.0) {
  312. $t = 0.38 + $f*0.58; $R = 1.08;
  313. } elsif ($w >= 3.0 && $w < 4.0) {
  314. $t = 0.96 + $f*0.48; $R = 1.08;
  315. } elsif ($w >= 4.0 && $w < 5.0) {
  316. $t= 1.44 + $f*0.46; $R = 1.08;
  317. } elsif ($w >= 5.0 && $w < 6.0) {
  318. $t= 1.9 + $f*0.6; $R = 1.08;
  319. } elsif ($w >= 6.0) {
  320. my $ff = $w - 6.0;
  321. $t = 2.5 + $ff*0.50; $R = 1.08;
  322. }
  323. #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
  324. # determine angle of the line to horizontal
  325. my $tx = 0; my $ty = 0; # core thinkness of a line
  326. my $Rx = 0; my $Ry = 0; # fading edge of a line
  327. my $cx = 0; my $cy = 0; # cap of a line
  328. my $ALW = 0.01;
  329. my $dx = $x2 - $x1;
  330. my $dy = $y2 - $y1;
  331. if (abs($dx) < $ALW) {
  332. # vertical
  333. $tx = $t; $ty = 0;
  334. $Rx = $R; $Ry = 0;
  335. if ($w > 0.0 && $w < 1.0) {
  336. $tx *= 8;
  337. } elsif ($w == 1.0) {
  338. $tx *= 10;
  339. }
  340. } elsif (abs($dy) < $ALW) {
  341. #horizontal
  342. $tx = 0; $ty = $t;
  343. $Rx = 0; $Ry = $R;
  344. if ($w > 0.0 && $w < 1.0) {
  345. $ty *= 8;
  346. } elsif ($w == 1.0) {
  347. $ty *= 10;
  348. }
  349. } else {
  350. if ($w < 3) { # approximate to make things even faster
  351. my $m = $dy/$dx;
  352. # and calculate tx,ty,Rx,Ry
  353. if ($m > -0.4142 && $m <= 0.4142) {
  354. # -22.5 < $angle <= 22.5, approximate to 0 (degree)
  355. $tx = $t * 0.1; $ty = $t;
  356. $Rx = $R * 0.6; $Ry = $R;
  357. } elsif ($m > 0.4142 && $m <= 2.4142) {
  358. # 22.5 < $angle <= 67.5, approximate to 45 (degree)
  359. $tx = $t * -0.7071; $ty = $t * 0.7071;
  360. $Rx = $R * -0.7071; $Ry = $R * 0.7071;
  361. } elsif ($m > 2.4142 || $m <= -2.4142) {
  362. # 67.5 < $angle <= 112.5, approximate to 90 (degree)
  363. $tx = $t; $ty = $t*0.1;
  364. $Rx = $R; $Ry = $R*0.6;
  365. } elsif ($m > -2.4142 && $m < -0.4142) {
  366. # 112.5 < angle < 157.5, approximate to 135 (degree)
  367. $tx = $t * 0.7071; $ty = $t * 0.7071;
  368. $Rx = $R * 0.7071; $Ry = $R * 0.7071;
  369. } else {
  370. # error in determining angle
  371. printf("error in determining angle: m=%.4f\n", $m);
  372. }
  373. } else { # calculate to exact
  374. $dx= $y1 - $y2;
  375. $dy= $x2 - $x1;
  376. my $L = sqrt($dx*$dx + $dy*$dy);
  377. $dx /= $L;
  378. $dy /= $L;
  379. $cx = -0.6*$dy; $cy=0.6*$dx;
  380. $tx = $t*$dx; $ty = $t*$dy;
  381. $Rx = $R*$dx; $Ry = $R*$dy;
  382. }
  383. }
  384. # draw the line by triangle strip
  385. glBegin(GL_TRIANGLE_STRIP);
  386. if (!$alphablend) {
  387. glColor3f($Br, $Bg, $Bb);
  388. } else {
  389. glColor4f($Cr, $Cg, $Cb, 0);
  390. }
  391. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge
  392. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  393. if (!$alphablend) {
  394. glColor3f($Cr, $Cg, $Cb);
  395. } else {
  396. glColor4f($Cr, $Cg, $Cb, $A);
  397. }
  398. glVertex2f($x1 - $tx, $y1 - $ty); # core
  399. glVertex2f($x2 - $tx, $y2 - $ty);
  400. glVertex2f($x1 + $tx, $y1 + $ty);
  401. glVertex2f($x2 + $tx, $y2 + $ty);
  402. if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
  403. # printf("skipped one fading edge\n");
  404. } else {
  405. if (!$alphablend) {
  406. glColor3f($Br, $Bg, $Bb);
  407. } else {
  408. glColor4f($Cr, $Cg, $Cb, 0);
  409. }
  410. glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge
  411. glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
  412. }
  413. glEnd();
  414. # cap
  415. if ($w < 3) {
  416. # do not draw cap
  417. } else {
  418. # draw cap
  419. glBegin(GL_TRIANGLE_STRIP);
  420. if (!$alphablend) {
  421. glColor3f($Br, $Bg, $Bb);
  422. } else {
  423. glColor4f($Cr, $Cg, $Cb, 0);
  424. }
  425. glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
  426. glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
  427. glColor3f($Cr, $Cg, $Cb);
  428. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
  429. glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
  430. glEnd();
  431. glBegin(GL_TRIANGLE_STRIP);
  432. if (!$alphablend) {
  433. glColor3f($Br, $Bg, $Bb);
  434. } else {
  435. glColor4f($Cr, $Cg, $Cb, 0);
  436. }
  437. glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
  438. glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
  439. glColor3f($Cr, $Cg, $Cb);
  440. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  441. glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
  442. glEnd();
  443. }
  444. }
  445. package Slic3r::GUI::Plater::2DToolpaths::Dialog;
  446. use Wx qw(:dialog :id :misc :sizer);
  447. use Wx::Event qw(EVT_CLOSE);
  448. use base 'Wx::Dialog';
  449. sub new {
  450. my $class = shift;
  451. my ($parent, $print) = @_;
  452. my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
  453. my $sizer = Wx::BoxSizer->new(wxVERTICAL);
  454. $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
  455. $self->SetSizer($sizer);
  456. $self->SetMinSize($self->GetSize);
  457. # needed to actually free memory
  458. EVT_CLOSE($self, sub {
  459. $self->EndModal(wxID_OK);
  460. $self->Destroy;
  461. });
  462. return $self;
  463. }
  464. 1;