Experimental gcode auto Z compensation ready to test...

User-Generated tips and tricks for the Rostock Max, Orion, H1.1, or H1 Printers
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Hello all, I've had an idea for a year on how to solve the calibration problem on delta printers. I dragged my feet on implementing it since it seemed like the firmware guys were making progress. But now, a year later, it still is not where I hoped it would be. So I picked up on my idea and implemented a working prototype. Here are the prerequisites:

1) you must have a flat build plate - borosilicate glass is fine.
2) you must calibrate the bed center and all three tower positions for Z=0. This is easily done manually
3) you must have your delta arm length correct, again, easily done manually

4) you MUST use KISSlicer. I have not attempted to use it on other gcode.

Here's how it works

Currently, the delta Z height grid must be created manually. You can use a dial indicator, feeler gauges, Kentucky windage or probing feature in the firmware. I have a spreadsheet that lays out the required measurements. These are 10mm square cells and cover the entire bed and there are 803 measurements required! But do't panic. there is a Kentucky windage approach that can significantly cut down on the number of measurements.

Once the Z map is created, it is saved as a CSV (comma separated values) file right from Excel or Numbers. This is used by the gcode post processor.

Current slicers create gcode like the following:

G1 X-9.4 Y39.25 Z0.2 E0
G1 X-10.2 Y39.18 E3.0334
G1 X-12.53 Y38.69 E3.1322
G1 X-14.35 Y38.24 E3.2103
G1 X-18.12 Y37 E3.3752
G1 X-20.69 Y35.95 E3.4906

Notice the first line (bold) sets the Z height, which is assumed for all subsequent print movements. I’ve conducted some experiments and written a gcode post-processor that uses the printer’s height map and makes the Z compensation adjustment in gcode. This is done through a simple lookup and then a simple linear interpolation of Z with the nearest neighbor:

G1 X-9.4 Y39.25 Z0.21 E0
G1 Z0.21 X-10.2 Y39.18 E3.0334
G1 Z0.21 X-12.53 Y38.69 E3.1322
G1 Z0.22 X-14.35 Y38.24 E3.2103
G1 Z0.22 X-18.12 Y37 E3.3752
G1 Z0.21 X-20.69 Y35.95 E3.4906

Notice that each movement (G1) now has an explicit Z coordinate. This tells the printer firmware to interpolate the movement in three dimensions and not assume Z0.20. In practice I’ve not noticed any difference in printer performance and it does indeed planerize the movement of the effector so the first layer is consistent with an even thickness. Since the build surface is actually flat (a requirement for this to work) the changes in the Z coordinate are simply compensating for non-planar movement due to mechanical or calibration errors. This technique can remove a significant amount of introduced error too. However, it should be noted that while we are focused on the Z coordinate (which is important to get first layer adhesion) X and Y are likely slightly off too. This might result in very slightly misshaped parts but in practice, on my delta printers running this compensation, I’ve not been able to measure this in the final part dimensions.

The post processor is written in Python and can be called directly from KISSlicer or you can call it from the command line. You pass in the file path to the CSV Z height map file and the gcode file. It spits out a new file with the Z mapping.

Why 803 measurements may not be needed
Ideally, the data collection would be completely automated so 803 data points would not be a chore. I haven't implemented that yet but wanted to get this out there to test. One approach os to simply focus on those areas on your printer that are far off. There are reports of 2mm "lift" between towers on some printers. The center part of the bed and near the towers is pretty consistent though. So leave all of the delta Z values set to "0" and then measure or even eyeball the delta Zs in the hot spots. A few cycles of test and tweak should get you in the ball park. You could start with a specific region/hot spot on your bed and create the Z map for that area, test it and see what the outcome is. If it looks good, then make the investment in more data points or a probe.

