////////////////////////////////////////////////////////////////////////////////
//
//  Licensed to the Apache Software Foundation (ASF) under one or more
//  contributor license agreements.  See the NOTICE file distributed with
//  this work for additional information regarding copyright ownership.
//  The ASF licenses this file to You under the Apache License, Version 2.0
//  (the "License"); you may not use this file except in compliance with
//  the License.  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package mx.utils
{

import mx.display.Graphics;

/**
 *  The Graphics class is an all-static class with utility methods
 *  related to the Graphics class.
 *  You do not create instances of GraphicsUtil;
 *  instead you simply call methods such as the
 *  <code>GraphicsUtil.drawRoundRectComplex()</code> method.
 *  
 *  @langversion 3.0
 *  @playerversion Flash 9
 *  @playerversion AIR 1.1
 *  @productversion Flex 3
 */
public class GraphicsUtil
{
	//include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class methods
	//
	//--------------------------------------------------------------------------
    
    /**
     * Draws a rounded rectangle using the size of a radius to draw the rounded corners. 
     * You must set the line style, fill, or both 
     * on the Graphics object before 
     * you call the <code>drawRoundRectComplex()</code> method 
     * by calling the <code>linestyle()</code>, 
     * <code>lineGradientStyle()</code>, <code>beginFill()</code>, 
     * <code>beginGradientFill()</code>, or 
     * <code>beginBitmapFill()</code> method.
     * 
     * @param graphics The Graphics object that draws the rounded rectangle.
     *
     * @param x The horizontal position relative to the 
     * registration point of the parent display object, in pixels.
     * 
     * @param y The vertical position relative to the 
     * registration point of the parent display object, in pixels.
     * 
     * @param width The width of the round rectangle, in pixels.
     * 
     * @param height The height of the round rectangle, in pixels.
     * 
     * @param topLeftRadius The radius of the upper-left corner, in pixels.
     * 
     * @param topRightRadius The radius of the upper-right corner, in pixels.
     * 
     * @param bottomLeftRadius The radius of the bottom-left corner, in pixels.
     * 
     * @param bottomRightRadius The radius of the bottom-right corner, in pixels.
     *
     *  
     *  @langversion 3.0
     *  @playerversion Flash 9
     *  @playerversion AIR 1.1
     *  @productversion Flex 3
     */
    public static function drawRoundRectComplex(graphics:Graphics, x:Number, y:Number, 
                                                width:Number, height:Number, 
                                                topLeftRadius:Number, topRightRadius:Number, 
                                                bottomLeftRadius:Number, bottomRightRadius:Number):void
    {
        var xw:Number = x + width;
        var yh:Number = y + height;
        
        // Make sure none of the radius values are greater than w/h.
        // These are all inlined to avoid function calling overhead
        var minSize:Number = width < height ? width * 2 : height * 2;
        topLeftRadius = topLeftRadius < minSize ? topLeftRadius : minSize;
        topRightRadius = topRightRadius < minSize ? topRightRadius : minSize;
        bottomLeftRadius = bottomLeftRadius < minSize ? bottomLeftRadius : minSize;
        bottomRightRadius = bottomRightRadius < minSize ? bottomRightRadius : minSize;
        
        // Math.sin and Math,tan values for optimal performance.
        // Math.rad = Math.PI / 180 = 0.0174532925199433
        // r * Math.sin(45 * Math.rad) =  (r * 0.707106781186547);
        // r * Math.tan(22.5 * Math.rad) = (r * 0.414213562373095);
        //
        // We can save further cycles by precalculating
        // 1.0 - 0.707106781186547 = 0.292893218813453 and
        // 1.0 - 0.414213562373095 = 0.585786437626905
        
        // bottom-right corner
        var a:Number = bottomRightRadius * 0.292893218813453;		// radius - anchor pt;
        var s:Number = bottomRightRadius * 0.585786437626905; 	// radius - control pt;
        graphics.moveTo(xw, yh - bottomRightRadius);
        graphics.curveTo(xw, yh - s, xw - a, yh - a);
        graphics.curveTo(xw - s, yh, xw - bottomRightRadius, yh);
        
        // bottom-left corner
        a = bottomLeftRadius * 0.292893218813453;
        s = bottomLeftRadius * 0.585786437626905;
        graphics.lineTo(x + bottomLeftRadius, yh);
        graphics.curveTo(x + s, yh, x + a, yh - a);
        graphics.curveTo(x, yh - s, x, yh - bottomLeftRadius);
        
        // top-left corner
        a = topLeftRadius * 0.292893218813453;
        s = topLeftRadius * 0.585786437626905;
        graphics.lineTo(x, y + topLeftRadius);
        graphics.curveTo(x, y + s, x + a, y + a);
        graphics.curveTo(x + s, y, x + topLeftRadius, y);
        
        // top-right corner
        a = topRightRadius * 0.292893218813453;
        s = topRightRadius * 0.585786437626905;
        graphics.lineTo(xw - topRightRadius, y);
        graphics.curveTo(xw - s, y, xw - a, y + a);
        graphics.curveTo(xw, y + s, xw, y + topRightRadius);
        graphics.lineTo(xw, yh - bottomRightRadius);
    }
    
    /**
     * Draws a rounded rectangle using the size of individual x and y radii to 
     * draw the rounded corners. 
     * You must set the line style, fill, or both 
     * on the Graphics object before 
     * you call the <code>drawRoundRectComplex2()</code> method 
     * by calling the <code>linestyle()</code>, 
     * <code>lineGradientStyle()</code>, <code>beginFill()</code>, 
     * <code>beginGradientFill()</code>, or 
     * <code>beginBitmapFill()</code> method.
     * 
     * @param graphics The Graphics object that draws the rounded rectangle.
     *
     * @param x The horizontal position relative to the 
     * registration point of the parent display object, in pixels.
     * 
     * @param y The vertical position relative to the 
     * registration point of the parent display object, in pixels.
     * 
     * @param width The width of the round rectangle, in pixels.
     * 
     * @param height The height of the round rectangle, in pixels.
     * 
     * @param radiusX The default radiusX to use, if corner-specific values are not specified.
     * This value must be specified.
     * 
     * @param radiusY The default radiusY to use, if corner-specific values are not specified. 
     * If 0, the value of radiusX is used.
     * 
     * @param topLeftRadiusX The x radius of the upper-left corner, in pixels. If NaN, 
     * the value of radiusX is used.
     * 
     * @param topLeftRadiusY The y radius of the upper-left corner, in pixels. If NaN,
     * the value of topLeftRadiusX is used.
     * 
     * @param topRightRadiusX The x radius of the upper-right corner, in pixels. If NaN,
     * the value of radiusX is used.
     * 
     * @param topRightRadiusY The y radius of the upper-right corner, in pixels. If NaN,
     * the value of topRightRadiusX is used.
     * 
     * @param bottomLeftRadiusX The x radius of the bottom-left corner, in pixels. If NaN,
     * the value of radiusX is used.
     * 
     * @param bottomLeftRadiusY The y radius of the bottom-left corner, in pixels. If NaN,
     * the value of bottomLeftRadiusX is used.
     * 
     * @param bottomRightRadiusX The x radius of the bottom-right corner, in pixels. If NaN,
     * the value of radiusX is used.
     * 
     * @param bottomRightRadiusY The y radius of the bottom-right corner, in pixels. If NaN,
     * the value of bottomRightRadiusX is used.
     * 
     *  
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     *  @productversion Flex 4
     */
    public static function drawRoundRectComplex2(graphics:Graphics, x:Number, y:Number, 
                                                width:Number, height:Number, 
                                                radiusX:Number, radiusY:Number,
                                                topLeftRadiusX:Number, topLeftRadiusY:Number,
                                                topRightRadiusX:Number, topRightRadiusY:Number,
                                                bottomLeftRadiusX:Number, bottomLeftRadiusY:Number,
                                                bottomRightRadiusX:Number, bottomRightRadiusY:Number):void
    {
        var xw:Number = x + width;
        var yh:Number = y + height;
        var maxXRadius:Number = width / 2;
        var maxYRadius:Number = height / 2;
        
        // Rules for determining radius for each corner:
        //  - If explicit nnnRadiusX value is set, use it. Otherwise use radiusX.
        //  - If explicit nnnRadiusY value is set, use it. Otherwise use corresponding nnnRadiusX.
        if (radiusY == 0)
            radiusY = radiusX;
        if (isNaN(topLeftRadiusX))
            topLeftRadiusX = radiusX;
        if (isNaN(topLeftRadiusY))
            topLeftRadiusY = topLeftRadiusX;
        if (isNaN(topRightRadiusX))
            topRightRadiusX = radiusX;
        if (isNaN(topRightRadiusY))
            topRightRadiusY = topRightRadiusX;
        if (isNaN(bottomLeftRadiusX))
            bottomLeftRadiusX = radiusX;
        if (isNaN(bottomLeftRadiusY))
            bottomLeftRadiusY = bottomLeftRadiusX;
        if (isNaN(bottomRightRadiusX))
            bottomRightRadiusX = radiusX;
        if (isNaN(bottomRightRadiusY))
            bottomRightRadiusY = bottomRightRadiusX;
        
        // Pin radius values to half of the width/height
        if (topLeftRadiusX > maxXRadius)
            topLeftRadiusX = maxXRadius;
        if (topLeftRadiusY > maxYRadius)
            topLeftRadiusY = maxYRadius;
        if (topRightRadiusX > maxXRadius)
            topRightRadiusX = maxXRadius;
        if (topRightRadiusY > maxYRadius)
            topRightRadiusY = maxYRadius;
        if (bottomLeftRadiusX > maxXRadius)
            bottomLeftRadiusX = maxXRadius;
        if (bottomLeftRadiusY > maxYRadius)
            bottomLeftRadiusY = maxYRadius;
        if (bottomRightRadiusX > maxXRadius)
            bottomRightRadiusX = maxXRadius;
        if (bottomRightRadiusY > maxYRadius)
            bottomRightRadiusY = maxYRadius;
        
        // Math.sin and Math,tan values for optimal performance.
        // Math.rad = Math.PI / 180 = 0.0174532925199433
        // r * Math.sin(45 * Math.rad) =  (r * 0.707106781186547);
        // r * Math.tan(22.5 * Math.rad) = (r * 0.414213562373095);
        //
        // We can save further cycles by precalculating
        // 1.0 - 0.707106781186547 = 0.292893218813453 and
        // 1.0 - 0.414213562373095 = 0.585786437626905
        
        // bottom-right corner
        var aX:Number = bottomRightRadiusX * 0.292893218813453;		// radius - anchor pt;
        var aY:Number = bottomRightRadiusY * 0.292893218813453;		// radius - anchor pt;
        var sX:Number = bottomRightRadiusX * 0.585786437626905; 	// radius - control pt;
        var sY:Number = bottomRightRadiusY * 0.585786437626905; 	// radius - control pt;
        graphics.moveTo(xw, yh - bottomRightRadiusY);
        graphics.curveTo(xw, yh - sY, xw - aX, yh - aY);
        graphics.curveTo(xw - sX, yh, xw - bottomRightRadiusX, yh);
        
        // bottom-left corner
        aX = bottomLeftRadiusX * 0.292893218813453;
        aY = bottomLeftRadiusY * 0.292893218813453;
        sX = bottomLeftRadiusX * 0.585786437626905;
        sY = bottomLeftRadiusY * 0.585786437626905;
        graphics.lineTo(x + bottomLeftRadiusX, yh);
        graphics.curveTo(x + sX, yh, x + aX, yh - aY);
        graphics.curveTo(x, yh - sY, x, yh - bottomLeftRadiusY);
        
        // top-left corner
        aX = topLeftRadiusX * 0.292893218813453;
        aY = topLeftRadiusY * 0.292893218813453;
        sX = topLeftRadiusX * 0.585786437626905;
        sY = topLeftRadiusY * 0.585786437626905;
        graphics.lineTo(x, y + topLeftRadiusY);
        graphics.curveTo(x, y + sY, x + aX, y + aY);
        graphics.curveTo(x + sX, y, x + topLeftRadiusX, y);
        
        // top-right corner
        aX = topRightRadiusX * 0.292893218813453;
        aY = topRightRadiusY * 0.292893218813453;
        sX = topRightRadiusX * 0.585786437626905;
        sY = topRightRadiusY * 0.585786437626905;
        graphics.lineTo(xw - topRightRadiusX, y);
        graphics.curveTo(xw - sX, y, xw - aX, y + aY);
        graphics.curveTo(xw, y + sY, xw, y + topRightRadiusY);
        graphics.lineTo(xw, yh - bottomRightRadiusY);
    }
}

}
