Exemplo n.º 1
0
 def load_item(self):
     from popupcad.filetypes.sketch import Sketch
     newitem = Sketch.open()
     if newitem is not None:
         self.items[newitem.id] = newitem
         self.refresh_list(newitem)
         self.items_updated.emit()
Exemplo n.º 2
0
 def load_item(self):
     from popupcad.filetypes.sketch import Sketch
     newitem = Sketch.open()
     if newitem is not None:
         self.items[newitem.id] = newitem
         self.refresh_list(newitem)
         self.items_updated.emit()
Exemplo n.º 3
0
    def new_method(self):
        from popupcad.guis.sketcher import Sketcher
        from popupcad.filetypes.sketch import Sketch

        def accept_method(sketch):
            self.design.sketches[sketch.id] = sketch
            self.refresh_list(sketch)
            self.items_updated.emit()

        sketcher = Sketcher(None,
                            Sketch.new(),
                            self.design,
                            accept_method=accept_method,
                            selectops=True)
        sketcher.show()
        sketcher.move_center()

        sketcher.graphicsview.zoomToFit()
Exemplo n.º 4
0
    def new_method(self):
        from popupcad.guis.sketcher import Sketcher
        from popupcad.filetypes.sketch import Sketch

        def accept_method(sketch):
            self.design.sketches[sketch.id] = sketch
            self.refresh_list(sketch)
            self.items_updated.emit()

        sketcher = Sketcher(
            None,
            Sketch.new(),
            self.design,
            accept_method=accept_method,
            selectops=True)
        sketcher.show()
        sketcher.move_center()
        
        sketcher.graphicsview.zoomToFit()
Exemplo n.º 5
0
 def load_item(self):
     from popupcad.filetypes.sketch import Sketch
     newitem = Sketch.open()
     self.items[newitem.id] = newitem
     self.refresh_list(newitem)
