Xorn
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
conn.py
Go to the documentation of this file.
1 # xorn.geda.netlist - gEDA Netlist Extraction and Generation
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2016 Roland Lutz
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20 ## \namespace xorn.geda.netlist.conn
21 ## Find and track the connections between net segments.
22 #
23 # Instances of net segments (including bus segments and pins) are
24 # represented by a pair <tt>(path, ob)</tt> where \c path is a
25 # sequence of component objects, the symbol of each containing the
26 # next component or (in the case of the last component) the net, and
27 # \c ob is the actual net object.
28 
29 import collections
30 
31 import xorn.proxy
32 import xorn.storage
33 
34 CONN_ENDPOINT, CONN_MIDPOINT = xrange(2)
35 
36 ## A single connection between two objects.
37 #
38 # Each instance represents a single unidirectional connection between
39 # two objects. The connection type is always in reference to how ob1
40 # is connected to ob0.
41 
42 Connection = collections.namedtuple('Connection', [
43  'x', 'y', 'type', 'path0', 'ob0', 'path1', 'ob1', 'whichend0', 'whichend1'
44 ])
45 
46 ## Return the absolute coordinates of the endpoints of a net instance.
47 #
48 # \returns a pair of pairs <tt>((x0, x1), (y0, y1))</tt>
49 #
50 # \throws ValueError if \c path contains an object which is not a component
51 # \throws ValueError if \c ob is not a net
52 
53 def endpoints((path, ob)):
54  data = ob.data()
55  if not isinstance(data, xorn.storage.Net):
56  raise ValueError
57 
58  x0 = data.x
59  y0 = data.y
60  x1 = data.x + data.width
61  y1 = data.y + data.height
62 
63  for component in reversed(path):
64  data = component.data()
65  if not isinstance(data, xorn.storage.Component):
66  raise ValueError
67 
68  if data.mirror:
69  x0 = -x0
70  x1 = -x1
71 
72  if data.angle == 90:
73  x0, y0 = -y0, x0
74  x1, y1 = -y1, x1
75  elif data.angle == 180:
76  x0, y0 = -x0, -y0
77  x1, y1 = -x1, -y1
78  elif data.angle == 270:
79  x0, y0 = y0, -x0
80  x1, y1 = y1, -x1
81 
82  x0 += data.x
83  y0 += data.y
84  x1 += data.x
85  y1 += data.y
86 
87  return (x0, x1), (y0, y1)
88 
89 ## Checks if a point is on an orthogonal net segment and between its
90 ## endpoints.
91 #
92 # \returns whether the net segment has horizontal or vertical
93 # orientation, the point is on the net segment, and it is not
94 # identical with one of its endpoints
95 #
96 # \throws ValueError if \c path contains an object which is not a component
97 # \throws ValueError if \c ob is not a net
98 
99 def s_conn_check_midpoint(instance, x, y):
100  (x0, x1), (y0, y1) = endpoints(instance)
101  return x0 == x and x1 == x and y > min(y0, y1) and y < max(y0, y1) \
102  or y0 == y and y1 == y and x > min(x0, x1) and x < max(x0, x1)
103 
104 ## Returns all net instances in a given revision.
105 #
106 # This includes all net objects in the revision as well as net objects
107 # inside symbols.
108 
109 def all_net_instances_in_revision(rev, path = ()):
110  for ob in rev.toplevel_objects():
111  for instance in all_net_instances_in_object(ob, path):
112  yield instance
113 
114 ## Returns all net instances in a given object.
115 #
116 # If \a ob is a net, returns the appropriate net segment. If \a is a
117 # component, returns net objects inside its symbol.
118 
119 def all_net_instances_in_object(ob, path = ()):
120  data = ob.data()
121  if isinstance(data, xorn.storage.Net):
122  return [(path, ob)]
123  elif isinstance(data, xorn.storage.Component):
125  xorn.proxy.RevisionProxy(data.symbol.prim_objs), path + (ob, ))
126  else:
127  return []
128 
129 
130 ## Return all connections of a net instance.
131 #
132 # This function searches for all geometrical conections of the net
133 # instance <tt>(path, ob)</tt> to all other connectable objects in the
134 # revision.
135 
136 def s_conn_update_line_object((path, ob), instances_by_endpoint,
137  horizontal_instances,
138  vertical_instances):
139  if path:
140  rev = xorn.proxy.RevisionProxy(path[-1].rev)
141  else:
142  rev = xorn.proxy.RevisionProxy(ob.rev)
143 
144  # rename argument variables
145  path0 = path; del path
146  ob0 = ob; del ob
147 
148  data0 = ob0.data()
149  if not isinstance(data0, xorn.storage.Net):
150  raise ValueError
151  x0, y0 = endpoints((path0, ob0)) # those are both PAIRS of coordinates
152 
153  # If ob0 is a pin, only check the correct end
154  if data0.is_pin:
155  ends0 = [0]
156  else:
157  ends0 = [0, 1]
158 
159  def can_connect((path0, ob0), (path1, ob1)):
160  # An object inside a symbol can only be connected up to another
161  # object if they are (a) both inside the same object, or (b)
162  # the object inside a symbol is a pin.
163 
164  # 1. Both objects are inside a symbol
165  if path0 and path1:
166  # If inside different symbols, both must be pins to connect.
167  if path0 != path1 and not data0.is_pin or not data1.is_pin:
168  return False
169 
170  # 2. Updating object is inside a symbol, but ob1 is not.
171  elif path0 and not path1:
172  if not data0.is_pin:
173  return False
174 
175  # 3. Updating object not inside symbol, but ob1 is.
176  elif not path0 and path1:
177  if not data1.is_pin:
178  return False
179 
180  return True
181 
182  for end in ends0:
183  for path1, ob1 in instances_by_endpoint.get((x0[end], y0[end]), []):
184  if ob1 == ob0:
185  continue
186 
187  data1 = ob1.data()
188  if not isinstance(data1, xorn.storage.Net):
189  raise ValueError
190  x1, y1 = endpoints((path1, ob1))
191 
192  # If ob1 is a pin, only check the correct end
193  if data1.is_pin:
194  ends1 = [0]
195  else:
196  ends1 = [0, 1]
197 
198  if not can_connect((path0, ob0), (path1, ob1)):
199  continue
200 
201  # Here is where you check the end points
202  # Check both end points of ob1
203  for w1 in ends1:
204  # Check both end points of ob0
205  for w0 in ends0:
206  # Check for coincidence and compatability between
207  # the objects being tested.
208  if x0[w0] == x1[w1] and y0[w0] == y1[w1] \
209  and data0.is_bus == data1.is_bus:
210  yield Connection(
211  x = x0[w0], y = y0[w0], type = CONN_ENDPOINT,
212  path0 = path0, ob0 = ob0, whichend0 = w0,
213  path1 = path1, ob1 = ob1, whichend1 = w1)
214  yield Connection(
215  x = x0[w0], y = y0[w0], type = CONN_ENDPOINT,
216  path0 = path1, ob0 = ob1, whichend0 = w1,
217  path1 = path0, ob1 = ob0, whichend1 = w0)
218 
219  for end in ends0:
220  for path1, ob1 in \
221  vertical_instances.get(x0[end], []) + \
222  horizontal_instances.get(y0[end], []):
223  if ob1 == ob0:
224  continue
225 
226  data1 = ob1.data()
227  if not isinstance(data1, xorn.storage.Net):
228  raise ValueError
229  x1, y1 = endpoints((path1, ob1))
230 
231  # If ob1 is a pin, only check the correct end
232  if data1.is_pin:
233  ends1 = [0]
234  else:
235  ends1 = [0, 1]
236 
237  if not can_connect((path0, ob0), (path1, ob1)):
238  continue
239 
240  # Check both end points of ob0 against midpoints of ob1
241  for w0 in ends0:
242  # check for midpoint of ob1, w0 endpoint of current obj
243 
244  # Pins are not allowed midpoint connections onto them.
245  # Allow nets to connect to the middle of buses.
246  # Allow compatible objects to connect.
247  if s_conn_check_midpoint((path1, ob1), x0[w0], y0[w0]) and \
248  not data1.is_pin and (
249  (not data0.is_pin and not data0.is_bus and data1.is_bus)
250  or data0.is_bus == data1.is_bus):
251  yield Connection(
252  x = x0[w0], y = y0[w0], type = CONN_MIDPOINT,
253  path0 = path0, ob0 = ob0, whichend0 = w0,
254  path1 = path1, ob1 = ob1, whichend1 = -1)
255  yield Connection(
256  x = x0[w0], y = y0[w0], type = CONN_MIDPOINT,
257  path0 = path1, ob0 = ob1, whichend0 = -1,
258  path1 = path0, ob1 = ob0, whichend1 = w0)
259 
260 def _append_or_create(d, key, item):
261  try:
262  l = d[key]
263  except KeyError:
264  l = d[key] = []
265  l.append(item)
266 
267 ## Tracks the connections of the net segments in a revision at a time.
268 
270  def __init__(self, rev):
271  self.rev = None
272  self.connections = []
273  self.connection_dict = {}
274 
278 
279  self.goto(rev)
280 
281  def goto(self, rev):
282  if rev.is_transient():
283  raise ValueError
284 
285  if self.rev is not None:
286  removed_objects = \
288  modified_objects = \
290  added_objects = \
292  else:
293  removed_objects = []
294  modified_objects = []
295  added_objects = rev.rev.get_objects()
296 
297  # remove objects
298  removed_instances = [instance
299  for ob in removed_objects + modified_objects
300  for instance in all_net_instances_in_object(
301  xorn.proxy.ObjectProxy(self.rev.rev, ob))]
302 
303  for instance in removed_instances:
304  (x0, x1), (y0, y1) = endpoints(instance)
305  self.instances_by_endpoint[x0, y0].remove(instance)
306  if not instance[1].data().is_pin:
307  self.instances_by_endpoint[x1, y1].remove(instance)
308  if x0 == x1:
309  self.vertical_instances[x0].remove(instance)
310  if y0 == y1:
311  self.horizontal_instances[y0].remove(instance)
312 
313  for conn in self.connections:
314  if (conn.path0, conn.ob0) == instance or \
315  (conn.path1, conn.ob1) == instance:
316  self.connections.remove(conn)
317  self.connection_dict[conn.path0, conn.ob0].remove(conn)
318 
319  self.rev = rev
320 
321  # add objects
322  added_instances = [instance
323  for ob in modified_objects + added_objects
324  for instance in all_net_instances_in_object(
325  xorn.proxy.ObjectProxy(self.rev.rev, ob))]
326 
327  for instance in added_instances:
328  (x0, x1), (y0, y1) = endpoints(instance)
329  _append_or_create(self.instances_by_endpoint, (x0, y0), instance)
330  if not instance[1].data().is_pin:
331  _append_or_create(self.instances_by_endpoint, (x1, y1), instance)
332  if x0 == x1:
333  _append_or_create(self.vertical_instances, x0, instance)
334  if y0 == y1:
335  _append_or_create(self.horizontal_instances, y0, instance)
336 
337  for instance in added_instances:
338  for conn in s_conn_update_line_object(instance,
341  self.vertical_instances):
342  if (conn.path0, conn.ob0) not in self.connection_dict or \
343  conn not in self.connection_dict[conn.path0, conn.ob0]:
344  self.connections.append(conn)
345  _append_or_create(
346  self.connection_dict, (conn.path0, conn.ob0), conn)
347 
348  ## Return all net instances directly connected to a net instance.
349 
350  def connected_to(self, instance):
351  result = []
352  result_set = set()
353  for conn in self.connection_dict.get(instance, []):
354  if (conn.path1, conn.ob1) not in result_set:
355  result.append((conn.path1, conn.ob1))
356  result_set.add((conn.path1, conn.ob1))
357  return result
def s_conn_update_line_object
Return all connections of a net instance.
Definition: conn.py:138
Schematic net segment, bus segment, or pin.
Definition: storage.py:549
def all_net_instances_in_object
Returns all net instances in a given object.
Definition: conn.py:119
def s_conn_check_midpoint
Checks if a point is on an orthogonal net segment and between its endpoints.
Definition: conn.py:99
def get_added_objects
Return a list of objects which are in one revision but not in another.
Definition: storage.py:354
High-level revision proxy class.
Definition: proxy.py:24
def get_removed_objects
Return a list of objects which are in one revision but not in another.
Definition: storage.py:365
tuple Connection
A single connection between two objects.
Definition: conn.py:42
Schematic component.
Definition: storage.py:527
High-level object proxy class.
Definition: proxy.py:77
High-level proxy classes for the storage backend.
Definition: proxy.py:1
Tracks the connections of the net segments in a revision at a time.
Definition: conn.py:269
def get_modified_objects
Return a list of objects which exist in two revisions but have different type or data.
Definition: storage.py:375
Xorn storage backend.
Definition: storage.py:1
def endpoints
Return the absolute coordinates of the endpoints of a net instance.
Definition: conn.py:53
def all_net_instances_in_revision
Returns all net instances in a given revision.
Definition: conn.py:109
def connected_to
Return all net instances directly connected to a net instance.
Definition: conn.py:350