Modify script to remove trailing Zeros

I do have the following script that generates a cutting list and I need to modify so that the generated values to remove the trailing Zeros from the values. I do need 100.5 if it that is the result but the 100.0 to be rounded to 100

# -*- coding: utf8 -*-

import Rhino
import Rhino.Geometry as g
import rhinoscriptsyntax as rs
import codecs
import sys
import System
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms
import Rhino.UI
import scriptcontext
import math
from decimal import Decimal
import webbrowser

__commandname__ = "spec"

LayersEffect = 0
DIR = {"x":(0,1,2),
"y":(1,0,2),
"z":(2,0,1)}
AXES = {0:Rhino.Geometry.Vector3d.XAxis,
1:Rhino.Geometry.Vector3d.YAxis,
2:Rhino.Geometry.Vector3d.ZAxis
}

def computeDims(obj):
    objRef = Rhino.DocObjects.ObjRef(obj)
    brep = objRef.Geometry()    
    edges = list(brep.Edges)
    edges.sort(key=lambda edge: edge.GetLength(), reverse=True)
    edge = edges[0]
    adFaces = list(edge.AdjacentFaces())
    faces = list(brep.Faces)
    adFaces = [faces[i] for i in adFaces]
    adFaces.sort(key=lambda face:face.GetSurfaceSize()[1]*face.GetSurfaceSize()[2],reverse=True)
    face = adFaces[0]
    faceNor = face.NormalAt(0,0)
    origin = edge.PointAtStart
    xPoint = edge.PointAtEnd
    xVec = Rhino.Geometry.Vector3d(xPoint-origin)
    xVec.Unitize()
    yVec = Rhino.Geometry.Vector3d.CrossProduct(faceNor, xVec)
    yVec.Unitize()
    plane = Rhino.Geometry.Plane(origin, xVec, yVec)
    surf = Rhino.Geometry.PlaneSurface(plane, Rhino.Geometry.Interval(0,1),Rhino.Geometry.Interval(0,1))
    #scriptcontext.doc.Objects.AddSurface(surf)#!!!!delete
    verts = list(brep.Vertices)
    worldPlane = g.Plane.WorldXY
    tMatrix = g.Transform.ChangeBasis(worldPlane, plane)
    map(lambda vert:vert.Transform(tMatrix),verts)
    verts = list(map(lambda vert:vert.Location,verts))
    verts.sort(key=lambda vert:vert.X)
    length = (verts[len(verts)-1].X - verts[0].X)*1 # added ()*1
    verts.sort(key=lambda vert:vert.Y)
    width = (verts[len(verts)-1].Y - verts[0].Y)*1 # added ()*1
    verts.sort(key=lambda vert:vert.Z)
    thickness = (verts[len(verts)-1].Z - verts[0].Z)*1 # added ()*1
    return [[(length)*1,(width)*1,(thickness)*1], [xVec,yVec,faceNor]] # added ()*1

class attr:
    def __init__(self, name, isEditable, isOn, valType, exportPos):
        self.name = name
        self.isEditable = isEditable
        self.isOn = isOn
        self.valType = valType
        self.exportPos = exportPos

ATTRS = (attr("N",True,True,int,1),
attr("Name",True,True,str,0),
attr("Length",True,True,int,2),
attr("Width",True,True,int,3),
attr("Thickness",True,True,int,4),
attr("Texture",True,True,str,-1),
attr("Edge_L_0.4",True,True,str,6),
attr("Edge_W_0.4",True,True,str,7),
attr("Edge_L",True,True,str,8),
attr("Edge_W",True,True,str,9),
attr("Layer",True,True,str,10),
attr("Comment",True,True,str,11)
)

QUANT_FIELD = attr("Quantity",True,True,int,5)

QUANTITY_COLUMN_POS = 5

MAX_DIM_LEN = 2780
MAX_DIM_WIDTH = 2050

DIM_PRECISION = rs.GetDocumentUserText ("easy_cut_precision")
if DIM_PRECISION==None:
    DIM_PRECISION = 0
else:
    try:
        DIM_PRECISION = int(DIM_PRECISION)
    except:
        DIM_PRECISION = 0

