FabLabRothenburg / inkscape-embroidery

embroidery Inkscape Extension by Jon Howell http://www.jonh.net/~jonh/inkscape-embroidery/
35 stars 14 forks source link

Custom filling angle support. Angle in degrees 0..180 #4

Closed sergstetsuk closed 6 years ago

sergstetsuk commented 7 years ago

Sometimes the default filling direction is not the best one. User may use custom filling angle with this patch

lexelby commented 7 years ago

Neat! I'd be interested to hear what about my branch didn't work well for you -- perhaps I can add features.

I'm currently in the middle of a big refactor on my fork's master branch. My ultimate goal is to handle fills in complicated regions automatically and without any jump stitches. The code would instead do running stitches around the outside to get from one section of the fill to the next. After that, I'll add options to do underlay, so that the code can underlay and fill an entire shape automatically.

sergstetsuk commented 7 years ago

Glad to hear. I took your work. It is much better than stesie's one. So about your branch: It has some troubles too.

  1. Dimensions 10 pixels per mm is not good. SVG assumes density 90DPI so pixels per mm = 90/25.4 This is how Inkscape work I think. http://wiki.inkscape.org/wiki/index.php/Units_In_Inkscape. In this case you can you real dimensions of your picture.
  2. Angle fill not working. I fixed it in my master branch. A couple lines of code and you'll get it too.
  3. Setting Options in separate dialog is not cosy (I think of angle fill). I select some objects and convert to embroidery with different angles. Some need vertical, some - horisontal. So this option is better to be in main dialog.
  4. Single thread waling (I have some thoughts about, if you're interested)
  5. Your code uses spaces instead of tabs. It is difficult to compare it with stesie branch. Why spaces? It worth to use one approach.
lexelby commented 7 years ago

I'm glad you've found my branch useful! I'm hoping we can turn this into a usable alternative to paid applications.

I can see that some of your issues are because we're using the plugin differently. It might help if I explain how I do it. Ultimately, I hope to write all this up into a README in my fork.

First of all, I have inkscape-embroidery output CSV, which can be consumed directly by Embroidermodder2. There's a script in there under experimental/python/converter that can convert CSV to PES for Brother machines (I have an SE400).

I've modified my branch extensively to allow me to set all of the embroider parameters as XML attributes on the <svg:path> tags. That way I can set a custom fill angle for each fill region, for example. Once I have all of my shapes set up the way I want, I run the plugin on the whole thing, embroidering everything at once, and saving to a .CSV file. Then I hit ctrl-Z -- I don't save the paths the script generates.

I also try to avoid using the traveling salesman code. It's not very good at reducing jump stitches, and it's random. I much prefer to specify the exact path that the needle follows as it stitches everything out. I use the latest beta version of inkscape which contains the "object details" palette. That gives me a list of all objects in every layer, so I can reorder them as needed. I wrote a "reorder" plugin that lets me select objects in order in inkscape and then restack them in that order.

Now, for your questions/issues, I have some info that might help:

  1. I ignore DPI and "real dimensions" in inkscape, and instead just remember that 10 pixels = 1 millimeter. It's a little confusing, but it gets the job done. I set my canvas size to 1000x1000 pixels, which is equivalent to 10x10 centimeters or about 4x4 inches, which is what my machine can handle. Going CSV -> embroidermodder2 -> PES gets me the right dimensions.
  2. Weird, angle fill works fine for me. Perhaps I fixed something along the way and can't remember what it was...
  3. Yeah, the "embroider params" thing is kind of a hack to help me deal with setting XML settings directly on each shape. But to me, it's worth it to be able to set exactly what parameters are used for each object, so that I always get the same output and don't have to remember fill angles for every shape.
  4. What is "single thread waling"? Do you mean avoiding jump stitches? I'm interested to hear your thoughts.
  5. Tabs are preferred for Python. I made the choice to convert because I've touched almost every line in the codebase. I haven't heard anything from @stesie, so I've essentially adopted the codebase.
sergstetsuk commented 7 years ago
  1. Well. It's cool that CSV can be consumed by Embroidermodder2 and can produce correct PES. But Embroidermodder2 has limited editing capabilities. At least for now. You can't delete segments or add stitch between two others. The program is only in alpa stage. I think it would be better to use Inkscape generated embroidery right in SVG format. Inkscape is much better as editor than Embroidermodder2. With Inkscape and SVG you can do manual adjustments, move segments, manual connect blocks and save your work. Autogenerated stitches are not always perfect. That's why real dimensions are preffered. You draw or take real picture and just translate it to embroidery. All you need is right pix_per_mm value. Using 90/25.4 gives you the same 10 px per mm saving real dimensions. And even if you edit SVG embroidery dimensions will be correct. Of course you can whether ignore this aspect and produce just correct CSV or produce both CSV and SVG with right dimensions. Any other future tool like svg2pes of cource will assume that SVG is realsized. 2-3. It's an interesting idea to save fill direction as an XML attribute in the path. It's not obvious but cool. I think you could combine 2 methods: if XML attr is present it is used, if no then dialog value is used as default one.
  2. Yeah. I have an idea that jump stitches can go not on the edge but in the middle of not yet filled zones. For this purpose it is worth to split filled area on blocks with no holes within them and which are adjacent to other block only from one side (north or south). Than you can figure the sequence of filling blocks when your jump stitches will be overwritten by fill stitches.
  3. If tabs are preffered maybe it's worth to return to tab based indentation?

My toolchain is something like this. Get a picture. convert to embroidery using right filling angle for each element or group of elements. Fix corners and other staff in the generated SVG. Convert with svg2pes. Check in Embroiderymodder2.

lexelby commented 7 years ago

Pardon, I misspoke. I meant to say, spaces are preferred for python.

lexelby commented 7 years ago
  1. I don't use Embroidermodder2 at all. Its interface is lacking, as you say -- it pretty much can't do anything. However, they did write a nice little library for converting embroidery formats. If you build libembriodermodder2, you get a command-line tool that can convert the CSV produced by inkscape-embroidery directly into a PES.

I can see the advantage to using "real" sizes if you're editing stitches manually, but I never do that. If the code didn't generate it how I wanted, either I need to modify the shapes, or I fix the code to meet my expectations.

As to using XML attributes and defaulting to the ones from the dialog: that's exactly what I do. But I almost never modify the settings in the dialog -- I have a keystroke (ctrl+e) bound to run the embroider extension without popping up the dialog first. In practice I specify all params on all of my objects via XML attributes. That way I'm sure that if I reload a file months from now, it'll stitch out the same.

  1. I'd love to see pseudocode of an algorithm that can do that. :) Also, if you do running stitches across an area that's going to have fill stitch later, does it cause a visible ripple?
sergstetsuk commented 7 years ago
  1. OK. If spaces are preffered I adopt my fork to 4 spaces indentation as your one. So I'll be able to contribute into your fork easily.
  2. Of course manual editing can be useful in some cases (for example sharp corners for lines 2mm width with zigzag fill are not perfectly generated). Another example: radial or circle shaped fill cannot be done with this plugin yet. You can actually do this using paths with narrow lines but it will be better to have some tool to create base pattern and then edit it. Besides if you need another (not known yet) format of output file it wold be nice to generate it from svg directly but not csv. So svg with correct dimentions is really needed. CSV of course must be correct too. All you need is just scale all data respectively.
  3. Tell me more about 'I have a keystroke (ctrl+e) bound to run the embroider extension without popping up the dialog first' How to do that? And how do you set XML attributes? Is it easy?(Oh. I see. You use params dialog. Fine.)
  4. About pseudocode I'll try. Maybe it is worth to continue discussion on your branch? Or via email directly?
sergstetsuk commented 7 years ago

If you use 4 spaces indent you should change PyEmb.py file to have 4 spaces instead of tabs too.

lexelby commented 7 years ago

Okay, my PyEmb.py is fixed. Just hadn't gotten there before now :)

lexelby commented 7 years ago

Yeah, we're probably getting to the point where we should find another place to discuss. I would mention that I only have sporadic availability to work on this project due to other demands on my time, so it's quite possible I may go dark at some point. I'm excited to have someone else working on this codebase though!

  1. Curved/radial fill is something I'd really love to implement, but I haven't yet figured out what kind of algorithm I'd need to do it. Ideas welcome!

As to the sharp corner issue: At this point, I never use the feature that converts a shape's stroke to zig-zag. Instead I use the new "satin column" feature I added. This lets you carefully control how the stitches go around the corner to avoid the issue where corners end up with sparse stitching.

As to new file formats: I still feel like libembroidery is the way to go here. It has an excellent internal representation of stitches and it has a ton of formats already built in -- basically every machine embroidery format currently or previously in the market. Adding a new one to libembroidery would be preferable to writing a svg -> file converter, IMO.

The reason I like to work with 10 pixels per millimeter is that pretty much every reference I've found as I've learned machine embroidery uses millimeters. I think in millimeters as I design. I need to be able to easily measure objects on screen so that I can get my stitching right. Clearly what we need here is for pixels_per_millimeter to be an option the user can specify!

  1. You can set up keystrokes for filters in inkscape's settings menu. It lets you set two keystrokes per filter: one to bring up the filter's parameter dialog, and the second to just re-run the filter with the last settings you used.

And yes, I use the params filter I created to set the XML attributes on objects quickly. It's still pretty clunky. The alternative is to hit control-shift-X to pull up inkscape's builtin XML editor.

I had a chance to look at all of your changes from my branch. I see you removed the hatch_fills option -- thank you! I kept meaning to do that. I'm not sure what the author wanted with that but it's not useful and looks ugly.

Good idea, changing everything from pixels to millimeters. Once we add the ability for the user to set pixels_per_millimeter, I'll be able to merge your chagnes into my branch. I can't do it yet because it would break all of my existing projects (10+ designs).

sergstetsuk commented 7 years ago
  1. Radial fill can be done as circle with very thick stroke and zigzag fill. Spiral fill can be done as inkscape spiral with many turns and stroke <0.5 pt. But it is hacking.
  2. libembroidery-convert can definitely accept CSV but cannot handle my SVGs (sadly)
  3. Your designs can easily be converted. You just resize image to real dimensions and divide all old values in pixels by 10. Then you get them in mm.
lexelby commented 7 years ago

Wow, lots of stuff here. I've also done a ton of work in my fork, and I'm not sure how possible it would be to merge yours with mine. :(

I did a huge refactor to have one class for each patch type. I added an algorithm to automatically route stitching for a fill region by traveling around the edge of the region. I entirely rewrote Params to have a GUI that shows existing settings for the selected shape. It also includes support for presets.

I'm now in the process of writing a detailed README, and once I finish, I'm going to call it version 1.0.

sergstetsuk commented 7 years ago

I see. Will try to examine and incorporate your changes to my branch. Today I tried to install plugin on Windows machine. It's a bit tricky. You can take windows related piece of installation instruction from my fork's README.md.

sergstetsuk commented 7 years ago

In your latest version I have "No module named backports.functools_lru_cache" error. Which module is needed? I can disable @cache related code and it seems to work. Could you open issues on your fork? Where can I find satin column and simple zigzag options now? Found! It is shown only for appropriate paths (with no fill at all)

stesie commented 7 years ago

hey, since both of you are very active wrt. inkscape-embroidery extension, opposed to me (for me it wasn't more than a weekend project to hack on) ... I'd happily start an organisation account here on github and move this repository to there, adding both of you as owners. Then you could merge and update as you see fit.

Would that help? Are there better options?

lexelby commented 7 years ago

@stesie Hi!

TBH, I'm happy enough to stick with my own fork for now. Now that I'm back from vacation, I'm probably going to be significantly less active. I've also implemented the vast majority of the features I want. Thanks though :)

lexelby commented 7 years ago

@sergstetsuk Re backports.functools_lru_cache, it's this: https://pypi.python.org/pypi/backports.functools_lru_cache/1.3

As you've seen, it'll run without caching -- although I'd wager it's fairly slow for more complex shapes. I'll add a fallback to a noop function if backports.functools_lru_cache can't be imported.

treveradams commented 7 years ago

@sergstetsuk, I am interested in your ideas. Can we work together to merge your changes with @lexelby's? I would like to use real dimensions. I also like his use of XML attributes. I would love to see other stitch types, etc. as options (settable by XML attributes). My interests lie somewhere in https://github.com/treveradams/libpes/issues/1. It sounds like we have some overlapping goals.

treveradams commented 7 years ago

I would like to figure out how to fix an error similar to http://stackoverflow.com/questions/20833344/fix-invalid-polygon-python-shapely as it hits me with every svg I am trying so that I cannot tell if the plugin will even work for me.

sergstetsuk commented 7 years ago

@treveradams Real dimentions are already possible. You should just set correct value for "pixels per mm" parameter i.e. "pixels/stitch". For real dimensions it will be 3.54 px/stitch = 90 px/inch / 2.54 mm/inch / 10 stitches/mm (taking to account Incscape's default pixel density). If you use custom density (for example 10 px/mm) than you shold use 1 px/stitch value. Sure I'd like to help you whatever I can. But my branch is almost a full copy of @lexelby. There are just some little improvements.

treveradams commented 7 years ago

Ah, it appeared to me yours as a fork from @stesie not @lexelby. One problem I am having, which is impeding me working on this, is that every SVG I am trying (don't have any of my own yet) such as https://upload.wikimedia.org/wikipedia/en/a/a4/Flag_of_the_United_States.svg and http://www.freepik.com/free-vector/coloured-eagle-design_952146.htm#term=eagle&page=1&position=13 either have holes or problems like TopologyException: Input geom 1 is invalid: Self-intersection at or near point 290.59255118048708 224.16407805209684 at 290.59255118048708 224.16407805209684. I am trying to figure that out as I would love to use this.

treveradams commented 7 years ago

@sergstetsuk, which of your branches is closes to @lexelby and (maybe most important) which is the one you will be continuing development on?

treveradams commented 7 years ago

I was thinking of something along the lines of the following (untested) code being added:

#@cache
def get_embroider_attribute(self, attribute_name):
    attributes = simplestyle.parseStyle(self.node.get("embroidery:settings"))
    if (attributes not in attributes):
        return None
    value = attributes[attribute_name]
    if value == 'none':
        return None
    return value

#@cache
def set_embroider_attribute(self, attribute_name, value):
attributes = []
    attributes = simplestyle.parseStyle(self.node.get("embroidery:settings"))
    style[style_name] = value
    if value == 'none':
        return None
    node.attrib["embroidery:settings"] = simplestyle.formatStyle(attributes)
    return value

Then things such as stitch length, stitch density, stitch angle, underlay related things, fill type, etc. could all be applied per object, which according to many of the design sites I have been looking at, is highly recommended (different stitch directions in different areas of the design, not the same color/part).

This would not replace the general options, they will still exist, but would be overridden by the per object options.

There appear to be at least 5 main underlay layouts so the values within the attribute might have the following names: underlay-type: contour, parallel, perpendicular, zig-zag, double-zig-zag, lattice, center-run (all but double can be seen https://embroideres.com/forum/blogs/entry/24-underlay-types-digitizing-tips/) underlay-angle underlay-row-spacing row-spacing fill-type (likely to just be satin for now) stitch-minimum-length stitch-maximum-length stitch-trim-minimum-length stitch-collapse-maximum-length already-stitched (i.e. true or false, this would be for paths that should be treated directly as stitches per https://github.com/frno7/libpes/blob/master/tools/pes-to-svg-emb.c)

treveradams commented 7 years ago

It appears that underlays can be stacked in some other software as well, so underlay may be iterative. Not sure how to deal with angle/rowspacing in the case.

sergstetsuk commented 7 years ago

My master is more improved.

  1. Float value for pixel per mm allowed (3.54 - is for real dimensions)
  2. Old fill algorithm refactored (not fully complete yet) is replaced by my vision of it for automatic hole detection and embroidery generation with hidden jump stitches between sections.
  3. svg2emb plugin - just ouputs all (or selected) paths into csv (for export embroidery after manual adjusments or manual creation if one needs)
  4. cache disabled (I couldn't make it work)
  5. Trim stitches may be generated in csv
treveradams commented 7 years ago

3.54? Shouldn't that be 25.4 or 2.54? or 10? I thought almost all, if not all, embroidery formats were 10 stitches max / mm, or better said, that their numbers were of that resolution. I have just created a pull request that hopefully gives you an idea of what I am wanting to add. I would love feed back.

treveradams commented 7 years ago

Sorry, I just realized that the make file wasn't installing the params module. You already doing what I was wanting to do. Sorry for the noise.

sergstetsuk commented 7 years ago

Yes, 10 stitches/mm. And yes 3.54 value for (pixels per mm). As I mentioned early the parameter's caption is wrong. It must be named something like "number of stitches per 10 pixels". For density 10px/mm it will be still 10 stitches per 10 px. But for real sized pictures, where default Inkscape's density is 90 px/in you will have 90/2.54/10 = 3.54

lexelby commented 7 years ago

Hi, @treveradams! Welcome to the project! Sorry I'm somewhat late to the party here -- my availability for working on this project tends to be in fits and starts.

First off, I think a good number of your questions can be answered in my docs branch: https://github.com/lexelby/inkscape-embroidery/blob/docs/README.md

It's incomplete as yet, but it should give you an idea of how I go about using inkscape-embroidery to produce actual PES files.

The "invalid geometry" thing can be tricky. What you're looking for is a self-intersection, that is, the edge of the shape crosses over itself. Looking at your shape, you probably don't see any self-intersections, right? Chances are one of the corners has an extremely tiny loop instead of being an actual corner.

One thing that might work to quickly eliminate loops like this is to use Inkscape's "simplify" command (ctrl+l by default). Of course, if that smooths your geometry more than you find acceptable, then that's no good.

Another way to detect this kind of thing is to look for spurious extra vertexes. Select your shape in the Node Editor tool and start drag-selecting each vertex, one at a time. Look down at the status bar at the bottom and see how many points are selected. If you see more than you expected, then there may be some doubled-up vertices with a very tiny self-intersection in there.

My latest master branch is converted over to use millimeters for all parameters, with a customizable "pixels per millimeter" setting in the main Embroider extension dialog. I set mine to 10 and use a 1000x1000 pixel canvas, giving me exactly the embroiderable area on my Brother SE400 (10x10cm, 4x4in).

My master branch also contains a GUI-based dialog I wrote for setting parameters for objects such as underlay type, stitch length, row spacing, etc. As you said, the recommended practice is to specify parameters for every object, and that's exactly the workflow I use. The GUI Params extension makes it easy to do this.

The README in my docs branch addresses how to deal with holes in your shapes: convert each into a shape that has no hole by cutting a tiny channel from the exterior to the interior. I do this by drawing a line with a stroke width of 0.1, Path -> Stroke to Path, then select that path and my fill area and use Path -> Difference (ctrl-minus). My algorithm tidily hides any traversal along the narrow channel amongst the neighboring fill rows, making it almost undetectable.

That said, I've been watching what @sergstetsuk is up to with trying to hide travel stitches in areas that will be embroidered later. I tried to envision a similar algorithm and just couldn't come up with a reasonable algorithm that I was convinced would work on all shapes and complete in a reasonable amount of time. I have yet to review his latest work, though.

@sergstetsuk with regard to the cache decorator, you should be able to resolve the exceptions by running pip install backports.functools_lru_cache. The caching is not absolutely critical, but it saves on calculations that would otherwise be performed many times during each embroidery run.

lexelby commented 7 years ago

I finished up my README and merged it into my master branch. Please let me know if anything is unclear!

sergstetsuk commented 7 years ago

@lexelby Thanks about lru_cache hint. I couldn't figure out how should I install it. Now I can uncomment cached operations. It seems it works much faster now.

sergstetsuk commented 7 years ago

@lexelby can you check such an issue in autofill algorithm? http://i.piccy.info/i9/9592cff25f34b946c5006259f9219f92/1485157157/29274/1111291/Zn_mok_ekranu_2017_01_23_09_37_33.png

lexelby commented 7 years ago

@sergstetsuk It's hard to tell what might be wrong just from that picture. Can you post the SVG?

sergstetsuk commented 7 years ago

@lexelby It seems to be the issue in "maximum fill stitch len" parameter value. issue.zip

lexelby commented 7 years ago

@sergstetsuk

A couple of things are going on in this SVG file. Some of the params are kind of not what I would expect and you're hitting some edge-cases in my code.

First of all, the M is pretty narrow, just around 5mm (assuming you're using 3.54 pixels per millimiter still?). Satin might work better.

Second, your max stitch length is quite big: 20mm. With staggers set to 1 like you have it, that's essentially going to result in satin. This is also where you're running into a couple of edge cases / bugs. First off, the fill region isn't being properly broken into runs because of the code in AutoFill.is_same_run:

    def is_same_run(self, segment1, segment2):
        if shgeo.Point(segment1[0]).distance(shgeo.Point(segment2[0])) > self.max_stitch_length:
            return False

        if shgeo.Point(segment1[1]).distance(shgeo.Point(segment2[1])) > self.max_stitch_length:
            return False

        return True

This code is really just a heuristic and it doesn't work well when the max stitch length is huge compared to the path you're filling.

So yes, AutoFill isn't handling this case well, but I think you might've meant to set max_stitch_length to 2mm instead of 20?

treveradams commented 7 years ago

@lexelby I am using @sergstetsuk branch. I see that spacing and zig-zag spacing is only available in the embroider part. It is not in the params. I think this should be available in both, params overriding if non-zero/present.

@sergstetsuk I don't seem to be able to get underlays to work with sergefill. Is this supposed to be this way?

I am sorry it takes me a while to get back to you all. I am learning how to design as I do this and I have other things which also demand my time. (I am sure the same can be said for both of you. Maybe not the design part.)

treveradams commented 7 years ago

Is it possible to make these work if you know a hole is a hole? Can there be such an option? Think text inside another object. You would want to difference the text from the original and then put the text back.

lexelby commented 7 years ago

@treveradams I'm not sure what you mean about (row?) spacing and zig-zag spacing. They're available in the Params ui. Here's where zigzag_spacing_mm is defined for SatinColumn, for example: https://github.com/sergstetsuk/inkscape-embroidery/blob/master/embroider.py#L1229.

SergFill doesn't support underlay; only my AutoFill. See SergFill's implementation: https://github.com/sergstetsuk/inkscape-embroidery/blob/master/embroider.py#L1081.

Is it possible to make these work if you know a hole is a hole?

I'm not sure what you mean here, but here's my recommendation on how to handle holes with AutoFill: https://github.com/lexelby/inkscape-embroidery/#autofill

It's not ideal, but it does work. Ultimately I hope to design a new auto-fill algorithm that handles holes automatically. As far as I can tell, in order to properly handle all kinds of regions with (potentially multiple) holes, an algorithm would need to have recursion/backtracking. I've been thinking through how to do this but it's slow going.

As for text inside a fill region, I wouldn't recommend trying to treat the letters as holes. Unless your text is very large, it will be most legible if you render it as satin stitch instead of fill. You can do satin stitch right on top of fill stitch, as in the big "CPL" in this patch: https://github.com/lexelby/inkscape-embroidery/blob/master/images/patches/clinton_poker_league.jpg. Satin-on-fill seems to be a quite common technique.

If you try to do a fill region with the text cut out as holes and then fill the text in with a different thread color, I'm pretty sure it won't work well. You'll find that the natural distortion you get in all machine embroidery will cause gaps of the underlying fabric to show through between the fill and the letters. Plus, you'll have a lot of trouble trying to do the little tiny bits of fill inside loops in the text such as the hole in an "O" or even the crook inside a "J". I'm not saying it's not possible to make it work, but it's going to be a lot of unnecessary trouble when satin-on-fill produces a good result much more easily.

I am sorry it takes me a while to get back to you all. I am learning how to design as I do this and I have other things which also demand my time. (I am sure the same can be said for both of you. Maybe not the design part.)

No sweat at all. Conversations over the course of weeks are about the speed I can handle right now :)

treveradams commented 7 years ago

@lexelby Do you think you can do a section in your README on text? I have been having a difficult time with that.

@sergstetsuk It seems that once autofill is set, it cannot be changed using params to serge fill.

lexelby commented 7 years ago

Sure, I'll see if I can find the time to add some docs on text. Text has been by far the most time-consuming part of every design I've done so far. A couple tips while I'm here:

sergstetsuk commented 7 years ago

@treveradams SergFill works when Manual fill is enabled. You shold try disable Autofill and enable ManualFill. This is because of gui implementation. It is supposed AutoFill ON or OFF only. GUI need refactoring for multiple fill algorithm support (as well as multiple border and satin algos in future).

Underlay is really not implemented in SergFill. It is a todo item if algorithm is accepted at all. It's just yet another fill 90 degrees rotated and with rowspacing x 3.

X3msnake commented 6 years ago

What Flavour of linux are you using?