1 /*
2  * node.c
3  *
4  *
5  * Author:
6  *  Richard Hult <rhult@hem.passagen.se>
7  *  Ricardo Markiewicz <rmarkie@fi.uba.ar>
8  *  Andres de Barbara <adebarbara@fi.uba.ar>
9  *  Marc Lorber <lorber.marc@wanadoo.fr>
10  *
11  * Web page: https://ahoi.io/project/oregano
12  *
13  * Copyright (C) 1999-2001  Richard Hult
14  * Copyright (C) 2003,2006  Ricardo Markiewicz
15  * Copyright (C) 2009-2012  Marc Lorber
16  *
17  * This program is free software; you can redistribute it and/or
18  * modify it under the terms of the GNU General Public License as
19  * published by the Free Software Foundation; either version 2 of the
20  * License, or (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public
28  * License along with this program; if not, write to the
29  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
30  * Boston, MA 02110-1301, USA.
31  */
32 
33 #include <math.h>
34 
35 #include "node.h"
36 #include "part.h"
37 
38 #include "debug.h"
39 
40 static void node_class_init (NodeClass *klass);
41 static void node_init (Node *node);
42 
43 enum { NODE_DOT_ADDED, NODE_DOT_REMOVED, VOLTAGE_CHANGED, LAST_SIGNAL };
44 
45 G_DEFINE_TYPE (Node, node, G_TYPE_OBJECT)
46 
47 static guint node_signals[LAST_SIGNAL] = {0};
48 
node_dispose(GObject * object)49 static void node_dispose (GObject *object) { G_OBJECT_CLASS (node_parent_class)->dispose (object); }
50 
node_finalize(GObject * object)51 static void node_finalize (GObject *object)
52 {
53 	g_return_if_fail (object != NULL);
54 
55 	// Remove the pins and wires encountered by the node.
56 	if (NODE (object)->pins) {
57 		g_slist_free (NODE (object)->pins);
58 	}
59 
60 	if (NODE (object)->wires) {
61 		g_slist_free (NODE (object)->wires);
62 	}
63 	G_OBJECT_CLASS (node_parent_class)->finalize (object);
64 }
65 
node_class_init(NodeClass * klass)66 static void node_class_init (NodeClass *klass)
67 {
68 	GObjectClass *object_class;
69 
70 	object_class = (GObjectClass *)klass;
71 	object_class->dispose = node_dispose;
72 	object_class->finalize = node_finalize;
73 	node_parent_class = g_type_class_peek_parent (klass);
74 
75 	node_signals[NODE_DOT_ADDED] =
76 	    g_signal_new ("node_dot_added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0,
77 	                  NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
78 
79 	node_signals[NODE_DOT_REMOVED] =
80 	    g_signal_new ("node_dot_removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0,
81 	                  NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
82 
83 	node_signals[VOLTAGE_CHANGED] =
84 	    g_signal_new ("voltage_changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0,
85 	                  NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
86 }
87 
node_init(Node * node)88 static void node_init (Node *node)
89 {
90 	node->pin_count = 0;
91 	node->wire_count = 0;
92 	node->pins = NULL;
93 	node->wires = NULL;
94 	node->visited = FALSE;
95 }
96 
node_new(Coords pos)97 Node *node_new (Coords pos)
98 {
99 	Node *node;
100 
101 	node = NODE (g_object_new (node_get_type (), NULL));
102 
103 	node->key = pos;
104 
105 	return node;
106 }
107 
108 #define SEP(p1, p2) ((fabs (p1.x - p2.x) < 1e-3) && (fabs (p1.y - p2.y) < 1e-3))
109 #define ON_THE_WIRE(p1, start, end)                                                                \
110 	(fabs ((end.y - start.y) * (p1.x - start.x) - (end.x - start.x) * (p1.y - start.y)) < 1.e-5)
node_needs_dot(Node * node)111 gboolean node_needs_dot (Node *node)
112 {
113 	Wire *wire1, *wire2;
114 	Coords start_pos1, length1, end_pos1;
115 	Coords start_pos2, length2, end_pos2;
116 
117 	NG_DEBUG ("node: %p --- pins: %i --- wires: %i", node, node->pin_count, node->wire_count);
118 
119 	// always display a black dot if a part hits a wire
120 	if (node->pin_count > 0 && node->wire_count > 0) {
121 		NG_DEBUG ("  TRUE (pins>0 && wires>0)");
122 		return TRUE;
123 	} else if (node->pin_count > 1 || node->wire_count > 2) {
124 		NG_DEBUG ("  TRUE (pins>1 || wires>2)");
125 		return TRUE;
126 	} else if (node->wire_count == 2) {
127 		// Check that we don't have two wire endpoints.
128 		wire1 = node->wires->data;
129 		wire2 = node->wires->next->data;
130 
131 		wire_get_pos_and_length (wire1, &start_pos1, &length1);
132 		wire_get_pos_and_length (wire2, &start_pos2, &length2);
133 
134 		end_pos1.x = start_pos1.x + length1.x;
135 		end_pos1.y = start_pos1.y + length1.y;
136 		end_pos2.x = start_pos2.x + length2.x;
137 		end_pos2.y = start_pos2.y + length2.y;
138 
139 		if (!(SEP (start_pos1, start_pos2) || SEP (start_pos1, end_pos2) ||
140 		      SEP (end_pos1, end_pos2) || SEP (end_pos1, start_pos2))) {
141 			return TRUE;
142 		}
143 		return FALSE;
144 	}
145 	NG_DEBUG (" FALSE (else)");
146 	return FALSE;
147 }
148 
node_add_pin(Node * node,Pin * pin)149 gboolean node_add_pin (Node *node, Pin *pin)
150 {
151 	gboolean dot;
152 
153 	g_return_val_if_fail (node != NULL, FALSE);
154 	g_return_val_if_fail (IS_NODE (node), FALSE);
155 	g_return_val_if_fail (pin != NULL, FALSE);
156 
157 	if (g_slist_find (node->pins, pin)) {
158 		NG_DEBUG ("node_add_pin: pin already there.\n");
159 		return FALSE;
160 	}
161 
162 	dot = node_needs_dot (node);
163 
164 	node->pins = g_slist_prepend (node->pins, pin);
165 	node->pin_count++;
166 	if (!dot && node_needs_dot (node))
167 		g_signal_emit_by_name (G_OBJECT (node), "node_dot_added", &node->key);
168 
169 	return TRUE;
170 }
171 
node_remove_pin(Node * node,Pin * pin)172 gboolean node_remove_pin (Node *node, Pin *pin)
173 {
174 	gboolean dot;
175 
176 	g_return_val_if_fail (node != NULL, FALSE);
177 	g_return_val_if_fail (IS_NODE (node), FALSE);
178 	g_return_val_if_fail (pin != NULL, FALSE);
179 
180 	if (node->pin_count == 0)
181 		return FALSE;
182 
183 	dot = node_needs_dot (node);
184 
185 	node->pins = g_slist_remove (node->pins, pin);
186 	node->pin_count--;
187 
188 	if (dot && !node_needs_dot (node))
189 		g_signal_emit_by_name (G_OBJECT (node), "node_dot_removed", &node->key);
190 
191 	return TRUE;
192 }
193 
node_add_wire(Node * node,Wire * wire)194 gboolean node_add_wire (Node *node, Wire *wire)
195 {
196 	gboolean dot;
197 
198 	g_return_val_if_fail (node != NULL, FALSE);
199 	g_return_val_if_fail (IS_NODE (node), FALSE);
200 	g_return_val_if_fail (wire != NULL, FALSE);
201 	g_return_val_if_fail (IS_WIRE (wire), FALSE);
202 
203 	if (g_slist_find (node->wires, wire)) {
204 		NG_DEBUG ("node_add_wire: wire already there.\n");
205 		return FALSE;
206 	}
207 
208 	dot = node_needs_dot (node);
209 
210 	node->wires = g_slist_prepend (node->wires, wire);
211 	node->wire_count++;
212 
213 	if (!dot && node_needs_dot (node))
214 		g_signal_emit_by_name (G_OBJECT (node), "node_dot_added", &node->key);
215 
216 	return TRUE;
217 }
218 
node_remove_wire(Node * node,Wire * wire)219 gboolean node_remove_wire (Node *node, Wire *wire)
220 {
221 	gboolean dot;
222 
223 	g_return_val_if_fail (node != NULL, FALSE);
224 	g_return_val_if_fail (IS_NODE (node), FALSE);
225 	g_return_val_if_fail (wire != NULL, FALSE);
226 	g_return_val_if_fail (IS_WIRE (wire), FALSE);
227 
228 	if (node->wire_count == 0)
229 		return FALSE;
230 
231 	if (!g_slist_find (node->wires, wire)) {
232 		NG_DEBUG ("node_remove_wire: not there.\n");
233 		return FALSE;
234 	}
235 
236 	dot = node_needs_dot (node);
237 
238 	node->wires = g_slist_remove (node->wires, wire);
239 	node->wire_count--;
240 
241 	if (dot && (!node_needs_dot (node)))
242 		g_signal_emit_by_name (G_OBJECT (node), "node_dot_removed", &node->key);
243 
244 	return TRUE;
245 }
246 
node_is_empty(Node * node)247 gboolean node_is_empty (Node *node)
248 {
249 	g_return_val_if_fail (node != NULL, FALSE);
250 	g_return_val_if_fail (IS_NODE (node), FALSE);
251 
252 	if ((node->wire_count == 0) && (node->pin_count == 0))
253 		return TRUE;
254 
255 	return FALSE;
256 }
257 
node_is_visited(Node * node)258 gboolean node_is_visited (Node *node)
259 {
260 	g_return_val_if_fail (node != NULL, FALSE);
261 	g_return_val_if_fail (IS_NODE (node), FALSE);
262 
263 	return node->visited;
264 }
265 
node_set_visited(Node * node,gboolean is_visited)266 void node_set_visited (Node *node, gboolean is_visited)
267 {
268 	g_return_if_fail (node != NULL);
269 	g_return_if_fail (IS_NODE (node));
270 
271 	node->visited = is_visited;
272 }
273 
274 #define HASH_EPSILON 1e-3
275 
276 /**
277  * Hash function to be used with a GHashTable (and others)
278  */
node_hash(gconstpointer key)279 guint node_hash (gconstpointer key)
280 {
281 	Coords *sp = (Coords *)key;
282 	register int x, y;
283 	const int shift = sizeof(int) * 8 / 2;
284 
285 	// Hash on any other node?
286 
287 	x = (int)rint (sp->x) % 1 << shift;
288 	y = (int)rint (sp->y) % 1 << shift;
289 
290 	return (y << shift) | x;
291 }
292 
293 /**
294  * Comparsion function to be used with a GHashTable (and others)
295  */
node_equal(gconstpointer a,gconstpointer b)296 gboolean node_equal (gconstpointer a, gconstpointer b)
297 {
298 	const Coords *ca = a;
299 	const Coords *cb = b;
300 
301 	if (fabs (ca->y - cb->y) > HASH_EPSILON)
302 		return 0;
303 
304 	if (fabs (ca->x - cb->x) > HASH_EPSILON)
305 		return 0;
306 
307 	return 1;
308 }
309