Cutting QR Code - Stuck in Floating Point Hell 😩

Cutting QR Code - Stuck in Floating Point Hell 😩

scott58PC2
Explorer Explorer
420 Views
10 Replies
Message 1 of 11

Cutting QR Code - Stuck in Floating Point Hell 😩

scott58PC2
Explorer
Explorer

Hey all,

 

I’m a long time software engineer and a fairly recent Fusion 360 user (it’s been about 2.5 years). I mostly use Fusion 360 to create various models and designs for the 3D printing company I work for.

We’re trying to automate a number of manual processes with scripts/add-ins. I’ve been tasked with creating an add-in that can stamp any face with a qr code of varying side length. I started by looking at Patrick Rainsberry’s (tapnair) QRCoder git repository and then settled on my own solution.

 

I decided to create an add-in that allowed a user to select a face, the depth of the QR code (cut depth) and the size, then would stamp the face with a generated qr code by cutting the white pixels out of the face and leaving the black pixels raised.

 

This was fairly straight forward. I pulled in the qrcode dependency via pip3 install qrcode --target /project/path to the project to get the QRCode object and filled out the command_execute() method in the commandDialog’s entry.py class.

At a high level my logic to stamp the QR Code is as follows:

  1. Use the qr code side length to calculate start X and Y and the individual pixel size.
  2. Convert the QRCode to a matrix and loop through the matrix identifying each white pixel.
  3. When a white pixel is found, calculate the coordinates needed for the 2 points to draw the pixel.
  4. Draw a pixel with the two points and the addTwoPointRectangle() method.
  5. After all white pixels have been drawn, loop through the sketch profiles and add them to an object collection.
  6. Use the object collection to extrude (cut) the white pixels the depth determined by the user.

 

At first I thought this was a complete failure as this produced a feature that looked nothing like a qr code, but I quickly realized there wasn’t really any reason this should fail. After a couple of hours of debugging I realized the problem was floating point arithmetic. I had entered floating point hell.

 

I came to this realization when I added a tiny gap between each pixel and the code looked perfect (minus the gaps). See the image below:

Screenshot 2025-05-23 at 8.09.27 PM.png

 

I then removed the gap and diagnosed further. I tried filtering out the smaller profiles and rounding certain calculations. It got me closer to a valid qr code but still nothing that would scan or resemble the code with gaps. This approach however, solidified my suspicion that floating point arithmetic was to blame as I had multiple examples where I’d get qr codes with tiny connected regions and/or features of the qr code that were merged into a single region. See the below photo for an example:

Screenshot 2025-05-23 at 8.06.55 PM.png

 

Below are the floating point calculations happening when looping through the matrix:

   pixel_size = side_length / qr_code_size
   start_x = center.x - (side_length / 2) # center is the center point of the selected face

   start_y = center.y - (side_length / 2)

   # loop through the col and row of the qr code matrix
   # if there’s a white pixel calculate two points and add to the sketch

   x0 = start_x + col * pixel_size
   y0 = start_y + row * pixel_size

   x1 = x0 + pixel_size
   y1 = y0 + pixel_size

   pt1 = adsk.core.Point3D.create(x0, y0)
   pt2 = adsk.core.Point3D.create(x1, y1)
   sketch.sketchCurves.sketchLines.addTwoPointRectangle(pt1, pt2)

 

I can’t seem to get the precision/rounding of the floating point numbers just right. I’ve tried rounding, flooring the number and even using Python’s Decimal object. I understand why this is happening (floating point hell, a common problem in computing) but I can’t seem to eliminate the creation of extra sketch profiles created from precision errors. I’m working in millimeters and the side_length and qr_code_size are variable due to user input. Please can someone help me get out of floating point hell!

  

Thanks

0 Likes
421 Views
10 Replies
Replies (10)
Message 2 of 11

BrianEkins
Mentor
Mentor

I don't think this is a floating-point issue. I know there can be issues with floating-point numbers, but I don't believe that should come into play with what you're doing because floating-point math is many orders of magnitude more precise than the precision Fusion uses when determining if two points are equal. I just saw what looks like a bug in this area the other day.

 

