Exemple #1
0
	def execute(self, context):#this is almost same as getobjectoutline, just without the need of operation data
		ob=bpy.context.active_object
		self.silh=utils.getObjectSilhouete('OBJECTS', objects=bpy.context.selected_objects)
		bpy.context.scene.cursor_location=(0,0,0)
		#smp=sgeometry.asMultiPolygon(self.silh)
		for smp in self.silh:
			polygon_utils_cam.shapelyToCurve(ob.name+'_silhouette',smp,0)#
		#bpy.ops.object.convert(target='CURVE')
		bpy.context.scene.cursor_location=ob.location
		bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
		return {'FINISHED'}
Exemple #2
0
	def execute(self, context):#this is almost same as getobjectoutline, just without the need of operation data
		ob=bpy.context.active_object
		self.silh=utils.getObjectSilhouete('OBJECTS', objects=bpy.context.selected_objects)
		bpy.context.scene.cursor_location=(0,0,0)
		#smp=sgeometry.asMultiPolygon(self.silh)
		for smp in self.silh:
			polygon_utils_cam.shapelyToCurve(ob.name+'_silhouette',smp,0)#
		#bpy.ops.object.convert(target='CURVE')
		bpy.context.scene.cursor_location=ob.location
		bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
		return {'FINISHED'}
def sliceObject(ob):
    settings = bpy.context.scene.cam_slice

    layers = getSlices(ob, settings.slice_distance)
    # print(layers)
    sliceobjects = []
    i = 1
    for layer in layers:
        pi = 1
        layerpolys = []
        for slicechunk in layer:
            # these functions here are totally useless conversions, could generate slices more directly, just lazy to  write new functions
            # print (slicechunk)
            nchp = []
            for p in slicechunk:
                nchp.append((p[0], p[1]))
            # print(slicechunk)
            ch = chunk.camPathChunk(nchp)

            # print(ch)
            pslices = chunk.chunksToShapely([ch])
            # print(pslices)
            for pslice in pslices:
                p = pslice  # -p1
                # print(p)
                text = '%i - %i' % (i, pi)
                bpy.ops.object.text_add()
                textob = bpy.context.active_object
                textob.data.size = 0.0035
                textob.data.body = text
                textob.data.align = 'CENTER'

                # print(len(ch.points))
                sliceobject = polygon_utils_cam.shapelyToCurve(
                    'slice', p, slicechunk[0][2])
                textob.location = (0, 0, 0)

                textob.parent = sliceobject

                sliceobject.data.extrude = settings.slice_distance / 2
                sliceobject.data.dimensions = '2D'
                sliceobjects.append(sliceobject)
                pi += 1
        # FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :(
        # for p in layerpolys:
        # for p1 in layerpolys:

        i += 1
    for o in sliceobjects:
        o.select = True
    bpy.ops.group.create(name='slices')
Exemple #4
0
def sliceObject(ob):
    settings = bpy.context.scene.cam_slice

    layers = getSlices(ob, settings.slice_distance)
    # print(layers)
    sliceobjects = []
    i = 1
    for layer in layers:
        pi = 1
        layerpolys = []
        for slicechunk in layer:
            # these functions here are totally useless conversions, could generate slices more directly, just lazy to  write new functions
            # print (slicechunk)
            nchp = []
            for p in slicechunk:
                nchp.append((p[0], p[1]))
                # print(slicechunk)
            ch = chunk.camPathChunk(nchp)

            # print(ch)
            pslices = chunk.chunksToShapely([ch])
            # print(pslices)
            for pslice in pslices:
                p = pslice  # -p1
                # print(p)
                text = "%i - %i" % (i, pi)
                bpy.ops.object.text_add()
                textob = bpy.context.active_object
                textob.data.size = 0.0035
                textob.data.body = text
                textob.data.align = "CENTER"

                # print(len(ch.points))
                sliceobject = polygon_utils_cam.shapelyToCurve("slice", p, slicechunk[0][2])
                textob.location = (0, 0, 0)

                textob.parent = sliceobject

                sliceobject.data.extrude = settings.slice_distance / 2
                sliceobject.data.dimensions = "2D"
                sliceobjects.append(sliceobject)
                pi += 1
                # FIXME: the polys on same layer which are hollow are not joined by now, this prevents doing hollow surfaces :(
                # for p in layerpolys:
                # for p1 in layerpolys:

        i += 1
    for o in sliceobjects:
        o.select = True
    bpy.ops.group.create(name="slices")
