package com.flagstone.cookbook;
/*
 * BasicShapes.java
 * Cookbook
 *
 * Copyright (c) 2001-2010 Flagstone Software Ltd. All rights reserved.
 *
 * This code is distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, AND Flagstone HEREBY DISCLAIMS ALL SUCH
 * WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
 */

import java.io.File;

import com.flagstone.transform.Background;
import com.flagstone.transform.Movie;
import com.flagstone.transform.MovieHeader;
import com.flagstone.transform.Place2;
import com.flagstone.transform.ShowFrame;
import com.flagstone.transform.datatype.Bounds;
import com.flagstone.transform.datatype.WebPalette;
import com.flagstone.transform.fillstyle.SolidFill;
import com.flagstone.transform.linestyle.LineStyle1;
import com.flagstone.transform.util.shape.Canvas;

/*
 * This example shows how the drawing commands available in the ShapeConstructor
 * can be used to draw simple geometric shapes and arbitrary paths. The example
 * draws a series of simple geometric shapes and discusses how the line and fill
 * styles are specified to render the paths drawn.
 *
 * To run this example, type the following on a command line:
 *
 *     java -cp ... com.flagstone.cookbook.BasicShapes file-out
 *
 * where:
 *
 *     file-out is the path where the file will be written. If no output file
 *     is specified then a file named after the example will be written to the
 *     current directory.
 */
