abey79 / vpype

The Swiss-Army-knife command-line tool for plotter vector graphics.
https://vpype.readthedocs.io/
MIT License
686 stars 61 forks source link

Include reading and writing of Embroidery files. #95

Closed tatarize closed 3 years ago

tatarize commented 3 years ago

I write the premier embroidery IO library for python. pyembroidery which basically is also entirely based on lines (since thread only goes straight). And it wouldn't be too hard to make vpype support for most embroidery formats. This would be pretty big for embroidery too. The API should be fairly reasonable, lists out the formats that can be read and written to and lets you load them up and get the lines they make.

https://github.com/EmbroidePy/pyembroidery

Embroidery formats are almost entirely commands sent to a xy plotter with a sewing machine head and are very similar to a pen plotter (though the hoop moves rather than the pen head). These formats are old and often limited (computerized embroidery basically predates computers). Threads tend to have colors, but without these random colors will be assigned. pyembroidery would take care of most oddities for you, like how jump stitches work and the max line lengths, cuts. Etc.

It would basically take hooking it up. Pyembroidery has no dependencies.

pattern = EmbPattern("filename.dst")
for block, thread in pattern.get_as_stitchblock():
     list_of_xy_values(block)  # Ignore the 3rd command value.

pattern.stitches will have all the the stitch list of 3 value lists. [X, Y, Stitch]. Would give you a list of tuples of (stitches, threads). For the stitch blocks, only the x ([0]) and y ([1]) matter since all the commands([2]) would entirely be STITCH.

pattern = EmbPattern()
for block in your_list_of_xy_values:
     pattern.add_block(block)
pattern.write("filename.pes")
abey79 commented 3 years ago

Very interesting. This looks like it would be rather easy to implement, the bulk of the difficult being as always the API (or UX in this case). Definitely worth looking into.

As it turns out, I'm completely new to embroidery. I can certainly see how this would make a decent CLI interface to pyembroidery's file format conversion capabilities. Out of curiosity, can you elaborate on how vpype's other features could be relevant to embroidery?

tatarize commented 3 years ago

Embroidery is similar to pen plotting but based entirely on straight lines. It can only change directions with a needle penetrating stitch. These lines usually need to be longer than 1mm and shorter than about 5mm. Though there are considerable differences that occur with length of lines. You get more of a cobble stone feel and sort of fluffy. The directional flow of the stitches is very important to the overall flow of the lines. Most embroidery is defined with closed shapes which are then filled, traditionally with a tatsumi algorithm which is like a flood fill of stitches with underpathing (thread that goes to a location that will be later covered with stitches) to various merge and split nodes. This is largely improved easier algorithms like Eulerian fills, which does the underpathing along the edge with minimum extra steps. A lot of the science and art here, as well as the math, is to do with the prohibitive extra expense of lifting your pen, vs. cutting a thread. Many machines will have a little cutting tool that will come out and cut the thread, but the machine typically has to slow down, tie off, cut the thread, move, tie back on (series of small stitches to make sure the thread attaches).

One of the best open source applications for embroidery is inkstitch/inkstitch (uses pyembroidery as the backend io stuff), and has two major fill algorithms within it. The aforementioned eulerian fill, and a sort of major rail, minor rung conversion routine for satin stitches. So named for their satiny look, these are usually extra thick lines which have directional cues (minor rungs) which are filled in with zig-zags that can angle around curves and widen and narrow as needed. The minor rungs in the design give these cues.

There is a lot of art in embroidery and some extra requirements but these other algorithms could actually be implemented in vype and it would pretty literally rank about 2nd among open source embroidery projects since very few exist, and those that do largely can't run those algorithms. There's some major players in commercial embroidery projects their software costs thousands upon thousands of dollars. But, for the most part all you'd need to know is sometimes maximum and minimum lengths matter and setting them is important and some other features like fills become really important.