Exemple #5
0
    def execute(
        self, context
    ):  # this is almost same as getobjectoutline, just without the need of operation data
        ob = context.active_object
        if self.opencurve and ob.type == 'CURVE':
            bpy.ops.object.duplicate()
            obj = context.active_object
            bpy.ops.object.transform_apply(location=True,
                                           rotation=True,
                                           scale=True)  # apply all transforms
            bpy.context.object.data.resolution_u = 60
            bpy.ops.object.convert(target='MESH')
            bpy.context.active_object.name = "temp_mesh"

            coords = []
            for v in obj.data.vertices:  # extract X,Y coordinates from the vertices data
                coords.append((v.co.x, v.co.y))

            simple.remove_multiple('temp_mesh')  # delete temporary mesh
            simple.remove_multiple('dilation')  # delete old dilation objects

            line = LineString(
                coords
            )  # convert coordinates to shapely LineString datastructure
            print("line length=", round(line.length * 1000), 'mm')

            dilated = line.buffer(
                self.offset,
                cap_style=1,
                resolution=16,
                mitre_limit=self.mitrelimit)  # use shapely to expand
            polygon_utils_cam.shapelyToCurve("dilation", dilated, 0)
        else:
            utils.silhoueteOffset(context, self.offset, int(self.style),
                                  self.mitrelimit)
        return {'FINISHED'}
