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