static VALUE rg_set_simple_transform(VALUE self, VALUE x, VALUE y, VALUE scale, VALUE rotation) { goo_canvas_item_set_simple_transform(SELF(self), NUM2DBL(x), NUM2DBL(y), NUM2DBL(scale), NUM2DBL(rotation)); return self; }
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)); }
static void start_animation_clicked (GtkWidget *button, gpointer data) { /* Absolute. */ goo_canvas_item_set_simple_transform (ellipse1, 100, 100, 1, 0); goo_canvas_item_animate (ellipse1, 500, 100, 2, 720, TRUE, 2000, 40, GOO_CANVAS_ANIMATE_BOUNCE); goo_canvas_item_set_simple_transform (rect1, 100, 200, 1, 0); goo_canvas_item_animate (rect1, 100, 200, 1, 350, TRUE, 40 * 36, 40, GOO_CANVAS_ANIMATE_RESTART); goo_canvas_item_set_simple_transform (rect3, 200, 200, 1, 0); goo_canvas_item_animate (rect3, 200, 200, 3, 0, TRUE, 400, 40, GOO_CANVAS_ANIMATE_BOUNCE); /* Relative. */ goo_canvas_item_set_simple_transform (ellipse2, 100, 400, 1, 0); goo_canvas_item_animate (ellipse2, 400, 0, 2, 720, FALSE, 2000, 40, GOO_CANVAS_ANIMATE_BOUNCE); goo_canvas_item_set_simple_transform (rect2, 100, 500, 1, 0); goo_canvas_item_animate (rect2, 0, 0, 1, 350, FALSE, 40 * 36, 40, GOO_CANVAS_ANIMATE_RESTART); goo_canvas_item_set_simple_transform (rect4, 200, 500, 1, 0); goo_canvas_item_animate (rect4, 0, 0, 3, 0, FALSE, 400, 40, GOO_CANVAS_ANIMATE_BOUNCE); }
// This is called when the wire data was moved. Update the view accordingly. static void wire_moved_callback (ItemData *data, SheetPos *pos, SheetItem *item) { WireItem *wire_item; g_return_if_fail (data != NULL); g_return_if_fail (IS_ITEM_DATA (data)); g_return_if_fail (item != NULL); g_return_if_fail (IS_WIRE_ITEM (item)); if (pos == NULL) return; wire_item = WIRE_ITEM (item); // Move the canvas item and invalidate the bbox cache. goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (item), pos->x, pos->y, 1.0, 0.0); wire_item->priv->cache_valid = FALSE; }
static void update_preview (Browser *br) { LibraryPart *library_part; gdouble new_x, new_y, x1, y1, x2, y2; gdouble text_width; gdouble width, height; GooCanvasBounds bounds; gdouble scale; cairo_matrix_t transf, affine; gchar *part_name; char *description; GtkTreeModel *model; GtkTreeIter iter; GtkTreeSelection *selection; // 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_IS_TREE_SELECTION (selection)) return; 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); // If there is already a preview part-item, destroy its group and create a // new one. if (br->preview != NULL) { goo_canvas_item_remove (GOO_CANVAS_ITEM (br->preview)); } br->preview = GOO_CANVAS_GROUP (goo_canvas_group_new ( goo_canvas_get_root_item (GOO_CANVAS (br->canvas)), NULL)); goo_canvas_set_bounds (GOO_CANVAS (br->canvas), 0.0, 0.0, 250.0, 110.0); g_object_get (br->preview, "width", &width, "height", &height, NULL); if (!library_part) return; part_item_create_canvas_items_for_preview (br->preview, library_part); // Unconstraint the canvas width & height to adjust for the part description g_object_set (br->preview, "width", -1.0, "height", -1.0, NULL); // Get the coordonates */ goo_canvas_item_get_bounds (GOO_CANVAS_ITEM (br->preview), &bounds); x1 = bounds.x1; x2 = bounds.x2; y1 = bounds.y1; y2 = bounds.y2; // Translate in such a way that the canvas centre remains in (0, 0) cairo_matrix_init_translate (&transf, -(x2 + x1) / 2.0f + PREVIEW_WIDTH / 2, -(y2 + y1) / 2.0f + PREVIEW_HEIGHT / 2); // Compute the scale of the widget if ((x2 - x1 != 0) || (y2 - y1 != 0)) { if ((x2 - x1) < (y2 - y1)) scale = 0.60f * PREVIEW_HEIGHT / (y2 - y1); else scale = 0.60f * PREVIEW_WIDTH / (x2 - x1); } else scale = 5; cairo_matrix_init_scale (&affine, scale, scale); cairo_matrix_multiply (&transf, &transf, &affine); // Apply the transformation goo_canvas_item_set_transform (GOO_CANVAS_ITEM (br->preview), &transf); // Compute the motion to centre the Preview widget new_x = 100 + (PREVIEW_WIDTH - x1 - x2) / 2; new_y = (PREVIEW_HEIGHT - y1 - y2) / 2 - 10; // Apply the transformation if (scale > 5.0) scale = 3.0; goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->preview), new_x, new_y, scale, 0.0); description = g_strdup (library_part->description); wrap_string (description, 20); g_object_set (br->description, "text", description, NULL); g_free (description); g_object_get (G_OBJECT (br->description), "width", &text_width, NULL); goo_canvas_item_set_simple_transform (GOO_CANVAS_ITEM (br->description), 50.0, -20.0, 1.0, 0.0); g_free (part_name); }
/** * 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; }
/* ===================================================================== * Periodically recalculate some submarine parameters, with a larger delay * =====================================================================*/ static gboolean update_timeout_slow() { gdouble delta_assiette; if(!boardRootItem) return FALSE; if(board_paused) return TRUE; /* speed : don't reach instantly the ordered speed */ if (speed_ordered != submarine_horizontal_speed) { submarine_horizontal_speed += (speed_ordered-submarine_horizontal_speed)/10.0; if (fabs(speed_ordered - submarine_horizontal_speed) < 0.1) submarine_horizontal_speed = speed_ordered; } /* assiette */ delta_assiette = (ballast_ar_air - ballast_av_air)/200.0 + (barre_av_angle - barre_ar_angle)/5.0*submarine_horizontal_speed; assiette -= delta_assiette*UPDATE_DELAY/10000.0; if (assiette < -30.0) assiette = -30.0; if (assiette > 30.0) assiette = 30.0; /* If surfacing, diminish the 'assiette' */ if ( depth <= 5.0 + SURFACE_DEPTH) { assiette *= depth/(depth+1.0); } /* update some dynamic parameters */ /* resulting_weight > 0 ==> the sub goes deeper regleur : this is the qty of water */ resulting_weight = weight - ballast_av_air - ballast_ar_air + regleur; submarine_vertical_speed = resulting_weight/300.0 + submarine_horizontal_speed*sin(DEG_TO_RAD(-assiette)); /* if depth rudders are in the same direction */ if (barre_ar_angle != 0.0 && barre_av_angle != 0.0) { if (fabs(barre_ar_angle)/barre_ar_angle == fabs(barre_av_angle)/barre_av_angle) { gdouble a = (fabs(barre_ar_angle) > fabs(barre_av_angle)) ? barre_av_angle : barre_ar_angle; submarine_vertical_speed += a * submarine_horizontal_speed/30.0; } } /* position & depth */ submarine_x += submarine_horizontal_speed * cos(DEG_TO_RAD(assiette)) * UPDATE_DELAY_SLOW/1000.0; depth += submarine_vertical_speed * UPDATE_DELAY_SLOW/1000.0; if (depth < SURFACE_DEPTH) depth = SURFACE_DEPTH; if (depth > MAX_DEPTH) depth = MAX_DEPTH; // show an alert if some parameters reach the limit if (depth >= MAX_DEPTH-20.0 || assiette == -30.0 || assiette == 30.0 || air == 0.0 || battery == 0.0) g_object_set (alert_submarine, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); else g_object_set (alert_submarine, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); /* if the submarine dives, stop charging air tanks and batteries */ if ( depth >= SURFACE_DEPTH+10.0 ) { if (air_charging) { air_charging = FALSE; gc_item_rotate_with_center(air_compressor_item, 0, TRIGGER_CENTER_X, TRIGGER_CENTER_Y ); } if (battery_charging) { battery_charging = FALSE; gc_item_rotate_with_center(battery_charger_item, 0, TRIGGER_CENTER_X, TRIGGER_CENTER_Y ); } } /* Check the submarine passed the right door */ if ( submarine_x > WRAP_X && !gamewon ) { /* Check its within the gate range */ GooCanvasBounds bounds; goo_canvas_item_get_bounds (submarine_item, &bounds); guint antena_height = 30; if(bounds.y1 + antena_height < gate_top_current_y || bounds.y2 > gate_bottom_y) { /* It's a crash */ submarine_explosion(); } else { gamewon = TRUE; ok(); } } /* Open the door */ if(treasure_captured && gate_top_current_y > gate_top_y) open_door(); /* display the submarine */ goo_canvas_item_set_simple_transform(submarine_item, submarine_x - submarine_width/2.0, depth + SURFACE_DEPTH - submarine_height/2.0, 1, -assiette); /* the frigate */ if ( frigate_item ) { /* detects a collision between the frigate and the submarine */ if (depth <= 30.0 && !submarine_destroyed) { GooCanvasBounds bounds; goo_canvas_item_get_bounds(frigate_item, &bounds); gdouble frigate_x = bounds.x1 + (bounds.x2 - bounds.x1) / 2; if ( abs(submarine_x - frigate_x) < 100 ) { submarine_explosion(); return TRUE; } } } /* whale detection */ { gdouble dist1 = hypot( submarine_x - whale_x, depth - whale_y); if ( ( dist1 < WHALE_DETECTION_RADIUS ) && !submarine_destroyed ) { g_object_set (whale, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); g_object_set (big_explosion, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); submarine_explosion(); return TRUE; } } /* treasure detection */ { gdouble dist1 = hypot( submarine_x - treasure_x, depth - treasure_y); if ( (dist1 < TREASURE_DETECTION_RADIUS) && !treasure_captured ) { gc_sound_play_ogg("sounds/tuxok.wav", NULL); g_object_set (treasure, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); treasure_captured = TRUE; open_door(); } } return TRUE; }
/* ===================================================================== * Periodically recalculate some submarine parameters, with a larger delay * =====================================================================*/ static gboolean update_timeout_slow() { gdouble delta_assiette; if(!boardRootItem) return FALSE; if(board_paused) return TRUE; /* speed : don't reach instantly the ordered speed */ if (speed_ordered != submarine_horizontal_speed) { submarine_horizontal_speed += (speed_ordered-submarine_horizontal_speed)/10.0; if (fabs(speed_ordered - submarine_horizontal_speed) < 0.1) submarine_horizontal_speed = speed_ordered; } /* assiette */ delta_assiette = (ballast_ar_air - ballast_av_air)/200.0 + (barre_av_angle - barre_ar_angle)/5.0*submarine_horizontal_speed; assiette -= delta_assiette*UPDATE_DELAY/10000.0; if (assiette < -30.0) assiette = -30.0; if (assiette > 30.0) assiette = 30.0; /* If surfacing, diminish the 'assiette' */ if ( depth <= 5.0 + SURFACE_DEPTH) { assiette *= depth/(depth+1.0); } /* update some dynamic parameters */ /* resulting_weight > 0 ==> the sub goes deeper regleur : this is the qty of water */ resulting_weight = weight - ballast_av_air - ballast_ar_air + regleur; submarine_vertical_speed = resulting_weight/300.0 + submarine_horizontal_speed*sin(DEG_TO_RAD(-assiette)); /* if depth rudders are in the same direction */ if (barre_ar_angle != 0.0 && barre_av_angle != 0.0) { if (fabs(barre_ar_angle)/barre_ar_angle == fabs(barre_av_angle)/barre_av_angle) { gdouble a = (fabs(barre_ar_angle) > fabs(barre_av_angle)) ? barre_av_angle : barre_ar_angle; submarine_vertical_speed += a * submarine_horizontal_speed/30.0; } } /* position & depth */ submarine_x += submarine_horizontal_speed * cos(DEG_TO_RAD(assiette)) * UPDATE_DELAY_SLOW/1000.0; depth += submarine_vertical_speed * UPDATE_DELAY_SLOW/1000.0; if (depth < SURFACE_DEPTH) depth = SURFACE_DEPTH; if (depth > MAX_DEPTH) depth = MAX_DEPTH; // show an alert if some parameters reach the limit if (depth >= MAX_DEPTH-20.0 || assiette == -30.0 || assiette == 30.0 || air == 0.0 || battery == 0.0) g_object_set (alert_submarine, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); else g_object_set (alert_submarine, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); /* if the submarine dives, stop charging air tanks and batteries */ if ( depth >= SURFACE_DEPTH+10.0 ) { if (air_charging) { air_charging = FALSE; gc_item_rotate_with_center(air_compressor_item, 0, TRIGGER_CENTER_X, TRIGGER_CENTER_Y ); } if (battery_charging) { battery_charging = FALSE; gc_item_rotate_with_center(battery_charger_item, 0, TRIGGER_CENTER_X, TRIGGER_CENTER_Y ); } } /* if the submarine is too close from right, put it on the left */ if ( submarine_x > WRAP_X ) { /* Check its within the gate range */ GooCanvasBounds bounds; goo_canvas_item_get_bounds (submarine_item, &bounds); if(bounds.y1 < gate_top_current_y || bounds.y2 > gate_bottom_y) { /* It's a crash */ submarine_explosion(); } else { gamewon = TRUE; /* Let the user play indefinitly at level 3 */ if(gcomprisBoard->level<3) gc_bonus_display(gamewon, GC_BONUS_SMILEY); else { submarine_x = SUBMARINE_INITIAL_X; depth = SUBMARINE_INITIAL_DEPTH; } } } /* Open the door */ if(treasure_captured && gate_top_current_y > gate_top_y) open_door(); /* display the submarine */ goo_canvas_item_set_simple_transform(submarine_item, submarine_x - submarine_width/2.0, depth + SURFACE_DEPTH - submarine_height/2.0, 1, -assiette); /* the frigate */ { GooCanvasBounds bounds; goo_canvas_item_get_bounds(frigate_item, &bounds); //goo_canvas_item_translate(frigate_item, - FRIGATE_SPEED * UPDATE_DELAY_SLOW/1000.0, 0.0); /* detects a collision between the frigate and the submarine */ if (depth <= 30.0 && !submarine_destroyed) if ( (submarine_x - submarine_width <= bounds.x1 && submarine_x >= bounds.x2) || (submarine_x - submarine_width >= bounds.x1 && submarine_x - submarine_width <= bounds.x2) || (submarine_x >= bounds.x1 && submarine_x <= bounds.x2) ) { submarine_explosion(); } /* wraps the destroyer if it reached the left side (and disappeared for a long time)*/ //if (bounds.x2 < -300.0) //gc_item_absolute_move( frigate_item, BOARDWIDTH, bounds.y1 ); } /* whale detection */ { gdouble dist1, dist2, dist3; dist1 = hypot( submarine_x -submarine_width/2 -whale_x, depth+SURFACE_IN_BACKGROUND-whale_y); dist2 = hypot(submarine_x - submarine_width - whale_x, depth+SURFACE_IN_BACKGROUND-whale_y); dist3 = hypot(submarine_x - whale_x, depth+SURFACE_IN_BACKGROUND-whale_y); /* magnetic detection (dist1) or collision with the whale (dist2 & dist3) */ if ( (dist1 < WHALE_DETECTION_RADIUS || dist2 < WHALE_DETECTION_RADIUS || dist3 < WHALE_DETECTION_RADIUS) && !submarine_destroyed ) { g_object_set (whale, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); g_object_set (big_explosion, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL); submarine_explosion(); } } /* treasure detection */ { gdouble dist1, dist2, dist3; dist1 = hypot( submarine_x -submarine_width/2 -treasure_x, depth+SURFACE_IN_BACKGROUND-treasure_y); dist2 = hypot(submarine_x - submarine_width - treasure_x, depth+SURFACE_IN_BACKGROUND-treasure_y); dist3 = hypot(submarine_x - treasure_x, depth+SURFACE_IN_BACKGROUND-treasure_y); /* magnetic detection (dist1) or collision with the treasure (dist2 & dist3) */ if ( (dist1 < TREASURE_DETECTION_RADIUS || dist2 < TREASURE_DETECTION_RADIUS || dist3 < TREASURE_DETECTION_RADIUS) && !treasure_captured ) { gc_sound_play_ogg("sounds/tuxok.wav", NULL); g_object_set (treasure, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL); treasure_captured = TRUE; open_door(); } } return TRUE; }