Exemple #6
0
def packCurves():
    if speedups.available:
        speedups.enable()
    t = time.time()
    packsettings = bpy.context.scene.cam_pack

    sheetsizex = packsettings.sheet_x
    sheetsizey = packsettings.sheet_y
    direction = packsettings.sheet_fill_direction
    distance = packsettings.distance
    rotate = packsettings.rotate

    polyfield = []  # in this, position, rotation, and actual poly will be stored.
    for ob in bpy.context.selected_objects:
        allchunks = []
        simple.activate(ob)
        bpy.ops.object.make_single_user(type='SELECTED_OBJECTS')
        bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
        z = ob.location.z
        bpy.ops.object.location_clear()
        bpy.ops.object.rotation_clear()

        chunks = utils.curveToChunks(ob)
        npolys = utils.chunksToShapely(chunks)
        # add all polys in silh to one poly
        poly = shapely.ops.unary_union(npolys)

        poly = poly.buffer(distance / 1.5, 8)
        poly = poly.simplify(0.0003)
        polyfield.append([[0, 0], 0.0, poly, ob, z])
    random.shuffle(polyfield)
    # primitive layout here:
    allpoly = prepared.prep(sgeometry.Polygon())  # main collision poly.
    # allpoly=sgeometry.Polygon()#main collision poly.

    shift = 0.0015  # one milimeter by now.
    rotchange = .3123456  # in radians

    xmin, ymin, xmax, ymax = polyfield[0][2].bounds
    if direction == 'X':
        mindist = -xmin
    else:
        mindist = -ymin
    i = 0
    p = polyfield[0][2]
    placedpolys = []
    rotcenter = sgeometry.Point(0, 0)
    for pf in polyfield:
        print(i)
        rot = 0
        porig = pf[2]
        placed = False
        xmin, ymin, xmax, ymax = p.bounds
        # p.shift(-xmin,-ymin)
        if direction == 'X':
            x = mindist
            y = -ymin
        if direction == 'Y':
            x = -xmin
            y = mindist

        iter = 0
        best = None
        hits = 0
        besthit = None
        while not placed:

            # swap x and y, and add to x
            # print(x,y)
            p = porig

            if rotate:
                # ptrans=srotate(p,rot,0,0)
                ptrans = affinity.rotate(p, rot, origin=rotcenter, use_radians=True)
                # ptrans = translate(ptrans,x,y)
                ptrans = affinity.translate(ptrans, x, y)
            else:
                # ptrans = translate(p,x,y)
                ptrans = affinity.translate(p, x, y)
            xmin, ymin, xmax, ymax = ptrans.bounds
            # print(iter,p.bounds)

            if xmin > 0 and ymin > 0 and (
                    (direction == 'Y' and xmax < sheetsizex) or (direction == 'X' and ymax < sheetsizey)):
                if not allpoly.intersects(ptrans):
                    # if allpoly.disjoint(ptrans):
                    # print('gothit')
                    # we do more good solutions, choose best out of them:
                    hits += 1
                    if best == None:
                        best = [x, y, rot, xmax, ymax]
                        besthit = hits
                    if direction == 'X':
                        if xmax < best[3]:
                            best = [x, y, rot, xmax, ymax]
                            besthit = hits
                    elif ymax < best[4]:
                        best = [x, y, rot, xmax, ymax]
                        besthit = hits

            if hits >= 15 or (
                    iter > 10000 and hits > 0):  # here was originally more, but 90% of best solutions are still 1
                placed = True
                pf[3].location.x = best[0]
                pf[3].location.y = best[1]
                pf[3].location.z = pf[4]
                pf[3].rotation_euler.z = best[2]

                pf[3].select_set(state=True)

                # print(mindist)
                mindist = mindist - 0.5 * (xmax - xmin)
                # print(mindist)
                # print(iter)

                # reset polygon to best position here:
                ptrans = affinity.rotate(porig, best[2], rotcenter, use_radians=True)
                # ptrans=srotate(porig,best[2],0,0)
                ptrans = affinity.translate(ptrans, best[0], best[1])
                # ptrans = translate(ptrans,best[0],best[1])

                # polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation
                keep = []
                print(best[0], best[1])
                # print(len(ptrans.exterior))
                # npoly=allpoly.union(ptrans)

                # for ci in range(0,len(allpoly)):
                #     cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci)
                #     if direction=='X' and cmaxx>mindist-.1:
                #             npoly.addContour(allpoly[ci])
                #     if direction=='Y' and cmaxy>mindist-.1:
                #             npoly.addContour(allpoly[ci])

                # allpoly=npoly
                placedpolys.append(ptrans)
                allpoly = prepared.prep(sgeometry.MultiPolygon(placedpolys))
                # *** temporary fix until prepared geometry code is setup properly
                # allpoly=sgeometry.MultiPolygon(placedpolys)

                # polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation

                # for c in p:
                #	allpoly.addContour(c)
                # cleanup allpoly
                print(iter, hits, besthit)
            if not placed:
                if direction == 'Y':
                    x += shift
                    mindist = y
                    if xmax + shift > sheetsizex:
                        x = x - xmin
                        y += shift
                if direction == 'X':
                    y += shift
                    mindist = x
                    if ymax + shift > sheetsizey:
                        y = y - ymin
                        x += shift
                if rotate: rot += rotchange
            iter += 1
        i += 1
    t = time.time() - t

    polygon_utils_cam.shapelyToCurve('test', sgeometry.MultiPolygon(placedpolys), 0)
    print(t)
