Beispiel #1
0
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
Beispiel #2
0
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
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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]
Beispiel #6
0
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
Beispiel #8
0
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