/* Copyright (c) 2011 by Simon Schneegans This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ using GLib.Math; namespace GnomePie { ///////////////////////////////////////////////////////////////////////// /// Renders the center of a Pie. ///////////////////////////////////////////////////////////////////////// public class CenterRenderer : GLib.Object { ///////////////////////////////////////////////////////////////////// /// The PieRenderer which owns this CenterRenderer. ///////////////////////////////////////////////////////////////////// private unowned PieRenderer parent; ///////////////////////////////////////////////////////////////////// /// The caption drawn in the center. Changes when the active slice /// changes. ///////////////////////////////////////////////////////////////////// private unowned Image? caption; ///////////////////////////////////////////////////////////////////// /// The color of the currently active slice. Used to colorize layers. ///////////////////////////////////////////////////////////////////// private Color color; ///////////////////////////////////////////////////////////////////// /// Two AnimatedValues: alpha is for global transparency (when /// fading in/out), activity is 1.0 if there is an active slice and /// 0.0 if there is no active slice. ///////////////////////////////////////////////////////////////////// private AnimatedValue activity; private AnimatedValue alpha; ///////////////////////////////////////////////////////////////////// /// C'tor, initializes all members. ///////////////////////////////////////////////////////////////////// public CenterRenderer(PieRenderer parent) { this.parent = parent; this.activity = new AnimatedValue.linear(0.0, 0.0, Config.global.theme.transition_time); this.alpha = new AnimatedValue.linear(0.0, 1.0, Config.global.theme.fade_in_time); this.color = new Color(); this.caption = null; } ///////////////////////////////////////////////////////////////////// /// Initiates the fade-out animation by resetting the targets of the /// AnimatedValues to 0.0. ///////////////////////////////////////////////////////////////////// public void fade_out() { this.activity.reset_target(0.0, Config.global.theme.fade_out_time); this.alpha.reset_target(0.0, Config.global.theme.fade_out_time); } ///////////////////////////////////////////////////////////////////// /// Should be called if the active slice of the PieRenderer changes. /// The members activity, caption and color are set accordingly. ///////////////////////////////////////////////////////////////////// public void set_active_slice(SliceRenderer? active_slice) { if (active_slice == null) { this.activity.reset_target(0.0, Config.global.theme.transition_time); } else { this.activity.reset_target(1.0, Config.global.theme.transition_time); this.caption = active_slice.caption; this.color = active_slice.color; } } ///////////////////////////////////////////////////////////////////// /// Draws all center layers and the caption. ///////////////////////////////////////////////////////////////////// public void draw(double frame_time, Cairo.Context ctx, double angle, double distance) { // get all center_layers var layers = Config.global.theme.center_layers; // update the AnimatedValues this.activity.update(frame_time); this.alpha.update(frame_time); // draw each layer foreach (var layer in layers) { ctx.save(); // calculate all values needed for animation/drawing double active_speed = (layer.active_rotation_mode == CenterLayer.RotationMode.TO_MOUSE) ? 0.0 : layer.active_rotation_speed; double inactive_speed = (layer.inactive_rotation_mode == CenterLayer.RotationMode.TO_MOUSE) ? 0.0 : layer.inactive_rotation_speed; double max_scale = layer.active_scale*this.activity.val + layer.inactive_scale*(1.0-this.activity.val); double max_alpha = layer.active_alpha*this.activity.val + layer.inactive_alpha*(1.0-this.activity.val); double colorize = ((layer.active_colorize == true) ? this.activity.val : 0.0) + ((layer.inactive_colorize == true) ? 1.0 - this.activity.val : 0.0); double max_rotation_speed = active_speed*this.activity.val + inactive_speed*(1.0-this.activity.val); CenterLayer.RotationMode rotation_mode = ((this.activity.val > 0.5) ? layer.active_rotation_mode : layer.inactive_rotation_mode); if (rotation_mode == CenterLayer.RotationMode.TO_MOUSE) { double diff = angle-layer.rotation; max_rotation_speed = layer.active_rotation_speed*this.activity.val + layer.inactive_rotation_speed*(1.0-this.activity.val); double smoothy = fabs(diff) < 0.9 ? fabs(diff) + 0.1 : 1.0; double step = max_rotation_speed*frame_time*smoothy; if (fabs(diff) <= step || fabs(diff) >= 2.0*PI - step) layer.rotation = angle; else { if ((diff > 0 && diff < PI) || diff < -PI) layer.rotation += step; else layer.rotation -= step; } } else if (rotation_mode == CenterLayer.RotationMode.TO_ACTIVE) { max_rotation_speed *= this.activity.val; double slice_angle = parent.slice_count() > 0 ? 2*PI/parent.slice_count() : 0; double direction = (int)((angle+0.5*slice_angle) / (slice_angle))*slice_angle; double diff = direction-layer.rotation; double step = max_rotation_speed*frame_time; if (fabs(diff) <= step || fabs(diff) >= 2.0*PI - step) layer.rotation = direction; else { if ((diff > 0 && diff < PI) || diff < -PI) layer.rotation += step; else layer.rotation -= step; } } else layer.rotation += max_rotation_speed*frame_time; layer.rotation = fmod(layer.rotation+2*PI, 2*PI); if (colorize > 0.0) ctx.push_group(); // transform the context ctx.rotate(layer.rotation); ctx.scale(max_scale, max_scale); // paint the layer layer.image.paint_on(ctx, this.alpha.val*max_alpha); // colorize it, if necessary if (colorize > 0.0) { ctx.set_operator(Cairo.Operator.ATOP); ctx.set_source_rgb(this.color.r, this.color.g, this.color.b); ctx.paint_with_alpha(colorize); ctx.set_operator(Cairo.Operator.OVER); ctx.pop_group_to_source(); ctx.paint(); } ctx.restore(); } // draw caption if (Config.global.theme.caption && caption != null && this.activity.val > 0) { ctx.save(); ctx.identity_matrix(); int pos = this.parent.size/2; ctx.translate(pos, (int)(Config.global.theme.caption_position) + pos); caption.paint_on(ctx, this.activity.val*this.alpha.val); ctx.restore(); } } } }