slic3r / Slic3r

Open Source toolpath generator for 3D printers
https://slic3r.org/
GNU Affero General Public License v3.0
3.3k stars 1.29k forks source link

[Feature Request]: SVG export of toolpaths (for Laser Engraving Machines) #4509

Open ElectroQuanta opened 5 years ago

ElectroQuanta commented 5 years ago

Background: Currently, only SVG export of slices is supported. However, many laser engraving machines SW work by importing some SVG, DXF file and assigning process parameters (e.g. speed, power, etc.) based on the line's color property. Thus, exporting also the toolpaths in the SVG file would enable direct application on laser machines.

Flow of Events:

  1. Import multiple stl files
  2. Slice them
  3. Generate custom tool paths
  4. Merge
  5. Export boundaries and infills to a single .svg file

Suggested starting point: I have found an utility in the code base (utils/view-toolpaths.pl) that generates the toolpaths and renders it. By default it operates only in 1 .stl file, but I have borrowed the code to merge files from the main perl script (slic3r.pl) successfully. Below is an example for 2 stl files (ring and disk). imagem

I have also added a custom svg_export function (lib/Slic3r/Print.pm) and, in spite of the successful merging, only contour and holes are exported and not the infills. See svg render example below: imagem

lordofhyphens commented 5 years ago

I strongly suggest any new development is done with the full C++ pathing, not any of the Perl files.

If you are looking for code to lift, examine SVGPrint.cpp

lordofhyphens commented 5 years ago

FYI:

https://github.com/slic3r/Slic3r/wiki/Running-Slic3r-from-git-on-Windows#building-with-cmake-and-ms-visual-studio-2017-community-edition

https://github.com/slic3r/Slic3r/wiki/Running-Slic3r-from-git-on-GNU-Linux#build-slic3r-c

https://github.com/slic3r/Slic3r/wiki/Running-Slic3r-from-git-on-OS-X#build-slic3r-c

ElectroQuanta commented 5 years ago

Thanks @lordofhyphens ;) I will look into it :)

ElectroQuanta commented 5 years ago

@lordofhyphens I just looking through the codebase and I cannot find SVGPrint.cpp as suggested above. Did you mean SVG.cpp? :)

lordofhyphens commented 5 years ago

Sorry: https://github.com/slic3r/Slic3r/blob/master/xs/src/libslic3r/SLAPrint.cpp

SVG.cpp is probably also relevant, though.

ElectroQuanta commented 5 years ago

Sorry to bother, but I'm struggling to understand some things related to the CLI interface. I followed the procedure mentioned in the docs to build the CLI.

Now, I was looking through the help for the CLI ./slic3r --help > usage.txt and I found the options provided are different from the ones listed for CLI using perl wrapper (via slic3r.pl).

However, I found this intriguing line: Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] and got me wondering if I need to build the CLI (C++) but call it via slic3r.pl.

Furthermore, the option --help is not listed in usage.txt I got, which seems to indicate a mismatch between the options listed and the ones supported.

Another simple test I've done, using an unsupported option, fails to reject it and goes into a loop: ./slic3r --export-svg mod1.stl However, this option appears in main (slic3r.cpp - lines 152-160). Update: The option is supported but terminates after throwing bad_alloc (2 min run): terminate called after throwing an instance of 'std::bad_alloc' what(): std::bad_alloc Aborted (core dumped)

Thus, summarizing:

  1. Can I build the CLI interface (C++) as standalone and don't bother with the Perl wrapper?
  2. If so, how can I effectively add and call the options that the Perl wrapper provides, namely:

(none of these appear as valid in the CLI).

Sorry for the long text and thanks in advance :)

lordofhyphens commented 5 years ago

The experimental C++ CLI interface is compeltely different than the Perl-based CLI. The Perl-based CLI is called through slic3r.pl and its primary code path is through there.

The C++ CLI pulls its options (and the help text) from CLIConfig and all of the individual command options. If it doesn't appear there, it isn't anywhere in that code path. I generated the --help document from PrintConfig.

The actual code that is executed when you call the CLI from the CMake build is in src/slic3r.cpp

