1 /**********************************************************************
2 * File: coutln.c (Formerly coutline.c)
3 * Description: Code for the C_OUTLINE class.
4 * Author: Ray Smith
5 * Created: Mon Oct 07 16:01:57 BST 1991
6 *
7 * (C) Copyright 1991, Hewlett-Packard Ltd.
8 ** Licensed under the Apache License, Version 2.0 (the "License");
9 ** you may not use this file except in compliance with the License.
10 ** You may obtain a copy of the License at
11 ** http://www.apache.org/licenses/LICENSE-2.0
12 ** Unless required by applicable law or agreed to in writing, software
13 ** distributed under the License is distributed on an "AS IS" BASIS,
14 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 ** See the License for the specific language governing permissions and
16 ** limitations under the License.
17 *
18 **********************************************************************/
19
20 #include "mfcpch.h"
21 #include <string.h>
22 #ifdef __UNIX__
23 #include <assert.h>
24 #endif
25 #include "coutln.h"
26
27 ELISTIZE_S (C_OUTLINE)
28 ICOORD C_OUTLINE::step_coords[4] = {
29 ICOORD (-1, 0), ICOORD (0, -1), ICOORD (1, 0), ICOORD (0, 1)
30 };
31
32 /**********************************************************************
33 * C_OUTLINE::C_OUTLINE
34 *
35 * Constructor to build a C_OUTLINE from a CRACKEDGE LOOP.
36 **********************************************************************/
37
C_OUTLINE(CRACKEDGE * startpt,ICOORD bot_left,ICOORD top_right,inT16 length)38 C_OUTLINE::C_OUTLINE (
39 //constructor
40 CRACKEDGE * startpt, //outline to convert
41 ICOORD bot_left, //bounding box
42 ICOORD top_right, inT16 length //length of loop
43 ):box (bot_left, top_right), start (startpt->pos) {
44 inT16 stepindex; //index to step
45 CRACKEDGE *edgept; //current point
46
47 stepcount = length; //no of steps
48 if (length == 0) {
49 steps = NULL;
50 return;
51 }
52 //get memory
53 steps = (uinT8 *) alloc_mem (step_mem());
54 memset(steps, 0, step_mem());
55 edgept = startpt;
56
57 for (stepindex = 0; stepindex < length; stepindex++) {
58 //set compact step
59 set_step (stepindex, edgept->stepdir);
60 edgept = edgept->next;
61 }
62 }
63
64
65 /**********************************************************************
66 * C_OUTLINE::C_OUTLINE
67 *
68 * Constructor to build a C_OUTLINE from a C_OUTLINE_FRAG.
69 **********************************************************************/
C_OUTLINE(ICOORD startpt,DIR128 * new_steps,inT16 length)70 C_OUTLINE::C_OUTLINE (
71 //constructor
72 //steps to copy
73 ICOORD startpt, DIR128 * new_steps,
74 inT16 length //length of loop
75 ):start (startpt) {
76 inT8 dirdiff; //direction difference
77 DIR128 prevdir; //previous direction
78 DIR128 dir; //current direction
79 DIR128 lastdir; //dir of last step
80 TBOX new_box; //easy bounding
81 inT16 stepindex; //index to step
82 inT16 srcindex; //source steps
83 ICOORD pos; //current position
84
85 pos = startpt;
86 stepcount = length; //no of steps
87 //get memory
88 steps = (uinT8 *) alloc_mem (step_mem());
89 memset(steps, 0, step_mem());
90
91 lastdir = new_steps[length - 1];
92 prevdir = lastdir;
93 for (stepindex = 0, srcindex = 0; srcindex < length;
94 stepindex++, srcindex++) {
95 new_box = TBOX (pos, pos);
96 box += new_box;
97 //copy steps
98 dir = new_steps[srcindex];
99 set_step(stepindex, dir);
100 dirdiff = dir - prevdir;
101 pos += step (stepindex);
102 if ((dirdiff == 64 || dirdiff == -64) && stepindex > 0) {
103 stepindex -= 2; //cancel there-and-back
104 prevdir = stepindex >= 0 ? step_dir (stepindex) : lastdir;
105 }
106 else
107 prevdir = dir;
108 }
109 ASSERT_HOST (pos.x () == startpt.x () && pos.y () == startpt.y ());
110 do {
111 dirdiff = step_dir (stepindex - 1) - step_dir (0);
112 if (dirdiff == 64 || dirdiff == -64) {
113 start += step (0);
114 stepindex -= 2; //cancel there-and-back
115 for (int i = 0; i < stepindex; ++i)
116 set_step(i, step_dir(i + 1));
117 }
118 }
119 while (stepindex > 1 && (dirdiff == 64 || dirdiff == -64));
120 stepcount = stepindex;
121 ASSERT_HOST (stepcount >= 4);
122 }
123
124 /**********************************************************************
125 * C_OUTLINE::C_OUTLINE
126 *
127 * Constructor to build a C_OUTLINE from a rotation of a C_OUTLINE.
128 **********************************************************************/
129
C_OUTLINE(C_OUTLINE * srcline,FCOORD rotation)130 C_OUTLINE::C_OUTLINE( //constructor
131 C_OUTLINE *srcline, //outline to
132 FCOORD rotation //rotate
133 ) {
134 TBOX new_box; //easy bounding
135 inT16 stepindex; //index to step
136 inT16 dirdiff; //direction change
137 ICOORD pos; //current position
138 ICOORD prevpos; //previous dest point
139
140 ICOORD destpos; //destination point
141 inT16 destindex; //index to step
142 DIR128 dir; //coded direction
143 uinT8 new_step;
144
145 stepcount = srcline->stepcount * 2;
146 if (stepcount == 0) {
147 steps = NULL;
148 box = srcline->box;
149 box.rotate(rotation);
150 return;
151 }
152 //get memory
153 steps = (uinT8 *) alloc_mem (step_mem());
154 memset(steps, 0, step_mem());
155
156 for (int iteration = 0; iteration < 2; ++iteration) {
157 DIR128 round1 = iteration == 0 ? 32 : 0;
158 DIR128 round2 = iteration != 0 ? 32 : 0;
159 pos = srcline->start;
160 prevpos = pos;
161 prevpos.rotate (rotation);
162 start = prevpos;
163 box = TBOX (start, start);
164 destindex = 0;
165 for (stepindex = 0; stepindex < srcline->stepcount; stepindex++) {
166 pos += srcline->step (stepindex);
167 destpos = pos;
168 destpos.rotate (rotation);
169 // printf("%i %i %i %i ", destpos.x(), destpos.y(), pos.x(), pos.y());
170 while (destpos.x () != prevpos.x () || destpos.y () != prevpos.y ()) {
171 dir = DIR128 (FCOORD (destpos - prevpos));
172 dir += 64; //turn to step style
173 new_step = dir.get_dir ();
174 // printf(" %i\n", new_step);
175 if (new_step & 31) {
176 set_step(destindex++, dir + round1);
177 prevpos += step(destindex - 1);
178 if (destindex < 2
179 || ((dirdiff =
180 step_dir (destindex - 1) - step_dir (destindex - 2)) !=
181 -64 && dirdiff != 64)) {
182 set_step(destindex++, dir + round2);
183 prevpos += step(destindex - 1);
184 } else {
185 prevpos -= step(destindex - 1);
186 destindex--;
187 prevpos -= step(destindex - 1);
188 set_step(destindex - 1, dir + round2);
189 prevpos += step(destindex - 1);
190 }
191 }
192 else {
193 set_step(destindex++, dir);
194 prevpos += step(destindex - 1);
195 }
196 while (destindex >= 2 &&
197 ((dirdiff =
198 step_dir (destindex - 1) - step_dir (destindex - 2)) == -64 ||
199 dirdiff == 64)) {
200 prevpos -= step(destindex - 1);
201 prevpos -= step(destindex - 2);
202 destindex -= 2; // Forget u turn
203 }
204 //ASSERT_HOST(prevpos.x() == destpos.x() && prevpos.y() == destpos.y());
205 new_box = TBOX (destpos, destpos);
206 box += new_box;
207 }
208 }
209 ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
210 dirdiff = step_dir (destindex - 1) - step_dir (0);
211 while ((dirdiff == 64 || dirdiff == -64) && destindex > 1) {
212 start += step (0);
213 destindex -= 2;
214 for (int i = 0; i < destindex; ++i)
215 set_step(i, step_dir(i + 1));
216 dirdiff = step_dir (destindex - 1) - step_dir (0);
217 }
218 if (destindex >= 4)
219 break;
220 }
221 ASSERT_HOST(destindex <= stepcount);
222 stepcount = destindex;
223 destpos = start;
224 for (stepindex = 0; stepindex < stepcount; stepindex++) {
225 destpos += step (stepindex);
226 }
227 ASSERT_HOST (destpos.x () == start.x () && destpos.y () == start.y ());
228 }
229
230 // Build a fake outline, given just a bounding box and append to the list.
FakeOutline(const TBOX & box,C_OUTLINE_LIST * outlines)231 void C_OUTLINE::FakeOutline(const TBOX& box, C_OUTLINE_LIST* outlines) {
232 C_OUTLINE_IT ol_it(outlines);
233 // Make a C_OUTLINE from the bounds. This is a bit of a hack,
234 // as there is no outline, just a bounding box, but it works nicely.
235 CRACKEDGE start;
236 start.pos = box.topleft();
237 C_OUTLINE* outline = new C_OUTLINE(&start, box.topleft(), box.botright(), 0);
238 ol_it.add_to_end(outline);
239 }
240
241 /**********************************************************************
242 * C_OUTLINE::area
243 *
244 * Compute the area of the outline.
245 **********************************************************************/
246
area()247 inT32 C_OUTLINE::area() { //winding number
248 int stepindex; //current step
249 inT32 total_steps; //steps to do
250 inT32 total; //total area
251 ICOORD pos; //position of point
252 ICOORD next_step; //step to next pix
253 C_OUTLINE_IT it = child ();
254
255 pos = start_pos ();
256 total_steps = pathlength ();
257 total = 0;
258 for (stepindex = 0; stepindex < total_steps; stepindex++) {
259 //all intersected
260 next_step = step (stepindex);
261 if (next_step.x () < 0)
262 total += pos.y ();
263 else if (next_step.x () > 0)
264 total -= pos.y ();
265 pos += next_step;
266 }
267 for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
268 total += it.data ()->area ();//add areas of children
269
270 return total;
271 }
272
273 /**********************************************************************
274 * C_OUTLINE::perimeter
275 *
276 * Compute the perimeter of the outline and its first level children.
277 **********************************************************************/
278
perimeter()279 inT32 C_OUTLINE::perimeter() {
280 inT32 total_steps; // Return value.
281 C_OUTLINE_IT it = child();
282
283 total_steps = pathlength();
284 for (it.mark_cycle_pt(); !it.cycled_list(); it.forward())
285 total_steps += it.data()->pathlength(); // Add perimeters of children.
286
287 return total_steps;
288 }
289
290
291 /**********************************************************************
292 * C_OUTLINE::outer_area
293 *
294 * Compute the area of the outline.
295 **********************************************************************/
296
outer_area()297 inT32 C_OUTLINE::outer_area() { //winding number
298 int stepindex; //current step
299 inT32 total_steps; //steps to do
300 inT32 total; //total area
301 ICOORD pos; //position of point
302 ICOORD next_step; //step to next pix
303
304 pos = start_pos ();
305 total_steps = pathlength ();
306 if (total_steps == 0)
307 return box.area();
308 total = 0;
309 for (stepindex = 0; stepindex < total_steps; stepindex++) {
310 //all intersected
311 next_step = step (stepindex);
312 if (next_step.x () < 0)
313 total += pos.y ();
314 else if (next_step.x () > 0)
315 total -= pos.y ();
316 pos += next_step;
317 }
318
319 return total;
320 }
321
322
323 /**********************************************************************
324 * C_OUTLINE::count_transitions
325 *
326 * Compute the number of x and y maxes and mins in the outline.
327 **********************************************************************/
328
count_transitions(inT32 threshold)329 inT32 C_OUTLINE::count_transitions( //winding number
330 inT32 threshold //on size
331 ) {
332 BOOL8 first_was_max_x; //what was first
333 BOOL8 first_was_max_y;
334 BOOL8 looking_for_max_x; //what is next
335 BOOL8 looking_for_min_x;
336 BOOL8 looking_for_max_y; //what is next
337 BOOL8 looking_for_min_y;
338 int stepindex; //current step
339 inT32 total_steps; //steps to do
340 //current limits
341 inT32 max_x, min_x, max_y, min_y;
342 inT32 initial_x, initial_y; //initial limits
343 inT32 total; //total changes
344 ICOORD pos; //position of point
345 ICOORD next_step; //step to next pix
346
347 pos = start_pos ();
348 total_steps = pathlength ();
349 total = 0;
350 max_x = min_x = pos.x ();
351 max_y = min_y = pos.y ();
352 looking_for_max_x = TRUE;
353 looking_for_min_x = TRUE;
354 looking_for_max_y = TRUE;
355 looking_for_min_y = TRUE;
356 first_was_max_x = FALSE;
357 first_was_max_y = FALSE;
358 initial_x = pos.x ();
359 initial_y = pos.y (); //stop uninit warning
360 for (stepindex = 0; stepindex < total_steps; stepindex++) {
361 //all intersected
362 next_step = step (stepindex);
363 pos += next_step;
364 if (next_step.x () < 0) {
365 if (looking_for_max_x && pos.x () < min_x)
366 min_x = pos.x ();
367 if (looking_for_min_x && max_x - pos.x () > threshold) {
368 if (looking_for_max_x) {
369 initial_x = max_x;
370 first_was_max_x = FALSE;
371 }
372 total++;
373 looking_for_max_x = TRUE;
374 looking_for_min_x = FALSE;
375 min_x = pos.x (); //reset min
376 }
377 }
378 else if (next_step.x () > 0) {
379 if (looking_for_min_x && pos.x () > max_x)
380 max_x = pos.x ();
381 if (looking_for_max_x && pos.x () - min_x > threshold) {
382 if (looking_for_min_x) {
383 initial_x = min_x; //remember first min
384 first_was_max_x = TRUE;
385 }
386 total++;
387 looking_for_max_x = FALSE;
388 looking_for_min_x = TRUE;
389 max_x = pos.x ();
390 }
391 }
392 else if (next_step.y () < 0) {
393 if (looking_for_max_y && pos.y () < min_y)
394 min_y = pos.y ();
395 if (looking_for_min_y && max_y - pos.y () > threshold) {
396 if (looking_for_max_y) {
397 initial_y = max_y; //remember first max
398 first_was_max_y = FALSE;
399 }
400 total++;
401 looking_for_max_y = TRUE;
402 looking_for_min_y = FALSE;
403 min_y = pos.y (); //reset min
404 }
405 }
406 else {
407 if (looking_for_min_y && pos.y () > max_y)
408 max_y = pos.y ();
409 if (looking_for_max_y && pos.y () - min_y > threshold) {
410 if (looking_for_min_y) {
411 initial_y = min_y; //remember first min
412 first_was_max_y = TRUE;
413 }
414 total++;
415 looking_for_max_y = FALSE;
416 looking_for_min_y = TRUE;
417 max_y = pos.y ();
418 }
419 }
420
421 }
422 if (first_was_max_x && looking_for_min_x) {
423 if (max_x - initial_x > threshold)
424 total++;
425 else
426 total--;
427 }
428 else if (!first_was_max_x && looking_for_max_x) {
429 if (initial_x - min_x > threshold)
430 total++;
431 else
432 total--;
433 }
434 if (first_was_max_y && looking_for_min_y) {
435 if (max_y - initial_y > threshold)
436 total++;
437 else
438 total--;
439 }
440 else if (!first_was_max_y && looking_for_max_y) {
441 if (initial_y - min_y > threshold)
442 total++;
443 else
444 total--;
445 }
446
447 return total;
448 }
449
450
451 /**********************************************************************
452 * C_OUTLINE::operator<
453 *
454 * Return TRUE if the left operand is inside the right one.
455 **********************************************************************/
456
457 BOOL8
operator <(const C_OUTLINE & other) const458 C_OUTLINE::operator< ( //winding number
459 const C_OUTLINE & other //other outline
460 ) const
461 {
462 inT16 count = 0; //winding count
463 ICOORD pos; //position of point
464 inT32 stepindex; //index to cstep
465
466 if (!box.overlap (other.box))
467 return FALSE; //can't be contained
468 if (stepcount == 0)
469 return other.box.contains(this->box);
470
471 pos = start;
472 for (stepindex = 0; stepindex < stepcount
473 && (count = other.winding_number (pos)) == INTERSECTING; stepindex++)
474 pos += step (stepindex); //try all points
475 if (count == INTERSECTING) {
476 //all intersected
477 pos = other.start;
478 for (stepindex = 0; stepindex < other.stepcount
479 && (count = winding_number (pos)) == INTERSECTING; stepindex++)
480 //try other way round
481 pos += other.step (stepindex);
482 return count == INTERSECTING || count == 0;
483 }
484 return count != 0;
485 }
486
487
488 /**********************************************************************
489 * C_OUTLINE::winding_number
490 *
491 * Return the winding number of the outline around the given point.
492 **********************************************************************/
493
winding_number(ICOORD point) const494 inT16 C_OUTLINE::winding_number( //winding number
495 ICOORD point //point to wind around
496 ) const {
497 inT16 stepindex; //index to cstep
498 inT16 count; //winding count
499 ICOORD vec; //to current point
500 ICOORD stepvec; //step vector
501 inT32 cross; //cross product
502
503 vec = start - point; //vector to it
504 count = 0;
505 for (stepindex = 0; stepindex < stepcount; stepindex++) {
506 stepvec = step (stepindex); //get the step
507 //crossing the line
508 if (vec.y () <= 0 && vec.y () + stepvec.y () > 0) {
509 cross = vec * stepvec; //cross product
510 if (cross > 0)
511 count++; //crossing right half
512 else if (cross == 0)
513 return INTERSECTING; //going through point
514 }
515 else if (vec.y () > 0 && vec.y () + stepvec.y () <= 0) {
516 cross = vec * stepvec;
517 if (cross < 0)
518 count--; //crossing back
519 else if (cross == 0)
520 return INTERSECTING; //illegal
521 }
522 vec += stepvec; //sum vectors
523 }
524 return count; //winding number
525 }
526
527
528 /**********************************************************************
529 * C_OUTLINE::turn_direction
530 *
531 * Return the sum direction delta of the outline.
532 **********************************************************************/
533
turn_direction() const534 inT16 C_OUTLINE::turn_direction() const { //winding number
535 DIR128 prevdir; //previous direction
536 DIR128 dir; //current direction
537 inT16 stepindex; //index to cstep
538 inT8 dirdiff; //direction difference
539 inT16 count; //winding count
540
541 if (stepcount == 0)
542 return 128;
543 count = 0;
544 prevdir = step_dir (stepcount - 1);
545 for (stepindex = 0; stepindex < stepcount; stepindex++) {
546 dir = step_dir (stepindex);
547 dirdiff = dir - prevdir;
548 ASSERT_HOST (dirdiff == 0 || dirdiff == 32 || dirdiff == -32);
549 count += dirdiff;
550 prevdir = dir;
551 }
552 ASSERT_HOST (count == 128 || count == -128);
553 return count; //winding number
554 }
555
556
557 /**********************************************************************
558 * C_OUTLINE::reverse
559 *
560 * Reverse the direction of an outline.
561 **********************************************************************/
562
reverse()563 void C_OUTLINE::reverse() { //reverse drection
564 DIR128 halfturn = MODULUS / 2; //amount to shift
565 DIR128 stepdir; //direction of step
566 inT16 stepindex; //index to cstep
567 inT16 farindex; //index to other side
568 inT16 halfsteps; //half of stepcount
569
570 halfsteps = (stepcount + 1) / 2;
571 for (stepindex = 0; stepindex < halfsteps; stepindex++) {
572 farindex = stepcount - stepindex - 1;
573 stepdir = step_dir (stepindex);
574 set_step (stepindex, step_dir (farindex) + halfturn);
575 set_step (farindex, stepdir + halfturn);
576 }
577 }
578
579
580 /**********************************************************************
581 * C_OUTLINE::move
582 *
583 * Move C_OUTLINE by vector
584 **********************************************************************/
585
move(const ICOORD vec)586 void C_OUTLINE::move( // reposition OUTLINE
587 const ICOORD vec // by vector
588 ) {
589 C_OUTLINE_IT it(&children); // iterator
590
591 box.move (vec);
592 start += vec;
593
594 for (it.mark_cycle_pt (); !it.cycled_list (); it.forward ())
595 it.data ()->move (vec); // move child outlines
596 }
597
598 // If this outline is smaller than the given min_size, delete this and
599 // remove from its list, via *it, after checking that *it points to this.
600 // Otherwise, if any children of this are too small, delete them.
601 // On entry, *it must be an iterator pointing to this. If this gets deleted
602 // then this is extracted from *it, so an iteration can continue.
RemoveSmallRecursive(int min_size,C_OUTLINE_IT * it)603 void C_OUTLINE::RemoveSmallRecursive(int min_size, C_OUTLINE_IT* it) {
604 if (box.width() < min_size || box.height() < min_size) {
605 ASSERT_HOST(this == it->data());
606 delete it->extract(); // Too small so get rid of it and any children.
607 } else if (!children.empty()) {
608 // Search the children of this, deleting any that are too small.
609 C_OUTLINE_IT child_it(&children);
610 for (child_it.mark_cycle_pt(); !child_it.cycled_list();
611 child_it.forward()) {
612 C_OUTLINE* child = child_it.data();
613 child->RemoveSmallRecursive(min_size, &child_it);
614 }
615 }
616 }
617
618 /**********************************************************************
619 * C_OUTLINE::plot
620 *
621 * Draw the outline in the given colour.
622 **********************************************************************/
623
624 #ifndef GRAPHICS_DISABLED
plot(ScrollView * window,ScrollView::Color colour) const625 void C_OUTLINE::plot( //draw it
626 ScrollView* window, //window to draw in
627 ScrollView::Color colour //colour to draw in
628 ) const {
629 inT16 stepindex; //index to cstep
630 ICOORD pos; //current position
631 DIR128 stepdir; //direction of step
632 DIR128 oldstepdir; //previous stepdir
633
634 pos = start; //current position
635 window->Pen(colour);
636 if (stepcount == 0) {
637 window->Rectangle(box.left(), box.top(), box.right(), box.bottom());
638 return;
639 }
640 window->SetCursor(pos.x(), pos.y());
641
642 stepindex = 0;
643 stepdir = step_dir (0); //get direction
644 while (stepindex < stepcount) {
645 do {
646 pos += step (stepindex); //step to next
647 stepindex++; //count steps
648 oldstepdir = stepdir;
649 //new direction
650 stepdir = step_dir (stepindex);
651 }
652 while (stepindex < stepcount
653 && oldstepdir.get_dir () == stepdir.get_dir ());
654 //merge straight lines
655 window->DrawTo(pos.x(), pos.y());
656 }
657 }
658 #endif
659
660
661 /**********************************************************************
662 * C_OUTLINE::operator=
663 *
664 * Assignment - deep copy data
665 **********************************************************************/
666
667 //assignment
operator =(const C_OUTLINE & source)668 C_OUTLINE & C_OUTLINE::operator= (
669 const C_OUTLINE & source //from this
670 ) {
671 box = source.box;
672 start = source.start;
673 if (steps != NULL)
674 free_mem(steps);
675 stepcount = source.stepcount;
676 steps = (uinT8 *) alloc_mem (step_mem());
677 memmove (steps, source.steps, step_mem());
678 if (!children.empty ())
679 children.clear ();
680 children.deep_copy(&source.children, &deep_copy);
681 return *this;
682 }
683
chain_step(int chaindir)684 ICOORD C_OUTLINE::chain_step(int chaindir) {
685 return step_coords[chaindir % 4];
686 }
687