Exemplo n.º 6
0
    def operate(self, design):

        design_copy = design
        # design_copy.reprocessoperations()

        part_to_insert = design_copy.operations[design_copy.operation_index(self.part_opref)]
        sheet_to_insert_into = design_copy.operations[design_copy.operation_index(self.sheet_opref)]
        release_to_insert_into = design_copy.operations[design_copy.operation_index(self.release_opref)]

        # build the op_links, then auto make the operation
        op = part_to_insert
        op_ref = op.id
        op_links = {'parent': [(op_ref, op.getoutputref())]}

        new_web = AutoWeb4(op_links,[self.support_offset,0],MultiValueOperation3.keepout_types.laser_keepout)
        new_web.setcustomname(op.name)

        # support = OperationOutput(new_web.output[1], "support", self)

        design_copy.addoperation(new_web)
        new_web.generate(design_copy)

        ######################## generate the same size construction line somewhere in the sheet file

        # get geom for line
        parts_bounding_box = new_web.output[1].generic_laminate().getBoundingBox()
        # parts_bounding_box  = support.generic_laminate().getBoundingBox()

        # make the sketch
        construction_geom_hinge = Sketch.new()
        tmp_geom = [(parts_bounding_box[0],parts_bounding_box[1]), (parts_bounding_box[0],parts_bounding_box[3])]
        construction_line = GenericLine.gen_from_point_lists(tmp_geom,[],construction=False)
        construction_geom_hinge.addoperationgeometries([construction_line])

        # add sketch to sketch list
        design_copy.sketches[construction_geom_hinge.id] = construction_geom_hinge

        ######################## generate the external transform geometry in the sheet

        # center the locate line top left as origin
        position_hinge = (-tmp_geom[0][0],-tmp_geom[0][1])
        locate_lines = [(x + position_hinge[0], y + position_hinge[1]) for (x,y) in tmp_geom]

        # lets make 4x4
        width = (parts_bounding_box[2] - parts_bounding_box[0])*self.sc + self.x_gap
        height = (parts_bounding_box[3] - parts_bounding_box[1])*self.sc + self.y_gap


        # check if will all fit in one window, if not fill first and check if remainder will fit in second window
        max_num_cols = divmod(self.sketch_bounding_box[2] - self.sketch_bounding_box[0], width)[0]
        max_num_rows = divmod(self.sketch_bounding_box[3] - self.sketch_bounding_box[1], height)[0]

        if max_num_cols == 0 or max_num_rows == 0:
            print('Cannot tile into this area')
            design.remove_operation(new_web)
            return Laminate()

        # check if can fit in one
        # if N <= max_num_rows*max_num_cols:
        rows = math.ceil(self.N / max_num_cols)
        cols = math.ceil(self.N / rows)          # spread across the two windows

        upper_right_origin_bounding_box = (self.sketch_bounding_box[0], self.sketch_bounding_box[3])

        n_count = 0
        arrayed_reference_lines = []
        for row in range(rows):
            for col in range(cols):
                if n_count >= self.N or n_count >= max_num_rows*max_num_cols*2:
                    break

                newx = upper_right_origin_bounding_box[0] + locate_lines[0][0] + col*width
                newy = upper_right_origin_bounding_box[1] - locate_lines[1][1] - row*height

                arrayed_reference_lines.append([(newx, newy), (newx, newy + height)])

                n_count = n_count + 1

        construction_geom_sheet = Sketch.new()
        construction_line = [GenericLine.gen_from_point_lists(line,[],construction=False) for
                     line in arrayed_reference_lines]
        construction_geom_sheet.addoperationgeometries(construction_line)

        # add sketch to sketch list
        design_copy.sketches[construction_geom_sheet.id] = construction_geom_sheet

        ######################## External transform the hinge onto the sheet construction line

        # # insert hinge into sheet as subdesign
        # sheet.subdesigns[hinge.id] = hinge

        # # make design links
        operation_links = {}
        operation_links['from'] = [(part_to_insert.id,0)]

        sketch_links = {}
        sketch_links['sketch_to'] = [construction_geom_sheet.id]
        sketch_links['sketch_from'] = [construction_geom_hinge.id]

        insert_part = TransformInternal(sketch_links, operation_links, 'scale', 'scale', 0, False, 1., 1.)
        insert_part.customname = 'Inserted part'

        design_copy.addoperation(insert_part)
        insert_part.generate(design_copy)
        insert_part_id = design_copy.operations[-1].id # save for later

        ######################## External transform the web.sheet to the construction line

        # # make design links
        operation_links = {}
        operation_links['from'] = [(new_web.id,1)]

        sketch_links = {}
        sketch_links['sketch_to'] = [construction_geom_sheet.id]
        sketch_links['sketch_from'] = [construction_geom_hinge.id]

        insert_webs = TransformInternal(sketch_links, operation_links, 'scale', 'scale', 0, False, 1., 1.)
        insert_webs.customname = 'Inserted part webs'

        design_copy.addoperation(insert_webs)
        insert_webs.generate(design_copy)

        ######################## Remove web.sheet from sheet, union external transform + generateed sheet with hole + web
        # first the difference
        # link 1 is the sheet
        sheet_with_hole = LaminateOperation2({'unary': [(sheet_to_insert_into.id,0)], 'binary': [(insert_webs.id,0)]},'difference')
        sheet_with_hole.customname = 'Sheet with hole'
        design_copy.addoperation(sheet_with_hole)
        sheet_with_hole.generate(design_copy)

        sheet_with_part = LaminateOperation2({'unary': [(sheet_with_hole.id,0), (insert_part_id,0)],
                                      'binary':[]},'union')

        sheet_with_part.customname = 'First pass cuts'
        design_copy.addoperation(sheet_with_part)
        sheet_with_part.generate(design_copy)

        # ######################## Make release cut laminate operation


        operation_links = {}
        operation_links['from'] = [(release_to_insert_into.id,0)]

        sketch_links = {}
        sketch_links['sketch_to'] = [construction_geom_sheet.id]
        sketch_links['sketch_from'] = [construction_geom_hinge.id]

        insert_release = TransformInternal(sketch_links, operation_links, 'scale', 'scale', 0, False, 1., 1.)

        design.addoperation(insert_release)
        insert_release.generate(design)

        ######################################### prepare outputs

        # delete the intermediate layers
        design.remove_operation(sheet_with_hole)
        design.remove_operation(insert_webs)
        design.remove_operation(insert_part)
        design.remove_operation(new_web)
        design.remove_operation(sheet_with_part)
        design.remove_operation(insert_release)

        self.output = [OperationOutput(sheet_with_part.output[0].csg, 'FirstCuts', self),
                       OperationOutput(sheet_with_part.output[0].csg, 'FirstCuts', self),
                       OperationOutput(insert_release.output[0].csg, 'Release', self)]

        return sheet_with_part.output[0].csg
