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); }
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); }
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; }
// Retrieves the bounding box. We use a caching scheme for this // since it's too expensive to calculate it every time we need it. inline static void get_cached_bounds (TextboxItem *item, Coords *p1, Coords *p2) { PangoFontDescription *font; Coords pos; TextboxItemPriv *priv; priv = item->priv; if (!priv->cache_valid) { Coords start_pos, end_pos; font = pango_font_description_from_string (TEXTBOX_FONT); item_data_get_pos (sheet_item_get_data (SHEET_ITEM (item)), &pos); start_pos.x = pos.x; start_pos.y = pos.y - 5; // - font->ascent; end_pos.x = pos.x + 5; // + rbearing; end_pos.y = pos.y + 5; // + font->descent; priv->bbox_start = start_pos; priv->bbox_end = end_pos; priv->cache_valid = TRUE; pango_font_description_free (font); } memcpy (p1, &priv->bbox_start, sizeof(Coords)); memcpy (p2, &priv->bbox_end, sizeof(Coords)); }
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; }
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; }
static void wire_changed (ItemData *data) { Coords loc; g_return_if_fail (IS_WIRE (data)); item_data_get_pos (data, &loc); g_signal_emit_by_name ((GObject *)data, "moved", &loc); g_signal_emit_by_name ((GObject *)data, "changed"); }
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; }
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; }
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)); }
void wire_get_end_pos (Wire *wire, Coords *pos) { 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); pos->x += priv->length.x; pos->y += priv->length.y; }
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); } }
/** * register a part to the nodestore */ gboolean node_store_add_part (NodeStore *self, Part *part) { NG_DEBUG ("-0-"); g_return_val_if_fail (self, FALSE); g_return_val_if_fail (IS_NODE_STORE (self), FALSE); g_return_val_if_fail (part, FALSE); g_return_val_if_fail (IS_PART (part), FALSE); GSList *iter, *copy; Node *node; Coords pin_pos; Coords part_pos; int i, num_pins; Pin *pins; num_pins = part_get_num_pins (part); pins = part_get_pins (part); item_data_get_pos (ITEM_DATA (part), &part_pos); for (i = 0; i < num_pins; i++) { // Use the position of the pin as hash key. pin_pos.x = part_pos.x + pins[i].offset.x; pin_pos.y = part_pos.y + pins[i].offset.y; // Retrieve a node for this position. node = node_store_get_or_create_node (self, pin_pos); // Add all the wires that intersect this pin to the node store. copy = get_wires_at_pos (self, pin_pos); for (iter = copy; iter; iter = iter->next) { Wire *wire = copy->data; node_add_wire (node, wire); wire_add_node (wire, node); } g_slist_free (copy); 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; }
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; }
/** * remove/unregister a part from the nodestore * this does _not_ free the part! */ gboolean node_store_remove_part (NodeStore *self, Part *part) { Node *node; Coords pin_pos; Coords part_pos; int i, num_pins; Pin *pins; g_return_val_if_fail (self, FALSE); g_return_val_if_fail (IS_NODE_STORE (self), FALSE); g_return_val_if_fail (part, 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), &part_pos); pins = part_get_pins (part); for (i = 0; i < num_pins; i++) { pin_pos.x = part_pos.x + pins[i].offset.x; pin_pos.y = part_pos.y + pins[i].offset.y; node = g_hash_table_lookup (self->nodes, &pin_pos); if (node) { if (!node_remove_pin (node, &pins[i])) { g_warning ("Could not remove pin[%i] from node %p.", i, node); 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, &pin_pos); g_object_unref (G_OBJECT (node)); } } else { return FALSE; } } return TRUE; }
static void sheet_item_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *spec) { GooCanvasItemSimple *simple = (GooCanvasItemSimple*) object; SheetItem *sheet_item; Coords pos; sheet_item = SHEET_ITEM (object); switch (prop_id) { case ARG_X: sheet_item->x = g_value_get_double (value); break; case ARG_Y: sheet_item->y = g_value_get_double (value); break; case ARG_WIDTH: sheet_item->width = g_value_get_double (value); break; case ARG_HEIGHT: sheet_item->height = g_value_get_double (value); break; case ARG_DATA: if (sheet_item->priv->data) { g_warning (_("Cannot set SheetItem after creation.")); break; } sheet_item->priv->data = g_value_get_pointer (value); item_data_get_pos (sheet_item->priv->data, &pos); sheet_item->x = pos.x; sheet_item->y = pos.y; break; case ARG_ACTION_GROUP: sheet_item->priv->action_group = g_value_get_pointer (value); gtk_ui_manager_insert_action_group (sheet_item->priv->ui_manager, sheet_item->priv->action_group, 0); break; default: break; } goo_canvas_item_simple_changed (simple, TRUE); }
static void flip_items (Sheet *sheet, GList *items, IDFlip direction) { GList *iter, *item_data_list; Coords center, b1, b2; Coords after; item_data_list = NULL; for (iter = items; iter; iter = iter->next) { item_data_list = g_list_prepend (item_data_list, sheet_item_get_data (iter->data)); } item_data_list_get_absolute_bbox (item_data_list, &b1, &b2); // FIXME center is currently not used by item_data_flip (part.c implentation) center.x = (b2.x + b1.x) / 2; center.y = (b2.y + b1.y) / 2; // FIXME - registering an item after flipping it still creates an offset as the position is still 0 for (iter = item_data_list; iter; iter = iter->next) { ItemData *item_data = iter->data; if (sheet->state == SHEET_STATE_NONE) item_data_unregister (item_data); item_data_flip (item_data, direction, ¢er); // Make sure we snap to grid. item_data_get_pos (item_data, &after); snap_to_grid (sheet->grid, &after.x, &after.y); item_data_set_pos (item_data, &after); if (sheet->state == SHEET_STATE_NONE) item_data_register (item_data); } g_list_free (item_data_list); }
static void flip_items (Sheet *sheet, GList *items, gboolean horizontal) { GList *list, *item_data_list; SheetPos center, b1, b2; SheetPos after; item_data_list = NULL; for (list = items; list; list = list->next) { item_data_list = g_list_prepend (item_data_list, sheet_item_get_data (list->data)); } item_data_list_get_absolute_bbox (item_data_list, &b1, &b2); center.x = (b2.x + b1.x) / 2; center.y = (b2.y + b1.y) / 2; for (list = item_data_list; list; list = list->next) { ItemData *item_data = list->data; if (sheet->state == SHEET_STATE_NONE) item_data_unregister (item_data); item_data_flip (item_data, horizontal, ¢er); // Make sure we snap to grid. item_data_get_pos (item_data, &after); snap_to_grid (sheet->grid, &after.x, &after.y); item_data_move (item_data, &after); if (sheet->state == SHEET_STATE_NONE) item_data_register (item_data); } g_list_free (item_data_list); g_list_free_full (list, g_object_unref); }
static void part_changed (ItemData *data) { Part *part; Coords loc = {0., 0.}; int angle = 0; IDFlip flip = ID_FLIP_NONE; g_return_if_fail (IS_PART (data)); part = (Part *)data; flip = part_get_flip (part); angle = part_get_rotation (part); item_data_get_pos (data, &loc); #if 0 //FIXME isn't it more sane to just emit the changed? g_signal_emit_by_name (data, "moved", &loc); g_signal_emit_by_name (data, "flipped", flip); g_signal_emit_by_name (data, "rotated", angle); #endif g_signal_emit_by_name (data, "changed"); }
/** * whenever the model changes, this one gets called to update the view representation * @attention this recalculates the matrix every time, this makes sure no errors stack up * @attention further reading on matrix manipulations * @attention http://www.cairographics.org/matrix_transform/ * @param data the model item, a bare C struct derived from ItemData * @param sheet_item the view item, derived from goo_canvas_group/item */ static void part_changed_callback (ItemData *data, SheetItem *sheet_item) { //TODO add static vars in order to skip the redraw if nothing changed //TODO may happen once in a while and the check is really cheap GSList *iter; GooCanvasAnchorType anchor; GooCanvasGroup *group; GooCanvasItem *canvas_item; PartItem *item; PartItemPriv *priv; Part *part; int index = 0; Coords pos; double scale_h, scale_v; // states int rotation; IDFlip flip; g_return_if_fail (sheet_item != NULL); g_return_if_fail (IS_PART_ITEM (sheet_item)); item = PART_ITEM (sheet_item); group = GOO_CANVAS_GROUP (item); part = PART (data); priv = item->priv; // init the states flip = part_get_flip (part); rotation = part_get_rotation (part); DEGSANITY (rotation); scale_h = (flip & ID_FLIP_HORIZ) ? -1. : 1.; scale_v = (flip & ID_FLIP_VERT) ? -1. : 1.; item_data_get_pos (data, &pos); // Move the canvas item and invalidate the bbox cache. goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (sheet_item), pos.x, pos.y, 1.0, 0.0); cairo_matrix_t morph, inv; cairo_status_t done; cairo_matrix_init_rotate (&morph, DEG2RAD (rotation)); cairo_matrix_scale (&morph, scale_h, scale_v); inv = morph; done = cairo_matrix_invert (&inv); if (done != CAIRO_STATUS_SUCCESS) { g_warning ("Failed to invert matrix. This should never happen. Never!"); return; } // rotate all items in the canvas group for (index = 0; index < group->items->len; index++) { canvas_item = GOO_CANVAS_ITEM (group->items->pdata[index]); goo_canvas_item_set_transform (GOO_CANVAS_ITEM (canvas_item), &morph); } // revert the rotation of all labels and change their anchor to not overlap too badly // this assures that the text is always horizontal and properly aligned anchor = angle_to_anchor (rotation); for (iter = priv->label_items; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // same for label nodes for (iter = priv->label_nodes; iter; iter = iter->next) { g_object_set (iter->data, "anchor", anchor, NULL); goo_canvas_item_set_transform (iter->data, &inv); } // Invalidate the bounding box cache. priv->cache_valid = FALSE; }
/** * add/register the wire to the nodestore * * @param store * @param wire * @returns TRUE if the wire was added or merged, else FALSE */ gboolean node_store_add_wire (NodeStore *store, Wire *wire) { GList *list; Node *node; int i = 0; g_return_val_if_fail (store, FALSE); g_return_val_if_fail (IS_NODE_STORE (store), FALSE); g_return_val_if_fail (wire, FALSE); g_return_val_if_fail (IS_WIRE (wire), FALSE); // Check for intersection with other wires. for (list = store->wires; list; list = list->next) { g_assert (list->data != NULL); g_assert (IS_WIRE (list->data)); Coords where = {-77.77, -77.77}; Wire *other = list->data; if (do_wires_intersect (wire, other, &where)) { if (is_t_crossing (wire, other, &where) || is_t_crossing (other, wire, &where)) { node = node_store_get_or_create_node (store, where); node_add_wire (node, wire); node_add_wire (node, other); wire_add_node (wire, node); wire_add_node (other, node); NG_DEBUG ("Add wire %p to wire %p @ %lf,%lf.\n", wire, other, where.x, where.y); } else { // magic node removal if a x crossing is overlapped with another wire node = node_store_get_node (store, where); NG_DEBUG ("Nuke that node [ %p ] at coords inbetween", node); if (node) { Coords c[4]; wire_get_start_and_end_pos (other, c + 0, c + 1); wire_get_start_and_end_pos (wire, c + 2, c + 3); if (!coords_equal (&where, c + 0) && !coords_equal (&where, c + 1) && !coords_equal (&where, c + 2) && !coords_equal (&where, c + 3)) { wire_remove_node (wire, node); wire_remove_node (other, node); node_remove_wire (node, wire); node_remove_wire (node, other); } } } } } // Check for overlapping with other wires. do { for (list = store->wires; list; list = list->next) { g_assert (list->data != NULL); g_assert (IS_WIRE (list->data)); Wire *other = list->data; Coords so, eo; const gboolean overlap = do_wires_overlap (wire, other, &so, &eo); NG_DEBUG ("overlap [ %p] and [ %p ] -- %s", wire, other, overlap == TRUE ? "YES" : "NO"); if (overlap) { Node *sn = node_store_get_node (store, eo); Node *en = node_store_get_node (store, so); #if 1 wire = vulcanize_wire (store, wire, other, &so, &eo); node_store_remove_wire (store, g_object_ref (other)); // equiv // wire_unregister // XXX FIXME this // modifies the list // we iterate over! // delay this until idle, so all handlers like adding view // representation are completed so existing wire-items can be deleted // properly // this is not fancy nor nice but seems to work fairly nicly g_idle_add (delayed_wire_delete, other); break; NG_DEBUG ("overlapping of %p with %p ", wire, other); #else if (!sn && !en) { wire = vulcanize_wire (store, wire, other, &so, &eo); } else if (!sn) { NG_DEBUG ("do_something(TM) : %p sn==NULL ", other); } else if (!en) { NG_DEBUG ("do_something(TM) : %p en==NULL ", other); } else { NG_DEBUG ("do_something(TM) : %p else ", other); } #endif } else { NG_DEBUG ("not of %p with %p ", wire, other); } } } while (list); // Check for intersection with parts (pins). for (list = store->parts; list; list = list->next) { g_assert (list->data != NULL); g_assert (IS_PART (list->data)); Coords part_pos; gint num_pins = -1; Part *part = list->data; num_pins = part_get_num_pins (part); item_data_get_pos (ITEM_DATA (part), &part_pos); // Go through all the parts and see which of their // pins that intersect the wire. for (i = 0; i < num_pins; i++) { Pin *pins; Coords lookup_pos; pins = part_get_pins (part); lookup_pos.x = part_pos.x + pins[i].offset.x; lookup_pos.y = part_pos.y + pins[i].offset.y; // If there is a wire at this pin's position, // add it to the return list. if (is_point_on_wire (wire, &lookup_pos)) { Node *node; node = node_store_get_node (store, lookup_pos); if (node != NULL) { // Add the wire to the node (pin) that it intersected. node_add_wire (node, wire); wire_add_node (wire, node); NG_DEBUG ("Add wire %p to pin (node) %p.\n", wire, node); } else { g_warning ("Bug: Found no node at pin at (%g %g).\n", lookup_pos.x, lookup_pos.y); } } } } 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; }
static void write_xml_part (Part *part, parseXmlContext *ctxt) { PartPriv *priv; xmlNodePtr node_part; gchar *str; Coords pos; priv = part->priv; // Create a node for the part. node_part = xmlNewChild (ctxt->node_parts, ctxt->ns, BAD_CAST "part", NULL); if (!node_part) { g_warning ("Failed during save of part %s.\n", priv->name); return; } str = g_strdup_printf ("%d", priv->rotation); xmlNewChild (node_part, ctxt->ns, BAD_CAST "rotation", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST str)); g_free (str); if (priv->flip & ID_FLIP_HORIZ) xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "horizontal")); if (priv->flip & ID_FLIP_VERT) xmlNewChild (node_part, ctxt->ns, BAD_CAST "flip", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST "vertical")); // Store the name. xmlNewChild (node_part, ctxt->ns, BAD_CAST "name", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->name)); // Store the name of the library the part resides in. xmlNewChild (node_part, ctxt->ns, BAD_CAST "library", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->library->name)); // Which symbol to use. xmlNewChild (node_part, ctxt->ns, BAD_CAST "symbol", xmlEncodeEntitiesReentrant (ctxt->doc, BAD_CAST priv->symbol_name)); // Position. item_data_get_pos (ITEM_DATA (part), &pos); str = g_strdup_printf ("(%g %g)", pos.x, pos.y); xmlNewChild (node_part, ctxt->ns, BAD_CAST "position", BAD_CAST str); g_free (str); // Create a node for the properties. ctxt->node_props = xmlNewChild (node_part, ctxt->ns, BAD_CAST "properties", NULL); if (!ctxt->node_props) { g_warning ("Failed during save of part %s.\n", priv->name); return; } else{ g_slist_foreach (priv->properties, (GFunc) write_xml_property, ctxt); } // Create a node for the labels. ctxt->node_labels = xmlNewChild (node_part, ctxt->ns, BAD_CAST "labels", NULL); if (!ctxt->node_labels) { g_warning ("Failed during save of part %s.\n", priv->name); return; } else{ g_slist_foreach (priv->labels, (GFunc) write_xml_label, ctxt); } }
/** * \brief rotate an item by an @angle increment (may be negative) * * @angle the increment the item will be rotated (usually 90° steps) * @center_pos if rotated as part of a group, this is the center to rotate *around */ static void part_rotate (ItemData *data, int angle, Coords *center_pos) { g_return_if_fail (data); g_return_if_fail (IS_PART (data)); cairo_matrix_t morph, morph_rot, local_rot; Part *part; PartPriv *priv; gboolean handler_connected; // Coords b1, b2; part = PART (data); priv = part->priv; // FIXME store vanilla coords, apply the morph // FIXME to these and store the result in the // FIXME instance then everything will be fine // XXX also prevents rounding yiggle up downs angle /= 90; angle *= 90; cairo_matrix_init_rotate (&local_rot, (double)angle * M_PI / 180.); cairo_matrix_multiply (item_data_get_rotate (data), item_data_get_rotate (data), &local_rot); morph_rot = *(item_data_get_rotate (data)); cairo_matrix_multiply (&morph, &morph_rot, item_data_get_translate (data)); Coords delta_to_center, delta_to_center_transformed; Coords delta_to_apply, delta_bbox; Coords bbox_center, bbox_center_transformed; Coords item_pos; // get bbox #if 0 // this causes #115 to reappear item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); bbox_center = coords_average (&b1, &b2); #endif item_data_get_pos (ITEM_DATA (part), &item_pos); Coords rotation_center; if (center_pos == NULL) { rotation_center = coords_sum (&bbox_center, &item_pos); } else { rotation_center = *center_pos; } delta_to_center_transformed = delta_to_center = coords_sub (&rotation_center, &item_pos); cairo_matrix_transform_point (&local_rot, &(delta_to_center_transformed.x), &(delta_to_center_transformed.y)); delta_to_apply = coords_sub (&delta_to_center, &delta_to_center_transformed); #define DEBUG_THIS 0 // use the cairo matrix funcs to transform the pin // positions relative to the item center // this is only indirectly related to displayin // HINT: we need to modify the actual pins to make the // pin tests work being used to detect connections gint i; gdouble x, y; // Rotate the pins. for (i = 0; i < priv->num_pins; i++) { x = priv->pins_orig[i].offset.x; y = priv->pins_orig[i].offset.y; cairo_matrix_transform_point (&morph_rot, &x, &y); if (fabs (x) < 1e-2) x = 0.0; if (fabs (y) < 1e-2) y = 0.0; priv->pins[i].offset.x = x; priv->pins[i].offset.y = y; } item_data_move (data, &delta_to_apply); handler_connected = g_signal_handler_is_connected (G_OBJECT (data), data->changed_handler_id); if (handler_connected) { g_signal_emit_by_name (G_OBJECT (data), "changed"); } else { NG_DEBUG ("handler not yet registerd."); } NG_DEBUG ("\n\n"); }
static void part_print (ItemData *data, cairo_t *cr, SchematicPrintContext *ctx) { GSList *objects, *labels; SymbolObject *object; LibrarySymbol *symbol; double x0, y0; int i, rotation; Part *part; PartPriv *priv; Coords pos; IDFlip flip; GooCanvasPoints *line; g_return_if_fail (data != NULL); g_return_if_fail (IS_PART (data)); part = PART (data); priv = part->priv; symbol = library_get_symbol (priv->symbol_name); if (symbol == NULL) { return; } item_data_get_pos (ITEM_DATA (part), &pos); x0 = pos.x; y0 = pos.y; cairo_save (cr); gdk_cairo_set_source_rgba (cr, &ctx->colors.components); rotation = part_get_rotation (part); flip = part_get_flip (part); if ((flip & ID_FLIP_HORIZ) && (flip & ID_FLIP_VERT)) rotation += 180; else if (flip == ID_FLIP_HORIZ) cairo_scale (cr, -1, 1); else if (flip == ID_FLIP_VERT) cairo_scale (cr, 1, -1); if (rotation %= 360) cairo_rotate (cr, rotation*M_PI/180); for (objects = symbol->symbol_objects; objects; objects = objects->next) { object = (SymbolObject *)(objects->data); switch (object->type) { case SYMBOL_OBJECT_LINE: line = object->u.uline.line; for (i = 0; i < line->num_points; i++) { double x, y; x = line->coords[i * 2]; y = line->coords[i * 2 + 1]; if (i == 0) cairo_move_to (cr, x0 + x, y0 + y); else cairo_line_to (cr, x0 + x, y0 + y); } break; case SYMBOL_OBJECT_ARC: { gdouble x1 = object->u.arc.x1; gdouble y1 = object->u.arc.y1; gdouble x2 = object->u.arc.x2; gdouble y2 = object->u.arc.y2; gdouble width, height, x, y; x = (x2 + x1) / 2; y = (y2 + y1) / 2; width = x2 - x1; height = y2 - y1; cairo_save (cr); cairo_translate (cr, x0 + x, y0 + y); cairo_scale (cr, width / 2.0, height / 2.0); cairo_arc (cr, 0.0, 0.0, 1.0, 0.0, 2 * M_PI); cairo_restore (cr); } break; default: g_warning ( "Print part: Part %s contains unknown object.", priv->name ); continue; } cairo_stroke (cr); } // We don't want to rotate labels text, only the (x,y) coordinate gdk_cairo_set_source_rgba (cr, &ctx->colors.labels); for (labels = part_get_labels (part); labels; labels = labels->next) { gdouble x, y; PartLabel *label = (PartLabel *)labels->data; gchar *text; /* gint text_width, text_height; */ x = label->pos.x + x0; y = label->pos.y + y0; text = part_property_expand_macros (part, label->text); /* Align the label. switch (rotation) { case 90: y += text_height*opc->scale; break; case 180: break; case 270: x -= text_width*opc->scale; break; case 0: default: break; } */ cairo_save (cr); cairo_move_to (cr, x, y); cairo_show_text (cr, text); cairo_restore (cr); g_free (text); } cairo_restore (cr); }
/** * flip a part in a given direction * @direction gives the direction the item will be flipped, end users pov! * @center the center to flip over - currently ignored FIXME */ static void part_flip (ItemData *data, IDFlip direction, Coords *center) { Part *part; PartPriv *priv; int i; cairo_matrix_t affine; double x, y; double scale_v, scale_h; gboolean handler_connected; Coords pos, trans; Coords b1, b2; Coords pos_new, pos_old, delta; //FIXME properly recenter after flipping //Coords part_center_before, part_center_after, delta; g_return_if_fail (data); g_return_if_fail (IS_PART (data)); part = PART (data); priv = part->priv; item_data_get_pos (data, &trans); // mask, just for the sake of cleanness direction &= ID_FLIP_MASK; // TODO evaluate if we really want to be able to do double flips (180* rots via flipping) g_assert (direction != ID_FLIP_MASK); // create a transformation _relativ_ to the current _state_ // reverse axis and fix the created offset by adding 2*pos.x or .y // convert the flip direction to binary, used in the matrix setup // keep in mind that we do relativ manipulations within the model // which in turn makes this valid for all rotations! scale_h = ((direction & ID_FLIP_HORIZ) != 0) ? -1. : 1.; scale_v = ((direction & ID_FLIP_VERT) != 0) ? -1. : 1.; // magic, if we are in either 270 or 90 state, we need to rotate the flip state by 90° to draw it properly // TODO maybe better put this into the rotation function if ((priv->rotation / 90) % 2 == 1) { priv->flip ^= ID_FLIP_MASK; } // toggle the direction priv->flip ^= direction; if ((priv->flip & ID_FLIP_MASK)== ID_FLIP_MASK) { priv->flip = ID_FLIP_NONE; priv->rotation += 180; priv->rotation %= 360; } cairo_matrix_init_scale (&affine, scale_h, scale_v); item_data_get_pos (data, &pos_old); pos_new = pos_old; cairo_matrix_transform_point (&affine, &pos_new.x, &pos_new.y); g_printf ("\ncenter %p [old] x=%lf,y=%lf -->", data, pos_old.x, pos_old.y); g_printf (" x=%lf, y=%lf\n", pos_new.x, pos_new.y); delta.x = - pos_new.x + pos_old.x; delta.y = - pos_new.y + pos_old.y; // flip the pins for (i = 0; i < priv->num_pins; i++) { x = priv->pins[i].offset.x; y = priv->pins[i].offset.y; cairo_matrix_transform_point (&affine, &x, &y); if (fabs (x) < 1e-2) x = 0.0; if (fabs (y) < 1e-2) y = 0.0; priv->pins[i].offset.x = x; priv->pins[i].offset.y = y; } item_data_snap (data); // tell the view handler_connected = g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA(part)->flipped_handler_id); if (handler_connected) { g_signal_emit_by_name (G_OBJECT (part), "flipped", priv->flip); // TODO - proper boundingbox center calculation item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); // flip the bounding box. cairo_matrix_transform_point (&affine, &b1.x, &b1.y); cairo_matrix_transform_point (&affine, &b2.x, &b2.y); item_data_set_relative_bbox (ITEM_DATA (part), &b1, &b2); item_data_set_pos (ITEM_DATA (part), &pos); // FIXME - proper recenter to boundingbox center } if (g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA (part)->changed_handler_id)) { g_signal_emit_by_name (G_OBJECT (part), "changed"); } }