Now I'm not expecting to be able to stitch traditional guilloche pattern though admittedly your pipeline shares a lot of Excentro-like workflow (http://www.excourse.com/excentro/) which is the premier software in that respect. But, merely the ability to generate lines, providing extensions and addons, and to save to the respective formats is actually important step forward. And the work your doing here has a lot of overlap with what embroidery does.

I'm not advocating that embroidery become a first class citizen of the vpype community, but it would be able to export, import and modify existing designs. And if you managed to implement the couple algorithms needed you could easily match what most software actually does, and satin stitches themselves might look reasonable when done on a pen plotter.

abey79 commented 3 years ago

This sounds like a rather vast topic and it's certainly very interesting. Adding support at IO level using pyembroidery seems to me a very reasonable step that would also benefit the plotter community (there are likely tons of embroidery designs and resources that could be put to good use on plotters). There are other, simple steps that could be useful, such as some kind of split command to cut long lines into segment of say 5mm.

What I'm afraid of, realistically, is that I don't see myself going much further into the topic beyond the above (fill algorithms, etc.) certainly because of time constraints but also because I'm not equipped with the required hardware to test stuff (and I'm not really interested to do so). I realised when I started to implement HPGL output support how important have some test hardware was and I ended up buying a vintage plotter (HP 7475).

Now the good news is that vpype is very easy to expend through plug-ins which don't suffer from any limitation that a vpype "native" command wouldn't have (a plug-in really is just a normal vpype command, except it's implemented in another repo than vpype's). If someone would be willing to invest the time, can totally see a vpype-embroidery project that would collect all sorts of commands related to embroidery. I would happily provide support and contributions as required of course. In the long run, if things solidify, the vpype-embroidery could easily be merged back into vpype if we feel that this would make sense from the technical, UX and organisational perspectives.

What do you think about this?

Note: as an illustration, I have a bunch of vpype commands in various stages of maturity in this repo. Notice the entry_points section in setup.py, that's the only thing it takes for a plug-in to be actually "seen" by vpype.

tatarize commented 3 years ago

It's a pretty vast topic, but it's not hard from a code standpoint. All the embroidery formats are a mess, I cleaned them up and made a pretty good method for doing the IO. There's a lot of designs that are solid and a lot of the ideas and concepts from the some mainstream embroidery applications like Wilcom's are pretty easy to see the range of things those applications do.

While it is actually essential to have hardware for somethings and testing the embroidery stuff would be helpful. For example the density of the thread matters a lot. You'd usually be able to have about 5 threads per mm, for full coverage of an area, and if you stitch too close together you can end up breaking the thread. There's some stuff like tie-ons and tie-offs where you make run a bit of a small back and forth line of threads before starting to move around because you need the thread to attach or the machine needs to be stopped, moved back several steps, and started again. There's things like pull compensation where the physical act of sewing pulls the fabric together, so future stitches lose registration not because the stepper motor skipped but because the actual location that point was intended to go has moved on the fabric itself. So there's a lot of stuff that can make it complex.

There's also a lot that makes it easy. Notably, you'd just be allowing some other IO stuff, and not bothering with the specifics, I do think adding in a sort of configurable zigzag line would be helpful for other vector operations. And I think adding in a Eulerian fill would also be helpful. I think you run a bunch of lines so making your operations be closed shapes might be an issue, which tends to be a step in an Eulerian fill. So I dunno how fills would turn out, since I haven't looked heavily into the pipelining other than knowing that I want to make that optparse code (used by click) work in MeerK40t's kernel for the commands there, since inlining attributes and settings could be pretty impressive.

Tying in some export and import formats that turn into lines should be pretty straight forward. Especially since I give a pretty useful API for listing those off. Which formats can be loaded and which can be saved. That seems like pretty low hanging fruit.

Also, I already have Eulerian fills as a bit of python code, that I cooked up myself originally in Java and then ported to python under an MIT license. So that aspect which is almost everything you'd need for that fill type would be a bit plug and play. It could somewhat satisfy #52, and depends a bit on how that shakes out.

