Trying to debug Pop/PushMatrix -- Can I see the stack?

I had some working code, added some simple translation, and now I’m getting missing a pushMatrix() error. I believe all my push, pop, and translate commands are balanced, so I don’t see where the error is.

Is there a way to viewthe size of the matrix stack (eg. len(matrixStack) so I can isolate where the problem occurs?

I took my code, removed most of the work, and left the matrix-related commands in – I don’t see where I’m unbalanced, poping more matrices than I push. Or else maybe I am missing some basic concept of how this is supposed to work.

The idea is: Start with A4-size page, translate in by a border margin, then iterate through some smaller cells on the upper part of the document, then translate back to the original border-margin point, and retranslate to a point farther down the page.

def setup():
    noLoop()

    #initial alignment for Dish Block
    translate(0,0)  # (redundant)
    pushMatrix() #store the default reference frame
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX)

    for col in range(COLS):
        for row in range(ROWS):
            pushMatrix() #store the default reference frame
            translate(CELL_WIDTH*col+0.5*CELL_WIDTH, CELL_HEIGHT*row+(0.5*(CELL_HEIGHT-LOWER_CIRCLE_MARGIN_MM*DPMM)))
            circle(0,0,PETRI_DIAMETER)
            popMatrix() #lose this shifted reference frame
    endRecord()
    
    for col in range(COLS):
        for row in range(ROWS):
            pushMatrix() #store the default reference frame
            translate(CELL_WIDTH*col+0.5*CELL_WIDTH, CELL_HEIGHT*row+(0.5*(CELL_HEIGHT-LOWER_CIRCLE_MARGIN_MM*DPMM)))

            print("placed {0} bugs in petri {1}".format(count,(col,row)))
            popMatrix() #lose this shifted reference frame
     
    for col in range(COLS):
        for row in range(ROWS):  
            pushMatrix() #store the default reference frame
            translate(CELL_WIDTH*col, CELL_HEIGHT*row)
            name_field = names.pop(0)
            popMatrix() #lose this shifted reference frame
    endRecord()
    
    

    #translate to TEXT BLOCK
    popMatrix() should go back to original(0,0)
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX+TEXT_BLOCK_HEIGHT_PX)
    rect(0,0,TEXT_BLOCK_WIDTH_PX, TEXT_BLOCK_HEIGHT_PX)
    
         
       
    
                
    print("Done!")
                    
def draw():
    pass
1 Like

I tested your code. It’s working fine for me (like, no errors). I tried this combination of constants:

BORDER_MARGIN_PX,BORDER_MARGIN_PX,COLS,ROWS,CELL_WIDTH,CELL_HEIGHT,PETRI_DIAMETER,LOWER_CIRCLE_MARGIN_MM,DPMM,BORDER_MARGIN_PX,BORDER_MARGIN_PX,TEXT_BLOCK_HEIGHT_PX,TEXT_BLOCK_WIDTH_PX,TEXT_BLOCK_HEIGHT_PX = 2,2,2,2,2,2,2,2,2,2,2,2,2,2
count, names = 2, [1,2,3,4]

Perhaps the problem is somewhere else in your program?

1 Like

That’s bizarre. Could it be there is some interaction with my use of beginRecord and endRecord ??? Because even if I do multiple pushMatrix() operations before, it still complains with the same failure on the final popMatrix()

The full code below.

add_library('svg')
# xml.etree module
from xml.etree import ElementTree
import math
import utils
import life


import random as ran

DPMM = 96/25.4  # dots per mm  
PAPER_WIDTH_PX = int(210 * DPMM)   #A4
PAPER_HEIGHT_PX = int(297 * DPMM)  #A4

BORDER_MARGIN_PX = 15 * DPMM
TEXT_BLOCK_HEIGHT_PX = 30 * DPMM
TEXT_BLOCK_WIDTH_PX = PAPER_WIDTH_PX - 2*BORDER_MARGIN_PX
DISH_BLOCK_HEIGHT_PX = PAPER_HEIGHT_PX - 2*BORDER_MARGIN_PX - TEXT_BLOCK_HEIGHT_PX
DISH_BLOCK_WIDTH_PX = PAPER_WIDTH_PX - 2*BORDER_MARGIN_PX

ROWS=3
COLS=2

CELL_HEIGHT = DISH_BLOCK_HEIGHT_PX / ROWS
CELL_WIDTH = DISH_BLOCK_WIDTH_PX / COLS