sheet_links= {'unary': [], 'binary': []}
first_cut_links= {'unary': [], 'binary': []}
second_cut_links= {'unary': [], 'binary': []}

ii = 0
while ii<5:
    points = numpy.random.rand(5,2)*20+[ii*20,0]
    body_polygon = popupcad.algorithms.triangulate.convex_hull(points)
    area,centroid,volume,mass,tris = body_polygon.mass_properties(1,-1,1,popupcad.SI_length_scaling)
    centroid = centroid[:2].tolist()
#    
    points = numpy.array(body_polygon.exteriorpoints()+[centroid])
    body_triangles= popupcad.algorithms.triangulate.triangulate(points)
    generic_lines = popupcad.algorithms.getjoints.getjoints(body_triangles,5)
    
    body_sketch = Sketch()
    body_sketch.addoperationgeometries([body_polygon])
    joints_sketch = Sketch()
    joints_sketch.addoperationgeometries(generic_lines)
    
    
    top_design.subdesigns[single_layer_joint_manufacturing.id] = single_layer_joint_manufacturing
    top_design.sketches[body_sketch.id] = body_sketch
    top_design.sketches[joints_sketch.id] = joints_sketch
    
    design_links = {}
    design_links['source'] = single_layer_joint_manufacturing.id
    sketch_list = []
    sketch_list.append(popupcad.manufacturing.sub_operation2.SketchData(sub_body_sketch.id,body_sketch.id))
    sketch_list.append(popupcad.manufacturing.sub_operation2.SketchData(sub_joint_sketch.id,joints_sketch.id))
    input_list = []
 sheet_links= {'unary': [], 'binary': []}
 first_cut_links= {'unary': [], 'binary': []}
 second_cut_links= {'unary': [], 'binary': []}
 
 ii = 0
 while ii<5:
     points = numpy.random.rand(5,2)*20+[ii*20,0]
     body_polygon = popupcad.algorithms.triangulate.convex_hull(points)
     area,centroid,volume,mass,tris = body_polygon.mass_properties(1,-1,1,popupcad.SI_length_scaling)
     centroid = centroid[:2].tolist()
 #    
     points = numpy.array(body_polygon.exteriorpoints()+[centroid])
     body_triangles= popupcad.algorithms.triangulate.triangulate(points)
     generic_lines = popupcad.algorithms.getjoints.getjoints(body_triangles,5)
     
     body_sketch = Sketch.new()
     body_sketch.addoperationgeometries([body_polygon])
     joints_sketch = Sketch.new()
     joints_sketch.addoperationgeometries(generic_lines)
     
     
     top_design.subdesigns[single_layer_joint_manufacturing.id] = single_layer_joint_manufacturing
     top_design.sketches[body_sketch.id] = body_sketch
     top_design.sketches[joints_sketch.id] = joints_sketch
     
     design_links = {}
     design_links['source'] = [single_layer_joint_manufacturing.id]
     sketch_list = []
     sketch_list.append(popupcad.manufacturing.sub_operation2.SketchData(sub_body_sketch.id,body_sketch.id))
     sketch_list.append(popupcad.manufacturing.sub_operation2.SketchData(sub_joint_sketch.id,joints_sketch.id))
     input_list = []