Exemple #7
0
def packCurves():
	if speedups.available:
		speedups.enable()
	t=time.time()
	packsettings=bpy.context.scene.cam_pack
	
	sheetsizex=packsettings.sheet_x
	sheetsizey=packsettings.sheet_y
	direction=packsettings.sheet_fill_direction
	distance=packsettings.distance
	rotate = packsettings.rotate
	
	polyfield=[]#in this, position, rotation, and actual poly will be stored.
	for ob in bpy.context.selected_objects:
		allchunks=[]
		simple.activate(ob)
		bpy.ops.object.make_single_user(type='SELECTED_OBJECTS')
		bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
		z=ob.location.z
		bpy.ops.object.location_clear()
		bpy.ops.object.rotation_clear()

		chunks=utils.curveToChunks(ob)
		npolys=utils.chunksToShapely(chunks)
		#add all polys in silh to one poly
		poly=shapely.ops.unary_union(npolys)
		
		poly=poly.buffer(distance/1.5,8)
		poly=poly.simplify(0.0003)
		polyfield.append([[0,0],0.0,poly,ob,z])
	random.shuffle(polyfield)
	#primitive layout here:
	allpoly=prepared.prep(sgeometry.Polygon())#main collision poly.
	#allpoly=sgeometry.Polygon()#main collision poly.
	
	shift=0.0015#one milimeter by now.
	rotchange=.3123456#in radians
	
	xmin,ymin,xmax,ymax=polyfield[0][2].bounds
	if direction=='X':
		mindist=-xmin
	else:
		mindist=-ymin
	i=0
	p=polyfield[0][2]
	placedpolys=[]
	rotcenter=sgeometry.Point(0,0)
	for pf in polyfield:
		print(i)
		rot=0
		porig=pf[2]
		placed=False
		xmin,ymin,xmax,ymax=p.bounds
		#p.shift(-xmin,-ymin)
		if direction=='X':
			x=mindist
			y=-ymin
		if direction=='Y':
			x=-xmin
			y=mindist
		
		iter=0
		best=None
		hits=0
		besthit=None
		while not placed:
			
			#swap x and y, and add to x
			#print(x,y)
			p=porig
			
			if rotate: 
				#ptrans=srotate(p,rot,0,0)
				ptrans=affinity.rotate(p,rot,origin = rotcenter, use_radians=True)
				#ptrans = translate(ptrans,x,y)
				ptrans = affinity.translate(ptrans,x,y)
			else:
				#ptrans = translate(p,x,y)
				ptrans = affinity.translate(p,x,y)
			xmin,ymin,xmax,ymax=ptrans.bounds
			#print(iter,p.bounds)
			
			if xmin>0 and ymin>0 and ((direction=='Y' and xmax<sheetsizex) or (direction=='X' and ymax<sheetsizey)):
				if not allpoly.intersects(ptrans):
				#if allpoly.disjoint(ptrans):
					#print('gothit')
					#we do more good solutions, choose best out of them:
					hits+=1
					if best==None:
						best=[x,y,rot,xmax,ymax]
						besthit=hits
					if direction=='X':
						if xmax<best[3]:
							best=[x,y,rot,xmax,ymax]
							besthit=hits
					elif ymax<best[4]:
						best=[x,y,rot,xmax,ymax]
						besthit=hits
					


					
			
			if hits>=15 or (iter>10000 and hits>0):#here was originally more, but 90% of best solutions are still 1
				placed=True
				pf[3].location.x=best[0]
				pf[3].location.y=best[1]
				pf[3].location.z=pf[4]
				pf[3].rotation_euler.z=best[2]
				
				
				pf[3].select=True
				
				#print(mindist)
				mindist=mindist-0.5*(xmax-xmin)
				#print(mindist)
				#print(iter)
				
				#reset polygon to best position here:
				ptrans=affinity.rotate(porig,best[2],rotcenter, use_radians = True)
				#ptrans=srotate(porig,best[2],0,0)
				ptrans = affinity.translate(ptrans,best[0],best[1])
				#ptrans = translate(ptrans,best[0],best[1])
				
				#polygon_utils_cam.polyToMesh(p,0.1)#debug visualisation
				keep=[]
				print(best[0],best[1])
				#print(len(ptrans.exterior))
				#npoly=allpoly.union(ptrans)
				'''
				for ci in range(0,len(allpoly)):
					cminx,cmaxx,cminy,cmaxy=allpoly.boundingBox(ci)
					if direction=='X' and cmaxx>mindist-.1:
							npoly.addContour(allpoly[ci])
					if direction=='Y' and cmaxy>mindist-.1:
							npoly.addContour(allpoly[ci])
				'''			
				#allpoly=npoly
				placedpolys.append(ptrans)
				allpoly=prepared.prep(sgeometry.MultiPolygon(placedpolys))
				#*** temporary fix until prepared geometry code is setup properly
				#allpoly=sgeometry.MultiPolygon(placedpolys)
				
				#polygon_utils_cam.polyToMesh(allpoly,0.1)#debug visualisation
				
				#for c in p:
				#	allpoly.addContour(c)
				#cleanup allpoly
				print(iter,hits,besthit)
			if not placed:
				if direction=='Y':
					x+=shift
					mindist=y
					if (xmax+shift>sheetsizex):
						x=x-xmin
						y+=shift
				if direction=='X':
					y+=shift
					mindist=x
					if (ymax+shift>sheetsizey):
						y=y-ymin
						x+=shift
				if rotate: rot+=rotchange
			iter+=1
		i+=1
	t=time.time()-t

	polygon_utils_cam.shapelyToCurve('test',sgeometry.MultiPolygon(placedpolys),0)
	print(t)
