1 #include "GeomDraw.h"
2 
3 namespace Upp {
4 
5 #define LLOG(x) // LOG(x)
6 
7 #define DEBUG_AREA 0
8 #define CHECK_DIST 0 // 0 = off, 1 = on (purging very short line segments)
9 
10 #define STAT_LINES 0 // 0 = off, 1 = on, 2 = log individual vertices
11 
DrawFatPolyPolygonOutline(Draw & draw,const Point * vertices,int vertex_counts,const int * polygon_counts,int polygon_count_counts,Color color,int width)12 static void DrawFatPolyPolygonOutline(Draw& draw,
13 	const Point *vertices, int vertex_counts,
14 	const int *polygon_counts, int polygon_count_counts,
15 	Color color, int width)
16 {
17 	if(IsNull(color))
18 		return;
19 	if(width <= 1)
20 	{
21 		DrawPolyPolygon(draw, vertices, vertex_counts, polygon_counts, polygon_count_counts, Null, 0, color);
22 		return;
23 	}
24 	enum { BATCH = 4000 };
25 	int nsegs = min<int>(BATCH, (vertex_counts + polygon_count_counts + 1) * width);
26 	int batch = 2 * nsegs;
27 	Buffer<Point> buffer(batch);
28 	Buffer<int> counts(nsegs);
29 	Fill(counts + 0, counts + nsegs, 2);
30 	int half = width >> 1;
31 	Point *gen = buffer, *genend = buffer + batch - 2 * width;
32 
33 	for(; --polygon_count_counts >= 0; vertices += *polygon_counts++)
34 	{
35 		int vert = *polygon_counts;
36 		int vw = vert * width;
37 		int pc = 2 * vw;
38 		Point prev = *vertices;
39 		for(const Point *next = vertices + vert; next-- > vertices; prev = *next)
40 		{
41 			int dx = next->x - prev.x, dy = next->y - prev.y;
42 			bool vert = (tabs(dx) < tabs(dy));
43 			if(vert)
44 			{
45 //				if(dy >= 0) { dy += width; prev.y -= half; }
46 //				else        { dy -= width; prev.y += half; }
47 
48 				prev.x -= half;
49 				for(int i = 0; i < width; i++, gen += 2)
50 				{
51 					gen[1].x = dx + (gen[0].x = prev.x + i);
52 					gen[1].y = dy + (gen[0].y = prev.y);
53 				}
54 			}
55 			else
56 			{
57 //				if(dx >= 0) { dx += width; prev.x -= half; }
58 //				else        { dx -= width; prev.x += half; }
59 
60 				prev.y -= half;
61 				for(int i = 0; i < width; i++, gen += 2)
62 				{
63 					gen[1].x = dx + (gen[0].x = prev.x);
64 					gen[1].y = dy + (gen[0].y = prev.y + i);
65 				}
66 			}
67 			if(gen >= genend)
68 			{
69 				int pc = gen - buffer;
70 				ASSERT(pc <= batch);
71 				ASSERT(!(pc & 1));
72 				draw.DrawPolyPolyline(buffer, pc, counts, pc >> 1, 0, color);
73 				gen = buffer;
74 			}
75 		}
76 	}
77 	if(int pc = gen - buffer)
78 	{
79 		ASSERT(pc <= batch);
80 		ASSERT(!(pc & 1));
81 		draw.DrawPolyPolyline(buffer, pc, counts, pc >> 1, 0, color);
82 		gen = buffer;
83 	}
84 }
85 
DrawFatPolyPolygonOutline(Draw & draw,const Vector<Point> & vertices,const Vector<int> & counts,Color color,int width)86 static void DrawFatPolyPolygonOutline(Draw& draw,
87 	const Vector<Point>& vertices, const Vector<int>& counts,
88 	Color color, int width)
89 {
90 	DrawFatPolyPolygonOutline(draw, vertices.Begin(), vertices.GetCount(),
91 		counts.Begin(), counts.GetCount(), color, width);
92 }
93 
94 LineStyle SolidLine      = { 0 };
95 LineStyle EmptyLine      = { -1 };
96 LineStyle DashLine       = { 8, -4, 0 };
97 LineStyle DotLine        = { 2, -4, 0 };
98 LineStyle DashDotLine    = { 8, -4, 2, -4, 0 };
99 LineStyle DashDotDotLine = { 8, -4, 2, -4, 2, -4, 0 };
100 
101 enum { MAX_COUNT = 2048, MAX_MANUAL = 50 };
102 
LineDraw()103 LineDraw::LineDraw()
104 : draw(0)
105 , MoveToRaw(0)
106 , LineToRaw(0)
107 , AddVector(0)
108 {
109 }
110 
LineDraw(Draw & draw,LineStyle pattern,Color color,int width,double dash)111 LineDraw::LineDraw(Draw& draw, LineStyle pattern, Color color, int width, double dash)
112 {
113 	Set(draw, pattern, color, width, dash);
114 }
115 
Set(Draw & _draw,LineStyle pattern,Color color,int width,double dash)116 void LineDraw::Set(Draw& _draw, LineStyle pattern, Color color, int width, double dash)
117 {
118 	draw = &_draw;
119 	empty = (*pattern < 0 || IsNull(color));
120 	if(empty)
121 	{
122 		active = 0;
123 		ClearExtent();
124 		return;
125 	}
126 	pen_width = (width >= 0 ? width : DotsToPixels(*draw, -width));
127 	if(dash < 0)
128 		dash *= -GetAvgPixelsPerDot(*draw);
129 	else if(dash == 0)
130 		dash = max(pen_width, 1);
131 	dash = max<double>(dash, 1);
132 	pen_color = color;
133 	if(pen_width <= 1)
134 		AddVector = &LineDraw::AddVectorThin;
135 	else if(draw->Pixels() && pen_width <= 5)
136 	{
137 		AddVector = &LineDraw::AddVectorThick;
138 		half_width = pen_width >> 1;
139 	}
140 	else
141 	{
142 		AddVector = &LineDraw::AddVectorArea;
143 		factor = pen_width / 2.0;
144 	}
145 	if(*pattern > 0)
146 	{
147 		if(dash == 0)
148 			dash = width;
149 		const signed char *p = pattern;
150 		while(*p++)
151 			;
152 		p--;
153 		while(p > pattern)
154 			segments.Add(*--p * dash);
155 		active = segments.End();
156 		remains = *--active;
157 	}
158 	else
159 	{
160 		remains = 1e100;
161 		active = NULL;
162 	}
163 	first = last = Null;
164 	Size size = draw->GetPageSize();
165 	clip = draw->GetPaintRect();
166 	max_rad = max(size.cx, size.cy);
167 	vertices.Clear();
168 	indices.Clear();
169 	ClearExtent();
170 }
171 
SetExtent(const Rect & rc)172 bool LineDraw::SetExtent(const Rect& rc)
173 {
174 	if(!(rc && clip) || empty)
175 		return false;
176 	bool in = clip.Contains(rc);
177 	MoveToRaw = active ? &LineDraw::MoveToDashed : &LineDraw::MoveToSolid;
178 	if(active)
179 		LineToRaw = in ? &LineDraw::LineToDashedSimple : &LineDraw::LineToDashedClip;
180 	else
181 		LineToRaw = in ? &LineDraw::LineToSolidSimple : &LineDraw::LineToSolidClip;
182 	return true;
183 }
184 
ClearExtent()185 void LineDraw::ClearExtent()
186 {
187 	MoveToRaw = empty ? &LineDraw::MoveToEmpty : active ? &LineDraw::MoveToDashed : &LineDraw::MoveToSolid;
188 	LineToRaw = empty ? &LineDraw::LineToEmpty : active ? &LineDraw::LineToDashedClip : &LineDraw::LineToSolidClip;
189 }
190 
Clear()191 void LineDraw::Clear()
192 {
193 	indices.Clear();
194 	vertices.Clear();
195 }
196 
Flush()197 void LineDraw::Flush()
198 {
199 	if(indices.IsEmpty())
200 		return;
201 	draw->DrawPolyPolyline(vertices, indices, 0, pen_color);
202 	vertices.Clear();
203 	indices.Clear();
204 }
205 
AddVectorThin(Point a,Point b)206 void LineDraw::AddVectorThin(Point a, Point b)
207 {
208 	if(a == b)
209 		return;
210 	if(!vertices.IsEmpty())
211 		if(vertices.Top() == a)
212 		{
213 			vertices.Add(b);
214 			indices.Top()++;
215 			return;
216 		}
217 		else if(vertices.Top() == b)
218 		{
219 			vertices.Add(a);
220 			indices.Top()++;
221 			return;
222 		}
223 	indices.Add(2);
224 	vertices.Add(a);
225 	vertices.Add(b);
226 	if(vertices.GetCount() >= MAX_COUNT)
227 		Flush();
228 }
229 
AddVectorThick(Point a,Point b)230 void LineDraw::AddVectorThick(Point a, Point b)
231 {
232 	Size vector = b - a;
233 	if((vector.cx | vector.cy) == 0)
234 		return;
235 	Size sh(sgn(vector.cx), sgn(vector.cy));
236 	Size mv(0, 0);
237 	if(tabs(vector.cx) > tabs(vector.cy))
238 	{
239 		mv.cy = 1;
240 		a.y += half_width;
241 		b.y += half_width;
242 	}
243 	else
244 	{
245 		mv.cx = 1;
246 		a.x += half_width;
247 		b.x += half_width;
248 	}
249 	indices.Add(2 * pen_width);
250 	int i = pen_width;
251 	while(i >= 2)
252 	{
253 		vertices.Add(a);
254 		vertices.Add(b);
255 		a -= mv;
256 		b -= mv;
257 		vertices.Add(b);
258 		vertices.Add(a);
259 		a -= mv;
260 		b -= mv;
261 		i -= 2;
262 	}
263 	if(i)
264 	{
265 		vertices.Add(a);
266 		vertices.Add(b);
267 	}
268 	if(vertices.GetCount() >= MAX_COUNT)
269 		Flush();
270 }
271 
AddVectorArea(Point a,Point b)272 void LineDraw::AddVectorArea(Point a, Point b)
273 {
274 //	static Point last(0, 0);
275 //	LOG(a << " - " << b << ": " << abs(a - b) << " (" << abs(a - last) << ")");
276 //	last = b;
277 
278 	Size vector = b - a;
279 	if((vector.cx | vector.cy) == 0)
280 		vector.cx = 1;
281 	double f = factor / sqrt(double(vector.cx * vector.cx + vector.cy * vector.cy));
282 	vector.cx = fround(f * vector.cx);
283 	vector.cy = fround(f * vector.cy);
284 	Size larger((vector.cy - vector.cx) * 3 >> 2, (vector.cy + vector.cx) * 3 >> 2);
285 	Point polygon[10];
286 	polygon[0].x = a.x + vector.cy; polygon[0].y = a.y - vector.cx;
287 	polygon[1].x = a.x + larger.cx; polygon[1].y = a.y - larger.cy;
288 	polygon[2].x = a.x - vector.cx; polygon[2].y = a.y - vector.cy;
289 	polygon[3].x = a.x - larger.cy; polygon[3].y = a.y - larger.cx;
290 	polygon[4].x = a.x - vector.cy; polygon[4].y = a.y + vector.cx;
291 	polygon[5].x = b.x - vector.cy; polygon[5].y = b.y + vector.cx;
292 	polygon[6].x = b.x - larger.cx; polygon[6].y = b.y + larger.cy;
293 	polygon[7].x = b.x + vector.cx; polygon[7].y = b.y + vector.cy;
294 	polygon[8].x = b.x + larger.cy; polygon[8].y = b.y + larger.cx;
295 	polygon[9].x = b.x + vector.cy; polygon[9].y = b.y - vector.cx;
296 	DrawPolygon(*draw, polygon, 10, pen_color, PEN_NULL, Black, 0);
297 //	draw->SetColor(pen_color);
298 //	draw->SetDrawPen(PEN_NULL, Black);
299 //	Polygon(*draw, polygon, 10);
300 }
301 
MoveToEmpty(Point pt)302 void LineDraw::MoveToEmpty(Point pt) {}
303 
MoveToSolid(Point pt)304 void LineDraw::MoveToSolid(Point pt)
305 {
306 	first = last = pt;
307 }
308 
MoveToDashed(Point pt)309 void LineDraw::MoveToDashed(Point pt)
310 {
311 	first = last = pt;
312 	active = segments.End();
313 	remains = *--active;
314 }
315 
LineToEmpty(Point pt)316 void LineDraw::LineToEmpty(Point pt)
317 {
318 }
319 
LineToSolidSimple(Point pt)320 void LineDraw::LineToSolidSimple(Point pt)
321 {
322 	if(!IsNull(last) && pt != last)
323 		(this ->* AddVector)(last, pt);
324 	last = pt;
325 }
326 
LineToSolidClip(Point pt)327 void LineDraw::LineToSolidClip(Point pt)
328 {
329 	if(!IsNull(last))
330 	{
331 		if(pt == last)
332 			return;
333 		Point next = pt;
334 		if(!clip.Contains(next) || !clip.Contains(last))
335 		{
336 			Pointf A = last, B = next;
337 			if(!ClipLine(A, B, clip))
338 			{
339 				last = pt;
340 				return;
341 			}
342 			last = PointfToPoint(A);
343 			next = PointfToPoint(B);
344 		}
345 		(this ->* AddVector)(last, next);
346 	}
347 	last = pt;
348 }
349 
LineToDashedClip(Point pt)350 void LineDraw::LineToDashedClip(Point pt)
351 {
352 	if(pt == last)
353 		return;
354 	if(!IsNull(last))
355 	{
356 		Point next = pt;
357 		if(!clip.Contains(next) || !clip.Contains(last))
358 		{
359 			Pointf A = last, B = next;
360 			if(!ClipLine(A, B, clip))
361 			{
362 				last = pt;
363 				return;
364 			}
365 			last = PointfToPoint(A);
366 			next = PointfToPoint(B);
367 		}
368 		LineToDashedSimple(next);
369 	}
370 	last = pt;
371 }
372 
LineToDashedSimple(Point pt)373 void LineDraw::LineToDashedSimple(Point pt)
374 {
375 	if(IsNull(last))
376 	{
377 		last = pt;
378 		return;
379 	}
380 	Size vector = pt - last;
381 	if((vector.cx | vector.cy) == 0)
382 		return;
383 	double segment = hypot(vector.cx, vector.cy), left = segment;
384 	for(;;)
385 	{
386 		if(remains > 0)
387 		{
388 			if(left < segment)
389 			{
390 				double r = left / segment;
391 				last.x = pt.x - fround(vector.cx * r);
392 				last.y = pt.y - fround(vector.cy * r);
393 			}
394 			if(remains >= left)
395 			{
396 				remains -= left;
397 				(this ->* AddVector)(last, pt);
398 				break;
399 			}
400 			else
401 			{
402 				double r = (left -= remains) / segment;
403 				Point end(pt.x - fround(vector.cx * r), pt.y - fround(vector.cy * r));
404 				(this ->* AddVector)(last, end);
405 				last = end;
406 			}
407 		}
408 		else
409 		{
410 			if(remains + left <= 0)
411 			{
412 				remains += left;
413 				break;
414 			}
415 			else
416 				left += remains;
417 		}
418 		if(active == segments.Begin())
419 			active = segments.End();
420 		remains = *--active;
421 	}
422 	last = pt;
423 }
424 
Close()425 void LineDraw::Close()
426 {
427 	if(empty)
428 		return;
429 	if(!IsNull(first.x) && first != last)
430 		LineTo(first);
431 	first = Null;
432 }
433 
Rectangle(const Rect & rc)434 void LineDraw::Rectangle(const Rect& rc)
435 {
436 	if(empty)
437 		return;
438 	MoveTo(rc.TopLeft());
439 	LineTo(Point(rc.right, rc.top));
440 	LineTo(rc.BottomRight());
441 	LineTo(Point(rc.left, rc.bottom));
442 	Close();
443 }
444 
RecurseArc(Point next,int length,int bulge,int levels)445 void LineDraw::RecurseArc(Point next, int length, int bulge, int levels)
446 {
447 	if(--levels < 0 || tabs(bulge) <= 1 || length <= 2)
448 		LineTo(next); // degenerate next line
449 	else
450 	{ // bisect arc
451 		Size normal = iscale(Size(next.y - last.y, last.x - next.x), bulge, length);
452 		Point centre = ((last + next) >> 1) + normal;
453 		int ll = (int)Length(centre - next);
454 		int hh = iscale(bulge, ll, 2 * ll + length);
455 		RecurseArc(centre, ll, hh, levels);
456 		RecurseArc(next, ll, hh, levels);
457 	}
458 }
459 
ArcTo(Point p,int bulge,int levels)460 void LineDraw::ArcTo(Point p, int bulge, int levels)
461 {
462 	if(empty)
463 		return;
464 	if(p == last)
465 		return;
466 	if(bulge >= -1 && bulge <= 1)
467 	{
468 		LineTo(p);
469 		return;
470 	}
471 	VecArcInfo info(last, Pointf(p), -bulge);
472 	if(!info.IsCurved())
473 	{
474 		LineTo(p);
475 		return;
476 	}
477 	if(info.radius >= max_rad)
478 	{
479 		RecurseArc(p, (int)Length(p - last), bulge, levels);
480 		return;
481 	}
482 	Point c = PointfToPoint(info.C);
483 	int rad = fround(info.radius);
484 	Rect rc(c.x - rad, c.y - rad, c.x + rad, c.y + rad);
485 	if(!active)
486 	{ // draw simple arc
487 		if(bulge >= 0)
488 			draw->DrawArc(rc, last, p, pen_width, pen_color);
489 		else
490 			draw->DrawArc(rc, p, last, pen_width, pen_color);
491 		last = p;
492 		return;
493 	}
494 
495 	double left = info.Length();
496 	double end_angle = info.reversed ? info.alpha : info.beta;
497 	double len_ang = (info.reversed ? info.beta - info.alpha : info.alpha - info.beta) / left;
498 
499 	int part = 1000;
500 	for(;;)
501 	{
502 		if(--part < 0)
503 			break;
504 
505 		if(remains > 0)
506 		{
507 			Point start = PointfToPoint(PolarPointf(info.C, info.radius, end_angle + len_ang * left));
508 			Point end;
509 			if(remains >= left)
510 			{
511 				remains -= left;
512 				left = 0;
513 				end = p;
514 			}
515 			else
516 				end = PointfToPoint(PolarPointf(info.C, info.radius, end_angle + len_ang * (left -= remains)));
517 			if(bulge >= 0)
518 				Swap(start, end);
519 			if(max(start.x - end.x, start.y - end.y) <= 2)
520 				draw->DrawLine(start, end, pen_width, pen_color);
521 			else
522 			{
523 				if(bulge >= 0)
524 					draw->DrawArc(rc, start, end, pen_width, pen_color);
525 				else
526 					draw->DrawArc(rc, end, start, pen_width, pen_color);
527 			}
528 			if(left == 0)
529 				break;
530 		}
531 		else if(remains < 0)
532 		{
533 			if(remains + left <= 0)
534 			{
535 				remains += left;
536 				break;
537 			}
538 			else
539 				left += remains;
540 		}
541 		if(active == segments.Begin())
542 			active = segments.End();
543 		remains = *--active;
544 	}
545 	last = p;
546 }
547 
Circle(Point centre,int radius)548 void LineDraw::Circle(Point centre, int radius)
549 {
550 	if(empty)
551 		return;
552 	Point east(centre.x + radius, centre.y), west(centre.x - radius, centre.y);
553 	MoveTo(east);
554 	ArcTo(west, radius);
555 	ArcTo(east, radius);
556 }
557 
Ellipse(const Rect & rc)558 void LineDraw::Ellipse(const Rect& rc)
559 {
560 	enum { SEGMENTS = 16 };
561 	Point centre = rc.CenterPoint();
562 	Size radius = rc.Size() >> 1;
563 	Point beg = Point(centre.x + radius.cx, centre.y);
564 	MoveTo(beg);
565 	for(int i = 1; i <= SEGMENTS; i++)
566 	{
567 		double a = i * (2 * M_PI / SEGMENTS) - M_PI / SEGMENTS;
568 		Point mid(centre.x + fround(radius.cx * cos(a)), centre.y + fround(radius.cy * sin(a)));
569 		a += M_PI / SEGMENTS;
570 		Point end(centre.x + fround(radius.cx * cos(a)), centre.y + fround(radius.cy * sin(a)));
571 		int bulge = 0;
572 		int dist = Squared(end - beg);
573 		if(dist)
574 			bulge = fround(tabs(VectorProduct(mid - beg, end - beg)) / sqrt(double(dist)));
575 		ArcTo(end, bulge);
576 		beg = end;
577 	}
578 }
579 
Set(Draw & _draw,const Rectf & _src,const Rectf & _dest,int flags,int reserve,double meter)580 void Plotter::Set(Draw& _draw, const Rectf& _src, const Rectf& _dest, int flags, int reserve, double meter)
581 {
582 	Rectf src = _src;
583 	if(src.right  <= src.left) src.right  = src.left + 1;
584 	if(src.bottom <= src.top)  src.bottom = src.top  + 1;
585 	Rectf dest = _dest;
586 	if(flags & MIRROR_X) Swap(dest.left, dest.right);
587 	if(flags & MIRROR_Y) Swap(dest.top,  dest.bottom);
588 	Pointf scale = dest.Size() / src.Size();
589 	if(flags & ISOTROPIC)
590 	{
591 		scale.x = sgn(scale.x) * AbsMin(scale);
592 		scale.y = sgn(scale.y) * fabs(scale.x);
593 	}
594 	Pointf delta = dest.CenterPoint() - src.CenterPoint() * scale;
595 	Set(_draw, scale, delta, reserve, meter);
596 }
597 
Set(Draw & _draw,Sizef _scale,Pointf _delta,int reserve,double meter)598 void Plotter::Set(Draw& _draw, Sizef _scale, Pointf _delta, int reserve, double meter)
599 {
600 	Set(_draw, Matrixf(Pointf(_scale.cx, 0), Pointf(0, _scale.cy), _delta), reserve, meter);
601 }
602 
Set(Draw & _draw,const Matrixf & matrix,int reserve,double meter)603 void Plotter::Set(Draw& _draw, const Matrixf& matrix, int reserve, double meter)
604 {
605 	Rect rc = _draw.GetPaintRect();
606 	if(reserve < 0)
607 		reserve = DotsToPixels(_draw, -reserve);
608 	rc.Inflate(reserve);
609 	Set(_draw, matrix, rc, meter);
610 }
611 
Set(Draw & _draw,const Matrixf & _transform,const Rect & _clip,double meter)612 void Plotter::Set(Draw& _draw, const Matrixf& _transform, const Rect& _clip, double meter)
613 {
614 	physical = _transform;
615 	logical = MatrixfInverse(_transform);
616 	signed_measure = MatrixfMeasure(physical);
617 	measure = fabs(signed_measure);
618 	logprec = 1.0 / measure;
619 	ortho = (physical.x.y == 0 && physical.y.x == 0);
620 	ltop = (ortho ? &Plotter::LtoPOrtho : &Plotter::LtoPFull);
621 	ltopoint = (ortho ? &Plotter::LtoPointOrtho : &Plotter::LtoPointFull);
622 	SetDraw(_draw, _clip, meter);
623 }
624 
SetDraw(Draw & _draw,const Rect & _clip,double meter)625 void Plotter::SetDraw(Draw& _draw, const Rect& _clip, double meter)
626 {
627 	draw = &_draw;
628 	SetPixelsPerDot(GetAvgPixelsPerDot(*draw));
629 	clip = _clip;
630 	logclip = PtoL(clip);
631 	logdiag = Diagonal(logclip);
632 	path_map = PATH_MAP_NULL;
633 	SetMapMeters(meter);
634 }
635 
Set(const Plotter & info,const Rect & clip)636 void Plotter::Set(const Plotter& info, const Rect& clip)
637 {
638 	ASSERT(info.draw);
639 	Set(*info.draw, info.physical, clip, info.map_meters);
640 	path_map = info.path_map;
641 	pixels_per_dot = info.pixels_per_dot;
642 	SetMapMeters(info.map_meters);
643 }
644 
Set(const Plotter & info,int reserve)645 void Plotter::Set(const Plotter& info, int reserve)
646 {
647 	ASSERT(info.draw);
648 	Set(*info.draw, info.physical, reserve);
649 	path_map = info.path_map;
650 	pixels_per_dot = info.pixels_per_dot;
651 	SetMapMeters(info.map_meters);
652 }
653 
SetMapMeters(double mm)654 void Plotter::SetMapMeters(double mm)
655 {
656 	map_meters = mm;
657 	physical_scale = fabs(measure) / (map_meters * GetPixelsPerMeter());
658 	LLOG("Plotter::SetMapMeters(" << mm << "): pixel per dot = " << pixels_per_dot << ", physical scale = " << physical_scale);
659 }
660 
SetXorMode(bool xm)661 void Plotter::SetXorMode(bool xm)
662 {
663 	if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(draw)) {
664 #ifdef PLATFORM_WIN32
665 		SetROP2(*sdraw, xm ? R2_NOTXORPEN : R2_COPYPEN);
666 #endif
667 #ifdef GUI_X11
668 		XSetFunction(Xdisplay, sdraw->GetGC(), xm ? X11_ROP2_NOT_XOR : X11_ROP2_COPY);
669 #endif
670 	}
671 }
672 
AddRectMatrix(Rectf & out,const Rectf & rc,const Matrixf & mx)673 static inline void AddRectMatrix(Rectf& out, const Rectf& rc, const Matrixf& mx)
674 {
675 	out.left   += mx.x.x * (mx.x.x >= 0 ? rc.left : rc.right) + mx.y.x * (mx.y.x >= 0 ? rc.top : rc.bottom);
676 	out.top    += mx.x.y * (mx.x.y >= 0 ? rc.left : rc.right) + mx.y.y * (mx.y.y >= 0 ? rc.top : rc.bottom);
677 	out.right  += mx.x.x * (mx.x.x <= 0 ? rc.left : rc.right) + mx.y.x * (mx.y.x <= 0 ? rc.top : rc.bottom);
678 	out.bottom += mx.x.y * (mx.x.y <= 0 ? rc.left : rc.right) + mx.y.y * (mx.y.y <= 0 ? rc.top : rc.bottom);
679 }
680 
LtoPrel(const Rectf & rc) const681 Rectf Plotter::LtoPrel(const Rectf& rc) const
682 {
683 	Rectf result(0, 0, 0, 0);
684 	AddRectMatrix(result, rc, physical);
685 	return result;
686 }
687 
LtoP(const Rectf & rc) const688 Rectf Plotter::LtoP(const Rectf& rc) const
689 {
690 	Rectf result = PointfRectf(physical.a);
691 	AddRectMatrix(result, rc, physical);
692 	return result;
693 }
694 
PtoLrel(const Rectf & rc) const695 Rectf Plotter::PtoLrel(const Rectf& rc) const
696 {
697 	Rectf result = Rectf(0, 0, 0, 0);
698 	AddRectMatrix(result, rc, logical);
699 	return result;
700 }
701 
PtoL(const Rectf & rc) const702 Rectf Plotter::PtoL(const Rectf& rc) const
703 {
704 	Rectf result = PointfRectf(logical.a);
705 	AddRectMatrix(result, rc, logical);
706 	return result;
707 }
708 
LtoPointFull(Pointf pt) const709 Point Plotter::LtoPointFull(Pointf pt) const
710 {
711 	return PointfToPoint(pt * physical);
712 }
713 
LtoPointOrtho(Pointf pt) const714 Point Plotter::LtoPointOrtho(Pointf pt) const
715 {
716 	return Point((int)(pt.x * physical.x.x + physical.a.x), (int)(pt.y * physical.y.y + physical.a.y));
717 }
718 
PaintRectPart(Draw & draw,int x,int y,int w,int h)719 static void PaintRectPart(Draw& draw, int x, int y, int w, int h)
720 {
721 	if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
722 #if defined(PLATFORM_WIN32)
723 		::PatBlt(*sdraw, x, y, w, h, PATINVERT);
724 #elif defined(GUI_X11)
725 		Point offset = sdraw->GetOffset();
726 		XFillRectangle(Xdisplay, sdraw->GetDrawable(), sdraw->GetGC(), x + offset.x, y + offset.y, w, h);
727 #endif
728 	}
729 }
730 
PaintRectPart(Draw & draw,const Rect & rc)731 static void PaintRectPart(Draw& draw, const Rect& rc) { PaintRectPart(draw, rc.left, rc.top, rc.Width(), rc.Height()); }
732 
733 enum { DRAG_STEP = 4 };
734 
PaintDragHorzLine(Draw & draw,const Rect & rc,Color c1,Color c2,Color bgnd,int mingap)735 void PaintDragHorzLine(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int mingap)
736 {
737 	Size sz = rc.Size();
738 	if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
739 #if defined(PLATFORM_WIN32)
740 		sdraw->BeginGdi();
741 		static word bmp_bits[8];
742 		HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, bmp_bits);
743 		HBRUSH brush = CreatePatternBrush(hbmp);
744 		DeleteObject(hbmp);
745 		COLORREF cc1 = (COLORREF)c1 ^ (COLORREF)bgnd, cc2 = (COLORREF)c2 ^ (COLORREF)bgnd;
746 		COLORREF old_bk = SetTextColor(*sdraw, cc1);
747 		HGDIOBJ old_brush = SelectObject(*sdraw, brush);
748 #elif defined(GUI_X11)
749 		XGCValues gcv_old, gcv_new;
750 		XGetGCValues(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
751 		gcv_new.function = X11_ROP2_XOR;
752 		gcv_new.foreground = GetXPixel(c1) ^ GetXPixel(bgnd);
753 		XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_new);
754 #endif
755 		int whites = (sz.cx - 2 * mingap + DRAG_STEP) / (2 * DRAG_STEP);
756 		if(rc.Width() <= 2 * mingap)
757 			PaintRectPart(*sdraw, rc);
758 		else if(whites <= 0) {
759 			PaintRectPart(*sdraw, rc.left, rc.top, mingap, sz.cy);
760 			PaintRectPart(*sdraw, rc.right - mingap, rc.top, mingap, sz.cy);
761 #if defined(PLATFORM_WIN32)
762 			SetTextColor(*sdraw, cc2);
763 #elif defined(GUI_X11)
764 			gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
765 			XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
766 #endif
767 			PaintRectPart(*sdraw, rc.left + mingap, rc.top, sz.cx - 2 * mingap, sz.cy);
768 		}
769 		else
770 		{
771 			int rem = sz.cx - whites * 2 * DRAG_STEP + DRAG_STEP;
772 			int lrem = rem >> 1, rrem = (rem + 1) >> 1;
773 			PaintRectPart(*sdraw, rc.left, rc.top, lrem, sz.cy);
774 			PaintRectPart(*sdraw, rc.right - rrem, rc.top, rrem, sz.cy);
775 			int i;
776 			int start = rc.left + lrem - DRAG_STEP;
777 			for(i = 1; i < whites; i++)
778 				PaintRectPart(*sdraw, start + 2 * DRAG_STEP * i, rc.top, DRAG_STEP, sz.cy);
779 #if defined(PLATFORM_WIN32)
780 			SetTextColor(*sdraw, cc2);
781 #elif defined(GUI_X11)
782 			gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
783 			XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
784 #endif
785 			start = rc.left + lrem;
786 			for(i = 0; i < whites; i++)
787 				PaintRectPart(*sdraw, start + 2 * DRAG_STEP * i, rc.top, DRAG_STEP, sz.cy);
788 		}
789 #if defined(PLATFORM_WIN32)
790 		SelectObject(*sdraw, old_brush);
791 		DeleteObject(brush);
792 		SetTextColor(*sdraw, old_bk);
793 		sdraw->EndGdi();
794 #elif defined(GUI_X11)
795 		XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
796 #endif
797 	}
798 }
799 
PaintDragVertLine(Draw & draw,const Rect & rc,Color c1,Color c2,Color bgnd,int mingap)800 void PaintDragVertLine(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int mingap)
801 {
802 	Size sz = rc.Size();
803 	if(SystemDraw *sdraw = dynamic_cast<SystemDraw *>(&draw)) {
804 #if defined(PLATFORM_WIN32)
805 		sdraw->BeginGdi();
806 		static word bmp_bits[8];
807 		HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, bmp_bits);
808 		HBRUSH brush = CreatePatternBrush(hbmp);
809 		DeleteObject(hbmp);
810 		COLORREF cc1 = (COLORREF)c1 ^ (COLORREF)bgnd, cc2 = (COLORREF)c2 ^ (COLORREF)bgnd;
811 		COLORREF old_bk = SetTextColor(*sdraw, cc1);
812 		HGDIOBJ old_brush = SelectObject(*sdraw, brush);
813 #elif defined(GUI_X11)
814 		XGCValues gcv_old, gcv_new;
815 		XGetGCValues(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
816 		gcv_new.function = X11_ROP2_XOR;
817 		gcv_new.foreground = GetXPixel(c1) ^ GetXPixel(bgnd);
818 		XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_new);
819 #endif
820 		int whites = (sz.cy - 2 * mingap + DRAG_STEP) / (2 * DRAG_STEP);
821 		if(rc.Height() <= 2 * mingap)
822 			PaintRectPart(*sdraw, rc);
823 		else if(whites <= 0)
824 		{
825 			PaintRectPart(*sdraw, rc.left, rc.top, sz.cx, mingap);
826 			PaintRectPart(*sdraw, rc.left, rc.bottom - mingap, sz.cx, mingap);
827 #if defined(PLATFORM_WIN32)
828 			SetTextColor(*sdraw, cc2);
829 #elif defined(GUI_X11)
830 			gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
831 			XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
832 #endif
833 			PaintRectPart(*sdraw, rc.left, rc.top + mingap, sz.cx, sz.cy - 2 * mingap);
834 		}
835 		else
836 		{
837 			int rem = sz.cy - whites * 2 * DRAG_STEP + DRAG_STEP;
838 			int lrem = rem >> 1, rrem = (rem + 1) >> 1;
839 			PaintRectPart(*sdraw, rc.left, rc.top, sz.cx, lrem);
840 			PaintRectPart(*sdraw, rc.left, rc.bottom - rrem, sz.cx, rrem);
841 			int i;
842 			int start = rc.top + lrem - DRAG_STEP;
843 			for(i = 1; i < whites; i++)
844 				PaintRectPart(*sdraw, rc.left, start + 2 * DRAG_STEP * i, sz.cx, DRAG_STEP);
845 #if defined(PLATFORM_WIN32)
846 			SetTextColor(*sdraw, cc2);
847 #elif defined(GUI_X11)
848 			gcv_new.foreground = GetXPixel(c2) ^ GetXPixel(bgnd);
849 			XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground, &gcv_new);
850 #endif
851 			start = rc.left + lrem;
852 			for(i = 0; i < whites; i++)
853 				PaintRectPart(*sdraw, rc.left, start + 2 * DRAG_STEP * i, sz.cx, DRAG_STEP);
854 		}
855 #if defined(PLATFORM_WIN32)
856 		SelectObject(*sdraw, old_brush);
857 		DeleteObject(brush);
858 		SetTextColor(*sdraw, old_bk);
859 		sdraw->EndGdi();
860 #elif defined(GUI_X11)
861 		XChangeGC(Xdisplay, sdraw->GetGC(), GCForeground | GCFunction, &gcv_old);
862 #endif
863 	}
864 }
865 
PaintDragRect(Draw & draw,const Rect & rc,Color c1,Color c2,Color bgnd,int width)866 void PaintDragRect(Draw& draw, const Rect& rc, Color c1, Color c2, Color bgnd, int width)
867 {
868 	if(rc.IsEmpty())
869 		return;
870 	if(rc.Height() <= 2 * width)
871 		PaintDragHorzLine(draw, rc, LtRed, White, SWhite, width);
872 	else if(rc.Width() <= 2 * width)
873 		PaintDragVertLine(draw, rc, LtRed, White, SWhite, width);
874 	else {
875 		PaintDragHorzLine(draw, Rect(rc.left, rc.top, rc.right, rc.top + width), LtRed, White, SWhite, width);
876 		PaintDragHorzLine(draw, Rect(rc.left, rc.bottom - width, rc.right, rc.bottom), LtRed, White, SWhite, width);
877 		PaintDragVertLine(draw, Rect(rc.left, rc.top + width, rc.left + width, rc.bottom - width), LtRed, White, SWhite, 0);
878 		PaintDragVertLine(draw, Rect(rc.right - width, rc.top + width, rc.right, rc.bottom - width), LtRed, White, SWhite, 0);
879 	}
880 }
881 
PlotDragRect(Plotter & plotter,const Rectf & r)882 void PlotDragRect(Plotter& plotter, const Rectf& r)
883 {
884 	if(!IsNull(r))
885 	{
886 		Rect rc = RectfToRect(plotter.LtoP(r).Inflated(1, 1, 2, 2)) & plotter.clip.Inflated(10);
887 		PaintDragRect(plotter.GetDraw(), rc, LtRed, White, SWhite, 3);
888 	}
889 }
890 
PlotterTool()891 PlotterTool::PlotterTool()
892 : last_point(0, 0)
893 , clip_arcs(true)
894 , LineToRaw(0) // acts like a pure virtual function: throws exception when called
895 , MoveToRaw(0)
896 {
897 }
898 
~PlotterTool()899 PlotterTool::~PlotterTool() {}
900 
Paint()901 void PlotterTool::Paint()
902 {
903 }
904 
Clear()905 void PlotterTool::Clear()
906 {
907 }
908 
Line(const Array<Pointf> & points)909 void PlotterTool::Line(const Array<Pointf>& points)
910 {
911 	if(!points.IsEmpty()) {
912 		bool line = false;
913 		for(int i = 0; i < points.GetCount(); i++)
914 			if(IsNull(points[i]))
915 				line = false;
916 			else {
917 				DrawTo(points[i], line);
918 				line = true;
919 			}
920 	}
921 }
922 
ArcToRaw(Pointf pt,double bulge)923 bool PlotterTool::ArcToRaw(Pointf pt, double bulge)
924 {
925 	return false;
926 }
927 
ArcTo(Pointf pt,double bulge,int levels)928 void PlotterTool::ArcTo(Pointf pt, double bulge, int levels)
929 {
930 	if(fabs(bulge) <= plotter.logprec)
931 		LineTo(pt);
932 	else
933 	{
934 		VecArcIterator arc(last_point, pt, bulge, LineToRaw);
935 		arc.Precision(plotter.logprec).ArcTo(THISBACK(ArcToRaw));
936 		if(clip_arcs)
937 			arc.Clip(plotter.logclip, MoveToRaw);
938 		arc.Go();
939 	}
940 }
941 
Rectangle(double x1,double y1,double x2,double y2,bool r)942 void PlotterTool::Rectangle(double x1, double y1, double x2, double y2, bool r)
943 {
944 	MoveTo(x1, y1);
945 	LineTo(x2, y1);
946 	LineTo(x2, y2);
947 	if(!r || min(fabs(x2 - x1), fabs(y2 - y1)) * plotter.measure > 1)
948 	{
949 		LineTo(x1, y2);
950 		LineTo(x1, y1);
951 	}
952 }
953 
Ellipse(const Rectf & rc)954 void PlotterTool::Ellipse(const Rectf& rc)
955 {
956 	enum { SEGMENTS = 16 };
957 	Pointf centre = rc.CenterPoint();
958 	Pointf radius = Sizef(rc.Size()) / 2.0;
959 	Pointf beg(centre.x + radius.x, centre.y);
960 	MoveTo(beg);
961 //	int sign = sgn(plotter.measure);
962 	for(int i = 1; i <= SEGMENTS; i++)
963 	{
964 		double a = i * (2 * M_PI / SEGMENTS) - M_PI / SEGMENTS;
965 		Pointf mid = centre + radius * PolarPointf(a);
966 		Pointf end = centre + radius * PolarPointf(a + M_PI / SEGMENTS);
967 		double bulge = 0, dist = end | beg;
968 		if(dist)
969 			bulge = fabs((mid - beg) % (end - beg)) / dist;
970 		ArcTo(end, bulge);
971 		beg = end;
972 	}
973 }
974 
ArrowHead(Pointf P,Pointf Q,double length,double angle)975 void PlotterTool::ArrowHead(Pointf P, Pointf Q, double length, double angle)
976 {
977 	double base = Bearing(P, Q);
978 	Line(PolarPointf(Q, length, base + angle), Q);
979 	Line(PolarPointf(Q, length, base - angle), Q);
980 }
981 
Arrow(Pointf P,Pointf Q,double length,double angle)982 void PlotterTool::Arrow(Pointf P, Pointf Q, double length, double angle)
983 {
984 	Line(P, Q);
985 	ArrowHead(P, Q, min(length, P | Q), angle);
986 }
987 
BiArrow(Pointf P,Pointf Q,double length,double angle)988 void PlotterTool::BiArrow(Pointf P, Pointf Q, double length, double angle)
989 {
990 	Line(P, Q);
991 	length = min(length, (P | Q) / 2);
992 	ArrowHead(P, Q, length, angle);
993 	ArrowHead(Q, P, length, angle);
994 }
995 
Circle(double x,double y,double radius)996 void PlotterTool::Circle(double x, double y, double radius)
997 {
998 	Pointf right(x + radius, y);
999 	Pointf left(x - radius, y);
1000 	MoveTo(right);
1001 	ArcTo(left, radius);
1002 	ArcTo(right, radius);
1003 }
1004 
1005 #ifdef PLOTTER_TIMING
1006 int  PathTool::segments = 0;
1007 int  PathTool::drawn = 0;
1008 long PathTool::ticks = 0;
1009 #endif
1010 
PathTool()1011 PathTool::PathTool()
1012 : use_last(false)
1013 , dummy(true)
1014 {
1015 	ClearExtent();
1016 }
1017 
~PathTool()1018 PathTool::~PathTool() {}
1019 
Reset()1020 void PathTool::Reset()
1021 {
1022 	dummy = true;
1023 	ClearExtent();
1024 }
1025 
Set(const Plotter & _info,const String & pattern,Color color,int width,double dash)1026 bool PathTool::Set(const Plotter& _info, const String& pattern, Color color, int width, double dash)
1027 {
1028 	if(dummy = IsNull(color)) {
1029 		Reset();
1030 		return false;
1031 	}
1032 	Clear();
1033 	plotter = _info;
1034 	pathdraw.Set(*plotter.draw, plotter.GetPath(pattern), color, width, dash, false);
1035 	use_last = false;
1036 	return true;
1037 }
1038 
SetExtent(const Rectf & rc)1039 bool PathTool::SetExtent(const Rectf& rc)
1040 {
1041 	if(!dummy && rc.Intersects(plotter.logclip)) {
1042 		Rectf rcc = plotter.LtoP(rc);
1043 		simple = rcc.left  >= plotter.clip.left  && rcc.top    >= plotter.clip.top
1044 			&&   rcc.right <= plotter.clip.right && rcc.bottom <= plotter.clip.bottom;
1045 		bool within = true;
1046 		if(simple)
1047 			within = pathdraw.SetExtent(RectfToRect(rcc));
1048 		else
1049 			pathdraw.ClearExtent();
1050 		MoveToRaw = (simple ? THISBACK(MoveToRawSimple) : THISBACK(MoveToRawClip));
1051 		LineToRaw = (simple ? THISBACK(LineToRawSimple) : THISBACK(LineToRawClip));
1052 		return within;
1053 	}
1054 	else {
1055 		MoveToRaw = THISBACK(MoveToRawDummy);
1056 		LineToRaw = THISBACK(LineToRawDummy);
1057 		return false;
1058 	}
1059 }
1060 
ClearExtent()1061 void PathTool::ClearExtent()
1062 {
1063 	if(dummy) {
1064 		MoveToRaw = THISBACK(MoveToRawDummy);
1065 		LineToRaw = THISBACK(LineToRawDummy);
1066 	}
1067 	else {
1068 		MoveToRaw = THISBACK(MoveToRawClip);
1069 		LineToRaw = THISBACK(LineToRawClip);
1070 		simple = false;
1071 		pathdraw.ClearExtent();
1072 	}
1073 }
1074 
Paint()1075 void PathTool::Paint()
1076 {
1077 	if(!dummy && plotter.draw)
1078 		pathdraw.Paint();
1079 	ClearExtent();
1080 }
1081 
Clear()1082 void PathTool::Clear()
1083 {
1084 	pathdraw.Clear();
1085 	ClearExtent();
1086 }
1087 
MoveToRawDummy(Pointf pt)1088 void PathTool::MoveToRawDummy(Pointf pt) {}
LineToRawDummy(Pointf pt)1089 void PathTool::LineToRawDummy(Pointf pt) {}
1090 
MoveToRawSimple(Pointf pt)1091 void PathTool::MoveToRawSimple(Pointf pt)
1092 {
1093 	pathdraw.MoveTo(plotter.LtoPoint(last_point = pt));
1094 }
1095 
MoveToRawClip(Pointf pt)1096 void PathTool::MoveToRawClip(Pointf pt)
1097 {
1098 	last_phys = plotter.LtoP(last_point = pt);
1099 	use_last = false;
1100 }
1101 
LineToRawSimple(Pointf pt)1102 void PathTool::LineToRawSimple(Pointf pt)
1103 {
1104 	if(IsNull(pt.x) || IsNull(pt.y))
1105 	{
1106 		use_last = false;
1107 		return;
1108 	}
1109 #ifdef PLOTTER_TIMING
1110 	ticks -= GetTickCount();
1111 	segments++;
1112 	drawn++;
1113 #endif
1114 	pathdraw.LineTo(plotter.LtoPoint(pt));
1115 }
1116 
LineToRawClip(Pointf pt)1117 void PathTool::LineToRawClip(Pointf pt)
1118 {
1119 	if(IsNull(pt.x) || IsNull(pt.y))
1120 	{
1121 		use_last = false;
1122 		return;
1123 	}
1124 	ASSERT(pt.x <= 1e100);
1125 #ifdef PLOTTER_TIMING
1126 	ticks -= GetTickCount();
1127 	segments++;
1128 #endif
1129 	Pointf B = plotter.LtoP(pt);
1130 #if CHECK_DIST
1131 	if(use_last && fabs(B.x - last_phys.x) < 1 && fabs(B.y - last_phys.y) < 1)
1132 	{
1133 #ifdef PLOTTER_TIMING
1134 		ticks += GetTickCount();
1135 #endif
1136 		return;
1137 	}
1138 #endif//CHECK_DIST
1139 	Pointf A = last_phys, end = B;
1140 	last_point = pt;
1141 	last_phys = B;
1142 	if(ClipLine(A, B, plotter.clip))
1143 	{ // draw line
1144 #ifdef PLOTTER_TIMING
1145 		drawn++;
1146 #endif
1147 		if(!use_last)
1148 			pathdraw.MoveTo(PointfToPoint(A));
1149 		pathdraw.LineTo(PointfToPoint(B));
1150 		use_last = (B | end) <= 1;
1151 	}
1152 	else
1153 		use_last = false;
1154 #ifdef PLOTTER_TIMING
1155 	ticks += GetTickCount();
1156 #endif
1157 }
1158 
ArcToRaw(Pointf pt,double bulge)1159 bool PathTool::ArcToRaw(Pointf pt, double bulge)
1160 {
1161 	if(bulge >= plotter.logdiag)
1162 		return false;
1163 	if(bulge <= plotter.logprec)
1164 	{
1165 		LineTo(pt);
1166 		return true;
1167 	}
1168 	Pointf pt_phys = plotter.LtoP(pt);
1169 	pathdraw.Arc(PointfToPoint(last_phys), PointfToPoint(pt_phys),
1170 		fround(bulge * plotter.signed_measure));
1171 	last_point = pt;
1172 	last_phys = pt_phys;
1173 	return true;
1174 }
1175 
AreaTool()1176 AreaTool::AreaTool()
1177 {
1178 	prev_ghost = false;
1179 	clip_arcs = false;
1180 	fill_color = Black;
1181 	dummy = true;
1182 	Clear();
1183 }
1184 
~AreaTool()1185 AreaTool::~AreaTool() {}
1186 
SetExtent(const Rectf & rc)1187 bool AreaTool::SetExtent(const Rectf& rc)
1188 {
1189 	if(!dummy && rc.Intersects(plotter.logclip)) {
1190 		Rectf rcc = plotter.LtoP(rc);
1191 		simple = rcc.left  >= plotter.clip.left  && rcc.top    >= plotter.clip.top
1192 			&&   rcc.right <= plotter.clip.right && rcc.bottom <= plotter.clip.bottom;
1193 		MoveToRaw = (simple ? THISBACK(MoveToRawSimple) : THISBACK(MoveToRawClip));
1194 		LineToRaw = (simple ? THISBACK(LineToRawSimple) : THISBACK(LineToRawClip));
1195 		return true;
1196 	}
1197 	else {
1198 		MoveToRaw = THISBACK(MoveToRawDummy);
1199 		LineToRaw = THISBACK(LineToRawDummy);
1200 		return false;
1201 	}
1202 }
1203 
ClearExtent()1204 void AreaTool::ClearExtent()
1205 {
1206 	if(dummy) {
1207 		MoveToRaw = THISBACK(MoveToRawDummy);
1208 		LineToRaw = THISBACK(LineToRawDummy);
1209 	}
1210 	else {
1211 		MoveToRaw = THISBACK(MoveToRawClip);
1212 		LineToRaw = THISBACK(LineToRawClip);
1213 	}
1214 	simple = false;
1215 }
1216 
Clear()1217 void AreaTool::Clear()
1218 {
1219 	begin_index = 0;
1220 	disjunct_begin_index = 0;
1221 	last_start = Null;
1222 	last_in = true;
1223 	vertices.Clear();
1224 	counts.Clear();
1225 	disjunct_counts.Clear();
1226 	ClearExtent();
1227 }
1228 
MoveToRawDummy(Pointf pt)1229 void AreaTool::MoveToRawDummy(Pointf pt) {}
LineToRawDummy(Pointf pt)1230 void AreaTool::LineToRawDummy(Pointf pt) {}
1231 
MoveToRawSimple(Pointf pt)1232 void AreaTool::MoveToRawSimple(Pointf pt)
1233 {
1234 	Flush();
1235 	if(prev_ghost)
1236 		ghost_lines.Add(vertices.GetCount());
1237 	vertices.Add(plotter.LtoPoint(last_point = pt));
1238 }
1239 
MoveToRawClip(Pointf pt)1240 void AreaTool::MoveToRawClip(Pointf pt)
1241 {
1242 	Flush();
1243 	if(prev_ghost)
1244 		ghost_lines.Add(vertices.GetCount());
1245 	vertices.Add(ClipBind(last_phys = plotter.LtoP(last_point = last_start = pt)));
1246 	last_in = (last_phys.x >= plotter.clip.left && last_phys.x <= plotter.clip.right
1247 		&&     last_phys.y >= plotter.clip.top  && last_phys.y <= plotter.clip.bottom);
1248 }
1249 
LineToRawSimple(Pointf pt)1250 void AreaTool::LineToRawSimple(Pointf pt)
1251 {
1252 //	RTIMING("AreaTool::LineToRawSimple");
1253 	Point ipt = plotter.LtoPoint(last_point = pt);
1254 	if(ipt != vertices.Top())
1255 	{
1256 		if(prev_ghost)
1257 			ghost_lines.Add(vertices.GetCount());
1258 		vertices.Add(ipt);
1259 	}
1260 }
1261 
LineToRawClip(Pointf pt)1262 void AreaTool::LineToRawClip(Pointf pt)
1263 {
1264 	if(IsNull(pt.x) || IsNull(pt.y))
1265 		return;
1266 //	RTIMING("AreaTool::LineToRawClip");
1267 #if DEBUG_AREA
1268 	Pointf anchor(-747102, -1043537);
1269 	bool show = (pt | anchor) <= 500 || (last_point | anchor) <= 500;
1270 #endif
1271 	Pointf next_phys = plotter.LtoP(pt);
1272 #if CHECK_DIST
1273 	if(fabs(next_phys.x - last_phys.x) < 1 && fabs(next_phys.y - last_phys.y) < 1)
1274 		return;
1275 #endif//CHECK_DIST
1276 	last_point = pt;
1277 	bool next_in = (next_phys.x >= plotter.clip.left && next_phys.x <= plotter.clip.right
1278 		&&          next_phys.y >= plotter.clip.top  && next_phys.y <= plotter.clip.bottom);
1279 #if DEBUG_AREA
1280 	int vc = vertices.GetCount();
1281 	if(show) {
1282 		LOG("[" << vc << "/" << begin_index << "] AreaTool::LineToRawClip " << pt << ", next_phys " << next_phys
1283 			<< ", top = " << vertices.Top() << ", last_in " << last_in << ", next_in " << next_in);
1284 	}
1285 #endif
1286 	if(next_in && last_in) {
1287 		Point ppt = PointfToPoint(next_phys);
1288 		if(ppt != vertices.Top())
1289 		{
1290 			if(prev_ghost)
1291 				ghost_lines.Add(vertices.GetCount());
1292 			vertices.Add(ppt);
1293 		}
1294 		last_phys = next_phys;
1295 		last_in = next_in;
1296 	}
1297 	else {
1298 		Pointf A = last_phys, B = next_phys;
1299 		bool clipped = ClipLine(A, B, plotter.clip);
1300 		Point cb = ClipBind(clipped ? B : next_phys);
1301 		Point ca = clipped ? ClipBind(A) : cb;
1302 		if(vertices.GetCount() > begin_index || clipped)
1303 			SkipTo(ca, (next_phys - last_phys) % (clip_center - last_phys) >= 0);
1304 		if(clipped && cb != vertices.Top()) {
1305 			if(prev_ghost)
1306 				ghost_lines.Add(vertices.GetCount());
1307 			vertices.Add(cb);
1308 		}
1309 		last_phys = next_phys;
1310 		last_in = (clipped && B == next_phys);
1311 	}
1312 #if DEBUG_AREA
1313 	Point check = plotter.LtoP(anchor);
1314 	for(int t = vc; t < vertices.GetCount(); t++)
1315 		if(tabs(vertices[t].x - check.x) <= 50 && tabs(vertices[t].y - check.y) <= 50)
1316 		{
1317 			LOG("AreaTool::LineToRawClip / orphan [" << t << "] = " << vertices[t] << " for " << pt);
1318 		}
1319 
1320 	if(show)
1321 	{
1322 		String plist;
1323 		while(vc < vertices.GetCount())
1324 			plist << " " << vertices[vc++];
1325 		LOG("//AreaTool::LineToRawClip " << plist);
1326 	}
1327 #endif
1328 }
1329 
SkipTo(Point pt,bool clockwise)1330 void AreaTool::SkipTo(Point pt, bool clockwise)
1331 {
1332 /*
1333 	if(vertices.GetCount() == begin_index)
1334 	{
1335 		vertices.Add(pt);
1336 		return;
1337 	}
1338 */
1339 	Point la = vertices.Top();
1340 	if(la == pt)
1341 		return;
1342 
1343 //	Size diff(pt.x + la.x - plotter.clip.left - plotter.clip.right, pt.y + la.y - plotter.clip.top - plotter.clip.bottom);
1344 	if(la.y <= plotter.clip.top) {
1345 		if(pt.y <= plotter.clip.top)
1346 			Horz(pt.x);
1347 		else if(pt.y >= plotter.clip.bottom)
1348 			Horz(clockwise ? plotter.clip.right : plotter.clip.left).Vert(plotter.clip.bottom).Horz(pt.x);
1349 		else if(pt.x <= plotter.clip.left)
1350 			Horz(plotter.clip.left).Vert(pt.y);
1351 		else if(pt.x >= plotter.clip.right)
1352 			Horz(plotter.clip.right).Vert(pt.y);
1353 		else
1354 			NEVER();
1355 	}
1356 	else if(la.y >= plotter.clip.bottom) {
1357 		if(pt.y <= plotter.clip.top)
1358 			Horz(clockwise ? plotter.clip.left : plotter.clip.right).Vert(plotter.clip.top).Horz(pt.x);
1359 		else if(pt.y >= plotter.clip.bottom)
1360 			Horz(pt.x);
1361 		else if(pt.x <= plotter.clip.left)
1362 			Horz(plotter.clip.left).Vert(pt.y);
1363 		else if(pt.x >= plotter.clip.right)
1364 			Horz(plotter.clip.right).Vert(pt.y);
1365 		else
1366 			NEVER();
1367 	}
1368 	else if(la.x <= plotter.clip.left) {
1369 		if(pt.y <= plotter.clip.top)
1370 			Vert(plotter.clip.top).Horz(pt.x);
1371 		else if(pt.y >= plotter.clip.bottom)
1372 			Vert(plotter.clip.bottom).Horz(pt.x);
1373 		else if(pt.x <= plotter.clip.left)
1374 			Vert(pt.y);
1375 		else if(pt.x >= plotter.clip.right)
1376 			Vert(clockwise ? plotter.clip.top : plotter.clip.bottom).Horz(plotter.clip.right).Vert(pt.y);
1377 		else
1378 			NEVER();
1379 	}
1380 	else if(la.x >= plotter.clip.right) {
1381 		if(pt.y <= plotter.clip.top)
1382 			Vert(plotter.clip.top).Horz(pt.x);
1383 		else if(pt.y >= plotter.clip.bottom)
1384 			Vert(plotter.clip.bottom).Horz(pt.x);
1385 		else if(pt.x <= plotter.clip.left)
1386 			Vert(clockwise ? plotter.clip.bottom : plotter.clip.top).Horz(plotter.clip.left).Vert(pt.y);
1387 		else if(pt.x >= plotter.clip.right)
1388 			Vert(pt.y);
1389 		else
1390 			NEVER();
1391 	}
1392 	else
1393 		NEVER(); // invalid case
1394 }
1395 
Flush()1396 void AreaTool::Flush()
1397 {
1398 	if((vertices.GetCount() - begin_index >= 3) && !simple) {
1399 		ASSERT(!IsNull(last_start));
1400 		LineTo(last_start);
1401 	}
1402 	int add = vertices.GetCount() - begin_index;
1403 	ASSERT(add >= 0);
1404 	if(add >= 2 && vertices.Top() == vertices[begin_index]) {
1405 		vertices.Drop(); // remove superfluous end point
1406 		if(!ghost_lines.IsEmpty() && ghost_lines.Top() == vertices.GetCount())
1407 			ghost_lines.Drop();
1408 		add--;
1409 	}
1410 	if(add <= 2) {
1411 		vertices.SetCountR(begin_index);
1412 		while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= begin_index)
1413 			ghost_lines.Drop();
1414 	}
1415 	else {
1416 		counts.Add(add);
1417 		begin_index = vertices.GetCount();
1418 	}
1419 }
1420 
Horz(int x)1421 AreaTool& AreaTool::Horz(int x)
1422 {
1423 	int count = vertices.GetCount() - begin_index;
1424 	ASSERT(count > 0);
1425 	Point *p = vertices.End();
1426 	int y = p[-1].y;
1427 	if(count >= 2 && y == p[-2].y) {
1428 		while(count > 2 && y == p[-3].y) {
1429 			count--;
1430 			p--;
1431 		}
1432 		p[-1].x = x;
1433 		int end = begin_index + count;
1434 		vertices.SetCountR(end);
1435 		while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= end)
1436 			ghost_lines.Drop();
1437 	}
1438 	else {
1439 		if(prev_ghost)
1440 			ghost_lines.Add(vertices.GetCount());
1441 		vertices.Add(Point(x, y));
1442 	}
1443 	return *this;
1444 }
1445 
Vert(int y)1446 AreaTool& AreaTool::Vert(int y)
1447 {
1448 	int count = vertices.GetCount() - begin_index;
1449 	ASSERT(count > 0);
1450 	Point *p = vertices.End();
1451 	int x = p[-1].x;
1452 	if(count >= 2 && x == p[-2].x) {
1453 		while(count > 2 && x == p[-3].x) {
1454 			count--;
1455 			p--;
1456 		}
1457 		p[-1].y = y;
1458 		int end = begin_index + count;
1459 		vertices.SetCountR(end);
1460 		while(!ghost_lines.IsEmpty() && ghost_lines.Top() >= end)
1461 			ghost_lines.Drop();
1462 	}
1463 	else {
1464 		if(prev_ghost)
1465 			ghost_lines.Add(vertices.GetCount());
1466 		vertices.Add(Point(x, y));
1467 	}
1468 	return *this;
1469 }
1470 
Reset()1471 void AreaTool::Reset()
1472 {
1473 	dummy = true;
1474 	Clear();
1475 }
1476 
Set(const Plotter & _info,Color _fill_color,uint64 _fill_pattern,const String & outline_pattern,Color outline_color,int outline_width,double outline_dash)1477 bool AreaTool::Set(const Plotter& _info, Color _fill_color, uint64 _fill_pattern,
1478 	const String& outline_pattern, Color outline_color, int outline_width, double outline_dash)
1479 {
1480 	dummy = IsNull(_fill_color) && IsNull(outline_color);
1481 	if(dummy) {
1482 		Reset();
1483 		return false;
1484 	}
1485 	if(outline_width < 0)
1486 		outline_width = DotsToPixels(*_info.draw, -outline_width);
1487 	const PathStyle& path = _info.GetPath(outline_pattern);
1488 	plotter = Plotter(_info, int(outline_width * path.width) + 2);
1489 	clip_center = Pointf(plotter.clip.CenterPoint());
1490 	fill_color = _fill_color;
1491 	fill_pattern = _fill_pattern;
1492 	double pw;
1493 	Color pc;
1494 	bool is_solid = path.IsSolid(pw, pc);
1495 	outline_pixels = (outline_width >= 0 ? outline_width : fround(_info.GetPixelsPerDot() * -outline_width));
1496 	pw *= outline_width;
1497 	is_line = !path.IsEmpty();
1498 	std_pen = !is_line || IsNull(outline_color)
1499 	|| (ghost_lines.IsEmpty() && is_solid && pw < (_info.draw->Dots() ? 20 : 2.5) /*&& !IsNull(fill_color)*/ && !fill_pattern);
1500 	if(std_pen) {
1501 		fill_outline_color = is_solid ? Nvl(pc, outline_color) : Color(Null);
1502 		fill_outline_style = is_solid ? fround(pw) : 0;
1503 		if(_info.draw->Pixels() && outline_width > 1)
1504 		{
1505 			thick_outline_color = fill_outline_color;
1506 			fill_outline_color = Null;
1507 			fill_outline_style = 0;
1508 		}
1509 	}
1510 	else {
1511 		raw_outline.Set(*plotter.draw, path, outline_color, outline_width, outline_dash, true);
1512 		fill_outline_color = thick_outline_color = Null;
1513 		fill_outline_style = 0;
1514 	}
1515 	point_pixels = outline_pixels;
1516 	point_color = Nvl(Nvl(pc, outline_color), fill_color);
1517 	Clear();
1518 #if DEBUG_AREA
1519 	LOG("AreaTool::Set, phys clip = " << plotter.clip << ", log clip = " << plotter.logclip
1520 		<< ", a = " << plotter.physical.a);
1521 #endif
1522 	return true;
1523 }
1524 
FlushFill()1525 void AreaTool::FlushFill()
1526 {
1527 	Flush();
1528 	if(IsFill() && disjunct_begin_index < vertices.GetCount())
1529 	{
1530 		disjunct_counts.Add(vertices.GetCount() - disjunct_begin_index);
1531 		disjunct_begin_index = vertices.GetCount();
1532 	}
1533 	if(vertices.GetCount() >= FLUSH_BATCH)
1534 		Paint();
1535 }
1536 
Paint()1537 void AreaTool::Paint()
1538 {
1539 	if(!plotter.draw)
1540 		return;
1541 	if(!(plotter.clip.Intersects(plotter.draw->GetPaintRect()))) {
1542 		Clear();
1543 		return;
1544 	}
1545 	Flush();
1546 	if(vertices.IsEmpty() || counts.IsEmpty())
1547 	{
1548 		Clear();
1549 		return;
1550 	}
1551 	if(disjunct_begin_index < vertices.GetCount())
1552 		disjunct_counts.Add(vertices.GetCount() - disjunct_begin_index);
1553 	if(!IsNull(fill_color) || !IsNull(fill_outline_color))
1554 		DrawPolyPolyPolygon(*plotter.draw, vertices, counts, disjunct_counts,
1555 			fill_color, fill_outline_style, fill_outline_color, fill_pattern);
1556 	if(!IsNull(thick_outline_color))
1557 		DrawFatPolyPolygonOutline(*plotter.draw, vertices, counts,
1558 			thick_outline_color, outline_pixels);
1559 /*
1560 	else if(fill_pattern)
1561 	{
1562 //		RTIMING("AreaTool::Fill(pattern)");
1563 		HDC hdc = *plotter.draw;
1564 		int old_rop = SetROP2(hdc, R2_MASKPEN);
1565 		HGDIOBJ old_brush = SelectObject(hdc, fill_pattern.GetBrush(Null, Null));
1566 		int old_color = SetTextColor(hdc, Black);
1567 		int old_bk = SetBkColor(hdc, White);
1568 		plotter.draw->SetDrawPen(PEN_NULL, Black);
1569 		if(counts.GetCount() == 1)
1570 			Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
1571 		else
1572 			PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
1573 		SetROP2(hdc, R2_MERGEPEN);
1574 		if(IsNull(fill_color)) // use color fill brush
1575 			SelectObject(hdc, fill_pattern.GetBrush(Null, Black));
1576 		else
1577 		{ // just change text color
1578 			SetTextColor(hdc, fill_color);
1579 			SetBkColor(hdc, Black);
1580 		}
1581 		if(counts.GetCount() == 1)
1582 			Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
1583 		else
1584 			PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
1585 		SetTextColor(hdc, old_color);
1586 		SetBkColor(hdc, old_bk);
1587 		SetROP2(hdc, old_rop);
1588 		SelectObject(hdc, old_brush);
1589 	}
1590 	else if(!IsNull(fill_color) || (std_pen && raw_outline_style != PEN_NULL))
1591 	{ // simple fill
1592 //		RTIMING("AreaTool::Fill(solid color)");
1593 		plotter.draw->SetDrawPen(raw_outline_style, raw_outline_color);
1594 		HGDIOBJ old_brush = 0;
1595 		if(IsNull(fill_color))
1596 		{
1597 			static HGDIOBJ null_brush = GetStockObject(NULL_BRUSH);
1598 			old_brush = SelectObject(*plotter.draw, null_brush);
1599 		}
1600 		else
1601 			plotter.draw->SetColor(fill_color);
1602 		if(counts.GetCount() == 1)
1603 			Polygon(*plotter.draw, (const POINT *)vertices.Begin(), vertices.GetCount());
1604 		else
1605 			PolyPolygon(*plotter.draw, (const POINT *)vertices.Begin(), counts.Begin(), counts.GetCount());
1606 		if(old_brush)
1607 			SelectObject(*plotter.draw, old_brush);
1608 	}
1609 */
1610 	if(!std_pen)
1611 	{ // manual outline
1612 //		RTIMING("AreaTool::Outline()");
1613 		if(ghost_lines.IsEmpty())
1614 		{
1615 			const Point *p = vertices.Begin();
1616 			for(int i = 0; i < counts.GetCount(); i++)
1617 			{
1618 				const Point *b = p, *n = p + counts[i];
1619 				raw_outline.MoveTo(*p);
1620 				while(++p < n)
1621 					raw_outline.LineTo(*p);
1622 /*
1623 				if((p[0].x >= plotter.clip.right || p[0].x <= plotter.clip.left) && p[-1].x == p[0].x
1624 				|| (p[0].y >= plotter.clip.bottom || p[0].y <= plotter.clip.top) && p[-1].y == p[0].y)
1625 					outline.MoveTo(*p);
1626 				else
1627 					outline.LineTo(*p);
1628 */
1629 //			if((b[0].x >= plotter.clip.right || b[0].x <= plotter.clip.left) && p[-1].x == b[0].x
1630 //			|| (b[0].y >= plotter.clip.bottom || b[0].y <= plotter.clip.top) && p[-1].y == b[0].y)
1631 //				;
1632 //			else
1633 //				outline.LineTo(*b);
1634 			}
1635 		}
1636 		else
1637 		{
1638 			int gl = 0;
1639 			int ngl = (gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
1640 			int v = 0;
1641 			for(int i = 0; i < counts.GetCount(); i++)
1642 			{
1643 				int nv = v + counts[i];
1644 				if(nv >= v + 2)
1645 				{
1646 					for(raw_outline.MoveTo(vertices[nv - 1]); v < nv; v++)
1647 					{
1648 						if(ngl == v)
1649 						{
1650 							ngl = (++gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
1651 							raw_outline.MoveTo(vertices[v]);
1652 						}
1653 						else
1654 							raw_outline.LineTo(vertices[v]);
1655 					}
1656 				}
1657 				else if(ngl < nv)
1658 					ngl = (++gl >= ghost_lines.GetCount() ? vertices.GetCount() : ghost_lines[gl]);
1659 				v = nv;
1660 			}
1661 		}
1662 		raw_outline.Paint();
1663 	}
1664 	Clear();
1665 }
1666 
1667 class EmptyMarker : public MarkTool::Marker
1668 {
1669 public:
Paint(Draw & draw,const Vector<Point> & pt)1670 	virtual void Paint(Draw& draw, const Vector<Point>& pt) {}
GetSize() const1671 	virtual int  GetSize() const                            { return 0; }
1672 };
1673 
1674 
Empty()1675 One<MarkTool::Marker> MarkTool::Empty()
1676 {
1677 	return new EmptyMarker;
1678 }
1679 
1680 class ImageMarker : public MarkTool::Marker
1681 {
1682 public:
1683 	ImageMarker(const Image& img, int size, Color color, bool antialias, bool ignore_hotspot);
1684 
1685 	virtual void  Paint(Draw& draw, const Vector<Point>& pt);
GetSize() const1686 	virtual int   GetSize() const { return linear_size; }
1687 
1688 private:
1689 	Image         img;
1690 	Size          imgsize;
1691 	Size          outsize;
1692 	Point         hotspot;
1693 	int           linear_size;
1694 	Color         color;
1695 //	bool          body;
1696 	bool          mask;
1697 	bool          antialias;
1698 	bool          ignore_hotspot;
1699 };
1700 
ImageMarker(const Image & srcimg,int size_,Color color_,bool antialias_,bool ignore_hotspot_)1701 ImageMarker::ImageMarker(const Image& srcimg, int size_, Color color_, bool antialias_, bool ignore_hotspot_)
1702 : imgsize(srcimg.GetSize())
1703 , color(color_)
1704 , antialias(antialias_)
1705 , ignore_hotspot(ignore_hotspot_)
1706 , outsize(0, 0)
1707 {
1708 	hotspot = (ignore_hotspot ? (Point)(srcimg.GetSize() >> 1) : srcimg.GetHotSpot());
1709 	if(imgsize.cx > 0 && imgsize.cy > 0) {
1710 		hotspot = iscale(hotspot, size_, max(imgsize.cx, imgsize.cy));
1711 		if(imgsize.cx > imgsize.cy)
1712 			outsize = Size(size_, iscale(size_, imgsize.cy, imgsize.cx));
1713 		else
1714 			outsize = Size(iscale(size_, imgsize.cx, imgsize.cy), size_);
1715 		if(imgsize == outsize || outsize.cx >= imgsize.cx && outsize.cy >= imgsize.cy && !antialias)
1716 			img = srcimg;
1717 		else {
1718 			Size xsize = 2 * imgsize;
1719 			while(2 * xsize.cx <= outsize.cx && 2 * xsize.cy <= outsize.cy && xsize.cx <= 256 && xsize.cy <= 256)
1720 				xsize <<= 2;
1721 			Size tmpsize = min(outsize, xsize);
1722 //			if(antialias)
1723 			img = Rescale(srcimg, tmpsize);
1724 			// else RescaleNoAA !! todo
1725 		}
1726 		imgsize = img.GetSize();
1727 	}
1728 	linear_size = max(
1729 		max(hotspot.x, outsize.cx - hotspot.x),
1730 		max(hotspot.y, outsize.cy - hotspot.y));
1731 	mask = (srcimg.GetKind() == IMAGE_OPAQUE);
1732 }
1733 
Paint(Draw & draw,const Vector<Point> & pts)1734 void ImageMarker::Paint(Draw& draw, const Vector<Point>& pts)
1735 {
1736 	if(img.IsEmpty())
1737 		return;
1738 	for(int t = 0; t < pts.GetCount(); t++) {
1739 		Point pt = pts[t] - hotspot;
1740 		draw.DrawImage(Rect(pt, outsize), img);
1741 	}
1742 }
1743 
Picture(Image pic,int size,Color color,bool antialias,bool ignore_hotspot)1744 One<MarkTool::Marker> MarkTool::Picture(Image pic, int size, Color color, bool antialias, bool ignore_hotspot)
1745 {
1746 	return new ImageMarker(pic, size, color, antialias, ignore_hotspot);
1747 }
1748 
1749 class AreaMarker : public MarkTool::Marker
1750 {
1751 public:
AreaMarker(int size,Color color,Color outline,int outline_width)1752 	AreaMarker(int size, Color color, Color outline, int outline_width)
1753 	: size(size), color(color), outline(outline), outline_width(outline_width)
1754 	{ out_size = (size + outline_width) >> 1; }
1755 
GetSize() const1756 	virtual int  GetSize() const { return out_size; }
1757 
1758 public:
1759 	int          size;
1760 	Color        color;
1761 	Color        outline;
1762 	int          outline_width;
1763 	int          out_size;
1764 };
1765 
1766 class SquareMarker : public AreaMarker
1767 {
1768 public:
SquareMarker(int size,Color color,Color outline,int outline_width)1769 	SquareMarker(int size, Color color, Color outline, int outline_width)
1770 	: AreaMarker(size, color, outline, outline_width) {}
1771 
1772 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1773 };
1774 
Paint(Draw & draw,const Vector<Point> & pt)1775 void SquareMarker::Paint(Draw& draw, const Vector<Point>& pt)
1776 {
1777 	Vector<Point> outpt;
1778 	int obj = pt.GetCount();
1779 	outpt.SetCount(4 * obj);
1780 	Point *op = outpt.Begin();
1781 	int half = size >> 1;
1782 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
1783 	{
1784 		op[0].x = op[3].x = ip->x - half;
1785 		op[0].y = op[1].y = ip->y - half;
1786 		op[1].x = op[2].x = ip->x + half;
1787 		op[2].y = op[3].y = ip->y + half;
1788 	}
1789 	Vector<int> outix;
1790 	outix.SetCount(obj, 4);
1791 	draw.DrawPolygons(outpt, outix, color, outline_width, outline);
1792 }
1793 
Square(int size,Color color,Color outline,int outline_width)1794 One<MarkTool::Marker> MarkTool::Square(int size, Color color, Color outline, int outline_width)
1795 {
1796 	return new SquareMarker(size, color, outline, outline_width);
1797 }
1798 
1799 class Square45Marker : public AreaMarker
1800 {
1801 public:
Square45Marker(int size,Color color,Color outline,int outline_width)1802 	Square45Marker(int size, Color color, Color outline, int outline_width)
1803 	: AreaMarker(size, color, outline, outline_width) {}
1804 
1805 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1806 };
1807 
Paint(Draw & draw,const Vector<Point> & pt)1808 void Square45Marker::Paint(Draw& draw, const Vector<Point>& pt)
1809 {
1810 	Vector<Point> outpt;
1811 	int obj = pt.GetCount();
1812 	outpt.SetCount(4 * obj);
1813 	Point *op = outpt.Begin();
1814 	int half = size >> 1;
1815 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
1816 	{
1817 		op[0].x = ip->x - half;
1818 		op[0].y = op[2].y = ip->y;
1819 		op[1].x = op[3].x = ip->x;
1820 		op[1].y = ip->y + half;
1821 		op[2].x = ip->x + half;
1822 		op[3].y = ip->y - half;
1823 	}
1824 	Vector<int> outix;
1825 	outix.SetCount(obj, 4);
1826 	draw.DrawPolygons(outpt, outix, color, outline_width, outline);
1827 }
1828 
Square45(int size,Color color,Color outline,int outline_width)1829 One<MarkTool::Marker> MarkTool::Square45(int size, Color color, Color outline, int outline_width)
1830 {
1831 	return new Square45Marker(size, color, outline, outline_width);
1832 }
1833 
1834 class TriangleMarker : public AreaMarker
1835 {
1836 public:
TriangleMarker(int size,Color color,Color outline,int outline_width)1837 	TriangleMarker(int size, Color color, Color outline, int outline_width)
1838 	: AreaMarker(size, color, outline, outline_width) {}
1839 
1840 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1841 };
1842 
Paint(Draw & draw,const Vector<Point> & pt)1843 void TriangleMarker::Paint(Draw& draw, const Vector<Point>& pt)
1844 {
1845 	int half = size >> 1;
1846 	Vector<Point> outpt;
1847 	int obj = pt.GetCount();
1848 	outpt.SetCount(3 * obj);
1849 	Point *op = outpt.Begin();
1850 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 3)
1851 	{
1852 		op[0].x = ip->x - half;
1853 		op[1].x = ip->x + half;
1854 		op[0].y = op[1].y = ip->y + half + 1;
1855 		op[2].x = ip->x;
1856 		op[2].y = ip->y - half;
1857 	}
1858 	Vector<int> outix;
1859 	outix.SetCount(obj, 3);
1860 	draw.DrawPolygons(outpt, outix, color, outline_width, outline);
1861 }
1862 
Triangle(int size,Color color,Color outline,int outline_width)1863 One<MarkTool::Marker> MarkTool::Triangle(int size, Color color, Color outline, int outline_width)
1864 {
1865 	return new TriangleMarker(size, color, outline, outline_width);
1866 }
1867 
1868 class NablaMarker : public AreaMarker
1869 {
1870 public:
NablaMarker(int size,Color color,Color outline,int outline_width)1871 	NablaMarker(int size, Color color, Color outline, int outline_width)
1872 	: AreaMarker(size, color, outline, outline_width) {}
1873 
1874 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1875 };
1876 
Paint(Draw & draw,const Vector<Point> & pt)1877 void NablaMarker::Paint(Draw& draw, const Vector<Point>& pt)
1878 {
1879 	int half = size >> 1;
1880 	Vector<Point> outpt;
1881 	int obj = pt.GetCount();
1882 	outpt.SetCount(3 * obj);
1883 	Point *op = outpt.Begin();
1884 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 3)
1885 	{
1886 		op[0].x = ip->x - half;
1887 		op[1].x = ip->x + half;
1888 		op[0].y = op[1].y = ip->y - half;
1889 		op[2].x = ip->x;
1890 		op[2].y = ip->y + half + 1;
1891 	}
1892 	Vector<int> outix;
1893 	outix.SetCount(obj, 3);
1894 	draw.DrawPolygons(outpt, outix, color, outline_width, outline);
1895 }
1896 
Nabla(int size,Color color,Color outline,int outline_width)1897 One<MarkTool::Marker> MarkTool::Nabla(int size, Color color, Color outline, int outline_width)
1898 {
1899 	return new NablaMarker(size, color, outline, outline_width);
1900 }
1901 
1902 class CircleMarker : public AreaMarker
1903 {
1904 public:
1905 	CircleMarker(int size, Color color, Color outline, int outline_width);
1906 
1907 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1908 };
1909 
CircleMarker(int size,Color color,Color outline,int outline_width)1910 CircleMarker::CircleMarker(int size, Color color, Color outline, int outline_width)
1911 : AreaMarker(size, color, Nvl(outline, color), IsNull(outline) ? 0 : outline_width)
1912 {
1913 }
1914 
Paint(Draw & draw,const Vector<Point> & pt)1915 void CircleMarker::Paint(Draw& draw, const Vector<Point>& pt)
1916 {
1917 	int half = size >> 1;
1918 	for(int t = 0; t < pt.GetCount(); t++)
1919 		draw.DrawEllipse(RectC(pt[t].x - half, pt[t].y - half, size, size),
1920 			color, outline_width, Nvl(outline, color));
1921 }
1922 
Circle(int size,Color color,Color outline,int outline_width)1923 One<MarkTool::Marker> MarkTool::Circle(int size, Color color, Color outline, int outline_width)
1924 {
1925 	return new CircleMarker(size, color, outline, outline_width);
1926 }
1927 
1928 class CrossMarker : public AreaMarker
1929 {
1930 public:
CrossMarker(int size,Color outline,int outline_width)1931 	CrossMarker(int size, Color outline, int outline_width)
1932 	: AreaMarker(size, Null, outline, outline_width) {}
1933 
1934 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1935 };
1936 
Paint(Draw & draw,const Vector<Point> & pt)1937 void CrossMarker::Paint(Draw& draw, const Vector<Point>& pt)
1938 {
1939 	int half = size >> 1;
1940 	Vector<Point> out;
1941 	out.SetCount(pt.GetCount() * 4);
1942 	Point *op = out.Begin();
1943 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 4)
1944 	{
1945 		op[0].x = ip->x - half;
1946 		op[1].x = ip->x + half;
1947 		op[0].y = op[1].y = ip->y;
1948 		op[2].x = op[3].x = ip->x;
1949 		op[2].y = ip->y - half;
1950 		op[3].y = ip->y + half;
1951 	}
1952 	Vector<int> seg;
1953 	seg.SetCount(2 * pt.GetCount(), 2);
1954 	draw.DrawPolyPolyline(out, seg, outline_width, outline);
1955 }
1956 
Cross(int size,Color outline,int outline_width)1957 One<MarkTool::Marker> MarkTool::Cross(int size, Color outline, int outline_width)
1958 {
1959 	return new CrossMarker(size, outline, outline_width);
1960 }
1961 
1962 class DiamondMarker : public AreaMarker
1963 {
1964 public:
1965 	DiamondMarker(int size, Color color, Color outline, int outline_width);
1966 
1967 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
1968 
1969 private:
1970 	int x2, y2, x4;
1971 };
1972 
DiamondMarker(int size,Color color,Color outline,int outline_width)1973 DiamondMarker::DiamondMarker(int size, Color color, Color outline, int outline_width)
1974 : AreaMarker(size, color, outline, outline_width)
1975 {
1976 	y2 = size >> 1;
1977 	x2 = size * 17 >> 5;
1978 	x4 = x2 >> 1;
1979 }
1980 
Paint(Draw & draw,const Vector<Point> & pt)1981 void DiamondMarker::Paint(Draw& draw, const Vector<Point>& pt)
1982 {
1983 	Vector<Point> out;
1984 	out.SetCount(pt.GetCount() * 5);
1985 	Point *op = out.Begin();
1986 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 5)
1987 	{
1988 		op[0].x = ip->x - x4; op[0].y = ip->y + y2;
1989 		op[1].x = ip->x + x4; op[1].y = ip->y + y2;
1990 		op[2].x = ip->x + x2; op[2].y = ip->y;
1991 		op[3].x = ip->x;      op[3].y = ip->y - y2;
1992 		op[4].x = ip->x - x2; op[4].y = ip->y;
1993 	}
1994 	Vector<int> seg;
1995 	seg.SetCount(pt.GetCount(), 5);
1996 	draw.DrawPolygons(out, seg, color, outline_width, outline);
1997 }
1998 
Diamond(int size,Color color,Color outline,int outline_width)1999 One<MarkTool::Marker> MarkTool::Diamond(int size, Color color, Color outline, int outline_width)
2000 {
2001 	return new DiamondMarker(size, color, outline, outline_width);
2002 }
2003 
2004 class HexagonMarker : public AreaMarker
2005 {
2006 public:
2007 	HexagonMarker(int size, Color color, Color outline, int outline_width);
2008 
2009 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
2010 
2011 private:
2012 	int x2, y2, x4;
2013 };
2014 
HexagonMarker(int size,Color color,Color outline,int outline_width)2015 HexagonMarker::HexagonMarker(int size, Color color, Color outline, int outline_width)
2016 : AreaMarker(size, color, outline, outline_width)
2017 {
2018 	y2 = size >> 1;
2019 	x2 = size * 19 >> 5;
2020 	x4 = x2 >> 1;
2021 }
2022 
Paint(Draw & draw,const Vector<Point> & pt)2023 void HexagonMarker::Paint(Draw& draw, const Vector<Point>& pt)
2024 {
2025 	Vector<Point> out;
2026 	out.SetCount(pt.GetCount() * 6);
2027 	Point *op = out.Begin();
2028 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 6)
2029 	{
2030 		op[0].x = ip->x - x4; op[0].y = ip->y + y2;
2031 		op[1].x = ip->x + x4; op[1].y = ip->y + y2;
2032 		op[2].x = ip->x + x2; op[2].y = ip->y;
2033 		op[3].x = ip->x + x4; op[3].y = ip->y - y2;
2034 		op[4].x = ip->x - x4; op[4].y = ip->y - y2;
2035 		op[5].x = ip->x - x2; op[5].y = ip->y;
2036 	}
2037 	Vector<int> seg;
2038 	seg.SetCount(pt.GetCount(), 6);
2039 	draw.DrawPolygons(out, seg, color, outline_width, outline);
2040 }
2041 
Hexagon(int size,Color color,Color outline,int outline_width)2042 One<MarkTool::Marker> MarkTool::Hexagon(int size, Color color, Color outline, int outline_width)
2043 {
2044 	return new HexagonMarker(size, color, outline, outline_width);
2045 }
2046 
2047 class StarMarker : public AreaMarker
2048 {
2049 public:
2050 	StarMarker(int size, Color color, Color outline, int outline_width);
2051 
2052 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
2053 
2054 private:
2055 	int x2, x4, X2, X4, y2, y4, Y2, Y4, dy;
2056 };
2057 
StarMarker(int size,Color color,Color outline,int outline_width)2058 StarMarker::StarMarker(int size, Color color, Color outline, int outline_width)
2059 : AreaMarker(size, color, outline, outline_width)
2060 {
2061 	y2 = size >> 1;
2062 	x2 = size * 18 >> 5;
2063 	x4 = size * 11 >> 5;
2064 	Y2 = y2 * 5 >> 4;
2065 	Y4 = Y2 * 5 >> 4;
2066 	X2 = x2 * 5 >> 4;
2067 	X4 = x4 * 5 >> 4;
2068 	dy = size / 18;
2069 }
2070 
Paint(Draw & draw,const Vector<Point> & pt)2071 void StarMarker::Paint(Draw& draw, const Vector<Point>& pt)
2072 {
2073 	Vector<Point> out;
2074 	out.SetCount(pt.GetCount() * 10);
2075 	Point *op = out.Begin();
2076 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 10)
2077 	{
2078 		int sy = ip->y + dy;
2079 		op[0].x = ip->x - x4; op[0].y = ip->y + y2;
2080 		op[1].x = ip->x;      op[1].y = sy      + Y2;
2081 		op[2].x = ip->x + x4; op[2].y = ip->y + y2;
2082 		op[3].x = ip->x + X2; op[3].y = sy      + Y4;
2083 		op[4].x = ip->x + x2; op[4].y = ip->y - Y4;
2084 		op[5].x = ip->x + X4; op[5].y = sy      - Y2;
2085 		op[6].x = ip->x;      op[6].y = ip->y - y2;
2086 		op[7].x = ip->x - X4; op[7].y = sy      - Y2;
2087 		op[8].x = ip->x - x2; op[8].y = ip->y - Y4;
2088 		op[9].x = ip->x - X2; op[9].y = sy      + Y4;
2089 	}
2090 	Vector<int> seg;
2091 	seg.SetCount(pt.GetCount(), 10);
2092 	draw.DrawPolygons(out, seg, color, outline_width, outline);
2093 }
2094 
Star(int size,Color color,Color outline,int outline_width)2095 One<MarkTool::Marker> MarkTool::Star(int size, Color color, Color outline, int outline_width)
2096 {
2097 	return new StarMarker(size, color, outline, outline_width);
2098 }
2099 
2100 class ThickCrossMarker : public AreaMarker
2101 {
2102 public:
2103 	ThickCrossMarker(int size, Color color, Color outline, int outline_width);
2104 
2105 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
2106 
2107 private:
2108 	int s2, s6;
2109 };
2110 
ThickCrossMarker(int size,Color color,Color outline,int outline_width)2111 ThickCrossMarker::ThickCrossMarker(int size, Color color, Color outline, int outline_width)
2112 : AreaMarker(size, color, outline, outline_width)
2113 {
2114 	s2 = size >> 1;
2115 	s6 = size / 6;
2116 }
2117 
Paint(Draw & draw,const Vector<Point> & pt)2118 void ThickCrossMarker::Paint(Draw& draw, const Vector<Point>& pt)
2119 {
2120 	Vector<Point> out;
2121 	out.SetCount(pt.GetCount() * 12);
2122 	Point *op = out.Begin();
2123 	for(const Point *ip = pt.Begin(), *ie = pt.End(); ip < ie; ip++, op += 12) {
2124 		int x = ip->x, y = ip->y;
2125 		op[0].x = op[11].x = x + s2;
2126 		op[1].x = op[2].x = op[9].x = op[10].x = x + s6;
2127 		op[3].x = op[4].x = op[7].x = op[8].x = x - s6;
2128 		op[5].x = op[6].x = x - s2;
2129 
2130 		op[2].y = op[3].y = y + s2;
2131 		op[0].y = op[1].y = op[4].y = op[5].y = y + s6;
2132 		op[6].y = op[7].y = op[10].y = op[11].y = y - s6;
2133 		op[8].y = op[9].y = y - s2;
2134 	}
2135 	Vector<int> seg;
2136 	seg.SetCount(pt.GetCount(), 12);
2137 	draw.DrawPolygons(out, seg, color, outline_width, outline);
2138 }
2139 
ThickCross(int size,Color color,Color outline,int outline_width)2140 One<MarkTool::Marker> MarkTool::ThickCross(int size, Color color, Color outline, int outline_width)
2141 {
2142 	return new ThickCrossMarker(size, color, outline, outline_width);
2143 }
2144 
2145 class LetterMarker : public MarkTool::Marker
2146 {
2147 public:
2148 	LetterMarker(char ascii, int angle, Font font, Color color, Color outline);
2149 
2150 	virtual void Paint(Draw& draw, const Vector<Point>& pt);
2151 	virtual int  GetSize() const;
2152 
2153 private:
2154 	void         PaintSimple(Point pt);
2155 	void         PaintOutline(Point pt);
2156 	void         PaintImage(Point pt);
2157 	void         PaintEmpty(Point pt);
2158 
2159 private:
2160 	Draw         *draw;
2161 	char          ascii;
2162 	Font          font;
2163 	Font          raw_font;
2164 	Color         color;
2165 	Color         outline;
2166 //	Image         image;
2167 //	ImageDraw     data;
2168 //	ImageMaskDraw mask;
2169 	int           angle;
2170 	int           img_size;
2171 	int           raw_angle;
2172 	Size          offset;
2173 
2174 //	HFONT        new_font, old_font;
2175 //	int          old_align;
2176 };
2177 
LetterMarker(char ascii,int angle,Font font,Color color,Color outline)2178 LetterMarker::LetterMarker(char ascii, int angle, Font font, Color color, Color outline)
2179 : ascii(ascii)
2180 , angle(angle)
2181 , font(font)
2182 , color(color)
2183 , outline(outline)
2184 {
2185 }
2186 
GetSize() const2187 int LetterMarker::GetSize() const
2188 {
2189 	Font raw = font;
2190 	if(raw.GetHeight() >= 0)
2191 		raw.Height(-raw.GetHeight());
2192 	else
2193 		raw.Height(-DotsToPixels(ScreenInfo(), -raw.GetHeight()));
2194 	FontInfo info = raw.Info();
2195 	return fceil(hypot(info[(byte)ascii], info.GetHeight())) >> 1;
2196 }
2197 
DrawTextWithMask(Draw & data,Draw & mask,int x,int y,char letter,int angle,Font font,Color cdata,Color cmask)2198 static void DrawTextWithMask(Draw& data, Draw& mask, int x, int y, char letter, int angle, Font font, Color cdata, Color cmask)
2199 {
2200 	data.DrawText(x, y, angle, &letter, font, cdata, 1);
2201 	mask.DrawText(x, y, angle, &letter, font, cmask, 1);
2202 }
2203 
Paint(Draw & draw,const Vector<Point> & pt)2204 void LetterMarker::Paint(Draw& draw, const Vector<Point>& pt)
2205 {
2206 	double ang = angle * (M_PI / 180), c = cos(ang), s = sin(ang);
2207 
2208 	raw_font = font;
2209 	if(raw_font.GetHeight() >= 0)
2210 		raw_font.Height(-raw_font.GetHeight());
2211 	else
2212 		raw_font.Height(-DotsToPixels(draw, -raw_font.GetHeight()));
2213 	Size size = GetTextSize(&ascii, raw_font, 1);
2214 //	offset = Pointf(size).Rotated(-angle * M_PI / 1800.0).AsSize() >> 1;
2215 
2216 /*
2217 	new_font = CreateFont(raw_font.GetHeight(),
2218 		                  0, 10 * angle, 10 * angle, raw_font.IsBold() ? FW_BOLD : FW_NORMAL,
2219 		                  raw_font.IsItalic(), raw_font.IsUnderline(), raw_font.IsStrikeout(),
2220 						  raw_font.GetCharSet() ? raw_font.GetCharSet() : DEFAULT_CHARSET,
2221 						  OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
2222 						  DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE,
2223 						  raw_font.GetFaceName());
2224 	draw->BeginGdi();
2225 	old_font = (HFONT)SelectObject(*draw, new_font);
2226 */
2227 //	Size size;
2228 //	GetTextExtentPoint32(*draw, &ascii, 1, &size);
2229 
2230 	raw_angle = 10 * angle;
2231 	offset.cx = fround((size.cx * c + size.cy * s) * 0.5);
2232 	offset.cy = fround((size.cx * s - size.cy * c) * -0.5);
2233 
2234 	if(IsNull(color))
2235 		;
2236 	else if(IsNull(outline))
2237 	{
2238 		for(int i = 0; i < pt.GetCount(); i++)
2239 			draw.DrawText(pt[i].x - offset.cx, pt[i].y - offset.cy, raw_angle, &ascii, raw_font, color, 1);
2240 	}
2241 	else
2242 	{
2243 		for(int i = 0; i < pt.GetCount(); i++)
2244 		{
2245 			Point pos = pt[i] - offset;
2246 			draw.DrawText(pos.x + 1, pos.y + 0, raw_angle, &ascii, raw_font, outline, 1);
2247 			draw.DrawText(pos.x + 0, pos.y - 1, raw_angle, &ascii, raw_font, outline, 1);
2248 			draw.DrawText(pos.x - 1, pos.y + 0, raw_angle, &ascii, raw_font, outline, 1);
2249 			draw.DrawText(pos.x + 0, pos.y + 1, raw_angle, &ascii, raw_font, outline, 1);
2250 			draw.DrawText(pos.x + 0, pos.y + 0, raw_angle, &ascii, raw_font, color, 1);
2251 		}
2252 	}
2253 
2254 /*
2255 	if(!IsNull(outline))
2256 	{
2257 		if(IsNull(color))
2258 		{ // generate image with outline
2259 			img_size = abs(size);
2260 			image = Image(*draw, img_size, img_size);
2261 			data.Open(*draw, image);
2262 			mask.Open(image);
2263 			Point pos((img_size >> 1) - offset.cx, (img_size >> 1) - offset.cy);
2264 			data.DrawRect(0, 0, img_size, img_size, Black);
2265 			mask.DrawRect(0, 0, img_size, img_size, White);
2266 			DrawTextWithMask(data, mask, pos.x + 1, pos.y + 0, ascii, raw_angle, raw_font, outline, Black);
2267 			DrawTextWithMask(data, mask, pos.x + 0, pos.y - 1, ascii, raw_angle, raw_font, outline, Black);
2268 			DrawTextWithMask(data, mask, pos.x - 1, pos.y + 0, ascii, raw_angle, raw_font, outline, Black);
2269 			DrawTextWithMask(data, mask, pos.x + 0, pos.y + 1, ascii, raw_angle, raw_font, outline, Black);
2270 			DrawTextWithMask(data, mask, pos.x + 0, pos.y + 0, ascii, raw_angle, raw_font, Black, White);
2271 			offset.cx = offset.cy = img_size >> 1;
2272 			PaintRaw = &LetterMarker::PaintImage;
2273 		}
2274 		else
2275 			PaintRaw = &LetterMarker::PaintOutline;
2276 	}
2277 */
2278 
2279 //	old_align = SetTextAlign(*draw, TA_LEFT | TA_TOP);
2280 //	SetTextColor(*draw, color);
2281 }
2282 
Letter(char ascii,int angle,Font font,Color color,Color outline)2283 One<MarkTool::Marker> MarkTool::Letter(char ascii, int angle, Font font, Color color, Color outline)
2284 {
2285 	return new LetterMarker(ascii, angle, font, color, outline);
2286 }
2287 
StandardMarker(int type,int size,Color color,Color outline,int outline_width)2288 One<MarkTool::Marker> MarkTool::StandardMarker(int type, int size, Color color, Color outline, int outline_width)
2289 {
2290 	switch(type)
2291 	{
2292 	case CIRCLE:   return Circle(size, color, outline, outline_width);
2293 	case SQUARE:   return Square(size, color, outline, outline_width);
2294 	case TRIANGLE: return Triangle(size, color, outline, outline_width);
2295 	case CROSS:    return Cross(size, outline, outline_width);
2296 	case DIAMOND:  return Diamond(size, color, outline, outline_width);
2297 	case HEXAGON:  return Hexagon(size, color, outline, outline_width);
2298 	case STAR:     return Star(size, color, outline, outline_width);
2299 	case SQUARE45: return Square45(size, color, outline, outline_width);
2300 	case NABLA:    return Nabla(size, color, outline, outline_width);
2301 	default:       return Empty();
2302 	}
2303 }
2304 
MarkTool()2305 MarkTool::MarkTool()
2306 {
2307 	PutRaw = &MarkTool::PutDummy;
2308 }
2309 
~MarkTool()2310 MarkTool::~MarkTool() {}
2311 
Reset()2312 void MarkTool::Reset()
2313 {
2314 	marker.Clear();
2315 	Clear();
2316 }
2317 
Set(const Plotter & info,One<Marker> _marker)2318 bool MarkTool::Set(const Plotter& info, One<Marker> _marker)
2319 {
2320 	if(!_marker) {
2321 		Reset();
2322 		return false;
2323 	}
2324 	marker = _marker;
2325 	size = marker->GetSize();
2326 	plotter.Set(info, size);
2327 	Clear();
2328 	return true;
2329 }
2330 
SetExtent(const Rectf & rc)2331 bool MarkTool::SetExtent(const Rectf& rc)
2332 {
2333 	if(marker && plotter.IntersectsLClip(rc)) {
2334 		Rectf crc = plotter.LtoP(rc);
2335 		if(plotter.InPClip(crc))
2336 			PutRaw = &MarkTool::PutSimple;
2337 		else
2338 			PutRaw = &MarkTool::PutClip;
2339 		return true;
2340 	}
2341 	else {
2342 		PutRaw = &MarkTool::PutDummy;
2343 		return false;
2344 	}
2345 }
2346 
ClearExtent()2347 void MarkTool::ClearExtent()
2348 {
2349 	if(marker)
2350 		PutRaw = &MarkTool::PutClip;
2351 	else
2352 		PutRaw = &MarkTool::PutDummy;
2353 }
2354 
Put(const Array<Pointf> & pt)2355 void MarkTool::Put(const Array<Pointf>& pt)
2356 {
2357 	for(int i = 0; i < pt.GetCount(); i++)
2358 		Put(pt[i]);
2359 }
2360 
Paint()2361 void MarkTool::Paint()
2362 {
2363 	if(!plotter.draw)
2364 		return;
2365 	Flush();
2366 	ClearExtent();
2367 }
2368 
Flush()2369 void MarkTool::Flush()
2370 {
2371 	if(!vertices.IsEmpty()) {
2372 		marker->Paint(*plotter.draw, vertices);
2373 		vertices.Clear();
2374 	}
2375 }
2376 
Clear()2377 void MarkTool::Clear()
2378 {
2379 	vertices.Clear();
2380 	ClearExtent();
2381 }
2382 
PutDummy(Pointf pt)2383 void MarkTool::PutDummy(Pointf pt) {}
2384 
PutSimple(Pointf pt)2385 void MarkTool::PutSimple(Pointf pt)
2386 {
2387 	if(IsNull(pt.x) || IsNull(pt.y))
2388 		return;
2389 	vertices.Add(plotter.LtoPoint(pt));
2390 	if(vertices.GetCount() >= BUF_COUNT)
2391 		Flush();
2392 }
2393 
PutClip(Pointf pt)2394 void MarkTool::PutClip(Pointf pt)
2395 {
2396 	if(IsNull(pt.x) || IsNull(pt.y))
2397 		return;
2398 	Pointf cpt = plotter.LtoP(pt);
2399 	if(plotter.InPClip(cpt))
2400 	{
2401 		vertices.Add(PointfToPoint(cpt));
2402 		if(vertices.GetCount() >= BUF_COUNT)
2403 			Flush();
2404 	}
2405 }
2406 
GetTextSize(const String & s) const2407 Size TextTool::Type::GetTextSize(const String& s) const
2408 {
2409 	int wd = 0;
2410 	const byte *b = s;
2411 	unsigned len = s.GetLength();
2412 	for(unsigned rep = len >> 2; rep; rep--)
2413 	{
2414 		wd += widths[b[0]] + widths[b[1]] + widths[b[2]] + widths[b[3]];
2415 		b += 4;
2416 	}
2417 	switch(len & 3)
2418 	{
2419 	case 3: wd += widths[b[0]] + widths[b[1]] + widths[b[2]]; break;
2420 	case 2: wd += widths[b[0]] + widths[b[1]]; break;
2421 	case 1: wd += widths[b[0]]; break;
2422 	}
2423 	return Size(wd, height);
2424 }
2425 
TextTool()2426 TextTool::TextTool()
2427 {
2428 }
2429 
~TextTool()2430 TextTool::~TextTool() {}
2431 
Set(const Plotter & info,Font _f,Color _c,Color _o,double _a,Alignment _x,Alignment _y,bool _flip,int _aprec)2432 void TextTool::Set(const Plotter& info, Font _f, Color _c, Color _o, double _a, Alignment _x, Alignment _y, bool _flip, int _aprec)
2433 {
2434 	cache_size = 0;
2435 	font_index = 0;
2436 	plotter    = info;
2437 	color      = _c;
2438 	outline    = _o;
2439 	angle      = _a;
2440 	x_align    = _x;
2441 	y_align    = _y;
2442 	flip       = _flip;
2443 	angle_prec = _aprec;
2444 	ASSERT(angle_prec > 0 && angle_prec <= 36000);
2445 	SetFont(_f);
2446 }
2447 
SetFont(Font _font)2448 void TextTool::SetFont(Font _font)
2449 {
2450 	if((font_index = font_map.Find(font = _font)) < 0)
2451 	{
2452 		if(font_map.GetCount() >= FONT_LIMIT)
2453 			Paint();
2454 		font_index = font_map.GetCount();
2455 		Type& type = font_map.Add(_font);
2456 		FontInfo fi = _font.Info();
2457 		short *out = type.widths;
2458 		for(int c = 256; --c >= 0; out[c] = fi.GetWidth(c))
2459 			;
2460 		type.height = fi.GetHeight();
2461 	}
2462 }
2463 
PutFlip(const String & text,Pointf pt,double angle)2464 void TextTool::PutFlip(const String& text, Pointf pt, double angle)
2465 {
2466 	if(flip && (angle >= 0.5 * M_PI && angle <= 1.5 * M_PI || angle <= -0.5 * M_PI))
2467 		angle -= M_PI;
2468 	Put(text, pt, angle);
2469 }
2470 
Put(const String & text,Pointf pt,double angle)2471 void TextTool::Put(const String& text, Pointf pt, double angle)
2472 {
2473 	if(IsNull(pt.x) || IsNull(pt.y))
2474 		return;
2475 	Size size = font_map[font_index].GetTextSize(text);
2476 	int delta = size.cx + size.cy;
2477 	Pointf pos = plotter.LtoP(pt);
2478 	if(pos.x >= plotter.clip.right + delta  || pos.x <= plotter.clip.left - delta
2479 	|| pos.y >= plotter.clip.bottom + delta || pos.y <= plotter.clip.top - delta)
2480 		return;
2481 	Pointf offset(0, 0);
2482 	switch(x_align)
2483 	{
2484 	case ALIGN_LEFT:   break;
2485 	case ALIGN_RIGHT:  offset.x -= size.cx; break;
2486 	default:           offset.x -= size.cx >> 1; break;
2487 	}
2488 	switch(y_align)
2489 	{
2490 	case ALIGN_TOP:    break;
2491 	case ALIGN_BOTTOM: offset.y -= size.cy; break;
2492 	default:           offset.y -= size.cy >> 1; break;
2493 	}
2494 	int apart = fround(angle * angle_prec / (2 * M_PI)) % angle_prec;
2495 	if(apart < 0) apart += angle_prec;
2496 	if(apart)
2497 		offset = Rotated(offset, (-2 * M_PI * apart) / angle_prec);
2498 	One<Item> item = new Item(PointfToPoint(pos + offset), text, color, outline);
2499 	int sz = item->GetSize();
2500 	if(cache_size + sz > CACHE_LIMIT) // flush
2501 		Paint();
2502 	cache_size += sz;
2503 	cache.GetAdd(font_index * angle_prec + apart).Add(item.Detach());
2504 }
2505 
Paint()2506 void TextTool::Paint()
2507 {
2508 	if(plotter.draw)
2509 		Flush();
2510 	Clear();
2511 }
2512 
Clear()2513 void TextTool::Clear()
2514 {
2515 	ClearCache();
2516 	SetFont(font);
2517 }
2518 
Flush()2519 void TextTool::Flush()
2520 {
2521 	Draw& draw = *plotter.draw;
2522 	for(int i = 0; i < cache.GetCount(); i++)
2523 	{
2524 		int angle = cache.GetKey(i);
2525 		Font font = font_map.GetKey(angle / angle_prec);
2526 		Font font_naa = Font(font).NonAntiAliased();
2527 		angle = (angle % angle_prec) * 3600 / angle_prec;
2528 		const Array<Item>& items = cache[i];
2529 		for(int t = 0; t < items.GetCount(); t++)
2530 		{
2531 			const Item& ii = items[t];
2532 			if(!IsNull(ii.outline))
2533 			{
2534 				draw.DrawText(ii.point.x - 1, ii.point.y, angle, ii.text, font_naa, ii.outline);
2535 				draw.DrawText(ii.point.x + 1, ii.point.y, angle, ii.text, font_naa, ii.outline);
2536 				draw.DrawText(ii.point.x, ii.point.y - 1, angle, ii.text, font_naa, ii.outline);
2537 				draw.DrawText(ii.point.x, ii.point.y + 1, angle, ii.text, font_naa, ii.outline);
2538 				draw.DrawText(ii.point.x, ii.point.y, angle, ii.text, font_naa, ii.color);
2539 			}
2540 			else
2541 				draw.DrawText(ii.point.x, ii.point.y, angle, ii.text, font, ii.color);
2542 		}
2543 	}
2544 	ClearCache();
2545 }
2546 
ClearCache()2547 void TextTool::ClearCache()
2548 {
2549 	font_map.Clear();
2550 	cache.Clear();
2551 	cache_size = 0;
2552 }
2553 
2554 #if 0
2555 class TestWindow : public TopWindow
2556 {
2557 public:
2558 	virtual void Paint(Draw& draw);
2559 };
2560 
2561 void TestWindow::Paint(Draw& draw)
2562 {
2563 	draw.DrawRect(draw.GetPaintRect(), LtCyan);
2564 	Plotter plotter(draw);
2565 	PathTool path;
2566 	path.Set(plotter, SolidLine);
2567 	for(int i = 0; i < 320; i += 20)
2568 	{
2569 		Rectf rc = Pointf(300, 200);
2570 		rc.Inflate(i);
2571 		path.Rectangle(rc);
2572 		path.Circle(Pointf(300, 200), i);
2573 	}
2574 	Rect rc = RectC(GetSize());
2575 	rc.DeflateRect(20, 20);
2576 	AreaTool area;
2577 	area.Set(Plotter(plotter, rc), LtBlue, Image(), DashDotDotLine, Black, -50, 0);
2578 	area.Rectangle(100, 100, 400, 400);
2579 	area.Circle(400, 250, 100);
2580 //	area.MoveTo(100, 0);
2581 //	area.LineTo(0, 400);
2582 //	area.LineTo(300, 500);
2583 //	area.Circle(300, 200, 200);
2584 	area.Paint();
2585 }
2586 
2587 void PlotterTest()
2588 {
2589 	Size size(600, 400);
2590 	TestWindow window;
2591 	window.SetRect(CalcWindowRect(window, size));
2592 	window.Sizeable().Zoomable();
2593 
2594 	window.Run();
2595 }
2596 #endif
2597 
2598 }
2599