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