Exemple #8
0
def medial_axis(o):
    print('operation: Medial Axis')

    simple.remove_multiple("medialMesh")

    from cam.voronoi import Site, computeVoronoiDiagram

    chunks = []

    gpoly = spolygon.Polygon()
    angle = o.cutter_tip_angle
    slope = math.tan(math.pi * (90 - angle / 2) / 180)  # angle in degrees
    # slope = math.tan((math.pi-angle)/2) #angle in radian
    new_cutter_diameter = o.cutter_diameter
    m_o_name = o.object_name
    if o.cutter_type == 'VCARVE':
        angle = o.cutter_tip_angle
        # start the max depth calc from the "start depth" of the operation.
        maxdepth = o.maxz - slope * o.cutter_diameter / 2
        # don't cut any deeper than the "end depth" of the operation.
        if maxdepth < o.minz:
            maxdepth = o.minz
            # the effective cutter diameter can be reduced from it's max
            # since we will be cutting shallower than the original maxdepth
            # without this, the curve is calculated as if the diameter was at the original maxdepth and we get the bit
            # pulling away from the desired cut surface
            new_cutter_diameter = (maxdepth - o.maxz) / (-slope) * 2
    elif o.cutter_type == 'BALLNOSE':
        maxdepth = -new_cutter_diameter / 2
    else:
        o.warnings += 'Only Ballnose, Ball and V-carve cutters\n are supported'
        return
    # remember resolutions of curves, to refine them,
    # otherwise medial axis computation yields too many branches in curved parts
    resolutions_before = []

    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            resolutions_before.append(ob.data.resolution_u)
            if ob.data.resolution_u < 64:
                ob.data.resolution_u = 64

    polys = utils.getOperationSilhouete(o)
    mpoly = sgeometry.shape(polys)
    mpoly_boundary = mpoly.boundary
    ipol = 0
    for poly in polys.geoms:
        ipol = ipol + 1
        print("polygon:", ipol)
        schunks = shapelyToChunks(poly, -1)
        schunks = chunksRefineThreshold(
            schunks, o.medial_axis_subdivision,
            o.medial_axis_threshold)  # chunksRefine(schunks,o)

        verts = []
        for ch in schunks:
            for pt in ch.points:
                # pvoro = Site(pt[0], pt[1])
                verts.append(pt)  # (pt[0], pt[1]), pt[2])
        # verts= points#[[vert.x, vert.y, vert.z] for vert in vertsPts]
        nDupli, nZcolinear = unique(verts)
        nVerts = len(verts)
        print(str(nDupli) + " duplicates points ignored")
        print(str(nZcolinear) + " z colinear points excluded")
        if nVerts < 3:
            print("Not enough points")
            return {'FINISHED'}
        # Check colinear
        xValues = [pt[0] for pt in verts]
        yValues = [pt[1] for pt in verts]
        if checkEqual(xValues) or checkEqual(yValues):
            print("Points are colinear")
            return {'FINISHED'}
        # Create diagram
        print("Tesselation... (" + str(nVerts) + " points)")
        xbuff, ybuff = 5, 5  # %
        zPosition = 0
        vertsPts = [Point(vert[0], vert[1], vert[2]) for vert in verts]
        # vertsPts= [Point(vert[0], vert[1]) for vert in verts]

        pts, edgesIdx = computeVoronoiDiagram(vertsPts,
                                              xbuff,
                                              ybuff,
                                              polygonsOutput=False,
                                              formatOutput=True)

        # pts=[[pt[0], pt[1], zPosition] for pt in pts]
        newIdx = 0
        vertr = []
        filteredPts = []
        print('filter points')
        ipts = 0
        for p in pts:
            ipts = ipts + 1
            if ipts % 500 == 0:
                sys.stdout.write('\r')
                # the exact output you're looking for:
                prog_message = "points: " + str(ipts) + " / " + str(
                    len(pts)) + " " + str(round(100 * ipts / len(pts))) + "%"
                sys.stdout.write(prog_message)
                sys.stdout.flush()

            if not poly.contains(sgeometry.Point(p)):
                vertr.append((True, -1))
            else:
                vertr.append((False, newIdx))
                if o.cutter_type == 'VCARVE':
                    # start the z depth calc from the "start depth" of the operation.
                    z = o.maxz - mpoly.boundary.distance(
                        sgeometry.Point(p)) * slope
                    if z < maxdepth:
                        z = maxdepth
                elif o.cutter_type == 'BALL' or o.cutter_type == 'BALLNOSE':
                    d = mpoly_boundary.distance(sgeometry.Point(p))
                    r = new_cutter_diameter / 2.0
                    if d >= r:
                        z = -r
                    else:
                        # print(r, d)
                        z = -r + sqrt(r * r - d * d)
                else:
                    z = 0  #
                # print(mpoly.distance(sgeometry.Point(0,0)))
                # if(z!=0):print(z)
                filteredPts.append((p[0], p[1], z))
                newIdx += 1

        print('filter edges')
        filteredEdgs = []
        ledges = []
        for e in edgesIdx:
            do = True
            # p1 = pts[e[0]]
            # p2 = pts[e[1]]
            # print(p1,p2,len(vertr))
            if vertr[e[0]][0]:  # exclude edges with allready excluded points
                do = False
            elif vertr[e[1]][0]:
                do = False
            if do:
                filteredEdgs.append((vertr[e[0]][1], vertr[e[1]][1]))
                ledges.append(
                    sgeometry.LineString((filteredPts[vertr[e[0]][1]],
                                          filteredPts[vertr[e[1]][1]])))
        # print(ledges[-1].has_z)

        bufpoly = poly.buffer(-new_cutter_diameter / 2, resolution=64)

        lines = shapely.ops.linemerge(ledges)
        # print(lines.type)

        if bufpoly.type == 'Polygon' or bufpoly.type == 'MultiPolygon':
            lines = lines.difference(bufpoly)
            chunks.extend(shapelyToChunks(bufpoly, maxdepth))
        chunks.extend(shapelyToChunks(lines, 0))

        # generate a mesh from the medial calculations
        if o.add_mesh_for_medial:
            polygon_utils_cam.shapelyToCurve('medialMesh', lines, 0.0)
            bpy.ops.object.convert(target='MESH')

    oi = 0
    for ob in o.objects:
        if ob.type == 'CURVE' or ob.type == 'FONT':
            ob.data.resolution_u = resolutions_before[oi]
            oi += 1

    # bpy.ops.object.join()
    chunks = utils.sortChunks(chunks, o)

    layers = getLayers(o, o.maxz, o.min.z)

    chunklayers = []

    for layer in layers:
        for chunk in chunks:
            if chunk.isbelowZ(layer[0]):
                newchunk = chunk.copy()
                newchunk.clampZ(layer[1])
                chunklayers.append(newchunk)

    if o.first_down:
        chunklayers = utils.sortChunks(chunklayers, o)

    if o.add_mesh_for_medial:  # make curve instead of a path
        simple.joinMultiple("medialMesh")

    chunksToMesh(chunklayers, o)
    # add pocket operation for medial if add pocket checked
    if o.add_pocket_for_medial:
        #        o.add_pocket_for_medial = False
        # export medial axis parameter to pocket op
        ops.Add_Pocket(None, maxdepth, m_o_name, new_cutter_diameter)