Exemplo n.º 9
0
        self.add_constraint(constraints.FixedConstraint)

    def add_constraint_angle(self):
        self.add_constraint(constraints.AngleConstraint)

    def add_constraint_parallel(self):
        self.add_constraint(constraints.ParallelLinesConstraint)

    def add_constraint_perpendicular(self):
        self.add_constraint(constraints.PerpendicularLinesConstraint)

    def add_constraint_equal(self):
        self.add_constraint(constraints.EqualLengthLinesConstraint)

    def add_constraint_horizontal(self):
        self.add_constraint(constraints.HorizontalConstraint)

    def add_constraint_vertical(self):
        self.add_constraint(constraints.VerticalConstraint)

    def add_constraint_point_line_distance(self):
        self.add_constraint(constraints.PointLineDistanceConstraint)

    def add_constraint_line_midpoint(self):
        self.add_constraint(constraints.LineMidpointConstraint)

if __name__ == "__main__":
    app = qg.QApplication(sys.argv)
    mw = Sketcher(None, Sketch.new())
    mw.show()
    sys.exit(app.exec_())
Exemplo n.º 10
0
    def add_constraint_distance(self):
        self.add_constraint(constraints.DistanceConstraint)
    def add_constraint_x_distance(self):
        self.add_constraint(constraints.XDistanceConstraint)
    def add_constraint_y_distance(self):
        self.add_constraint(constraints.YDistanceConstraint)
    def add_constraint_fixed(self):
        self.add_constraint(constraints.FixedConstraint)
    def add_constraint_angle(self):
        self.add_constraint(constraints.AngleConstraint)
    def add_constraint_parallel(self):
        self.add_constraint(constraints.ParallelLinesConstraint)
    def add_constraint_perpendicular(self):
        self.add_constraint(constraints.PerpendicularLinesConstraint)
    def add_constraint_equal(self):
        self.add_constraint(constraints.EqualLengthLinesConstraint)
    def add_constraint_horizontal(self):
        self.add_constraint(constraints.HorizontalConstraint)
    def add_constraint_vertical(self):
        self.add_constraint(constraints.VerticalConstraint)
    def add_constraint_point_line_distance(self):
        self.add_constraint(constraints.PointLineDistanceConstraint)
    def add_constraint_line_midpoint(self):
        self.add_constraint(constraints.LineMidpointConstraint)
        
        
if __name__ == "__main__":
    app = qg.QApplication(sys.argv)
    mw = Sketcher(None, Sketch.new())
    mw.show()
    sys.exit(app.exec_())
Exemplo n.º 11
0
    second_cut_links = {'unary': [], 'binary': []}

    ii = 0
    while ii < 5:
        points = numpy.random.rand(5, 2) * 20 + [ii * 20, 0]
        body_polygon = popupcad.algorithms.triangulate.convex_hull(points)
        area, centroid, volume, mass, tris = body_polygon.mass_properties(
            1, -1, 1)
        centroid = centroid[:2].tolist()
        #
        points = numpy.array(body_polygon.exteriorpoints() + [centroid])
        body_triangles = popupcad.algorithms.triangulate.triangulate(points)
        generic_lines = popupcad.algorithms.getjoints.getjoints(
            body_triangles, 5)

        body_sketch = Sketch.new()
        body_sketch.addoperationgeometries([body_polygon])
        joints_sketch = Sketch.new()
        joints_sketch.addoperationgeometries(generic_lines)

        top_design.subdesigns[single_layer_joint_manufacturing.
                              id] = single_layer_joint_manufacturing
        top_design.sketches[body_sketch.id] = body_sketch
        top_design.sketches[joints_sketch.id] = joints_sketch

        design_links = {}
        design_links['source'] = [single_layer_joint_manufacturing.id]
        sketch_list = []
        sketch_list.append(
            popupcad.manufacturing.sub_operation2.SketchData(
                sub_body_sketch.id, body_sketch.id))
