PDFlib Cookbook

cookbook

path_objects/aligned_path_objects

Path objects are attached at a line by an attachment point and with an alignment.

Download Java Code  Switch to PHP Code  Show Output 

/*
 * Aligned path objects
 * 
 * Create a schematic street map from line and ring segments. The line segments
 * can have arbitrary length, the ring segments can have arbitrary radius and
 * angle. This is implemented with path objects. By using named points and
 * the "attachmentpoint" and "align" options the positioning of the segments
 * in a seamless fashion is very easy.
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 10
 * Required data: none
 */
package com.pdflib.cookbook.pdflib.path_objects;

import com.pdflib.pdflib;
import com.pdflib.PDFlibException;

public class aligned_path_objects {

    /**
     * The basic unit in points.
     */
    final static double BU = 20;

    /**
     * Size of the circle for the reference point.
     */
    final static double REF_POINT_SIZE = BU / 20;

    /**
     * The page width
     */
    final static double PG_WIDTH = 11 * BU;

    /**
     * The page height
     */
    final static double PG_HEIGHT = 11 * BU;

    /**
     * The street width in basic units.
     */
    final static double STREET_WIDTH = 0.15;
    
    /**
     * The pdflib object
     */
    private pdflib p;

    private interface segment {
        public int create_path() throws PDFlibException;
    }

    private class ring_segment implements segment {
        ring_segment(double radius, double phi) {
            this.radius = radius;
            this.phi = phi;
        }

        /**
         * Create a path for a ring segment using polar coordinates. A positive
         * angle means a turn to the left, a negative angle means a turn to the
         * right.
         */
        public int create_path() throws PDFlibException {
            double r1 = radius * BU;
            double r2 = (radius + STREET_WIDTH) * BU;
            double psi = 0;
            
            /*
             * Ring with phi > 0 is constructed in the first quadrant.
             * Ring with phi < 0 is constructed in the fourth quadrant: 
             * Rotation through -180.
             */
            if (phi < 0) {
                final double rad = r1;
                r1 = r2;
                r2 = rad;
                psi = -180;
            }
            
            int path = p.add_path_point(-1, r1, psi, "move", "polar name=pivot");
            p.add_path_point(path, r1, psi + phi / 2, "control", "polar");
            p.add_path_point(path, r1, psi + phi, "circular",
                    "polar name=attach");
            p.add_path_point(path, r2, psi + phi, "line", "polar name=dir");
            p.add_path_point(path, r2, psi + phi / 2, "control", "polar");
            p.add_path_point(path, r2, psi, "circular", "polar");

            return path;
        }

        double radius;
        double phi;
    }

    private class line_segment implements segment {
        line_segment(double length) {
            this.length = length;
        }

        /**
         * Create a path for a line segment.
         */
        public int create_path() throws PDFlibException {
            final double l = length * BU;
            final double w = STREET_WIDTH * BU;

            int path = p.add_path_point(-1, 0, 0, "move", "name=pivot");
            p.add_path_point(path, 0, l, "line", "name=attach");
            p.add_path_point(path, w, l, "line", "name=dir");
            p.add_path_point(path, w, 0, "line", "");

            return path;
        }

        double length;
    }