The SVG Export, as far as I know, in the C++ CLI is still specialized for the SLAPrint, which I'm sure has bugs in it.

If you want to call the options that the Perl script provides, they'll need to be coded into CLIConfig and implemented as part of slic3r.cpp.

If you don't want to do that, then you're stuck calling the slic3r.pl script and building with the Perl build system.

ElectroQuanta commented 5 years ago

I've managed to compile and run a functional CLI with SVG export, with bare minimum configuration: ./slic3r --layer-height=0.5 --export-svg mod1.stl --output model.svg.

Now, I was trying to run it by loading a config.ini file (generated from the GUI version 1.2.9), using: ./slic3r --load-config=config.ini --export-svg mod1.stl --output model.svg

but I get the following error: Assertion failed: (spacing > 0), function _align_to_grid, file ./xs/src/libslic3r/Point.cpp, line 319 Abort trap: 6

which is the following condition: assert(spacing > 0);

Could you provide me more details about how this function works? And which of the options could be the cause of this crash?

Thanks in advance :)

lordofhyphens commented 5 years ago

The first thing to look at is to run it in gdb and get the backtrace. Between the backtrace and some prior knowledge that infill < 100% may cause issues (it has in the past, I thought it had been fixed).

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff7ab52f1 in __GI_abort () at abort.c:79
#2  0x00007ffff7aaca8a in __assert_fail_base (fmt=0x7ffff7c00ec8 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=assertion@entry=0x555555b5a0f6 "spacing > 0", 
    file=file@entry=0x555555b5a0c8 "/home/lenox/slic3r/xs/src/libslic3r/Point.cpp", line=line@entry=319, 
    function=function@entry=0x555555b5a1e0 <Slic3r::_align_to_grid(long, long)::__PRETTY_FUNCTION__> "Slic3r::coord_t Slic3r::_align_to_grid(Slic3r::coord_t, Slic3r::coord_t)") at assert.c:92
#3  0x00007ffff7aacb02 in __GI___assert_fail (assertion=0x555555b5a0f6 "spacing > 0", file=0x555555b5a0c8 "/home/lenox/slic3r/xs/src/libslic3r/Point.cpp", line=319, 
    function=0x555555b5a1e0 <Slic3r::_align_to_grid(long, long)::__PRETTY_FUNCTION__> "Slic3r::coord_t Slic3r::_align_to_grid(Slic3r::coord_t, Slic3r::coord_t)") at assert.c:101
