/*traverse the stack and return the magnetic field*/ int mcmagnet_get_field(double x, double y, double z, double t, double *bx,double *by, double *bz, void *dummy){ mcmagnet_field_info *p=stack[0]; Coords in,loc,b,bsum={0,0,0},zero={0,0,0}; Rotation r; /*PROP_MAGNET takes care of transforming local "PROP" coordinates to lab system*/ in.x=x;in.y=y;in.z=z; int i=0,stat=1; p=stack[i]; *bx=0;*by=0;*bz=0; if (!p) return 0; //mcmagnet_print_stack(); //printf("getfield_(lab):_(xyz,t)=( %g %g %g %g )\n",x,y,z,t); while(p){ /*transform to the coordinate system of the particular magnetic function*/ loc=coords_sub(rot_apply(*(p->rot),in),*(p->pos)); stat=(p->func) (loc.x,loc.y,loc.z,t,&(b.x),&(b.y),&(b.z),p->data); /*check if the field function should be garbage collected*/ //printf("getfield_(loc):_(xyz,t)=( %g %g %g %g )\n",loc.x,loc.y,loc.z,t); if (stat){ /*transform to the lab system and add up. (resusing loc variable - to now contain the field in lab coords)*/ rot_transpose(*(p->rot),r); loc=rot_apply(r,b); bsum.x+=loc.x;bsum.y+=loc.y;bsum.z+=loc.z; //printf("Bs=(%g %g %g), B=(%g %g %g)\n",bsum.x,bsum.y,bsum.z,loc.x,loc.y,loc.z); } if (p->stop) break; p=stack[++i]; } /*we now have the magnetic field in lab coords in loc., transfer it back to caller*/ *bx=bsum.x; *by=bsum.y; *bz=bsum.z; return 1; }
/** * 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 * FIXME XXX TODO an issue arises as the center changes with part_rotate * FIXME XXX TODO the view callback needs to compensate this somehow */ static void part_rotate (ItemData *data, int angle, Coords *center_pos) { cairo_matrix_t affine; double x, y; Part *part; PartPriv *priv; int i, tot_rotation; Coords b1, b2; Coords part_center_before, part_center_after, delta; Coords delta_cp_before, delta_cp_after; gboolean handler_connected; g_return_if_fail (data); g_return_if_fail (IS_PART (data)); if (angle == 0) return; part = PART (data); priv = part->priv; tot_rotation = (priv->rotation + angle + 360) % 360; NG_DEBUG ("rotation: angle=%i tot_rotation=%i", angle, tot_rotation); // use the cairo matrix funcs to transform the pin // positions relative to the item center // this is only indirectly related to displaying cairo_matrix_init_rotate (&affine, (double)angle * M_PI / 180.); if (center_pos) { delta_cp_before = coords_sub (&part_center_before, center_pos); delta_cp_after = delta_cp_before; cairo_matrix_transform_point (&affine, &delta_cp_after.x, &delta_cp_after.y); } priv->rotation = tot_rotation; angle = tot_rotation; // Rotate 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; } // Rotate the bounding box, recenter to old center item_data_get_relative_bbox (ITEM_DATA (part), &b1, &b2); part_center_before = coords_average (&b1, &b2); 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); part_center_after = coords_average (&b1, &b2); delta = coords_sub (&part_center_before, &part_center_after); if (center_pos) { Coords diff = coords_sub (&delta_cp_after, &delta_cp_before); coords_add (&delta, &diff); } item_data_move (data, &delta); item_data_snap (data); handler_connected = g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA (part)->rotated_handler_id); if (handler_connected) { g_signal_emit_by_name (G_OBJECT (part), "rotated", tot_rotation); } handler_connected = g_signal_handler_is_connected (G_OBJECT (part), ITEM_DATA (part)->changed_handler_id); if (handler_connected) { g_signal_emit_by_name (G_OBJECT (part), "changed"); } }
static void wire_rotate (ItemData *data, int angle, Coords *center_pos) { cairo_matrix_t affine; double x, y; Wire *wire; WirePriv *priv; Coords b1, b2; Coords wire_center_before, wire_center_after, delta; Coords delta_cp_before, delta_cp_after; g_return_if_fail (data != NULL); g_return_if_fail (IS_WIRE (data)); if (angle == 0) return; wire = WIRE (data); item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2); wire_center_before = coords_average (&b1, &b2); priv = wire->priv; if (priv->direction == WIRE_DIR_VERT) { priv->direction = WIRE_DIR_HORIZ; } else if (priv->direction == WIRE_DIR_HORIZ) { priv->direction = WIRE_DIR_VERT; } cairo_matrix_init_rotate (&affine, (double)angle * M_PI / 180.0); // Rotate the wire's end point. x = priv->length.x; y = priv->length.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->length.x = x; priv->length.y = y; if (center_pos) { delta_cp_before = coords_sub (&wire_center_before, center_pos); delta_cp_after = delta_cp_before; cairo_matrix_transform_point (&affine, &delta_cp_after.x, &delta_cp_after.y); } // Update bounding box. wire_update_bbox (wire); item_data_get_absolute_bbox (ITEM_DATA (wire), &b1, &b2); wire_center_after = coords_average (&b1, &b2); delta = coords_sub (&wire_center_before, &wire_center_after); if (center_pos) { Coords diff = coords_sub (&delta_cp_after, &delta_cp_before); coords_add (&delta, &diff); } item_data_move (ITEM_DATA (wire), &delta); // item_data_snap (ITEM_DATA (wire), NULL); //FIXME XXX // Let the views (canvas items) know about the rotation. g_signal_emit_by_name (G_OBJECT (wire), "rotated", angle); // legacy g_signal_emit_by_name (G_OBJECT (wire), "changed"); }
// Event handler for a "floating" group of objects. int sheet_item_floating_event (Sheet *sheet, const GdkEvent *event) { SheetPriv *priv; GList *list; static gboolean keep = FALSE; // Remember the start position of the mouse cursor. static Coords last = {0., 0.}; // Mouse cursor position in window coordinates, snapped to the grid spacing. static Coords snapped = {0., 0.}; // Move the selected item(s) by this movement. Coords delta = {0., 0.}; g_return_val_if_fail (sheet != NULL, FALSE); g_return_val_if_fail (IS_SHEET (sheet), FALSE); g_return_val_if_fail (sheet->priv->floating_objects != NULL, FALSE); priv = sheet->priv; switch (event->type) { case GDK_BUTTON_RELEASE: g_signal_stop_emission_by_name (sheet, "event"); break; case GDK_BUTTON_PRESS: if (sheet->state != SHEET_STATE_FLOAT) return TRUE; switch (event->button.button) { case 2: case 4: case 5: return FALSE; case 1: // do not free the floating items, but use them like a stamp keep = event->button.state & GDK_CONTROL_MASK; // Continue adding if CTRL is pressed if (!keep) { sheet->state = SHEET_STATE_NONE; g_signal_stop_emission_by_name (sheet, "event"); if (g_signal_handler_is_connected (sheet, sheet->priv->float_handler_id)) g_signal_handler_disconnect (sheet, sheet->priv->float_handler_id); sheet->priv->float_handler_id = 0; } // FIXME assert that `Coords current` has been set by now! for (list = priv->floating_objects; list; list = list->next) { SheetItem *floating_item; ItemData *floating_data; // Create a real item. floating_item = list->data; if (!keep) { floating_data = sheet_item_get_data (floating_item); g_object_set (floating_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); } else { // FIXME the bounding box of the clone is wrong floating_data = item_data_clone (sheet_item_get_data (floating_item)); } g_object_ref (G_OBJECT (floating_data)); NG_DEBUG ("Item Data Pos will be %lf %lf", snapped.x, snapped.y) item_data_set_pos (floating_data, &snapped); item_data_snap (floating_data, sheet->grid); schematic_add_item (schematic_view_get_schematic_from_sheet (sheet), floating_data); if (!keep) g_object_unref (G_OBJECT (floating_item)); } if (keep) { g_object_set (G_OBJECT (priv->floating_group), "x", snapped.x, "y", snapped.y, NULL); } else { g_list_free (priv->floating_objects); priv->floating_objects = NULL; } break; case 3: // Cancel the "float-placement" for button-3 clicks. g_signal_stop_emission_by_name (sheet, "event"); sheet_item_cancel_floating (sheet); break; } break; case GDK_2BUTTON_PRESS: case GDK_3BUTTON_PRESS: g_signal_stop_emission_by_name (sheet, "event"); return TRUE; case GDK_MOTION_NOTIFY: // keep track of the position, as `sheet_get_pointer*()` does not work // in other events than MOTION_NOTIFY #if 0 { Coords tmp; last = current; if (sheet_get_pointer (sheet, &tmp.x, &tmp.y)) { snapped_current = current = tmp; snap_to_grid (sheet->grid, &snapped_current.x, &snapped_current.y); } } #endif if (sheet->state != SHEET_STATE_FLOAT && sheet->state != SHEET_STATE_FLOAT_START) return FALSE; g_signal_stop_emission_by_name (sheet, "event"); // Get pointer position independantly of the zoom if (sheet->state == SHEET_STATE_FLOAT_START) { sheet->state = SHEET_STATE_FLOAT; last.x = last.y = 0.; // Reparent the selected objects so that we can move them // efficiently. for (list = priv->floating_objects; list; list = list->next) { sheet_item_reparent (SHEET_ITEM (list->data), priv->floating_group); // Set the floating item visible g_object_set (G_OBJECT (list->data), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); } #if 0 GooCanvasBounds box; goo_canvas_item_get_bounds (priv->floating_group, &box); #endif NG_DEBUG ("\n\n\nFLOAT ### START\n\n\n\n"); } sheet_get_pointer_snapped (sheet, &snapped.x, &snapped.y); delta = coords_sub (&snapped, &last); NG_DEBUG ("drag floating current sx=%lf sy=%lf \n", snapped.x, snapped.y); NG_DEBUG ("drag floating last lx=%lf ly=%lf \n", last.x, last.y); NG_DEBUG ("drag floating delta -> dx=%lf dy=%lf \n", delta.x, delta.y); #if !FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK last = snapped; #else goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->floating_group), NULL); #endif goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), delta.x, delta.y); break; case GDK_KEY_PRESS: switch (event->key.keyval) { case GDK_KEY_r: case GDK_KEY_R: { Coords bbdelta; GooCanvasBounds bounds; // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); bbdelta.x = (bounds.x2 - bounds.x1) / 2.; bbdelta.y = (bounds.y2 - bounds.y1) / 2.; sheet_rotate_ghosts (sheet); // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->floating_group), &bounds); bbdelta.x -= (bounds.x2 - bounds.x1) / 2.; bbdelta.y -= (bounds.y2 - bounds.y1) / 2.; snap_to_grid (sheet->grid, &bbdelta.x, &bbdelta.y); goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->floating_group), bbdelta.x, bbdelta.y); } break; default: return FALSE; } default: return FALSE; } return TRUE; }
// Event handler for a SheetItem gboolean sheet_item_event (GooCanvasItem *sheet_item, GooCanvasItem *sheet_target_item, GdkEvent *event, Sheet *sheet) { // Remember the last position of the mouse cursor. GooCanvas *canvas; SheetPriv *priv; GList *list; static Coords last, current, snapped; // snapped : Mouse cursor position in window coordinates, snapped to the grid // spacing. // delta : Move the selected item(s) by this movement. Coords delta; g_return_val_if_fail (sheet_item != NULL, FALSE); g_return_val_if_fail (sheet != NULL, FALSE); priv = sheet->priv; canvas = GOO_CANVAS (sheet); switch (event->type) { case GDK_BUTTON_PRESS: // Grab focus to sheet for correct use of events gtk_widget_grab_focus (GTK_WIDGET (sheet)); switch (event->button.button) { case 1: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); sheet->state = SHEET_STATE_DRAG_START; g_assert (sheet_get_pointer (sheet, &last.x, &last.y)); break; case 3: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); if (sheet->state != SHEET_STATE_NONE) return TRUE; // Bring up a context menu for right button clicks. if (!SHEET_ITEM (sheet_item)->priv->selected && !((event->button.state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)) sheet_select_all (sheet, FALSE); sheet_item_select (SHEET_ITEM (sheet_item), TRUE); sheet_item_run_menu (SHEET_ITEM (sheet_item), sheet, (GdkEventButton *)event); break; default: return FALSE; } break; case GDK_2BUTTON_PRESS: // Do not interfere with object dragging. if (sheet->state == SHEET_STATE_DRAG) return FALSE; switch (event->button.button) { case 1: if (sheet->state == SHEET_STATE_DRAG_START) sheet->state = SHEET_STATE_NONE; g_signal_stop_emission_by_name (sheet_item, "button_press_event"); g_signal_emit_by_name (sheet_item, "double_clicked"); break; default: return FALSE; } break; case GDK_3BUTTON_PRESS: g_signal_stop_emission_by_name (sheet_item, "button_press_event"); return TRUE; case GDK_BUTTON_RELEASE: switch (event->button.button) { case 1: if (sheet->state != SHEET_STATE_DRAG && sheet->state != SHEET_STATE_DRAG_START) return TRUE; g_signal_stop_emission_by_name (sheet_item, "button-release-event"); if (sheet->state == SHEET_STATE_DRAG_START) { sheet->state = SHEET_STATE_NONE; if (!(event->button.state & GDK_SHIFT_MASK)) sheet_select_all (sheet, FALSE); if (IS_SHEET_ITEM (sheet_item)) sheet_item_select (SHEET_ITEM (sheet_item), TRUE); return TRUE; } // Get the mouse motion g_assert (sheet_get_pointer (sheet, &snapped.x, &snapped.y)); delta = coords_sub (&snapped, &last); sheet->state = SHEET_STATE_NONE; goo_canvas_pointer_ungrab (canvas, GOO_CANVAS_ITEM (sheet_item), event->button.time); // Reparent the selected objects to the normal group // to have correct behaviour for (list = priv->selected_objects; list; list = list->next) { sheet_item_reparent (SHEET_ITEM (list->data), sheet->object_group); } for (list = priv->selected_objects; list; list = list->next) { ItemData *item_data; item_data = SHEET_ITEM (list->data)->priv->data; item_data_move (item_data, &delta); item_data_snap (item_data, sheet->grid); item_data_register (item_data); } break; } case GDK_KEY_PRESS: switch (event->key.keyval) { case GDK_KEY_r: { #ifndef FIXME_STILL_MINI_OFFSET Coords bbdelta; GooCanvasBounds bounds; // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->selected_group), &bounds); bbdelta.x = (bounds.x2 - bounds.x1) / 2.; bbdelta.y = (bounds.y2 - bounds.y1) / 2.; #endif sheet_rotate_selection (sheet, 90); #ifndef FIXME_STILL_MINI_OFFSET // Center the objects around the mouse pointer. goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (priv->selected_group), &bounds); bbdelta.x -= (bounds.x2 - bounds.x1) / 2.; bbdelta.y -= (bounds.y2 - bounds.y1) / 2.; snap_to_grid (sheet->grid, &bbdelta.x, &bbdelta.y); goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), bbdelta.x, bbdelta.y); #endif } break; default: return FALSE; } return TRUE; case GDK_MOTION_NOTIFY: if (sheet->state != SHEET_STATE_DRAG && sheet->state != SHEET_STATE_DRAG_START) return FALSE; if (sheet->state == SHEET_STATE_DRAG_START) { sheet->state = SHEET_STATE_DRAG; // Update the selection if needed. if (IS_SHEET_ITEM (sheet_item) && (!SHEET_ITEM (sheet_item)->priv->selected)) { if (!(event->button.state & GDK_SHIFT_MASK)) { sheet_select_all (sheet, FALSE); } sheet_item_select (SHEET_ITEM (sheet_item), TRUE); } // Reparent the selected objects so that we can move them // efficiently. for (list = priv->selected_objects; list; list = list->next) { ItemData *item_data; item_data = SHEET_ITEM (list->data)->priv->data; item_data_unregister (item_data); sheet_item_reparent (SHEET_ITEM (list->data), priv->selected_group); } goo_canvas_pointer_grab (canvas, GOO_CANVAS_ITEM (sheet_item), GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, NULL, event->button.time); } // Set last_x & last_y to the pointer position sheet_get_pointer (sheet, &snapped.x, &snapped.y); delta = coords_sub (&snapped, &last); // Check that we don't move outside the sheet... // Horizontally: /* if (cx1 <= 0) { // leftmost edge dx = dx - x1; snap_to_grid (sheet->grid, &dx, NULL); snapped_x = last_x + dx; } else if (cx2 >= sheet_width) { // rightmost edge dx = dx - (x2 - sheet_width / priv->zoom); snap_to_grid (sheet->grid, &dx, NULL); snapped_x = last_x + dx; } // And vertically: if (cy1 <= 0) { // upper edge dy = dy - y1; snap_to_grid (sheet->grid, NULL, &dy); snapped_y = last_y + dy; } else if (cy2 >= sheet_height) { // lower edge dy = dy - (y2 - sheet_height / priv->zoom); snap_to_grid (sheet->grid, NULL, &dy); snapped_y = last_y + dy; } //last_x = snapped_x; //last_y = snapped_y; */ #if !FIXME_INCREMENTAL_MOVMENT_DOES_NOT_WORK last = snapped; #else goo_canvas_item_set_transform (GOO_CANVAS_ITEM (priv->selected_group), NULL); #endif goo_canvas_item_translate (GOO_CANVAS_ITEM (priv->selected_group), delta.x, delta.y); return TRUE; default: return FALSE; } return TRUE; }
/** * \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"); }