Dealing with rectangles and squares in Rhino vbscript


#1

Hi,
Currently I am either creating rectangles and squares or finding rectangles and squares for tile scripts I am working on.
I noticed there are no create rectangle methods in RS. Currently I either use the polygon method or interp curve through points, but I wish there was a rectangle method. Like:
Rhino.addrectangle
Getrectangle returns the number of squares and rectangles in the document
GetRectangleSize returns the length and width dimensions of a rectangle
Maybe I missed it and it’s there? Thanks for any hints or scripting tips to deal with creating and finding squares and rectangles.
RM


(Pascal Golay) #2

Hi Roland- you can make your own reusable function using AddPolyline, right? GetRectangle returns the four points, and you can just feed those directly to AddPolyline

Call MakeRectangle()
Sub MakeRectangle()
	Dim apts
	apts = Rhino.GetRectangle()
	
	If Not isArray(aPts) Then Exit Sub
	
	Rhino.AddPolyline array(apts(0), apts(1), apts(2), apts(3), apts(0))
	
End Sub

-Pascal


#3

Thanks Pascal that helps. Any ideas on finding rectangles in a document?
RM


#4

Hi Roland, as you’re looking for ideas perhaps something along the lines of

GetObjects ( const 4 for curves, select=false) - iterate through the array if present

  • IsPolyLine - IsCurveClosed - ObjectGripCount (is 4) - IsCurvePlanar
  • then EnableObjectGrips - ObjectGripLocations for coords EnableObjectGrips false
  • measure perimeter lengths for comparison and diagonals for squareness
  • select the result if square or rectangle or both depending on which conditions are met.

(Pascal Golay) #5

Hi Roland- I’d do a search for closed polylines that have four segments, then some kind of filtering of those - check for planarity and maybe a check of curve area against a rectangle that has the same length sides as segments one and two, something like that…

-Pascal


#6

Thanks Brian and Pascal.
I was afraid of all that, sure wish Dale would implement a method to check for rectangles.
Thanks for the quick suggestions.
RM


(Pascal Golay) #7
Something like this: 

Function FindRectangles()
    	FindRectangles = Null
    	Dim aCrv: aCrv = Rhino.ObjectsByType(4)
    	If Not isArray(aCrv) Then Exit Function
    	
    	Dim apts, sCrv, aRectangles(), n
    	n = 0
    	
    	For Each sCrv In aCrv
    		If Rhino.IsObjectNormal(sCrv) Then
    			If Rhino.IsPolyline(sCrv) Then
    				If Rhino.IsCurveClosed(sCrv) Then
    					If Rhino.IsCurvePlanar(sCrv) Then
    						apts = Rhino.PolylineVertices(sCrv)
    						If Ubound(aPts) = 4 Then
    							If Rhino.Distance(aPts(0), aPts(2)) - Rhino.Distance(aPts(1), aPts(3)) < Rhino.UnitAbsoluteTolerance() Then
    								ReDim Preserve aRectangles(n)
    								aRectangles(n) = sCrv
    								n = n + 1
    							End If
    						End If 
    					End If
    				End If
    			End If
    			
    		End If
    	Next
    	
    	If n > 0 Then FindRectangles = aRectangles
    	
    End Function

#8

Thanks Pascal very cool I’ll give that a try.
RM


#9

Hey Roland, all,

Below is a Python definition (which you can just add to your scripts) which has a few more options.  If you just pass GetRectangles() with no arguments, it should return all the rectangles in the document. You can also pass in optional arguments to get rectangles by length and/or area, with a tolerance (for length and area) and whether you want to select them or not.

The criteria used to determine “rectangularity” are 2 sets of opposite sides the same length plus two diagonals the same length. It should also select rectangular curves that are not polylines but could become so if simplified.

Edit: just added an optional “square” argument. When passed as True, will only get squares (within tolerance). If omitted or False, both squares and rectangles will be selected.

Anyway, FWIW… --Mitch

import rhinoscriptsyntax as rs
import Rhino

def GetRectangles(length=None, area=None, square=False, tolerance=None, select=True):
    crvIDs=rs.ObjectsByType(4,state=1)
    if not crvIDs: return
    matches=[]
    absTol=rs.UnitAbsoluteTolerance()
    angTol=rs.UnitAngleTolerance()
    if tolerance==None: tolerance=absTol
    redraw=rs.EnableRedraw(False)
    for crvID in crvIDs:
        if rs.IsCurveClosed and rs.IsCurvePlanar:
            crv=rs.coercecurve(crvID)
            if not crv.IsClosed or not crv.IsPlanar: continue
            rc, polyline=crv.TryGetPolyline()
            if not rc: continue
            if polyline.SegmentCount==4:
                if length:
                    if abs(polyline.Length-length)>tolerance: continue
                pts=[pt for pt in polyline]
                d1=pts[1].DistanceTo(pts[0])
                d2=pts[2].DistanceTo(pts[1])
                d3=pts[2].DistanceTo(pts[3])
                d4=pts[3].DistanceTo(pts[0])
                diag1=pts[0].DistanceTo(pts[2])
                diag2=pts[1].DistanceTo(pts[3])
                if abs(d1 - d3)<tolerance and abs(d2 - d4)<tolerance:
                    if abs(diag1 - diag2)>tolerance: continue
                    if square:
                        if abs(d2-d1)>tolerance: continue
                    if area:
                        if abs((d1*d2) - area)>tolerance: continue
                    matches.append(crvID)
    if select and len(matches)>0:
        rs.UnselectAllObjects()
        rs.SelectObjects(matches)
    rs.EnableRedraw(redraw)
    return matches

#10

Thanks Mitch very nice of you it looks like yours checks for parallelograms?
Pascal many thanks for the function it works really well. One thing I need to tune in it is I need to exclude parallelogram rectangles.
Thanks again for all the help guys.
RM


#11

Pascal’s script checks the two diagonals for equal length. This will effectively exclude parallelograms, but it may however include trapezoids that have two opposite equal length sides, as the diagonals of those will also have the identical length.

I check first for two sets of equal length opposite sides, then I check the diagonals. That should exclude both trapezoids and parallelograms (if my reasoning is correct).

–Mitch


(Dale Fugier) #12

Maybe this is the ticket…