static void item_data_dispose (GObject *object) { ItemDataPriv *priv = ITEM_DATA (object)->priv; // Remove the item from the sheet node store if there. if (priv->store) { item_data_unregister (ITEM_DATA (object)); } g_slice_free (ItemDataPriv, priv); G_OBJECT_CLASS (item_data_parent_class)->dispose (object); }
static gboolean create_textbox_event (Sheet *sheet, GdkEvent *event) { switch (event->type) { case GDK_3BUTTON_PRESS: case GDK_2BUTTON_PRESS: return TRUE; case GDK_BUTTON_PRESS: if (event->button.button == 4 || event->button.button == 5) return FALSE; if (event->button.button == 1) { if (sheet->state == SHEET_STATE_TEXTBOX_WAIT) sheet->state = SHEET_STATE_TEXTBOX_START; return TRUE; } else return FALSE; case GDK_BUTTON_RELEASE: if (event->button.button == 4 || event->button.button == 5) return FALSE; if (sheet->state == SHEET_STATE_TEXTBOX_START) { Textbox *textbox; Coords pos; sheet->state = SHEET_STATE_NONE; sheet_get_pointer (sheet, &pos.x, &pos.y); textbox = textbox_new (NULL); textbox_set_text (textbox, _ ("Label")); item_data_set_pos (ITEM_DATA (textbox), &pos); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), ITEM_DATA (textbox)); schematic_view_reset_tool (schematic_view_get_schematicview_from_sheet (sheet)); g_signal_handlers_disconnect_by_func (G_OBJECT (sheet), G_CALLBACK (create_textbox_event), sheet); } return TRUE; default: return FALSE; } return TRUE; }
static void textbox_flip (ItemData *data, gboolean horizontal, SheetPos *center) { double affine[6]; ArtPoint src, dst; Textbox *textbox; TextboxPriv *priv; SheetPos b1, b2; SheetPos textbox_center, delta; g_return_if_fail (data != NULL); g_return_if_fail (IS_TEXTBOX (data)); textbox = TEXTBOX (data); if (center) { item_data_get_absolute_bbox (ITEM_DATA (textbox), &b1, &b2); textbox_center.x = b1.x + (b2.x - b1.x) / 2; textbox_center.y = b1.y + (b2.y - b1.y) / 2; } priv = textbox->priv; if (horizontal) art_affine_scale (affine, -1, 1); else art_affine_scale (affine, 1, -1); /* * Let the views (canvas items) know about the rotation. */ g_signal_emit_by_name(G_OBJECT (textbox), "flipped", horizontal); if (center) { SheetPos textbox_pos; item_data_get_pos (ITEM_DATA (textbox), &textbox_pos); src.x = textbox_center.x - center->x; src.y = textbox_center.y - center->y; art_affine_point (&dst, &src, affine); delta.x = -src.x + dst.x; delta.y = -src.y + dst.y; item_data_move (ITEM_DATA (textbox), &delta); } }
void test_nodestore () { gint i; NodeStore *store; Part *part; Wire *wire; Node *node; Coords p_pos = {111.,22.}; Coords n_pos = {111.,33.}; Coords w_pos = {111.,7.}; Coords w_len = {0.,88.}; store = node_store_new (); part = part_new (); wire = wire_new (); // add one Pin with a offset that is on the wire when rotation N*Pi times Pin *pin = g_new (Pin, 1); pin->offset.x = n_pos.x - p_pos.x; pin->offset.y = n_pos.y - p_pos.y; GSList *list = NULL; list = g_slist_prepend (list, pin); part_set_pins (part, list); g_slist_free (list); item_data_set_pos (ITEM_DATA (part), &p_pos); item_data_set_pos (ITEM_DATA (wire), &w_pos); wire_set_length (wire, &w_len); node_store_add_part (store, part); node_store_add_wire (store, wire); { for (i=0; i<11; i++) item_data_rotate (ITEM_DATA (part), 90, NULL); item_data_set_pos (ITEM_DATA (part), &w_len); for (i=0; i<4; i++) item_data_rotate (ITEM_DATA (part), 90, NULL); item_data_set_pos (ITEM_DATA (part), &n_pos); for (i=0; i<7; i++) item_data_rotate (ITEM_DATA (part), -90, NULL); item_data_set_pos (ITEM_DATA (part), &p_pos); } g_assert (node_store_is_wire_at_pos (store, n_pos)); g_assert (node_store_is_pin_at_pos (store, n_pos)); node = node_store_get_node (store, n_pos); g_assert (!node_is_empty (node)); g_assert (node_needs_dot (node)); node_store_remove_part (store, part); node_store_remove_wire (store, wire); g_object_unref (store); }
/* static */ void textbox_update_bbox (Textbox *textbox) { PangoFontDescription *font; /* Unused variables int width; int rbearing; int lbearing; int ascent, descent; */ SheetPos b1, b2; TextboxPriv *priv; priv = textbox->priv; font = pango_font_description_from_string(priv->font); /* TODO : Find out how to do this with Pango. */ /* gdk_string_extents (font, priv->text, &lbearing, &rbearing, &width, &ascent, &descent); gdk_font_unref (font); */ b1.x = 0.0; b1.y = 0.0-5; // - font->ascent; b2.x = 0.0+5; // + rbearing; b2.y = 0.0+5; // + font->descent; item_data_set_relative_bbox (ITEM_DATA (textbox), &b1, &b2); pango_font_description_free(font); }
static void write_xml_textbox (Textbox *textbox, parseXmlContext *ctxt) { xmlNodePtr node_textbox; gchar *str; Coords pos; g_return_if_fail (textbox != NULL); if (!IS_TEXTBOX (textbox)) return; // Create a node for the textbox. node_textbox = xmlNewChild (ctxt->node_textboxes, ctxt->ns, BAD_CAST "textbox", NULL); if (!node_textbox) { g_warning ("Failed during save of text box.\n"); return; } item_data_get_pos (ITEM_DATA (textbox), &pos); str = g_strdup_printf ("(%g %g)", pos.x, pos.y); xmlNewChild (node_textbox, ctxt->ns, BAD_CAST "position", BAD_CAST str); g_free (str); str = textbox_get_text (textbox); xmlNewChild (node_textbox, ctxt->ns, BAD_CAST "text", BAD_CAST str); }
gboolean node_store_is_pin_at_pos (NodeStore *store, Coords pos) { int num_pins; Coords 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 (fabs (x - pos.x) < NODE_EPSILON && fabs (y - pos.y) < NODE_EPSILON) return TRUE; } } return FALSE; }
PartItem * part_item_canvas_new (Sheet *sheet, Part *part) { PartItem *part_item; PartItemPriv *priv; GooCanvasItem *item; ItemData *item_data; g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); g_return_val_if_fail (part != NULL, NULL); g_return_val_if_fail (IS_PART (part), NULL); item = g_object_new (TYPE_PART_ITEM, NULL); g_object_set (item, "parent", sheet->object_group, NULL); part_item = PART_ITEM (item); g_object_set (part_item, "data", part, NULL); priv = part_item->priv; priv->label_group = GOO_CANVAS_ITEM (goo_canvas_group_new ( GOO_CANVAS_ITEM (part_item), "width", -1.0, "height", -1.0, NULL)); g_object_unref (item); priv->node_group = GOO_CANVAS_ITEM (goo_canvas_group_new ( GOO_CANVAS_ITEM (part_item), NULL)); g_object_set (GOO_CANVAS_ITEM (priv->node_group), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); item_data = ITEM_DATA (part); item_data->rotated_handler_id = g_signal_connect_object (G_OBJECT (part), "rotated", G_CALLBACK (part_rotated_callback), G_OBJECT (part_item), 0); item_data->flipped_handler_id = g_signal_connect_object (G_OBJECT (part), "flipped", G_CALLBACK (part_flipped_callback), G_OBJECT (part_item), 0); item_data->moved_handler_id = g_signal_connect_object (G_OBJECT (part), "moved", G_CALLBACK (part_moved_callback), G_OBJECT (part_item), 0); item_data->changed_handler_id = g_signal_connect_object (G_OBJECT (part), "changed", G_CALLBACK (part_changed_callback), G_OBJECT (part_item), 0); return part_item; }
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; } } } g_list_free_full (p, g_object_unref); return 0; }
void node_store_get_bounds (NodeStore *store, NodeRect *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); } g_list_free_full (list, g_object_unref); }
/** * @returns the rotation in degrees * @attention steps of 90 degrees only! */ gint part_get_rotation (Part *part) { ItemData *item; gdouble register a, b, c, d, sx, sy; cairo_matrix_t *t; g_return_val_if_fail (part != NULL, 0); g_return_val_if_fail (IS_PART (part), 0); item = ITEM_DATA (part); t = item_data_get_rotate (item); a = t->xx; b = t->xy; c = t->yx; d = t->yy; sx = a * a + c * c; sy = b * b + d * d; if (G_UNLIKELY (abs (sx) < 1e-10 && abs (sy) < 1e-10)) { g_warning ("Unabled to calculate rotation from matrix. Assuming 0°."); return 0; } gint register r = -1; if (abs (sx) > abs (sy)) r = 90 * (gint)(2. * acos (a / sqrt (sx)) / M_PI); else r = 90 * (gint)(2. * acos (d / sqrt (sy)) / M_PI); return r; }
gint node_store_count_items (NodeStore *store, NodeRect *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++; } } g_list_free_full (list, g_object_unref); return n; }
void wire_dbg_print (Wire *w) { Coords pos; item_data_get_pos (ITEM_DATA (w), &pos); NG_DEBUG ("Wire %p is defined by (%lf,%lf) + lambda * (%lf,%lf)\n", w, pos.x, pos.y, w->priv->length.x, w->priv->length.y); }
ItemData *item_data_new (void) { ItemData *item_data; item_data = ITEM_DATA (g_object_new (item_data_get_type (), NULL)); return item_data; }
static ItemData *wire_clone (ItemData *src) { Wire *new_wire; ItemDataClass *id_class; g_return_val_if_fail (src != NULL, NULL); g_return_val_if_fail (IS_WIRE (src), NULL); id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (src)); if (id_class->copy == NULL) return NULL; new_wire = g_object_new (TYPE_WIRE, NULL); id_class->copy (ITEM_DATA (new_wire), src); return ITEM_DATA (new_wire); }
void wire_get_start_pos (Wire *wire, Coords *pos) { g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); g_return_if_fail (pos != NULL); item_data_get_pos (ITEM_DATA (wire), pos); }
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) ip_list = g_slist_prepend (ip_list, node); } } } g_list_free_full (list, g_object_unref); return ip_list; }
TextboxItem *textbox_item_new (Sheet *sheet, Textbox *textbox) { GooCanvasItem *item; TextboxItem *textbox_item; TextboxItemPriv *priv; Coords pos; ItemData *item_data; g_return_val_if_fail (sheet != NULL, NULL); g_return_val_if_fail (IS_SHEET (sheet), NULL); item_data_get_pos (ITEM_DATA (textbox), &pos); item = g_object_new (TYPE_TEXTBOX_ITEM, NULL); g_object_set (item, "parent", sheet->object_group, NULL); textbox_item = TEXTBOX_ITEM (item); g_object_set (textbox_item, "data", textbox, NULL); priv = textbox_item->priv; priv->text_canvas_item = goo_canvas_text_new ( GOO_CANVAS_ITEM (textbox_item), textbox_get_text (textbox), 0.0, 0.0, -1, GOO_CANVAS_ANCHOR_SW, "font", TEXTBOX_FONT, "fill-color", NORMAL_COLOR, NULL); item_data = ITEM_DATA (textbox); item_data->rotated_handler_id = g_signal_connect_object (G_OBJECT (textbox), "rotated", G_CALLBACK (textbox_rotated_callback), G_OBJECT (textbox_item), 0); item_data->flipped_handler_id = g_signal_connect_object (G_OBJECT (textbox), "flipped", G_CALLBACK (textbox_flipped_callback), G_OBJECT (textbox_item), 0); item_data->moved_handler_id = g_signal_connect_object (G_OBJECT (textbox), "moved", G_CALLBACK (textbox_moved_callback), G_OBJECT (textbox_item), 0); textbox->text_changed_handler_id = g_signal_connect_object ( G_OBJECT (textbox), "text_changed", G_CALLBACK (textbox_text_changed_callback), G_OBJECT (textbox_item), 0); textbox_update_bbox (textbox); return textbox_item; }
static void wire_changed_callback (Wire *wire, WireItem *item) { Coords start_pos, length; GooCanvasPoints *points; g_return_if_fail (wire != NULL); g_return_if_fail (IS_ITEM_DATA (wire)); g_return_if_fail (item != NULL); g_return_if_fail (IS_WIRE_ITEM (item)); wire_get_pos_and_length (wire, &start_pos, &length); Sheet *sheet = SHEET (goo_canvas_item_get_canvas (GOO_CANVAS_ITEM (item))); if (G_UNLIKELY(!sheet)) { g_warning ("Failed to determine the Sheet the item is glued to. This should never happen. Ever!"); } else { item_data_snap (ITEM_DATA (wire), sheet->grid); } // Move the canvas item and invalidate the bbox cache. goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (item), start_pos.x, start_pos.y, 1.0, 0.0); item->priv->cache_valid = FALSE; points = goo_canvas_points_new (2); points->coords[0] = 0; points->coords[1] = 0; points->coords[2] = length.x; points->coords[3] = length.y; // this does handle cleanup of previous points internally g_object_set (item->priv->line, "points", points, NULL); goo_canvas_points_unref (points); g_object_set (item->priv->resize1, "x", -RESIZER_SIZE, "y", -RESIZER_SIZE, "width", 2 * RESIZER_SIZE, "height", 2 * RESIZER_SIZE, NULL); g_object_set (item->priv->resize2, "x", length.x-RESIZER_SIZE, "y", length.y-RESIZER_SIZE, "width", 2 * RESIZER_SIZE, "height", 2 * RESIZER_SIZE, NULL); goo_canvas_item_request_update (GOO_CANVAS_ITEM (item->priv->line)); }
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; NG_DEBUG ("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; }
static ItemData *part_clone (ItemData *src) { Part *src_part, *new_part; ItemDataClass *id_class; g_return_val_if_fail (src != NULL, NULL); g_return_val_if_fail (IS_PART (src), NULL); id_class = ITEM_DATA_CLASS (G_OBJECT_GET_CLASS (src)); if (id_class->copy == NULL) return NULL; src_part = PART (src); new_part = g_object_new (TYPE_PART, NULL); new_part->priv->pins = g_new0 (Pin, src_part->priv->num_pins); id_class->copy (ITEM_DATA (new_part), src); return ITEM_DATA (new_part); }
static ItemData * textbox_clone (ItemData *src) { Textbox *src_textbox, *new_textbox; ItemDataClass *id_class; g_return_val_if_fail (src != NULL, NULL); g_return_val_if_fail (IS_TEXTBOX (src), NULL); id_class = ITEM_DATA_CLASS(G_OBJECT_GET_CLASS(src)); if (id_class->copy == NULL) return NULL; src_textbox = TEXTBOX(src); new_textbox = TEXTBOX(g_object_new(TYPE_TEXTBOX, NULL)); id_class->copy (ITEM_DATA (new_textbox), src); return ITEM_DATA (new_textbox); }
Part * part_new (Grid *grid) { Part *part; part = PART (g_object_new (TYPE_PART, NULL)); item_data_set_grid (ITEM_DATA (part), grid); return part; }
void wire_update_bbox (Wire *wire) { Coords b1, b2, pos, length; wire_get_pos_and_length (wire, &pos, &length); b1.x = b1.y = 0.0; b2 = length; item_data_set_relative_bbox (ITEM_DATA (wire), &b1, &b2); }
/** * @brief */ void Ai_RegisterItem(const g_item_t *item) { const uint16_t index = ITEM_DATA(item, index); if (index >= MAX_ITEMS) { aim.gi->Warn("Bad index for item: %d\n", index); return; } if (ai_items[index]) { return; } ai_items[index] = item; ai_num_items++; if (ITEM_DATA(item, type) == ITEM_WEAPON) { ai_num_weapons++; } }
static void place_cmd (GtkWidget *widget, Browser *br) { LibraryPart *library_part; char *part_name; Coords pos; Sheet *sheet; Part *part; GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *selection; schematic_view_reset_tool (br->schematic_view); sheet = schematic_view_get_sheet (br->schematic_view); // Get the current selected row selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (br->list)); model = gtk_tree_view_get_model (GTK_TREE_VIEW (br->list)); if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { return; } gtk_tree_model_get (model, &iter, 0, &part_name, -1); library_part = library_get_part (br->library, part_name); part = part_new_from_library_part (library_part); if (!part) { oregano_error (_("Unable to load required part")); return; } pos.x = pos.y = 0; item_data_set_pos (ITEM_DATA (part), &pos); sheet_connect_part_item_to_floating_group (sheet, (gpointer) br->schematic_view); sheet_select_all (sheet, FALSE); sheet_clear_ghosts (sheet); sheet_add_ghost_item (sheet, ITEM_DATA (part)); }
void wire_get_pos_and_length (Wire *wire, Coords *pos, Coords *length) { WirePriv *priv; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); g_return_if_fail (pos != NULL); priv = wire->priv; item_data_get_pos (ITEM_DATA (wire), pos); *length = priv->length; }
static void item_data_get_gproperty (GObject *object, guint prop_id, GValue *value, GParamSpec *spec) { ItemData *item_data = ITEM_DATA (object); switch (prop_id) { case ARG_STORE: g_value_set_pointer (value, item_data->priv->store); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (item_data, prop_id, spec); } }
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 { return FALSE; } } return TRUE; }
void wire_get_start_and_end_pos (Wire *wire, Coords *start, Coords *end) { WirePriv *priv; g_return_if_fail (wire != NULL); g_return_if_fail (IS_WIRE (wire)); g_return_if_fail (start != NULL); g_return_if_fail (end != NULL); priv = wire->priv; item_data_get_pos (ITEM_DATA (wire), start); *end = coords_sum (start, &(priv->length)); }