Using Edje Perspective
This tutorial demonstrates how you can create a 3D projection of a 2D object.
Edje Perspective is a graphical tool that makes 2D objects appear like they have a 3D appearance.
Edje Perspective can be used in all Edje objects to create and configure a perspective objects and to set the to an Edje object or a canvas, affecting all the objects within that have no specific perspective already defined.
Creating the Basic Application
The perspective application has buttons to move the Edje object and change its perspective. The Edje object is a 2D object that changes its position so that the perspective point can be applied.
Note |
---|
The loaded Edje object must have the perspective option enabled in its EDC source code. |
Create a simple Edje object that has 4 states: left-top, right-top, left-bottom, and right-bottom. Clicking a button emits signals into the object and makes the object move.
-
The following example implements the Edje object:
group { name: "example/group"; min: 480 320; parts { part { name: "bg"; type: RECT; mouse_events: 1; description { state: "default" 0.0; } } part { name: "rectangle"; type: RECT; mouse_events: 0; description { state: "default" 0.0; color: 255 0 0 128; rel1 { offset: -5 -5; to: "title"; } rel2 { offset: 4 4; to: "title"; } map { on: 1; perspective_on: 1; rotation { x: 45; y: 15; } } } } part { name: "title"; type: TEXT; mouse_events: 0; description { state: "default" 0.0; color: 200 200 200 255; align: 0.0 0.5; rel1.relative: 0.2 0.2; rel2.relative: 0.2 0.2; text { text: "Perspective example"; font: "Sans"; size: 16; min: 1 1; } map { on: 1; perspective_on: 1; rotation { x: 45; y: 15; } } } description { state: "right" 0.0; inherit: "default" 0.0; rel1.relative: 0.5 0.2; rel2.relative: 0.5 0.2; } description { state: "bottom" 0.0; inherit: "default" 0.0; rel1.relative: 0.2 0.8; rel2.relative: 0.2 0.8; } description { state: "bottomright" 0.0; inherit: "default" 0.0; rel1.relative: 0.5 0.8; rel2.relative: 0.5 0.8; } } } programs { program { name: "move,right"; signal: "move,1,0"; action: STATE_SET "right" 0.0; transition: SINUSOIDAL 1.0; target: "title"; after: "animation,end"; } program { name: "move,bottom"; signal: "move,0,1"; action: STATE_SET "bottom" 0.0; transition: SINUSOIDAL 1.0; target: "title"; after: "animation,end"; } program { name: "move,bottomright"; signal: "move,1,1"; action: STATE_SET "bottomright" 0.0; transition: SINUSOIDAL 1.0; target: "title"; after: "animation,end"; } program { name: "move,default"; signal: "move,0,0"; action: STATE_SET "default" 0.0; transition: SINUSOIDAL 1.0; target: "title"; after: "animation,end"; } program { name: "animation,end"; action: SIGNAL_EMIT "animation,end" ""; } } }
-
Create the application. Create a basic window widget that is going to be loaded and created in the main starting function:
// Window ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE); elm_win_conformant_set(ad->win, EINA_TRUE); elm_win_autodel_set(ad->win, EINA_TRUE); if (elm_win_wm_rotation_supported_get(ad->win)) { int rots[4] = { 0, 90, 180, 270 }; elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4); }
-
Add a conformant and set the main layout of the application.
The main layout is described in the EDC part and consists of 2 swallows. The first swallow has the Edje object loaded into it. The second contains a box of buttons to manipulate the perspective of the Edje object.
// Conformant ad->conform = elm_conformant_add(ad->win); elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW); elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE); evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(ad->win, ad->conform); evas_object_show(ad->conform); // Base layout app_get_resource(EDJ_FILE, edj_path, (int)PATH_MAX); ad->layout = elm_layout_add(ad->win); elm_layout_file_set(ad->layout, edj_path, GRP_MAIN); evas_object_size_hint_weight_set(ad->layout, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_size_hint_weight_set(ad->layout, EVAS_HINT_FILL, EVAS_HINT_FILL); eext_object_event_callback_add(ad->layout, EEXT_CALLBACK_BACK, layout_back_cb, ad); elm_object_content_set(ad->conform, ad->layout);
-
Create the Edje object on Evas using the evas_object_evas_get() function. This function is very useful because you must operate on the canvas, but have only an object pointer to a window object.
Evas *evas; evas = evas_object_evas_get(ad->win); ad->edje_obj = edje_object_add(evas);
-
Instantiate a new Edje object by creating a new Edje smart object and returning its Evas_Object handle:
ad->edje_obj = edje_object_add(evas);
-
An Edje object is useless without a source file set to it, so use the edje_object_file_set() function to set the object into the main layout:
ad->edje_obj = edje_object_add(evas); edje_object_file_set(ad->edje_obj, edj_path, "example/group"); evas_object_move(ad->edje_obj, 0, 0); evas_object_show(ad->edje_obj); elm_object_part_content_set(ad->layout, "swallow", ad->edje_obj);
Figure: Main layout with the swallow part highlighted
-
Create the actual perspective object, define its position, focal distance and Z plane position, and set it as global:
ad->ps = edje_perspective_new(evas); edje_perspective_set(ad->ps, 160, 320, 0, ad->focal); edje_perspective_global_set(ad->ps, EINA_TRUE);
To set the perspective of the Edje object, instead of setting it as global to the entire canvas, you can use the edje_object_perspective_set() function.
-
Create a new perspective in the canvas by setting up the transformation for the perspective object:
void edje_perspective_set(Edje_Perspective * ps, Evas_Coord px, Evas_Coord py, Evas_Coord z0, Evas_Coord foc)
This sets the parameters of the perspective transformation. X, Y, and Z values are used. The px and py points specify the infinite distance point in the 3D conversion, where all lines converge. The z0 point specifies the Z value at which there is a 1:1 mapping between spatial coordinates and screen coordinates. Any points on the Z value do not have their X and Y values modified in the transformation. The points further away (with a higher Z value) shrink into the distance, and those that are closer expand and become bigger.
The foc value determines the focal length of the camera. This is the distance between the camera lens plane and the z0 Z value. This allows for some depth control. The foc value must be greater than 0.
-
The Swallow buttons layout part contains a box of buttons to manipulate the perspective of the loaded Edje object.
Figure: Main layout with swallow buttons highlighted
Add the box and pack the buttons into it. Name the first button Global and register a callback for clicking:
Evas_Object *box, *button; // Adding box box = elm_box_add(ad->layout); evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, 0.0); evas_object_size_hint_align_set(box, EVAS_HINT_FILL, 0.0); elm_box_horizontal_set(box, EINA_TRUE); evas_object_show(box); elm_object_part_content_set(ad->layout, "swallow.buttons", box); button = elm_button_add(ad->layout); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_object_text_set(button, "move"); evas_object_show(button); elm_box_pack_end(box, button); evas_object_smart_callback_add(button, "clicked", _on_btn_clicked, ad); button = elm_button_add(ad->layout); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_object_text_set(button, "more"); evas_object_show(button); elm_box_pack_end(box, button); evas_object_smart_callback_add(button, "clicked", _on_btn_more_clicked, ad); button = elm_button_add(ad->layout); evas_object_size_hint_align_set(button, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(button, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_object_text_set(button, "less"); evas_object_show(button); elm_box_pack_end(box, button); evas_object_smart_callback_add(button, "clicked", _on_btn_less_clicked, ad); ad->edje_obj = edje_object_add(evas); edje_object_file_set(ad->edje_obj, edj_path, "example/group"); evas_object_move(ad->edje_obj, 0, 0); evas_object_show(ad->edje_obj); elm_object_part_content_set(ad->layout, "swallow", ad->edje_obj);
-
Show the main window:
evas_object_show(ad->win);
Figure: Main screen
Playing with the Perspective
-
The callback for a clicked button is converted to a signal determining where the text and rectangle must be moved:
static void _part_move(appdata_s *ad, int dx, int dy) { char emission[64]; dlog_print(DLOG_ERROR, "AAAA", "x,y = %d, %d", dx, dy); snprintf(emission, sizeof(emission), "move,%d,%d", dx, dy); edje_object_signal_emit(ad->edje_obj, emission, ""); }
-
The callback representing the Edje object move calls the part_move() function that directly sends a signal to the Edje part. The moving action is treated in the program according to the passed signal.
static void _on_btn_clicked(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; static int i = 0; switch (i) { case 0: _part_move(ad, 1, 0); break; case 1: _part_move(ad, 1, 1); break; case 2: _part_move(ad, 0, 1); break; case 3: _part_move(ad, 0, 0); break; } i++; i = i%4; }
Figure: Moving process
->
-
By clicking the More and Less buttons, you can increase or decrease the focal length of the camera:
static void _on_btn_more_clicked(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; ad->focal += 5; edje_perspective_set(ad->ps, 160, 320, 0, ad->focal); edje_perspective_global_set(ad->ps, EINA_TRUE); } static void _on_btn_less_clicked(void *data, Evas_Object *obj, void *event_info) { appdata_s *ad = data; ad->focal -= 5; edje_perspective_set(ad->ps, 160, 320, 0, ad->focal); edje_perspective_global_set(ad->ps, EINA_TRUE); }
Figure: Decreasing and increasing the focal length