class Specification:
    QUANTITY_COLUMN_POS = 5
    def __init__(self, attrs, details):
        self.details = [] #{}
        idSet = set(range(len(details)))
        self.table = []
        while len(idSet)>0:
            curId = idSet.pop()
            curDet = details[curId]
            curDetails = [curDet]
            idLst = list(idSet)
            for id in idLst:
                if curDet == details[id]:
                    curDetails.append(details[id])
                    idSet.remove(id)
            self.details.append(curDetails)
        self.buildTable()
    def buildTable(self):
        self.table = []
        for details in self.details:
            specRow = list(details[0].getParams())
            specRow.insert(self.QUANTITY_COLUMN_POS, len(details))
            self.table.append(specRow)
        self.table, self.details = (list(t) for t in zip(*sorted(zip(self.table, self.details), key=lambda x: (int(x[0][0]) if int(x[0][0])>0 else 9999, x[0][10], -float(x[0][4])))))
    def autoNum(self):
        i = 1
        for tableItem in self.table:
            for detail in self.details[i-1]:
                detail.N = i
                detail.refresh()
            tableItem[0] = i
            i += 1
            


class SpecDialog(forms.Dialog[bool]):

    def __init__(self):
        self.Title = "EasyCut"
        self.selected = []
        self.edgeshighlightMode = -1
        self.m_gridview = forms.GridView()
        self.m_gridview.ShowHeader = True
        self.Padding = drawing.Padding(10)
        self.Resizable = True
        num=0
        for idx in range(len(ATTRS)):
            if num == QUANTITY_COLUMN_POS:
                attr = QUANT_FIELD
                column = forms.GridColumn()
                column.HeaderText = attr.name
                column.Editable = attr.isEditable
                column.DataCell = forms.TextBoxCell(num)
                self.m_gridview.Columns.Add(column)
                num+=1
            attr = ATTRS[idx]
            if attr.isOn:

                column = forms.GridColumn()
                column.HeaderText = attr.name
                column.Editable = attr.isEditable
                column.DataCell = forms.TextBoxCell(num)
                self.m_gridview.Columns.Add(column)
                num+=1
                
        self.precision_dropdownlist = forms.DropDown()
        self.precision_dropdownlist.DataStore = ['1', '0.1', '0.01', '0.001', '0.0001', '0.00001']
        self.precision_dropdownlist.SelectedIndex = DIM_PRECISION
        self.precision_label = forms.Label(Text = "Dimensions precision " )
        self.precision_dropdownlist.DropDownClosed += self.changePrecisionVal
        
        layout0 = forms.TableLayout()
        cell = forms.TableCell(self.precision_label,scaleWidth=False)
        cell.ScaleWidth = False
        cell2 = forms.TableCell(self.precision_dropdownlist,scaleWidth=False)
        cell2.ScaleWidth = False
        row = forms.TableRow(None, cell, cell2)
        row.ScaleHeight = False
        layout0.Rows.Add(row)
        
        
        self.m_gridview.CellClick += self.gridClick
        self.m_gridview.SelectionChanged += self.gridSelChanged
        self.m_gridview.CellEdited += self.gridEdited
        self.m_gridview.CellFormatting += self.OnCellFormatting
        self.buttonAutoNum = forms.Button(self.buttonAutoNumClick)
        self.buttonAutoNum.Text = "Auto number"
        self.button = forms.Button(self.buttonClick)
        self.button.Text = "Export"
        layout = forms.TableLayout()
        layout.Spacing = drawing.Size(5, 5)
        cell = forms.TableCell(self.m_gridview)
        row = forms.TableRow(cell)
        row.ScaleHeight = True
        layout.Rows.Add(layout0)
        layout.Rows.Add(row)
        
        layout2 = forms.TableLayout()
        layout2.Spacing = drawing.Size(5, 5)
        
        cell = forms.TableCell(self.button,True)
        cell2 = forms.TableCell(self.buttonAutoNum,True)
        row = forms.TableRow([cell,cell2])
        
        layout2.Rows.Add(row)
        layout.Rows.Add(layout2)
        layout2 = forms.TableLayout()
        layout2.Spacing = drawing.Size(5, 5)
        self.m_linkbutton = forms.LinkButton(Text = 'Easycut')
        self.m_linkbutton.Click += self.OnLinkButtonClick
        self.m_donatebutton = forms.LinkButton(Text = 'Donate',Style = "right-align")
        self.m_donatebutton.Click += self.OnDonateButtonClick
        cell = forms.TableCell(self.m_linkbutton,True)
        cell2 = forms.TableCell(None,False)
        cell3 = forms.TableCell(self.m_donatebutton,False)
        row = forms.TableRow([cell,cell2,cell3])
        layout2.Rows.Add(row)
        
        layout.Rows.Add(layout2)
        
        self.Content = layout
        
    def changePrecisionVal(self, sender, e):
        global DIM_PRECISION
        DIM_PRECISION = sender.SelectedIndex
        rs.SetDocumentUserText ("easy_cut_precision", str(DIM_PRECISION))
        specification = self.spec.details
        for row in specification:
            for det in row:
                det.setDims()

        
        self.rebuild()

    
    def OnCellFormatting(self, sender, e):
        if float(self.spec.table[e.Row][2]) > MAX_DIM_LEN:
            e.BackgroundColor = drawing.Colors.Red 
    def setData(self, spec):
        self.spec = spec
        self.m_gridview.DataStore = spec.table

    def rebuild(self):
        self.spec.buildTable()
        #print self.spec.table
        self.m_gridview.DataStore = self.spec.table
    def setObjs(self, dic):
        self.objs = dic
    def unselectAll(self):
        for det in self.selected:
            det.unselect()
        self.selected = []
    def gridClick(self,sender,e):
        row = e.Row
        col = e.Column
        dets = self.spec.details[row]
        self.edgeshighlightMode = -1
        if col >= 7 and col <= 10:
            self.edgeshighlightMode = (col - 7)%2
        self.unselectAll()
        if self.edgeshighlightMode>=0:
            for det in dets:
                det.getEdges(det.lcs[self.edgeshighlightMode])
        else:
            for det in dets:
                det.select()
            self.selected += dets
        scriptcontext.doc.Views.Redraw()
        
    def gridSelChanged(self,sender,e):
        self.unselectAll()
        dets = []
        for row in sender.SelectedRows:
            dets += self.spec.details[row]
        for detLst in self.spec.details:
            for det in detLst:
                det.unhighlighAll()
        scriptcontext.doc.Views.Redraw()
        if self.edgeshighlightMode >=0:
            for det in dets:
                det.getEdges(det.lcs[self.edgeshighlightMode])
        else:
           for det in dets:
               det.select()
           self.selected += dets
        scriptcontext.doc.Views.Redraw()


    def gridEdited(self, sender, e):
        row = e.Row
        col = e.Column
        dets = self.spec.details[row]
        cols = self.m_gridview.Columns
        attr = cols[col].HeaderText
        val = self.m_gridview.DataStore[row][col]
        if attr == "N":
            try:
                int(val)
            except:
                val=dets[0].N
                self.m_gridview.DataStore[row][col] = dets[0].N
                rs.MessageBox("number can be only an integer")
        for det in dets:
            setattr(det, attr, val)
            det.refresh()
        self.rebuild()
    def buttonClick(self, sender, e):
        data = []
        nameRow = []
        cols = [i.HeaderText for i in self.m_gridview.Columns]
        i = 0
        num=0
        curRow = []
        while i < (len(cols)):
            if i == QUANT_FIELD.exportPos:
                exportPos = QUANT_FIELD.exportPos
                if exportPos>0:
                    nameRow.append((exportPos,cols[i]))
                i += 1
            else:
                exportPos = ATTRS[num].exportPos
                if exportPos>=0:
                    nameRow.append((exportPos,cols[i]))
                num+=1
                i += 1
        nameRow = [i[1] for i in list(sorted(nameRow, key = lambda x: x[0]))]
        data.append(nameRow)
                
        for dataRow in self.m_gridview.DataStore:
            i = 0
            num=0
            curRow = []
            while i < (len(dataRow)):
                if i == QUANT_FIELD.exportPos:
                    exportPos = QUANT_FIELD.exportPos
                    if exportPos>0:
                        curRow.append((exportPos,dataRow[i]))
                    i += 1
                else:
                    exportPos = ATTRS[num].exportPos
                    if exportPos>=0:
                        curRow.append((exportPos,dataRow[i]))
                    num+=1
                    i += 1

            curRow = [i[1] for i in list(sorted(curRow, key = lambda x: x[0]))]
            data.append(curRow)

        ExportObjBBData(data)

    def buttonAutoNumClick(self, sender, e):
        self.spec.autoNum()
        self.rebuild()

    def OnLinkButtonClick(self, sender, e):
        webbrowser.open("https://easycut3d.online")
        
    def OnDonateButtonClick(self, sender, e):
        webbrowser.open("http://easycut3d.online/donate.html")