#4  0x00005555556bf7f7 in Slic3r::_align_to_grid(long, long) ()
#5  0x00005555556bf89c in Slic3r::Point::align_to_grid(Slic3r::Point const&, Slic3r::Point const&) ()
#6  0x00005555557ee2d1 in Slic3r::FillRectilinear::_fill_single_direction(Slic3r::ExPolygon, std::pair<float, Slic3r::Point> const&, long, std::vector<Slic3r::Polyline, std::allocator<Slic3r::Polyline> >*) ()
#7  0x00005555557efd8d in Slic3r::FillRectilinear::_fill_surface_single(unsigned int, std::pair<float, Slic3r::Point> const&, Slic3r::ExPolygon&, std::vector<Slic3r::Polyline, std::allocator<Slic3r::Polyline> >*) ()
#8  0x00005555557e59ca in Slic3r::Fill::fill_surface(Slic3r::Surface const&) ()
#9  0x0000555555722283 in Slic3r::SLAPrint::_infill_layer(unsigned long, Slic3r::Fill const*) ()
#10 0x0000555555728ce8 in boost::_mfi::mf2<void, Slic3r::SLAPrint, unsigned long, Slic3r::Fill const*>::operator()(Slic3r::SLAPrint*, unsigned long, Slic3r::Fill const*) const ()
#11 0x00005555557284e3 in void boost::_bi::list3<boost::_bi::value<Slic3r::SLAPrint*>, boost::arg<1>, boost::_bi::value<Slic3r::Fill*> >::operator()<boost::_mfi::mf2<void, Slic3r::SLAPrint, unsigned long, Slic3r::Fill const*>, boost::_bi::rrlist1<unsigned long> >(boost::_bi::type<void>, boost::_mfi::mf2<void, Slic3r::SLAPrint, unsigned long, Slic3r::Fill const*>&, boost::_bi::rrlist1<unsigned long>&, int) ()
#12 0x0000555555727a00 in void boost::_bi::bind_t<void, boost::_mfi::mf2<void, Slic3r::SLAPrint, unsigned long, Slic3r::Fill const*>, boost::_bi::list3<boost::_bi::value<Slic3r::SLAPrint*>, boost::arg<1>, boost::_bi::value<Slic3r::Fill*> > >::operator()<unsigned long>(unsigned long&&) ()
#13 0x0000555555726ea0 in boost::detail::function::void_function_obj_invoker1<boost::_bi::bind_t<void, boost::_mfi::mf2<void, Slic3r::SLAPrint, unsigned long, Slic3r::Fill const*>, boost::_bi::list3<boost::_bi::value<Slic3r::SLAPrint*>, boost::arg<1>, boost::_bi::value<Slic3r::Fill*> > >, void, unsigned long>::invoke(boost::detail::function::function_buffer&, unsigned long) ()
#14 0x00005555557274c6 in boost::function1<void, unsigned long>::operator()(unsigned long) const ()
#15 0x00005555557261e1 in void Slic3r::_parallelize_do<unsigned long>(std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*, boost::mutex*, boost::function<void (unsigned long)>) ()
#16 0x0000555555729aab in void boost::_bi::list3<boost::_bi::value<std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*>, boost::_bi::value<boost::mutex*>, boost::_bi::value<boost::function<void (unsigned long)> > >::operator()<void (*)(std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*, boost::mutex*, boost::function<void (unsigned long)>), boost::_bi::list0>(boost::_bi::type<void>, void (*&)(std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*, boost::mutex*, boost::function<void (unsigned long)>), boost::_bi::list0&, int) ()
#17 0x0000555555729a15 in boost::_bi::bind_t<void, void (*)(std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*, boost::mutex*, boost::function<void (unsigned long)>), boost::_bi::list3<boost::_bi::value<std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*>, boost::_bi::value<boost::mutex*>, boost::_bi::value<boost::function<void (unsigned long)> > > >::operator()() ()
#18 0x00005555557299dc in boost::detail::thread_data<boost::_bi::bind_t<void, void (*)(std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*, boost::mutex*, boost::function<void (unsigned long)>), boost::_bi::list3<boost::_bi::value<std::queue<unsigned long, std::deque<unsigned long, std::allocator<unsigned long> > >*>, boost::_bi::value<boost::mutex*>, boost::_bi::value<boost::function<void (unsigned long)> > > > >::run() ()
ElectroQuanta commented 5 years ago

I've tried to follow your suggestion of using gdb and so I tried to set it up. On my Mac it is quirky and challenging (gdb is deprecated in favor of lldb), and I had to install following the instructions here.

I managed to install it, but I get the warningwarning: unhandled dyld version (15) gdb, which seems to be a problem only if working with dynamic libs.

Mac

OS version: macOS 10.13.3 Toolchain:

Build procedure

  1. I build the application with the flag -DCMAKE_BUILD_TYPE=RelwithDebInfo to enable gdb debugging, as follows:

    cmake --build . -- -j4 -- -DCMAKE_BUILD_TYPE=RelwithDebInfo
  2. Call out gdb on the executable:

    gdb slic3r
  3. Run it with arguments:

    (gdb): r --load config.ini --export-svg mod1.svg --output mod1gdb.svg

And after this I get the following output:

Assertion failed: (spacing > 0),Assertion failed: (spacing > 0), function _align_to_grid, file / function _align_to_grid, file .../Slic3r/xs/src/libslic3r/Point.cpp, line 319
  1. I try to set-up a breakpoint with absolute path to file, but if fails, issuing:
    No source file named .../xs/src/libslic3r/Point.cpp.
    Make breakpoint pending on future shared library load? (y or [n])

Even, if i press yand run step 3, the output is always the same (of step 3).

Could you please provide me more details about your workflow to obtain the debug message you referred above ?

LInux:

OS version: Ubuntu 16.04 LTS Toolchain:

