1
2 /**
3 * @file labels.c
4 * This file contains functions for label and label candidate manipulation
5 */
6 #include "labels.h"
7 static int label_skyline(FT_Face face, const char *charset, label_t * label);
8 static struct line_pnts *box_trans_rot(struct bound_box * bb, label_point_t * p,
9 double angle);
10 static void label_point_candidates(label_t * label);
11 static void label_line_candidates(label_t * label);
12 static int candidate_compare(const void *a, const void *b);
13 static double label_avedist(label_t * label, label_candidate_t * candidate);
14 static double label_flatness(label_t * label, label_candidate_t * candidate);
15 static double label_pointover(label_t * label, label_candidate_t * candidate);
16 static double label_lineover(label_t * label, label_candidate_t * candidate,
17 int linetype);
18 static double min_dist_2_lines(struct line_pnts *skyline,
19 struct line_pnts *swathline,
20 label_point_t * p);
21 static int box_overlap(struct bound_box * a, struct bound_box * b);
22 static int box_overlap2(struct line_pnts *a, struct line_pnts *b);
23
24 /**
25 * The font size in map units. A global variable because I'm lazy :P
26 */
27 static double font_size = 0.0;
28
29 /**
30 * The ideal distance, that is the distance between the base line of a label and a line feature.
31 */
32 static double ideal_distance;
33
34 /**The vector Map structure*/
35 static struct Map_info Map;
36
37 /**
38 * The size of the buffer around features where labels should never be
39 */
40 static double buffer = 0.0;
41
labels_init(struct params * p,int * n_labels)42 label_t *labels_init(struct params *p, int *n_labels)
43 {
44 label_t *labels;
45 int legal_types, layer, i = 0, error, sql_len;
46 size_t label_sz;
47 struct field_info *fi;
48 dbDriver *driver;
49 FT_Library library;
50 FT_Face face;
51 struct GFONT_CAP *font_cap;
52
53 fprintf(stderr, "Initialising labels...");
54 legal_types = Vect_option_to_types(p->type);
55
56 /* open vector for read only */
57 if (Vect_open_old(&Map, p->map->answer, "") < 0)
58 G_fatal_error(_("Unable to open vector map <%s>"), p->map->answer);
59
60 label_sz = Vect_get_num_primitives(&Map, legal_types);
61
62 G_debug(1, "Need to allocate %lu bytes of memory",
63 sizeof(label_t) * label_sz);
64 labels = (label_t *) G_malloc(sizeof(label_t) * label_sz);
65 G_debug(1, "labels=%p", labels);
66
67 if (labels == NULL)
68 G_fatal_error(_("Cannot allocate %lu bytes of memory"),
69 sizeof(label_t) * label_sz);
70
71 /* open database */
72 layer = atoi(p->layer->answer);
73 fi = Vect_get_field(&Map, layer);
74 if (fi == NULL)
75 G_fatal_error(_("Unable to get layer info for vector map"));
76 driver = db_start_driver_open_database(fi->driver, fi->database);
77 if (driver == NULL)
78 G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
79 fi->database, fi->driver);
80 db_set_error_handler_driver(driver);
81
82 sql_len = strlen(p->column->answer) + strlen(fi->table) +
83 strlen(fi->key) + 30;
84
85 /* initialize FT 2 library */
86 if (FT_Init_FreeType(&library))
87 G_fatal_error(_("Unable to initialise FreeType"));
88 font_cap = find_font_from_freetypecap(p->font->answer);
89 if (font_cap == NULL)
90 G_fatal_error(_("Unable to find font '%s'\n"), p->font->answer);
91 if (font_cap->type != GFONT_FREETYPE)
92 G_fatal_error(_("Font '%s' is not a FreeType font\n"),
93 p->font->answer);
94 error = FT_New_Face(library, font_cap->path, 0, &face);
95 if (error == FT_Err_Unknown_File_Format)
96 G_fatal_error(_("Font file format is not supported by FreeType"));
97 else if (error)
98 G_fatal_error(_("Font file can not be loaded"));
99 p->font->answer = G_store(font_cap->name);
100 free_freetypecap(font_cap);
101
102 font_size = atof(p->size->answer);
103 buffer = atof(p->isize->answer);
104
105 /* use 1 point = 1 map unit */
106 if (FT_Set_Char_Size(face, (int)((font_size) * 64.0), 0, 100, 100))
107 G_fatal_error(_("Unable to set font size"));
108
109 /* start reading the map */
110 while (1) {
111 struct line_pnts *Points;
112 struct line_cats *Cats;
113
114 dbCursor cursor;
115 dbTable *table;
116 dbColumn *column;
117 dbString query, value;
118
119 int type, cat, more, nrows;
120 char *sql;
121
122 if (i == label_sz) { /* we need more memory */
123 label_sz += 100;
124 G_debug(1, "Need to resize %p to %lu bytes of memory",
125 (void *)labels, sizeof(label_t) * label_sz);
126 labels = G_realloc(labels, sizeof(label_t) * label_sz);
127 if (labels == NULL) {
128 G_fatal_error(_("Cannot allocate more memory"));
129 }
130 }
131
132 G_percent(i, label_sz, 10);
133
134 memset(&labels[i], 0, sizeof(label_t));
135
136 Points = Vect_new_line_struct();
137 Cats = Vect_new_cats_struct();
138
139 type = Vect_read_next_line(&Map, Points, Cats);
140 if (type == -1)
141 G_fatal_error(_("Unable to read vector map"));
142 if (type == -2)
143 break; /* EOF */
144 if (!(legal_types & type))
145 continue;
146
147 Vect_cat_get(Cats, layer, &cat);
148 if (cat < 0)
149 continue; /* no cat for this field */
150
151 sql = (char *) G_malloc(sql_len);
152 /* Read label from database */
153 sprintf(sql, "select %s from %s where %s = %d", p->column->answer,
154 fi->table, fi->key, cat);
155 G_debug(3, "SQL: %s", sql);
156 db_init_string(&query);
157 db_set_string(&query, sql);
158 G_free(sql);
159
160 if (db_open_select_cursor(driver, &query, &cursor, DB_SEQUENTIAL) !=
161 DB_OK)
162 G_fatal_error(_("Unable to select attributes"));
163 db_free_string(&query);
164 nrows = db_get_num_rows(&cursor);
165 if (nrows < 1) {
166 G_warning(_("No record for category %d in table <%s>"), cat,
167 fi->table);
168 continue;
169 }
170
171 if (db_fetch(&cursor, DB_NEXT, &more) != DB_OK || !more)
172 continue;
173
174 table = db_get_cursor_table(&cursor);
175 column = db_get_table_column(table, 0); /* first column */
176
177 db_init_string(&value);
178 db_convert_column_value_to_string(column, &value);
179 db_close_cursor(&cursor);
180
181 G_debug(3, "Label: %s", db_get_string(&value));
182
183 /* ignore empty strings */
184 if (strlen(db_get_string(&value)) == 0)
185 continue;
186
187 labels[i].text = G_store(db_get_string(&value));
188 labels[i].cat = cat;
189 labels[i].type = type;
190 labels[i].shape = Points;
191 G_debug(3, "Label [%d]: %s, cat=%d, type=0x%02x", i, labels[i].text,
192 labels[i].cat, labels[i].type);
193
194 /* make a skyline for the text */
195 label_skyline(face, p->charset->answer, &labels[i]);
196
197 i++;
198
199 db_free_string(&value);
200 /* Vect_destroy_line_struct(Points); */
201 Vect_destroy_cats_struct(Cats);
202 }
203
204 {
205 FT_UInt glyph_index;
206
207 glyph_index = FT_Get_Char_Index(face, 'X');
208 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT))
209 G_fatal_error("Cannot determine ideal height");
210 ideal_distance = 0.3 * face->glyph->metrics.height / 64.0;
211 }
212
213 FT_Done_Face(face);
214 FT_Done_FreeType(library);
215 db_close_database_shutdown_driver(driver);
216 /* Vect_close(&Map); */
217 G_percent(label_sz, label_sz, 10);
218
219 *n_labels = i;
220 return labels;
221
222 }
223
224 /**
225 * This function calculates the skyline of a label and stores it in the label structure.
226 * @param face The openned FT library face to use.
227 * @param The charset to use
228 * @param The label to which we want to create a skyline
229 */
label_skyline(FT_Face face,const char * charset,label_t * label)230 static int label_skyline(FT_Face face, const char *charset, label_t * label)
231 {
232 int i, len;
233 double advance = 0.0;
234
235 len = strlen(label->text);
236 label->skyline = Vect_new_line_struct();
237 G_debug(3, "Creating skyline for '%s'", label->text);
238
239 for (i = 0; i < len; i++) {
240 FT_UInt glyph_index;
241
242 glyph_index = FT_Get_Char_Index(face, label->text[i]);
243 if (FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT))
244 G_warning(_("Cannot load glyph for '%c'"), label->text[i]);
245
246 /* insert the 4 corners of the bounding box */
247 {
248 label_point_t top_left, top_right, bottom_right, bottom_left;
249
250 G_debug(5,
251 "horiBearingX=%ld horiBearingY=%ld width=%ld height=%ld advance=%ld",
252 face->glyph->metrics.horiBearingX,
253 face->glyph->metrics.horiBearingY,
254 face->glyph->metrics.width, face->glyph->metrics.height,
255 face->glyph->metrics.horiAdvance);
256
257 top_left.x = advance;
258 top_left.y = face->glyph->metrics.horiBearingY / 64.0;
259
260 top_right.x = advance + face->glyph->metrics.horiAdvance / 64.0;
261 top_right.y = face->glyph->metrics.horiBearingY / 64.0;
262
263 bottom_right.x =
264 advance + face->glyph->metrics.horiAdvance / 64.0;
265 bottom_right.y =
266 (face->glyph->metrics.horiBearingY -
267 face->glyph->metrics.height) / 64.0;
268
269 bottom_left.x = advance;
270 bottom_left.y = (face->glyph->metrics.horiBearingY -
271 face->glyph->metrics.height) / 64.0;
272
273 if (i == 0) {
274 G_debug(5, "Character(%d) '%c': Adding UL point (%lf,%lf)",
275 i, label->text[i], top_left.x, top_left.y);
276 Vect_append_point(label->skyline, top_left.x, top_left.y,
277 0.0);
278 G_debug(5, "Character(%d) '%c': Adding UR point (%lf,%lf)", i,
279 label->text[i], top_right.x, top_right.y);
280 Vect_append_point(label->skyline, top_right.x, top_right.y,
281 0.0);
282
283 G_debug(5, "Character(%d) '%c': Adding LR point (%lf,%lf)",
284 i, label->text[i], bottom_right.x, bottom_right.y);
285 Vect_append_point(label->skyline,
286 bottom_right.x, bottom_right.y, 0.0);
287
288 G_debug(5, "Character(%d) '%c': Adding LL point (%lf,%lf)",
289 i, label->text[i], bottom_left.x, bottom_left.y);
290 Vect_append_point(label->skyline,
291 bottom_left.x, bottom_left.y, 0.0);
292 Vect_append_point(label->skyline, top_left.x, top_left.y,
293 0.0);
294 }
295 else {
296 G_debug(5, "Character(%d) '%c': Adding UL point (%lf,%lf)",
297 i, label->text[i], top_left.x, top_left.y);
298 Vect_line_insert_point(label->skyline, i * 2,
299 top_left.x, top_left.y, 0.0);
300 G_debug(5, "Character(%d) '%c': Adding UR point (%lf,%lf)",
301 i, label->text[i], top_right.x, top_right.y);
302 Vect_line_insert_point(label->skyline, i * 2 + 1,
303 top_right.x, top_right.y, 0.0);
304
305 G_debug(5, "Character(%d) '%c': Adding LR point (%lf,%lf)",
306 i, label->text[i], bottom_right.x, bottom_right.y);
307 Vect_line_insert_point(label->skyline, i * 2 + 2,
308 bottom_right.x, bottom_right.y, 0.0);
309
310 G_debug(5, "Character(%d) '%c': Adding LL point (%lf,%lf)",
311 i, label->text[i], bottom_left.x, bottom_left.y);
312 Vect_line_insert_point(label->skyline, i * 2 + 3,
313 bottom_left.x, bottom_left.y, 0.0);
314 }
315
316 advance += face->glyph->metrics.horiAdvance / 64.0;
317 G_debug(5, "Total advance %lf", advance);
318 }
319 }
320 /* remove duplicate points */
321 Vect_line_prune(label->skyline);
322 /* get the boundingbox */
323 Vect_line_box(label->skyline, &label->bb);
324 return 1;
325 }
326
327 /**
328 * This function generates label candidates for each label.
329 * @param labels The array of labels to generate candidate places for.
330 * @param n_labels The size of the array.
331 */
label_candidates(label_t * labels,int n_labels)332 void label_candidates(label_t * labels, int n_labels)
333 {
334 int i;
335
336 /* generate candidate location for each label based on feture type
337 * see chapter 5 of MERL-TR-96-04 */
338 fprintf(stderr, "Generating label candidates: ...");
339 for (i = 0; i < n_labels; i++) {
340 G_percent(i, n_labels - 1, 1);
341 switch (labels[i].type) {
342 case GV_POINT:
343 G_debug(3, "Line (%d): %s", i, labels[i].text);
344 label_point_candidates(&labels[i]);
345 break;
346 case GV_LINE:
347 G_debug(3, "Line (%d): %s", i, labels[i].text);
348 label_line_candidates(&labels[i]);
349 break;
350 /* case GV_AREA:
351 * label_area_candidates(labels[i]);
352 * break; */
353 default:
354 /* this should never be reached */
355 break;
356 }
357 }
358 Vect_close(&Map);
359 return;
360 }
361
362 /**
363 * This function generates candidates for point features.
364 * @param label. The point label to which we want to create label candidates.
365 */
label_point_candidates(label_t * label)366 static void label_point_candidates(label_t * label)
367 {
368 double height, width;
369 int i;
370 label_candidate_t *candidates;
371
372 candidates = G_calloc(19, sizeof(label_candidate_t));
373 if (candidates == NULL) {
374 G_fatal_error("Cannot allocate memory.");
375 }
376
377 height = label->bb.N - label->bb.S;
378 width = label->bb.E - label->bb.W;
379 /* 2 upper left-hand side labels are placed so that they are
380 * 1/3 and 2/3 of label height above point, and right aligned */
381 candidates[0].point.x = label->shape->x[0] - width - buffer * 0.75;
382 candidates[0].point.y = label->shape->y[0] + (5.0 / 9.0) * height;
383 candidates[0].score = 0.63;
384
385 candidates[1].point.x = label->shape->x[0] - width - buffer * 0.85;
386 candidates[1].point.y = label->shape->y[0] + (1.0 / 3.0) * height;
387 candidates[1].score = 0.44;
388 /* same height as label point */
389 candidates[2].point.x = label->shape->x[0] - width - buffer * 0.95;
390 candidates[2].point.y = label->shape->y[0];
391 candidates[2].score = 0.07;
392 /* 3 lower left-hand side labels are placed so that they are
393 * 1/3, 2/3 and 3/3 of label height below point, and right aligned */
394 candidates[3].point.x = label->shape->x[0] - width - buffer * 0.95;
395 candidates[3].point.y = label->shape->y[0] - (1.0 / 3.0) * height;
396 candidates[3].score = 0.10;
397
398 candidates[4].point.x = label->shape->x[0] - width - buffer * 0.95;
399 candidates[4].point.y = label->shape->y[0] - (5.0 / 9.0) * height;
400 candidates[4].score = 0.02;
401
402 candidates[5].point.x = label->shape->x[0] - width - buffer * 0.95;
403 candidates[5].point.y = label->shape->y[0] - height;
404 candidates[5].score = 0.37;
405
406 /* 2 upper right-hand side labels are placed so that they are
407 * 1/3 and 2/3 of label height above point*/
408 candidates[6].point.x = label->shape->x[0] + buffer * 0.85;
409 candidates[6].point.y = label->shape->y[0] + (5.0 / 9.0) * height;
410 candidates[6].score = 0.41;
411
412 candidates[7].point.x = label->shape->x[0] + buffer * 0.95;
413 candidates[7].point.y = label->shape->y[0] + (1.0 / 3.0) * height;
414 candidates[7].score = 0.33;
415 /* same height as label point */
416 candidates[8].point.x = label->shape->x[0] + buffer;
417 candidates[8].point.y = label->shape->y[0];
418 candidates[8].score = 0.00;
419 /* 4 lower left-hand side labels are placed so that they are
420 * 1/4, 2/4 3/4 and 4/4 of label height below point*/
421 candidates[9].point.x = label->shape->x[0] + buffer;
422 candidates[9].point.y = label->shape->y[0] - 0.25 * height;
423 candidates[9].score = 0.04;
424
425 candidates[10].point.x = label->shape->x[0] + buffer;
426 candidates[10].point.y = label->shape->y[0] - 0.5 * height;
427 candidates[10].score = 0.3;
428
429 candidates[11].point.x = label->shape->x[0] + buffer;
430 candidates[11].point.y = label->shape->y[0] - 0.75 * height;
431 candidates[11].score = 0.12;
432
433 candidates[12].point.x = label->shape->x[0] + buffer;
434 candidates[12].point.y = label->shape->y[0] - height;
435 candidates[12].score = 0.59;
436
437 /* 3 labels above, centered, centered on left 1/3 and centered
438 * on right 1/3 of the label */
439 candidates[13].point.x = label->shape->x[0] - (1.0 / 3.0) * width;
440 candidates[13].point.y = label->shape->y[0] + fabs(label->bb.S) + buffer;
441 candidates[13].score = 0.70;
442
443 candidates[14].point.x = label->shape->x[0] - 0.5 * width;
444 candidates[14].point.y = label->shape->y[0] + fabs(label->bb.S) + buffer;
445 candidates[14].score = 0.89;
446
447 candidates[15].point.x = label->shape->x[0] - (2.0 / 3.0) * width;
448 candidates[15].point.y = label->shape->y[0] + fabs(label->bb.S) + buffer;
449 candidates[15].score = 0.74;
450
451 /* 3 labels below, centered, centered on left 1/3 and centered
452 * on right 1/3 of the label */
453 candidates[16].point.x = label->shape->x[0] - (1.0 / 3.0) * width;
454 candidates[16].point.y = label->shape->y[0] - height - buffer;
455 candidates[16].score = 0.74;
456
457 candidates[17].point.x = label->shape->x[0] - 0.5 * width;
458 candidates[17].point.y = label->shape->y[0] - height - buffer;
459 candidates[17].score = 0.89;
460
461 candidates[18].point.x = label->shape->x[0] - (2.0 / 3.0) * width;
462 candidates[18].point.y = label->shape->y[0] - height - buffer;
463 candidates[18].score = 1.0;
464
465 for (i = 0; i < 19; i++) {
466 candidates[i].score += 10.0 * label_pointover(label, &candidates[i]);
467 candidates[i].score += 15.0 * label_lineover(label, &candidates[i],
468 GV_LINE);
469 G_debug(5, "calling label_lineover('%s', %d)", label->text, i);
470 candidates[i].score += 10.0 * label_lineover(label, &candidates[i],
471 GV_BOUNDARY);
472 candidates[i].rotation = 0;
473 }
474
475 /* randomly choose one candidate to be the current */
476 label->current_candidate = (int)(19.0 * (rand() / (RAND_MAX + 1.0)));
477 label->candidates = candidates;
478 label->n_candidates = 19;
479 }
480
481 /**
482 * This function generates label candidates for a line feature.
483 * @param label The label for which to create the candidates.
484 */
label_line_candidates(label_t * label)485 static void label_line_candidates(label_t * label)
486 {
487 double height, width, inc, length, pos;
488 label_candidate_t *above_candidates, *below_candidates, *candidates;
489 int i, n, n_c;
490
491 height = label->bb.N - label->bb.S;
492 width = label->bb.E - label->bb.W;
493 inc = width / 8.0;
494 length = Vect_line_length(label->shape);
495
496 n = (int)(length / inc);
497 if (n == 0) {
498 /* treat the line as a point feature */
499 struct line_pnts *tmp, *tmp_shape;
500 double x, y;
501
502 tmp = Vect_new_line_struct();
503 Vect_point_on_line(label->shape, length / 2.0, &x, &y,
504 NULL, NULL, NULL);
505 Vect_append_point(tmp, x, y, 0);
506 tmp_shape = label->shape;
507 label->shape = tmp;
508
509 label_point_candidates(label);
510 label->shape = tmp_shape;
511 Vect_destroy_line_struct(tmp);
512
513 return;
514 }
515 above_candidates = G_calloc(n, sizeof(label_candidate_t));
516 below_candidates = G_calloc(n, sizeof(label_candidate_t));
517 if ((above_candidates == NULL) || (below_candidates == NULL)) {
518 G_fatal_error("Cannot allocate memory.");
519 }
520
521 /* find all candidate labels */
522 for (pos = width / 2.0, i = 0; pos < (length - 1.5 * width); pos += inc) {
523 label_point_t p1, p2, minimum_above_distance_p,
524 minimum_below_distance_p;
525 int seg1, seg2, j;
526 struct line_pnts *above_skyline, *below_skyline, *baseline;
527 double above_distance = 0.0, below_distance = 0.0,
528 minimum_above_distance = 0.0, minimum_below_distance = 0.0, angle;
529 double flatness, centerdness;
530
531 seg1 =
532 Vect_point_on_line(label->shape, pos, &p1.x, &p1.y, NULL, NULL,
533 NULL);
534 seg2 =
535 Vect_point_on_line(label->shape, pos + width, &p2.x, &p2.y, NULL,
536 NULL, NULL);
537
538 G_debug(1, "pos=%lf i=%d p1 at (%lf,%lf), p2 at (%lf,%lf)",
539 pos, i, p1.x, p1.y, p2.x, p2.y);
540
541 angle = atan2((p2.y - p1.y), (p2.x - p1.x));
542
543 if ((angle > M_PI / 2) || (angle < -M_PI / 2)) {
544 /* turn label around 180 degrees if it would be upside down */
545 double tmp;
546
547 tmp = p1.x;
548 p1.x = p2.x;
549 p2.x = tmp;
550
551 tmp = p1.y;
552 p1.y = p2.y;
553 p2.y = tmp;
554
555 if (angle < 0)
556 angle += M_PI;
557 else
558 angle -= M_PI;
559 }
560
561 /* find the maximum above_distance and below_distance from the swath
562 * "diagonal" to determine maximum deviation from a straight line
563 * create the swath lines at the same time.
564 */
565 above_candidates[i].swathline = Vect_new_line_struct();
566 below_candidates[i].swathline = Vect_new_line_struct();
567 if ((above_candidates[i].swathline == NULL) ||
568 (below_candidates[i].swathline == NULL))
569 G_fatal_error("Cannot allocate memory!");
570 Vect_append_point(above_candidates[i].swathline, p1.x, p1.y, 0);
571 Vect_append_point(below_candidates[i].swathline, p1.x, p1.y, 0);
572
573 baseline = Vect_new_line_struct();
574 Vect_append_point(baseline, p1.x, p1.y, 0);
575 Vect_append_point(baseline, p2.x, p2.y, 0);
576
577 Vect_append_point(above_candidates[i].swathline, p1.x, p1.y, 0);
578 Vect_append_point(below_candidates[i].swathline, p1.x, p1.y, 0);
579
580 for (j = seg1 + 1; j < seg2; j++) {
581 double x, y, d;
582
583 Vect_line_distance(baseline, label->shape->x[j],
584 label->shape->y[j], 0, 0,
585 &x, &y, NULL, &d, NULL, NULL);
586 if (label->shape->y[j] < y) {
587 /* swathline is beneath the "diagonal" */
588 if (d > below_distance) {
589 below_distance = d;
590 }
591 }
592 else {
593 /* swatline is above or on the "diagonal" */
594 if (d > above_distance) {
595 above_distance = d;
596 }
597 }
598 Vect_append_point(above_candidates[i].swathline,
599 label->shape->x[j], label->shape->y[j], 0);
600 Vect_append_point(below_candidates[i].swathline,
601 label->shape->x[j], label->shape->y[j], 0);
602 }
603
604 Vect_append_point(above_candidates[i].swathline, p2.x, p2.y, 0);
605 Vect_append_point(below_candidates[i].swathline, p2.x, p2.y, 0);
606 Vect_destroy_line_struct(baseline);
607
608 if (above_distance == 0.0) {
609 above_distance = height - label->bb.N;
610 }
611 if (below_distance == 0.0) {
612 below_distance = height - label->bb.S;
613 }
614
615 /* place a skyline at above_distance above line, and
616 * below_distance + height below line */
617 {
618 label_point_t tp;
619
620 tp.x = p1.x - above_distance * sin(angle);
621 tp.y = p1.y + above_distance * cos(angle);
622 above_skyline = skyline_trans_rot(label->skyline, &tp, angle);
623 tp.x = p1.x + (below_distance + height) * sin(angle);
624 tp.y = p1.y - (below_distance + height) * cos(angle);
625 below_skyline = skyline_trans_rot(label->skyline, &tp, angle);
626 }
627 /* find minimum distance between swath line and skylines */
628 minimum_above_distance = min_dist_2_lines(above_skyline,
629 above_candidates[i].
630 swathline,
631 &minimum_above_distance_p);
632 minimum_below_distance =
633 min_dist_2_lines(below_skyline, below_candidates[i].swathline,
634 &minimum_below_distance_p);
635
636 /* adjust skylines so that the minimum distance is equal to the ideal
637 * distance (= 0.3 * glyph height of capital X) */
638 above_distance += ideal_distance - minimum_above_distance;
639 below_distance += ideal_distance - minimum_below_distance;
640
641 Vect_destroy_line_struct(above_skyline);
642 Vect_destroy_line_struct(below_skyline);
643
644 above_candidates[i].point.x = p1.x - above_distance * sin(angle);
645 above_candidates[i].point.y = p1.y + above_distance * cos(angle);
646
647 below_candidates[i].point.x =
648 p1.x + (below_distance + height) * sin(angle);
649 below_candidates[i].point.y =
650 p1.y - (below_distance + height) * cos(angle);
651
652 G_debug(1, "above at (%lf,%lf) below at (%lf,%lf)",
653 above_candidates[i].point.x, above_candidates[i].point.y,
654 below_candidates[i].point.x, below_candidates[i].point.y);
655
656 above_candidates[i].above = 1;
657 below_candidates[i].above = 0;
658 above_candidates[i].rotation = angle;
659 below_candidates[i].rotation = angle;
660
661 above_candidates[i].score = 0.0;
662 below_candidates[i].score = 0.0;
663 /* AveDist */
664 above_candidates[i].score +=
665 label_avedist(label, &above_candidates[i]);
666 below_candidates[i].score +=
667 label_avedist(label, &below_candidates[i]);
668
669 /* flatness */
670 flatness = label_flatness(label, &above_candidates[i]);
671 above_candidates[i].score += flatness;
672 flatness = label_flatness(label, &below_candidates[i]);
673 below_candidates[i].score += flatness;
674
675 /* centerdness */
676 centerdness = 3.0 * fabs(2.0 * pos / length - 1.0);
677 above_candidates[i].score += centerdness;
678 below_candidates[i].score += centerdness;
679
680 /* PointOver */
681 above_candidates[i].score += 10.0 *
682 label_pointover(label, &above_candidates[i]);
683 below_candidates[i].score += 10.0 *
684 label_pointover(label, &below_candidates[i]);
685
686 /* LineOver */
687 above_candidates[i].lineover = 15.0 *
688 label_lineover(label, &above_candidates[i], GV_LINE);
689 above_candidates[i].score += above_candidates[i].lineover;
690
691 below_candidates[i].lineover = 15.0 *
692 label_lineover(label, &below_candidates[i], GV_LINE);
693 below_candidates[i].score += below_candidates[i].lineover;
694
695 /* AreaOver */
696 above_candidates[i].score += 10.0 *
697 label_lineover(label, &above_candidates[i], GV_BOUNDARY);
698 below_candidates[i].score += 10.0 *
699 label_lineover(label, &below_candidates[i], GV_BOUNDARY);
700
701 /* aboveness */
702 below_candidates[i].score += 1.25;
703
704 i++;
705 }
706 n = i;
707
708 if (n == 0) {
709 /* treat the line as a point feature */
710 struct line_pnts *tmp, *tmp_shape;
711 double x, y;
712
713 tmp = Vect_new_line_struct();
714 Vect_point_on_line(label->shape, length / 2.0, &x, &y,
715 NULL, NULL, NULL);
716 Vect_append_point(tmp, x, y, 0);
717 tmp_shape = label->shape;
718 label->shape = tmp;
719
720 label_point_candidates(label);
721 label->shape = tmp_shape;
722 Vect_destroy_line_struct(tmp);
723 return;
724 }
725
726 candidates = G_calloc(n * 2, sizeof(label_candidate_t));
727 for (i = 0; i < n; i++) {
728 memcpy(&candidates[i * 2], &above_candidates[i],
729 sizeof(label_candidate_t));
730 memcpy(&candidates[i * 2 + 1], &below_candidates[i],
731 sizeof(label_candidate_t));
732 }
733 G_free(above_candidates);
734 G_free(below_candidates);
735
736 n_c = n * 2;
737 /* pick the 32 best candidates */
738 qsort(candidates, n_c, sizeof(label_candidate_t), candidate_compare);
739
740 if (n_c > 32) {
741 label_candidate_t *tmp;
742
743 for (i = 32; i < n; i++) {
744 Vect_destroy_line_struct(candidates[i].baseline);
745 Vect_destroy_line_struct(candidates[i].swathline);
746 }
747
748 tmp = G_realloc(candidates, sizeof(label_candidate_t) * 32);
749 if (tmp != NULL) {
750 candidates = tmp;
751 }
752 n_c = 32;
753 }
754 label->current_candidate =
755 (int)((double)(n_c) * (rand() / (RAND_MAX + 1.0)));
756 label->candidates = candidates;
757 label->n_candidates = n_c;
758 }
759
760 /**
761 * This function compares two label candidates scores and determines which is better.
762 * @param a Candidate A
763 * @param b Candidate B
764 * @return -1 if candidate a has a lower score then candidate b. = if the
765 * scores are equal, and 1 if candidate a has a higher score then candidate b.
766 */
candidate_compare(const void * a,const void * b)767 static int candidate_compare(const void *a, const void *b)
768 {
769 const label_candidate_t *ca = a, *cb = b;
770
771 if (ca->score < cb->score) {
772 return -1;
773 }
774 else if (ca->score == cb->score) {
775 return 0;
776 }
777 else {
778 return 1;
779 }
780 }
781
skyline_trans_rot(struct line_pnts * skyline,label_point_t * p,double angle)782 struct line_pnts *skyline_trans_rot(struct line_pnts *skyline,
783 label_point_t * p, double angle)
784 {
785 int i;
786 struct line_pnts *Points;
787
788 Points = Vect_new_line_struct();
789
790 for (i = 0; i < skyline->n_points; i++) {
791 double x, y;
792
793 x = skyline->x[i] * cos(angle) - skyline->y[i] * sin(angle);
794 y = skyline->x[i] * sin(angle) + skyline->y[i] * cos(angle);
795 Vect_append_point(Points, x + p->x, y + p->y, 0);
796 }
797
798 return Points;
799 }
800
801 /**
802 * This function rotates and translates the label bounding box to the
803 * given point, and returns it as a polygon.
804 * @param bb The bounding box to translate and rotate.
805 * @param p The point to translate the bounding box to
806 * @param angle The angle (in radians) to rotate the label counter-clockwise
807 * @return A lint_pnts structure containing the rotated and translated
808 * bounding box as a polygon.
809 */
box_trans_rot(struct bound_box * bb,label_point_t * p,double angle)810 static struct line_pnts *box_trans_rot(struct bound_box * bb, label_point_t * p,
811 double angle)
812 {
813 struct line_pnts *Points;
814 double x0, y0, x1, y1, x2, y2;
815
816 Points = Vect_new_line_struct();
817
818 /* Lower Left, no rotation needed */
819 x0 = p->x + bb->W;
820 y0 = p->y + bb->S;
821 Vect_append_point(Points, x0, y0, 0);
822 /* Lower Right */
823 x1 = (bb->E - bb->W) * cos(angle);
824 y1 = (bb->E - bb->W) * sin(angle);
825 Vect_append_point(Points, x0 + x1, y0 + y1, 0);
826
827 /* Upper Right */
828 x2 = (bb->N - bb->S) * sin(angle);
829 y2 = (bb->N - bb->S) * cos(angle);
830 /* First translate to LR, and then translate like UL */
831 Vect_append_point(Points, x0 + x1 - x2, y0 + y1 + y2, 0);
832
833 /* Upper Left */
834 Vect_append_point(Points, x0 - x2, y0 + y2, 0);
835
836 /* close polygon */
837 Vect_append_point(Points, x0, y0, 0);
838
839 return Points;
840 }
841
842 /**
843 * This function calculates the AveDist metric for line label candidates
844 * @param label The label to which candidate belongs to.
845 * @candidate The candidate of which we are to calculate the metric.
846 * @return The metric;
847 */
label_avedist(label_t * label,label_candidate_t * candidate)848 static double label_avedist(label_t * label, label_candidate_t * candidate)
849 {
850 struct line_pnts *trsk;
851 double avedist = 0.0;
852 int i;
853
854 G_debug(3, "Candidate point is: (%lf,%lf)",
855 candidate->point.x, candidate->point.y);
856 trsk = skyline_trans_rot(label->skyline, &candidate->point,
857 candidate->rotation);
858
859 for (i = 0; i < trsk->n_points; i++) {
860 double d;
861
862 Vect_line_distance(candidate->swathline, trsk->x[i], trsk->y[i],
863 0, 0, NULL, NULL, NULL, &d, NULL, NULL);
864 avedist += d;
865 }
866
867 for (i = 0; i < candidate->swathline->n_points; i++) {
868 double d;
869
870 Vect_line_distance(trsk, candidate->swathline->x[i],
871 candidate->swathline->y[i], 0, 0,
872 NULL, NULL, NULL, &d, NULL, NULL);
873 avedist += d;
874 }
875
876 avedist /= (candidate->swathline->n_points + trsk->n_points);
877 Vect_destroy_line_struct(trsk);
878
879 return ((avedist - ideal_distance) * (avedist - ideal_distance)) /
880 (ideal_distance * ideal_distance);
881 }
882
883 /**
884 * This function calculates the Flatness metric for line label candidates
885 * @param label The label to which candidate belongs to.
886 * @candidate The candidate of which we are to calculate the metric.
887 * @return The metric;
888 */
label_flatness(label_t * label,label_candidate_t * candidate)889 static double label_flatness(label_t * label, label_candidate_t * candidate)
890 {
891 struct line_pnts *line;
892 double flatness = 0.0, x0, y0, x1, y1, x2, y2;
893 int i;
894
895 /* first generate a line which is parallel to the baseline,
896 but the ideal distance away from it, and is between the label and the
897 base line */
898 line = Vect_new_line_struct();
899 if (candidate->above) {
900 x0 = x1 =
901 candidate->point.x + ideal_distance * sin(candidate->rotation);
902 y0 = y1 =
903 candidate->point.y - ideal_distance * cos(candidate->rotation);
904 }
905 else {
906 x0 = x1 =
907 candidate->point.x - ideal_distance * sin(candidate->rotation);
908 y0 = y1 =
909 candidate->point.y + ideal_distance * cos(candidate->rotation);
910 }
911
912 Vect_append_point(line, x1, y1, 0);
913 x2 = x1 + (label->bb.E - label->bb.W) * sin(candidate->rotation);
914 y2 = y1 + (label->bb.E - label->bb.W) * cos(candidate->rotation);
915 Vect_append_point(line, x2, y2, 0);
916
917 /* now calculate the are between candidate->swathline and line */
918
919 for (i = 1; i < candidate->swathline->n_points; i++) {
920 int r;
921 double b, h;
922 double px1, py1, pz1, px2, py2, pz2;
923
924 r = Vect_segment_intersection(x1, y1, 0, x2, y2, 0,
925 candidate->swathline->x[i - 1],
926 candidate->swathline->y[i - 1],
927 0,
928 candidate->swathline->x[i],
929 candidate->swathline->y[i],
930 0,
931 &px1, &py1, &pz1, &px2, &py2, &pz2, 0);
932 /* Now calculate the area between the swath and the line */
933 switch (r) {
934 case 0: /* no intersection */
935 dig_distance2_point_to_line(candidate->swathline->x[i],
936 candidate->swathline->y[i], 0,
937 x1, y1, 0, x2, y2, 0, 0,
938 &px1, &py1, &pz1, &h, NULL);
939 h = (sqrt(pow(x1 - candidate->swathline->x[i - 1], 2.0) +
940 pow(y1 - candidate->swathline->y[i - 1], 2.0)) +
941 h) / 2.0;
942 b = sqrt(pow(px1 - x1, 2) + pow(py1 - y1, 2));
943 flatness += b * h;
944 x1 = px1;
945 y1 = py1;
946 break;
947 case 1:
948 h = sqrt(pow(x1 - candidate->swathline->x[i - 1], 2.0) +
949 pow(y1 - candidate->swathline->y[i - 1], 2.0));
950 b = sqrt(pow(px1 - x1, 2) + pow(py1 - y1, 2));
951 flatness += b * h * 0.5; /* the first triangle */
952 x1 = px1;
953 y1 = py1;
954 dig_distance2_point_to_line(candidate->swathline->x[i],
955 candidate->swathline->y[i], 0,
956 x1, y1, 0, x2, y2, 0, 0,
957 &px1, &py1, &pz1, &h, NULL);
958 b = sqrt(pow(px1 - x1, 2) + pow(py1 - y1, 2));
959 flatness += b * h * 0.5; /* the second triangle */
960 x1 = px1;
961 y1 = py1;
962 break;
963 case 3:
964 x1 = px2;
965 y1 = py2;
966 break;
967 case 5:
968 x1 = px2;
969 y1 = py2;
970 break;
971 default:
972 G_fatal_error("Programming error!!\n");
973 break;
974 }
975 }
976
977 flatness /= sqrt((x2 - x0) * (x2 - x0) + (y2 - y0) * (y2 - y0)); /* this is d'' */
978 flatness = (flatness * flatness) / (ideal_distance * ideal_distance);
979
980 Vect_destroy_line_struct(line);
981 return flatness;
982 }
983
984 /**
985 * This function checks if the label candidate overlaps with a point feature.
986 * And calculates the PointOver metric.
987 * @param label The label whose candidate we are investigating.
988 * @param candidate The label candidate we are investigating.
989 * @return The unweighted raw score of the PointOver metric for this label.
990 */
label_pointover(label_t * label,label_candidate_t * candidate)991 static double label_pointover(label_t * label, label_candidate_t * candidate)
992 {
993 double pointover;
994 struct ilist *il;
995 struct line_pnts *trbb;
996 int n;
997
998 il = Vect_new_list();
999
1000 /* trsk = skyline_trans_rot(label->skyline, &candidate->point,
1001 candidate->rotation);
1002 */
1003 trbb = box_trans_rot(&label->bb, &candidate->point, candidate->rotation);
1004 n = Vect_select_lines_by_polygon(&Map, trbb, 0, NULL, GV_POINT, il);
1005
1006 pointover = (double)il->n_values;
1007 Vect_destroy_list(il);
1008
1009 return pointover;
1010 }
1011
1012 /**
1013 * This function calculates the LineOver metric for a label candidate.
1014 * @param label The label whose candidate we are investigating.
1015 * @param candidate The label candidate we are investigating.
1016 * @return The unweighted raw score of the LineOver metric for this label.
1017 */
label_lineover(label_t * label,label_candidate_t * candidate,int linetype)1018 static double label_lineover(label_t * label, label_candidate_t * candidate,
1019 int linetype)
1020 {
1021 double lineover = 0.0;
1022 struct ilist *il;
1023 struct line_pnts *trbb;
1024 label_point_t b;
1025 int i, n;
1026
1027 il = Vect_new_list();
1028 G_debug(5, "Candidate point is: (%lf,%lf)",
1029 candidate->point.x, candidate->point.y);
1030 /* trsk = skyline_trans_rot(label->skyline, &candidate->point,
1031 candidate->rotation); */
1032 b.x = abs((label->bb.E - label->bb.W) * cos(candidate->rotation));
1033 b.y = abs((label->bb.E - label->bb.W) * sin(candidate->rotation));
1034
1035 trbb = box_trans_rot(&label->bb, &candidate->point, candidate->rotation);
1036 n = Vect_select_lines_by_polygon(&Map, trbb, 0, NULL, linetype, il);
1037
1038 if (n == 0) {
1039 return 0.0;
1040 }
1041
1042 for (i = 0; i < il->n_values; i++) {
1043 int j, found = 0;
1044 struct line_pnts *line;
1045 label_point_t v, v1, v2;
1046
1047 line = Vect_new_line_struct();
1048 Vect_read_line(&Map, line, NULL, il->value[i]);
1049
1050 for (j = 1; j < line->n_points; j++) {
1051 int k;
1052
1053 for (k = 1; k < trbb->n_points; k++) {
1054 int r;
1055 double x1, x2, y1, y2, z1, z2;
1056
1057 r = Vect_segment_intersection(trbb->x[k - 1], trbb->y[k - 1],
1058 0, trbb->x[k], trbb->y[k], 0,
1059 line->x[j - 1], line->y[j - 1],
1060 0, line->x[j], line->y[j], 0,
1061 &x1, &y1, &z1, &x2, &y2, &z2,
1062 0);
1063 if (r > 0) { /* intersection at one point */
1064 if (found == 0) {
1065 found = 1;
1066 v1.x = x1;
1067 v1.y = y1;
1068 }
1069 else {
1070 found++;
1071 if (r > 1) {
1072 v2.x = x2;
1073 v2.y = y2;
1074 }
1075 else {
1076 v2.x = x1;
1077 v2.y = y1;
1078 }
1079 }
1080 }
1081 }
1082 }
1083 if (found > 1) {
1084 double cosvb;
1085
1086 v.x = abs(v2.x - v1.x);
1087 v.y = abs(v2.y - v1.y);
1088 cosvb = ((b.x * v.x + b.y * v.y) /
1089 (sqrt(b.x * b.x + b.y * b.y) *
1090 sqrt(v.x * v.x + v.y * v.y)));
1091 lineover += 1.0 + 9.0 * cosvb;
1092 }
1093 Vect_destroy_line_struct(line);
1094 }
1095
1096 Vect_destroy_list(il);
1097 return lineover;
1098 }
1099
1100 /**
1101 * This function calculates the minimum distance between a skyline and a swath line.
1102 * @param skyline The skyline to investigate.
1103 * @param swathline The swath line to investigate.
1104 * @param p The point on the skyline which is neares to the swath line is stored in this structure.
1105 * @return The distance in map units.
1106 */
min_dist_2_lines(struct line_pnts * skyline,struct line_pnts * swathline,label_point_t * p)1107 static double min_dist_2_lines(struct line_pnts *skyline,
1108 struct line_pnts *swathline, label_point_t * p)
1109 {
1110 int i;
1111 double dist = 10000000000000000.0;
1112
1113 for (i = 0; i < skyline->n_points; i++) {
1114 double x, y, d;
1115
1116 Vect_line_distance(swathline, skyline->x[i], skyline->y[i], 0, 0,
1117 &x, &y, NULL, &d, NULL, NULL);
1118 if (d < dist) {
1119 dist = d;
1120 p->x = skyline->x[i];
1121 p->y = skyline->y[i];
1122 }
1123 }
1124
1125 for (i = 0; i < swathline->n_points; i++) {
1126 double x, y, d;
1127
1128 Vect_line_distance(skyline, swathline->x[i], swathline->y[i], 0, 0,
1129 &x, &y, NULL, &d, NULL, NULL);
1130 if (d < dist) {
1131 dist = d;
1132 p->x = x;
1133 p->y = y;
1134 }
1135 }
1136
1137 return dist;
1138 }
1139
1140 /**
1141 * This function finds label -label overlaps.
1142 * @param labels The array of labels
1143 * @param n_labels The size of the array
1144 */
label_candidate_overlap(label_t * labels,int n_labels)1145 void label_candidate_overlap(label_t * labels, int n_labels)
1146 {
1147 int i;
1148
1149 fprintf(stderr, "Finding label overlap: ...");
1150 for (i = 0; i < n_labels; i++) {
1151 int j;
1152
1153 for (j = 0; j < labels[i].n_candidates; j++) {
1154 int k;
1155
1156 for (k = i + 1; k < n_labels; k++) {
1157 int l;
1158
1159 for (l = 0; l < labels[k].n_candidates; l++) {
1160 int overlap = 0;
1161
1162 if ((labels[i].candidates[j].rotation == 0) &&
1163 (labels[k].candidates[l].rotation == 0)) {
1164 struct bound_box a, b;
1165
1166 a.N =
1167 labels[i].bb.N + labels[i].candidates[j].point.y;
1168 a.E =
1169 labels[i].bb.E + labels[i].candidates[j].point.x;
1170 a.W =
1171 labels[i].bb.W + labels[i].candidates[j].point.x;
1172 a.S =
1173 labels[i].bb.S + labels[i].candidates[j].point.y;
1174
1175 b.N =
1176 labels[k].bb.N + labels[k].candidates[l].point.y;
1177 b.E =
1178 labels[k].bb.E + labels[k].candidates[l].point.x;
1179 b.W =
1180 labels[k].bb.W + labels[k].candidates[l].point.x;
1181 b.S =
1182 labels[k].bb.S + labels[k].candidates[l].point.y;
1183 overlap = box_overlap(&a, &b);
1184 }
1185 else {
1186 struct line_pnts *a = NULL, *b = NULL;
1187
1188 a = box_trans_rot(&labels[i].bb,
1189 &labels[i].candidates[j].point,
1190 labels[i].candidates[j].rotation);
1191 b = box_trans_rot(&labels[k].bb,
1192 &labels[k].candidates[l].point,
1193 labels[k].candidates[l].rotation);
1194 overlap = box_overlap2(a, b);
1195 Vect_destroy_line_struct(a);
1196 Vect_destroy_line_struct(b);
1197 }
1198 if (overlap) {
1199 int n;
1200 label_intersection_t *li;
1201
1202 n = ++(labels[i].candidates[j].n_intersections);
1203 li = G_realloc(labels[i].candidates[j].intersections,
1204 n * sizeof(label_intersection_t));
1205 if (li == NULL)
1206 G_fatal_error("\nUnable to allocate memory\n");
1207 li[n - 1].label = &labels[k];
1208 li[n - 1].candidate = l;
1209 if ((labels[k].current_candidate == l) &&
1210 (labels[i].current_candidate == j)) {
1211 labels[i].current_score += LABEL_OVERLAP_WEIGHT;
1212 labels[k].current_score += LABEL_OVERLAP_WEIGHT;
1213 }
1214 labels[i].candidates[j].intersections = li;
1215 n = ++(labels[k].candidates[l].n_intersections);
1216 li = G_realloc(labels[k].candidates[l].intersections,
1217 n * sizeof(label_intersection_t));
1218 if (li == NULL)
1219 G_fatal_error("\nUnable to allocate memory\n");
1220 li[n - 1].label = &labels[i];
1221 li[n - 1].candidate = j;
1222
1223 labels[k].candidates[l].intersections = li;
1224 }
1225 }
1226 }
1227 }
1228 G_percent(i, n_labels, 1);
1229 }
1230 G_percent(n_labels, n_labels, 1);
1231 }
1232
1233 /**
1234 * This function checks if the two given boxes overlap.
1235 * @param a Bounding box A
1236 * @param b Bounding box B
1237 * @return REtruns 1 if the two boxes overlap 0 if not.
1238 */
box_overlap(struct bound_box * a,struct bound_box * b)1239 static int box_overlap(struct bound_box * a, struct bound_box * b)
1240 {
1241 int vert = 0, hori = 0;
1242
1243 if (((a->W < b->W) && (b->W < a->E)) || ((a->W < b->E) && (b->E < a->E)))
1244 vert = 1;
1245 if (((b->W < a->W) && (a->W < b->E)) || ((b->W < a->E) && (a->E < b->E)))
1246 vert = 1;
1247
1248 if (((a->S < b->S) && (b->S < a->N)) || ((a->S < b->N) && (b->N < a->N)))
1249 hori = 1;
1250 if (((b->S < a->S) && (a->S < b->N)) || ((b->S < a->N) && (a->N < b->N)))
1251 hori = 1;
1252
1253 return (hori && vert);
1254 }
1255
1256 /**
1257 * This function checks if two rotated boxes overlap. The boxes are stored
1258 * as polygons and the function assumes that each box has exactly 4 sides.
1259 * @param a Bounding box A
1260 * @param b Bounding box B
1261 * @return returns 1 if the given boxes overlap. 0 if not.
1262 */
box_overlap2(struct line_pnts * a,struct line_pnts * b)1263 static int box_overlap2(struct line_pnts *a, struct line_pnts *b)
1264 {
1265 int i, r = 0;
1266
1267 for (i = 0; i < (a->n_points - 1); i++) {
1268 int j;
1269
1270 for (j = 0; j < (b->n_points - 1); j++) {
1271 double d[6];
1272
1273 r += Vect_segment_intersection(a->x[i], a->y[i], 0,
1274 a->x[i + 1], a->y[i + 1], 0,
1275 b->x[j], b->y[j], 0,
1276 b->x[j + 1], a->y[j + 1], 0,
1277 &d[0], &d[1], &d[2],
1278 &d[3], &d[4], &d[5], 0);
1279 }
1280 }
1281 if (r > 1)
1282 return 1;
1283 else
1284 return 0;
1285 }
1286