If you create the profile and then manually select the different profiles to create the extrusion, do you still see the problem? If so, can you post the f3d here for the developers to look at? If you only see the problem when using the API to create the extrusion, there's a possibility there's a bug somewhere in the API pipeline to create the extrusion.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 3 of 11

Jorge_Jaramillo
Collaborator
Collaborator

Hi,

 

I believe your problem is caused by creating SketchPoint's which are too close to each other.

In a previous development I made, I just looked for a previous created SketchPoint in the Sketch which is at a maximum distance (tolerance) before creating any new SketchPoint.

 

So, you can use the SketchPoint.geometry.isEqualToByTolerance(newPoint, MAX_TOLERANCE) to check if other point is "equal" to the one you need to be created.

 

Please note:

isEqualToByTolerance method checks to see if this point and another point are equal within the specified tolerance

MAX_TOLERANCE = 1e-6

newPoint : Point3D = Point3D(x0, y0)

SketchPoint is taken inside a loop over Sketch.sketchPoints collection from the Sketch your code is working on.

 

This is link to the Fusion's API reference for the isEqualToByTolerance method: https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-3CCAD4A1-0008-483C-88D1-0B30774C0A2B

 

According to my experience, I'd suggest to work with tolerances when working with floating point numbers when checking for "equality".

 

I hope this could help.

 

Regards,

Jorge Jaramillo

 

0 Likes
Message 4 of 11

scott58PC2
Explorer
Explorer

Hey Jorge,

I've seen a couple things on my end since my first post that suggest that this might not be a floating point issue as well. I'm interested in using SketchPoint.geometry.isEqualToByTolerance(newPoint, MAX_TOLERANCE) with my current solution.

@Jorge_Jaramillo Just to clarify. The solution you're suggesting is that before I create the new points to create the profile via addTwoPointRectangle(pt1, pt2) I should loop through all of the previous points inside of the Sketch.sketchPoints collection and use SketchPoint.geometry.isEqualToByTolerance(newPoint, MAX_TOLERANCE) to check if there are any pre-existing points that are equal. If they are equal I return and reuse the previous points, if they are not equal I use the newly created points. Is this accurate, let me know please?

@BrianEkins I'm going to try Jorge's approach. If this is the issue I'll let you know, if this still seems to be bug I'll post the .f3d for the devs to take a look.

Thanks both for the help!

Scott

0 Likes
Message 5 of 11

Jorge_Jaramillo
Collaborator
Collaborator

Hi @scott58PC2 ,

 

Yes. What you described is what I meant. 

I hope this could help.

 

Regards,

Jorge Jaramillo

 

0 Likes
Message 6 of 11

scott58PC2
Explorer
Explorer

Hey Guys,

I've added a generate_point_with_tolerance(sketch: adsk.fusion.Sketch, newPoint: adsk.core.Point3D) -> adsk.core.Point3D method to my code that checks to see if there exists a point within the 1e-6 tolerance. If it exists, the method returns the point inside the tolerance instead of the new point. Below is my code that utilizes the new tolerance method:

   pixel_size = side_length / qr_code_size
   start_x = center.x - (side_length / 2) # center is the center point of the selected face

   start_y = center.y - (side_length / 2)

   # loop through the col and row of the qr code matrix
   # if there’s a white pixel calculate two points and add to the sketch

   for row in range(qr_code_size):
    for col in range(qr_code_size):
        if not qr_code_matrix[row][col]:
           x0 = start_x + col * pixel_size
           y0 = start_y + row * pixel_size

           x1 = x0 + pixel_size
           y1 = y0 + pixel_size

           pt1 = adsk.core.Point3D.create(x0, y0)
           pt2 = adsk.core.Point3D.create(x1, y1)

           pt1_tolerance = __generate_point_with_tolerance(sketch, pt1)
           pt2_tolerance = __generate_point_with_tolerance(sketch, pt2)
           
           sketch.sketchCurves.sketchLines.addTwoPointRectangle(pt1_tolerance, pt2_tolerance)

    # generate_point_with_tolerance method definition
    def __generate_point_with_tolerance(sketch: adsk.fusion.Sketch, newPoint: adsk.core.Point3D) -> adsk.core.Point3D:
        tolerance = 1e-6

        for point in sketch.sketchPoints:
            if point.geometry.isEqualToByTolerance(newPoint, tolerance):
                return point

        return newPoint


