1 /*
2  * node-store.c
3  *
4  *
5  * Store nodes, wires to hashtables.
6  * Generate new wires, merge new wire create requests where possible with
7  *already existing ones
8  *
9  *
10  * Authors:
11  *  Richard Hult <rhult@hem.passagen.se>
12  *  Ricardo Markiewicz <rmarkie@fi.uba.ar>
13  *  Andres de Barbara <adebarbara@fi.uba.ar>
14  *  Marc Lorber <lorber.marc@wanadoo.fr>
15  *  Bernhard Schuster <bernhard@ahoi.io>
16  *  Guido Trentalancia <guido@trentalancia.com>
17  *
18  * Web page: https://ahoi.io/project/oregano
19  *
20  * Copyright (C) 1999-2001  Richard Hult
21  * Copyright (C) 2003,2006  Ricardo Markiewicz
22  * Copyright (C) 2009-2012  Marc Lorber
23  * Copyright (C) 2012-2013  Bernhard Schuster
24  * Copyright (C) 2017       Guido Trentalancia
25  *
26  * This program is free software; you can redistribute it and/or
27  * modify it under the terms of the GNU General Public License as
28  * published by the Free Software Foundation; either version 2 of the
29  * License, or (at your option) any later version.
30  *
31  * This program is distributed in the hope that it will be useful,
32  * but WITHOUT ANY WARRANTY; without even the implied warranty of
33  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
34  * General Public License for more details.
35  *
36  * You should have received a copy of the GNU General Public
37  * License along with this program; if not, write to the
38  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
39  * Boston, MA 02110-1301, USA.
40  */
41 
42 #include <glib.h>
43 #include <glib-object.h>
44 #include <gtk/gtk.h>
45 #include <math.h>
46 
47 #define NODE_EPSILON 1e-10
48 #define HASH_EPSILON 1e-3
49 #include "node-store.h"
50 #include "node-store-private.h"
51 #include "node.h"
52 #include "part.h"
53 #include "wire.h"
54 #include "wire-private.h"
55 
56 #include "debug.h"
57 
58 /* NODE_EPSILON is used to check for intersection. */
59 /* HASH_EPSILON is used in the hash equality check function. */
60 
61 /* Share an endpoint? */
62 #define SEP(p1x, p1y, p2x, p2y) (IS_EQ (p1x, p2x) && IS_EQ (p1y, p2y))
63 
64 /* Equals? */
65 #define IS_EQ(a, b) (fabs ((a) - (b)) < NODE_EPSILON)
66 
67 #define ON_THE_WIRE(p1, start, end)                                                                \
68 	(fabs ((end.y - start.y) * (p1.x - start.x) - (end.x - start.x) * (p1.y - start.y)) <          \
69 	 NODE_EPSILON)
70 
71 static void node_store_class_init (NodeStoreClass *klass);
72 static void node_store_init (NodeStore *store);
73 static void node_store_finalize (GObject *self);
74 static void node_store_dispose (GObject *self);
75 
76 enum { NODE_DOT_ADDED, NODE_DOT_REMOVED, LAST_SIGNAL };
77 
78 G_DEFINE_TYPE (NodeStore, node_store, G_TYPE_OBJECT);
79 
80 static guint node_store_signals[LAST_SIGNAL] = {0};
81 
node_store_dispose(GObject * self)82 static void node_store_dispose (GObject *self)
83 {
84 	G_OBJECT_CLASS (node_store_parent_class)->dispose (self);
85 }
86 
node_store_finalize(GObject * object)87 static void node_store_finalize (GObject *object)
88 {
89 	NodeStore *self = NODE_STORE (object);
90 
91 	if (self->nodes) {
92 		g_hash_table_destroy (self->nodes);
93 		self->nodes = NULL;
94 	}
95 
96 	if (self->wires) {
97 		g_list_free (self->wires);
98 		self->wires = NULL;
99 	}
100 	if (self->parts) {
101 		g_list_free (self->parts);
102 		self->parts = NULL;
103 	}
104 	if (self->items) {
105 		g_list_free (self->items);
106 		self->items = NULL;
107 	}
108 	if (self->textbox) {
109 		g_list_free (self->textbox);
110 		self->textbox = NULL;
111 	}
112 
113 	G_OBJECT_CLASS (node_store_parent_class)->finalize (object);
114 }
115 
node_store_class_init(NodeStoreClass * klass)116 static void node_store_class_init (NodeStoreClass *klass)
117 {
118 	GObjectClass *gobject_class;
119 
120 	gobject_class = G_OBJECT_CLASS (klass);
121 	node_store_parent_class = g_type_class_peek_parent (klass);
122 
123 	gobject_class->finalize = node_store_finalize;
124 	gobject_class->dispose = node_store_dispose;
125 
126 	node_store_signals[NODE_DOT_ADDED] =
127 	    g_signal_new ("node_dot_added", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_FIRST,
128 	                  G_STRUCT_OFFSET (NodeStoreClass, node_dot_added), NULL, NULL,
129 	                  g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
130 
131 	node_store_signals[NODE_DOT_REMOVED] =
132 	    g_signal_new ("node_dot_removed", TYPE_NODE_STORE, G_SIGNAL_RUN_FIRST,
133 	                  G_STRUCT_OFFSET (NodeStoreClass, node_dot_removed), NULL, NULL,
134 	                  g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
135 }
136 
node_store_init(NodeStore * self)137 static void node_store_init (NodeStore *self)
138 {
139 	self->nodes = g_hash_table_new (node_hash, node_equal);
140 	self->wires = NULL;
141 	self->parts = NULL;
142 	self->items = NULL;
143 	self->textbox = NULL;
144 }
145 
146 ////////////////////////////////////////////////////////////////////////////////
147 //         END BOILERPLATE
148 ////////////////////////////////////////////////////////////////////////////////
149 
node_store_new(void)150 NodeStore *node_store_new (void) { return NODE_STORE (g_object_new (TYPE_NODE_STORE, NULL)); }
151 
node_dot_added_callback(Node * node,Coords * pos,NodeStore * store)152 static void node_dot_added_callback (Node *node, Coords *pos, NodeStore *store)
153 {
154 	g_return_if_fail (store != NULL);
155 	g_return_if_fail (IS_NODE_STORE (store));
156 
157 	g_signal_emit_by_name (G_OBJECT (store), "node_dot_added", pos);
158 }
159 
node_dot_removed_callback(Node * node,Coords * pos,NodeStore * store)160 static void node_dot_removed_callback (Node *node, Coords *pos, NodeStore *store)
161 {
162 	g_return_if_fail (store);
163 	g_return_if_fail (IS_NODE_STORE (store));
164 
165 	g_signal_emit_by_name (G_OBJECT (store), "node_dot_removed", pos);
166 }
167 
168 /**
169  * lookup if a node at @pos exists, if so return it, otherwise
170  * create one, add it to the nodestore and return it
171  */
node_store_get_or_create_node(NodeStore * self,Coords pos)172 Node *node_store_get_or_create_node (NodeStore *self, Coords pos)
173 {
174 	Node *node;
175 
176 	g_return_val_if_fail (self, NULL);
177 	g_return_val_if_fail (IS_NODE_STORE (self), NULL);
178 
179 	node = g_hash_table_lookup (self->nodes, &pos);
180 
181 	if (!node) {
182 		// Create a node at (x, y) and return it.
183 		node = node_new (pos);
184 
185 		g_signal_connect_object (G_OBJECT (node), "node_dot_added",
186 		                         G_CALLBACK (node_dot_added_callback), G_OBJECT (self), 0);
187 
188 		g_signal_connect_object (G_OBJECT (node), "node_dot_removed",
189 		                         G_CALLBACK (node_dot_removed_callback), G_OBJECT (self), 0);
190 
191 		g_hash_table_insert (self->nodes, &node->key, node);
192 	}
193 
194 	return node;
195 }
196 
197 /**
198  * register a part to the nodestore
199  */
node_store_add_part(NodeStore * self,Part * part)200 gboolean node_store_add_part (NodeStore *self, Part *part)
201 {
202 	NG_DEBUG ("-0-");
203 	g_return_val_if_fail (self, FALSE);
204 	g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
205 	g_return_val_if_fail (part, FALSE);
206 	g_return_val_if_fail (IS_PART (part), FALSE);
207 
208 	GSList *iter, *copy;
209 	Node *node;
210 	Coords pin_pos;
211 	Coords part_pos;
212 	int i, num_pins;
213 	Pin *pins;
214 
215 	num_pins = part_get_num_pins (part);
216 	pins = part_get_pins (part);
217 
218 	item_data_get_pos (ITEM_DATA (part), &part_pos);
219 
220 	for (i = 0; i < num_pins; i++) {
221 		// Use the position of the pin as hash key.
222 		pin_pos.x = part_pos.x + pins[i].offset.x;
223 		pin_pos.y = part_pos.y + pins[i].offset.y;
224 
225 		// Retrieve a node for this position.
226 		node = node_store_get_or_create_node (self, pin_pos);
227 
228 		// Add all the wires that intersect this pin to the node store.
229 		copy = get_wires_at_pos (self, pin_pos);
230 		for (iter = copy; iter; iter = iter->next) {
231 			Wire *wire = copy->data;
232 
233 			node_add_wire (node, wire);
234 			wire_add_node (wire, node);
235 		}
236 
237 		g_slist_free (copy);
238 
239 		node_add_pin (node, &pins[i]);
240 	}
241 
242 	g_object_set (G_OBJECT (part), "store", self, NULL);
243 	self->parts = g_list_prepend (self->parts, part);
244 	self->items = g_list_prepend (self->items, part);
245 
246 	return TRUE;
247 }
248 
249 /**
250  * remove/unregister a part from the nodestore
251  * this does _not_ free the part!
252  */
node_store_remove_part(NodeStore * self,Part * part)253 gboolean node_store_remove_part (NodeStore *self, Part *part)
254 {
255 	Node *node;
256 	Coords pin_pos;
257 	Coords part_pos;
258 	int i, num_pins;
259 	Pin *pins;
260 
261 	g_return_val_if_fail (self, FALSE);
262 	g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
263 	g_return_val_if_fail (part, FALSE);
264 	g_return_val_if_fail (IS_PART (part), FALSE);
265 
266 	self->parts = g_list_remove (self->parts, part);
267 	self->items = g_list_remove (self->items, part);
268 
269 	num_pins = part_get_num_pins (part);
270 	item_data_get_pos (ITEM_DATA (part), &part_pos);
271 
272 	pins = part_get_pins (part);
273 	for (i = 0; i < num_pins; i++) {
274 		pin_pos.x = part_pos.x + pins[i].offset.x;
275 		pin_pos.y = part_pos.y + pins[i].offset.y;
276 
277 		node = g_hash_table_lookup (self->nodes, &pin_pos);
278 		if (node) {
279 			if (!node_remove_pin (node, &pins[i])) {
280 				g_warning ("Could not remove pin[%i] from node %p.", i, node);
281 				return FALSE;
282 			}
283 
284 			// If the node is empty after removing the pin,
285 			// remove the node as well.
286 			if (node_is_empty (node)) {
287 				g_hash_table_remove (self->nodes, &pin_pos);
288 				g_object_unref (G_OBJECT (node));
289 			}
290 		} else {
291 			return FALSE;
292 		}
293 	}
294 
295 	return TRUE;
296 }
297 
node_store_add_textbox(NodeStore * self,Textbox * text)298 gboolean node_store_add_textbox (NodeStore *self, Textbox *text)
299 {
300 	g_object_set (G_OBJECT (text), "store", self, NULL);
301 	self->textbox = g_list_prepend (self->textbox, text);
302 
303 	return TRUE;
304 }
305 
node_store_remove_textbox(NodeStore * self,Textbox * text)306 gboolean node_store_remove_textbox (NodeStore *self, Textbox *text)
307 {
308 	self->textbox = g_list_remove (self->textbox, text);
309 
310 	return TRUE;
311 }
312 
313 /**
314  * Remove all wires in the nodestore that overlap with a given
315  * wire (preserve the longest of the two overlapping wires).
316  * This is basically an optimization of the nodestore.
317  */
node_store_remove_overlapping_wires(NodeStore * store,Wire * wire)318 void node_store_remove_overlapping_wires (NodeStore *store, Wire *wire)
319 {
320 	gdouble a, b;
321 	Coords start_pos, end_pos, start_pos_other, end_pos_other;
322 	Coords length, length_other;
323 	Coords so, eo;
324 	GList *list, *to_be_removed = NULL;
325 	Node *sn, *en;
326 	Wire *other;
327 	gboolean overlap;
328 
329 	g_return_if_fail (store != NULL);
330 	g_return_if_fail (IS_NODE_STORE (store));
331 	g_return_if_fail (wire != NULL);
332 	g_return_if_fail (IS_WIRE (wire));
333 
334 	// Check for overlapping with other wires.
335 	for (list = store->wires; list && list->data != wire; list = list->next) {
336 		g_assert (list->data != NULL);
337 		g_assert (IS_WIRE (list->data));
338 		other = list->data;
339 		overlap = do_wires_overlap (wire, other, &so, &eo);
340 		NG_DEBUG ("overlap [ %p] and [ %p ] -- %s", wire, other,
341 		          overlap == TRUE ? "YES" : "NO");
342 		if (overlap) {
343 			sn = node_store_get_node (store, eo);
344 			en = node_store_get_node (store, so);
345 			if (!sn && !en)
346 				wire = vulcanize_wire (store, wire, other);
347 			wire_get_pos_and_length (wire, &start_pos, &length);
348 			wire_get_pos_and_length (other, &start_pos_other, &length_other);
349 			wire_get_start_and_end_pos (wire, &start_pos, &end_pos);
350 			wire_get_start_and_end_pos (other, &start_pos_other, &end_pos_other);
351 			if (length.x < 0) {
352 				a = start_pos.x;
353 				b = end_pos.x;
354 				start_pos.x = b;
355 				end_pos.x = a;
356 				length.x = -length.x;
357 			}
358 			if (length.y < 0) {
359 				a = start_pos.y;
360 				b = end_pos.y;
361 				start_pos.y = b;
362 				end_pos.y = a;
363 				length.y = -length.y;
364 			}
365 			if (length_other.x < 0) {
366 				a = start_pos_other.x;
367 				b = end_pos_other.x;
368 				start_pos_other.x = b;
369 				end_pos_other.x = a;
370 				length_other.x = -length_other.x;
371 			}
372 			if (length_other.y < 0) {
373 				a = start_pos_other.y;
374 				b = end_pos_other.y;
375 				start_pos_other.y = b;
376 				end_pos_other.y = a;
377 				length_other.y = -length_other.y;
378 			}
379 			if (length.x > 0 && length_other.x > 0 && length.y == 0 && length_other.y == 0) {
380 				if (start_pos.x >= start_pos_other.x && end_pos.x <= end_pos_other.x)
381 					to_be_removed = g_list_append (to_be_removed, wire);
382 				if (start_pos_other.x >= start_pos.x && end_pos_other.x <= end_pos.x)
383 					to_be_removed = g_list_append (to_be_removed, other);
384 			} else if (length.y > 0 && length_other.y > 0 && length.x == 0 && length_other.x == 0) {
385 				if (start_pos.y >= start_pos_other.y && end_pos.y <= end_pos_other.y)
386 					to_be_removed = g_list_append (to_be_removed, wire);
387 				if (start_pos_other.y >= start_pos.y && end_pos_other.y <= end_pos.y)
388 					to_be_removed = g_list_append (to_be_removed, other);
389 			}
390 		}
391 	}
392 
393 	// Remove all the wires that overlap with a given wire
394 	for (list = to_be_removed; list; list = list->next) {
395 		if (node_store_remove_wire (store, list->data))
396 			wire_delete (list->data);
397 	}
398 	g_list_free (to_be_removed);
399 }
400 
401 /**
402  * add/register the wire to the nodestore
403  *
404  * @param store
405  * @param wire
406  * @returns TRUE if the wire was added or merged, else FALSE
407  */
node_store_add_wire(NodeStore * store,Wire * wire)408 gboolean node_store_add_wire (NodeStore *store, Wire *wire)
409 {
410 	GList *list;
411 	Node *node;
412 	Coords where;
413 	int i = 0;
414 
415 	g_return_val_if_fail (store, FALSE);
416 	g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
417 	g_return_val_if_fail (wire, FALSE);
418 	g_return_val_if_fail (IS_WIRE (wire), FALSE);
419 
420 	// Check for intersection with other wires.
421 	for (list = store->wires; list; list = list->next) {
422 		g_assert (list->data != NULL);
423 		g_assert (IS_WIRE (list->data));
424 
425 		Wire *other = list->data;
426 		if (do_wires_intersect (wire, other, &where)) {
427 			if (is_t_crossing (wire, other, &where) || is_t_crossing (other, wire, &where)) {
428 
429 				node = node_store_get_or_create_node (store, where);
430 
431 				node_add_wire (node, wire);
432 				node_add_wire (node, other);
433 
434 				wire_add_node (wire, node);
435 				wire_add_node (other, node);
436 
437 				NG_DEBUG ("Add wire %p to wire %p @ %lf,%lf.\n", wire, other, where.x, where.y);
438 			} else {
439 				// magic node removal if a x crossing is overlapped with another wire
440 				node = node_store_get_node (store, where);
441 				NG_DEBUG ("Nuke that node [ %p ] at coords inbetween", node);
442 				if (node) {
443 					Coords c[4];
444 					wire_get_start_and_end_pos (other, c + 0, c + 1);
445 					wire_get_start_and_end_pos (wire, c + 2, c + 3);
446 					if (!coords_equal (&where, c + 0) && !coords_equal (&where, c + 1) &&
447 					    !coords_equal (&where, c + 2) && !coords_equal (&where, c + 3)) {
448 
449 						wire_remove_node (wire, node);
450 						wire_remove_node (other, node);
451 						node_remove_wire (node, wire);
452 						node_remove_wire (node, other);
453 					}
454 				}
455 			}
456 		}
457 	}
458 
459 	node_store_remove_overlapping_wires (store, wire);
460 
461 	// Check for intersection with parts (pins).
462 	for (list = store->parts; list; list = list->next) {
463 		g_assert (list->data != NULL);
464 		g_assert (IS_PART (list->data));
465 
466 		Coords part_pos;
467 		gint num_pins = -1;
468 		Part *part = list->data;
469 
470 		num_pins = part_get_num_pins (part);
471 		item_data_get_pos (ITEM_DATA (part), &part_pos);
472 
473 		// Go through all the parts and see which of their
474 		// pins that intersect the wire.
475 		for (i = 0; i < num_pins; i++) {
476 			Pin *pins;
477 			Coords lookup_pos;
478 
479 			pins = part_get_pins (part);
480 			lookup_pos.x = part_pos.x + pins[i].offset.x;
481 			lookup_pos.y = part_pos.y + pins[i].offset.y;
482 
483 			// If there is a wire at this pin's position,
484 			// add it to the return list.
485 			if (is_point_on_wire (wire, &lookup_pos)) {
486 				Node *node;
487 				node = node_store_get_node (store, lookup_pos);
488 
489 				if (node != NULL) {
490 					// Add the wire to the node (pin) that it intersected.
491 					node_add_wire (node, wire);
492 					wire_add_node (wire, node);
493 					NG_DEBUG ("Add wire %p to pin (node) %p.\n", wire, node);
494 				} else {
495 					g_warning ("Bug: Found no node at pin at (%g %g).\n", lookup_pos.x,
496 					           lookup_pos.y);
497 				}
498 			}
499 		}
500 	}
501 
502 	g_object_set (G_OBJECT (wire), "store", store, NULL);
503 	store->wires = g_list_prepend (store->wires, wire);
504 	store->items = g_list_prepend (store->items, wire);
505 
506 	return TRUE;
507 }
508 
509 /**
510  * removes/unregisters a wire from the nodestore
511  * this does _not_ free the wire itself!
512  */
node_store_remove_wire(NodeStore * store,Wire * wire)513 gboolean node_store_remove_wire (NodeStore *store, Wire *wire)
514 {
515 	GSList *copy, *iter;
516 	Coords lookup_key;
517 
518 	g_return_val_if_fail (store, FALSE);
519 	g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
520 	g_return_val_if_fail (wire, FALSE);
521 	g_return_val_if_fail (IS_WIRE (wire), FALSE);
522 
523 	if (item_data_get_store (ITEM_DATA (wire)) == NULL) {
524 		g_warning ("Trying to remove not-stored wire %p.", wire);
525 		return FALSE;
526 	}
527 
528 	store->wires = g_list_remove (store->wires, wire);
529 	store->items = g_list_remove (store->items, wire);
530 
531 	// If the nodes that this wire passes through will be
532 	// empty when the wire is removed, remove the node as well.
533 
534 	// FIXME if done properly, a list copy is _not_ necessary
535 	copy = g_slist_copy (wire_get_nodes (wire));
536 	for (iter = copy; iter; iter = iter->next) {
537 		Node *node = iter->data;
538 
539 		lookup_key = node->key;
540 
541 		node_remove_wire (node, wire);
542 		wire_remove_node (wire, node);
543 
544 		if (node_is_empty (node))
545 			g_hash_table_remove (store->nodes, &lookup_key);
546 	}
547 
548 	g_slist_free (copy);
549 
550 	return TRUE;
551 }
552 
553 /**
554  * check if there is at least one wire at the specified position
555  *
556  * @param store which NodeStore to check
557  * @param pos the position
558  * @returns TRUE or FALSE
559  */
node_store_is_wire_at_pos(NodeStore * store,Coords pos)560 gboolean node_store_is_wire_at_pos (NodeStore *store, Coords pos)
561 {
562 	GList *iter;
563 
564 	g_return_val_if_fail (store, FALSE);
565 	g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
566 
567 	for (iter = store->wires; iter; iter = iter->next) {
568 		Wire *wire = iter->data;
569 
570 		if (is_point_on_wire (wire, &pos))
571 			return TRUE;
572 	}
573 	return FALSE;
574 }
575 
576 /**
577  * lookup node at specified position
578  *
579  * @param store which store to check
580  * @param pos where to check in that store
581  * @returns the node pointer if there is a node, else NULL
582  */
node_store_get_node(NodeStore * store,Coords pos)583 Node *node_store_get_node (NodeStore *store, Coords pos)
584 {
585 	Node *node;
586 
587 	g_return_val_if_fail (store != NULL, NULL);
588 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
589 
590 	node = g_hash_table_lookup (store->nodes, &pos);
591 
592 	if (!node) {
593 		NG_DEBUG ("No node at (%g, %g)", pos.x, pos.y);
594 	} else {
595 		NG_DEBUG ("Found node at (%g, %g)", pos.x, pos.y);
596 	}
597 	return node;
598 }
599 
600 /**
601  * Call GHFunc for each node in the nodestore
602  */
node_store_node_foreach(NodeStore * store,GHFunc * func,gpointer user_data)603 void node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data)
604 {
605 	g_return_if_fail (store != NULL);
606 	g_return_if_fail (IS_NODE_STORE (store));
607 
608 	g_hash_table_foreach (store->nodes, (gpointer)func, user_data);
609 }
610 
611 /**
612  * [transfer-none]
613  */
node_store_get_parts(NodeStore * store)614 GList *node_store_get_parts (NodeStore *store)
615 {
616 	g_return_val_if_fail (store != NULL, NULL);
617 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
618 
619 	return store->parts;
620 }
621 
622 /**
623  * [transfer-none]
624  */
node_store_get_wires(NodeStore * store)625 GList *node_store_get_wires (NodeStore *store)
626 {
627 	g_return_val_if_fail (store != NULL, NULL);
628 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
629 
630 	return store->wires;
631 }
632 
633 /**
634  * [transfer-none]
635  */
node_store_get_items(NodeStore * store)636 GList *node_store_get_items (NodeStore *store)
637 {
638 	g_return_val_if_fail (store != NULL, NULL);
639 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
640 
641 	return store->items;
642 }
643 
644 /**
645  * the caller has to free the list himself, but not the actual data items!
646  */
node_store_get_node_positions(NodeStore * store)647 GList *node_store_get_node_positions (NodeStore *store)
648 {
649 	GList *result;
650 
651 	g_return_val_if_fail (store != NULL, NULL);
652 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
653 
654 	result = NULL;
655 	g_hash_table_foreach (store->nodes, (GHFunc)add_node_position, &result);
656 
657 	return result;
658 }
659 
660 /**
661  * the caller has to free the list himself, but not the actual data items!
662  */
node_store_get_nodes(NodeStore * store)663 GList *node_store_get_nodes (NodeStore *store)
664 {
665 	GList *result;
666 
667 	g_return_val_if_fail (store != NULL, NULL);
668 	g_return_val_if_fail (IS_NODE_STORE (store), NULL);
669 
670 	result = NULL;
671 	g_hash_table_foreach (store->nodes, (GHFunc)add_node, &result);
672 
673 	return result;
674 }
675 
node_store_get_bounds(NodeStore * store,NodeRect * rect)676 void node_store_get_bounds (NodeStore *store, NodeRect *rect)
677 {
678 	GList *list;
679 	Coords p1, p2;
680 
681 	g_return_if_fail (store != NULL);
682 	g_return_if_fail (IS_NODE_STORE (store));
683 	g_return_if_fail (rect != NULL);
684 
685 	rect->x0 = G_MAXDOUBLE;
686 	rect->y0 = G_MAXDOUBLE;
687 	rect->x1 = -G_MAXDOUBLE;
688 	rect->y1 = -G_MAXDOUBLE;
689 
690 	for (list = store->items; list; list = list->next) {
691 		item_data_get_absolute_bbox (ITEM_DATA (list->data), &p1, &p2);
692 
693 		rect->x0 = MIN (rect->x0, p1.x);
694 		rect->y0 = MIN (rect->y0, p1.y);
695 		rect->x1 = MAX (rect->x1, p2.x);
696 		rect->y1 = MAX (rect->y1, p2.y);
697 	}
698 }
699 
node_store_count_items(NodeStore * store,NodeRect * rect)700 gint node_store_count_items (NodeStore *store, NodeRect *rect)
701 {
702 	GList *list;
703 	Coords p1, p2;
704 	ItemData *data;
705 	gint n;
706 
707 	g_return_val_if_fail (store != NULL, 0);
708 	g_return_val_if_fail (IS_NODE_STORE (store), 0);
709 
710 	if (rect == NULL)
711 		return g_list_length (store->items);
712 
713 	for (list = store->items, n = 0; list; list = list->next) {
714 		data = ITEM_DATA (list->data);
715 		item_data_get_absolute_bbox (data, &p1, &p2);
716 		if (p1.x <= rect->x1 && p1.y <= rect->y1 && p2.x >= rect->x0 && p2.y >= rect->y0) {
717 			n++;
718 		}
719 	}
720 
721 	return n;
722 }
723 
draw_dot(Coords * pos,Node * value,cairo_t * cr)724 static void draw_dot (Coords *pos, Node *value, cairo_t *cr)
725 {
726 	if (node_needs_dot (value)) {
727 		cairo_save (cr);
728 		cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
729 		cairo_translate (cr, pos->x, pos->y);
730 		cairo_scale (cr, 1.0, 1.0);
731 		cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI);
732 		cairo_fill (cr);
733 		cairo_restore (cr);
734 	}
735 }
736 
node_store_print_items(NodeStore * store,cairo_t * cr,SchematicPrintContext * ctx)737 void node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ctx)
738 {
739 	GList *list;
740 	ItemData *data;
741 
742 	g_return_if_fail (store != NULL);
743 	g_return_if_fail (IS_NODE_STORE (store));
744 
745 	cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
746 	for (list = store->items; list; list = list->next) {
747 		data = ITEM_DATA (list->data);
748 		item_data_print (data, cr, ctx);
749 	}
750 
751 	g_hash_table_foreach (store->nodes, (GHFunc)draw_dot, cr);
752 }
753 
node_store_is_pin_at_pos(NodeStore * store,Coords pos)754 gboolean node_store_is_pin_at_pos (NodeStore *store, Coords pos)
755 {
756 	int num_pins;
757 	Coords part_pos;
758 	GList *p;
759 	Part *part;
760 	Pin *pins;
761 	int i;
762 	gdouble x, y;
763 
764 	for (p = store->parts; p; p = p->next) {
765 		part = PART (p->data);
766 
767 		num_pins = part_get_num_pins (part);
768 
769 		item_data_get_pos (ITEM_DATA (part), &part_pos);
770 
771 		pins = part_get_pins (part);
772 		for (i = 0; i < num_pins; i++) {
773 			x = part_pos.x + pins[i].offset.x;
774 			y = part_pos.y + pins[i].offset.y;
775 
776 			if (fabs (x - pos.x) < NODE_EPSILON && fabs (y - pos.y) < NODE_EPSILON)
777 				return TRUE;
778 		}
779 	}
780 	return FALSE;
781 }
782