/* Copyright (C) 1989, 1992, 1993, 1994 Aladdin Enterprises.  All rights reserved.
  
  This file is part of Aladdin Ghostscript.
  
  Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  License (the "License") for full details.
  
  Every copy of Aladdin Ghostscript must include a copy of the License,
  normally in a plain ASCII text file named PUBLIC.  The License grants you
  the right to copy, modify and redistribute Aladdin Ghostscript, but only
  under certain conditions described in the License.  Among other things, the
  License requires that the copyright notice and this notice be preserved on
  all copies.
*/

/* gzpath.h */
/* Private representation of paths for Ghostscript library */
/* Requires gxfixed.h */
#include "gxpath.h"
#include "gsstruct.h"			/* for extern_st */

/* Paths are represented as a linked list of line or curve segments, */
/* similar to what pathforall reports. */

/* Definition of a path segment: a segment start, a line, */
/* or a Bezier curve. */
typedef enum {
	s_start,
	s_line,
	s_line_close,
	s_curve
} segment_type;
#define segment_common\
	segment *prev;\
	segment *next;\
	segment_type type;\
	gs_fixed_point pt;		/* initial point for starts, */\
					/* final point for others */
/* Forward declarations for structure types */
typedef struct segment_s segment;
typedef struct subpath_s subpath;

/* A generic segment.  This is never instantiated. */
struct segment_s {
	segment_common
};

/* Line segments have no special data. */
typedef struct {
	segment_common
} line_segment;
#define private_st_line()	/* in gxpath.c */\
  gs_private_st_ptrs2(st_line, line_segment, "line",\
    segment_enum_ptrs, segment_reloc_ptrs, prev, next)

/* Line_close segments are for the lines appended by closepath. */
/* They point back to the subpath being closed. */
typedef struct {
	segment_common
	subpath *sub;
} line_close_segment;
#define private_st_line_close()	/* in gxpath.c */\
  gs_private_st_suffix_add1(st_line_close, line_close_segment, "close",\
    close_enum_ptrs, close_reloc_ptrs,\
    segment_enum_ptrs, segment_reloc_ptrs, sub)

/* Curve segments store the control points, not the coefficients. */
/* We may want to change this someday. */
typedef struct {
	segment_common
	gs_fixed_point p1, p2;
} curve_segment;
#define private_st_curve()	/* in gxpath.c */\
  gs_private_st_composite_only(st_curve, curve_segment, "curve",\
    segment_enum_ptrs, segment_reloc_ptrs)

/* A start segment.  This serves as the head of a subpath. */
/* The closer is only used temporarily when filling, */
/* to close an open subpath. */
struct subpath_s {
	segment_common
	segment *last;			/* last segment of subpath, */
					/* points back to here if empty */
	int curve_count;		/* # of curves */
	line_close_segment closer;
	char/*bool*/ is_closed;		/* true if subpath is closed */
};
#define private_st_subpath()	/* in gxpath.c */\
  gs_private_st_suffix_add1(st_subpath, subpath, "subpath",\
    subpath_enum_ptrs, subpath_reloc_ptrs,\
    segment_enum_ptrs, segment_reloc_ptrs, last)

/* Define a structure that can hold any kind of segment. */
/* (We never allocate these in heap memory.) */
typedef union union_segment_s {
	segment common;
	subpath start;
	line_segment line;
	line_close_segment line_close;
	curve_segment curve;
} union_segment;

/*
 * Here is the actual structure of a path.
 * The path state reflects the most recent operation on the path as follows:
 *	Operation	position_valid	subpath_open
 *	newpath		0		0
 *	moveto		1		-1
 *	lineto/curveto	1		1
 *	closepath	1		0
 */
struct gx_path_s {
	gs_memory_t *memory;
	gs_fixed_rect bbox;		/* bounding box (in device space) */
	segment *box_last;		/* bbox incorporates segments */
					/* up to & including this one */
	subpath *first_subpath;
	subpath *current_subpath;
	int subpath_count;
	int curve_count;
	gs_fixed_point position;	/* current position */
	int subpath_open;		/* 0 = newpath or closepath, */
					/* -1 = moveto, 1 = lineto/curveto */
	char/*bool*/ position_valid;
	char/*bool*/ bbox_set;		/* true if setbbox is in effect */
	char/*bool*/ shares_segments;	/* if true, this path shares its */
					/* segment storage with the one in */
					/* the previous saved graphics state */
};
extern_st(st_path);
#define public_st_path()	/* in gxpath.c */\
  gs_public_st_ptrs3(st_path, gx_path, "path",\
    path_enum_ptrs, path_reloc_ptrs, box_last, first_subpath, current_subpath)
