1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/inter/index-module/Chapter 4/Render EPS Map.w
2021-07-27 13:55:53 +01:00

602 lines
24 KiB
OpenEdge ABL

[RenderEPSMap::] Render EPS Map.
To render the spatial map of rooms as an EPS (Encapsulated PostScript) file.
@ The test case |Index-MapEPS-ROTA| may be useful here: its output is a plain
text file, but simply removing the top line (which contains the test case number)
and then renaming it to have file extension |.eps| should make it a valid EPS
file, which can be opened in Ghostscript, Photoshop or similar.
=
void RenderEPSMap::render_map_as_EPS(filename *F, text_stream *F_alt, index_session *session) {
localisation_dictionary *LD = Indexing::get_localisation(session);
SpatialMap::establish_spatial_coordinates(session);
HTMLMap::compute_room_colours(session);
@<Prepare the EPS levels@>;
@<Open a stream and write the EPS map to it@>;
}
@<Prepare the EPS levels@> =
faux_instance_set *faux_set = Indexing::get_set_of_instances(session);
@<Create the main EPS map super-level@>;
for (int z=session->calc.Universe.corner1.z; z>=session->calc.Universe.corner0.z; z--)
@<Create an EPS map level for this z-slice@>;
FauxInstances::decode_hints(session, 2);
if (session->changed_global_room_colour == FALSE)
@<Inherit EPS room colours from those used in the World Index@>;
@<Create the main EPS map super-level@> =
EPS_map_level *main_eml = CREATE(EPS_map_level);
Indexing::add_EPS_map_levels(session, main_eml);
main_eml->width = ConfigureIndexMap::get_int_mp(I"minimum-map-width", NULL, session);
main_eml->actual_height = 0;
main_eml->titling_point_size = ConfigureIndexMap::get_int_mp(I"title-size", NULL, session);
main_eml->titling = Str::new();
Localisation::roman(main_eml->titling, LD, I"Index.EPSMap.DefaultTitle");
main_eml->contains_titling = TRUE;
main_eml->contains_rooms = FALSE;
ConfigureIndexMap::prepare_map_parameter_scope(&(main_eml->map_parameters), session);
ConfigureIndexMap::put_text_mp(I"title", &(main_eml->map_parameters), main_eml->titling, session);
@<Create an EPS map level for this z-slice@> =
EPS_map_level *eml = CREATE(EPS_map_level);
Indexing::add_EPS_map_levels(session, eml);
eml->contains_rooms = TRUE;
eml->map_level = z;
eml->y_max = -100000, eml->y_min = 100000;
faux_instance *R;
LOOP_OVER_FAUX_ROOMS(faux_set, R)
if (Room_position(R).z == z) {
if (Room_position(R).y < eml->y_min) eml->y_min = Room_position(R).y;
if (Room_position(R).y > eml->y_max) eml->y_max = Room_position(R).y;
}
Str::clear(eml->titling);
HTMLMap::devise_level_rubric(z, eml->titling, session);
if (Str::len(eml->titling) == 0) eml->contains_titling = FALSE;
else eml->contains_titling = TRUE;
ConfigureIndexMap::prepare_map_parameter_scope(&(eml->map_parameters), session);
ConfigureIndexMap::put_text_mp(I"subtitle", &(eml->map_parameters), eml->titling, session);
LOOP_OVER_FAUX_ROOMS(faux_set, R)
if (Room_position(R).z == z) {
FauxInstances::get_parameters(R)->wider_scope = &(eml->map_parameters);
}
@<Inherit EPS room colours from those used in the World Index@> =
faux_instance *R;
LOOP_OVER_FAUX_ROOMS(faux_set, R)
ConfigureIndexMap::put_text_mp(I"room-colour", FauxInstances::get_parameters(R),
R->fimd.colour, session);
@<Open a stream and write the EPS map to it@> =
text_stream *OUT = F_alt;
text_stream EPS_struct;
if (F_alt == NULL) {
OUT = &EPS_struct;
if (STREAM_OPEN_TO_FILE(OUT, F, ISO_ENC) == FALSE) {
#ifdef CORE_MODULE
Problems::fatal_on_file("Can't open index file", F);
#endif
#ifndef CORE_MODULE
Errors::fatal_with_file("can't open index file", F);
#endif
}
}
faux_instance_set *faux_set = Indexing::get_set_of_instances(session);
@<Plot the map itself@>;
@<Plot all of the rubrics onto the EPS map@>;
if (F_alt == NULL) {
STREAM_CLOSE(OUT);
}
@<Plot the map itself@> =
int blh, /* total height of the EPS map area (not counting border) */
blw, /* total width of the EPS map area (not counting border) */
border = ConfigureIndexMap::get_int_mp(I"border-size", NULL, session),
vskip = ConfigureIndexMap::get_int_mp(I"vertical-spacing", NULL, session);
@<Compute the dimensions of the EPS map@>;
int bounding_box_width = blw+2*border, bounding_box_height = blh+2*border;
RenderEPSMap::EPS_compile_header(OUT, bounding_box_width, bounding_box_height,
ConfigureIndexMap::get_text_mp(I"title-font", NULL, session),
ConfigureIndexMap::get_int_mp(I"title-size", NULL, session));
if (ConfigureIndexMap::get_int_mp(I"map-outline", NULL, session))
@<Draw a big rectangular outline around the entire EPS map@>;
linked_list *L = Indexing::get_list_of_EPS_map_levels(session);
EPS_map_level *eml;
LOOP_OVER_LINKED_LIST(eml, EPS_map_level, L) {
map_parameter_scope *level_scope = &(eml->map_parameters);
int mapunit = ConfigureIndexMap::get_int_mp(I"grid-size", level_scope, session);
if (eml->contains_rooms == FALSE)
if (ConfigureIndexMap::get_int_mp(I"map-outline", NULL, session))
@<Draw an intermediate strut in the big rectangular outline@>;
if (eml->contains_titling)
@<Draw the title for this EPS map level@>;
if (eml->contains_rooms) {
faux_instance *R;
LOOP_OVER_FAUX_ROOMS(faux_set, R)
if (Room_position(R).z == eml->map_level)
@<Establish EPS coordinates for this room@>;
LOOP_OVER_FAUX_ROOMS(faux_set, R)
if (Room_position(R).z == eml->map_level)
@<Draw the map connections from this room as EPS paths@>;
LOOP_OVER_FAUX_ROOMS(faux_set, R)
if (Room_position(R).z == eml->map_level)
@<Draw the boxes for the rooms themselves@>;
}
}
@<Compute the dimensions of the EPS map@> =
int total_chunk_height = 0, max_chunk_width = 0;
EPS_map_level *eml;
LOOP_BACKWARDS_OVER(eml, EPS_map_level) {
map_parameter_scope *level_scope = &(eml->map_parameters);
int mapunit = ConfigureIndexMap::get_int_mp(I"grid-size", level_scope, session);
int p = ConfigureIndexMap::get_int_mp(I"title-size", level_scope, session);
if (eml->contains_rooms)
p = ConfigureIndexMap::get_int_mp(I"subtitle-size", level_scope, session);
eml->titling_point_size = p;
eml->width = (session->calc.Universe.corner1.x-session->calc.Universe.corner0.x+2)*mapunit;
if (eml->allocation_id == 0) eml->actual_height = 0;
else eml->actual_height = (eml->y_max-eml->y_min+1)*mapunit;
eml->eps_origin = total_chunk_height + border;
eml->height = eml->actual_height + vskip;
if (eml->contains_rooms) eml->height += vskip;
if (eml->contains_titling) eml->height += eml->titling_point_size+vskip;
total_chunk_height += eml->height;
if (max_chunk_width < eml->width) max_chunk_width = eml->width;
}
blh = total_chunk_height;
blw = max_chunk_width;
@ The outline is a little like drawing the shape of a bookcase: there's a big
rectangle around the whole thing...
@<Draw a big rectangular outline around the entire EPS map@> =
WRITE("newpath %% Ruled outline outer box of map\n");
RenderEPSMap::EPS_compile_rectangular_path(OUT, border, border, border+blw, border+blh);
WRITE("stroke\n");
@ ...and then there are horizontal shelves dividing it into compartments.
(Each map level will be drawn inside one of these compartments.)
@<Draw an intermediate strut in the big rectangular outline@> =
WRITE("newpath %% Ruled horizontal line\n");
RenderEPSMap::EPS_compile_horizontal_line_path(OUT, border, blw+border, eml->eps_origin);
WRITE("stroke\n");
@<Draw the title for this EPS map level@> =
int y = eml->eps_origin + vskip + eml->actual_height;
if (eml->contains_rooms) {
if (ConfigureIndexMap::get_int_mp(I"monochrome", level_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else
RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"subtitle-colour", level_scope, session));
RenderEPSMap::plot_stream_at(OUT,
ConfigureIndexMap::get_text_mp(I"subtitle", level_scope, session),
NULL, 128,
ConfigureIndexMap::get_text_mp(I"subtitle-font", level_scope, session),
border*2, y+vskip,
ConfigureIndexMap::get_int_mp(I"subtitle-size", level_scope, session),
FALSE, FALSE);
} else {
if (ConfigureIndexMap::get_int_mp(I"monochrome", level_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"title-colour", level_scope, session));
RenderEPSMap::plot_stream_at(OUT,
ConfigureIndexMap::get_text_mp(I"title", NULL, session),
NULL, 128,
ConfigureIndexMap::get_text_mp(I"title-font", level_scope, session),
border*2, y+2*vskip,
ConfigureIndexMap::get_int_mp(I"title-size", level_scope, session),
FALSE, TRUE);
}
@<Establish EPS coordinates for this room@> =
map_parameter_scope *room_scope = FauxInstances::get_parameters(R);
int bx = Room_position(R).x-session->calc.Universe.corner0.x;
int by = Room_position(R).y-eml->y_min;
int offs = ConfigureIndexMap::get_int_mp(I"room-offset", room_scope, session);
int xpart = offs%10000, ypart = offs/10000;
while (xpart > 5000) xpart-=10000;
while (xpart < -5000) xpart+=10000;
bx = (bx)*mapunit + border + mapunit/2;
by = (by)*mapunit + eml->eps_origin + vskip + mapunit/2;
bx += xpart*mapunit/100;
by += ypart*mapunit/100;
R->fimd.eps_x = bx;
R->fimd.eps_y = by;
@<Draw the map connections from this room as EPS paths@> =
map_parameter_scope *room_scope = FauxInstances::get_parameters(R);
RenderEPSMap::EPS_compile_line_width_setting(OUT,
ConfigureIndexMap::get_int_mp(I"route-thickness", room_scope, session));
int bx = R->fimd.eps_x;
int by = R->fimd.eps_y;
int boxsize = ConfigureIndexMap::get_int_mp(I"room-size", room_scope, session)/2;
int R_stiffness = ConfigureIndexMap::get_int_mp(I"route-stiffness", room_scope, session);
int dir;
LOOP_OVER_STORY_DIRECTIONS(dir) {
faux_instance *T = SpatialMap::room_exit(R, dir, NULL);
int exit = session->story_dir_to_page_dir[dir];
if (FauxInstances::is_a_room(T))
@<Draw a single map connection as an EPS arrow@>;
}
RenderEPSMap::EPS_compile_line_width_unsetting(OUT);
@<Draw a single map connection as an EPS arrow@> =
int T_stiffness = ConfigureIndexMap::get_int_mp(I"route-stiffness",
FauxInstances::get_parameters(T), session);
if (ConfigureIndexMap::get_int_mp(I"monochrome", level_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else
RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"route-colour", level_scope, session));
if ((Room_position(T).z == Room_position(R).z) &&
(SpatialMap::room_exit(T, SpatialMap::opposite(dir, session), FALSE) == R))
@<Draw a two-ended arrow for a two-way horizontal connection@>
else
@<Draw a one-way arrow for a distant or off-level connection@>;
@ We don't want to draw this twice (once for R, once for T), so we draw it
just for the earlier-defined room.
@<Draw a two-ended arrow for a two-way horizontal connection@> =
if (R->allocation_id <= T->allocation_id)
RenderEPSMap::EPS_compile_Bezier_curve(OUT,
R_stiffness*mapunit, T_stiffness*mapunit,
bx, by, exit,
T->fimd.eps_x, T->fimd.eps_y, SpatialMap::opposite(exit, session), session);
@ A one-way arrow has the destination marked on it textually, since it doesn't
actually go there in any visual way.
@<Draw a one-way arrow for a distant or off-level connection@> =
int scaled = 1;
vector E = SpatialMap::direction_as_vector(exit, session);
switch(exit) {
case 8: E = U_vector_EPS; scaled = 2; break;
case 9: E = D_vector_EPS; scaled = 2; break;
case 10: E = IN_vector_EPS; scaled = 2; break;
case 11: E = OUT_vector_EPS; scaled = 2; break;
}
RenderEPSMap::EPS_compile_dashed_arrow(OUT, boxsize/scaled, E, bx, by);
RenderEPSMap::plot_text_at(OUT, NULL, T,
ConfigureIndexMap::get_int_mp(I"annotation-length", NULL, session),
ConfigureIndexMap::get_text_mp(I"annotation-font", NULL, session),
bx+E.x*boxsize*6/scaled/5, by+E.y*boxsize*6/scaled/5,
ConfigureIndexMap::get_int_mp(I"annotation-size", NULL, session),
TRUE, TRUE);
@<Draw the boxes for the rooms themselves@> =
map_parameter_scope *room_scope = FauxInstances::get_parameters(R);
int bx = R->fimd.eps_x;
int by = R->fimd.eps_y;
int boxsize = ConfigureIndexMap::get_int_mp(I"room-size", room_scope, session)/2;
@<Draw the filled box for the room@>;
@<Draw the outline of the box for the room@>;
@<Write in the name of the room@>;
@<Draw the filled box for the room@> =
WRITE("newpath %% Room interior\n");
if (ConfigureIndexMap::get_int_mp(I"monochrome", room_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 75);
else RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"room-colour", room_scope, session));
RenderEPSMap::EPS_compile_room_boundary_path(OUT, bx, by, boxsize,
ConfigureIndexMap::get_text_mp(I"room-shape", room_scope, session));
WRITE("fill\n\n");
@<Draw the outline of the box for the room@> =
if (ConfigureIndexMap::get_int_mp(I"room-outline", room_scope, session)) {
RenderEPSMap::EPS_compile_line_width_setting(OUT,
ConfigureIndexMap::get_int_mp(I"room-outline-thickness", room_scope, session));
WRITE("newpath %% Room outline\n");
if (ConfigureIndexMap::get_int_mp(I"monochrome", level_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"room-outline-colour", room_scope, session));
RenderEPSMap::EPS_compile_room_boundary_path(OUT, bx, by, boxsize,
ConfigureIndexMap::get_text_mp(I"room-shape", room_scope, session));
WRITE("stroke\n");
RenderEPSMap::EPS_compile_line_width_unsetting(OUT);
}
@<Write in the name of the room@> =
int offs = ConfigureIndexMap::get_int_mp(I"room-name-offset", room_scope, session);
int xpart = offs%10000, ypart = offs/10000;
while (xpart > 5000) xpart-=10000;
while (xpart < -5000) xpart+=10000;
bx += xpart*mapunit/100;
by += ypart*mapunit/100;
if (ConfigureIndexMap::get_int_mp(I"monochrome", level_scope, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else RenderEPSMap::EPS_compile_set_colour(OUT,
ConfigureIndexMap::get_text_mp(I"room-name-colour", room_scope, session));
text_stream *legend = ConfigureIndexMap::get_text_mp(I"room-name", room_scope, session);
faux_instance *room_to_name = NULL;
if (Str::len(legend) == 0) { room_to_name = R; legend = NULL; }
RenderEPSMap::plot_text_at(OUT, legend, room_to_name,
ConfigureIndexMap::get_int_mp(I"room-name-length", room_scope, session),
ConfigureIndexMap::get_text_mp(I"room-name-font", room_scope, session),
bx, by, ConfigureIndexMap::get_int_mp(I"room-name-size", room_scope, session),
TRUE, TRUE);
@<Plot all of the rubrics onto the EPS map@> =
rubric_holder *rh;
LOOP_OVER_LINKED_LIST(rh, rubric_holder, faux_set->rubrics) {
int bx = 0, by = 0;
int xpart = rh->at_offset%10000, ypart = rh->at_offset/10000;
int mapunit = ConfigureIndexMap::get_int_mp(I"grid-size", NULL, session);
while (xpart > 5000) xpart-=10000;
while (xpart < -5000) xpart+=10000;
if (ConfigureIndexMap::get_int_mp(I"monochrome", NULL, session))
RenderEPSMap::EPS_compile_set_greyscale(OUT, 0);
else RenderEPSMap::EPS_compile_set_colour(OUT, rh->colour);
faux_instance *O = rh->offset_from;
if (O) {
bx = O->fimd.eps_x;
by = O->fimd.eps_y;
}
bx += xpart*mapunit/100; by += ypart*mapunit/100;
RenderEPSMap::plot_text_at(OUT, rh->annotation, NULL, 128, rh->font, bx, by,
rh->point_size, TRUE, TRUE); /* centred both horizontally and vertically */
}
@h Writing text in EPS.
All of words written on the map -- titles, labels for arrows, rubrics, and so
on -- come from here.
@d MAX_EPS_TEXT_LENGTH 1000
@d MAX_EPS_ABBREVIATED_LENGTH MAX_EPS_TEXT_LENGTH
=
void RenderEPSMap::plot_text_at(OUTPUT_STREAM, text_stream *text_to_plot, faux_instance *I,
int abbrev_to, text_stream *font, int x, int y, int pointsize, int centre_h, int centre_v) {
TEMPORARY_TEXT(txt)
if (text_to_plot) {
WRITE_TO(txt, "%S", text_to_plot);
} else if (I) {
@<Try taking the name from the printed name property of the room@>;
@<If that fails, try taking the name from its source text name@>;
} else return;
RenderEPSMap::plot_stream_at(OUT, txt, I, abbrev_to, font, x, y, pointsize,
centre_h, centre_v);
DISCARD_TEXT(txt)
}
@<Try taking the name from the printed name property of the room@> =
if (Str::len(I->printed_name) > 0) {
WRITE_TO(txt, "%S", I->printed_name);
}
@<If that fails, try taking the name from its source text name@> =
if (Str::len(txt) == 0) {
text_stream *N = FauxInstances::get_name(I);
if (Str::len(N)) return;
WRITE_TO(txt, "%S", N);
}
@ =
void RenderEPSMap::plot_stream_at(OUTPUT_STREAM, text_stream *text_to_plot,
faux_instance *I, int abbrev_to, text_stream *font, int x, int y, int pointsize,
int centre_h, int centre_v) {
TEMPORARY_TEXT(txt)
Str::copy(txt, text_to_plot);
@<Abbreviate the text to be printed by stripping dispensable letters@>;
RenderEPSMap::EPS_compile_text(OUT, txt, x, y, font, pointsize, centre_h, centre_v);
DISCARD_TEXT(txt)
}
@ The following cuts the text down to the abbreviation length by knocking out,
in sequence: (a) lower-case vowels; (b) spaces; (c) lower-case consonants; (d)
punctuation marks. If that doesn't do it, the text is simply truncated. For
example, "Peisey-Nancroix" abbreviated to 10 is "Pesy-Nncrx" and to 5
is "PsyNn".
@<Abbreviate the text to be printed by stripping dispensable letters@> =
if (abbrev_to > MAX_EPS_ABBREVIATED_LENGTH) abbrev_to = MAX_EPS_ABBREVIATED_LENGTH;
while (Str::len(txt) > abbrev_to) {
int j;
for (j=Str::len(txt)-1; j>=0; j--)
if (Characters::vowel(Str::get_at(txt, j))) goto RemoveOne;
for (j=Str::len(txt)-1; j>=0; j--)
if (Str::get_at(txt, j) == ' ') goto RemoveOne;
for (j=Str::len(txt)-1; j>=0; j--)
if (islower(Str::get_at(txt, j))) goto RemoveOne;
for (j=Str::len(txt)-1; j>=0; j--)
if (isupper(Str::get_at(txt, j)) == FALSE) goto RemoveOne;
Str::truncate(txt, abbrev_to);
break;
RemoveOne: Str::delete_nth_character(txt, j);
}
@h EPS header.
EPS files are identified and version-numbered by a header, as follows.
=
void RenderEPSMap::EPS_compile_header(OUTPUT_STREAM, int bounding_box_width,
int bounding_box_height, text_stream *default_font, int default_point_size) {
WRITE("%%!PS-Adobe EPSF-3.0\n");
WRITE("%%%%BoundingBox: 0 0 %d %d\n", bounding_box_width, bounding_box_height);
WRITE("%%%%IncludeFont: %S\n", default_font);
WRITE("/%S findfont %d scalefont setfont\n", default_font, default_point_size);
}
@h Circles and rectangles.
In EPS files, there's an imaginary pen which traces out "paths". These begin
whenever the pen moves to a new location, and then continue until they are
closed (joined up back to the start position) with a |closepath| command.
=
void RenderEPSMap::EPS_compile_circular_path(OUTPUT_STREAM, int x0, int y0, int radius) {
WRITE("%d %d moveto %% rightmost point\n", x0+radius, y0);
WRITE("%d %d %d %d %d arc %% full circle traced anticlockwise\n",
x0, y0, radius, 0, 360);
WRITE("closepath\n");
}
void RenderEPSMap::EPS_compile_rectangular_path(OUTPUT_STREAM, int x0, int y0,
int x1, int y1) {
WRITE("%d %d moveto %% bottom left corner\n", x0, y0);
WRITE("%d %d lineto %% bottom side\n", x1, y0);
WRITE("%d %d lineto %% right side\n", x1, y1);
WRITE("%d %d lineto %% top side\n", x0, y1);
WRITE("closepath\n");
}
@ The boundary of a room is always one of these:
=
void RenderEPSMap::EPS_compile_room_boundary_path(OUTPUT_STREAM, int bx, int by,
int boxsize, text_stream *shape) {
if (Str::cmp(shape, I"square") == 0)
RenderEPSMap::EPS_compile_rectangular_path(OUT,
bx-boxsize, by-boxsize, bx+boxsize, by+boxsize);
else if (Str::cmp(shape, I"rectangle") == 0)
RenderEPSMap::EPS_compile_rectangular_path(OUT,
bx-2*boxsize, by-boxsize, bx+2*boxsize, by+boxsize);
else if (Str::cmp(shape, I"circle") == 0)
RenderEPSMap::EPS_compile_circular_path(OUT, bx, by, boxsize);
else
RenderEPSMap::EPS_compile_rectangular_path(OUT,
bx-boxsize, by-boxsize, bx+boxsize, by+boxsize);
}
@h Straight lines.
=
void RenderEPSMap::EPS_compile_horizontal_line_path(OUTPUT_STREAM, int x0, int x1, int y) {
WRITE("%d %d moveto %% LHS\n", x0, y);
WRITE("%d %d lineto %% RHS\n", x1, y);
WRITE("closepath\n");
}
@h Dashed arrows.
=
void RenderEPSMap::EPS_compile_dashed_arrow(OUTPUT_STREAM, int length, vector Dir,
int x0, int y0) {
WRITE("[2 1] 0 setdash %% dashed line for arrow\n");
WRITE("%d %d moveto %% room centre\n", x0, y0);
WRITE("%d %d rlineto %% arrow out\n", Dir.x*length, Dir.y*length);
WRITE("stroke\n");
WRITE("[] 0 setdash %% back to normal solid lines\n");
}
@h Bezier curves.
The other sort of path we'll need is a Bézier curve, a quadratic curve which
interpolates between vectors. EPS has support for these built-in; see any
reference book on PostScript.
=
void RenderEPSMap::EPS_compile_Bezier_curve(OUTPUT_STREAM, int stiffness0, int stiffness1,
int x0, int y0, int exit0, int x1, int y1, int exit1, index_session *session) {
int cx1, cy1, cx2, cy2;
vector E = SpatialMap::direction_as_vector(exit0, session);
cx1 = x0+E.x*stiffness0/100; cy1 = y0+E.y*stiffness0/100;
E = SpatialMap::direction_as_vector(exit1, session);
cx2 = x1+E.x*stiffness1/100; cy2 = y1+E.y*stiffness1/100;
WRITE("%d %d moveto %% start of Bezier curve\n", x0, y0);
WRITE("%d %d %d %d %d %d curveto %% control points 1, 2 and end\n",
cx1, cy1, cx2, cy2, x1, y1);
WRITE("stroke\n");
}
@h Line thickness.
The following routines should be used in nested pairs, so that the PostScript
stack is kept in order.
=
void RenderEPSMap::EPS_compile_line_width_setting(OUTPUT_STREAM, int new) {
WRITE("currentlinewidth %% Push old line width onto stack\n");
WRITE("%d setlinewidth\n", new);
}
void RenderEPSMap::EPS_compile_line_width_unsetting(OUTPUT_STREAM) {
WRITE("setlinewidth %% Pull old line width from stack\n");
}
@h Text.
In EPS world, text is just another sort of path.
=
void RenderEPSMap::EPS_compile_text(OUTPUT_STREAM, text_stream *text, int x, int y,
text_stream *font, int pointsize, int centre_h, int centre_v) {
WRITE("/%S findfont %d scalefont setfont\n", font, pointsize);
WRITE("newpath (%S)\n", text);
if (centre_h) WRITE("dup stringwidth add 2 div %d exch sub %% = X centre-offset\n", x);
else WRITE("%d %% = X\n", x);
if (centre_v) WRITE("%d %d 2 div sub %% = Y centre-offset\n", y, pointsize);
else WRITE("%d %% = Y\n", y);
WRITE("moveto show\n");
}
@h RGB colours.
Inform internally stores colours as six hexadecimal digits, in traditional
HTML way: |RRGGBB|, with each colour from 0 to 255. In EPS files, colours
are written as triples of floating point numbers $0 \leq b \leq 1$.
EPS uses reverse Polish notation, so the command here is: |R G B setrgbcolor|.
=
void RenderEPSMap::EPS_compile_set_colour(OUTPUT_STREAM, text_stream *htmlcolour) {
if (Str::len(htmlcolour) != 6) {
WRITE_TO(STDERR, "Colour '%S'\n", htmlcolour);
internal_error("Improper HTML colour");
}
RenderEPSMap::choose_colour_beam(OUT, Str::get_at(htmlcolour, 0), Str::get_at(htmlcolour, 1));
RenderEPSMap::choose_colour_beam(OUT, Str::get_at(htmlcolour, 2), Str::get_at(htmlcolour, 3));
RenderEPSMap::choose_colour_beam(OUT, Str::get_at(htmlcolour, 4), Str::get_at(htmlcolour, 5));
WRITE("setrgbcolor %% From HTML colour %S\n", htmlcolour);
}
void RenderEPSMap::choose_colour_beam(OUTPUT_STREAM, int hex1, int hex2) {
int k = RenderEPSMap::hex_to_int(hex1)*16 + RenderEPSMap::hex_to_int(hex2);
WRITE("%.6g ", (double) (((float) k)/255.0));
}
int RenderEPSMap::hex_to_int(int hex) {
switch(hex) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: internal_error("Improper character in HTML colour");
}
return 0;
}
@ EPS also supports greyscale, where there's only one beam:
=
void RenderEPSMap::EPS_compile_set_greyscale(OUTPUT_STREAM, int N) {
WRITE("%0.02f setgray %% greyscale %d/100ths of white\n", (float) N/100, N);
}