Thanks in advance :)

lordofhyphens commented 5 years ago

I used "gdb" as a shorthand for "a debugger".

Use the build type Debug. I don't think I put the the debug flags in the Cmake.

ElectroQuanta commented 5 years ago

Thanks for the answer.

Comments

I'm building it directly from command-line, which means the only way to select the build type is through Cmake.

I selected the Debugbuild (as you suggested) and I got the same results. Some suggest to add the following to the CMakeLists.txt:

set(CMAKE_BUILD_TYPE Debug)

But I do not find this file to test it out.

Furthermore, I have tried the tutorial for gdb (with g++compiling with flag gto enable debugging), like so:

g++ -g test.cpp -o test

If I do this, I obtain a folder on the build root directory named test.dsym/ which contains the symbols list for gdb. Now, if I try to set up a break point, like mentioned above:

(gdb) b test.cpp:5

I obtain a successful result.

Question

You use an IDE? Would you mind sharing how you do the debugging?

Thanks in advance :) P.S. sorry for the n00b question

Update

I've added

set(CMAKE_BUILD_TYPE RelWithDebInfo)

to CMakeLists.txt and now I can set breakpoints ;) Thanks for all your help :)

ElectroQuanta commented 5 years ago

Update

I've managed to set everything correctly and I can now run gdb on slic3r :)

Notes on gdb and CMake

However, I noticed something interesting: depending on the type of CMAKE_BUILD_TYPE the results obtained are different:

4 exprtk::parser::parse_function_call<19ul> (this=0x7000060c3830, function=, function_name=...)

at .../Slic3r/xs/src/exprtk/exprtk.hpp:20887
I guess it has something to to with the compiler options (`-g O0` / `-g O3`) but nonetheless I thought it was worth to mention.

Exception: assert(spacing > 0)
--
Related to the exception `assert(spacing > 0)` mentioned [here](https://github.com/slic3r/Slic3r/issues/4509#issuecomment-416010009), I get the following backtrace, after setting a breakpoint there:

0 Slic3r::_align_to_grid (coord=-19993481, spacing=0) at .../Slic3r/xs/src/libslic3r/Point.cpp:319

1 0x00000001006d4eb0 in Slic3r::Point::align_to_grid (this=0x7000005f8348, spacing=..., base=...)

  at .../Slic3r/xs/src/libslic3r/Point.cpp:330

2 0x000000010050b2f5 in std::1::vector_base<Slic3r::Pointf, std::__1::allocator >::capacity (this=0x7000005faf90)

  at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/vector:359

3 std::1::vector_base<Slic3r::Pointf, std::1::allocator >::~vector_base (this=0x7000005fae78)

  at /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/vector:442

4 0x0000000000000000 in ?? ()


which reports `coord=-19993481, spacing=0`, causing the exception raising.
- You have mentioned that this is probably related to `fill_density < 100` and you thought it had been fixed in the past. Can you please elaborate more on that?
- It is strange that I don't get any exception with the bare minimum config mentioned [here](https://github.com/slic3r/Slic3r/issues/4509#issuecomment-416008639), which makes me think it has something to do with the `config.ini` file ([new_from_ini.ini.txt](https://github.com/slic3r/Slic3r/files/2330371/new_from_ini.ini.txt)) options, although I get a strange svg output file ([model26082018-0020.svg.txt](https://github.com/slic3r/Slic3r/files/2330335/model26082018-0020.svg.txt)) with the middle layers being dummy layers.
- AFAIK slicing performs well outside of SLAPrint.cpp. So, where should I look to try to find the causes of this undesired behaviour in this particular module?.

Thanks in advance and sorry for the long post :)
ElectroQuanta commented 5 years ago

Config.ini& Fill_density

I've found out, as suggested, that the fill_densityconfig option is the culprit of the exception mentioned above . However, I found some more interesting things that I thing it's worth sharing.

For the successful SVG exports, I've obtained filled regions (white; the 1st one) and unfilled regions (black; all subsequent ones). I know that the SVG export only considers the contours with the fill-color property being responsible for the filling. So far, so good.

However, I have some questions:

Floating-point exception