Exemple #9
0
def pocket(o):
    print('operation: pocket')
    scene = bpy.context.scene

    simple.remove_multiple("3D_poc")

    max_depth = checkminz(o)
    cutter_angle = math.radians(o.cutter_tip_angle / 2)
    c_offset = o.cutter_diameter / 2
    if o.cutter_type == 'VCARVE':
        c_offset = -max_depth * math.tan(cutter_angle)
    elif o.cutter_type == 'CYLCONE':
        c_offset = -max_depth * math.tan(cutter_angle) + o.cylcone_diameter / 2
    elif o.cutter_type == 'BALLCONE':
        c_offset = -max_depth * math.tan(cutter_angle) + o.ball_radius
    if c_offset > o.cutter_diameter / 2:
        c_offset = o.cutter_diameter / 2

    p = utils.getObjectOutline(c_offset, o, False)
    approxn = (min(o.max.x - o.min.x, o.max.y - o.min.y) /
               o.dist_between_paths) / 2
    print("approximative:" + str(approxn))
    print(o)

    i = 0
    chunks = []
    chunksFromCurve = []
    lastchunks = []
    centers = None
    firstoutline = p  # for testing in the end.
    prest = p.buffer(-c_offset, o.circle_detail)
    while not p.is_empty:
        if o.pocketToCurve:
            polygon_utils_cam.shapelyToCurve(
                '3dpocket', p, 0.0)  # make a curve starting with _3dpocket

        nchunks = shapelyToChunks(p, o.min.z)
        # print("nchunks")
        pnew = p.buffer(-o.dist_between_paths, o.circle_detail)
        # print("pnew")

        nchunks = limitChunks(nchunks, o)
        chunksFromCurve.extend(nchunks)
        parentChildDist(lastchunks, nchunks, o)
        lastchunks = nchunks

        percent = int(i / approxn * 100)
        progress('outlining polygons ', percent)
        p = pnew

        i += 1

    # if (o.poc)#TODO inside outside!
    if (o.movement_type == 'CLIMB' and o.spindle_rotation_direction
            == 'CW') or (o.movement_type == 'CONVENTIONAL'
                         and o.spindle_rotation_direction == 'CCW'):
        for ch in chunksFromCurve:
            ch.points.reverse()

    chunksFromCurve = utils.sortChunks(chunksFromCurve, o)

    chunks = []
    layers = getLayers(o, o.maxz, checkminz(o))

    for l in layers:
        lchunks = setChunksZ(chunksFromCurve, l[1])
        if o.ramp:
            for ch in lchunks:
                ch.zstart = l[0]
                ch.zend = l[1]

        # helix_enter first try here TODO: check if helix radius is not out of operation area.
        if o.helix_enter:
            helix_radius = c_offset * o.helix_diameter * 0.01  # 90 percent of cutter radius
            helix_circumference = helix_radius * pi * 2

            revheight = helix_circumference * tan(o.ramp_in_angle)
            for chi, ch in enumerate(lchunks):
                if not chunksFromCurve[chi].children:
                    p = ch.points[
                        0]  # TODO:intercept closest next point when it should stay low
                    # first thing to do is to check if helix enter can really enter.
                    checkc = Circle(helix_radius + c_offset, o.circle_detail)
                    checkc = affinity.translate(checkc, p[0], p[1])
                    covers = False
                    for poly in o.silhouete:
                        if poly.contains(checkc):
                            covers = True
                            break

                    if covers:
                        revolutions = (l[0] - p[2]) / revheight
                        # print(revolutions)
                        h = Helix(helix_radius, o.circle_detail, l[0], p,
                                  revolutions)
                        # invert helix if not the typical direction
                        if (o.movement_type == 'CONVENTIONAL'
                                and o.spindle_rotation_direction == 'CW') or (
                                    o.movement_type == 'CLIMB'
                                    and o.spindle_rotation_direction == 'CCW'):
                            nhelix = []
                            for v in h:
                                nhelix.append((2 * p[0] - v[0], v[1], v[2]))
                            h = nhelix
                        ch.points = h + ch.points
                    else:
                        o.warnings = o.warnings + 'Helix entry did not fit! \n '
                        ch.closed = True
                        ch.rampZigZag(l[0], l[1], o)
        # Arc retract here first try:
        if o.retract_tangential:  # TODO: check for entry and exit point before actual computing... will be much better.
            # TODO: fix this for CW and CCW!
            for chi, ch in enumerate(lchunks):
                # print(chunksFromCurve[chi])
                # print(chunksFromCurve[chi].parents)
                if chunksFromCurve[chi].parents == [] or len(
                        chunksFromCurve[chi].parents) == 1:

                    revolutions = 0.25
                    v1 = Vector(ch.points[-1])
                    i = -2
                    v2 = Vector(ch.points[i])
                    v = v1 - v2
                    while v.length == 0:
                        i = i - 1
                        v2 = Vector(ch.points[i])
                        v = v1 - v2

                    v.normalize()
                    rotangle = Vector((v.x, v.y)).angle_signed(Vector((1, 0)))
                    e = Euler((0, 0, pi / 2.0))  # TODO:#CW CLIMB!
                    v.rotate(e)
                    p = v1 + v * o.retract_radius
                    center = p
                    p = (p.x, p.y, p.z)

                    # progress(str((v1,v,p)))
                    h = Helix(o.retract_radius, o.circle_detail,
                              p[2] + o.retract_height, p, revolutions)

                    e = Euler(
                        (0, 0,
                         rotangle + pi))  # angle to rotate whole retract move
                    rothelix = []
                    c = []  # polygon for outlining and checking collisions.
                    for p in h:  # rotate helix to go from tangent of vector
                        v1 = Vector(p)

                        v = v1 - center
                        v.x = -v.x  # flip it here first...
                        v.rotate(e)
                        p = center + v
                        rothelix.append(p)
                        c.append((p[0], p[1]))

                    c = sgeometry.Polygon(c)
                    # print('çoutline')
                    # print(c)
                    coutline = c.buffer(c_offset, o.circle_detail)
                    # print(h)
                    # print('çoutline')
                    # print(coutline)
                    # polyToMesh(coutline,0)
                    rothelix.reverse()

                    covers = False
                    for poly in o.silhouete:
                        if poly.contains(coutline):
                            covers = True
                            break

                    if covers:
                        ch.points.extend(rothelix)

        chunks.extend(lchunks)

    if o.ramp:
        for ch in chunks:
            ch.rampZigZag(ch.zstart, ch.points[0][2], o)

    if o.first_down:
        chunks = utils.sortChunks(chunks, o)

    if o.pocketToCurve:  # make curve instead of a path
        simple.joinMultiple("3dpocket")

    else:
        chunksToMesh(chunks, o)  # make normal pocket path