2DToolpaths.pm 31 KB

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