Plotting issue with RhinoPython on first run

rhinopython

#1

I’m building a script in RhinoPython and rhinoscriptsytax that will plot two small squares next to an angled, larger square. The set-up is supposed to look like this:

Right now, when I run the code for the first time after opening Rhino, it looks like this:

What’s happening is that the two squares plot on top of each other at the origin [0,0,0]. I am using blocks to handle the plotting.

Here’s my current code:

  1. Class of functions used to plot the squares:

    class vizRhino:

     # Prepares data from line segments
     def data_prep(self, line_set):
         lineSeg_x = [[0 for x in range (2)] for y in range(len(line_set))]
         lineSeg_y = [[0 for x in range (2)] for y in range(len(line_set))]
         lineSeg_z = [[0 for x in range (2)] for y in range(len(line_set))]
         
         for i in range(len(line_set)):
             for j in range(2):
                 if j == 0 :
                     lineSeg_x[i][j] = float(line_set[i].start.x.value)
                     lineSeg_y[i][j] = float(line_set[i].start.y.value)
                     lineSeg_z[i][j] = float(line_set[i].start.z.value)
                 else: 
                     lineSeg_x[i][j] = float(line_set[i].end.x.value)
                     lineSeg_y[i][j] = float(line_set[i].end.y.value)
                     lineSeg_z[i][j] = float(line_set[i].end.z.value)
     
         return lineSeg_x, lineSeg_y, lineSeg_z
     
     # Plots line segments
     def Line(self, num, lineX, lineY, lineZ, coords, color):
         block = []
    
         for i in range(len(lineX)):
             line = rs.AddLine((lineX[i][0], lineY[i][0], lineZ[i][0]), (lineX[i][1], lineY[i][1], lineZ[i][1]))
             block.append(line)
             rs.ObjectColor(line, [199,21,133])
         
         rs.AddBlock(block, [0,0,0], 'block %i'%num, delete_input = True)
         rs.InsertBlock('block %i'%num, coords)
     
     # Plots point/Updates point location
     def Point(self, pointSource):
         for i in pointSource:
             pt = rs.AddPoint(float(i.value.x.value), float(i.value.y.value), float(i.value.z.value))
             rs.ObjectColor(pt, [199,21,133])
    
         return pt
    
  2. Plotting as called within the main code

    RULE PLOTTING

     ruleSides = [r.lhs, r.rhs]
     count = 1 
     for j in range(len(ruleSides)):
         block = []
         lineX, lineY, lineZ = viz.data_prep(ruleSides[j].elements[1].elements)
    
     for i in range(len(lineX)):
         
         line = rs.AddLine((lineX[i][0], lineY[i][0], lineZ[i][0]), (lineX[i][1], lineY[i][1], lineZ[i][1]))
         block.append(line)
         rs.ObjectColor(line, [199,21,133])
         
     if count-1 == 0:
         coords = [-5.5,1,0]
         rs.AddTextDot("LHS",(-5.5,-1,0))
     elif count-1 == 1:
         coords = [-2,1,0]
         rs.AddTextDot("RHS",(-2,-1,0))
         
     point = viz.Point(ruleSides[j].elements[0])
     block.append(point)
     rs.AddBlock(block, coords, 'block %i'%count, delete_input = True)
     rs.InsertBlock('block %i'%count, coords)
    
     count+=1
    

This code is causing the plot to look like that of the second picture. My goal is to get it to plot the squares separately and away from each other from the very first run after I open Rhino. When I run the code subsequent times, the squares are plotted in the correct place, so I’m not sure if this is a mistake with how I handle the block instances?


#2

I cannot run your code as r is not defined.
It is used in:

 ruleSides = [r.lhs, r.rhs]

#3

To run the code would require another package called ‘sortal’ which was developed by my colleague (it’s a zip file), and the rule file itself, which defines the class that r is an instance of (a .py file).

Since sending the files might be too much trouble, I’ll give the coordinates for each side instead (they’re basically a set of coordinates that are then stored in lineX, lineY, lineZ). Extracting the coordinates is the purpose of viz.data_prep().

  1. For r.lhs, viz.data_prep returns the following:

lineX = [[-1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]

lineY = [[-1.0, 1.0], [-1.0, 1.0], [-1.0, -1.0], [1.0, 1.0]]

lineZ = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]

  1. For r.rhs, viz.data_prep returns the following:

lineX = [[-1.0, -1.0], [1.0, 1.0], [-1.0, -0.60000000000000009], [0.59999999999999998, 1.0], [-1.0, 0.59999999999999998], [-0.60000000000000009, 1.0], [-1.0, 1.0], [-1.0, 1.0]]

lineY= [[-1.0, 1.0], [-1.0, 1.0], [0.59999999999999998, -1.0], [1.0, -0.60000000000000009], [0.59999999999999998, 1.0], [-1.0, -0.60000000000000009], [-1.0, -1.0], [1.0, 1.0]]

lineZ = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]

Hope this helps clarify things.


#4

I made a simple version of your code and saw the same problem; does not work first time (all squares are left at origin) but works the next times. I then modified the code, setting the base_point for AddBlock to [0,0,0]. Now on the first call it looks the same as on subsequent calls. Here is the modified code:

import rhinoscriptsyntax as rs
count = 1
for j in range(2):
    block = []
    if j == 0:
        lineX = [[-1.0, -1.0], [1.0, 1.0], [-1.0, 1.0], [-1.0, 1.0]]
        lineY = [[-1.0, 1.0], [-1.0, 1.0], [-1.0, -1.0], [1.0, 1.0]]
        lineZ = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
    if j == 1:
        lineX = [[-1.0, -1.0], [1.0, 1.0], [-1.0, -0.60000000000000009], [0.59999999999999998, 1.0], [-1.0, 0.59999999999999998], [-0.60000000000000009, 1.0], [-1.0, 1.0], [-1.0, 1.0]]
        lineY = [[-1.0, 1.0], [-1.0, 1.0], [0.59999999999999998, -1.0], [1.0, -0.60000000000000009], [0.59999999999999998, 1.0], [-1.0, -0.60000000000000009], [-1.0, -1.0], [1.0, 1.0]]
        lineZ = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]]
    for i in range(len(lineX)):
        line = rs.AddLine((lineX[i][0], lineY[i][0], lineZ[i][0]), (lineX[i][1], lineY[i][1], lineZ[i][1]))
        block.append(line)
        rs.ObjectColor(line, [199,21,133])
    if count-1 == 0:
        coords = [-5.5,1,0]
        rs.AddTextDot("LHS",(-5.5,-1,0))
    elif count-1 == 1:
        coords = [-2,1,0]
        rs.AddTextDot("RHS",(-2,-1,0))
    rs.AddBlock(block, [0,0,0], 'block %i'%count, delete_input = True)
    rs.InsertBlock('block %i'%count, coords)
    count+=1

#5

Thanks! This solved my plotting problem.


#6

I have not used AddBlock or InsertBlock in any of my code previously so I studied the behavior of these some more and this is what I think they are doing: AddBlock associates a base_point with the bock. InsertBlock inserts a block so that its base_point is placed at the coordinates provided. So if the base_point of the block is the same as the coordinates used in InsertBlock then the block does not move.

The remaining mystery is why your original code worked as you intended on subsequent tries. After looking at the Python code for AddBlock my conclusion is as follows. After the first try, even if you delete the blocks from the viewport, the named blocks still exist in the database. What AddBlock does in this case is that it detects the named block, does not create a new block but it does still delete the inputs. But this existing block was created by InsertBlock on the first try which, I believe, gave it a base_point of [0,0,0]. So now InsertBlock moves the block’s base point of [0,0,0] to the coords you provided and everything works as intended. I tested this by inserting DeleteBlock just before the AddBlock and again using coords for the base_point in add block:

try: rs.DeleteBlock('block %i'%count)
except: pass
rs.AddBlock(block, coords, 'block %i'%count, delete_input = True)
rs.InsertBlock('block %i'%count, coords)

Now the code works the same on the first try and on subsequent tries, always leaving the blocks at the origin. Of course the code you want is:

try: rs.DeleteBlock('block %i'%count)
except: pass
rs.AddBlock(block, [0,0,0], 'block %i'%count, delete_input = True)
rs.InsertBlock('block %i'%count, coords)

with AddBlock assigning a base_point of [0,0,0] to the block. I would leave in the DeleteBlock statement as it will help keep your code from failing due to name collisions with previously created blocks. I protected it with try: & except: pass because it fails if the block does not exist and stops the code. You could instead put in a line to detect if the block already exists and then only delete it in that case…


#7

Here is the code without the try/except construct:

if rs.IsBlock('block %i'%count): rs.DeleteBlock('block %i'%count)
rs.AddBlock(block, [0,0,0], 'block %i'%count, delete_input = True)
rs.InsertBlock('block %i'%count, coords)