Exemplo n.º 12
0
    def operate(self, design):

        """
        Return a generic_laminate ref of a layup laminate with all the layers of the part and with the appropriate 25x25mm alignment
        features compatible with the Wood lab micro-robotics manufacturing process.

        Input:
        Design -> a popupcad design file

        Output:
        layup -> A handle to the layup design file
        subop -> A subop which is inserted into the input design file to reduce the number of operations
        """


        #### general geometry constants that most layups will have
        sheet_width = self.values[0]        # mm
        hole_offset = self.values[1]        # location of hole in from corner
        hole_rad    = self.values[2]        # alignment pin geoms

        cross_len   = .75                   # tick length
        cross_horiz = sheet_width/2 - 2*cross_len        # horizontal dimension from center crosshair
        dt          = 0.001                 # small thickness for crosshair

        buff_x      = 5                     # for window sizes
        buff_y      = 1
        wind_h      = 1
        space_x     = 1.3

        # window width, maximum of 1 mm
        wind_w      = lambda N: max(min((sheet_width - 2*buff_x)/(N + 1.3*N - 1.3), 1),0.01)

        # the laminate design
        layup = design # popupcad.filetypes.design.Design.new()
        layer_list = design.return_layer_definition().layers

        # initiate the sketches
        ############# sheet first
        sheet = Sketch.new()
        tmp_geom = [(-sheet_width/2., -sheet_width/2.), (-sheet_width/2.,  sheet_width/2.),
                    ( sheet_width/2.,  sheet_width/2.), ( sheet_width/2., -sheet_width/2.)]
        sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(tmp_geom,[])
        sheet.addoperationgeometries([sheet_poly])

        ############# holes second
        holes = Sketch.new()
        tmp_geom = [(-sheet_width/2. + hole_offset, -sheet_width/2. + hole_offset),
                    (-sheet_width/2. + hole_offset,  sheet_width/2. - hole_offset),
                    ( sheet_width/2. - hole_offset,  sheet_width/2. - hole_offset),
                    ( sheet_width/2. - hole_offset, -sheet_width/2. + hole_offset)]
        # make list of hole geometry
        holes_poly = [popupcad.filetypes.genericshapes.GenericCircle.gen_from_point_lists([pt, (pt[0]+hole_rad, pt[1])],[])
                                            for pt in tmp_geom]
        holes.addoperationgeometries(holes_poly)

        ############# upper triangle
        left_tri = Sketch.new()
        tmp_geom = [(-sheet_width/2. + hole_offset/4, sheet_width/2. - hole_offset*(2/3)),
                    (-sheet_width/2. + hole_offset/4 + hole_rad,  sheet_width/2. - hole_offset*(2/3)),
                    (-sheet_width/2. + hole_offset/4 + 0.5*hole_rad,  sheet_width/2. - hole_offset*(2/3) + 1.2*hole_rad*.75)]
        # make list of hole geometry
        sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(tmp_geom,[])
        left_tri.addoperationgeometries([sheet_poly])

        ############# crosshairs
        cross_hairs = Sketch.new()
        tmp_geom_horiz = [(0,-cross_len), (0,cross_len)]
        tmp_geom_vert  = [(-cross_len,0), (cross_len,0)]
        shift = [-cross_horiz, 0, cross_horiz]

        cross_poly_horiz = [popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists([(tmp_geom_horiz[0][0] + c - dt/2.,
                                                                                                tmp_geom_horiz[0][1] - dt/2.),
                                                                                               (tmp_geom_horiz[1][0] + c - dt/2.,
                                                                                                tmp_geom_horiz[1][1] - dt/2.),
                                                                                               (tmp_geom_horiz[1][0] + c + dt/2.,
                                                                                                tmp_geom_horiz[1][1] + dt/2.),
                                                                                               (tmp_geom_horiz[0][0] + c + dt/2.,
                                                                                                tmp_geom_horiz[0][1] - dt/2.)],
                                                                                               [])
                                                                                        for c in shift]

        cross_poly_vert  = [popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists([(tmp_geom_vert[0][0] + c - dt/2.,
                                                                                                tmp_geom_vert[0][1] - dt/2.),
                                                                                               (tmp_geom_vert[1][0] + c - dt/2.,
                                                                                                tmp_geom_vert[1][1] + dt/2.),
                                                                                               (tmp_geom_vert[1][0] + c + dt/2.,
                                                                                                tmp_geom_vert[1][1] + dt/2.),
                                                                                               (tmp_geom_vert[0][0] + c + dt/2.,
                                                                                                tmp_geom_vert[0][1] - dt/2.)],
                                                                                               [])
                                                                                        for c in shift]

        cross_hairs.addoperationgeometries(cross_poly_horiz + cross_poly_vert)

        # Build the sheet with holes
        # Add the sketches to the sketch list
        layup.sketches[sheet.id] = sheet
        layup.sketches[holes.id] = holes
        layup.sketches[cross_hairs.id] = cross_hairs
        layup.sketches[left_tri.id] = left_tri

        # get the layer links for making sketch ops
        layer_links = [layer.id for layer in layer_list]

        holes_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp({'sketch': [holes.id]},layer_links)
        holes_sketch .name = "Holes"

        trian_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp({'sketch': [left_tri.id]},layer_links)
        trian_sketch .name = "Left triangle"

        sheet_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp({'sketch': [sheet.id]},layer_links)
        sheet_sketch.name = "sheet"

        cross_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp({'sketch': [cross_hairs.id]},layer_links)
        cross_sketch.name = "Crosshairs"

        # laminate operation to combine cross hairs and holes
        sheet_with_holes = popupcad.manufacturing.laminateoperation2.LaminateOperation2({'unary': [(sheet_sketch.id,0)],
                                                                                         'binary': [(holes_sketch.id,0),
                                                                                                    (cross_sketch.id,0),
                                                                                                    (trian_sketch.id,0)]},
                                                                                        'difference')
        sheet_with_holes.name = "Sheet with holes"

        ############# rectangle windows
        windows = [Sketch.new() for _ in layer_list]
        windows_sketchop = []
        # make windows, center on middle of sheet at bottom
        window_width = wind_w(len(windows))
        window_coords = np.array([round(kk*(1 + space_x)*window_width,4) for kk in range(len(windows))])
        window_coords = list(window_coords - np.mean(window_coords)) # center is 0

        for kk, (layer, window, x_coord) in enumerate(zip(layer_list,
                                                          windows,
                                                          window_coords)):

            window.name = layer.name + '_window'

            tmp_geom = [(x_coord, -sheet_width/2. + buff_y),
                        (x_coord,  -sheet_width/2. + buff_y + wind_h),
                        (x_coord + window_width, -sheet_width/2. + buff_y + wind_h),
                        (x_coord + window_width, -sheet_width/2. + buff_y)]
            sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(tmp_geom,[])
            window.addoperationgeometries([sheet_poly])
            layup.sketches[window.id] = window

            # make a sketch op on all layers above the current layer, this will be removed with a difference from the sheet
            windows_sketchop.append(popupcad.manufacturing.simplesketchoperation.SimpleSketchOp({'sketch': [window.id]},
                                                                                       layer_links[kk+1:]))
            windows_sketchop[-1].name = "Window_" + layer.name

        # laminate operation to remove windows from sheet with holes
        sheet_with_windows = popupcad.manufacturing.laminateoperation2.LaminateOperation2({'unary': [(sheet_with_holes.id,0)],
                                                                                         'binary': [(sktch.id,0) for sktch
                                                                                                    in windows_sketchop]},
                                                                                         'difference')
        sheet_with_windows.name = "Final sheet"

        # add the sketch ops to the design and generate the sketch op
        other_ops = windows_sketchop + [trian_sketch, holes_sketch, sheet_sketch, cross_sketch, sheet_with_holes, sheet_with_windows]
        [layup.addoperation(item) for item in other_ops]
        [item.generate(layup) for item in other_ops]
        [layup.remove_operation(item) for item in other_ops]

        self.output = [OperationOutput(sheet_with_windows.output[0], "OutputLaminate", self)]
        return sheet_with_windows.output[0].csg