class Detail:
    def __init__(self, obj):
        self.obj = obj
        self.selected = 0
        self.id = obj
        attrs = rs.GetUserText (obj)
        attrs = [i.name for i in ATTRS if i.isEditable]
        for attr in attrs:
            setattr(self, attr, rs.GetUserText (str(obj), attr))
        self.setDims()
        objRef = Rhino.DocObjects.ObjRef(self.id)
        obj = objRef.Object()
        index = obj.Attributes.LayerIndex
        self.Layer = scriptcontext.doc.Layers[index].Name
    def setDims(self):        
#        bb=rs.BoundingBox(self.obj)
#        if bb:
#            x=round((bb[1].X-bb[0].X,DIM_PRECISION))*1 # added ()*1
#            y=round(bb[3].Y-bb[0].Y,DIM_PRECISION)
#            z=round(bb[4].Z-bb[0].Z,DIM_PRECISION)            
#        else:
#            x=0 ; y=0 ; z=0
#        self.dims = [x,y,z]
        self.dims, self.lcs = computeDims(self.obj)
#        texture = self.Texture
#        if texture in DIR:
#           dir = DIR[texture]
#           self.Texture = texture
#           self.lcs = dir
#        else:
#            dir = [0,1,2]
#            self.lcs = [i[0] for i in sorted(list(enumerate(self.dims)), key = lambda x:x[1])]
#            self.lcs.reverse()
#            self.dims.sort()
#            self.dims.reverse()
#            alert = "texture missing"
#        x,y,z = self.dims[dir[0]],self.dims[dir[1]],self.dims[dir[2]]
#        if z>y:
#            self.dims = [x,z,y]
#            self.lcs = [self.lcs[0],self.lcs[2],self.lcs[1]]
#        else:
#            self.dims = [x,y,z]
#        x = self.dims[0]
#        y = self.dims[1]
#        z = self.dims[2]
        self.Length = format(self.dims[0],'.' + str(DIM_PRECISION) + 'f')