https://github.com/meerk40t/meerk40t/blob/745a5794169211758094825c1884d43662c7ef70/CutPlanner.py#L101

Most of the code a bit away from vectors and into graph theory since the method Eulerian graphs are a graph thing and not strictly a vector which is always just a single path along a graph. Folding that into an addon might be fine, unless you actually do some graph work elsewhere in which case it might be a bit more reasonable. Though it doesn't seem like you'd want any proper path/graphs getting sent along the pipeline, even if you could duck cast it with the next command and throw an error where needed (Type error: "Pipelines contains path, it must contain a segment list. Use -pointalize"). But, converting a closed shape to filled shape is already planned and not difficult. I already have the code for it. If you have multiple closed shapes it'll solve all the connected path, using even-odd fill rule.

tatarize commented 3 years ago

I'm not sure how the plugin thing is supposed to work. I could likely copypasta some code in if I gathered the point. So lets say I want a simple plugin, called sinewave. I set it like the examples, add in click options for frequency and phase. I take in some geometries, I measure the length of the geometry then I apply the sine wave such that at points t along the path, I reposition all the points along the normal tangent of the segment (for lines just add tau/4 to the direction-angle and move in that direction via polar coords) so I get frequency sine waves for any shape by performing an offset curve, with sinewave magnitude.

Now, I'm sure everybody will love it, so I submit a pull request to vype-explorations or my own repository and folks magically get this? What if they used the pre-installed version of vpype that just put that in the command line for them. Is the work just left drifting in the aether? Or eventually does it seem really cool is audited enough that it gets moved towards core?

