t-oster / LibLaserCut

a platform independant library to control Lasercutters. This is the base library for VisiCut
http://visicut.org
Other
60 stars 55 forks source link

Ruida driver: Support mirroring X Axis #198

Open khenriks opened 11 months ago

khenriks commented 11 months ago

On my laser cutter, when I use VisiCut, the output is reversed of what it should be. It's mirrored left to right.

I think the reason is because my lasercutter has an origin in the top right. VisiCut seems to assume it's in the top left. Is there a setting I can use to configure the origin position (and direction!) correctly?

mgmax commented 11 months ago

If I remember correctly, this is supported only for certain lasercutter types. Please send a screenshot of your laser device settings in VisiCut.

khenriks commented 10 months ago

Ok, here are my device settings: lasercutter_settings

mgmax commented 10 months ago

Moving the issue to LibLaserCut (which is the library for the lasercutter drivers used by VisiCut).

The ruida driver unfortunately does not have the "Flip X Axis" setting that your machine needs. You can take a look at the code of the Generic GCode driver, which has this setting, and try to copy/adapt the relevant parts to the Ruida driver.

Search for all lines with "flip" here: https://github.com/t-oster/LibLaserCut/blob/c2bee3a153aa17d48b4e559ee10ab4d816b336eb/src/main/java/de/thomas_oster/liblasercut/drivers/GenericGcodeDriver.java

tatarize commented 10 months ago

Checking back over here since I'm currently expanding the beta support for MeerK40t Ruida driver. There are a couple mirroring commands ( https://github.com/meerk40t/meerk40t/blob/main/meerk40t/ruida/rdjob.py ):

ARRAY_EN_MIRROR_CUT = b"\xE7\x0B" ARRAY_MIRROR_CUT_DISTANCE = b"\xE7\x0C" ARRAY_MIRROR = b"\xE7\x24" ELEMENT_ARRAY_MIRROR = b"\xF2\x07"

But, these are not a solution.

It seems that the real solution would be fix the coordinate system math. Basically a laser with a home position in the upper-left corner and a shape there is same as one flipped horizontally across the bed, for a laser in the upper-right. I don't seem to recall this being commonly used in Visicut.

The way I ended up solving this for Meerk40t was to implement the 4point -> 4point math of design space to laser-bed space. Giving a perspective matrix (of which only an affine subset was needed mapping 3pt to 3pt). It's fairly easy to say your corner dots are flipped by replacing C0 with C1 and C2 with C3, and solving for the mapped matrix.

   def perspective(cls, p1, p2, p3, p4):
        x1, y1 = p1
        x2, y2 = p2
        x3, y3 = p3
        x4, y4 = p4

        i = 1
        try:
            j = (y1 - y2 + y3 - y4) / (y2 - y3)
            k = (x1 - x2 + x3 - x4) / (x4 - x3)
            m = (y4 - y3) / (y2 - y3)
            n = (x2 - x3) / (x4 - x3)

            h = i * (j - k * m) / (1 - m * n)
            g = i * (k - j * n) / (1 - m * n)
        except ZeroDivisionError:
            h = 0.0
            g = 0.0

        c = x1 * i
        f = y1 * i
        a = x4 * (g + i) - x1 * i
        b = x2 * (h + i) - x1 * i
        d = y4 * (g + i) - y1 * i
        e = y2 * (h + i) - y1 * i

        return cls(a, d, b, e, c, f)

There are even some lasers like galvo scanners that can basically have things effectively rotated to the clockwise etc, and this sort of system to create 3x3 -> 3x3 matrices lets you easily fix the orientations (and scaling) before sending it to the driver.

The problem here is not really a driver issue, it will often occur in basically any driver based on the direction of the stepper-motors.

In a standard preamble you usually do both array_en_mirror_cut() and array_mirror. @kkaempf or @jnweiger may know what exactly those commands do. Other software that address this problem for Ruida like Lightburn, do include these commands but they are not flipped you change the home corner (rather it changes the design space).