#        self.Width = format(self.dims[1],'.' + str(Decimal(DIM_PRECISION).normalize()) + 'f') # added (Decimal(DIM_PRECISION).normalize())
        self.Width = format(self.dims[1],'.' + str(DIM_PRECISION) + 'f') # added (Decimal(DIM_PRECISION).normalize())
#        string = str(self.dims[1])
#        w_zeros = self.string.rstrip('0').rstrip('.') if '.' in string else string
#        self.Width = format(w_zeros,'.' + str(Decimal(DIM_PRECISION).normalize()) + 'f') # added (Decimal(DIM_PRECISION).normalize())
        self.Thickness = format(self.dims[2],'.' + str(DIM_PRECISION) + 'f')
    def select(self):
        self.selected = 1
        rs.SelectObject(self.id)
    def unselect(self):
        if self.selected:
            rs.UnselectObject(self.id)
    def getParams(self):
        params = []
        for attr in ATTRS:
            name = attr.name
            params.append(getattr(self, name))
        return params
    def highlightEdges(self, dir):
        pass
    def refresh(self):
        for attr in ATTRS:
            name = attr.name
            rs.SetUserText (str(self.id), name, getattr(self, name))
        self.setDims()
        for attr in ATTRS:
            name = attr.name
            rs.SetUserText (str(self.id), name, getattr(self, name))

    def getEdges(self, dirVec):
        id = self.id
        nor = self.lcs[2]
        objRef = Rhino.DocObjects.ObjRef(id)
        brep = objRef.Geometry()
        obj = objRef.Object()
        faces = brep.Faces#doesn't work for extrusions
        edges = []
        for face in faces:
            obj.HighlightSubObject(face.ComponentIndex(), False)
            faceNor = face.NormalAt(0,0)
            cond1 = (round(faceNor*nor)==0) #perpendicular
            cond2 = (round(faceNor*dirVec)==0) #perpendicular
            if cond1 and cond2:               
               obj.HighlightSubObject(face.ComponentIndex(), True)
               scriptcontext.doc.Views.Redraw()
    def unhighlighAll(self):
        id = self.id
        objRef = Rhino.DocObjects.ObjRef(id)
        brep = objRef.Geometry()
        obj = objRef.Object()
        faces = brep.Faces#doesn't work for extrusions
        for face in faces:
            obj.HighlightSubObject(face.ComponentIndex(), False)
    def __eq__(self, obj):
        return (self.getParams() == obj.getParams())

        
