aboutsummaryrefslogtreecommitdiff
path: root/gerberex/rs274x.py
blob: 13d3421ef1008376f352ddf759c4fb8d5ad31e1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright 2019 Hiroshi Murayama <opiopan@gmail.com>

import gerber.rs274x
from gerber.gerber_statements import ADParamStmt, CoordStmt
from gerberex.gerber_statements import AMParamStmt, AMParamStmtEx, ADParamStmtEx
from gerberex.utility import rotate

class GerberFile(gerber.rs274x.GerberFile):
    @classmethod
    def from_gerber_file(cls, gerber_file):
        if not isinstance(gerber_file, gerber.rs274x.GerberFile):
            raise Exception('only gerber.rs274x.GerberFile object is specified')
        
        def swap_statement(statement):
            if isinstance(statement, AMParamStmt) and not isinstance(statement, AMParamStmtEx):
                return AMParamStmtEx.from_stmt(statement)
            elif isinstance(statement, ADParamStmt) and not isinstance(statement, AMParamStmtEx):
                return ADParamStmtEx.from_stmt(statement)
            else:
                return statement
        statements = [swap_statement(statement) for statement in gerber_file.statements]
        return cls(statements, gerber_file.settings, gerber_file.primitives,\
                   gerber_file.apertures, gerber_file.filename)

    def __init__(self, statements, settings, primitives, apertures, filename=None):
        super(GerberFile, self).__init__(statements, settings, primitives, apertures, filename)

    def offset(self, x_offset=0, y_offset=0):
        for statement in self.statements:
            if isinstance(statement, CoordStmt):
                if statement.x is not None:
                    statement.x += x_offset
                if statement.y is not None:
                    statement.y += y_offset
            else:
                statement.offset(x_offset, y_offset)
        for primitive in self.primitives:
            primitive.offset(x_offset, y_offset)

    def rotate(self, angle, center=(0,0)):
        if angle % 360 == 0:
            return
        self._generalize_aperture()
        last_x = 0
        last_y = 0
        last_rx = 0
        last_ry = 0
        for statement in self.statements:
            if isinstance(statement, AMParamStmtEx):
                statement.rotate(angle, center)
            elif isinstance(statement, CoordStmt) and statement.x != None and statement.y != None:
                if statement.i != None and statement.j != None:
                    cx = last_x + statement.i
                    cy = last_y + statement.j
                    cx, cy = rotate(cx, cy, angle, center)
                    statement.i = cx - last_rx
                    statement.j = cy - last_ry
                last_x = statement.x
                last_y = statement.y
                last_rx, last_ry = rotate(statement.x, statement.y, angle, center)
                statement.x = last_rx
                statement.y = last_ry
    
    def _generalize_aperture(self):
        RECTANGLE = 0
        LANDSCAPE_OBROUND = 1
        PORTRATE_OBROUND = 2
        POLYGON = 3
        macro_defs = [
            ('MACR', AMParamStmtEx.rectangle),
            ('MACLO', AMParamStmtEx.landscape_obround),
            ('MACPO', AMParamStmtEx.portrate_obround),
            ('MACP', AMParamStmtEx.polygon)
        ]

        need_to_change = False
        insert_point = 0
        last_aperture = 0
        macros = {}
        for idx in range(0, len(self.statements)):
            statement = self.statements[idx]
            if isinstance(statement, AMParamStmtEx):
                macros[statement.name] = statement
                if not need_to_change:
                    insert_point = idx + 1
            if isinstance(statement, ADParamStmt) and statement.shape in ['R', 'O', 'P']:
                need_to_change = True
                last_aperture = idx
        
        if need_to_change:
            for idx in range(0, len(macro_defs)):
                macro_def = macro_defs[idx]
                name = macro_def[0]
                num = 1
                while name in macros:
                    name = '%s_%d' % (macro_def[0], num)
                    num += 1
                self.statements.insert(insert_point, macro_def[1](name, self.units))
                macro_defs[idx] = (name, macro_def[1])
            for idx in range(insert_point, last_aperture + len(macro_defs) + 1):
                statement = self.statements[idx]
                if isinstance(statement, ADParamStmt):
                    if statement.shape == 'R':
                        statement.shape = macro_defs[RECTANGLE][0]
                    elif statement.shape == 'O':
                        x = statement.modifiers[0][0] \
                            if len(statement.modifiers[0]) > 0 else 0
                        y = statement.modifiers[0][1] \
                            if len(statement.modifiers[0]) > 1 else 0
                        statement.shape = macro_defs[LANDSCAPE_OBROUND][0] \
                                          if x > y else macro_defs[PORTRATE_OBROUND][0] 
                    elif statement.shape == 'P':
                        statement.shape = macro_defs[POLYGON][0]