summaryrefslogtreecommitdiff
path: root/src/model/node-store.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/model/node-store.c')
-rw-r--r--src/model/node-store.c1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/src/model/node-store.c b/src/model/node-store.c
new file mode 100644
index 0000000..2154b8b
--- /dev/null
+++ b/src/model/node-store.c
@@ -0,0 +1,1227 @@
+/*
+ * node-store.c
+ *
+ * This is where the circuit elements (pins/wires) are stored.
+ *
+ * Authors:
+ * Richard Hult <rhult@hem.passagen.se>
+ * Ricardo Markiewicz <rmarkie@fi.uba.ar>
+ * Andres de Barbara <adebarbara@fi.uba.ar>
+ *
+ * Web page: http://arrakis.lug.fi.uba.ar/
+ *
+ * Copyright (C) 1999-2001 Richard Hult
+ * Copyright (C) 2003,2006 Ricardo Markiewicz
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <math.h>
+#include <glib.h>
+#include <glib-object.h>
+#include "node-store.h"
+#include "node.h"
+#include "part.h"
+#include "wire.h"
+#include "wire-private.h"
+#include "item-data.h"
+
+/*
+ * NODE_EPSILON is used to check for intersection.
+ * HASH_EPSILON is used in the hash equality check function.
+ */
+#define NODE_EPSILON 1e-10
+#define HASH_EPSILON 1e-3
+
+/* Share an endpoint? */
+#define SEP(p1x,p1y,p2x,p2y) (IS_EQ(p1x, p2x) && IS_EQ(p1y, p2y))
+
+/* Equals? */
+#define IS_EQ(a,b) (fabs ((a) - (b)) < NODE_EPSILON)
+
+#define ON_THE_WIRE(p1,start,end) ( fabs((end.y-start.y)*(p1.x-start.x)-(end.x-start.x)*(p1.y-start.y))<NODE_EPSILON )
+
+static void node_store_class_init (NodeStoreClass *klass);
+static void node_store_init (NodeStore *store);
+static guint node_hash (gconstpointer key);
+static int node_equal (gconstpointer a, gconstpointer b);
+static GSList *wires_intersect (NodeStore *store, double x1, double y1,
+ double x2, double y2);
+static GSList *wire_intersect_parts (NodeStore *store, Wire *wire);
+static int is_wire_at_pos (double x1, double y1, double x2, double y2,
+ SheetPos pos);
+static GSList *wires_at_pos (NodeStore *store, SheetPos pos);
+static int do_wires_intersect (double Ax, double Ay, double Bx, double By,
+ double Cx, double Cy, double Dx, double Dy, SheetPos *pos);
+static void node_store_finalize(GObject *self);
+static void node_store_dispose(GObject *self);
+
+typedef struct {
+ Wire *wire;
+ SheetPos pos;
+} IntersectionPoint;
+
+enum {
+ DOT_ADDED,
+ DOT_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint node_store_signals [LAST_SIGNAL] = { 0 };
+static GObjectClass *parent_class = NULL;
+
+GType
+node_store_get_type (void)
+{
+ static GType node_store_type = 0;
+
+ if (!node_store_type) {
+ static const GTypeInfo node_store_info = {
+ sizeof (NodeStoreClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) node_store_class_init,
+ NULL,
+ NULL,
+ sizeof (NodeStore),
+ 0,
+ (GInstanceInitFunc) node_store_init,
+ NULL
+ };
+
+ node_store_type = g_type_register_static (G_TYPE_OBJECT, "NodeStore",
+ &node_store_info, 0);
+ }
+
+ return node_store_type;
+}
+
+static void
+node_store_finalize(GObject *object)
+{
+ NodeStore *self = NODE_STORE(object);
+
+ if (self->nodes) {
+ g_hash_table_destroy (self->nodes);
+ self->nodes = NULL;
+ }
+
+ if (self->wires) {
+ g_list_free (self->wires);
+ self->wires = NULL;
+ }
+ if (self->parts) {
+ g_list_free (self->parts);
+ self->parts = NULL;
+ }
+ if (self->items) {
+ g_list_free (self->items);
+ self->items = NULL;
+ }
+
+ parent_class->finalize(object);
+}
+
+static void
+node_store_dispose(GObject *self)
+{
+ parent_class->dispose(self);
+}
+
+static void
+node_store_class_init (NodeStoreClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS(klass);
+ parent_class = g_type_class_peek_parent(klass);
+
+ gobject_class->finalize = node_store_finalize;
+ gobject_class->dispose = node_store_dispose;
+
+ node_store_signals [DOT_ADDED] =
+ g_signal_new ("dot_added",
+ G_TYPE_FROM_CLASS(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NodeStoreClass, dot_added),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+
+ node_store_signals [DOT_REMOVED] =
+ g_signal_new ("dot_removed",
+ TYPE_NODE_STORE,
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NodeStoreClass, dot_removed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE,
+ 1, G_TYPE_POINTER);
+}
+
+static void
+node_store_init (NodeStore *self)
+{
+ self->nodes = g_hash_table_new (node_hash, node_equal);
+ self->wires = NULL;
+ self->parts = NULL;
+ self->items = NULL;
+ self->textbox = NULL;
+}
+
+NodeStore *
+node_store_new (void)
+{
+ return NODE_STORE(g_object_new (TYPE_NODE_STORE, NULL));
+}
+
+static void
+dot_added_callback (Node *node, SheetPos *pos, NodeStore *store)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_signal_emit_by_name(G_OBJECT(store), "dot_added", pos);
+}
+
+static void
+dot_removed_callback (Node *node, SheetPos *pos, NodeStore *store)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_signal_emit_by_name(G_OBJECT(store), "dot_removed", pos);
+}
+
+Node *
+node_store_get_or_create_node (NodeStore *self, SheetPos pos)
+{
+ Node *node;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (self), NULL);
+
+ node = g_hash_table_lookup (self->nodes, &pos);
+
+ if (!node) {
+ /*
+ * Create a node at (x, y) and return it.
+ */
+ node = node_new (pos);
+
+ g_signal_connect_object(
+ G_OBJECT (node),
+ "dot_added",
+ G_CALLBACK (dot_added_callback),
+ G_OBJECT (self),
+ 0);
+
+ g_signal_connect_object (
+ G_OBJECT (node),
+ "dot_removed",
+ G_CALLBACK (dot_removed_callback),
+ G_OBJECT (self),
+ 0);
+
+ g_hash_table_insert (self->nodes, &node->key, node);
+ }
+
+ /*
+ * If there was a previously stored node here, just
+ * return that node.
+ */
+ return node;
+}
+
+int
+node_store_add_part (NodeStore *self, Part *part)
+{
+ GSList *wire_list, *list;
+ Node *node;
+ SheetPos lookup_key;
+ SheetPos part_pos;
+ gdouble x, y;
+ int i, num_pins;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ num_pins = part_get_num_pins (part);
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ for (i = 0; i < num_pins; i++) {
+ Pin *pins;
+
+ pins = part_get_pins (part);
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ /*
+ * Use the position of the pin as hash key.
+ */
+ lookup_key.x = x;
+ lookup_key.y = y;
+
+ /*
+ * Retrieve a node for this position.
+ */
+ node = node_store_get_or_create_node (self, lookup_key);
+
+ /*
+ * Add all the wires that intersect this pin to the node store.
+ */
+ wire_list = wires_at_pos (self, lookup_key);
+
+ for (list = wire_list; list; list = list->next) {
+ Wire *wire = list->data;
+
+ /* g_print ("Add pin to wire.\n"); */
+
+ node_add_wire (node, wire);
+ wire_add_node (wire, node);
+ }
+
+ g_slist_free (wire_list);
+
+ node_add_pin (node, &pins[i]);
+ }
+
+ g_object_set (G_OBJECT (part), "store", self, NULL);
+ self->parts = g_list_prepend (self->parts, part);
+ self->items = g_list_prepend (self->items, part);
+
+ return TRUE;
+}
+
+int
+node_store_remove_part (NodeStore *self, Part *part)
+{
+ Node *node;
+ SheetPos lookup_key;
+ SheetPos pos;
+ gdouble x, y;
+ int i, num_pins;
+ Pin *pins;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (self), FALSE);
+ g_return_val_if_fail (part != NULL, FALSE);
+ g_return_val_if_fail (IS_PART (part), FALSE);
+
+ self->parts = g_list_remove (self->parts, part);
+ self->items = g_list_remove (self->items, part);
+
+ num_pins = part_get_num_pins (part);
+ item_data_get_pos (ITEM_DATA (part), &pos);
+
+ pins = part_get_pins (part);
+ for (i = 0; i < num_pins; i++) {
+ x = pos.x + pins[i].offset.x;
+ y = pos.y + pins[i].offset.y;
+
+ /*
+ * Use the position of the pin as lookup key.
+ */
+ lookup_key.x = x;
+ lookup_key.y = y;
+
+ node = g_hash_table_lookup (self->nodes, &lookup_key);
+ if (node) {
+ if (!node_remove_pin (node, &pins[i])) {
+ g_warning ("Couldn't remove pin.");
+ return FALSE;
+ }
+
+ /*
+ * If the node is empty after removing the pin,
+ * remove the node as well.
+ */
+ if (node_is_empty (node)) {
+ g_hash_table_remove (self->nodes, &lookup_key);
+ g_object_unref(G_OBJECT(node));
+ }
+ } else {
+ /* FIXME: Fix this or just silently return if the part is
+ non-existant? */
+ g_warning ("No node to remove part from.");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+int
+node_store_add_textbox (NodeStore *self, Textbox *text)
+{
+ g_object_set (G_OBJECT (text), "store", self, NULL);
+ self->textbox = g_list_prepend (self->textbox, text);
+
+ return TRUE;
+}
+
+int
+node_store_remove_textbox (NodeStore *self, Textbox *text)
+{
+ self->textbox = g_list_remove (self->textbox, text);
+
+ return TRUE;
+}
+
+int
+node_store_add_wire (NodeStore *store, Wire *wire)
+{
+ gdouble x1, y1, x2, y2;
+ GSList *ip_list, *list;
+ IntersectionPoint *ipoint;
+ Node *node;
+ WirePriv *priv;
+ SheetPos pos, length;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+/* if (wire_get_store (wire) != NULL) {
+ g_warning ("Trying to add already stored wire.");
+ return FALSE;
+ }
+*/
+
+/* g_print ("ADD WIRE\n");*/
+
+ priv = wire->priv;
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ x1 = pos.x;
+ y1 = pos.y;
+ x2 = x1 + length.x;
+ y2 = y1 + length.y;
+
+ /*
+ * Check for intersection with other wires.
+ */
+ ip_list = wires_intersect (store, x1, y1, x2, y2);
+
+ for (list = ip_list; list; list = list->next) {
+ ipoint = list->data;
+
+
+ //g_print ("(%g %g) (%g %g), ip (%g %g)\n", x1, y1, x2, y2, ipoint->pos.x, ipoint->pos.y);
+
+
+ if (IS_EQ (x1, x2) && ((ipoint->pos.y == y1) || (ipoint->pos.y == y2))) {
+ SheetPos w_pos, w_length;
+ gboolean can_join;
+ GSList *nodes;
+
+ wire_get_pos_and_length (ipoint->wire, &w_pos, &w_length);
+ gdouble _x1, _x2, _y1, _y2;
+
+ _x1 = w_pos.x;
+ _y1 = w_pos.y;
+ _x2 = _x1 + w_length.x;
+ _y2 = _y1 + w_length.y;
+
+ can_join = TRUE;
+ nodes = wire_get_nodes (wire);
+ for (; nodes; nodes = nodes->next) {
+ SheetPos p1;
+ Node *node = (Node *)nodes->data;
+
+ p1.x = _x1;
+ p1.y = _y1;
+ if ((node->key.x == p1.x) && (node->key.y == p1.y)) {
+ can_join = FALSE;
+ break;
+ }
+ p1.x = _x2;
+ p1.y = _y2;
+ if ((node->key.x == p1.x) && (node->key.y == p1.y)) {
+ can_join = FALSE;
+ break;
+ }
+ }
+
+ if (IS_EQ(_x1, _x2) && can_join) {
+ if (w_pos.x < pos.x) pos.x = w_pos.x;
+ if (w_pos.y < pos.y) pos.y = w_pos.y;
+ length.x += w_length.x;
+ length.y += w_length.y;
+
+ /* Update the new size and pos of the wire */
+ item_data_unregister (ITEM_DATA (ipoint->wire));
+ wire_set_length (ipoint->wire, &length);
+ item_data_set_pos (ITEM_DATA (ipoint->wire), &pos);
+ wire_update_bbox (ipoint->wire);
+ item_data_register (ITEM_DATA (ipoint->wire));
+
+ /* Done!, return -1 so wire si deleted */
+ return -1;
+ }
+ } else if (IS_EQ (y1, y2) && ((ipoint->pos.x == x1) || (ipoint->pos.x == x2))) {
+ SheetPos w_pos, w_length;
+ gboolean can_join;
+ GSList *nodes;
+
+ wire_get_pos_and_length (ipoint->wire, &w_pos, &w_length);
+ gdouble _x1, _x2, _y1, _y2;
+
+ _x1 = w_pos.x;
+ _y1 = w_pos.y;
+ _x2 = _x1 + w_length.x;
+ _y2 = _y1 + w_length.y;
+
+ can_join = TRUE;
+ nodes = wire_get_nodes (wire);
+ for (; nodes; nodes = nodes->next) {
+ SheetPos p;
+ Node *node = (Node *)nodes->data;
+
+ p.x = _x1;
+ p.y = _y1;
+ if ((node->key.x == p.x) && (node->key.y == p.y)) {
+ can_join = FALSE;
+ break;
+ }
+ p.x = _x2;
+ p.y = _y2;
+ if ((node->key.x == p.x) && (node->key.y == p.y)) {
+ can_join = FALSE;
+ break;
+ }
+ }
+
+ if (IS_EQ(_y1, _y2) && can_join) {
+ if (w_pos.x < pos.x) pos.x = w_pos.x;
+ if (w_pos.y < pos.y) pos.y = w_pos.y;
+ length.x += w_length.x;
+ length.y += w_length.y;
+
+ /* Update the new size and pos of the wire */
+ item_data_unregister (ITEM_DATA (ipoint->wire));
+ wire_set_length (ipoint->wire, &length);
+ item_data_set_pos (ITEM_DATA (ipoint->wire), &pos);
+ wire_update_bbox (ipoint->wire);
+ item_data_register (ITEM_DATA (ipoint->wire));
+
+ /* Done!, return -1 so wire si deleted */
+ return -1;
+ }
+ }
+
+ node = node_store_get_or_create_node (store, ipoint->pos);
+
+ /*
+ * Add the wire, and also the wire that is intersected.
+ */
+ node_add_wire (node, wire);
+ node_add_wire (node, ipoint->wire);
+
+ wire_add_node (wire, node);
+ wire_add_node (ipoint->wire, node);
+
+/* g_print ("Add wire to wire.\n");*/
+
+ g_free (ipoint);
+ }
+ g_slist_free (ip_list);
+
+ /*
+ * Check for intersection with parts (pins).
+ */
+ ip_list = wire_intersect_parts (store, wire);
+
+ for (list = ip_list; list; list = list->next) {
+ node = list->data;
+
+ /*
+ * Add the wire to the node (pin) that it intersected.
+ */
+ node_add_wire (node, wire);
+ wire_add_node (wire, node);
+
+/* g_print ("Add wire to pin.\n");*/
+ }
+
+ g_slist_free (ip_list);
+
+ g_object_set (G_OBJECT (wire), "store", store, NULL);
+ store->wires = g_list_prepend (store->wires, wire);
+ store->items = g_list_prepend (store->items, wire);
+
+ return TRUE;
+}
+
+int
+node_store_remove_wire (NodeStore *store, Wire *wire)
+{
+ gdouble x1, y1, x2, y2;
+ GSList *list;
+ SheetPos lookup_key, pos, length;
+ WirePriv *priv;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ priv = wire->priv;
+
+ if (item_data_get_store (ITEM_DATA (wire)) == NULL) {
+ g_warning ("Trying to remove non-stored wire.");
+ return FALSE;
+ }
+
+ wire_get_pos_and_length (wire, &pos, &length);
+
+ x1 = pos.x;
+ y1 = pos.y;
+ x2 = x1 + length.x;
+ y2 = y1 + length.y;
+
+ store->wires = g_list_remove (store->wires, wire);
+ store->items = g_list_remove (store->items, wire);
+
+ /*
+ * If the nodes that this wire passes through will be
+ * empty when the wire is removed, remove the node as well.
+ */
+
+ /*
+ * We must work on a copy of the nodes list, since it
+ * changes as we remove nodes.
+ */
+ list = g_slist_copy (wire_get_nodes (wire));
+
+ for (; list; list = list->next) {
+ Node *node = list->data;
+
+ lookup_key = node->key;
+
+ node_remove_wire (node, wire);
+
+ wire_remove_node (wire, node);
+
+ if (node_is_empty (node))
+ g_hash_table_remove (store->nodes, &lookup_key);
+ }
+
+ g_slist_free (list);
+
+ return TRUE;
+}
+
+static GSList *
+wire_intersect_parts (NodeStore *store, Wire *wire)
+{
+ GList *list;
+ GSList *ip_list;
+ Node *node;
+ SheetPos lookup_pos;
+ SheetPos part_pos, wire_pos, wire_length;
+ Part *part;
+ double x, y, wire_x1, wire_y1, wire_x2, wire_y2;
+ int i, num_pins;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+ g_return_val_if_fail (wire != NULL, FALSE);
+ g_return_val_if_fail (IS_WIRE (wire), FALSE);
+
+ ip_list = NULL;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+
+ wire_x1 = wire_pos.x;
+ wire_x2 = wire_pos.x + wire_length.x;
+ wire_y1 = wire_pos.y;
+ wire_y2 = wire_pos.y + wire_length.y;
+
+ /*
+ * Go through all the parts and see which of their
+ * pins that intersect the wire.
+ */
+ for (list = store->parts; list; list = list->next) {
+ part = list->data;
+
+ num_pins = part_get_num_pins (part);
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ for (i = 0; i < num_pins; i++) {
+ Pin *pins;
+
+ pins = part_get_pins (part);
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ lookup_pos.x = x;
+ lookup_pos.y = y;
+
+ /*
+ * If there is a wire at this pin's position,
+ * add it to the return list.
+ */
+ if (is_wire_at_pos (wire_x1, wire_y1, wire_x2, wire_y2, lookup_pos)) {
+ node = node_store_get_node (store, lookup_pos);
+
+ if (node == NULL)
+ g_warning ("Bug: Found no node at pin at (%g %g).\n", x, y);
+ else
+ ip_list = g_slist_prepend (ip_list, node);
+ }
+ }
+ }
+
+ return ip_list;
+}
+
+static GSList *
+wires_at_pos (NodeStore *store, SheetPos pos)
+{
+ GList *list;
+ GSList *wire_list;
+ Wire *wire;
+ SheetPos wire_pos, wire_length;
+ double x1, y1, x2, y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ wire_list = NULL;
+
+ for (list = store->wires; list; list = list->next) {
+ wire = list->data;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+ x1 = wire_pos.x;
+ y1 = wire_pos.y;
+ x2 = x1 + wire_length.x;
+ y2 = y1 + wire_length.y;
+
+ if (is_wire_at_pos (x1, y1, x2, y2, pos))
+ wire_list = g_slist_prepend (wire_list, wire);
+ }
+
+ return wire_list;
+}
+
+int
+node_store_is_wire_at_pos (NodeStore *store, SheetPos pos)
+{
+ GList *list;
+ Wire *wire;
+ SheetPos wire_pos, wire_length;
+ double x1, y1, x2, y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ for (list = store->wires; list; list = list->next) {
+ wire = list->data;
+
+ wire_get_pos_and_length (wire, &wire_pos, &wire_length);
+ x1 = wire_pos.x;
+ y1 = wire_pos.y;
+ x2 = x1 + wire_length.x;
+ y2 = y1 + wire_length.y;
+
+ if (is_wire_at_pos (x1, y1, x2, y2, pos))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GSList *
+wires_intersect (NodeStore *store, double x1, double y1, double x2, double y2)
+{
+ GList *list;
+ GSList *ip_list;
+ Wire *wire2;
+ SheetPos pos, wire2_pos, wire2_length;
+ SheetPos wire1_start_pos, wire1_end_pos;
+ double wire2_x1, wire2_y1, wire2_x2, wire2_y2;
+
+ g_return_val_if_fail (store != NULL, FALSE);
+ g_return_val_if_fail (IS_NODE_STORE (store), FALSE);
+
+ wire1_start_pos.x = x1;
+ wire1_start_pos.y = y1;
+ wire1_end_pos.x = x2;
+ wire1_end_pos.y = y2;
+
+ /*
+ * Search through all the wires. Is there a better way?
+ */
+ ip_list = NULL;
+ for (list = store->wires; list; list = list->next) {
+ wire2 = list->data;
+
+ wire_get_pos_and_length (wire2, &wire2_pos, &wire2_length);
+ wire2_x1 = wire2_pos.x;
+ wire2_y1 = wire2_pos.y;
+ wire2_x2 = wire2_x1 + wire2_length.x;
+ wire2_y2 = wire2_y1 + wire2_length.y;
+
+ if (do_wires_intersect (x1, y1, x2, y2, wire2_x1, wire2_y1,
+ wire2_x2, wire2_y2, &pos)) {
+ IntersectionPoint *ip;
+
+ ip = g_new0 (IntersectionPoint, 1);
+
+ ip->wire = wire2;
+ ip->pos = pos;
+ ip_list = g_slist_prepend (ip_list, ip);
+ }
+ }
+
+ return ip_list;
+}
+
+/*
+ * node_store_get_node
+ *
+ * Find the node that has an element at a certain position.
+ */
+Node *
+node_store_get_node (NodeStore *store, SheetPos pos)
+{
+ Node *node;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ node = g_hash_table_lookup (store->nodes, &pos);
+
+/* if (!node)
+ g_print ("No node at (%g, %g)\n", pos.x, pos.y);
+ else
+ g_print ("Found node at (%g, %g)\n", pos.x, pos.y);
+*/
+ return node;
+}
+
+static guint
+node_hash (gconstpointer key)
+{
+ SheetPos *sp = (SheetPos *) key;
+ int x, y;
+
+ /* hasha på varannan bit? */
+ /* Can anybody translate this? */
+
+ x = (int)rint (sp->x) % 256;
+ y = (int)rint (sp->y) % 256;
+
+ return (y << 8) | x;
+}
+
+static int
+node_equal (gconstpointer a, gconstpointer b)
+{
+ SheetPos *spa, *spb;
+
+ spa = (SheetPos *) a;
+ spb = (SheetPos *) b;
+
+ if (fabs (spa->y - spb->y) > HASH_EPSILON) {
+/* if (fabs (spa->y - spb->y) < 2.0)
+ g_print ("y mellan EPSILON och 2.0!\n");
+*/
+ return 0;
+ }
+
+ if (fabs (spa->x - spb->x) > HASH_EPSILON) {
+/* if (fabs (spa->x - spb->x) < 5.0)
+ g_print ("x mellan EPSILON och 2.0!\n");
+*/
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+node_store_node_foreach (NodeStore *store, GHFunc *func, gpointer user_data)
+{
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ g_hash_table_foreach (store->nodes, (gpointer)func, user_data);
+}
+
+static int
+is_wire_at_pos (double x1, double y1, double x2, double y2, SheetPos pos)
+{
+ double k, x0, y0;
+
+ x0 = pos.x;
+ y0 = pos.y;
+
+ if (!IS_EQ (x1, x2) && !IS_EQ (y1, y2)) {
+ k = ((y2 - y1)) / ((x2 - x1));
+ if (IS_EQ (y0, (y1 + k * (x0 - x1)))) {
+ return TRUE;
+ }
+ } else if (IS_EQ (x2, x1) && IS_EQ (x1, x0)) {
+ if (y0 >= y1 && y0 <= y2) {
+ return TRUE;
+ }
+ } else if (IS_EQ (y2, y1) && IS_EQ (y1, y0)) {
+ if (x0 >= x1 && x0 <= x2) {
+ return TRUE;
+ }
+ }
+
+// g_print ("no match: (%g %g) -> (%g %g), (%g %g)\n", x1, y1, x2, y2, pos.x, pos.y);
+
+ return FALSE;
+}
+
+/*
+ * Decides if two wires intersect. Note that wires that share an
+ * endpoint are considered intersecting each other. Intersection point
+ * is returned in pos.
+ */
+static int
+do_wires_intersect (double Ax, double Ay, double Bx, double By, double Cx,
+ double Cy, double Dx, double Dy, SheetPos *pos)
+{
+ double r, s, d;
+
+ /*
+ * Wires don't intersect if they share an endpoint. NOTE: they do here...
+ */
+ if (SEP (Ax, Ay, Cx, Cy)) { /* same starting point */
+ pos->x = Ax;
+ pos->y = Ay;
+ return TRUE;
+ } else if (SEP (Ax, Ay, Dx, Dy)) { /* 1st start == 2nd end */
+ pos->x = Ax;
+ pos->y = Ay;
+ return TRUE;
+ } else if (SEP (Bx, By, Cx, Cy)) { /* 1st end == 2nd start */
+ pos->x = Bx;
+ pos->y = By;
+ return TRUE;
+ } else if (SEP (Bx, By, Dx, Dy)) { /* 1st end == 2nd end */
+ pos->x = Bx;
+ pos->y = By;
+ return TRUE;
+ }
+
+ /*
+ * Calculate the denominator.
+ */
+ d = ((Bx - Ax) * (Dy - Cy) - (By - Ay) * (Dx - Cx));
+
+ /*
+ * We have two parallell wires if d = 0.
+ */
+ if (fabs (d) < NODE_EPSILON) {
+ return FALSE;
+ }
+
+ r = ((Ay - Cy) * (Dx - Cx) - (Ax - Cx) * (Dy - Cy));
+
+ /*
+ * Colinear wires, if r = 0. FIXME: check for intersection?
+ * not needed since we already checked for starts and ends
+ */
+ if (fabs (r) < NODE_EPSILON) {
+
+ }
+
+ r = r / d;
+ s = ((Ay - Cy) * (Bx - Ax) - (Ax - Cx) * (By - Ay)) / d;
+
+ /*
+ * Check for intersection, which we have for values of
+ * r and s in [0, 1].
+ */
+ if (r >= 0 &&
+ (r - 1.0) < NODE_EPSILON &&
+ s >= 0 &&
+ (s - 1.0) < NODE_EPSILON) {
+
+ /*
+ * Calculate the intersection point.
+ */
+ pos->x = Ax + r * (Bx - Ax);
+ pos->y = Ay + r * (By - Ay);
+
+ /*
+ to be accepted only if it coincides with the start or end
+ of any of the wires
+ */
+ if ( SEP(pos->x,pos->y,Ax,Ay) ||
+ SEP(pos->x,pos->y,Bx,By) ||
+ SEP(pos->x,pos->y,Cx,Cy) ||
+ SEP(pos->x,pos->y,Dx,Dy)
+ )
+
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+GList *
+node_store_get_parts (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->parts;
+}
+
+GList *
+node_store_get_wires (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->wires;
+}
+
+GList *
+node_store_get_items (NodeStore *store)
+{
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ return store->items;
+}
+
+/*
+ * Debugging code.
+ */
+
+void
+node_store_dump_wires (NodeStore *store)
+{
+ GList *wires;
+
+ g_print ("\n------------------- Dump wires -------------------\n");
+
+ for (wires = store->wires; wires; wires = wires->next) {
+ Wire *wire;
+ SheetPos start, length;
+
+ wire = wires->data;
+ wire_get_pos_and_length (wire, &start, &length);
+
+ g_print ("(%g %g) -> (%g %g): %d nodes.\n",
+ start.x, start.y,
+ start.x + length.x, start.y + length.y,
+ g_slist_length (wire->priv->nodes));
+ }
+
+ g_print ("\n");
+}
+
+static void
+add_node (gpointer key, Node *node, GList **list)
+{
+ *list = g_list_prepend (*list, node);
+}
+
+static void
+add_node_position (gpointer key, Node *node, GList **list)
+{
+ if (node_needs_dot (node))
+ *list = g_list_prepend (*list, key);
+}
+
+GList *
+node_store_get_node_positions (NodeStore *store)
+{
+ GList *result;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ result = NULL;
+ g_hash_table_foreach (store->nodes, (GHFunc) add_node_position, &result);
+
+ return result;
+}
+
+GList *
+node_store_get_nodes (NodeStore *store)
+{
+ GList *result;
+
+ g_return_val_if_fail (store != NULL, NULL);
+ g_return_val_if_fail (IS_NODE_STORE (store), NULL);
+
+ result = NULL;
+ g_hash_table_foreach (store->nodes, (GHFunc) add_node, &result);
+
+ return result;
+}
+
+void
+node_store_get_bounds (NodeStore *store, ArtDRect *rect)
+{
+ GList *list;
+ SheetPos p1, p2;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+ g_return_if_fail (rect != NULL);
+
+ rect->x0 = G_MAXDOUBLE;
+ rect->y0 = G_MAXDOUBLE;
+ rect->x1 = -G_MAXDOUBLE;
+ rect->y1 = -G_MAXDOUBLE;
+
+ for (list = store->items; list; list = list->next) {
+ item_data_get_absolute_bbox (ITEM_DATA (list->data), &p1, &p2);
+
+ rect->x0 = MIN (rect->x0, p1.x);
+ rect->y0 = MIN (rect->y0, p1.y);
+ rect->x1 = MAX (rect->x1, p2.x);
+ rect->y1 = MAX (rect->y1, p2.y);
+ }
+}
+
+gint
+node_store_count_items (NodeStore *store, ArtDRect *rect)
+{
+ GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+ gint n;
+
+ g_return_val_if_fail (store != NULL, 0);
+ g_return_val_if_fail (IS_NODE_STORE (store), 0);
+
+ if (rect == NULL)
+ return g_list_length (store->items);
+
+ for (list = store->items, n = 0; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_get_absolute_bbox (data, &p1, &p2);
+ if (p1.x <= rect->x1 && p1.y <= rect->y1 &&
+ p2.x >= rect->x0 && p2.y >= rect->y0) {
+ n++;
+/*
+ if (p1.x >= rect->x0 && p1.y >= rect->y0 && p2.x <= rect->x1 && p2.y <= rect->y1) {
+*/
+ }
+ }
+
+ return n;
+}
+
+static void
+draw_dot (SheetPos *key, Node *value, cairo_t *cr)
+{
+ if (node_needs_dot (value)) {
+ cairo_save (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ cairo_translate (cr, key->x, key->y);
+ cairo_scale (cr, 1.0, 1.0);
+ cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI);
+ cairo_fill (cr);
+ cairo_restore (cr);
+ }
+}
+
+void
+node_store_print_items (NodeStore *store, cairo_t *cr, SchematicPrintContext *ctx)
+{
+ GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+
+ cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
+ for (list = store->items; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_print (data, cr, ctx);
+ }
+
+ g_hash_table_foreach (store->nodes, (GHFunc)draw_dot, cr);
+}
+
+void
+node_store_print_labels (NodeStore *store, cairo_t *opc, ArtDRect *rect)
+{
+/* GList *list;
+ SheetPos p1, p2;
+ ItemData *data;
+
+ g_return_if_fail (store != NULL);
+ g_return_if_fail (IS_NODE_STORE (store));
+ g_return_if_fail (rect != NULL);
+
+ for (list = store->textbox; list; list = list->next) {
+ data = ITEM_DATA (list->data);
+ item_data_get_absolute_bbox (data, &p1, &p2);
+// if (p1.x >= rect->x0 && p1.y >= rect->y0 && p2.x <= rect->x1 && p2.y <= rect->y1) {
+ if ((p1.x <= rect->x1) && (p1.y <= rect->y1) &&
+ (p2.x >= rect->x0) && (p2.y >= rect->y0)) {
+ item_data_print (data, opc);
+ }
+ }
+*/
+}
+
+int
+node_store_is_pin_at_pos (NodeStore *store, SheetPos pos)
+{
+ int num_pins;
+ SheetPos part_pos;
+ GList *p;
+ Part *part;
+ Pin *pins;
+ int i;
+ gdouble x, y;
+
+ for (p = store->parts; p; p = p->next) {
+ part = PART (p->data);
+
+ num_pins = part_get_num_pins (part);
+
+ item_data_get_pos (ITEM_DATA (part), &part_pos);
+
+ pins = part_get_pins (part);
+ for (i = 0; i < num_pins; i++) {
+ x = part_pos.x + pins[i].offset.x;
+ y = part_pos.y + pins[i].offset.y;
+
+ if ((x == pos.x) && (y == pos.y)) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}