#define st_path_max_ptrs 3

/* Macros equivalent to a few heavily used procedures. */
/* Be aware that these macros may evaluate arguments more than once. */
#define gx_path_current_point_inline(ppath,ppt)\
 ( !ppath->position_valid ? gs_note_error(gs_error_nocurrentpoint) :\
   ((ppt)->x = ppath->position.x, (ppt)->y = ppath->position.y, 0) )
/* ...rel_point rather than ...relative_point is because */
/* some compilers dislike identifiers of >31 characters. */
#define gx_path_add_rel_point_inline(ppath,dx,dy)\
 ( !ppath->position_valid || ppath->bbox_set ?\
   gx_path_add_relative_point(ppath, dx, dy) :\
   (ppath->position.x += dx, ppath->position.y += dy,\
    ppath->subpath_open = 0) )

/* ------ Clipping paths ------ */

/*
 * For clipping, a path is represented as a list of rectangles.
 * Normally, a path is created as a list of segments;
 * installing it as a clipping path creates the rectangle list.
 * However, when the clipping path originates in some other way
 * (e.g., from initclip, or for clipping a cached character),
 * or if it is a non-trivial intersection of two paths,
 * the resulting clipping path exists only as a rectangle list;
 * clippath constructs the segment representation if needed.
 * Note that even if the path only exists as a rectangle list,
 * its bounding box (path.bbox) is still correct.
 */

/*
 * Rectangle list structure.
 * Consecutive gx_clip_rect entries either have the same Y values,
 * or ymin of this entry >= ymax of the previous entry.
 */
typedef struct gx_clip_rect_s gx_clip_rect;
struct gx_clip_rect_s {
	gx_clip_rect *next, *prev;
	int ymin, ymax;			/* ymax > ymin */
	int xmin, xmax;			/* xmax > xmin */
};
/* The descriptor is public only for gxacpath.c. */
extern_st(st_clip_rect);
#define public_st_clip_rect()	/* in gxcpath.c */\
  gs_public_st_ptrs2(st_clip_rect, gx_clip_rect, "clip_rect",\
    clip_rect_enum_ptrs, clip_rect_reloc_ptrs, next, prev)
#define st_clip_rect_max_ptrs 2

/*
 * A clip list may consist either of a single rectangle,
 * with null head and tail, or a list of rectangles.  In the latter case,
 * there is a dummy head entry with p.x = q.x to cover Y values
 * starting at min_int, and a dummy tail entry to cover Y values
 * ending at max_int.  This eliminates the need for end tests.
 */
struct gx_clip_list_s {
	gx_clip_rect single;		/* (has next = prev = 0) */
	gx_clip_rect *head;
	gx_clip_rect *tail;
	int count;			/* # of rectangles not counting */
					/* head or tail */
	int container_offset;		/* offset from beginning of */
					/* containing object (for GC) */
};
#define private_st_clip_list()	/* in gxcpath.c */\
  gs_private_st_ptrs2(st_clip_list, gx_clip_list, "clip_list",\
    clip_list_enum_ptrs, clip_list_reloc_ptrs, head, tail)
#define st_clip_list_max_ptrs 2	/* head, tail */
#define clip_list_is_rectangle(clp) ((clp)->count <= 1)

/* gx_clip_path is a 'subclass' of gx_path. */
struct gx_clip_path_s {
	gx_path path;
	gs_fixed_rect cbox;		/* an inner clipping rectangle */
					/* for a quick check */
	gx_clip_list list;
	char segments_valid;		/* segment representation is valid */
	char shares_list;		/* if true, this path shares its */
					/* clip list storage with the one in */
					/* the previous saved graphics state */
};
extern_st(st_clip_path);
#define public_st_clip_path()	/* in gxcpath.c */\
  gs_public_st_composite(st_clip_path, gx_clip_path, "clip_path",\
    clip_path_enum_ptrs, clip_path_reloc_ptrs)
#define st_clip_path_max_ptrs (st_path_max_ptrs + st_clip_list_max_ptrs)
#define gx_cpath_is_rectangle(pcpath, pbox)\
  (clip_list_is_rectangle(&(pcpath)->list) ?\
   (*(pbox) = (pcpath)->cbox, 1) : 0)
