2 # (c) Copyright 2013 TJ <hacker@iam.tj>
3 # Licensed on the terms of the GNU General Public License version 3 (see COPYING)
5 # Base2 Runner: Circuit design and management
7 # TODO: module in development, not ready for any use yet
10 import os.path, pygame, engine, library
11 from library import enum
15 """ Models a straight line segment of a complete Trace """
17 angles = ( 0 , 45 , 90, 135, 180, 225, 270, 315)
19 # references to an attached Via and its position
23 def __init__(self, length=1, direction=library.directions.E, via=None, via_position=library.positions.NONE):
24 """ default segment runs for one game tile length left-to-right
25 length: number of game 'tiles' this segment covers
26 direction: enum directions
28 via_position: enum positions
30 # ensure all values are legal before doing any assignment
31 if not direction in library.directions.reverse_mapping:
32 # 'direction' is an index enum, so ensure it exists in the enum before using it to select the angle
33 raise IndexError('direction invalid')
34 elif not via_position in library.positions:
35 # 'position' is an enum so ensure it is valid
36 raise IndexError('position invalid')
37 elif via and not isinstance(via, Via):
38 raise TypeError('expecting a Via')
41 self.angle = angles[direction]
43 self.via_position = via_position
45 def set_via(self, via, position):
46 """ link to the related Via
48 position: enum via_pos
50 if not isinstance(via, Via):
51 raise TypeError('expecting a Via')
52 elif not position in library.positions:
53 # FIXME: ensure the line above works for testing position's membership
54 raise IndexError('position invalid')
57 self.via_position = position
59 def set_direction(self, direction):
60 """ set the direction the segment is headed
61 direction: enum directions
63 if not direction in library.directions.reverse_mapping:
64 raise IndexError('direction invalid')
65 self.angle = self.angles(direction)
69 """ Models a single circuit trace made up of multiple segments """
70 segments = list() # ordered list of one or more TraceSegments
72 # colours['metal'] = (192, 192, 192) # colours of the trace
73 # colours['shadow'] = (64, 64, 64)
74 # colours['reflection'] = (255, 255, 255)
76 def check_range(self, colour):
77 """ Ensure a colour value is legal """
78 if not (type(colour) == tuple or type(colour) == int):
79 raise TypeError('colour expeted to be tuple or int')
80 elif not len(colour) == 3:
81 raise ValueError('colour tuples require exactly 3 items (RGB)')
83 if type(colour) == int:
84 return colour >= 0 and colour <= 255
86 return self.check_range(colour[0]) and self.check_range(colour[1]) and self.check_range(colour[2])
88 def __init__(self, metal=(192, 192, 192), shadow=(64, 64, 64), reflection=(255, 255, 255), segments=None):
89 # set the colours of the trace to give a 3D raised appearance
90 if metal and check_range(metal): self.colours['metal'] = metal
91 if shadow and check_range(shadow): self.colours['shadow'] = shadow
92 if reflection and check_range(reflection): self.colours['reflection'] = reflection
95 self.segments = TraceSegment()
97 self.segments = segments
99 with self.segments[0]:
101 via_position = library.positions.NONE
103 def draw(self, surface):
104 """ Draw the Trace onto pygame.Surface """
109 """ Models a through-hole otherwise known as a Via """
110 trace = None # the Trace this via is attached to
111 linked = None # the Via this via links to
115 """ Group of traces all going in the same direction """
116 width = 8 # default is an 8-bit bus
119 def __init__(self, buswidth=8):
120 """ Create a new bus consisting of 'buswidth' traces """
123 class IntegratedCircuit:
124 """ semiconductor component """
125 pins = list() # index = pin number, value = signal name
128 class DualInLine(IntegratedCircuit):
129 """ Dual In-Line Integrated Circuit """
133 def __init__(self, pin_qty):
134 if not (pin_qty > 0 and pin_qty % 2 == 0):
135 raise ValueError('DIL ICs have even number of pins')
137 for p in range(1, pin_qty+1):
138 self.pins.insert(p, library.signals.NC)
142 """ Models a single logic gate """
148 def __init__(self, gate_type, inputs=None):
149 if not gate_type in library.gates:
152 self.gate_type = gate_type
155 # default to 2 inputs at logic 0
156 self.inputs = (False, False)
157 elif not (type(inputs) is list and isinstance(inputs[0], bool)):
161 self.input_max = len(self.inputs)
165 """ Needs to be over-ridden in derived classes """
168 def set_input(self, input, value):
169 if not(input >= 0 and input < self.input_max):
170 raise ValueError("input expected between 0 and" % self.input_max)
172 if not isinstance(value, bool):
173 raise TypeError('Boolean expected')
175 self.inputs[input] = value
178 def get_output(self):
181 def min_inputs(self, inputs, minimum):
182 """ enforce a minimum number of logic inputs in the list """
183 return inputs if len(inputs) >= minimum else (inputs, (False for i in range(0, minimum - len(inputs))))
186 # Derive each of the common logic gate types
188 def __init__(self, inputs=None):
189 Gate.__init__(library.gates.AND, self.min_inputs(inputs, 2))
192 self.output = self.inputs[0]
193 for i in range(1, self.input_max):
194 self.output &= self.inputs[i] # AND
198 def __init__(self, inputs=None):
199 Gate.__init__(library.gates.OR, self.min_inputs(inputs, 2))
202 self.output = self.inputs[0]
203 for i in range (1, self.input_max):
204 self.output |= self.inputs[i] # OR
208 def __init__(self, inputs=None):
209 Gate.__init__(library.gates.XOR, self.min_inputs(inputs, 2))
212 self.output = self.inputs[0]
213 for i in range (1, self.input_max):
214 self.output ^= self.inputs[i] # XOR
218 def __init__(self, inputs=(False, )):
220 raise ValueError('NOT must have only 1 input')
221 Gate.__init__(library.gates.NOT, inputs)
224 self.output = ~self.inputs[0] # invert
227 class GateNAND(Gate):
228 def __init__(self, inputs=None):
229 Gate.__init__(library.gates.NAND, self.min_inputs(inputs, 2))
232 self.output = self.inputs[0]
233 for i in range(1, self.input_max):
234 self.output &= self.inputs[i] # AND
235 self.output = ~self.output # invert
239 def __init__(self, inputs=None):
240 Gate.__init__(library.gates.NOR, self.min_inputs(inputs, 2))
243 self.output = self.inputs[0]
244 for i in range (1, self.input_max):
245 self.output |= self.inputs[i] # OR
246 self.output = ~self.output # invert
250 """ Models an IC that contains one or more digital logic gates """
251 gates = list() # list of gates
253 def __init__(self, gates=()):
254 if not type(gates) is list:
255 raise TypeError('expected list')
256 elif not type(gates[0]) is Gate:
257 raise TypeError('expected Gate')
262 if not type(gate) is Gate:
263 raise TypeError('expected Gate')
265 self.gates.append(gate)
267 class ICLogicDIL(ICLogic, DualInLine):
268 """ Model of a Logic IC ina DIL package """
269 def __init__(self, gates=(), pins_qty=14):
270 ICLogic.__init(gates)
271 DualInLine.__init__(pins_qty)
274 class SN7400(ICLogicDIL):
275 """ Model of a Texas Instruments SN7400 quad 2-input NAND gate DIP14 package """
278 # call the super classes constructors
280 for i in range(0, 4):
281 gates.append(GateNAND())
282 ICLogicDIL.__init__(gates, 14)
285 class SN741G00(ICLogicDIL):
286 """ Model of a Texas Instruments SN741G00 single 2-input NAND gate """
289 class TSSOP(IntegratedCircuit):
290 """ Thin Shrink Small Outline Package """
294 def __init__(self, pins):
299 """ Models a digital electronics logic circuit printed on a circuit board """
306 def trace_add(self, trace_list):
310 def component_insert(self, component):
311 """ Adds a component to the circuit """