[09:43:19]     <-- cc   (Checksum match)
[09:43:19]     --> d800 (Start Process)
[09:43:19]     --> da01062000000000760000000076 (Set 06 20 (mem: 0320) (File Total Length) = 118 (0x00000076) 118 (0x00000076))
[09:43:19]     --> d7   (End Of File)
[09:43:19]     -**-> d810   (Ref Point Mode 2, Machine Zero/Absolute Position)
[09:43:19]     -**-> e601   (Set Absolute)
[09:43:19]     -**-> f0 (Ref Point Set)
[09:43:19]     -**-> f10200 (Enable Block Cutting (0))
[09:43:19]     -**-> d800   (Start Process)
[09:43:19]     -**-> e70600000000000000000000   (Feed Repeat (0, 0))
[09:43:19]     -**-> e73800 (Set Feed Auto Pause 0)
[09:43:19]     -**-> e7030000015a600000014b10   (Process TopLeft (28000μm, 26000μm))
[09:43:19]     -**-> e7070000036c180000030650   (Process BottomRight(63000μm, 50000μm))
[09:43:19]     -**-> e7500000015a600000014b10   (Document Min Point(218μm, 203μm))
[09:43:19]     -**-> e7510000036c180000030650   (Document Max Point(63000μm, 50000μm))
[09:43:19]     -**-> e7040001000100000000000000000000   (Process Repeat (1, 1, 0, 0, 0, 0, 0))
[09:43:19]     -**-> e70500 (Array Direction (0))
[09:43:19]     -**-> c904000000042270   (0, Speed 70.0mm/s)
[09:43:19]     -**-> c63100194d (0, Power 1 Min=(20.001220703125))
[09:43:19]     -**-> c63200194d (0, Power 1 Max=(20.001220703125))
[09:43:19]     -**-> c64100194d (0, Power 2 Min (20.001220703125))
[09:43:19]     -**-> c64200194d (0, Power 2 Max (20.001220703125))
[09:43:19]     -**-> ca06000000000000   (0, Color #00000000)
[09:43:19]     -**-> ca410000   (0, Work Mode 0)
[09:43:19]     -**-> e752000000015a600000014b10 (0, Min Point(28000μm, 26000μm))
[09:43:19]     -**-> e753000000036c180000030650 (0, MaxPoint(63000μm, 50000μm))
[09:43:19]     -**-> e761000000015a600000014b10 (0, MinPointEx(28000μm, 26000μm))
[09:43:19]     -**-> e762000000036c180000030650 (0, MaxPointEx(63000μm, 50000μm))
[09:43:19]     -**-> ca2200 (0, Max Layer)
[09:43:19]     -**-> e754000000000000   (Pen Offset 0: 0μm)
[09:43:19]     -**-> e754010000000000   (Pen Offset 1: 0μm)
[09:43:19]     -**-> e755000000000000   (Layer Offset 0: 0μm)
[09:43:19]     -**-> e755010000000000   (Layer Offset 1: 0μm)
[09:43:19]     -**-> f10300000000000000000000   (Display Offset (0μm, 0μm))
[09:43:19]     -**-> f10000 (Element Max Index (0))
[09:43:19]     -**-> f10100 (Element Name Max Index(0))
[09:43:19]     -**-> f20000 (Element Index (0))
[09:43:19]     -**-> f20100 (Element Name Index (0))
[09:43:19]     -**-> f202052a391c41046a150820   (Element Name (b'\x05*9\x1cA\x04j\x15\x08 '))
[09:43:19]     -**-> f2030000015a600000014b10   (Element Array Min Point (28000μm, 26000μm))
[09:43:19]     -**-> f2040000036c180000030650   (Element Array Max Point (63000μm, 50000μm))
[09:43:19]     -**-> f2050001000100000211380000013b40   (Element Array (1, 1, 0, 273, 7168, 1, 7616))
[09:43:19]     -**-> f20600000000000000000000   (Element Array Add (0μm, 0μm))
[09:43:19]     -**-> f20700 (Element Array Mirror (0))
[09:43:19]     -**-> ea00   (Array Start (0))
[09:43:19]     -**-> e76000 (Set Current Element Index (0))
[09:43:19]     -**-> e7130000015a600000014b10   (Array Min Point (28000μm, 26000μm))
[09:43:19]     -**-> e7170000036c180000030650   (Array Max Point (63000μm, 50000μm))
[09:43:19]     -**-> e7230000015a600000014b10   (Array Add (28000μm, 26000μm))
[09:43:19]     -**-> e72400 (Array Mirror 0)
[09:43:19]     -**-> e7370000036c180000030650   (Array Even Distance 63000 50000)
[09:43:19]     -**-> e7080001000100000211380000013b40   (Array Repeat (1, 1, 0, 273, 7168, 1, 7616))
[09:43:19]     -**-> ca0100 (End Layer)
[09:43:19]     -**-> ca0200 (0, Layer Number)
[09:43:19]     -**-> ca0130 (EnLaser2Offset 0)
[09:43:19]     -**-> ca0110 (Layer Device 0)
[09:43:19]     -**-> ca0112 (Air Assist Off)
[09:43:19]     -**-> c9020000042270 (Speed Laser 1 70.0mm/s)
[09:43:19]     -**-> c6120000030650 (Laser On Delay 50.0ms)
[09:43:19]     -**-> c6130000000000 (Laser Off Delay 0.0ms)
[09:43:19]     -**-> c6501633   (Through Power 1 (17.498779296875))
[09:43:19]     -**-> c6511633   (Through Power 2 (17.498779296875))
[09:43:19]     -**-> c601194d   (Power 1 min=20.001220703125)
[09:43:19]     -**-> c602194d   (Power 1 max=20.001220703125)
[09:43:19]     -**-> c621194d   (Power 2 min=20.001220703125)
[09:43:19]     -**-> c622194d   (Power 2 max=20.001220703125)
[09:43:19]     -**-> ca0301 (EnLaserTube Start)
[09:43:19]     -**-> 880000015a600000014b10 (Move Absolute (28000μm, 26000μm))
[09:43:19]     -**-> a80000015a600000030650 (Cut Absolute (28000μm, 50000μm))
[09:43:19]     -**-> a80000036c180000030650 (Cut Absolute (63000μm, 50000μm))
[09:43:19]     -**-> a80000036c180000014b10 (Cut Absolute (63000μm, 26000μm))
[09:43:19]     -**-> a80000015a600000014b10 (Cut Absolute (28000μm, 26000μm))
[09:43:19]     -**-> ab0064 (Cut Vertical Relative (+100μm))
[09:43:19]     -**-> c6110000060d20 (Add Delay 100.0ms)
[09:43:19]     -**-> eb (Array End)
[09:43:19]     -**-> e700   (Block End)
[09:43:19]     -**-> da01062000000000760000000076   (Set 06 20 (mem: 0320)= 118 (0x00000076) 118 (0x00000076))
[09:43:19]     <-- b'L\x00\x01\xc6'
[09:43:19]     Connection to ('127.0.0.1', 64443) was closed.
[09:43:24]     -**-> d7 (End Of File)

Here's a standard very basic rectangle being sent over the Lightburn Bridge protocol. We have the mirror commands but they don't use other settings even when swapped. They just consider the left side the far side.

To solve this within the driver it would require that we realtime-query the bed size and internal to the driver perform the matrix flip which does not seem to be the driver's job.

mgmax commented 10 months ago

I think that the current solution as in other drivers should do the job for now. Keep it simple as long as possible. The bed width is already known in the driver as a configuration option.

https://github.com/t-oster/LibLaserCut/blob/c2bee3a153aa17d48b4e559ee10ab4d816b336eb/src/main/java/de/thomas_oster/liblasercut/drivers/GenericGcodeDriver.java#L527-L528

Once this does not work anymore, then we can create a helper class or similar for the transformations. This will probably have to be handled separately for raster engrave and for vector cutting.

tatarize commented 10 months ago

That should mostly work for most applications. With a couple caveats. It does sort of require that you use absolute positioning and that bed space and design space are uniform. There are a few lasers with potential xy-swapping, and it won't account for issues with slightly non-uniform scaling that sometimes will happen with belt stretching. But, yes 99% of the cases should be covered with those bits of code.

Depending a bit on the laser for whether rasters would necessarily work. Though limiting things to isFlipAxis should give you mostly the same directions. Though if you do a vector bottom to top a y-flip becomes a top-to-bottom, since the bottom-to-top is really going max-y to min-y (and a y-flipped top-to-bottom is the same thing).

kkaempf commented 10 months ago

@khenriks - can you give #206 a try and comment on the results there ?

TheAssassin commented 8 months ago

Can confirm this bug on an OMTech Polar with the Ruida driver. I'll have time to have a closer look soon with the device.