PETRI_DIAMETER = 0.85*min(CELL_WIDTH, CELL_HEIGHT)
LOWER_CIRCLE_MARGIN_MM = 10#*DPMM

TEXT_SIZE_PX = 0.8*LOWER_CIRCLE_MARGIN_MM * DPMM

print("CELL_HEIGHT {0}  CELL_WIDTH {1}".format(CELL_HEIGHT, CELL_WIDTH))


OUTPUT_FN = '.\\graphics\\combined.svg'


def settings():
    size(PAPER_WIDTH_PX, PAPER_HEIGHT_PX)




def setup():
    noLoop()
    svgs = [ '.\\graphics\\99_dishes.svg', ]
    
    #initial alignment for Dish Block
    translate(0,0)
    pushMatrix() #store the default reference frame       

    #even pushing multiple uncessary times results in same failure.
    #translate(0,0)
    #pushMatrix()     
 
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX)

    beginRecord(SVG, svgs[0])
    noFill()
    for col in range(COLS):
        for row in range(ROWS):
            pushMatrix() #store the default reference frame
            #translate(CELL_WIDTH*col, CELL_HEIGHT*row)
            #circle(0.5*CELL_WIDTH,CELL_HEIGHT-0.5*PETRI_DIAMETER-LOWER_CIRCLE_MARGIN_MM*DPMM,PETRI_DIAMETER)
            translate(CELL_WIDTH*col+0.5*CELL_WIDTH, CELL_HEIGHT*row+(0.5*(CELL_HEIGHT-LOWER_CIRCLE_MARGIN_MM*DPMM)))
            circle(0,0,PETRI_DIAMETER)
            popMatrix() #lose this shifted reference frame
    endRecord()
    
    
    #speciesFactory = life.GermSpeciesFactory(PETRI_DIAMETER)
    speciesFactory = life.BezierGermSpeciesFactory(PETRI_DIAMETER)
    
    names = []
    for col in range(COLS):
        for row in range(ROWS):
            #factory = ran.choice([life.BezierGermSpeciesFactory(PETRI_DIAMETER),life.GermSpeciesFactory(PETRI_DIAMETER)])
            factory = life.BezierGermSpeciesFactory(PETRI_DIAMETER)
            singleSpecies = factory.new_species()
            print(singleSpecies)
            names.append(singleSpecies.name)
            bugs = []
            svgs.append(".\\graphics\\{0}_cell.svg".format((col,row)))
            beginRecord(SVG, svgs[-1])
            pushMatrix() #store the default reference frame
            #xtrans, ytrans = (CELL_WIDTH*col+0.5*CELL_WIDTH, CELL_HEIGHT*row+0.5*CELL_HEIGHT)
            #print("translating to {0}, {1}".format(xtrans, ytrans))
            #translate(xtrans, ytrans)
            translate(CELL_WIDTH*col+0.5*CELL_WIDTH, CELL_HEIGHT*row+(0.5*(CELL_HEIGHT-LOWER_CIRCLE_MARGIN_MM*DPMM)))

            count = 0
            for _ in range(ran.randint(0,20)):
                #print("col {0}   row {1}  bug {2}".format(col, row, _))
                bugs.append(singleSpecies.germ())
                bugs[-1].plot(show_control=False)
                count+=1
            print("placed {0} bugs in petri {1}".format(count,(col,row)))
                    
            #choose specie and quantity
            # get list of n-germs, name from a new species
            popMatrix() #lose this shifted reference frame
            endRecord()
     
    FONT_SIZE=10

    svgs.append(".\\graphics\\88_text.svg")
    beginRecord(SVG, svgs[-1])   
    for col in range(COLS):
        for row in range(ROWS):  
            pushMatrix() #store the default reference frame
            translate(CELL_WIDTH*col, CELL_HEIGHT*row)
            fill(0)
            name_field = names.pop(0)
            text(name_field,
                0.5*CELL_WIDTH,
                CELL_HEIGHT-(0.5*LOWER_CIRCLE_MARGIN_MM*DPMM)-0.5*FONT_SIZE,
                CELL_WIDTH,
                LOWER_CIRCLE_MARGIN_MM*DPMM
                )        
            popMatrix() #lose this shifted reference frame
    endRecord()
    
    
    svgs.append(".\\graphics\\77_title.svg")
    beginRecord(SVG, svgs[-1])   
    #translate to TEXT BLOCK
    print("dies here...")
    popMatrix() #goes back to original(0,0)  ###FAILS HERE
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX+TEXT_BLOCK_HEIGHT_PX)
    rect(0,0,TEXT_BLOCK_WIDTH_PX, TEXT_BLOCK_HEIGHT_PX)
    
    endRecord()
    
    
    
    
    
    
    
    # create an empty combined.svg file
    beginRecord(SVG, OUTPUT_FN); endRecord()
    ElementTree.register_namespace('', 'http://www.w3.org/2000/svg')
    tree = ElementTree.parse(OUTPUT_FN)
    combined = tree.getroot()
    
    # add the (circles/squares) svgs to the combined.svg file
    for svg in svgs:
        file = ElementTree.parse(svg).getroot()
        group = ElementTree.SubElement(combined, 'g')
        group.set('id', svg)
        group.set('inkscape:groupmode', 'layer')
        group.set('inkscape:label', svg)
        for child in file.getchildren():
            group.append(child)
    
    # save the changes to the combined.svg file
    tree.write(OUTPUT_FN)
    
    if True:    
        with open(OUTPUT_FN) as f:
            s = f.read()
        
        with open(OUTPUT_FN, "w") as f:
            old_string = "text style=\"stroke:none;\""
            new_string = "text style='stroke:none;text-anchor:middle;dominant-baseline:center'"       
            s = s.replace(old_string, new_string)
            f.write(s)
                
    print("Done!")
                    
