Source code for cadquery.sketch

fromtypingimport(Union,Optional,List,Dict,Callable,Tuple,Iterable,Iterator,Any,Sequence,TypeVar,castastcast,Literal,overload,)frommathimporttan,sin,cos,pi,radians,remainderfromitertoolsimportproduct,chainfrommultimethodimportmultimethodfromtypishimportinstance_of,get_typefrom.hullimportfind_hullfrom.selectorsimportStringSyntaxSelector,Selectorfrom.typesimportRealfrom.utilsimportget_arityfrom.occ_impl.shapesimport(Shape,Face,Edge,Wire,Compound,Vertex,edgesToWires,compound,VectorLike,)from.occ_impl.geomimportLocation,Vectorfrom.occ_impl.exportersimportexportfrom.occ_impl.importers.dxfimport_importDXFfrom.occ_impl.sketch_solverimport(SketchConstraintSolver,ConstraintKind,ConstraintInvariants,DOF,arc_first,arc_last,arc_point,)#%% typesModes=Literal["a","s","i","c","r"]# add, subtract, intersect, construct, replacePoint=Union[Vector,Tuple[Real,Real]]TOL=1e-6T=TypeVar("T",bound="Sketch")SketchVal=Union[Shape,Location]# %% utilitiesdef_sanitize_for_bool(obj:SketchVal)->Shape:"""    Make sure that a Shape is selected    """ifisinstance(obj,Location):raiseValueError("Location was provided, Shape is required.")returnobjdef_to_compound(obj:Shape)->Compound:ifisinstance(obj,Compound):returnobjelse:returnCompound.makeCompound((obj,))# %% ConstraintclassConstraint(object):tags:Tuple[str,...]args:Tuple[Edge,...]kind:ConstraintKindparam:Anydef__init__(self,tags:Tuple[str,...],args:Tuple[Edge,...],kind:ConstraintKind,param:Any=None,):# validate based on the solver provided specifkindnotinConstraintInvariants:raiseValueError(f"Unknown constraint{kind}.")arity,types,param_type,converter=ConstraintInvariants[kind]ifarity!=len(tags):raiseValueError(f"Invalid number of entities for constraint{kind}. Provided{len(tags)}, required{arity}.")ifany(e.geomType()notintypesforeinargs):raiseValueError(f"Unsupported geometry types{[e.geomType()foreinargs]} for constraint{kind}.")ifnotinstance_of(param,param_type):raiseValueError(f"Unsupported argument types{get_type(param)}, required{param_type}.")# if all is fine store everything and possibly convert the paramsself.tags=tagsself.args=argsself.kind=kindself.param=tcast(Any,converter)(param)ifconverterelseparam# %% Sketch
[docs]classSketch(object):""" 2D sketch. Supports faces, edges and edges with constraints based construction. """parent:Anylocs:List[Location]_faces:Compound_edges:List[Edge]_selection:Optional[List[SketchVal]]_constraints:List[Constraint]_tags:Dict[str,Sequence[SketchVal]]_solve_status:Optional[Dict[str,Any]]
[docs]def__init__(self:T,parent:Any=None,locs:Iterable[Location]=(Location(),),obj:Optional[Compound]=None,):""" Construct an empty sketch. """self.parent=parentself.locs=list(locs)self._faces=objifobjelseCompound.makeCompound(())self._edges=[]self._selection=Noneself._constraints=[]self._tags={}self._solve_status=None
[docs]def__iter__(self)->Iterator[Face]:""" Iterate over faces-locations combinations. If not faces are present iterate over edges: """ifself._faces:returniter(fforlinself.locsforfinself._faces.moved(l).Faces())else:returniter(e.moved(l)forlinself.locsforeinself._edges)
def_tag(self:T,val:Sequence[SketchVal],tag:str):self._tags[tag]=val# face construction
[docs]defface(self:T,b:Union[Wire,Iterable[Edge],Shape,T],angle:Real=0,mode:Modes="a",tag:Optional[str]=None,ignore_selection:bool=False,)->T:""" Construct a face from a wire or edges. """res:Union[Face,Sketch,Compound]ifisinstance(b,Wire):res=Face.makeFromWires(b)elifisinstance(b,Sketch):res=belifisinstance(b,Shape):res=compound(b.Faces())elifisinstance(b,Iterable):wires=edgesToWires(tcast(Iterable[Edge],b))res=Face.makeFromWires(*(wires[0],wires[1:]))else:raiseValueError(f"Unsupported argument{b}")ifangle!=0:res=res.moved(Location(Vector(),Vector(0,0,1),angle))returnself.each(lambdal:res.moved(l),mode,tag,ignore_selection)
[docs]defimportDXF(self:T,filename:str,tol:float=1e-6,exclude:List[str]=[],include:List[str]=[],angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Import a DXF file and construct face(s) """res=Compound.makeCompound(_importDXF(filename,tol,exclude,include))returnself.face(res,angle,mode,tag)
[docs]defrect(self:T,w:Real,h:Real,angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct a rectangular face. """res=Face.makePlane(h,w).rotate(Vector(),Vector(0,0,1),angle)returnself.each(lambdal:res.located(l),mode,tag)
[docs]defcircle(self:T,r:Real,mode:Modes="a",tag:Optional[str]=None)->T:""" Construct a circular face. """res=Face.makeFromWires(Wire.makeCircle(r,Vector(),Vector(0,0,1)))returnself.each(lambdal:res.located(l),mode,tag)
[docs]defellipse(self:T,a1:Real,a2:Real,angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct an elliptical face. """res=Face.makeFromWires(Wire.makeEllipse(a1,a2,Vector(),Vector(0,0,1),Vector(1,0,0),rotation_angle=angle))returnself.each(lambdal:res.located(l),mode,tag)
[docs]deftrapezoid(self:T,w:Real,h:Real,a1:Real,a2:Optional[float]=None,angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct a trapezoidal face. """v1=Vector(-w/2,-h/2)v2=Vector(w/2,-h/2)v3=Vector(-w/2+h/tan(radians(a1)),h/2)v4=Vector(w/2-h/tan(radians(a2)ifa2elseradians(a1)),h/2)returnself.polygon((v1,v2,v4,v3,v1),angle,mode,tag)
[docs]defslot(self:T,w:Real,h:Real,angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct a slot-shaped face. """p1=Vector(-w/2,h/2)p2=Vector(w/2,h/2)p3=Vector(-w/2,-h/2)p4=Vector(w/2,-h/2)p5=Vector(-w/2-h/2,0)p6=Vector(w/2+h/2,0)e1=Edge.makeLine(p1,p2)e2=Edge.makeThreePointArc(p2,p6,p4)e3=Edge.makeLine(p4,p3)e4=Edge.makeThreePointArc(p3,p5,p1)wire=Wire.assembleEdges((e1,e2,e3,e4))returnself.face(wire,angle,mode,tag)
[docs]defregularPolygon(self:T,r:Real,n:int,angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct a regular polygonal face. """pts=[Vector(r*sin(i*2*pi/n),r*cos(i*2*pi/n))foriinrange(n+1)]returnself.polygon(pts,angle,mode,tag)
[docs]defpolygon(self:T,pts:Iterable[Point],angle:Real=0,mode:Modes="a",tag:Optional[str]=None,)->T:""" Construct a polygonal face. """w=Wire.makePolygon((pifisinstance(p,Vector)elseVector(*p)forpinpts),False,True)returnself.face(w,angle,mode,tag)
# distribute locations
[docs]defrarray(self:T,xs:Real,ys:Real,nx:int,ny:int)->T:""" Generate a rectangular array of locations. """ifnx<1orny<1:raiseValueError(f"At least 1 elements required, requested{nx},{ny}")locs=[]offset=Vector((nx-1)*xs,(ny-1)*ys)*0.5fori,jinproduct(range(nx),range(ny)):locs.append(Location(Vector(i*xs,j*ys)-offset))ifself._selection:selection:Sequence[Union[Shape,Location,Vector]]=self._selectionelse:selection=[Vector()]returnself.push((l*elifisinstance(el,Location)elsel*Location(el.Center()))forlinlocsforelinselection)
[docs]defparray(self:T,r:Real,a1:Real,da:Real,n:int,rotate:bool=True)->T:""" Generate a polar array of locations. """ifn<1:raiseValueError(f"At least 1 element required, requested{n}")locs=[]ifabs(remainder(da,360))<TOL:angle=da/nelse:angle=da/(n-1)ifn>1elsea1foriinrange(0,n):phi=a1+(angle*i)x=r*cos(radians(phi))y=r*sin(radians(phi))loc=Location(Vector(x,y))locs.append(loc)ifself._selection:selection:Sequence[Union[Shape,Location,Vector]]=self._selectionelse:selection=[Vector()]returnself.push((l*el*Location(Vector(0,0),Vector(0,0,1),(a1+(angle*i))ifrotateelse0))fori,linenumerate(locs)forelin[elifisinstance(el,Location)elseLocation(el.Center())forelinselection])
[docs]defdistribute(self:T,n:int,start:Real=0,stop:Real=1,rotate:bool=True)->T:""" Distribute locations along selected edges or wires. """ifn<1:raiseValueError(f"At least 1 element required, requested{n}")ifnotself._selection:raiseValueError("Nothing selected to distribute over")if1-abs(stop-start)<TOL:trimmed=Falseelse:trimmed=True# closed edge or wire parametersparams_closed=[start+i*(stop-start)/nforiinrange(n)]# open or trimmed edge or wire parametersparams_open=[start+i*(stop-start)/(n-1)ifn-1>0elsestartforiinrange(n)]locs=[]forelinself._selection:ifisinstance(el,(Wire,Edge)):ifel.IsClosed()andnottrimmed:params=params_closedelse:params=params_openifrotate:locs.extend(el.locations(params,planar=True,))else:locs.extend(Location(v)forvinel.positions(params))else:raiseValueError(f"Unsupported selection:{el}")returnself.push(locs)
[docs]defpush(self:T,locs:Iterable[Union[Location,Point]],tag:Optional[str]=None,)->T:""" Set current selection to given locations or points. """self._selection=[lifisinstance(l,Location)elseLocation(Vector(l))forlinlocs]iftag:self._tag(self._selection[:],tag)returnself
[docs]defeach(self:T,callback:Callable[[Location],Union[Face,"Sketch",Compound]],mode:Modes="a",tag:Optional[str]=None,ignore_selection:bool=False,)->T:""" Apply a callback on all applicable entities. """res:List[Face]=[]locs:List[Location]=[]ifself._selectionandnotignore_selection:forelinself._selection:ifisinstance(el,Location):loc=elelse:loc=Location(el.Center())locs.append(loc)else:locs.append(Location())forlocinlocs:tmp=callback(loc)ifisinstance(tmp,Sketch):res.extend(tmp._faces.Faces())elifisinstance(tmp,Compound):res.extend(tmp.Faces())else:res.append(tmp)iftag:self._tag(res,tag)ifmode=="a":self._faces=self._faces.fuse(*res)elifmode=="s":self._faces=self._faces.cut(*res)elifmode=="i":self._faces=self._faces.intersect(*res)elifmode=="r":self._faces=compound(res)elifmode=="c":ifnottag:raiseValueError("No tag specified - the geometry will be unreachable")else:raiseValueError(f"Invalid mode:{mode}")returnself
# modifiers
[docs]defhull(self:T,mode:Modes="a",tag:Optional[str]=None)->T:""" Generate a convex hull from current selection or all objects. """ifself._selection:rv=find_hull(elforelinself._selectionifisinstance(el,Edge))elifself._faces:rv=find_hull(elforelinself._faces.Edges())elifself._edges:rv=find_hull(self._edges)else:raiseValueError("No objects available for hull construction")self.face(rv,mode=mode,tag=tag,ignore_selection=bool(self._selection))returnself
[docs]defoffset(self:T,d:Real,mode:Modes="a",tag:Optional[str]=None)->T:""" Offset selected wires or edges. """ifself._selection:rv=(el.offset2D(d)forelinself._selectionifisinstance(el,Wire))forelinchain.from_iterable(rv):self.face(el,mode=mode,tag=tag,ignore_selection=bool(self._selection))else:raiseValueError("Selection is needed to offset")returnself
def_matchFacesToVertices(self)->Dict[Face,List[Vertex]]:rv={}ifself._selection:forfinself._faces.Faces():f_vertices=f.Vertices()rv[f]=[vforvinself._selectionifisinstance(v,Vertex)andvinf_vertices]else:raiseValueError("Selection is needed to match vertices to faces")returnrv
[docs]deffillet(self:T,d:Real)->T:""" Add a fillet based on current selection. """f2v=self._matchFacesToVertices()self._faces=Compound.makeCompound(k.fillet2D(d,v)ifvelsekfork,vinf2v.items())returnself
[docs]defchamfer(self:T,d:Real)->T:""" Add a chamfer based on current selection. """f2v=self._matchFacesToVertices()self._faces=Compound.makeCompound(k.chamfer2D(d,v)ifvelsekfork,vinf2v.items())returnself
[docs]defclean(self:T)->T:""" Remove internal wires. """self._faces=self._faces.clean()returnself
# selectiondef_unique(self:T,vals:List[SketchVal])->List[SketchVal]:tmp={hash(v):vforvinvals}returnlist(tmp.values())def_select(self:T,s:Optional[Union[str,Selector]],kind:Literal["Faces","Wires","Edges","Vertices"],tag:Optional[str]=None,)->T:rv=[]iftag:forelinself._tags[tag]:rv.extend(getattr(el,kind)())elifself._selection:forelinself._selection:ifnotisinstance(el,Location):rv.extend(getattr(el,kind)())else:rv.extend(getattr(self._faces,kind)())forelinself._edges:rv.extend(getattr(el,kind)())ifsandisinstance(s,Selector):filtered=s.filter(rv)elifsandisinstance(s,str):filtered=StringSyntaxSelector(s).filter(rv)else:filtered=rvself._selection=self._unique(filtered)returnself
[docs]deftag(self:T,tag:str)->T:""" Tag current selection. """ifself._selection:self._tags[tag]=list(self._selection)else:raiseValueError("Selection is needed to tag")returnself
[docs]defselect(self:T,*tags:str)->T:""" Select based on tags. """self._selection=[]fortagintags:self._selection.extend(self._tags[tag])returnself
[docs]deffaces(self:T,s:Optional[Union[str,Selector]]=None,tag:Optional[str]=None)->T:""" Select faces. """returnself._select(s,"Faces",tag)
[docs]defwires(self:T,s:Optional[Union[str,Selector]]=None,tag:Optional[str]=None)->T:""" Select wires. """returnself._select(s,"Wires",tag)
[docs]defedges(self:T,s:Optional[Union[str,Selector]]=None,tag:Optional[str]=None)->T:""" Select edges. """returnself._select(s,"Edges",tag)
[docs]defvertices(self:T,s:Optional[Union[str,Selector]]=None,tag:Optional[str]=None)->T:""" Select vertices. """returnself._select(s,"Vertices",tag)
[docs]defreset(self:T)->T:""" Reset current selection. """self._selection=Nonereturnself
[docs]defdelete(self:T)->T:""" Delete selected object. """ifself._selection:forobjinself._selection:ifisinstance(obj,Face):self._faces.remove(obj)elifisinstance(obj,Edge):self._edges.remove(obj)else:raiseValueError(f"Deletion of{obj} not supported")else:raiseValueError("Selection is needed to delete")self.reset()returnself
# edge based interfacedef_startPoint(self)->Vector:ifnotself._edges:raiseValueError("No free edges available")e=self._edges[0]returne.startPoint()def_endPoint(self)->Vector:ifnotself._edges:raiseValueError("No free edges available")e=self._edges[-1]returne.endPoint()
[docs]defedge(self:T,val:Edge,tag:Optional[str]=None,forConstruction:bool=False)->T:""" Add an edge to the sketch. """val.forConstruction=forConstructionself._edges.append(val)iftag:self._tag([val],tag)returnself
@multimethoddefsegment(self:T,p1:Point,p2:Point,tag:Optional[str]=None,forConstruction:bool=False,)->T:""" Construct a segment. """val=Edge.makeLine(Vector(p1),Vector(p2))returnself.edge(val,tag,forConstruction)@segment.registerdefsegment(self:T,p2:Point,tag:Optional[str]=None,forConstruction:bool=False)->T:p1=self._endPoint()val=Edge.makeLine(p1,Vector(p2))returnself.edge(val,tag,forConstruction)
[docs]@segment.registerdefsegment(self:T,l:Real,a:Real,tag:Optional[str]=None,forConstruction:bool=False,)->T:p1=self._endPoint()d=Vector(l*cos(radians(a)),l*sin(radians(a)))val=Edge.makeLine(p1,p1+d)returnself.edge(val,tag,forConstruction)
@multimethoddefarc(self:T,p1:Point,p2:Point,p3:Point,tag:Optional[str]=None,forConstruction:bool=False,)->T:""" Construct an arc. """val=Edge.makeThreePointArc(Vector(p1),Vector(p2),Vector(p3))returnself.edge(val,tag,forConstruction)@arc.registerdefarc(self:T,p2:Point,p3:Point,tag:Optional[str]=None,forConstruction:bool=False,)->T:p1=self._endPoint()val=Edge.makeThreePointArc(Vector(p1),Vector(p2),Vector(p3))returnself.edge(val,tag,forConstruction)
[docs]@arc.registerdefarc(self:T,c:Point,r:Real,a:Real,da:Real,tag:Optional[str]=None,forConstruction:bool=False,)->T:ifabs(da)>=360:val=Edge.makeCircle(r,Vector(c),angle1=a,angle2=a,orientation=da>0)else:p0=Vector(c)p1=p0+r*Vector(cos(radians(a)),sin(radians(a)))p2=p0+r*Vector(cos(radians(a+da/2)),sin(radians(a+da/2)))p3=p0+r*Vector(cos(radians(a+da)),sin(radians(a+da)))val=Edge.makeThreePointArc(p1,p2,p3)returnself.edge(val,tag,forConstruction)
@multimethoddefspline(self:T,pts:Iterable[Point],tangents:Optional[Iterable[Point]],periodic:bool,tag:Optional[str]=None,forConstruction:bool=False,)->T:""" Construct a spline edge. """val=Edge.makeSpline([Vector(*p)forpinpts],[Vector(*t)fortintangents]iftangentselseNone,periodic,)returnself.edge(val,tag,forConstruction)
[docs]@spline.registerdefspline(self:T,pts:Iterable[Point],tag:Optional[str]=None,forConstruction:bool=False,)->T:returnself.spline(pts,None,False,tag,forConstruction)
[docs]defbezier(self:T,pts:Iterable[Point],tag:Optional[str]=None,forConstruction:bool=False,)->T:""" Construct an bezier curve. The edge will pass through the last points, and the inner points are bezier control points. """val=Edge.makeBezier([Vector(*p)forpinpts])returnself.edge(val,tag,forConstruction)
[docs]defclose(self:T,tag:Optional[str]=None)->T:""" Connect last edge to the first one. """self.segment(self._endPoint(),self._startPoint(),tag)returnself
[docs]defassemble(self:T,mode:Modes="a",tag:Optional[str]=None)->T:""" Assemble edges into faces. """returnself.face((eforeinself._edgesifnote.forConstruction),0,mode,tag)
# constraints@multimethoddefconstrain(self:T,tag:str,constraint:ConstraintKind,arg:Any)->T:""" Add a constraint. """self._constraints.append(Constraint((tag,),(self._tags[tag][0],),constraint,arg))returnself
[docs]@constrain.registerdefconstrain(self:T,tag1:str,tag2:str,constraint:ConstraintKind,arg:Any)->T:self._constraints.append(Constraint((tag1,tag2),(self._tags[tag1][0],self._tags[tag2][0]),constraint,arg,))returnself
[docs]defsolve(self:T)->T:""" Solve current constraints and update edge positions. """entities=[]# list with all degrees of freedome2i={}# mapping from tags to indices of entitiesgeoms=[]# geometry types# fill entities, e2i and geomsfori,(k,v)inenumerate(filter(lambdakv:isinstance(kv[1][0],Edge),self._tags.items())):v0=tcast(Edge,v[0])# dispatch on geom typeifv0.geomType()=="LINE":p1=v0.startPoint()p2=v0.endPoint()ent:DOF=(p1.x,p1.y,p2.x,p2.y)elifv0.geomType()=="CIRCLE":p=v0.arcCenter()p1=v0.startPoint()-pp2=v0.endPoint()-ppm=v0.positionAt(0.5)-pa1=Vector(0,1).getSignedAngle(p1)a2=p1.getSignedAngle(p2)a3=p1.getSignedAngle(pm)ifa3>0anda2<0:a2+=2*pielifa3<0anda2>0:a2-=2*piradius=v0.radius()ent=(p.x,p.y,radius,a1,a2)else:continueentities.append(ent)e2i[k]=igeoms.append(v0.geomType())# build the POD constraint listconstraints=[]forcinself._constraints:ix=(e2i[c.tags[0]],e2i[c.tags[1]]iflen(c.tags)==2elseNone)constraints.append((ix,c.kind,c.param))# optimizesolver=SketchConstraintSolver(entities,constraints,geoms)res,self._solve_status=solver.solve()self._solve_status["x"]=res# translate back the solution - update edgesforg,(k,i)inzip(geoms,e2i.items()):el=res[i]# dispatch on geom typeifg=="LINE":p1=Vector(el[0],el[1])p2=Vector(el[2],el[3])e=Edge.makeLine(p1,p2)elifg=="CIRCLE":p1=Vector(*arc_first(el))p2=Vector(*arc_point(el,0.5))p3=Vector(*arc_last(el))e=Edge.makeThreePointArc(p1,p2,p3)# overwrite the low level objectself._tags[k][0].wrapped=e.wrappedreturnself
# misc
[docs]defcopy(self:T)->T:""" Create a partial copy of the sketch. """rv=self.__class__()rv._faces=self._faces.copy()returnrv
@overloaddefmoved(self:T,loc:Location)->T:...@overloaddefmoved(self:T,loc1:Location,loc2:Location,*locs:Location)->T:...@overloaddefmoved(self:T,locs:Sequence[Location])->T:...@overloaddefmoved(self:T,x:Real=0,y:Real=0,z:Real=0,rx:Real=0,ry:Real=0,rz:Real=0,)->T:...@overloaddefmoved(self:T,loc:VectorLike)->T:...@overloaddefmoved(self:T,loc1:VectorLike,loc2:VectorLike,*locs:VectorLike)->T:...@overloaddefmoved(self:T,loc:Sequence[VectorLike])->T:...
[docs]defmoved(self:T,*args,**kwargs)->T:""" Create a partial copy of the sketch with moved _faces. """rv=self.__class__()rv._faces=self._faces.moved(*args,**kwargs)returnrv
[docs]deflocated(self:T,loc:Location)->T:""" Create a partial copy of the sketch with a new location. """rv=self.__class__(locs=(loc,))rv._faces=self._faces.copy()returnrv
[docs]deffinalize(self)->Any:""" Finish sketch construction and return the parent. """returnself.parent
[docs]defval(self:T)->SketchVal:""" Return the first selected item, underlying compound or first edge. """ifself._selectionisnotNone:rv=self._selection[0]elifnotself._facesandself._edges:rv=self._edges[0]else:rv=self._facesreturnrv
[docs]defvals(self:T)->List[SketchVal]:""" Return all selected items, underlying compound or all edges. """rv:List[SketchVal]ifself._selectionisnotNone:rv=list(self._selection)elifnotself._facesandself._edges:rv=list(self._edges)else:rv=list(self._faces)returnrv
[docs]defadd(self:T)->T:""" Add selection to the underlying faces. """self._faces+=compound(self._selection).faces()returnself
[docs]defsubtract(self:T)->T:""" Subtract selection from the underlying faces. """self._faces-=compound(self._selection).faces()returnself
[docs]defreplace(self:T)->T:""" Replace the underlying faces with the selection. """self._faces=compound(self._selection).faces()returnself
[docs]def__add__(self:T,other:"Sketch")->T:""" Fuse self and other. """res=_sanitize_for_bool(self.val())+_sanitize_for_bool(other.val())returnself.__class__(obj=_to_compound(res))
[docs]def__sub__(self:T,other:"Sketch")->T:""" Subtract other from self. """res=_sanitize_for_bool(self.val())-_sanitize_for_bool(other.val())returnself.__class__(obj=_to_compound(res))
[docs]def__mul__(self:T,other:"Sketch")->T:""" Intersect self and other. """res=_sanitize_for_bool(self.val())*_sanitize_for_bool(other.val())returnself.__class__(obj=_to_compound(res))
[docs]def__truediv__(self:T,other:"Sketch")->T:""" Split self with other. """res=_sanitize_for_bool(self.val())/_sanitize_for_bool(other.val())returnself.__class__(obj=_to_compound(res))
def__getitem__(self:T,item:Union[int,Sequence[int],slice])->T:vals=self.vals()ifisinstance(item,Iterable):self._selection=[vals[i]foriinitem]elifisinstance(item,slice):self._selection=vals[item]else:self._selection=[vals[item]]returnself
[docs]deffilter(self:T,f:Callable[[SketchVal],bool])->T:""" Filter items using a boolean predicate. :param f: Callable to be used for filtering. :return: Sketch object with filtered items. """self._selection=list(filter(f,self.vals()))returnself
[docs]defmap(self:T,f:Callable[[SketchVal],SketchVal]):""" Apply a callable to every item separately. :param f: Callable to be applied to every item separately. :return: Sketch object with f applied to all items. """self._selection=list(map(f,self.vals()))returnself
[docs]defapply(self:T,f:Callable[[Iterable[SketchVal]],Iterable[SketchVal]]):""" Apply a callable to all items at once. :param f: Callable to be applied. :return: Sketch object with f applied to all items. """self._selection=list(f(self.vals()))returnself
[docs]defsort(self:T,key:Callable[[SketchVal],Any])->T:""" Sort items using a callable. :param key: Callable to be used for sorting. :return: Sketch object with items sorted. """self._selection=list(sorted(self.vals(),key=key))returnself
[docs]definvoke(self:T,f:Union[Callable[[T],T],Callable[[T],None],Callable[[],None]]):""" Invoke a callable mapping Sketch to Sketch or None. Supports also callables that take no arguments such as breakpoint. Returns self if callable returns None. :param f: Callable to be invoked. :return: Sketch object. """arity=get_arity(f)rv=selfifarity==0:f()# type: ignoreelifarity==1:res=f(self)# type: ignoreifresisnotNone:rv=reselse:raiseValueError("Provided function{f} accepts too many arguments")returnrv
[docs]defexport(self:T,fname:str,tolerance:float=0.1,angularTolerance:float=0.1,opt:Optional[Dict[str,Any]]=None,)->T:""" Export Sketch to file. :param path: Filename. :param tolerance: the deflection tolerance, in model units. Default 0.1. :param angularTolerance: the angular tolerance, in radians. Default 0.1. :param opt: additional options passed to the specific exporter. Default None. :return: Self. """export(self,fname,tolerance=tolerance,angularTolerance=angularTolerance,opt=opt)returnself