Exemplo n.º 13
0
    def operate(self, design):
        """
        Return a generic_laminate ref of a layup laminate with all the layers of the part and with the appropriate 25x25mm alignment
        features compatible with the Wood lab micro-robotics manufacturing process.

        Input:
        Design -> a popupcad design file

        Output:
        layup -> A handle to the layup design file
        subop -> A subop which is inserted into the input design file to reduce the number of operations
        """

        #### general geometry constants that most layups will have
        sheet_width = self.values[0]  # mm
        hole_offset = self.values[1]  # location of hole in from corner
        hole_rad = self.values[2]  # alignment pin geoms

        cross_len = .75  # tick length
        cross_horiz = sheet_width / 2 - 2 * cross_len  # horizontal dimension from center crosshair
        dt = 0.001  # small thickness for crosshair

        buff_x = 5  # for window sizes
        buff_y = 1
        wind_h = 1
        space_x = 1.3

        # window width, maximum of 1 mm
        wind_w = lambda N: max(
            min((sheet_width - 2 * buff_x) / (N + 1.3 * N - 1.3), 1), 0.01)

        # the laminate design
        layup = design  # popupcad.filetypes.design.Design.new()
        layer_list = design.return_layer_definition().layers

        # initiate the sketches
        ############# sheet first
        sheet = Sketch.new()
        tmp_geom = [(-sheet_width / 2., -sheet_width / 2.),
                    (-sheet_width / 2., sheet_width / 2.),
                    (sheet_width / 2., sheet_width / 2.),
                    (sheet_width / 2., -sheet_width / 2.)]
        sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(
            tmp_geom, [])
        sheet.addoperationgeometries([sheet_poly])

        ############# holes second
        holes = Sketch.new()
        tmp_geom = [
            (-sheet_width / 2. + hole_offset, -sheet_width / 2. + hole_offset),
            (-sheet_width / 2. + hole_offset, sheet_width / 2. - hole_offset),
            (sheet_width / 2. - hole_offset, sheet_width / 2. - hole_offset),
            (sheet_width / 2. - hole_offset, -sheet_width / 2. + hole_offset)
        ]
        # make list of hole geometry
        holes_poly = [
            popupcad.filetypes.genericshapes.GenericCircle.
            gen_from_point_lists([pt, (pt[0] + hole_rad, pt[1])], [])
            for pt in tmp_geom
        ]
        holes.addoperationgeometries(holes_poly)

        ############# upper triangle
        left_tri = Sketch.new()
        tmp_geom = [
            (-sheet_width / 2. + hole_offset / 4,
             sheet_width / 2. - hole_offset * (2 / 3)),
            (-sheet_width / 2. + hole_offset / 4 + hole_rad,
             sheet_width / 2. - hole_offset * (2 / 3)),
            (-sheet_width / 2. + hole_offset / 4 + 0.5 * hole_rad,
             sheet_width / 2. - hole_offset * (2 / 3) + 1.2 * hole_rad * .75)
        ]
        # make list of hole geometry
        sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(
            tmp_geom, [])
        left_tri.addoperationgeometries([sheet_poly])

        ############# crosshairs
        cross_hairs = Sketch.new()
        tmp_geom_horiz = [(0, -cross_len), (0, cross_len)]
        tmp_geom_vert = [(-cross_len, 0), (cross_len, 0)]
        shift = [-cross_horiz, 0, cross_horiz]

        cross_poly_horiz = [
            popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(
                [(tmp_geom_horiz[0][0] + c - dt / 2.,
                  tmp_geom_horiz[0][1] - dt / 2.),
                 (tmp_geom_horiz[1][0] + c - dt / 2.,
                  tmp_geom_horiz[1][1] - dt / 2.),
                 (tmp_geom_horiz[1][0] + c + dt / 2.,
                  tmp_geom_horiz[1][1] + dt / 2.),
                 (tmp_geom_horiz[0][0] + c + dt / 2.,
                  tmp_geom_horiz[0][1] - dt / 2.)], []) for c in shift
        ]

        cross_poly_vert = [
            popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(
                [(tmp_geom_vert[0][0] + c - dt / 2.,
                  tmp_geom_vert[0][1] - dt / 2.),
                 (tmp_geom_vert[1][0] + c - dt / 2.,
                  tmp_geom_vert[1][1] + dt / 2.),
                 (tmp_geom_vert[1][0] + c + dt / 2.,
                  tmp_geom_vert[1][1] + dt / 2.),
                 (tmp_geom_vert[0][0] + c + dt / 2.,
                  tmp_geom_vert[0][1] - dt / 2.)], []) for c in shift
        ]

        cross_hairs.addoperationgeometries(cross_poly_horiz + cross_poly_vert)

        # Build the sheet with holes
        # Add the sketches to the sketch list
        layup.sketches[sheet.id] = sheet
        layup.sketches[holes.id] = holes
        layup.sketches[cross_hairs.id] = cross_hairs
        layup.sketches[left_tri.id] = left_tri

        # get the layer links for making sketch ops
        layer_links = [layer.id for layer in layer_list]

        holes_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp(
            {'sketch': [holes.id]}, layer_links)
        holes_sketch.name = "Holes"

        trian_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp(
            {'sketch': [left_tri.id]}, layer_links)
        trian_sketch.name = "Left triangle"

        sheet_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp(
            {'sketch': [sheet.id]}, layer_links)
        sheet_sketch.name = "sheet"

        cross_sketch = popupcad.manufacturing.simplesketchoperation.SimpleSketchOp(
            {'sketch': [cross_hairs.id]}, layer_links)
        cross_sketch.name = "Crosshairs"

        # laminate operation to combine cross hairs and holes
        sheet_with_holes = popupcad.manufacturing.laminateoperation2.LaminateOperation2(
            {
                'unary': [(sheet_sketch.id, 0)],
                'binary': [(holes_sketch.id, 0), (cross_sketch.id, 0),
                           (trian_sketch.id, 0)]
            }, 'difference')
        sheet_with_holes.name = "Sheet with holes"

        ############# rectangle windows
        windows = [Sketch.new() for _ in layer_list]
        windows_sketchop = []
        # make windows, center on middle of sheet at bottom
        window_width = wind_w(len(windows))
        window_coords = np.array([
            round(kk * (1 + space_x) * window_width, 4)
            for kk in range(len(windows))
        ])
        window_coords = list(window_coords -
                             np.mean(window_coords))  # center is 0

        for kk, (layer, window,
                 x_coord) in enumerate(zip(layer_list, windows,
                                           window_coords)):

            window.name = layer.name + '_window'

            tmp_geom = [(x_coord, -sheet_width / 2. + buff_y),
                        (x_coord, -sheet_width / 2. + buff_y + wind_h),
                        (x_coord + window_width,
                         -sheet_width / 2. + buff_y + wind_h),
                        (x_coord + window_width, -sheet_width / 2. + buff_y)]
            sheet_poly = popupcad.filetypes.genericshapes.GenericPoly.gen_from_point_lists(
                tmp_geom, [])
            window.addoperationgeometries([sheet_poly])
            layup.sketches[window.id] = window

            # make a sketch op on all layers above the current layer, this will be removed with a difference from the sheet
            windows_sketchop.append(
                popupcad.manufacturing.simplesketchoperation.SimpleSketchOp(
                    {'sketch': [window.id]}, layer_links[kk + 1:]))
            windows_sketchop[-1].name = "Window_" + layer.name

        # laminate operation to remove windows from sheet with holes
        sheet_with_windows = popupcad.manufacturing.laminateoperation2.LaminateOperation2(
            {
                'unary': [(sheet_with_holes.id, 0)],
                'binary': [(sktch.id, 0) for sktch in windows_sketchop]
            }, 'difference')
        sheet_with_windows.name = "Final sheet"

        # add the sketch ops to the design and generate the sketch op
        other_ops = windows_sketchop + [
            trian_sketch, holes_sketch, sheet_sketch, cross_sketch,
            sheet_with_holes, sheet_with_windows
        ]
        [layup.addoperation(item) for item in other_ops]
        [item.generate(layup) for item in other_ops]
        [layup.remove_operation(item) for item in other_ops]

        self.output = [
            OperationOutput(sheet_with_windows.output[0], "OutputLaminate",
                            self)
        ]
        return sheet_with_windows.output[0].csg