def makeDetail(keys, objs):
    if not objs: return
    for obj in objs:
        for key in keys:
            cur_keys = rs.GetUserText (obj)
            if key not in cur_keys:
                rs.SetUserText (obj, key, "0", False)
            else:
                val = rs.GetUserText (obj, key)
                if len(val)<1 or val==" ":
                    rs.SetUserText (obj, key, "0", False)
def main():
    msg="Select polysurface objects to export data"
    objs = rs.GetObjects(msg,16,preselect=True)
    if not objs: return
    extrusions = []
    rs.UnselectAllObjects()
    for obj in objs:
        if rs.ObjectType(obj)==1073741824:
            extrusions.append(obj)
    if len(extrusions)>0:
        rs.SelectObjects(extrusions)
        resp = rs.MessageBox ("Selected objects will be converted to polysurfaces.\n Press OK to continue, Cancel to abort",1)
        if resp == 1:
            rs.Command("ConvertExtrusion _Enter")
            rs.UnselectAllObjects()
        else:
            return
    keys = [i.name for i in ATTRS if i.isEditable]
    makeDetail(keys, objs)
    details = []
    spec = dict([])
    ids = dict([])
    for obj in objs:
        detail = Detail(obj)
        details.append(detail)
    spec = Specification([], details)
    dialog = SpecDialog()
    dialog.setData(spec)
    rs.UnselectAllObjects()
    Rhino.UI.EtoExtensions.ShowSemiModal(dialog, Rhino.RhinoDoc.ActiveDoc, Rhino.UI.RhinoEtoApp.MainWindow)

def ExportObjBBData(data):
    filter = "CSV File (*.csv)|*.csv||"
    filename = rs.SaveFileName("Save data file as", filter)
    if not filename: return
    file = codecs.open(filename, encoding='utf-8', mode='w+')
    s = ""
    for itm in data:
        itm = [ i if isinstance(i, str) else str(i) for i in itm]
        s += "|".join(itm)
        s += "\n"
    file.write(s)
    file.close()


def RunCommand( is_interactive ):
    main()
    return 0

RunCommand(True)

I asked the ChatGPT how to modify the script to remove the trailing zeros values and here it is the answer:

key = "{0} x {1} x {2}".format(format(length, '.1f').rstrip('0').rstrip('.'),
                                format(width, '.1f').rstrip('0').rstrip('.'),
                                format(height, '.1f').rstrip('0').rstrip('.'))

I don’t understand exactly how to incorporate this part into the initial script.

ChatGPT might have been drinking. This formatting should do it:

foo = "{:g}".format(yourFloat)

Edit: Quick validation test:

1 Like

From the little I think I understand from the script … :blush:
Here is my attempt.

I would edit the ExportObjBBData function, adding two instructions. In this way:

def ExportObjBBData(data):
    filter = "CSV File (*.csv)|*.csv||"
    filename = rs.SaveFileName("Save data file as", filter)
    if not filename: return
    file = codecs.open(filename, encoding='utf-8', mode='w+')
    s = ""
    for itm in data:
        itm = [ i if isinstance(i, str) else str(i) for i in itm]
