1 /* 2 * path_data.cc -- ePiX implementation classes for polygons and paths 3 * 4 * This file is part of ePiX, a C++ library for creating high-quality 5 * figures in LaTeX 6 * 7 * Version 1.2.17 8 * Last Change: June 26, 2017 9 */ 10 11 /* 12 * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2017 13 * Andrew D. Hwang <rot 13 nujnat at ubylpebff dot rqh> 14 * Department of Mathematics and Computer Science 15 * College of the Holy Cross 16 * Worcester, MA, 01610-2395, USA 17 */ 18 19 /* 20 * ePiX is free software; you can redistribute it and/or modify it 21 * under the terms of the GNU General Public License as published by 22 * the Free Software Foundation; either version 2 of the License, or 23 * (at your option) any later version. 24 * 25 * ePiX is distributed in the hope that it will be useful, but WITHOUT 26 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 27 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 28 * License for more details. 29 * 30 * You should have received a copy of the GNU General Public License 31 * along with ePiX; if not, write to the Free Software Foundation, Inc., 32 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 33 */ 34 35 #include <list> 36 #include <algorithm> 37 38 #include "errors.h" 39 #include "constants.h" 40 41 #include "triples.h" 42 #include "camera.h" 43 #include "sphere.h" 44 45 #include "edge_data.h" 46 #include "active_screen.h" 47 #include "clipping.h" 48 49 #include "paint_style.h" 50 51 #include "halfspace.h" 52 #include "pen_line.h" 53 #include "pen_fill.h" 54 55 #include "screen_data.h" 56 #include "screen.h" 57 58 #include "path_data.h" 59 60 namespace ePiX { 61 path_data()62 path_data::path_data() : m_closed(false), m_filled(false) { } 63 path_data(const std::vector<P> & pv,bool closed,bool filled)64 path_data::path_data(const std::vector<P>& pv, bool closed, bool filled) 65 : m_closed(closed), m_filled(closed && filled) 66 { 67 if (pv.size() == 1) 68 m_data.push_back(edge3d(pv.at(0), pv.at(0), true)); // will delete later 69 70 else 71 for (unsigned int i=0; i<pv.size()-1; ++i) 72 m_data.push_back(edge3d(pv.at(i), pv.at(i+1), true)); 73 } 74 path_data(const std::list<edge3d> & data,bool closed,bool filled)75 path_data::path_data(const std::list<edge3d>& data, bool closed, bool filled) 76 : m_closed(closed), m_filled(closed && filled), m_data(data) { } 77 78 clone() const79 path_data* path_data::clone() const 80 { 81 return new path_data(*this); 82 } 83 84 85 // join to arg; no effect if we're closed pt(const P & arg)86 path_data& path_data::pt(const P& arg) 87 { 88 if (0 < m_data.size()) 89 { 90 const P hd((--m_data.end())->head()); // terminal point 91 m_data.push_back(edge3d(hd, arg, true)); 92 } 93 94 else 95 m_data.push_back(edge3d(arg, arg, true)); 96 97 return *this; 98 } 99 100 101 // concatenate path_data segments operator +=(path_data pd)102 path_data& path_data::operator+= (path_data pd) 103 { 104 if (!m_closed) 105 m_data.splice(m_data.end(), pd.m_data); 106 107 return *this; 108 } 109 110 // concatenate, reversing second sequence operator -=(path_data pd)111 path_data& path_data::operator-= (path_data pd) 112 { 113 if (!m_closed) 114 { 115 for (std::list<edge3d>::iterator ep=pd.m_data.begin(); 116 ep != pd.m_data.end(); ++ep) 117 (*ep).reverse(); // swap tails and heads 118 119 pd.m_data.reverse(); // reverse list order 120 m_data.splice(m_data.end(), pd.m_data); 121 } 122 123 return *this; 124 } 125 is_closed() const126 bool path_data::is_closed() const 127 { 128 return m_closed; 129 } 130 is_filled() const131 bool path_data::is_filled() const 132 { 133 return m_filled; 134 } 135 136 137 // set flag and close up if necessary close()138 path_data& path_data::close() 139 { 140 if (0 < m_data.size()) 141 { 142 m_closed=true; 143 144 const P tail_pt((m_data.begin())->tail()); 145 const P head_pt((--m_data.end())->head()); 146 147 if (tail_pt != head_pt) 148 m_data.push_back(edge3d(head_pt, tail_pt, true)); 149 } 150 151 return *this; 152 } 153 154 // no action unless we're closed fill(const bool arg)155 path_data& path_data::fill(const bool arg) 156 { 157 if (m_closed) 158 m_filled=arg; 159 return *this; 160 } 161 162 clip()163 path_data& path_data::clip() 164 { 165 if (m_filled) 166 the_clip_box().clip_loop(m_data); 167 168 else 169 the_clip_box().clip_path(m_data); 170 171 return *this; 172 } 173 clip_to(const halfspace & knife)174 path_data& path_data::clip_to(const halfspace& knife) 175 { 176 if (m_filled) 177 knife.clip_loop(m_data); 178 else 179 knife.clip_path(m_data); 180 return *this; 181 } 182 183 clip_to(const Sphere & S,const P & viewpt,bool back)184 path_data& path_data::clip_to(const Sphere& S, const P& viewpt, 185 bool back) 186 { 187 const double rad(S.radius()); 188 189 P dir(S.center() - viewpt); 190 const double dist(norm(dir)); 191 192 if (rad < dist) // viewpt is outside 193 { 194 dir *= 1/dist; // unit vector from center to viewpt 195 double x(rad*rad/dist); 196 197 halfspace knife(S.center() - x*dir, dir); 198 199 if (back) 200 knife.reverse(); 201 202 clip_to(knife); 203 } 204 205 else 206 epix_warning("Can't clip path to sphere from interior viewpoint"); 207 208 return *this; 209 } 210 211 photo(screen & scr,const Camera & mycam,const Color & fill,const pen_data & line,const pen_data & base) const212 void path_data::photo(screen& scr, const Camera& mycam, const Color& fill, 213 const pen_data& line, const pen_data& base) const 214 { 215 path_data tmp_data(*this); 216 217 tmp_data.clip(); // clip_box 218 if (mycam.needs_clip()) 219 tmp_data.clip_to(mycam.clip_plane()); 220 221 std::list<edge2d> edges; 222 unsigned int edge_ct(0); // don't print filled unless >= 3 non-null edges 223 224 for (std::list<edge3d>::const_iterator p=tmp_data.m_data.begin(); 225 p != tmp_data.m_data.end(); ++p) 226 { 227 edge2d tmp(mycam((*p).tail()), mycam((*p).head()), (*p).is_seen()); 228 229 if (!tmp.is_null()) // endpoints not equal 230 { 231 ++edge_ct; 232 edges.push_back(tmp); 233 } 234 } 235 236 // draw fill bordered by base, then re-draw border with line/base pens 237 if (m_filled && 3 <= edge_ct) 238 { 239 scr.m_screen->add_tile(pen_fill(mycam(fill), 240 line.seen_through(mycam), edges)); 241 if (line.width() < base.width()) 242 scr.m_screen->add_tile(pen_line(line.seen_through(mycam), 243 base.seen_through(mycam), edges)); 244 } 245 246 else if (1 <= edge_ct) 247 { 248 // print edges in groups 249 std::list<edge2d>::iterator p(edges.begin()); 250 std::list<edge2d> path_buf; 251 252 int count(0); 253 while (p != edges.end()) 254 { 255 path_buf.push_back(*p); 256 p = edges.erase(p); 257 258 if (++count == 250 || p == edges.end()) // Magic number 259 { 260 scr.m_screen->add_tile(pen_line(line.seen_through(mycam), 261 base.seen_through(mycam), 262 path_buf)); 263 path_buf.clear(); 264 count = 0; 265 } 266 } 267 } 268 } 269 data() const270 std::vector<P> path_data::data() const 271 { 272 std::list<P> tmp; 273 std::list<edge3d>::const_iterator ep(m_data.begin()); 274 275 // first point is duplicated by constructor 276 while (ep != m_data.end()) 277 { 278 tmp.push_back((*ep).head()); 279 ++ep; 280 } 281 282 std::vector<P> value; 283 value.assign(tmp.begin(), tmp.end()); 284 285 return value; 286 } 287 draw() const288 void path_data::draw() const 289 { 290 photo(*active_screen(), cam(), the_paint_style().fill_color(), 291 the_paint_style().line_pen(), the_paint_style().base_pen()); 292 } 293 draw(const Color & col,const pen_data & pen) const294 void path_data::draw(const Color& col, const pen_data& pen) const 295 { 296 photo(*active_screen(), cam(), col, pen, Xfine()); 297 } 298 } // end of namespace 299