    private void run() {
        /* This is where the data files are. Adjust if necessary. */
        final String searchpath = "../input";
        final String outfile = "aligned_path_objects.pdf";
        final String title = "Aligned path objects";
        int exitcode = 0;

        try {
            p = new pdflib();

            p.set_option("searchpath={" + searchpath + "}");

            p.set_option("errorpolicy=exception");

            if (p.begin_document(outfile, "") == -1)
                throw new Exception("Error: " + p.get_errmsg());

            p.set_info("Creator", "PDFlib Cookbook");
            p.set_info("Title", title);

            p.begin_page_ext(PG_WIDTH, PG_HEIGHT, "");

            /*
             * Description
             */
            String optlist = "fontname=NotoSerif-Regular fontsize=" + BU / 2;

            p.fit_textline("Aligned path objects", 2 * BU, PG_HEIGHT - 2 * BU,
                optlist);

            final segment segments[] = { 
                new line_segment(4.5),
                new ring_segment(0.5, -70),
                new line_segment(3.5),
                new ring_segment(0.2, -135),
                new line_segment(0.5), 
                new ring_segment(0.1, 35),
                new ring_segment(7, 80),
                new ring_segment(0.1, -170), 
                new line_segment(3),
                new ring_segment(0.5, -60),
                new line_segment(2.5), 
                new ring_segment(0.1, 90),
                new ring_segment(1.2, 60),
                new ring_segment(0.3, -100),
                new line_segment(2.5),
                new ring_segment(0.3, -80),
                new line_segment(1.5),
                new ring_segment(0.15, 160),
                new line_segment(0.7),
                new ring_segment(0.15, 60),
                new ring_segment(0.15, -40),
                new line_segment(0.6),
                new ring_segment(0.15, -85),
                new line_segment(0.755),
                new ring_segment(0.15, -104),
                new line_segment(0.99),
            };

            /*
             * Initial direction
             */
            double dx = 0.469;
            double dy = 0.883;
            String align_opt = "align={" + dx + " " + dy + "}";

            /*
             * Start point
             */
            final double startx = 4.7 * BU;
            final double starty = 1 * BU;

            double x = startx;
            double y = starty;

            /*
             * Loop over segments and draw them one by one
             */
            int i;
            int path;
            for (i = 0, path = -1; i < segments.length; i += 1) {
                if (path != -1) {
                    /*
                     * Compute the coordinates of the next pivot point
                     */
                    final double xatt = p.info_path(path, "px", align_opt
                        + " attachmentpoint=pivot name=attach");
                    final double yatt = p.info_path(path, "py", align_opt
                        + " attachmentpoint=pivot name=attach");

                    /*
                     * Compute the new alignment vector
                     */
                    final double xdir = p.info_path(path, "px", 
                            align_opt + " attachmentpoint=pivot name=dir");
                    final double ydir = p.info_path(path, "py", 
                            align_opt + " attachmentpoint=pivot name=dir");
                    dx = xdir - xatt;
                    dy = ydir - yatt;
                    align_opt = "align={" + dx + " " + dy + "}";

                    /*
                     * New reference vector
                     */
                    x += xatt;
                    y += yatt;
                    
                    /*
                     * Get rid of previous path
                     */
                    p.delete_path(path);
                }

                /*
                 * Create the path object and draw the path, taking the "pivot"
                 * point as attachment point and aligned to the alignment
                 * vector.
                 */
                path = segments[i].create_path();

                p.draw_path(path, x, y,
                    align_opt + " fillcolor=white attachmentpoint=pivot "
                        + "close stroke fill linewidth=0.5");

                /*
                 * Mark the pivot point
                 */
                p.setcolor("fill", "red", 0, 0, 0, 0);
                p.circle(x, y, REF_POINT_SIZE / 2);
                p.fill();
            }

            /*
             * Delete last path object
             */
            if (path != -1) {
                p.delete_path(path);
            }

            /*
             * Redraw the first pivot point on top
             */
            p.setcolor("fill", "red", 0, 0, 0, 0);
            p.circle(startx, starty, REF_POINT_SIZE / 2);
            p.fill();

            p.end_page_ext("");
            p.end_document("");
        }
        catch (PDFlibException e) {
            System.err.println("PDFlib exception occurred:");
            System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() +
                ": " + e.get_errmsg());
            exitcode = 1;
        }
        catch (Exception e) {
            System.err.println(e);
            exitcode = 1;
        }
        finally {
            if (p != null) {
                p.delete();
            }
            System.exit(exitcode);
        }
    }

    public static void main(String argv[]) {
        aligned_path_objects instance = new aligned_path_objects();
        instance.run();
    }
}