I've run other .stlmodels, namely spikey_top,stl, just to check if it was not it to blame. I ran it with fill_density=40%.

Now, instead of the aforementioned abort trap, I get another one: Floating-point exception 8, which tells me it has nothing to do with the CMAKE_BUILD_TYPE as discussed here.

I've backtraced it through gdband I get the following:

Thread 4 received signal SIGFPE, Arithmetic exception.
[Switching to Thread 0x2213 of process 6309]
0x00000001002a604c in ?? ()
(gdb) bt
#0  0x00000001002a604c in ?? ()
#1  0x0000700001f949f0 in ?? ()
#2  0x0000000100219b78 in exprtk::details::all_nodes_valid<11ul, double> (b=...) at ../xs/src/exprtk/exprtk.hpp:5119
#3  exprtk::parser<double>::expression_generator<double>::synthesize_expression<exprtk::details::function_N_node<double, exprtk::ifunction<double>, 11ul>, 11ul> (this=0x0, f=0x700001f94a08, branch=...) at ../xs/src/exprtk/exprtk.hpp:34236
#4  0x0000000000000000 in ?? ()

And if I set the breakpoint at exprtk.hpp:5119, run it again and backtrace it, I now have a segmentation fault:

Thread 4 received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x260b of process 5833]
0x00000001002199f4 in exprtk::details::all_nodes_valid<11ul, double> (b=...) at .../xs/src/exprtk/exprtk.hpp:5119
5119                if (0 == b[i]) return false;

with the culprit being:

      template <std::size_t N, typename T>
      inline bool all_nodes_valid(expression_node<T>* (&b)[N])
      {
         for (std::size_t i = 0; i < N; ++i)
         {
            if (0 == b[i]) return false;
         }
         return true;
      }

I hope this is somehow helpful to clarify this issue. Once again, sorry for the long post and thanks in advance :)

ArashPartow commented 5 years ago

@ElectroQuanta Why do you think _'all_nodes_valid'_ is the problem here?

Could you please do a rebuild with the following define: exprtk_enable_debugging

It can either be a compiler/cmake option, eg: -Dexprtk_enable_debugging or just have #define exprtk_enable_debugging just before #include "exprtk.hpp" is mentioned.

Once you've done the build can you perform a run that results in the problem and provide the output that is generated to stdout.

ElectroQuanta commented 5 years ago

@ArashPartow, I have already identified the issue and sent a pull request on that.

An ill-defined option was passed along, resulting in program crash and It had nothing to do with exprtk.hpp.

I merely tried to debug the problem as suggested, as I'm fairly new to this codebase, so I was just providing the inputs as they came along :)

ArashPartow commented 5 years ago

@ElectroQuanta aah ok, good to know alls been resolved.

ElectroQuanta commented 5 years ago

Update

I've managed to hack a working solution, from which I can generate the toolpaths and export it to SVG format from multiple input models (.stl)

However, I was trying to implement also the dont-arrangeoption which led me back to #3494, the homologous option in the perl path.

How do you think one can implement this in the C++ path? Any thoughts? :)

lordofhyphens commented 5 years ago

dont-arrange works in the Perl side by inhibiting an natural translation/centering mechanic that is only in the gcode-export (model.arrange_objects(print.config.min_object_distance()); in slic3r.cpp).

That code path isn't taken when you use export-svg.

For gcode export, you'd do the same inhibition in slic3r.cpp/

ElectroQuanta commented 5 years ago

Sorry, my bad, I was using it in conjunction with the --mergeoption.

Nonetheless, I've managed to implement a similar solution that suits my current needs, based on your suggestions.

Now, I can export toolpaths from multiple models to an .svgfile, preserving the absolute coordinates. Here is a screenshot of the fine result. imagem

It's still a very clumsy solution, and that's the reason I don't push it upstream yet; I must refactor it before (when I have more free time; I'm currently finishing the MSc :) ).

Nonetheless, if it's worthy to anyone it's on my github fork.

With that said, @lordofhyphens I cannot thank you enough for all your precious help and guidance 🥇 Soon enough I hope to collaborate more actively in this project.

If you wish, you can close this issue :)