Like you could certainly make an offset curve function that does the same thing as my hypothetical sinewave there but give a static offset for all the values along the normal vectors for each of them. Put that into core, and everybody would have it. But, I don't see the path from, I think it would be great if, rather than just circle, people have a spiral function add in damping, revolutions, specialty options like Fermat's spiral, or a l-system fractal (everybody loves sierpinski's triangles (https://en.wikipedia.org/wiki/L-system#Example_5:_Sierpinski_triangle)), clearly these would be rather fundamental would they be better off put in core?

abey79 commented 3 years ago

Click plugin are kind of magic, yeah. They just "show up" once installed. People would typically have vpype running already, so they'd pip install my-great-plugin and it would show up (e.g. listed by vpype --help). Alternatively, since vpype would be listed as one of the plug-in's dependency, they could just pip install my-great-plugin and vpype would be automagically be installed as a side effect.

Of course, magic doesn't exist and it's actually click-plugins doing the work. It involves on vpype's side this line next to the main CLI function:

@with_plugins(iter_entry_points("vpype.plugins"))
@click.group(cls=GroupedGroup, chain=True)
# bunch of @click.options()
def cli(ctx, verbose, include, history, seed, config):
   pass
   # ...

Then, on the plug-in side, the command(s) entry points must be listed in setup.py, eg. for vpype-text:

# ...
setup(
    name="vpype-text",
    version="0.1.0",
    # ...
    entry_points='''
            [vpype.plugins]
            text=vpype_text.vpype_text:vpype_text # format is: cmdname=package.module:function
        ''',
)

(Notice the "vpype.plugins" key that links both pieces of code.)

Did I mention Click is effin great?!

Now, plug-ins can be spread in any number of repositories, grouped or otherwise. I myself have I think 3 plug-ins with standalone repositories and vpype-exploration which is a grab bag of random stuff including my own gen art stuff and experimental things for which I'm not making any sort of guarantee. Also, a plug-in is just a function, so it can be moved very easily around, including merged back to vpype's core repository if we feel so. So we don't really need to worry to much about making the right choice at the beginning.

If you envision that more than one embroidery-specific commands are likely to be made, what I suggest is the following:

Sounds good?

(Eventually, I'll even be looking at automating the setup.py part, to avoid the ugly, manual list like in vpype-explorations' setup.py)

tatarize commented 3 years ago

Hm. I could likely swing read_emb write_emb and eulerian_fill. It occurs to me I have an organization for python embroidery.


I mean there's a few pretty obvious commands to make. Some of them might be kinda fundamental though, and might want to be in vpype main.

Arc - Attributes, Control, Sagatta, Bulge yes, you have an arc, but the parameterization is pretty weak, in fact the control point as a parameterization is the most free-handed way to draw an arc. Draw an arc from start to end, going through the control. These all exist in svgelements.Arc Star - Attributes: Major, Minor, Phase, Points. Basic Shape Spiral - Attributes: R-major, R-minor, Revolutions, Damping, Types-Normal, Fermat. Basic Shape LSystem - Attributes: X_offset, Y_offset, Angle, Random_Angle_Variance, initial_Sequence. Rules. Iterations. Finalizing_Rules. Lsystem fractals, some of the attributes are a bit cryptic, like the random angle variance is needed to make trees look real Offset - Attributes Distance - Static offset along the normal vector. SineWave - Frequency. Magnitude. - Sinewave dynamic along normal vector. Cycloid - Frequency. Offset, Radius. - Move path points to a spinning circle outside. Two of these along a circle (really an undamped spiral since circles have a single revolution) Random - Attributes: Max Distance Types-Normal, Gaussian Move points in random directions Pattern Path - Attributes: Sequence_Pattern, Distance - Convert line into a patterned sequence of points each unit distance, conforming to the lines Smooth - Attributes: Smoothing degree. Moves each point closer to the midpoint between the point before and the point after Snap - Attributes: Value. Snap all Points to a multiple of the given value, rounding to the nearest position Harmonograph - SineX - Damping, phase, amplitude. SineY - Damping, phase, amplitude. Limit - Old Victorian pendulum based drawing machine

tatarize commented 3 years ago

https://github.com/EmbroidePy/vpype-embroidery exists now, should have a request to put you in a team with access.

See if you had a vpype organization you coulda made an embroidery team and given me access... actually that'd still be an option. (vpype not taken).

abey79 commented 3 years ago

Some of them might be kinda fundamental though, and might want to be in vpype main.

Absolutely.

Some quick notes before I start setting up vpype-embroidery.

tatarize commented 3 years ago

I patched in some of it likely broken. For the big one efill.

I see you don't have angle objects in the main. I'll exclude angle.

tatarize commented 3 years ago

I was going to copypasta your read_svg stuff for the embroidery. I think I got a gist of the main stuff.

efill though has to convert things into a bunch of segments and sort them based on y, and use that datastructure to sort the insideness of all line segments stepping through the y-values from min to max. It's likely the fastest method of doing that. Though none of my code is np. Which heavily optimize that process, then it creates a graph out of those edge pieces and rungs and then solves for the Eulerian walk of that graph and spits it out as some points or whatever. I might need to work on the conversions there.

The read and write stuff is a bit more spit it and forget it.

tatarize commented 3 years ago

embroidery-out

Congrats. It's a baby embroidery.

tatarize commented 3 years ago

duck2

Look, it's a duck I loaded up. I saved it again in png but it totally loaded and saved. I've run some conversions too. All seems fine.

vpype read-emb duck.pes write-emb duck2.png

(pyembroidery does have a png writer written in pure python).

tatarize commented 3 years ago

Finished up efill, debugging that was a pain.

Should be more or less correct. I might do a documentation update on efill to make it clear how it's working so if somebody wants to speed it up later they could. Obviously adjacency matrices with numpy for the graphs would likely speed things up. Though most of the code is pretty solid overall.

tatarize commented 3 years ago

Renamed the read and write to eread and ewrite though somehow I lost the vpype plugin status.

github.com/EmbroidePy/vpype-embroidery

Other than bately not working it's pretty solid.

epype

epype-zoom

tatarize commented 3 years ago

vype-embroidery satisfies this.