class MultiValueOperation3(Operation2): name = 'Multi-Value Operation' valuenames = ['value'] show = ['keepout'] defaults = [0.] keepout_types = enum.enum(laser_keepout=301, mill_keepout=302, mill_flip_keepout=303) keepout_type_default = keepout_types.laser_keepout def __init__(self, *args): super(MultiValueOperation3, self).__init__() self.id = id(self) self.editdata(*args) def copy(self): new = type(self)(self.operation_links.copy(), self.values[:], self.keepout_type) new.id = self.id new.customname = self.customname return new def editdata(self, operation_links, values, keepout_type): super(MultiValueOperation3, self).editdata(operation_links, {}, {}) self.values = values self.keepout_type = keepout_type @classmethod def buildnewdialog(cls, design, currentop): dialog = Dialog(cls.keepout_types, cls.valuenames, cls.defaults, design.operations, currentop, cls.show, keepouttype=cls.keepout_type_default) return dialog def buildeditdialog(self, design): operation_ref, output_index = self.operation_links['parent'][0] operation_index = design.operation_index(operation_ref) dialog = Dialog(self.keepout_types, self.valuenames, self.defaults, design.prioroperations(self), operation_index, self.show, self.values, self.keepout_type, output_index) return dialog
class Removability2(MultiValueOperation3): name = 'Removability' valuenames = [] defaults = [] keepout_types = enum.enum(one_way_up='one_way_up', one_way_down='one_way_down', two_way='two_way') keepout_type_default = keepout_types.one_way_up def operate(self, design): operation_ref, output_index = self.operation_links['parent'][0] ls1 = design.op_from_ref(operation_ref).output[output_index].csg if self.keepout_type == self.keepout_types.one_way_up: keepout = popupcad.algorithms.removability.one_way_up(ls1) elif self.keepout_type == self.keepout_types.one_way_down: keepout = popupcad.algorithms.removability.one_way_down(ls1) elif self.keepout_type == self.keepout_types.two_way: keepout = popupcad.algorithms.removability.two_way(ls1) else: raise Exception return keepout
class Constraint(object): name = 'Constraint' deletable = [] CleanupFlags = enum(NotDeletable=101, Deletable=102) def __init__(self, vertex_ids, segment_ids): self.vertex_ids = vertex_ids self.segment_ids = segment_ids self.id = id(self) def copy(self, identical=True): new = type(self)(self.vertex_ids[:], self.segment_ids[:]) if identical: new.id = self.id return new def upgrade(self, *args, **kwargs): return self def init_symbolics(self): self._vertices = [SymbolicVertex(id) for id in self.vertex_ids] self._segments = [ SymbolicLine(SymbolicVertex(id1), SymbolicVertex(id2)) for id1, id2 in self.segment_ids ] self._segment_vertices = [ SymbolicVertex(id) for id in self.vertices_in_lines() ] @classmethod def new(cls, *objects): obj = cls(*cls._define_internals(*objects)) obj.check_valid() return obj @property def generated_equations(self): try: return self._generated_equations except AttributeError: self._generated_equations = self.symbolic_equations() return self._generated_equations @generated_equations.deleter def generated_equations(self): try: del self._generated_equations except AttributeError: pass try: del self._f_constraints except AttributeError: pass try: del self._f_J except AttributeError: pass @property def f_jacobian(self): try: return self._f_jacobian except AttributeError: self._f_jacobian = sympy.utilities.lambdify( self.variables, self.jacobian().tolist()) return self._f_jacobian @property def f_constraints(self): try: return self._f_constraints except AttributeError: self._f_constraints = sympy.utilities.lambdify( self.variables, self.generated_equations) return self._f_constraints def mapped_f_constraints(self, *args): args = (self._B.dot(args)) y = self._A.dot(self.f_constraints(*args)) return y def mapped_f_jacobian(self, *args): args = (self._B.dot(args)) y = self._A.dot(self.f_jacobian(*args)).dot(self._B) return y @property def variables(self): variables = [] for equation in self.generated_equations: variables.extend(equation.atoms(Variable)) variables = set(variables) variables = sorted(variables, key=lambda var: str(var)) return variables def jacobian(self): eq = sympy.Matrix(self.generated_equations) J = eq.jacobian(self.variables) return J def build_system_mapping(self, sys_vars, num_eq, eq_indeces): m = num_eq n = len(self.generated_equations) o = len(self.variables) p = len(sys_vars) A = numpy.zeros((m, n)) for ii, jj in zip(eq_indeces, range(len(self.generated_equations))): A[ii, jj] = 1 B = numpy.zeros((o, p)) for ii, item in enumerate(self.variables): jj = sys_vars.index(item) B[ii, jj] = 1 self._A = A self._B = B # return A,B def edit(self): pass @staticmethod def _define_internals(*objects): from popupcad.geometry.line import Line from popupcad.geometry.vertex import BaseVertex vertex_ids = [] segment_ids = [] segment_vertex_ids = [] vertices = [] segments = [] segment_vertices = [] for item in objects: if isinstance(item, BaseVertex): vertex_ids.append(item.id) vertices.append(item.constraints_ref()) elif isinstance(item, Line): segment_ids.append( tuple(sorted((item.vertex1.id, item.vertex2.id)))) segment_vertex_ids.append(item.vertex1.id) segment_vertex_ids.append(item.vertex2.id) segments.append(item.constraints_ref()) segment_vertices.extend(item.vertex_constraints_ref()) else: print('wrong thing supplied') return vertex_ids, segment_ids def vertices_in_lines(self): return [vertex for tuple1 in self.segment_ids for vertex in tuple1] def __str__(self): return self.name def getlines(self): try: return self._segments except AttributeError: self.init_symbolics() return self._segments def getallvertices(self): try: return self._vertices + self._segment_vertices except AttributeError: self.init_symbolics() return self._vertices + self._segment_vertices def getvertices(self): try: return self._vertices except AttributeError: self.init_symbolics() return self._vertices def symbolic_equations(self): return [] def properties(self): from dev_tools.propertyeditor import PropertyEditor return PropertyEditor(self) def cleanup(self, objects): self.cleanup_objects(objects) if self.valid(): return self.CleanupFlags.NotDeletable else: return self.CleanupFlags.Deletable def cleanup_objects(self, objects): current_ids = frozenset([item.id for item in objects]) self.vertex_ids = list( frozenset(self.vertex_ids).intersection(current_ids)) segment_ids = [] for id1, id2 in self.segment_ids: if (id1 in current_ids) and (id2 in current_ids): segment_ids.append((id1, id2)) self.segment_ids = segment_ids def exactly_two_points(self): return len(set(self.vertex_ids + self.vertices_in_lines())) == 2 def at_least_two_points(self): return len(set(self.vertex_ids + self.vertices_in_lines())) >= 2 def exactly_two_lines(self): return len(self.segment_ids) == 2 def at_least_two_lines(self): return len(self.segment_ids) >= 2 def at_least_one_line(self): return len(self.segment_ids) >= 1 def exactly_one_point_and_one_line(self): return len(self.segment_ids) == 1 and len(self.vertex_ids) == 1 def throwvalidityerror(self): raise WrongArguments('Need exactly one point and one line') def at_least_one_point(self): return len(set(self.vertex_ids + self.vertices_in_lines())) >= 1 all_validity_tests = [] all_validity_tests.append((exactly_two_points, 'Need exactly two points')) all_validity_tests.append( (at_least_two_points, 'Need at least two points')) all_validity_tests.append((exactly_two_lines, 'Need exactly two lines')) all_validity_tests.append((at_least_two_lines, 'Need at least two lines')) all_validity_tests.append((at_least_one_line, 'Need at least one line')) all_validity_tests.append((exactly_one_point_and_one_line, 'Need exactly one point and one line')) all_validity_tests.append((at_least_one_point, 'Need at least one point')) validity_tests = [ exactly_two_points, at_least_two_points, exactly_two_lines, at_least_two_lines, at_least_one_line, exactly_one_point_and_one_line, at_least_one_point ] def check_valid(self): for check in self.validity_tests: if not check(self): raise WrongArguments(dict(self.all_validity_tests)[check]) def valid(self): for check in self.validity_tests: if not check(self): return False return True
class TransformExternal(Operation2): name = 'External Transform' transformtypes = enum(scale='scale', custom='custom') def copy(self): new = TransformExternal(self.sketch_links, self.design_links, self.subopref, self.sub_sketch_id, self.transformtype_x, self.transformtype_y, self.shift, self.flip, self.scalex, self.scaley) new.customname = self.customname new.id = self.id return new def __init__(self, *args): super(TransformExternal, self).__init__() self.editdata(*args) self.id = id(self) def editdata(self, sketch_links, design_links, subopref, sub_sketch_id, transformtype_x, transformtype_y, shift, flip, scalex, scaley): super(TransformExternal, self).editdata({}, sketch_links, design_links) self.subopref = subopref self.sub_sketch_id = sub_sketch_id self.transformtype_x = transformtype_x self.transformtype_y = transformtype_y self.shift = shift self.flip = flip self.scalex = scalex self.scaley = scaley def operate(self, design): subdesign = design.subdesigns[self.design_links['subdesign'][0]] sketch_from_id = self.sub_sketch_id sketch_to_id = self.sketch_links['sketch_to'][0] sketch_from = subdesign.sketches[sketch_from_id] sketch_to = design.sketches[sketch_to_id] operation_ref, output_index = self.subopref csg_laminate = subdesign.operations[subdesign.operation_index( operation_ref)].output[output_index].csg for geom in sketch_from.operationgeometry: if not geom.is_construction(): geom_from = geom break geoms_to = [ geom for geom in sketch_to.operationgeometry if not geom.is_construction() ] if self.transformtype_x == self.transformtypes.scale: scale_x = None elif self.transformtype_x == self.transformtypes.custom: scale_x = self.scalex if self.transformtype_y == self.transformtypes.scale: scale_y = None elif self.transformtype_y == self.transformtypes.custom: scale_y = self.scaley step = 1 if self.flip: step = -1 if self.shift > 0: outshift = self.shift inshift = 0 elif self.shift < 0: outshift = 0 inshift = -self.shift else: outshift = 0 inshift = 0 layerdef_from = subdesign.return_layer_definition() layerdef_to = design.return_layer_definition() return popupcad.algorithms.manufacturing_functions.transform_csg( layerdef_from, layerdef_to, inshift, outshift, step, geom_from, geoms_to, csg_laminate, scale_x, scale_y) @classmethod def buildnewdialog(cls, design, currentop): dialog = Dialog(design, design.operations) return dialog def buildeditdialog(self, design): dialog = Dialog(design, design.prioroperations(self), self) return dialog def to_internal_transform(self, sketch_mapping_dict, op_mapping_dict): from popupcad.manufacturing.transform_internal import TransformInternal sketch_links = self.sketch_links.copy() sketch_links['sketch_from'] = [sketch_mapping_dict[self.sub_sketch_id]] operation_link = self.subopref operation_link = op_mapping_dict[operation_link[0]], operation_link[1] operation_links = {'from': [operation_link]} new = TransformInternal(sketch_links, operation_links, self.transformtype_x, self.transformtype_y, self.shift, self.flip, self.scalex, self.scaley) new.customname = self.customname return new
class ScrapOperation2(Operation2): name = 'Scrap' valuenames = ['device buffer'] show = [] defaults = [1.] def copy(self): new = type(self)(self.operation_links, self.values, self.keepout_type) new.customname = self.customname new.id = self.id return new keepout_types = enum.enum(laser_keepout=301, mill_keepout=302, mill_flip_keepout=303) def __init__(self, *args): super(ScrapOperation2, self).__init__() self.editdata(*args) self.id = id(self) def editdata(self, operation_links, values, keepout_type): super(ScrapOperation2, self).editdata(operation_links, {}, {}) self.values = values self.keepout_type = keepout_type @classmethod def buildnewdialog(cls, design, currentop): dialog = Dialog(cls.keepout_types, cls.valuenames, cls.defaults, design.operations, cls.show, keepouttype=cls.keepout_types.laser_keepout) return dialog def buildeditdialog(self, design): sheet_id, sheet_output = self.operation_links['sheet'][0] device_id, device_output = self.operation_links['device'][0] sheet_index = design.operation_index(sheet_id) device_index = design.operation_index(device_id) operationindeces = [[sheet_index, sheet_output], [device_index, device_output]] dialog = Dialog(self.keepout_types, self.valuenames, self.defaults, design.prioroperations(self), self.show, operationindeces, self.values, self.keepout_type) return dialog def generate(self, design): import popupcad import popupcad.algorithms.removability as removability sheet_id, sheet_output = self.operation_links['sheet'][0] device_id, device_output = self.operation_links['device'][0] sheet = design.op_from_ref(sheet_id).output[sheet_output].csg device = design.op_from_ref(device_id).output[device_output].csg removable_both, removable_up, removable_down = removability.generate_removable_scrap( device, sheet, device_buffer=self.values[0] * popupcad.csg_processing_scaling) a = OperationOutput(removable_both, 'removable_both', self) b = OperationOutput(removable_up, 'removable_up', self) c = OperationOutput(removable_down, 'removable_down', self) self.output = [a, a, b, c]
class TransformInternal(Operation2): name = 'Internal Transform' transformtypes = enum(scale='scale', custom='custom') def copy(self): new = TransformInternal(self.sketch_links, self.operation_links, self.transformtype_x, self.transformtype_y, self.shift, self.flip, self.scalex, self.scaley) new.customname = self.customname new.id = self.id return new def __init__(self, *args): super(TransformInternal, self).__init__() self.editdata(*args) self.id = id(self) def editdata(self, sketch_links, operation_links, transformtype_x, transformtype_y, shift, flip, scalex, scaley): super(TransformInternal, self).editdata(operation_links, sketch_links, {}) self.transformtype_x = transformtype_x self.transformtype_y = transformtype_y self.shift = shift self.flip = flip self.scalex = scalex self.scaley = scaley def operate(self, design): opref, output = self.operation_links['from'][0] CSG = design.op_from_ref(opref).output[output].csg sketch_from_id = self.sketch_links['sketch_from'][0] sketch_to_id = self.sketch_links['sketch_to'][0] sketch_from = design.sketches[sketch_from_id] sketch_to = design.sketches[sketch_to_id] for geom in sketch_from.operationgeometry: if not geom.is_construction(): geom_from = geom break geoms_to = [ geom for geom in sketch_to.operationgeometry if not geom.is_construction() ] if self.transformtype_x == self.transformtypes.scale: scale_x = None elif self.transformtype_x == self.transformtypes.custom: scale_x = self.scalex if self.transformtype_y == self.transformtypes.scale: scale_y = None elif self.transformtype_y == self.transformtypes.custom: scale_y = self.scaley step = 1 if self.flip: step = -1 if self.shift > 0: outshift = self.shift inshift = 0 elif self.shift < 0: outshift = 0 inshift = -self.shift else: outshift = 0 inshift = 0 layerdef = design.return_layer_definition() return popupcad.algorithms.manufacturing_functions.transform_csg( layerdef, layerdef, inshift, outshift, step, geom_from, geoms_to, CSG, scale_x, scale_y) @classmethod def buildnewdialog(cls, design, currentop): dialog = Dialog(design, design.operations) return dialog def buildeditdialog(self, design): dialog = Dialog(design, design.prioroperations(self), self) return dialog
class GenericShapeBase(object): display = ['construction', 'exterior', 'interiors'] editable = ['construction'] shapetypes = enum(line='line', polyline='polyline', polygon='polygon', circle='circle', rect2point='rect2point') deletable = [] def __init__(self, exterior, interiors, construction=False, test_shapely=False): self.id = id(self) self.exterior = exterior self.interiors = interiors self.construction = construction # self.exterior = self.condition_loop(self.exterior) # self.interiors = [self.condition_loop(interior) for interior in self.interiors] self.exterior = self.remove_redundant_points(self.exterior) self.interiors = [ self.remove_redundant_points(interior) for interior in self.interiors ] def is_valid_bool(self): try: self.is_valid() return True except: return False def is_valid(self): shapely = self.to_shapely() if not shapely.is_simple: raise (NotSimple) if not shapely.is_valid: raise (ShapeInvalid) @classmethod def lastdir(cls): return popupcad.lastshapedir @classmethod def setlastdir(cls, directory): popupcad.lastshapedir = directory def isValid(self): notempty = self.len_exterior() > 0 return notempty def copy_data(self, new_type, identical=True): exterior = [vertex.copy(identical) for vertex in self.get_exterior()] interiors = [[vertex.copy(identical) for vertex in interior] for interior in self.get_interiors()] new = new_type(exterior, interiors, self.is_construction()) if identical: new.id = self.id return new def copy(self, identical=True): return self.copy_data(type(self), identical) def upgrade(self, identical=True): exterior = [ vertex.upgrade(identical) for vertex in self.get_exterior() ] interiors = [[vertex.upgrade(identical) for vertex in interior] for interior in self.get_interiors()] new = type(self)(exterior, interiors, self.is_construction()) if identical: new.id = self.id return new def get_exterior(self): return self.exterior def get_interiors(self): return self.interiors def is_construction(self): try: return self.construction except AttributeError: self.construction = False return self.construction def set_construction(self, test): self.construction = test def exteriorpoints(self, scaling=1): return [vertex.getpos(scaling) for vertex in self.get_exterior()] def interiorpoints(self, scaling=1): return [[vertex.getpos(scaling) for vertex in interior] for interior in self.get_interiors()] def exteriorpoints_3d(self, z=0): points = numpy.array( [vertex.getpos() for vertex in self.get_exterior()]) size = list(points.shape) size[1] += 1 points2 = numpy.zeros(size) points2[:, :2] = points points2[:, 2] = z return points2.tolist() def interiorpoints_3d(self, z=0): interiors2 = [] for interior in self.get_interiors(): points = numpy.array([vertex.getpos() for vertex in interior]) size = list(points.shape) size[1] += 1 points2 = numpy.zeros(size) points2[:, :2] = points points2[:, 2] = z interiors2.append(points2.tolist()) return interiors2 def vertices(self): vertices = self.get_exterior()[:] [vertices.extend(interior) for interior in self.get_interiors()] return vertices def points(self, scaling=1): return [vertex.getpos(scaling) for vertex in self.vertices()] def segments_closed(self): points = self.get_exterior() segments = list(zip(points, points[1:] + points[:1])) for points in self.get_interiors(): segments.extend(list(zip(points, points[1:] + points[:1]))) return segments def segments_open(self): points = self.get_exterior() segments = list(zip(points[:-1], points[1:])) for points in self.get_interiors(): segments.extend(list(zip(points[:-1], points[1:]))) return segments def segmentpoints(self, scaling=1): segments = self.segments() segmentpoints = [(point1.getpos(scaling), point2.getpos(scaling)) for point1, point2 in segments] return segmentpoints def painterpath(self): exterior = self.exteriorpoints(scaling=popupcad.view_scaling) interiors = self.interiorpoints(scaling=popupcad.view_scaling) return self.gen_painterpath(exterior, interiors) def gen_painterpath(self, exterior, interiors): path = qg.QPainterPath() return path def properties(self): from dev_tools.propertyeditor import PropertyEditor return PropertyEditor(self) def addvertex_exterior(self, vertex, special=False): self.exterior.append(vertex) self.update_handles() def addvertex_exterior_special(self, vertex, special=False): if len(self.get_exterior()) > 2: if special: a = [v.getpos() for v in self.get_exterior()] b = list(zip(a, a[1:] + a[:1])) c = numpy.array(b) d = numpy.array(vertex.getpos()) e = c - d f = e.reshape(-1, 4) g = (f**2).sum(1) h = g.argmin() self.insert_exterior_vertex(h + 1, vertex) self.update_handles() return self.append_exterior_vertex(vertex) self.update_handles() def removevertex(self, vertex): if vertex in self.exterior: ii = self.exterior.index(vertex) self.exterior.pop(ii) for interior in self.interiors: if vertex in self.interior: ii = interior.index(vertex) interior.pop(ii) self.update_handles() def checkedge(self, edge): import popupcad.algorithms.points as points for pt1, pt2 in zip(edge[:-1], edge[1:]): if points.twopointsthesame( pt1, pt2, popupcad.distinguishable_number_difference): raise Exception @staticmethod def _condition_loop(loop, round_vertices=False, test_rounded_vertices=True, remove_forward_redundancy=True, remove_loop_reduncancy=True, terminate_with_start=False, decimal_places=None): if len(loop) > 0: if remove_forward_redundancy: new_loop = [loop.pop(0)] while not not loop: v1 = new_loop[-1] v2 = loop.pop(0) if test_rounded_vertices: equal = v1.rounded_is_equal(v2, decimal_places) else: equal = v1.identical(v2) if not equal: new_loop.append(v2) else: new_loop = loop[:] v1 = new_loop[0] v2 = new_loop[-1] if test_rounded_vertices: equal = v1.rounded_is_equal(v2, decimal_places) else: equal = v1.identical(v2) if terminate_with_start: if not equal: new_loop.append(v1.copy(identical=False)) if remove_loop_reduncancy: if equal: new_loop.pop(-1) if round_vertices: new_loop = [item.round(decimal_places) for item in new_loop] return new_loop else: return loop def _condition(self, round_vertices=False, test_rounded_vertices=True, remove_forward_redundancy=True, remove_loop_reduncancy=True, terminate_with_start=False, decimal_places=None): self.exterior = self._condition_loop(self.exterior, round_vertices=False, test_rounded_vertices=True, remove_forward_redundancy=True, remove_loop_reduncancy=True, terminate_with_start=False, decimal_places=None) self.interiors = [ self._condition_loop(interior, round_vertices=False, test_rounded_vertices=True, remove_forward_redundancy=True, remove_loop_reduncancy=True, terminate_with_start=False, decimal_places=None) for interior in self.interiors ] @classmethod def condition_loop(cls, loop): return cls._condition_loop(loop) # def condition(self): # self.exterior = self.condition_loop(self.exterior) # self.interiors = [self.condition_loop(interior) for interior in self.interiors] @classmethod def gen_from_point_lists(cls, exterior_p, interiors_p, **kwargs): exterior = [ShapeVertex(point) for point in exterior_p] interiors = [[ShapeVertex(point) for point in interior] for interior in interiors_p] return cls(exterior, interiors, **kwargs) def genInteractiveVertices(self): try: return self._exteriorhandles, self._interiorhandles except AttributeError: self.update_handles() return self._exteriorhandles, self._interiorhandles def update_handles(self): try: for handle in self._handles: handle.harddelete() except AttributeError: pass exterior = [vertex.gen_interactive() for vertex in self.get_exterior()] interiors = [[vertex.gen_interactive() for vertex in interior] for interior in self.get_interiors()] handles = exterior[:] [handles.extend(interior) for interior in interiors] self._exteriorhandles = exterior self._interiorhandles = interiors self._handles = handles def len_exterior(self): return len(self.get_exterior()) def get_handles(self): try: return self._handles except AttributeError: self.update_handles() return self._handles def get_exterior_handles(self): try: return self._exteriorhandles except AttributeError: self.update_handles() return self._exteriorhandles def triangles3(self): return [] @staticmethod def generateQPolygon(points): poly = qg.QPolygonF( [qc.QPointF(*(point)) for point in numpy.array(points)]) return poly def is_equal(self, other): if isinstance(self, type(other)): if len(self.get_exterior()) == len(other.get_exterior()) and len( self.get_interiors()) == len(other.get_interiors()): for point1, point2 in zip(self.get_exterior(), other.get_exterior()): if not point1.is_equal( point2, popupcad.distinguishable_number_difference): return False for interior1, interior2 in zip(self.get_interiors(), other.get_interiors()): if len(interior1) != len(interior2): return False for point1, point2 in zip(interior1, interior2): if not point1.is_equal( point2, popupcad.distinguishable_number_difference): return False return True return False def scale(self, m): [item.scale(m) for item in self.get_exterior()] [ item.scale(m) for interior in self.get_interiors() for item in interior ] def shift(self, dxdy): [item.shift(dxdy) for item in self.get_exterior()] [ item.shift(dxdy) for interior in self.get_interiors() for item in interior ] def transform(self, T): exteriorpoints = (T.dot(numpy.array( self.exteriorpoints_3d(z=1)).T)).T[:, :2].tolist() interiorpoints = [(T.dot(numpy.array(interior).T)).T[:, :2].tolist() for interior in self.interiorpoints_3d(z=1)] return self.gen_from_point_lists(exteriorpoints, interiorpoints) def constrained_shift(self, dxdy, constraintsystem): a = [(item, dxdy) for item in self.get_exterior()] a.extend([(item, dxdy) for interior in self.get_interiors() for item in interior]) constraintsystem.constrained_shift(a) def flip(self): self.exterior = self.get_exterior()[::-1] self.interiors = [interior[::-1] for interior in self.get_interiors()] def hollow(self): return [self] def fill(self): return [self] def insert_exterior_vertex(self, ii, vertex): self.exterior.insert(ii, vertex) def append_exterior_vertex(self, vertex): self.exterior.append(vertex) def output_dxf(self, model_space, layer=None): csg = self.to_shapely() new = popupcad.algorithms.csg_shapely.to_generic(csg) return new.output_dxf(model_space, layer) def __lt__(self, other): return self.exteriorpoints()[0] < other.exteriorpoints()[0] def find_minimal_enclosing_circle(self): from popupcad.algorithms.minimal_enclosing_circle import numerical_stable_circle return numerical_stable_circle(self.exteriorpoints) #Gets the center def get_center(self): '''Retrieves the center point of the shape''' points = self.exteriorpoints() x_values = [point[0] / popupcad.SI_length_scaling for point in points] y_values = [point[1] / popupcad.SI_length_scaling for point in points] x = float(sum(x_values)) / len(x_values) y = float(sum(y_values)) / len(y_values) return (x, y) def exterior_points_from_center(self): '''Retrieves the exterior points relative to the center''' center = self.get_center() points = self.exteriorpoints() x_values = [ point[0] / popupcad.SI_length_scaling - center[0] for point in points ] y_values = [ point[1] / popupcad.SI_length_scaling - center[1] for point in points ] return list(zip(x_values, y_values)) @classmethod def remove_redundant_points(cls, points, scaling=1, loop_test=True): newpoints = [] if len(points) > 0: points = points[:] newpoints.append(points.pop(0)) while not not points: newpoint = points.pop(0) if not popupcad.algorithms.points.twopointsthesame( newpoints[-1].getpos(scaling), newpoint.getpos(scaling), popupcad.distinguishable_number_difference): if len(points) == 0 and loop_test: if not popupcad.algorithms.points.twopointsthesame( newpoints[0].getpos(scaling), newpoint.getpos(scaling), popupcad.distinguishable_number_difference): newpoints.append(newpoint) else: newpoints.append(newpoint) return newpoints
class PlaceOperation8(Operation2): name = 'PlaceOp' transformtypes = enum(scale='scale', custom='custom') def copy(self): new = PlaceOperation8(self.sketch_links, self.design_links, self.subopref, self.transformtype_x, self.transformtype_y, self.shift, self.flip, self.scalex, self.scaley) new.customname = self.customname new.id = self.id return new def upgrade_special(self, design): subdesign = design.subdesigns[self.design_links['subdesign'][0]] sub_sketch_id = subdesign.findlocatesketch_id() from popupcad.manufacturing.transform_external import TransformExternal sketch_links = {} sketch_links['sketch_to'] = self.sketch_links['place'] new = TransformExternal(sketch_links, self.design_links, self.subopref, sub_sketch_id, self.transformtype_x, self.transformtype_y, self.shift, self.flip, self.scalex, self.scaley) new.customname = self.customname new.id = self.id return new def __init__(self, *args): super(PlaceOperation8, self).__init__() self.editdata(*args) self.id = id(self) def editdata(self, sketch_links, design_links, subopref, transformtype_x, transformtype_y, shift, flip, scalex, scaley): super(PlaceOperation8, self).editdata({}, sketch_links, design_links) # self.sketchid = sketchid # self.subdesignid = subdesignid self.subopref = subopref self.transformtype_x = transformtype_x self.transformtype_y = transformtype_y self.shift = shift self.flip = flip self.scalex = scalex self.scaley = scaley def operate(self, design): subdesign = design.subdesigns[self.design_links['subdesign'][0]] sketch_to_id = self.sketch_links['place'][0] sketch_to = design.sketches[sketch_to_id] operation_ref, output_index = self.subopref csg_laminate = subdesign.operations[subdesign.operation_index( operation_ref)].output[output_index].csg geom_from = subdesign.findlocateline() geoms_to = [ geom for geom in sketch_to.operationgeometry if not geom.is_construction() ] if self.transformtype_x == self.transformtypes.scale: scale_x = None elif self.transformtype_x == self.transformtypes.custom: scale_x = self.scalex if self.transformtype_y == self.transformtypes.scale: scale_y = None elif self.transformtype_y == self.transformtypes.custom: scale_y = self.scaley step = 1 if self.flip: step = -1 if self.shift > 0: outshift = self.shift inshift = 0 elif self.shift < 0: outshift = 0 inshift = -self.shift else: outshift = 0 inshift = 0 layerdef_from = subdesign.return_layer_definition() layerdef_to = design.return_layer_definition() return popupcad.algorithms.manufacturing_functions.transform_csg( layerdef_from, layerdef_to, inshift, outshift, step, geom_from, geoms_to, csg_laminate, scale_x, scale_y) def fromQTransform(self, tin): tout = numpy.array([[tin.m11(), tin.m12(), tin.m13()], [tin.m21(), tin.m22(), tin.m23()], [tin.m31(), tin.m32(), tin.m33()]]).T return tout def toQTransform(self, tin): tout = qg.QTransform(tin[1][1], tin[1][2], tin[1][3], tin[2][1], tin[2][2], tin[2][3], tin[3][1], tin[3][2], tin[3][3]) return tout @classmethod def buildnewdialog(cls, design, currentop): dialog = Dialog(design, design.operations) return dialog def buildeditdialog(self, design): sketch = design.sketches[self.sketch_links['place'][0]] subdesign = design.subdesigns[self.design_links['subdesign'][0]] dialog = Dialog(design, design.prioroperations(self), sketch=sketch, subdesign=subdesign, subopref=self.subopref, transformtype_x=self.transformtype_x, transformtype_y=self.transformtype_y, shift=self.shift, flip=self.flip, scalex=self.scalex, scaley=self.scaley) return dialog