This didn't seem to fix the issue. @Jorge_Jaramillo do you see anything wrong with my implementation? I tried returning the point a few different ways as I was worried I was over looking or unaware of how Python manages custom objects under the hood but they all produced the same result which was essentially the exact same as the previous implementation. Am I missing something here?

@BrianEkins Unless I've overlooked something fairly major in my above approach I'm starting to think this might be a bug as you first suggested. I stepped through each row iteration and looked at how the QR Code is being drawn row by row. I found a few interesting things. The first few iterations seem to be fine (rows 0 - 5). I've attached a photo of the first few iterations.

Screenshot 2025-06-10 at 12.43.10 AM.png

The next iteration (row 0 - 6) seems to break the QR Code and is where I start to see profiles being unintentionally added. Below is an image of the QR code at row iteration 6:

Screenshot 2025-06-10 at 12.40.06 AM.png

You can see that once I start drawing regions of the sketch that encircle or surround other features of the QR code, ADSK groups the profiles together effectively removing that part of the QR code. @BrianEkins Is this expected behaviour or some sort of optimization I can toggle on or off? Or do you think this could be a bug? If it's a bug can we discuss how I can help the development team resolve this? Let me know.

Regards,
Scott
 

 

0 Likes
Message 7 of 11

Jorge_Jaramillo
Collaborator
Collaborator

Hi,

Can you share the piece of the code you use to select and cut the "white" profiles?

 

0 Likes
Message 8 of 11

scott58PC2
Explorer
Explorer

Hey @Jorge_Jaramillo,

Here's how I add the newly drawn profiles and cut them from the surface:

  # Draw rectangle in nested for loop with addTwoPointRectangle() above

  profiles = adsk.core.ObjectCollection.create()

  # Add profiles to collection start at 1 as first profile is always the sketch face
  for i in range(1, sketch.profiles.count):
      profiles.add(sketch.profiles.item(i))

  # Extrude cut using the profile collection
  extrude_input = design.activeComponent.features.extrudeFeatures.createInput(profiles, adsk.fusion.FeatureOperations.CutFeatureOperation)
  depth_value = adsk.core.ValueInput.createByString(f'-{depth} cm')
  extrude_input.setDistanceExtent(False, depth_value)
  design.activeComponent.features.extrudeFeatures.add(extrude_input)


@Jorge_Jaramillo Let me know if you have any questions or need anything else.

Scott

0 Likes
Message 9 of 11

Jorge_Jaramillo
Collaborator
Collaborator

Hi,

 

I believe that you have to filter just the profiles that represent a "white pixel".

It happens that when there is an area surrounded completed by a set of profiles, that area becomes a profile itself, like the blue area in the image below:

Jorge_Jaramillo_0-1749787337038.png

 

You need to select the profiles that represent a "white pixel" inside  the for loop.

Does it make sense to you?

 

Regards,

Jorge Jaramillo

 

 

0 Likes
Message 10 of 11

scott58PC2
Explorer
Explorer

@Jorge_Jaramillo I see what you're saying. Off the top of your head do you know if an easy way to filter these? I tried using area but that was not effective. Thoughts?

0 Likes
Message 11 of 11

Jorge_Jaramillo
Collaborator
Collaborator

Hi @scott58PC2 ,

 

I'd change the algorithm for something like this:

- Make a grid with "N+1" vertical lines and "N+1" horizontal lines; this will make a profile grid NxN with a profile for each pixel in the QRcode.

- Iterate over the lines in the QR's matrix:

   - Iterate over each cell in the line:

      - If it's a white pixel, look for the equivalent profile in the grid, and add it (the profile) to the profile collection

- Make the extrude with the profile collection.

 

This way you certainly extrude the right profiles, no matter of the size or position (inner, outer) of them.

 

Please note: the way you are building the QRcode in the XY plane, it will be flipped vertically; some readers won't read it.

 

I hope this help you.

 

Regards,

Jorge Jaramillo

 

0 Likes