If you are interested in testing this, I'm happy to give you the spreadsheet and code. I can not spend a lot of time on support so please don't expect me to hand hold you through setting everything up. Perhaps folks can help each other here and if you have a question, post here, don't ask me privately! I've only created a spreadsheet for the Rostock but it could be easily changed to support the Orion or other deltas. The program is called ZMapper.py and I'm the only one who has tested it so it likely will have a bug or two. At this point, it does a simple table lookup to get the delta Z value. Ideally it will do a linear interpolation, but I want to validate the concept before putting a lot of effort into it. I don't want to post the code at this point so PM me please.

Regards,
Michael

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

I can post the spreadsheet file so you can see what's involved.

EDIT: New file includes layout for Orion beds.
Rostock-Orion-HeightMap.xlsx
(556.48 KiB) Downloaded 465 times

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Good show!

Have you considered pairing that up with the likes of a project like this?
http://hackaday.io/project/511-digital- ... face-probe

I'm looking for a more convenient dial indicator which is capable of providing measurement output, but the HF variety probably isn't too terrible.

With that, you could make a pretty awesome table with proper z offset for every point on the plate.

I'm a Python developer by trade, but I suck at math. Feel free to send me the script. I'll check it out and test it.
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Yes. I actually run a CNC router (thanks Eaglezsoar) and two CNC mills and have electronic Z setter gauges for all of them. On mills, machinists don't mind having a dedicated tool to set the datum. But it's a pain to change "tools" on a 3D printer so something more like a problem is probably a better solution.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Z measurement instructions...

Post by mhackney »

I forgot to add this to the original post so here goes...

The values in the CSV (matrix) are delta Z values. First you need the Z length at X=Y=Z=0 (center of the plate, nozzle just grazing a sheet of paper).

For each data point you collect with a probe, you want to SUBTRACT the Z length from that value to get delta Z values. "Dips" will be positive, "humps" will be negative. Here is an example that shows the map of my Rostock Max V1:
Screen Shot 2015-02-02 at 5.17.18 PM.png
Note that it has 2 low areas between the X-Z and Y-Z towers (Z is at the top).

If I had humps, there would be some (-) values in there.

So, if you think about it, if you know that you have a "dip" between the X-Z towers that's about 1mm (you could even measure it with a feeler gauge - metric) you could populate this area by hand and slowly decrease the value out to the edge of the area by decreasing the delta Z by a bit (say .05mm is reasonable). You'd be surprised how close you can actually get it.

Finally, once you create the spreadsheet, export or save it as a CSV file with a cvs extension.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Updated the spreadsheet to include the Orion bed layout.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Awesome. I sent you an e-mail.

Subject: My E-mail - RE: Zmapper.py

Probably went to your spam folder if it's not in your inbox.

Oh, I haven't tried KISSlicer yet, but it runs on Linux so I'll give it a shot.

I'll start the measurements tonight.
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Got it and replied. Yup, it had found its way into my spam folder!

You can also run it from the command line. But this is untested on gcode from other slicers, only KISS at this time.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Oh, and I added an instructions tab in the spreadsheet with more information on how to create the delta Z map.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
McSlappy
Printmaster!
Posts: 800
Joined: Wed Dec 11, 2013 9:11 pm
Location: Queensland, Australia
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by McSlappy »

This is cool.
I loved my Rostock so much I now sell them in Oz :)
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

If it works for folks it will be cool! I am very pleased with the results I am getting though so I think it will be a case of hardening the code so it is robust. Then I need to sort out how best to create the matrix in a firmware independent way.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Wow... I just realized something...

I can drive my printer with Python.

Script below may not work properly with waiting for temperatures and stuff.. The printer sends back temperature data while waiting, but I'm not sure what messages come after the temperature target is reached. I'm using GCODE without any temperature controls whatsoever, just movements, messages, and alarms.

So, all I have to do for the manual matrix build is: generate gcode moves to all the coordinates in the matrix, send one at a time to the printer and wait for input from the user to move to the next.

Measure, record, on to the next point.

Obviously the next step is: Replace me with a digital dial indicator, and change the resolution from 10mm to 0.01mm or so.

