2DToolpaths.pm 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. # 2D preview of the tool paths of a single layer, using a thin line.
  2. # OpenGL is used to render the paths.
  3. # Vojtech also added a 2D simulation of under/over extrusion in a single layer.
  4. package Slic3r::GUI::Plater::2DToolpaths;
  5. use strict;
  6. use warnings;
  7. use utf8;
  8. use Slic3r::Print::State ':steps';
  9. use Wx qw(:misc :sizer :slider :statictext wxWHITE);
  10. use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
  11. use base qw(Wx::Panel Class::Accessor);
  12. # Color Scheme
  13. use Slic3r::GUI::ColorScheme;
  14. __PACKAGE__->mk_accessors(qw(print enabled));
  15. sub new {
  16. my $class = shift;
  17. my ($parent, $print) = @_;
  18. my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
  19. if ( ( defined $Slic3r::GUI::Settings->{_}{colorscheme} ) && ( my $getScheme = Slic3r::GUI::ColorScheme->can($Slic3r::GUI::Settings->{_}{colorscheme}) ) ) {
  20. $getScheme->();
  21. $self->SetBackgroundColour(Wx::Colour->new(@BACKGROUND255));
  22. } else {
  23. Slic3r::GUI::ColorScheme->getDefault();
  24. $self->SetBackgroundColour(Wx::wxWHITE);
  25. }
  26. # init GUI elements
  27. my $canvas = $self->{canvas} = Slic3r::GUI::Plater::2DToolpaths::Canvas->new($self, $print);
  28. my $slider = $self->{slider} = Wx::Slider->new(
  29. $self, -1,
  30. 0, # default
  31. 0, # min
  32. # we set max to a bogus non-zero value because the MSW implementation of wxSlider
  33. # will skip drawing the slider if max <= min:
  34. 1, # max
  35. wxDefaultPosition,
  36. wxDefaultSize,
  37. wxVERTICAL | wxSL_INVERSE,
  38. );
  39. my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
  40. [40,-1], wxALIGN_CENTRE_HORIZONTAL);
  41. $z_label->SetFont($Slic3r::GUI::small_font);
  42. my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
  43. $vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
  44. $vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
  45. my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
  46. $sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
  47. $sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
  48. EVT_SLIDER($self, $slider, sub {
  49. $self->set_z($self->{layers_z}[$slider->GetValue])
  50. if $self->enabled;
  51. });
  52. EVT_KEY_DOWN($canvas, sub {
  53. my ($s, $event) = @_;
  54. my $key = $event->GetKeyCode;
  55. if ($key == 85 || $key == 315) {
  56. $slider->SetValue($slider->GetValue + 1);
  57. $self->set_z($self->{layers_z}[$slider->GetValue]);
  58. } elsif ($key == 68 || $key == 317) {
  59. $slider->SetValue($slider->GetValue - 1);
  60. $self->set_z($self->{layers_z}[$slider->GetValue]);
  61. } else {
  62. $event->Skip;
  63. }
  64. });
  65. $self->SetSizer($sizer);
  66. $self->SetMinSize($self->GetSize);
  67. $sizer->SetSizeHints($self);
  68. # init print
  69. $self->{print} = $print;
  70. $self->reload_print;
  71. return $self;
  72. }
  73. sub reload_print {
  74. my ($self) = @_;
  75. # we require that there's at least one object and the posSlice step
  76. # is performed on all of them (this ensures that _shifted_copies was
  77. # populated and we know the number of layers)
  78. if (!$self->print->object_step_done(STEP_SLICE)) {
  79. $self->enabled(0);
  80. $self->{slider}->Hide;
  81. $self->{canvas}->Refresh; # clears canvas
  82. return;
  83. }
  84. $self->{canvas}->bb($self->print->total_bounding_box);
  85. $self->{canvas}->_dirty(1);
  86. my %z = (); # z => 1
  87. foreach my $object (@{$self->{print}->objects}) {
  88. foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
  89. $z{$layer->print_z} = 1;
  90. }
  91. }
  92. $self->enabled(1);
  93. $self->{layers_z} = [ sort { $a <=> $b } keys %z ];
  94. $self->{slider}->SetRange(0, scalar(@{$self->{layers_z}})-1);
  95. if ((my $z_idx = $self->{slider}->GetValue) <= $#{$self->{layers_z}}) {
  96. $self->set_z($self->{layers_z}[$z_idx]);
  97. } else {
  98. $self->{slider}->SetValue(0);
  99. $self->set_z($self->{layers_z}[0]) if @{$self->{layers_z}};
  100. }
  101. $self->{slider}->Show;
  102. $self->Layout;
  103. }
  104. sub set_z {
  105. my ($self, $z) = @_;
  106. return if !$self->enabled;
  107. $self->{z_label}->SetLabel(sprintf '%.2f', $z);
  108. $self->{canvas}->set_z($z);
  109. }
  110. package Slic3r::GUI::Plater::2DToolpaths::Canvas;
  111. use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
  112. use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
  113. use base qw(Wx::GLCanvas Class::Accessor);
  114. use Wx::GLCanvas qw(:all);
  115. use List::Util qw(min max first);
  116. use Slic3r::Geometry qw(scale unscale epsilon X Y);
  117. use Slic3r::Print::State ':steps';
  118. # Color Scheme
  119. use Slic3r::GUI::ColorScheme;
  120. __PACKAGE__->mk_accessors(qw(
  121. print z layers color init
  122. bb
  123. _camera_bb
  124. _dirty
  125. _zoom
  126. _camera_target
  127. _drag_start_xy
  128. ));
  129. # make OpenGL::Array thread-safe
  130. {
  131. no warnings 'redefine';
  132. *OpenGL::Array::CLONE_SKIP = sub { 1 };
  133. }
  134. sub new {
  135. my ($class, $parent, $print) = @_;
  136. if ( ( defined $Slic3r::GUI::Settings->{_}{colorscheme} ) && ( Slic3r::GUI::ColorScheme->can($Slic3r::GUI::Settings->{_}{colorscheme}) ) ) {
  137. my $myGetSchemeName = \&{"Slic3r::GUI::ColorScheme::$Slic3r::GUI::Settings->{_}{colorscheme}"};
  138. $myGetSchemeName->();
  139. } else {
  140. Slic3r::GUI::ColorScheme->getDefault();
  141. }
  142. my $self = (Wx::wxVERSION >= 3.000003) ?
  143. # The wxWidgets 3.0.3-beta have a bug, they crash with NULL attribute list.
  144. $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "",
  145. [WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, 0]) :
  146. $class->SUPER::new($parent);
  147. # Immediatelly force creation of the OpenGL context to consume the static variable s_wglContextAttribs.
  148. $self->GetContext();
  149. $self->print($print);
  150. $self->_zoom(1);
  151. # 2D point in model space
  152. $self->_camera_target(Slic3r::Pointf->new(0,0));
  153. EVT_PAINT($self, sub {
  154. my $dc = Wx::PaintDC->new($self);
  155. $self->Render($dc);
  156. });
  157. EVT_SIZE($self, sub { $self->_dirty(1) });
  158. EVT_IDLE($self, sub {
  159. return unless $self->_dirty;
  160. return if !$self->IsShownOnScreen;
  161. $self->Resize;
  162. $self->Refresh;
  163. });
  164. EVT_MOUSEWHEEL($self, sub {
  165. my ($self, $e) = @_;
  166. return if !$self->GetParent->enabled;
  167. my $old_zoom = $self->_zoom;
  168. # Calculate the zoom delta and apply it to the current zoom factor
  169. my $zoom = -$e->GetWheelRotation() / $e->GetWheelDelta();
  170. if ($Slic3r::GUI::Settings->{_}{invert_zoom}) {
  171. $zoom *= -1;
  172. }
  173. $zoom = max(min($zoom, 4), -4);
  174. $zoom /= 10;
  175. $self->_zoom($self->_zoom / (1-$zoom));
  176. $self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
  177. {
  178. # In order to zoom around the mouse point we need to translate
  179. # the camera target. This math is almost there but not perfect yet...
  180. my $camera_bb_size = $self->_camera_bb->size;
  181. my $size = Slic3r::Pointf->new($self->GetSizeWH);
  182. my $pos = Slic3r::Pointf->new($e->GetPositionXY);
  183. # calculate the zooming center in pixel coordinates relative to the viewport center
  184. my $vec = Slic3r::Pointf->new($pos->x - $size->x/2, $pos->y - $size->y/2); #-
  185. # calculate where this point will end up after applying the new zoom
  186. my $vec2 = $vec->clone;
  187. $vec2->scale($old_zoom / $self->_zoom);
  188. # move the camera target by the difference of the two positions
  189. $self->_camera_target->translate(
  190. -($vec->x - $vec2->x) * $camera_bb_size->x / $size->x,
  191. ($vec->y - $vec2->y) * $camera_bb_size->y / $size->y, #//
  192. );
  193. }
  194. $self->_dirty(1);
  195. $self->Refresh;
  196. });
  197. EVT_MOUSE_EVENTS($self, \&mouse_event);
  198. return $self;
  199. }
  200. sub zoom{
  201. my($self, $direction) = @_;
  202. if( $direction eq 'in'){
  203. $self->_zoom($self->_zoom / (1+0.3));
  204. }
  205. elsif($direction eq 'out'){
  206. $self->_zoom($self->_zoom / (1-0.3));
  207. $self->_zoom(1) if $self->_zoom > 1; # prevent from zooming out too much
  208. }
  209. #apply changes
  210. $self->_dirty(1);
  211. $self->Refresh;
  212. }
  213. sub mouse_event {
  214. my ($self, $e) = @_;
  215. return if !$self->GetParent->enabled;
  216. my $pos = Slic3r::Pointf->new($e->GetPositionXY);
  217. if ($e->Entering && &Wx::wxMSW) {
  218. # wxMSW needs focus in order to catch mouse wheel events
  219. $self->SetFocus;
  220. } elsif ($e->Dragging) {
  221. if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
  222. # if dragging, translate view
  223. if (defined $self->_drag_start_xy) {
  224. my $move = $self->_drag_start_xy->vector_to($pos); # in pixels
  225. # get viewport and camera size in order to convert pixel to model units
  226. my ($x, $y) = $self->GetSizeWH;
  227. my $camera_bb_size = $self->_camera_bb->size;
  228. # compute translation in model units
  229. $self->_camera_target->translate(
  230. -$move->x * $camera_bb_size->x / $x,
  231. $move->y * $camera_bb_size->y / $y, # /**
  232. );
  233. $self->_dirty(1);
  234. $self->Refresh;
  235. }
  236. $self->_drag_start_xy($pos);
  237. }
  238. } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
  239. $self->_drag_start_xy(undef);
  240. } else {
  241. $e->Skip();
  242. }
  243. }
  244. sub set_z {
  245. my ($self, $z) = @_;
  246. my $print = $self->print;
  247. # can we have interlaced layers?
  248. my $interlaced = (defined first { $_->config->support_material } @{$print->objects})
  249. || (defined first { $_->config->infill_every_layers > 1 } @{$print->regions});
  250. my $max_layer_height = $print->max_allowed_layer_height;
  251. my @layers = ();
  252. foreach my $object (@{$print->objects}) {
  253. foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
  254. if ($interlaced) {
  255. push @layers, $layer
  256. if $z > ($layer->print_z - $max_layer_height - epsilon)
  257. && $z <= $layer->print_z + epsilon;
  258. } else {
  259. push @layers, $layer if abs($layer->print_z - $z) < epsilon;
  260. }
  261. }
  262. }
  263. # reverse layers so that we draw the lowermost (i.e. current) on top
  264. $self->z($z);
  265. $self->layers([ reverse @layers ]);
  266. $self->Refresh;
  267. }
  268. sub Render {
  269. my ($self, $dc) = @_;
  270. # prevent calling SetCurrent() when window is not shown yet
  271. return unless $self->IsShownOnScreen;
  272. return unless my $context = $self->GetContext;
  273. $self->SetCurrent($context);
  274. $self->InitGL;
  275. if ($DEFAULT_COLORSCHEME==1){
  276. glClearColor(1, 1, 1, 0);
  277. } else{
  278. glClearColor(@BACKGROUND_COLOR, 0);
  279. }
  280. glClear(GL_COLOR_BUFFER_BIT);
  281. if (!$self->GetParent->enabled || !$self->layers) {
  282. glFlush();
  283. $self->SwapBuffers;
  284. return;
  285. }
  286. glDisable(GL_DEPTH_TEST);
  287. glMatrixMode(GL_MODELVIEW);
  288. glLoadIdentity();
  289. # anti-alias
  290. if (0) {
  291. glEnable(GL_LINE_SMOOTH);
  292. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  293. glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
  294. glHint(GL_POLYGON_SMOOTH_HINT, GL_DONT_CARE);
  295. }
  296. my $tess;
  297. if (!(&Wx::wxMSW && $OpenGL::VERSION < 0.6704)) {
  298. # We can't use the GLU tesselator on MSW with older OpenGL versions
  299. # because of an upstream bug:
  300. # http://sourceforge.net/p/pogl/bugs/16/
  301. $tess = gluNewTess();
  302. gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
  303. gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
  304. gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
  305. gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
  306. gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
  307. gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
  308. }
  309. foreach my $layer (@{$self->layers}) {
  310. my $object = $layer->object;
  311. # only draw the slice for the current layer
  312. next unless abs($layer->print_z - $self->z) < epsilon;
  313. # draw slice contour
  314. glLineWidth(1);
  315. foreach my $copy (@{ $object->_shifted_copies }) {
  316. glPushMatrix();
  317. glTranslatef(@$copy, 0);
  318. foreach my $slice (@{$layer->slices}) {
  319. glColor3f(@TOOL_SHADE); # Inside part shade
  320. if ($tess) {
  321. gluTessBeginPolygon($tess);
  322. foreach my $polygon (@$slice) {
  323. gluTessBeginContour($tess);
  324. gluTessVertex_p($tess, @$_, 0) for @$polygon;
  325. gluTessEndContour($tess);
  326. }
  327. gluTessEndPolygon($tess);
  328. }
  329. glColor3f(@TOOL_COLOR); # Perimeter
  330. foreach my $polygon (@$slice) {
  331. foreach my $line (@{$polygon->lines}) {
  332. glBegin(GL_LINES);
  333. glVertex2f(@{$line->a});
  334. glVertex2f(@{$line->b});
  335. glEnd();
  336. }
  337. }
  338. }
  339. glPopMatrix();
  340. }
  341. }
  342. my $skirt_drawn = 0;
  343. my $brim_drawn = 0;
  344. foreach my $layer (@{$self->layers}) {
  345. my $object = $layer->object;
  346. my $print_z = $layer->print_z;
  347. # draw brim
  348. if ($self->print->step_done(STEP_BRIM) && $layer->id == 0 && !$brim_drawn) {
  349. $self->color(@TOOL_DARK);
  350. $self->_draw(undef, $print_z, $_) for @{$self->print->brim};
  351. $brim_drawn = 1;
  352. }
  353. if ($self->print->step_done(STEP_SKIRT)
  354. && ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id)
  355. && !$skirt_drawn) {
  356. $self->color(@TOOL_DARK);
  357. $self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
  358. $skirt_drawn = 1;
  359. }
  360. foreach my $layerm (@{$layer->regions}) {
  361. if ($object->step_done(STEP_PERIMETERS)) {
  362. $self->color(@TOOL_STEPPERIM);
  363. $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->perimeters};
  364. }
  365. if ($object->step_done(STEP_INFILL)) {
  366. $self->color(@TOOL_INFILL);
  367. $self->_draw($object, $print_z, $_) for map @$_, @{$layerm->fills};
  368. }
  369. }
  370. if ($object->step_done(STEP_SUPPORTMATERIAL)) {
  371. if ($layer->isa('Slic3r::Layer::Support')) {
  372. $self->color(@TOOL_SUPPORT);
  373. $self->_draw($object, $print_z, $_) for @{$layer->support_fills};
  374. $self->_draw($object, $print_z, $_) for @{$layer->support_interface_fills};
  375. }
  376. }
  377. }
  378. gluDeleteTess($tess) if $tess;
  379. glFlush();
  380. $self->SwapBuffers;
  381. }
  382. sub _draw {
  383. my ($self, $object, $print_z, $path) = @_;
  384. if ($path->isa('Slic3r::ExtrusionPath::Collection')) {
  385. $self->_draw($object, $print_z, $_) for @{$path};
  386. }else{
  387. my @paths = ($path->isa('Slic3r::ExtrusionLoop') || $path->isa('Slic3r::ExtrusionMultiPath'))
  388. ? @$path
  389. : ($path);
  390. $self->_draw_path($object, $print_z, $_) for @paths;
  391. }
  392. }
  393. sub _draw_path {
  394. my ($self, $object, $print_z, $path) = @_;
  395. return if $print_z - $path->height > $self->z - epsilon;
  396. if (abs($print_z - $self->z) < epsilon) {
  397. glColor3f($self->color->[0], $self->color->[1], $self->color->[2]);
  398. } else {
  399. glColor3f(0.8, 0.8, 0.8);
  400. }
  401. glLineWidth(1);
  402. if (defined $object) {
  403. foreach my $copy (@{ $object->_shifted_copies }) {
  404. glPushMatrix();
  405. glTranslatef(@$copy, 0);
  406. foreach my $line (@{$path->polyline->lines}) {
  407. glBegin(GL_LINES);
  408. glVertex2f(@{$line->a});
  409. glVertex2f(@{$line->b});
  410. glEnd();
  411. }
  412. glPopMatrix();
  413. }
  414. } else {
  415. foreach my $line (@{$path->polyline->lines}) {
  416. glBegin(GL_LINES);
  417. glVertex2f(@{$line->a});
  418. glVertex2f(@{$line->b});
  419. glEnd();
  420. }
  421. }
  422. }
  423. sub InitGL {
  424. my $self = shift;
  425. return if $self->init;
  426. return unless $self->GetContext;
  427. $self->init(1);
  428. }
  429. sub GetContext {
  430. my ($self) = @_;
  431. if (Wx::wxVERSION >= 2.009) {
  432. return $self->{context} ||= Wx::GLContext->new($self);
  433. } else {
  434. return $self->SUPER::GetContext;
  435. }
  436. }
  437. sub SetCurrent {
  438. my ($self, $context) = @_;
  439. if (Wx::wxVERSION >= 2.009) {
  440. return $self->SUPER::SetCurrent($context);
  441. } else {
  442. return $self->SUPER::SetCurrent;
  443. }
  444. }
  445. sub Resize {
  446. my ($self) = @_;
  447. return unless $self->GetContext;
  448. return unless $self->bb;
  449. $self->_dirty(0);
  450. $self->SetCurrent($self->GetContext);
  451. my ($x, $y) = $self->GetSizeWH;
  452. glViewport(0, 0, $x, $y);
  453. glMatrixMode(GL_PROJECTION);
  454. glLoadIdentity();
  455. my $bb = $self->bb->clone;
  456. # center bounding box around origin before scaling it
  457. my $bb_center = $bb->center;
  458. $bb->translate(@{$bb_center->negative});
  459. # scale bounding box according to zoom factor
  460. $bb->scale($self->_zoom);
  461. # reposition bounding box around original center
  462. $bb->translate(@{$bb_center});
  463. # translate camera
  464. $bb->translate(@{$self->_camera_target});
  465. # keep camera_bb within total bb
  466. # (i.e. prevent user from panning outside the bounding box)
  467. {
  468. my @translate = (0,0);
  469. if ($bb->x_min < $self->bb->x_min) {
  470. $translate[X] += $self->bb->x_min - $bb->x_min;
  471. }
  472. if ($bb->y_min < $self->bb->y_min) {
  473. $translate[Y] += $self->bb->y_min - $bb->y_min;
  474. }
  475. if ($bb->x_max > $self->bb->x_max) {
  476. $translate[X] -= $bb->x_max - $self->bb->x_max;
  477. }
  478. if ($bb->y_max > $self->bb->y_max) {
  479. $translate[Y] -= $bb->y_max - $self->bb->y_max;
  480. }
  481. $self->_camera_target->translate(@translate);
  482. $bb->translate(@translate);
  483. }
  484. # save camera
  485. $self->_camera_bb($bb);
  486. my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
  487. if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
  488. # adjust Y
  489. my $new_y = $y * ($x2 - $x1) / $x;
  490. $y1 = ($y2 + $y1)/2 - $new_y/2;
  491. $y2 = $y1 + $new_y;
  492. } else {
  493. my $new_x = $x * ($y2 - $y1) / $y;
  494. $x1 = ($x2 + $x1)/2 - $new_x/2;
  495. $x2 = $x1 + $new_x;
  496. }
  497. glOrtho($x1, $x2, $y1, $y2, 0, 1);
  498. glMatrixMode(GL_MODELVIEW);
  499. }
  500. sub line {
  501. my (
  502. $x1, $y1, $x2, $y2, # coordinates of the line
  503. $w, # width/thickness of the line in pixel
  504. $Cr, $Cg, $Cb, # RGB color components
  505. $Br, $Bg, $Bb, # color of background when alphablend=false
  506. # Br=alpha of color when alphablend=true
  507. $alphablend, # use alpha blend or not
  508. ) = @_;
  509. my $t;
  510. my $R;
  511. my $f = $w - int($w);
  512. my $A;
  513. if ($alphablend) {
  514. $A = $Br;
  515. } else {
  516. $A = 1;
  517. }
  518. # determine parameters t,R
  519. if ($w >= 0 && $w < 1) {
  520. $t = 0.05; $R = 0.48 + 0.32 * $f;
  521. if (!$alphablend) {
  522. $Cr += 0.88 * (1-$f);
  523. $Cg += 0.88 * (1-$f);
  524. $Cb += 0.88 * (1-$f);
  525. $Cr = 1.0 if ($Cr > 1.0);
  526. $Cg = 1.0 if ($Cg > 1.0);
  527. $Cb = 1.0 if ($Cb > 1.0);
  528. } else {
  529. $A *= $f;
  530. }
  531. } elsif ($w >= 1.0 && $w < 2.0) {
  532. $t = 0.05 + $f*0.33; $R = 0.768 + 0.312*$f;
  533. } elsif ($w >= 2.0 && $w < 3.0) {
  534. $t = 0.38 + $f*0.58; $R = 1.08;
  535. } elsif ($w >= 3.0 && $w < 4.0) {
  536. $t = 0.96 + $f*0.48; $R = 1.08;
  537. } elsif ($w >= 4.0 && $w < 5.0) {
  538. $t= 1.44 + $f*0.46; $R = 1.08;
  539. } elsif ($w >= 5.0 && $w < 6.0) {
  540. $t= 1.9 + $f*0.6; $R = 1.08;
  541. } elsif ($w >= 6.0) {
  542. my $ff = $w - 6.0;
  543. $t = 2.5 + $ff*0.50; $R = 1.08;
  544. }
  545. #printf( "w=%f, f=%f, C=%.4f\n", $w, $f, $C);
  546. # determine angle of the line to horizontal
  547. my $tx = 0; my $ty = 0; # core thinkness of a line
  548. my $Rx = 0; my $Ry = 0; # fading edge of a line
  549. my $cx = 0; my $cy = 0; # cap of a line
  550. my $ALW = 0.01;
  551. my $dx = $x2 - $x1;
  552. my $dy = $y2 - $y1;
  553. if (abs($dx) < $ALW) {
  554. # vertical
  555. $tx = $t; $ty = 0;
  556. $Rx = $R; $Ry = 0;
  557. if ($w > 0.0 && $w < 1.0) {
  558. $tx *= 8;
  559. } elsif ($w == 1.0) {
  560. $tx *= 10;
  561. }
  562. } elsif (abs($dy) < $ALW) {
  563. #horizontal
  564. $tx = 0; $ty = $t;
  565. $Rx = 0; $Ry = $R;
  566. if ($w > 0.0 && $w < 1.0) {
  567. $ty *= 8;
  568. } elsif ($w == 1.0) {
  569. $ty *= 10;
  570. }
  571. } else {
  572. if ($w < 3) { # approximate to make things even faster
  573. my $m = $dy/$dx;
  574. # and calculate tx,ty,Rx,Ry
  575. if ($m > -0.4142 && $m <= 0.4142) {
  576. # -22.5 < $angle <= 22.5, approximate to 0 (degree)
  577. $tx = $t * 0.1; $ty = $t;
  578. $Rx = $R * 0.6; $Ry = $R;
  579. } elsif ($m > 0.4142 && $m <= 2.4142) {
  580. # 22.5 < $angle <= 67.5, approximate to 45 (degree)
  581. $tx = $t * -0.7071; $ty = $t * 0.7071;
  582. $Rx = $R * -0.7071; $Ry = $R * 0.7071;
  583. } elsif ($m > 2.4142 || $m <= -2.4142) {
  584. # 67.5 < $angle <= 112.5, approximate to 90 (degree)
  585. $tx = $t; $ty = $t*0.1;
  586. $Rx = $R; $Ry = $R*0.6;
  587. } elsif ($m > -2.4142 && $m < -0.4142) {
  588. # 112.5 < angle < 157.5, approximate to 135 (degree)
  589. $tx = $t * 0.7071; $ty = $t * 0.7071;
  590. $Rx = $R * 0.7071; $Ry = $R * 0.7071;
  591. } else {
  592. # error in determining angle
  593. printf("error in determining angle: m=%.4f\n", $m);
  594. }
  595. } else { # calculate to exact
  596. $dx= $y1 - $y2;
  597. $dy= $x2 - $x1;
  598. my $L = sqrt($dx*$dx + $dy*$dy);
  599. $dx /= $L;
  600. $dy /= $L;
  601. $cx = -0.6*$dy; $cy=0.6*$dx;
  602. $tx = $t*$dx; $ty = $t*$dy;
  603. $Rx = $R*$dx; $Ry = $R*$dy;
  604. }
  605. }
  606. # draw the line by triangle strip
  607. glBegin(GL_TRIANGLE_STRIP);
  608. if (!$alphablend) {
  609. glColor3f($Br, $Bg, $Bb);
  610. } else {
  611. glColor4f($Cr, $Cg, $Cb, 0);
  612. }
  613. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry); # fading edge
  614. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  615. if (!$alphablend) {
  616. glColor3f($Cr, $Cg, $Cb);
  617. } else {
  618. glColor4f($Cr, $Cg, $Cb, $A);
  619. }
  620. glVertex2f($x1 - $tx, $y1 - $ty); # core
  621. glVertex2f($x2 - $tx, $y2 - $ty);
  622. glVertex2f($x1 + $tx, $y1 + $ty);
  623. glVertex2f($x2 + $tx, $y2 + $ty);
  624. if ((abs($dx) < $ALW || abs($dy) < $ALW) && $w <= 1.0) {
  625. # printf("skipped one fading edge\n");
  626. } else {
  627. if (!$alphablend) {
  628. glColor3f($Br, $Bg, $Bb);
  629. } else {
  630. glColor4f($Cr, $Cg, $Cb, 0);
  631. }
  632. glVertex2f($x1 + $tx+ $Rx, $y1 + $ty + $Ry); # fading edge
  633. glVertex2f($x2 + $tx+ $Rx, $y2 + $ty + $Ry);
  634. }
  635. glEnd();
  636. # cap
  637. if ($w < 3) {
  638. # do not draw cap
  639. } else {
  640. # draw cap
  641. glBegin(GL_TRIANGLE_STRIP);
  642. if (!$alphablend) {
  643. glColor3f($Br, $Bg, $Bb);
  644. } else {
  645. glColor4f($Cr, $Cg, $Cb, 0);
  646. }
  647. glVertex2f($x1 - $Rx + $cx, $y1 - $Ry + $cy);
  648. glVertex2f($x1 + $Rx + $cx, $y1 + $Ry + $cy);
  649. glColor3f($Cr, $Cg, $Cb);
  650. glVertex2f($x1 - $tx - $Rx, $y1 - $ty - $Ry);
  651. glVertex2f($x1 + $tx + $Rx, $y1 + $ty + $Ry);
  652. glEnd();
  653. glBegin(GL_TRIANGLE_STRIP);
  654. if (!$alphablend) {
  655. glColor3f($Br, $Bg, $Bb);
  656. } else {
  657. glColor4f($Cr, $Cg, $Cb, 0);
  658. }
  659. glVertex2f($x2 - $Rx - $cx, $y2 - $Ry - $cy);
  660. glVertex2f($x2 + $Rx - $cx, $y2 + $Ry - $cy);
  661. glColor3f($Cr, $Cg, $Cb);
  662. glVertex2f($x2 - $tx - $Rx, $y2 - $ty - $Ry);
  663. glVertex2f($x2 + $tx + $Rx, $y2 + $ty + $Ry);
  664. glEnd();
  665. }
  666. }
  667. package Slic3r::GUI::Plater::2DToolpaths::Dialog;
  668. use Wx qw(:dialog :id :misc :sizer);
  669. use Wx::Event qw(EVT_CLOSE);
  670. use base 'Wx::Dialog';
  671. sub new {
  672. my $class = shift;
  673. my ($parent, $print) = @_;
  674. my $self = $class->SUPER::new($parent, -1, "Toolpaths", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
  675. my $sizer = Wx::BoxSizer->new(wxVERTICAL);
  676. $sizer->Add(Slic3r::GUI::Plater::2DToolpaths->new($self, $print), 1, wxEXPAND, 0);
  677. $self->SetSizer($sizer);
  678. $self->SetMinSize($self->GetSize);
  679. # needed to actually free memory
  680. EVT_CLOSE($self, sub {
  681. $self->EndModal(wxID_OK);
  682. $self->Destroy;
  683. });
  684. return $self;
  685. }
  686. 1;