public class BasicShapes {
    public static void main(String[] args) {
        try {
            String out = args.length == 0 ? "BasicShapes.swf" : args[0];
            BasicShapes example = new BasicShapes();
            Movie movie = new Movie();
            example.createMovie(movie);
            movie.encodeToFile(new File(out));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void createMovie(Movie movie) {
        /*
         * Canvas allows coordinates to be specified either in twips or pixels.
         * Pixels makes life a little easier.
         */
        Canvas path = new Canvas();
        path.setPixels(true);

        int uid = 1;
        int width = 150;
        int height = 100;
        int cornerRadius = 10;
        int identifier = 0;

        MovieHeader header = new MovieHeader();
        header.setFrameRate(1.0f);
        header.setFrameSize(new Bounds(-4000, -4000, 4000, 4000));
        movie.add(header);
        movie.add(new Background(WebPalette.LIGHT_BLUE.color()));

        /*
         * Draw a rectangle with the origin at the centre of the shape.
         */
        identifier = uid++;
        rect(path, width / 2, -height / 2, width, height);
        movie.add(path.defineShape(identifier));
        movie.add(Place2.show(identifier, 1, 0, 0));
        movie.add(ShowFrame.getInstance());

        /*
         * Draw a rectangle with rounded corners.
         */
        identifier = uid++;
        rect(path, width / 2, height / 2, width, height, cornerRadius);
        movie.add(path.defineShape(identifier));
        movie.add(Place2.replace(identifier, 1));
        movie.add(ShowFrame.getInstance());

        /*
         * Draw a circle.
         */
        identifier = uid++;
        circle(path, -width / 2, height / 2, height / 2);
        movie.add(path.defineShape(identifier));
        movie.add(Place2.replace(identifier, 1));
        movie.add(ShowFrame.getInstance());

        /*
         * Draw an ellipse.
         */
        identifier = uid++;
        ellipse(path, -width / 2, -height / 2, width / 2, height / 2);
        movie.add(path.defineShape(identifier));
        movie.add(Place2.replace(identifier, 1));
        movie.add(ShowFrame.getInstance());

        /*
         * Draw a polyline. The first point of a polyline is a move relative to
         * the current drawing point - which for a new path is (0,0). While the
         * geometric shapes drawn previous were all closed the polygon method
         * can be used to draw part of a shape so the newPath() and styles must
         * be explicitly specified to draw the shape.
         */
        identifier = uid++;

        int[] points = new int[] { 0, -100, 10, 0, 0, 90, 90, 0, 0, 20, -90, 0,
                0, 90, -20, 0, 0, -90, -90, 0, 0, -20, 90, 0, 0, -90, 10, 0 };

        path.clear();
        path.setLineStyle(new LineStyle1(20, WebPalette.BLACK.color()));
        path.setFillStyle(new SolidFill(WebPalette.RED.color()));
        path.rpolygon(points);
        path.close();

        movie.add(path.defineShape(identifier));
        movie.add(Place2.replace(identifier, 1));
        movie.add(ShowFrame.getInstance());

        /*
         * Draw a cubic bezier curve.
         *
         * This simple curve is included to show how cubic Bezier curves are
         * drawn. Flash only directly supports quadratic Bezier curves.
         * Converting from cubic to quadratic is mathematically difficult so
         * the cubic curve is flattened and drawn as a series of straight lines.
         * The results are visually appealing and the slight increase in the
         * size of the Flash file is compensated for by the increase in drawing
         * performance. Note that the path is closed before the shape is
         * generated. The Canvas tracks the initial and current drawing points.
         * When a call to close() is made it draws a straight line (if required)
         * between the current point and the initial point to ensure that the
         * shape is closed and will be rendered correctly.
         */
        identifier = uid++;

        path.clear();
        path.setLineStyle(new LineStyle1(20, WebPalette.BLACK.color()));
        path.setFillStyle(new SolidFill(WebPalette.RED.color()));
        path.curve(0, -100, 150, -100, 150, 0);
        path.close();

        movie.add(path.defineShape(identifier));
        movie.add(Place2.replace(identifier, 1));
        movie.add(ShowFrame.getInstance());
    }

    private void rect(Canvas path, int x, int y, int width, int height) {
        path.clear();
        path.setLineStyle(new LineStyle1(20, WebPalette.BLACK.color()));
        path.setFillStyle(new SolidFill(WebPalette.RED.color()));
        path.move(x - width / 2, y - height / 2);
        path.rline(width, 0);
        path.rline(0, height);
        path.rline(-width, 0);
        path.rline(0, -height);
        path.close();
    }

    private void rect(Canvas path, int x, int y, int width, int height,
            int radius) {
        int shortestSide = (height < width) ? height : width;

        if (radius > shortestSide / 2)
            radius = shortestSide / 2;

        path.clear();
        path.setLineStyle(new LineStyle1(20, WebPalette.BLACK.color()));
        path.setFillStyle(new SolidFill(WebPalette.RED.color()));
        path.move(x, y - height / 2);
        path.rline(width / 2 - radius, 0);
        path.rcurve(radius, 0, 0, radius);
        path.rline(0, height - 2 * radius);
        path.rcurve(0, radius, -radius, 0);
        path.rline(-(width - 2 * radius), 0);
        path.rcurve(-radius, 0, 0, -radius);
        path.rline(0, -(height - 2 * radius));
        path.rcurve(0, -radius, radius, 0);
        path.close();
    }

    private void ellipse(Canvas path, int x, int y, int rx, int ry) {
        boolean wasInPixels = path.isPixels();

        if (wasInPixels) {
            path.setPixels(false);

            x *= 20;
            y *= 20;
            rx *= 20;
            ry *= 20;
        }

        int startX = (int) (0.707 * rx) + x;
        int startY = (int) (0.707 * ry) + y;

        int ax = (int) (0.293 * rx);
        int ay = (int) (0.293 * ry);
        int cx = (int) (0.414 * rx);
        int cy = (int) (0.414 * ry);

        path.clear();
        path.setLineStyle(new LineStyle1(20, WebPalette.BLACK.color()));
        path.setFillStyle(new SolidFill(WebPalette.RED.color()));
        path.move(startX, startY);
        path.rcurve(-ax, ay, -cx, 0);
        path.rcurve(-cx, 0, -ax, -ay);
        path.rcurve(-ax, -ay, 0, -cy);
        path.rcurve(0, -cy, ax, -ay);
        path.rcurve(ax, -ay, cx, 0);
        path.rcurve(cx, 0, ax, ay);
        path.rcurve(ax, ay, 0, cy);
        path.rcurve(0, cy, -ax, ay);
        path.close();

        path.setPixels(wasInPixels);
    }

    private void circle(final Canvas path, int x, int y, int r) {
        ellipse(path, x, y, r, r);
    }
}