def draw():
    pass

And if you place an endRecord() above the problematic line?

    ...
    endRecord()
    popMatrix() #goes back to original(0,0)  ###FAILS HERE
    ...

Yeah, so I moved the popMatrix() outside the begin/endRecord block and it works:

        popMatrix()
        svgs.append(".\\graphics\\77_title.svg")
        beginRecord(SVG, svgs[-1])   
        #translate to TEXT BLOCK
        print("dies here...")
        noFill()
        translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX+DISH_BLOCK_HEIGHT_PX)
        rect(0,0,TEXT_BLOCK_WIDTH_PX, TEXT_BLOCK_HEIGHT_PX)
        endRecord()

I don’t understand why this is required, as in other blocks, the popMatrix() occurs inside a begin/EndRecord() Wish there was a way to view ‘the stack’ and figure out what’s going on behind the scenes.

Feels like there is some weird interaction I don’t understand between beginRecord endRecord and *matrix functions. When I run printMatrix inside a record block, for example, it prints two matrices to the console.

for example:


   print("before anything")
    printMatrix()
    pushMatrix() #store the default reference frame       

    beginRecord(SVG,svgs[0])
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX)
    print("translated BORDER_MARGIN_PX: {0}".format(BORDER_MARGIN_PX))
    printMatrix()
    noFill()
    rect(0,0,10,10)
    endRecord()
   
    print("done")

yields:

before anything
1.0000 0.0000 0.0000
0.0000 1.0000 0.0000

translated BORDER_MARGIN_PX: 56.6929133858
01.0000 00.0000 56.6929
00.0000 01.0000 56.6929

01.0000 00.0000 56.6929
00.0000 01.0000 56.6929

done

Yeah, I’m suspecting that that it maintains a separate transformationMatrix for the SVG recrod I’m creating. When I’m not inside an SVG record, it strictly shows a single matrix:

   print("before anything")
    printMatrix()
    pushMatrix() #store the default reference frame       

    beginRecord(SVG,svgs[0])
    translate(BORDER_MARGIN_PX, BORDER_MARGIN_PX)
    print("translated BORDER_MARGIN_PX: {0}".format(BORDER_MARGIN_PX))
    printMatrix()
    noFill()
    rect(0,0,10,10)
    endRecord()
   
    print("done") 
   
    print("is this a single?")  #yes it's a single
    printMatrix()
    print("-----")
    > 
   before anything

1.0000 0.0000 0.0000
0.0000 1.0000 0.0000

translated BORDER_MARGIN_PX: 56.6929133858
01.0000 00.0000 56.6929
00.0000 01.0000 56.6929

01.0000 00.0000 56.6929
00.0000 01.0000 56.6929

done
is this a single?
01.0000 00.0000 56.6929
00.0000 01.0000 56.6929

_ _

1 Like