/* * format.cc -- ePiX output format interface and label-writing functions * * This file is part of ePiX, a c++ library for creating high-quality * figures in LaTeX * * Version 1.2.0-2 * Last Change: September 26, 2007 */ /* * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 * Andrew D. Hwang * Department of Mathematics and Computer Science * College of the Holy Cross * Worcester, MA, 01610-2395, USA * */ /* * ePiX is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ePiX is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with ePiX; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include "enums.h" #include "constants.h" #include "errors.h" #include "utils.h" #include "functions.h" #include "marker_style.h" #include "label_style.h" #include "path_style.h" #include "Color.h" #include "pen_data.h" #include "pairs.h" #include "edge_data.h" #include "length.h" #include "picture.h" // circ may need to add White to pallet #include "picture_data.h" #include "format.h" namespace ePiX { // public, non-virtual // LaTeX label-generating commands written in terms of protected interface std::string format::print_mark(const pair& here, const pair& offset, const epix_mark_type mk, const marker_sizes& sz, const label_state& style, const std::string& len) const { // convert dotsize to len length true_diam(sz.dot_size()); double diam(true_diam.to(len).magnitude()); // set diameter if (mk == DOT || mk == CIRC) diam *= 0.75; else if (mk == DDOT) diam *= 0.5; // and print if (mk == SPOT || mk == DOT || mk == DDOT) return print_circle_marker(here + pt_to_len(offset, len), diam, true, style.text_color(), len); else if (mk == CIRC) // Draw filled dot, then ring { Color bk(style.mask_color()); // use White for unset mask if (bk.is_unset()) { bk = White(); the_picture().add_to_pallet(bk); } return print_circle_marker(here + pt_to_len(offset, len), diam, true, bk, len) + print_circle_marker(here + pt_to_len(offset, len), diam, false, style.text_color(), len); } else if (mk == RING) return print_circle_marker(here + pt_to_len(offset, len), diam, false, style.text_color(), len); else return put_box(here + pt_to_len(offset, len), print_latex_label(style, print_latex_marker(mk, sz, len), false)); // no mask or box } // public, non-virtual std::string format::print_text(const pair& here, const pair& offset, const std::string& label, const label_state& style, const std::string& len) const { return put_box(here + pt_to_len(offset, len), print_latex_label(style, label, true)); } // public, non-virtual std::string format::print_line(const edge2d& e, const pair& offset, const pen_data& line, const pen_data& base, const path_state& style, const std::string& len) const { if ((line.width() < length(EPIX_EPSILON)) || (line.color().is_unset()) || !e.is_seen() ) return ""; // else std::list edges; edges.push_back(e); return print_line(edges, offset, line, base, style, len); } // public, non-virtual (file header comment block) std::string format::file_comments(const std::string& units, const length& hoffset, const length& voffset, double Xmin, double Xmax, double Ymin, double Ymax, double Hsize, double Vsize) const { std::ostringstream obuf; const std::string hoff_dir(length(0) < hoffset ? "right" : "left"); const std::string voff_dir(length(0) < voffset ? "up" : "down"); const double hoff(hoffset.magnitude()); const double voff(voffset.magnitude()); std::stringstream bd_box, act_sz, fg_off; bd_box << " Cartesian bounding box: [" << Xmin << "," << Xmax << "] x [" << Ymin << "," << Ymax<< "]"; act_sz << " Actual size: " << Hsize << " x " << Vsize << units; fg_off << " Figure offset: " << hoff_dir << " by " << hoff << units << ", " << voff_dir << " by " << voff << units; obuf << print_comment("ePiX-" + epix_version()) << print_comment("") << print_comment(bd_box.str()) << print_comment(act_sz.str()) << print_comment(fg_off.str()) << print_comment("") << print_comment(usepackages()) << print_comment(""); return obuf.str(); } // end of file_comments() // public, virtual std::string format::pic_header(const pair& sizes, const pair& offsets, const std::set& pallet, const std::string& len) const { std::stringstream obuf; obuf << print_pallet(pallet) << set_unitlength(len) << start_picture(sizes, offsets); return obuf.str(); } // public, virtual std::string format::pic_footer() const { return end_picture(); } // public, non-virtual std::string format::print_color(const std::string& model, const std::string& name, const std::vector& dens) const { if (dens.size() == 3) return print_color(model, name, dens.at(0), dens.at(1), dens.at(2)); else if (dens.size() == 4) return print_color(model, name, dens.at(0), dens.at(1), dens.at(2), dens.at(3)); else if (dens.size() == 0) // Neutral() return ""; else { epix_warning("format::print_color(): Internal error"); return print_color("rgb", "rgb_000000", 0, 0, 0); } } // protected, non-virtual (one-pen version) std::string format::print_line(const std::list& edges, const pair& offset, const pen_data& line, const path_state& style, const std::string& attribs, const std::string& len) const { if ( (line.width() < length(EPIX_EPSILON)) || (line.color().is_unset()) || edges.begin() == edges.end() ) return ""; // else std::stringstream value, obuf; obuf << set_pen_state(line); if (style.is_solid()) obuf << print_paths(edges, offset, attribs, len); else // not solid { // compute diameter of dots length lw(line.width()); lw.to(len); // compute unit length in pt length units("1"+len); units.to("pt"); double in_pt(units.magnitude()); double pattern_size(style.separation()); // in pt // split each edge into pattern_size chunks if necessary for (std::list::const_iterator ep = edges.begin(); ep != edges.end(); ++ep) { double curr_len(((*ep).length())*in_pt); // number of dash patterns *ep can hold unsigned int N((unsigned int) ceil(curr_len/pattern_size)); if (N == 1) obuf << print_edge((*ep), offset, style.breakpts(), lw.magnitude(), line.color(), attribs, len); if (N > 1) { pair T((*ep).tail()), dir((1.0/N)*((*ep).head() - T)); for (unsigned int i=0; i edges; edges.push_back(e); return print_line(edges, offset, line, style, attribs, len); } // local to this file std::string __epix_rule(double x, double y) { std::stringstream buf; buf << "\\rule{" << x << "pt}{" << y << "pt}"; return buf.str(); } // protected, non-virtual std::string format::xdefinecolor(const std::string& model, const std::string& name, double d1, double d2, double d3) const { std::stringstream obuf; obuf << "\\xdefinecolor{" << name << "}"; if (model == "cmy") { double bk(min(min(d1,d2),d3)); obuf << "{cmyk}{" << d1-bk << "," << d2-bk << "," << d3-bk << "," << bk << "}%" << std::endl; } else obuf << "{" << model << "}{" << d1 << "," << d2 << "," << d3 << "}%" << std::endl; return obuf.str(); } // protected, non-virtual std::string format::xdefinecolor(const std::string& model, const std::string& name, double d1, double d2, double d3, double d4) const { std::stringstream obuf; obuf << "\\xdefinecolor{" << name << "}{cmyk}{" << d1 << "," << d2 << "," << d3 << "," << d4 << "}%" << std::endl; return obuf.str(); } // protected, virtual std::string format::print_pallet(const std::set& pallet) const { std::stringstream obuf; for (std::set::const_iterator cp=pallet.begin(); cp!=pallet.end(); ++cp) obuf << print_color((*cp).model(), (*cp).name(), (*cp).densities()); return obuf.str(); } // protected, virtual std::string format::print(const pair& arg) const { std::stringstream o; o << "(" << truncate(arg.x1()) << "," << truncate(arg.x2()) << ")"; return o.str(); } // protected, virtual std::string format::print(const length& len) const { return len.name(); } // protected, virtual std::string format::print(const Color& col) const { return col.name(); } // protected, non-virtual (print non-solid edge as segments and dots) std::string format::print_edge(const edge2d& edge, const pair& offset, const std::vector bkpts, double diam, const Color& col, const std::string& attribs, const std::string& len) const { if (!edge.is_seen()) return ""; if (bkpts.size() % 2 != 0) { epix_warning("Internal error in path pattern"); return ""; } // else std::stringstream obuf; const pair T(edge.tail() + pt_to_len(offset, len)); const pair dir(edge.head() - edge.tail()); for (unsigned int i=0; i < bkpts.size(); i += 2) { double curr(bkpts.at(i)), next(bkpts.at(i+1)); if (EPIX_EPSILON < next - curr) // dash obuf << start_open_path(attribs) << print(T + curr*dir) << path_connector() << print(T + next*dir) << end_open_path(attribs); else // dot obuf << print_circle_marker(T + curr*dir, diam, true, col, len); } return obuf.str(); } // protected, non-virtual (solid path, handle (offset) data only) std::string format::print_paths(const std::list& edges, const pair& offset, const std::string& attribs, const std::string& len) const { std::stringstream obuf; std::list::const_iterator ep(edges.begin()); const pair shift(pt_to_len(offset, len)); bool all_visible(true); // all edges visible? // Assume not for now const std::string pname(start_open_path(attribs)); // print contiguous lists of visible edges while (ep != edges.end()) { if ((*ep).is_seen()) // print contiguous visible path { obuf << pname << print((*ep).tail() + shift) << path_connector() << print((*ep).head() + shift); ++ep; while ((*ep).is_seen() && ep != edges.end()) obuf << path_connector() << print((*ep++).head() + shift); obuf << end_open_path(attribs); } else // invisible edge { ++ep; all_visible = false; } } std::string value(obuf.str()); --ep; // last edge // if we're a closed loop of visible edges, adjust open/close path if (all_visible && (*edges.begin()).tail() == (*ep).head()) { value.replace(0, start_open_path(attribs).size(), start_closed_path(attribs)); value.replace(value.size()-end_open_path(attribs).size(), end_open_path(attribs).size(), end_closed_path(attribs)); } return break_lines(value, path_break()); } //// Label and marker functions //// // protected, non-virtual (print LaTeX command for marker of specified size) std::string format::print_latex_marker(const epix_mark_type mark, const marker_sizes& sz, const std::string& len) const { std::stringstream obuf; obuf << "$"; switch(mark) { case PLUS: obuf << "+"; break; case OPLUS: obuf << "\\oplus"; break; case TIMES: obuf << "\\times"; break; case OTIMES: obuf << "\\otimes"; break; case DIAMOND: obuf << "\\diamond"; break; case UP: obuf << "\\bigtriangleup"; break; case DOWN: obuf << "\\bigtriangledown"; break; case BOX: obuf << __epix_rule(0.75*sz.dot_size(), 0.75*sz.dot_size()); break; case BBOX: obuf << __epix_rule(0.5*sz.dot_size(), 0.5*sz.dot_size()); break; case HTICK: obuf << __epix_rule(0.5, 2*sz.tick_size()); break; case VTICK: obuf << __epix_rule(2*sz.tick_size(), 0.5); break; // should never see these cases case SPOT: case DOT: case DDOT: case CIRC: case RING: epix_warning("Mark type internal error"); // fall through case PATH: // fall through case TEXT: default: return ""; // N.B. return } obuf << "$"; return obuf.str(); } // public, non-virtual // handles font face, size, and color std::string format::print_latex_hbox(const label_state& style, const std::string& label) const { std::stringstream text_buf; bool need_ff(style.fontface() != "rm"); // roman bool need_fs(style.fontsize() != "nsz"); // normalsize text_buf << "\\hbox{\\color{" << print(style.text_color()) << "}"; if (need_ff) text_buf << "\\text" << style.fontface() << "{"; if (need_fs) text_buf << "\\" << style.fontsize() << " "; text_buf << label; if (need_ff) text_buf << "}"; text_buf << "}"; return text_buf.str(); } // protected, virtual std::string format::path_break() const { std::stringstream buf; buf << std::endl << " "; return buf.str(); } // inserted between points of a path std::string format::path_connector() const { return ""; } // protected, non-virtual // handles rotation, masking, border, and alignment std::string format::print_latex_label(const label_state& style, const std::string& label, bool boxed) const { std::stringstream obuf; // need rotatebox? bool rotated(EPIX_EPSILON < fabs(style.label_angle())); // need colorbox? bool masked(boxed && style.is_masked()); // need extra fcolorbox? bool padded(style.label_padding() != length(0)); obuf << "\\makebox(0,0)" << print(style.align_to()); // "none" -> "" obuf << "{"; if (rotated) obuf << "\\rotatebox{" << style.label_angle() << "}{"; if (masked) { pen_data bpen(style.label_border()); // draw border? if (!bpen.color().is_unset() && length(EPIX_EPSILON) < bpen.width()) obuf << "\\fboxrule" << print(bpen.width()) << "%" << std::endl << "\\fcolorbox{" << print(bpen.color()) << "}{" << print(style.mask_color()) << "}"; else obuf << "\\colorbox{" << print(style.mask_color()) << "}"; if (padded) { // cancel off the xcolor default length extra_pad(style.label_padding() + length(-3)); obuf << "{\\fboxrule" << print(extra_pad) << "%" << std::endl << "\\fcolorbox{" << print(style.mask_color()) << "}{" << print(style.mask_color()) << "}"; } obuf << "{"; } // finally, the payload obuf << print_latex_hbox(style, label); // close wrappers if (masked) { obuf << "}"; if (padded) obuf << "}"; } if (rotated) obuf << "}"; obuf << "}"; return obuf.str(); } // end of print_latex_label // private (convert an epix_label_posn to a string) std::string format::print(const epix_label_posn arg) const { std::string val; switch(arg) { case none: return ""; // other cases return at bottom case c: val="c"; break; case r: val="l"; // opposite of LaTeX break; case tr: case rt: val="bl"; break; case t: val="b"; break; case tl: case lt: val="br"; break; case l: val="r"; break; case bl: case lb: val="tr"; break; case b: val="t"; break; case br: case rb: val="tl"; break; } return "[" + val + "]"; } // end of format::print(align_type) // private (true pt offset to len) pair format::pt_to_len(pair offset, const std::string& len) const { return offset *= length("1pt").to(len).magnitude(); } } // end of namespace