Thank you mhackney for turning my brain on!

ETA: real-time adjustments? Looping a circle pattern and being able to change a z value on the fly? Sounds like fun!

Code: Select all

__author__ = 'jfindley'
import serial


def prep_x(x):
    return x and (x.strip().startswith("G") or x.strip().startswith("M"))


def parse_x(x):
    return x.strip().split(";")[0]

s = serial.Serial("<your port here>", <your baud rate here>)
gcode = [parse_x(x) for x in open("<your gcode here>", 'r').readlines() if prep_x(x)]

wait_count = 0
while True:
    data = s.readline()
    print data

    if "wait" in data:
        wait_count += 1
     
    # Printer has had time to settle
    if wait_count == 10:
        break

for line in gcode:
    print "Sending: ", line
    while True:
        s.write(line.strip() + "\n")
        res = s.readline()

        # If the printer is busy, it doesn't say anything. Or, it'll tell you to resend, or it'll say OK
        while not res:
            res = s.readline()

        print "Response: ", res
        if not "Resend" in res:
            break
print "GCODE executed. Finishing."

User avatar
Jimustanguitar
ULTIMATE 3D JEDI
Posts: 2608
Joined: Sun Mar 31, 2013 1:35 am
Location: Notre Dame area
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by Jimustanguitar »

I've got a Rostock script in the works. It's 74 pages of GCode, but I think it's correct. I'll test that today and then either MHackney or myself will post it for everyone.

Stay tuned :)
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

ZMapper.py download

Post by mhackney »

Thanks guys. I got your script Jim. I'll try it out as soon as my printer frees up. Meanwhile, I'm posting the ZMapper.py code here now. Share and share alike as they say! It is a zip file.
ZMapper.py.zip
(2.19 KiB) Downloaded 301 times

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Figures that as soon as I get a new idea to play with that I'd get a huge project at work with a short deadline AND a cold/fever.

I did find some time to work out a script that generates ZMapper compatible points given a build plate radius and point spacing (e.g., 10mm, 1mm, etc) and the manual interface for iterating over the points within the buildplate, left to right, top to bottom, sending the G1 commands to the printer, and prompting the user to enter the z measurement at each one before moving to the next. Hopefully I can get around to testing it today. I'll try to make a cross platform way of auto-detecting the serial port the printer is connected to today. I think as long as a RAMBo is being used, it will work.

I also spent a little time analyzing cura, slic3r, and KISSlicer gcode and found a way to support them all. slic3r will be a little more difficult because they don't do any of the fancy commenting that cura and KISSlicer do. Will need use positive lookahead assertions for that one.

Anyone have any recommendations on a reasonably priced (~300USD) digital indicator with RS232 output?

The mitutoyo ones are neat, but I think they require another module which costs ~245USD.
User avatar
dpmacri
Printmaster!
Posts: 314
Joined: Sat Aug 17, 2013 1:01 am
Location: Beaverton, OR, USA

Re: Experimental gcode auto Z compensation ready to test...

Post by dpmacri »

After reading this thread and finding this article: http://www.robotroom.com/Digital-Indicator-1.html I went ahead and ordered the BG Micro digital indicator. I have no idea how well (or not) it will work but figured I'd attach it to my effector and take a stab at writing an auto-sampling routine.