######## ADDED CODE START
        if itm.endswith( '.0' ):
          itm = itm[ : -2 ]
######## ADDED CODE END
        s += "|".join(itm)
        s += "\n"
    file.write(s)
    file.close()

HTH

I do made the changes but unfortunately I do get the following error:

spec_error

I think needs to define first the endswith part. I only know some very basic Python scripting, so it’s just a guess.

ChatGPT might have been drinking.

Yeas, definitely was drinking too much because I spent an entire hour on chat and got only funky responses.

My bad.
I tried to use a list as a string … :roll_eyes: Sorry.

Maybe this one …


def ExportObjBBData(data):
    filter = "CSV File (*.csv)|*.csv||"
    filename = rs.SaveFileName("Save data file as", filter)
    if not filename: return
    file = codecs.open(filename, encoding='utf-8', mode='w+')
    s = ""
    for itm in data:
        itm = [ i if isinstance(i, str) else str(i) for i in itm]
######## ADDED CODE START
        for ix in range( len( itm ) ):
            if itm[ ix ].endswith( '.0' ):
                itm[ ix ] = itm[ ix ][ : -2 ]
######## ADDED CODE END
        s += "|".join(itm)
        s += "\n"
    file.write(s)
    file.close()

I just checked the code and it is working only if all the parts have .0 values but as soon as I have a part in the project with a different value after the . it is not stripping the trailing zeroes at all.

Please use this file as reference. have two parts that have a .9 at the end of the length.

Cutlist_export_test.3dm (713.0 KB)

OK.
Let’s try to format the numbers then, just like Anders suggested …
Only one line to edit, from the original script:


def ExportObjBBData(data):
    filter = "CSV File (*.csv)|*.csv||"
    filename = rs.SaveFileName("Save data file as", filter)
    if not filename: return
    file = codecs.open(filename, encoding='utf-8', mode='w+')
    s = ""
    for itm in data:
######### THE FOLLOWING LINE HAS BEEN EDITED
        itm = [ i if isinstance(i, str) else ('%g' % i) for i in itm]
        s += "|".join(itm)
        s += "\n"
    file.write(s)
    file.close()

Unfortunatelly still not working:

Name|N|Length|Width|Thickness|Quantity|Edge_L_0.4|Edge_W_0.4|Edge_L|Edge_W|Layer|Comment
R|1|1030.1|150.0|18.0|1|0|0|0|0|18MDF|0
L|1|1030.1|150.0|18.0|1|0|0|0|0|18MDF|0
top|2|370.0|150.0|18.0|1|0|0|0|0|18MDF|0
bottom|2|370.0|150.0|18.0|1|0|0|0|0|18MDF|0
back|3|1030.0|406.0|12.0|1|0|0|0|0|12MDF|0

I don’t know why those trailing zeros are still there …
… Maybe the fractional part is not exaclty zero …

But if all the wrong values end by “.0” now …
I think that inserting those three lines again might help.

def ExportObjBBData(data):
    filter = "CSV File (*.csv)|*.csv||"
    filename = rs.SaveFileName("Save data file as", filter)
    if not filename: return
    file = codecs.open(filename, encoding='utf-8', mode='w+')
    s = ""
    for itm in data:
######### THE FOLLOWING LINE HAS BEEN EDITED
        itm = [ i if isinstance(i, str) else ('%g' % i) for i in itm]
######### ADDED CODE START
        for ix in range( len( itm ) ):
            if itm[ ix ].endswith( '.0' ):
                itm[ ix ] = itm[ ix ][ : -2 ]
######### ADDED CODE END        
        s += "|".join(itm)
        s += "\n"
    file.write(s)
    file.close()

Much better. Now I have the correct dimensions in the exported .csv file. However, one more thing. It is possible to write the same values into the Values of the corresponding Keys into Attribute User Text? To have the Length value as 1030.1 instead of 1030 how it is writing right now?

Keys

Sure it’s possible.
But I’m afraid I’m not able to do that.

Finding the output routine was easy.
But the overall logic of the script is too complex for me.
Sorry

1 Like

Yes, need to find the part of the code that it is writing this Keys/Values and also I wasn’t able myself to find that part. Anyway thank you very much for the help.

1 Like