Thanks for the great contribution Michael!
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Heh, I saw that one, too, but the flat end turned me off (I mean, you can't go wrong for < $10, but still)

I'll post up my script so far when I get home. I added a function to generate a 3d scatterplot of the buildplate with z-compensation added, so you can see what the buildplate looks like in terms of points in space.
User avatar
dpmacri
Printmaster!
Posts: 314
Joined: Sat Aug 17, 2013 1:01 am
Location: Beaverton, OR, USA

Re: Experimental gcode auto Z compensation ready to test...

Post by dpmacri »

radicaldev wrote:Heh, I saw that one, too, but the flat end turned me off (I mean, you can't go wrong for < $10, but still)

I'll post up my script so far when I get home. I added a function to generate a 3d scatterplot of the buildplate with z-compensation added, so you can see what the buildplate looks like in terms of points in space.
Yeah, I had the same thought but figured it was 1/3 the price of one from Harbor Freight. I figured I'll modify it somehow by either attaching something else to the end or cutting it to a sharper/rounded point.
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Let me know how you like it. I'm going to pick up the HF one today because I haven't found an indicator I feel like dumping $500 on yet.

SeeMeCNC shipped my spare parts really fast, so I've got a box on my doorstep with a new platform, arms, joints, axles, clips, etc for some unconcerned hackery this weekend.

Should be easy enough to decode the output with an Arduino. I've got plenty of them laying around.

If it turns out that I can print a pretty accurate platform that can be interchanged with the original one, and the HF indicator works, and a 3.3v arduino pro micro works for this purpose, then since I hate the 3.3v versions of the pro-micro, I may just make up some boards and send them to people who want it.
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

Code so far. Might work on Windows and Mac.

If you have issues, it's probably related to the matplotlib backend, which you may want to change from TKAgg to wx or something, in the generator.py file in the lib directory.

Should work if your printer has a RAMBo board.

Edit values in EZMapper.py including build plate radius, com port, and baud rate.

The speeds and times set are modest, and show how the algorithm works. There are some issues still with the printer communication, but as I learn more about driving it, It'll straighten out.

Comes out of the box neutered, shoul dbe able to just run EZMapper.py and see the magic happen.
Attachments
RadicalZMapper.zip
(1.53 MiB) Downloaded 288 times
User avatar
bvandiepenbos
Printmaster!
Posts: 923
Joined: Thu Apr 05, 2012 11:25 pm
Location: Goshen, IN
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by bvandiepenbos »

Nice work Michael and all.
~*Brian V.

RostockMAX v2 (Stock)
MAX METAL "ShortyMAX"
MAX METAL Rostock MAX Printer Frame
NEMESIS Air Delta v1 & v2 -Aluminum delta printers
Rostock MAX "KITT" - Tri-Force Frame
GRABER i3 "Slim"
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

In talking with Jimustanguitar yesterday about the data collection I had a bit of an "ah ha" moment. The Rostock has a BIG bed that requires over 700 data points to map out. But, most of the points are not in problem areas. For instance, almost everyone can dial in a 4" diameter area at the center of their bed. It's the areas between towers that are the most problematic. With the 4 point calibration for delta radius and tower lengths you can be certain that things are planar from the center to each of the three towers - and this is easy to accomplish with manual calibration on every printer I've worked with. It's that peak nether region between towers that is an issue. So, I came up with a way to reduce the number of collected data points to 25 (Rostock) or 19 (Orian/Kossel) and use interpolation to fill in the blanks. My math shows that even if your printer has a 5mm non-planarity issue, I should be able to compensate that out to less than .05mm and maybe more. I recalled a form of interpolation that I used on a digital camera product I developed in 2002 called Barycentric interpolation that should work well. The idea is to divide the platter up into triangles and use the corners of those triangles as data collection points. Then, one uses the corners to interpolate any point within the triangle. There is a good summary of it here: https://classes.soe.ucsc.edu/cmps160/Fa ... lation.pdf

The cool thing is, I can arrange the data points/triangle corners such that I get better coverage/resolution in the problem areas between towers. It might take a little experimentation to find an optimal arrangement of points. But reducing the number of data points collect to 25 means that you could do the matrix generation manually in 5 minutes using a simple touch probe.

(The touch probe is simply a 1/4" or so rod about 1/4" tall that has a wire attached. Another wire is attached to your hot end. These two wires run into your RAMBo on the Z min switch input. When the nozzle touches the probe, it completes the circuit, triggering a "touch". This is very reproducible and accurate and used on CNC machines. The movement of the nozzle is done via a gcode script similar to the one Jim created. As the nozzle moves to a new point, you just position the probe under it and wait for the touch. I did it in 3 minutes last night. Very simple, very effective, very cheap, very convenient.)

Cheers,
Michael

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
radicaldev
Printmaster!
Posts: 64
Joined: Wed Jan 21, 2015 11:40 pm

Re: Experimental gcode auto Z compensation ready to test...

Post by radicaldev »

This is a really cool thread. I've learned a lot from it.

I can't wait to get my indicator set up so I can map my bed, and then compare different compensation techniques.

The M code M114 allegedly returns the current position, but it behaves in an odd manner. Sometimes, if you execute a G1 command followed by an M114, the printer won't respond with the coordinates until the G1 finishes, which is awesome. Sometimes, though, it will respond immediately with the coordinates given to the G1, before the print head actually gets there.

My hope is that I can remove all the wait times and just rely on the printer being told to go somewhere, and then telling me when it actually got there, so that measurements and steps could be taken in a continuous (e.g., fast) manner.

does z-min check continuity or voltage? If so, then the touch probe could also be a .005" shim with a piece of wire (powered, if voltage is required) carefully attached to it.

One of the things I'm curious about is whether or not there is a difference in z height at different layer heights.

e.g., for z=0, real z may actually be high by 10 thousandths of an inch, but at z=1, maybe it's high by 8 thousandths. The indicator should be able to shed some light on that.
User avatar
bvandiepenbos
Printmaster!
Posts: 923
Joined: Thu Apr 05, 2012 11:25 pm
Location: Goshen, IN
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by bvandiepenbos »

(The touch probe is simply a 1/4" or so rod about 1/4" tall that has a wire attached. Another wire is attached to your hot end. These two wires run into your RAMBo on the Z min switch input. When the nozzle touches the probe, it completes the circuit, triggering a "touch". This is very reproducible and accurate and used on CNC machines. The movement of the nozzle is done via a gcode script similar to the one Jim created. As the nozzle moves to a new point, you just position the probe under it and wait for the touch. I did it in 3 minutes last night. Very simple, very effective, very cheap, very convenient.)
BRILLIANT!! you don't even need to fart around with a dial indicator. Simple and just as accurate (or more) than a indicator.
I love it.
~*Brian V.

RostockMAX v2 (Stock)
MAX METAL "ShortyMAX"
MAX METAL Rostock MAX Printer Frame
NEMESIS Air Delta v1 & v2 -Aluminum delta printers
Rostock MAX "KITT" - Tri-Force Frame
GRABER i3 "Slim"
User avatar
mhackney
ULTIMATE 3D JEDI
Posts: 5391
Joined: Mon Mar 26, 2012 4:15 pm
Location: MA, USA
Contact:

Re: Experimental gcode auto Z compensation ready to test...

Post by mhackney »

Repetier supports a probing gcode - G30 - that will probe and report the point. You need that for probes that are triggered. For an indicator, a simple G1 should be fine with a pause. Jim's script does that.

I just sent Jim the updated 25 points to scan so he is working on the new script. I've already implemented the Barycentric interpolation and "point in triangle" test and had another "ah ha" moment with that. They are combined into a single function. Turns out that Barycentric coordinates are a good way to determine if a point is in a triangle. So, if you are testing to see which triangle the point is in and it is, you've already done all the math. I love it when things come together/

Z min would act just like an endstop switch. It triggers when the probe touches (so it is a normally open NO switch). It checks continuity. Yes, the probe could be thin. I use .001" SS for my milling machine. I plan to do the same for this probe. But I wanted to keep it conceptually simple for folks here. We can get into the details/enhancements once we have some results!

Your last question is a good one. Mechanical issues may very well result in non-linear delta zs. Calibration errors might too, I need to think about that.

Sublime Layers - my blog on Musings and Experiments in 3D Printing Technology and Art

Start Here:
A Strategy for Successful (and Great) Prints

Strategies for Resolving Print Artifacts

The Eclectic Angler
Post Reply

Return to “General Tips 'N Tricks”