1/* ******Functions included in start/_NG_utilities.def:****** 2 * 3 * so_balance ref_meanmax im1 im2 mask blur gauss * 4 * nonzero_mean im = no_out * 5 * so_meanmax im = result * 6 * so_calculate ref_meanmax im mask = result * 7 * simple_frame frame im_w im_h ov cs ms bf option = result * 8 * corner_frame frame im_w im_h ov cs ms bf = result * 9 * build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result * 10 * complex_frame frame im_w im_h ov cs es ms bf option= result * 11 * complex_edge ra rb t bl d = rc * 12 * frame_lr_min r_l r_r target bw = result * 13 * frame_tb_min r_t r_b target bw = result * 14 * frame_position_image im ref os colour= result * 15 * merge_array bw arr = result * 16 * merge_to_scale im target blend dir = result * 17 * select_ellipse line width = mask * 18 * select_tetragon p1 p2 p3 p4 = mask * 19 * select_polygon pt_list = mask * 20 * perspective_transform to from = trans'' * 21 * sort_pts_clockwise l = l'' * 22 */ 23 24/* Called from: 25* _NG_Extra.def Clone_area_item 26*/ 27so_balance ref_meanmax im1 im2 mask gauss 28 = result 29 { 30 //ref_meanmax = so_meanmax im1; 31 so_values = so_calculate ref_meanmax im2 mask; 32 im2_cor_a = clip2fmt im2.format im2'', has_member "format" im2 33 = im2'' 34 {im2'' = im2 * (so_values?0) + (so_values?1);} 35 // Option to convert replacement image to scaled gaussian noise 36 im2_cor = im2_cor_a, gauss == false 37 = clip2fmt im2_cor_a.format gauss_im 38 {gauss_im = 39 im_gaussnoise im2_cor_a.width im2_cor_a.height ref_meanmax?0 40(deviation im2_cor_a);} 41 result = im_blend (get_image mask) (get_image 42im2_cor) (get_image im1); 43 }; 44 45//////////////////////////////////////////////////////////////////////////////// 46/* Calculates the mean of the non zero pixels. 47 * 48 * Called from: 49 * _NG_utilities so_meanmax 50 */ 51nonzero_mean im = no_out 52 { 53 zero_im = (im == 0); 54 zero_mean = mean zero_im; 55 no_mean = mean im; 56 no_out = no_mean/(1 - (zero_mean/255)); 57 }; 58 59//////////////////////////////////////////////////////////////////////////////// 60/* Calculates the max and nonzero mean of an image 61 * 62 * Called from: 63 * _NG_utilities so_balance 64 * _NG_utilities so_calculate 65 * _NG_Extra.def Clone_area_item 66 * _NG_Extra.def Balance_item.Balance_find_item 67 */ 68so_meanmax im = result 69 { 70 mean_of_im = nonzero_mean im; 71 adjusted_im = im - mean_of_im; 72 max_of_im = max adjusted_im; 73 74 result = [mean_of_im, max_of_im]; 75 }; 76 77//////////////////////////////////////////////////////////////////////////////// 78/* Calculates the scale and offset required to match a reference mean and max 79 * 80 * Called from: 81 * _NG_utilities so_balance 82 * _NG_Extra.def Balance_item.Balance_find_item 83 */ 84so_calculate ref_meanmax im mask = result 85 { 86 im' = if_then_else mask im 0; 87 im_values = so_meanmax im'; 88 89 mean_of_ref = ref_meanmax?0; 90 mean_of_im = im_values?0; 91 92 max_of_ref = ref_meanmax?1; 93 max_of_im = im_values?1; 94 95 scale = (max_of_ref)/(max_of_im); 96 offset = mean_of_ref - (mean_of_im * scale); 97 result = [ scale, offset ]; 98 }; 99 100//////////////////////////////////////////////////////////////////////////////// 101/* Extends or shortens the central sections of a simple frame to fit round a given image. 102 * 103 * Called from: 104 * _NG_Extra.def Frame_item.Simple_frame_item 105 */ 106simple_frame frame im_w im_h ov cs ms bf option = result 107 { 108 cs' = (1 - cs); 109 ms' = (0.5 - (ms/2)); 110 ms'' = (1 - cs); 111 112 //Regions 113 r_tl = Region_relative frame 0 0 cs cs; 114 r_tr = fliplr r_tl, option == true 115 = Region_relative frame cs' 0 cs cs; 116 r_bl = Region_relative frame 0 cs' cs cs; 117 r_br = fliplr r_bl, option == true 118 = Region_relative frame cs' cs' cs cs; 119 120 r_mt = Region_relative frame ms' 0 ms cs; 121 r_mb = Region_relative frame ms' ms'' ms cs; 122 r_ml = Region_relative frame 0 ms' cs ms; 123 r_mr = fliplr r_ml, option == true 124 = Region_relative frame ms'' ms' cs ms; 125 126 result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; 127 }; 128 129//////////////////////////////////////////////////////////////////////////////// 130/* Copies and extends a simple frame corner to produce a complete frame to fit round a given image. 131 * 132 * Called from: 133 * _NG_Extra.def Frame_item.Frame_corner_item 134 */ 135corner_frame frame im_w im_h ov cs ms bf = result 136 { 137 cs' = (1 - cs); 138 ms' = (0.5 - (ms/2)); 139 140 //Regions 141 r_tl = Region_relative frame 0 0 cs cs; 142 r_tr = fliplr r_tl; 143 r_bl = fliptb r_tl; 144 r_br = fliplr r_bl; 145 r_mt = Region_relative frame ms' 0 ms cs; 146 r_mb = fliptb r_mt; 147 r_ml = Region_relative frame 0 ms' cs ms;; 148 r_mr = fliplr r_ml; 149 result = build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf; 150 }; 151 152//////////////////////////////////////////////////////////////////////////////// 153/* Completes the frame building process for simple_frame and corner_frame. 154 * 155 * _NG_utilities simple_frame 156 * _NG_utilities corner_frame 157 */ 158build_frame r_tl r_tr r_bl r_br r_mt r_mb r_ml r_mr im_w im_h ov bf = result 159 { 160 //Find pixel thickness of frames section 161 s_width = r_ml.width - mean (im_profile (map_unary fliplr (r_ml.value)?0) 1); 162 s_height = r_mt.height - mean (im_profile (map_unary fliptb (r_mt.value)?0) 0); 163 164 w_target = im_w + (2 * (s_width - ov)); 165 h_target = im_h + (2 * (s_height - ov)); 166 167 blend = bf * r_tl.width; 168 169 cw_target = w_target - (2 * r_tl.width) + (2 * blend), w_target > (2 * r_tl.width) 170 = w_target; 171 ch_target = h_target - (2 * r_tl.height) + (2 * blend), h_target > (2 * r_tl.height) 172 = h_target; 173 174 //Use regions to produce sections 175 top = merge_to_scale r_mt cw_target blend 0; 176 bottom = merge_to_scale r_mb cw_target blend 0; 177 left = merge_to_scale r_ml ch_target blend 1; 178 right = merge_to_scale r_mr ch_target blend 1; 179 middle = Image 180 (image_new cw_target ch_target left.bands left.format left.coding left.type 0 0 0); 181 182 //Build sections into full frame. 183 row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) 184 = merge_array blend [[r_tl, top, r_tr]]; 185 row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) 186 = merge_array blend [[left, middle, right]]; 187 row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) 188 = merge_array blend [[r_bl, bottom, r_br]]; 189 190 result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) 191 = merge_array blend [[row_1], [row_2], [row_3]]; 192 }; 193 194//////////////////////////////////////////////////////////////////////////////// 195/* Extends or shortens the central sections of a frame, preserving any central details on each 196 * edge, to fit round a given image. 197 * 198 * Called from: 199 * _NG_Extra.def Frame_item.Complex_frame_item 200 */ 201complex_frame frame im_w im_h ov cs es ms bf option= result 202 { 203 cs' = (1 - cs); 204 ms' = (0.5 - (ms/2)); 205 es' = (0.25 - (es/2)); 206 207 r_tl = Region_relative frame 0 0 cs cs; 208 r_tr = fliplr r_tl, option == true 209 = Region_relative frame cs' 0 cs cs; 210 r_bl = Region_relative frame 0 cs' cs cs; 211 r_br = fliplr r_bl, option == true 212 = Region_relative frame cs' cs' cs cs; 213 214 r_mt = Region_relative frame ms' 0 ms cs; 215 r_mb = Region_relative frame ms' cs' ms cs; 216 r_ml = Region_relative frame 0 ms' cs ms; 217 r_mr = fliplr r_ml, option == true 218 = Region_relative frame cs' ms' cs ms; 219 220 r_et = Region_relative frame es' 0 es cs; 221 r_eb = Region_relative frame es' cs' es cs; 222 r_el = Region_relative frame 0 es' cs es; 223 r_er = fliplr r_el, option == true 224 = Region_relative frame cs' es' cs es; 225 226 //Find pixel thickness of frames section 227 s_width = r_el.width - mean (im_profile (map_unary fliplr (r_el.value)?0) 1); 228 s_height = r_et.height - mean (im_profile (map_unary fliptb (r_et.value)?0) 0); 229 230 w_target = im_w + (2 * (s_width - ov)); 231 h_target = im_h + (2 * (s_height - ov)); 232 min_size = foldr1 min_pair [r_tl.width, r_tl.height, 233 r_mt.width, r_mt.height, 234 r_et.width, r_et.height]; 235 blend = bf * min_size; 236 237 cw_target = w_target - (2 * r_tl.width) + (2 * blend); 238 ch_target = h_target - (2 * r_tl.height) + (2 * blend); 239 240 top = complex_edge r_mt r_et cw_target blend 0; 241 bottom = complex_edge r_mb r_eb cw_target blend 0; 242 left = complex_edge r_ml r_el ch_target blend 1; 243 right = complex_edge r_mr r_er ch_target blend 1; 244 middle = Image 245 (image_new top.width left.height left.bands left.format left.coding left.type 0 0 0); 246 247 //Build regions into full frame. 248 row_1 = frame_lr_min r_tl r_tr w_target blend, ( w_target < (r_tl.width * 2)) 249 = merge_array blend [[r_tl, top, r_tr]]; 250 row_2 = frame_lr_min left right w_target blend, ( w_target < (r_tl.width * 2)) 251 = merge_array blend [[left, middle, right]]; 252 row_3 = frame_lr_min r_bl r_br w_target blend, ( w_target < (r_tl.width * 2)) 253 = merge_array blend [[r_bl, bottom, r_br]]; 254 result = frame_tb_min row_1 row_3 h_target blend, (h_target < (r_tl.height * 2)) 255 = merge_array blend [[row_1], [row_2], [row_3]]; 256 }; 257 258//////////////////////////////////////////////////////////////////////////////// 259/* Function called by complex frame, used to produce section 260 * 261 * Called from: 262 * _NG_utilities.def complex_frame 263 */ 264complex_edge ra rb t bl d = rc 265 { 266 e1 = ceil (ra.width - t)/2, d == 0 267 = 0; 268 e2 = 0, d == 0 269 = ceil (ra.height - t)/2; 270 e3 = t, d == 0 271 = ra.width; 272 e4 = ra.height, d == 0 273 = t; 274 275 check = ra.width, d == 0; 276 = ra.height; 277 278 rai = get_image ra; 279 280 t2 = (t - ra.width + (2 * bl))/2, d == 0 281 = (t - ra.height + (2 * bl))/2; 282 283 rc = ra , t <= 0 284 = Image (im_extract_area rai e1 e2 e3 e4), t <= check 285 = merge_array bl [[rb',ra,rb']], d == 0 286 = merge_array bl [[rb'],[ra],[rb']] 287 {rb' = merge_to_scale rb t2 bl d;} 288 } 289 290////////////////////////////////////////////////////////////////////////////// 291/* Blends two images left/right to produce an image a specific width. 292 * 293 * _NG_utilities build_frame 294 * _NG_utilities complex_frame 295 */ 296frame_lr_min r_l r_r target bw = result 297 { 298 //Calculating the new widh required for each image. 299 no = (target/2 + bw); 300 n_w = no, (r_l.width > no) 301 = r_l.width; 302 303 //Removing excess from what will be the middle of the final image. 304 n_l = im_extract_area r_l.value 0 0 n_w r_l.height; 305 n_r = im_extract_area r_r.value (r_r.width - n_w) 0 n_w r_l.height; 306 307 //Merge the two image together with a bw*2 pixel overlap. 308 result = Image (im_lrmerge n_l n_r ((bw*2) - n_w) 0 bw); 309 }; 310 311////////////////////////////////////////////////////////////////////////////// 312/* Blends two images top/bottom to produce an image a specific width. 313 * 314 * _NG_utilities build_frame 315 * _NG_utilities complex_frame 316 */ 317frame_tb_min r_t r_b target bw = result 318 { 319 //Calculating the new height required for each image. 320 no = (target/2 + bw); 321 n_h = no, (r_t.height > no) 322 = r_t.height; 323 324 //Removing excess from what will be the middle of the final image. 325 n_t = im_extract_area r_t.value 0 0 r_t.width n_h; 326 n_b = im_extract_area r_b.value 0 (r_b.height - n_h) r_b.width n_h; 327 328 //Merge the two image together with a 50 pixel overlap. 329 result = Image (im_tbmerge n_t n_b 0 ((bw*2) -n_h) bw); 330 }; 331 332////////////////////////////////////////////////////////////////////////////// 333/* Resixe canvas of an image to accomodate a frame and possible mount 334 * 335 * Called from: 336 * _NG_Extra.def Frame_item.Frame_corner_item 337 * _NG_Extra.def Frame_item.Simple_frame_item 338 * _NG_Extra.def Frame_item.Complex_frame_item 339 */ 340frame_position_image im ref os colour= result 341 { 342 background = image_new ref.width ref.height 343 im.bands im.format im.coding im.type colour 0 0; 344 345 result = insert_noexpand xp yp im background 346 { 347 xp = (ref.width - im.width)/2; 348 yp = (ref.height - im.height - os)/2; 349 } 350 }; 351 352 353////////////////////////////////////////////////////////////////////////////// 354/* Merges an array of images together according to blend width bw 355 * 356 * Called from: 357 * _NG_Utilites.def build_frame 358 * _NG_Utilites.def complex_frame 359 * _NG_Utilites.def complex_edge 360 */ 361merge_array bw arr = result 362 { 363 merge_lr bw im1 im2 = im3 364 { 365 bw' = im_header_int "Xsize" (get_image im1); 366 bw'' = -(bw' - bw); 367 im3 = im_lrmerge (get_image im1) (get_image im2) bw'' 0 bw; 368 } 369 merge_tb bw im1 im2 = im3 370 { 371 bw' = im_header_int "Ysize" (get_image im1); 372 bw'' = -(bw' - bw); 373 im3 = im_tbmerge (get_image im1) (get_image im2) 0 bw'' bw; 374 } 375 376 im_out = (image_set_origin 0 0 @ 377 foldl1 (merge_tb bw) @ 378 map (foldl1 (merge_lr bw))) arr; 379 result = Image im_out; 380 } 381 382////////////////////////////////////////////////////////////////////////////// 383/* Repeatably top/bottom add clones of im, with a defined overlap, until final height > target 384 * 385 * Called from: 386 * _NG_Utilites.def build_frame 387 * _NG_Utilites.def complex_edge 388 */ 389merge_to_scale im target blend dir = result 390 { 391 blend' = floor blend; 392 393 //allow fir lr or tb process 394 var_a = im.width, dir == 0 395 = im.height; 396 397 var_w = im.width, dir == 1 398 = target, target > blend' 399 = blend'; 400 var_h = im.height, dir == 0 401 = target, target > blend' 402 = blend'; 403 404 //total numner of copies of im requires, taking overlap into account. 405 no_loops = ceil ((log ((target - blend')/(var_a - blend')))/(log 2)); 406 407 process im no = result 408 { 409 pr_a = im_header_int "Xsize" (get_image im), dir == 0 410 = im_header_int "Ysize" (get_image im); 411 pr_b = -(pr_a - blend' + 1); 412 413 im' = im_lrmerge (get_image im) (get_image im) pr_b 0 blend', dir == 0 414 = im_tbmerge (get_image im) (get_image im) 0 pr_b blend'; 415 no' = no - 1; 416 417 result = im', no' < 1 418 = process im' no'; 419 } 420 421 im_tmp = im.value, var_a > target 422 = process im no_loops; 423 424 result = Image (im_extract_area (get_image im_tmp) 0 0 var_w var_h); 425 } 426 427////////////////////////////////////////////////////////////////////////////// 428/* Selects an elispe based on a line and a width 429 * 430 * Called from: 431 * _NG_Extra.def Select_item.Elipse 432 */ 433select_ellipse line width = mask 434 { 435 im = line.image; 436 437 //Make a 2 band image whose value equals its coordinates. 438 im_coor = Image (make_xy im.width im.height); 439 440 //Adjust the values to center tham on (line.left, line.top) 441 im_cent = im_coor - Vector [line.left,line.top]; 442 443 w = line.width; 444 h = line.height; 445 446 angle = 270, w == 0 && h < 0 447 = 90, w == 0 && h >= 0 448 = 360 + atan (h/w), w > 0 && h < 0 449 = atan (h/w), w > 0 && h >= 0 450 = 180 + atan (h/w); 451 452 a = ( (h ** 2) + (w ** 2) )**0.5; 453 b = a * width; 454 455 x' = ( (cos angle) * im_cent?0) + ( (sin angle) * im_cent?1); 456 y' = ( (cos angle) * im_cent?1) - ( (sin angle) * im_cent?0); 457 458 mask = ( (b**2) * (x'**2) ) + ( (a**2) * (y'**2) ) <= (a * b)**2; 459 } 460 461////////////////////////////////////////////////////////////////////////////// 462/* Selects a tetragon based on four points. 463 * 464 * Called from: 465 * _NG_Extra.def Select_item.Tetragon 466 * _NG_Extra.def Perspective_item 467 */ 468select_tetragon p1 p2 p3 p4 = mask 469 { 470 //Put points in clockwise order starting at the top left. 471 pt_list = sort_pts_clockwise [p1, p2, p3, p4]; 472 473 pair_list = [ 474 [ pt_list?0, pt_list?1 ], 475 [ pt_list?1, pt_list?2 ], 476 [ pt_list?2, pt_list?3 ], 477 [ pt_list?3, pt_list?0 ] ]; 478 479 //Make xy image the same size as p1.image; 480 im_xy = Image (make_xy p1.image.width p1.image.height); 481 white = Image (image_new p1.image.width p1.image.height 1 0 Image_coding.NOCODING 1 255 0 0); 482 483 mask = foldl process white pair_list; 484 485 /* Treat each pair of point as a vector going from p1 to p2, 486 * then select all to right of line. This is done for each pair, 487 * the results are all combined to select the area defined by 488 * the four points. 489 */ 490 process im_in pair = im_out 491 { 492 x = (pair?0).left; 493 y = (pair?0).top; 494 x'= (pair?1).left; 495 y'= (pair?1).top; 496 497 w = x' - x; 498 h = y' - y; 499 500 m = 0, x == x' 501 = (y-y')/(x-x'); 502 c = 0, x == x' 503 = ((y*x') - (y'*x))/(x' - x); 504 505 mask= im_xy?1 - (im_xy?0 * m) >= c, w > 0 506 = im_xy?1 - (im_xy?0 * m) <= c, w < 0 507 = im_xy?0 <= x, w == 0 && h > 0 508 = im_xy?0 >= x; 509 510 im_out = im_in & mask; 511 } 512 } 513 514////////////////////////////////////////////////////////////////////////////// 515/* Selects a tetragon based on four points. 516 * 517 * Called from: 518 * _NG_Extra.def Select_item.Polygon 519 */ 520select_polygon pt_list = mask 521 { 522 group_check = is_instanceof "Group" pt_list; 523 pt_l = pt_list.value, group_check 524 = pt_list; 525 526 im = (pt_l?0).image; 527 im_xy = Image (make_xy im.width im.height); 528 black = Image (image_new im_xy.width im_xy.height 1 0 Image_coding.NOCODING 1 0 0 0); 529 530 x = im_xy?0; 531 y = im_xy?1; 532 533 pt_l' = grp_trip pt_l; 534 535 mask = foldl process black pt_l'; 536 537 /*Takes a group adds the first two the end and then creates a lists of 538 *lists [[a, b, c], [b, c, d] .... [x, a, b]] 539 */ 540 grp_trip l = l'' 541 { 542 px = take 2 l; 543 l' = join l px; 544 start = [(take 3 l')]; 545 rest = drop 3 l'; 546 547 process a b = c 548 { 549 x = (last a)?1; 550 x'= (last a)?2; 551 x'' = [[x, x', b]]; 552 c = join a x''; 553 } 554 555 l'' = foldl process start rest; 556 }; 557 558 process im_in triplet = im_out 559 { 560 p1 = triplet?0; 561 p2 = triplet?1; 562 p3 = triplet?2; 563 564 //check for change in x direction between p1-p2 and p2 -p3 565 dir_1 = sign (p2.left - p1.left); 566 dir_2 = sign (p3.left - p2.left); 567 dir = dir_1 + dir_2; 568 569 //define min x limit. 570 min_x = p1.left, p1.left < p2.left 571 = p2.left + 1, dir != 0 572 = p2.left; 573 574 //define max x limit. 575 max_x = p1.left, p1.left > p2.left 576 = p2.left - 1, dir != 0 577 = p2.left; 578 579 //equation of line defined by p1 and p2 580 m = line_m p1 p2; 581 c = line_c p1 p2; 582 583 //Every thing below the line 584 im_test = ((y >= (m * x) + c) & (x >= min_x) & (x <= max_x)); 585 586 im_out = im_in ^ im_test; 587 } 588 589 line_c p1 p2 = c 590 {m = line_m p1 p2; 591 c = p1.top - (m * p1.left);}; 592 593 line_m p1 p2 = (p2.top - p1.top)/(p2.left - p1.left), p2.left != p1.left 594 = 0; 595 } 596 597////////////////////////////////////////////////////////////////////////////// 598/* Selects a tetragon based on four points. 599 * 600 * Called from: 601 * _NG_Extra.def Perspective_match_item 602 * _NG_Extra.def Perspective_item 603 */ 604perspective_transform to from = trans'' 605 { 606 /* 607 * Tramsformation matrix is calculated on the bases of the following functions: 608 * x' = c0x + c1y + c2xy + c3 609 * y' = c4x + c5y + c6xy + c7 610 * 611 * The functions used in vips im_transform works based on the functions: 612 * x = x' + b0 + b2x' + b4y' + b6x'y' 613 * y = y' + b1 + b3x' + b5y' + b7x'y' 614 * 615 * and is applied in the form of the matrix: 616 * 617 * [[b0, b1], 618 * [b2, b3], 619 * [b4, b5], 620 * [b6, b7]] 621 * 622 * Therefore our required calculated matrix will be 623 * 624 * [[ c3 , c7], 625 * [(c0 - 1) , c4], 626 * [ c1 , (c5 - 1)], 627 * [ c2 , c6]] 628 * 629 * to = [x1, y1, x2, y2, x3, y3, x4, y4] 630 * from = [x1', y1', x2', y2', x3', y3', x4', y4'] 631 * trans = [[c0], [c1], [c2], [c3], [c4], [c5], [c6], [c7]] 632 * 633 */ 634 635 to' = Matrix 636 [[to?0, to?1, ((to?0)*(to?1)), 1, 0, 0, 0, 0], 637 [0, 0, 0, 0, to?0, to?1, ((to?0)*(to?1)), 1], 638 [to?2, to?3, ((to?2)*(to?3)), 1, 0, 0, 0, 0], 639 [0, 0, 0, 0, to?2, to?3, ((to?2)*(to?3)), 1], 640 [to?4, to?5, ((to?4)*(to?5)), 1, 0, 0, 0, 0], 641 [0, 0, 0, 0, to?4, to?5, ((to?4)*(to?5)), 1], 642 [to?6, to?7, ((to?6)*(to?7)), 1, 0, 0, 0, 0], 643 [0, 0, 0, 0, to?6, to?7, ((to?6)*(to?7)), 1]]; 644 645 from' = Matrix (transpose [from]); 646 647 to'' = to' ** (-1); 648 649 trans = to'' * from'; 650 trans' = trans.value; 651 trans''= Matrix [[(trans'?3)?0, (trans'?7)?0 ], 652 [((trans'?0)?0 - 1), (trans'?4)?0 ], 653 [(trans'?1)?0, ((trans'?5)?0 - 1)], 654 [(trans'?2)?0, (trans'?6)?0 ]]; 655 } 656 657 658 659////////////////////////////////////////////////////////////////////////////// 660/* Sort a list of points into clockwise order. 661 * 662 * Called from: 663 * _NG_utilities.def select_tetragon 664 * _NG_Extra.def Perspective_match_item 665 * _NG_Extra.def Perspective_item 666 */ 667sort_pts_clockwise l = l'' 668 { 669 // sort functions: 670 f_top a b = a.top < b.top; 671 f_left a b = a.left < b.left; 672 f_right a b = a.left > b.left; 673 674 l' = sortc f_top l; 675 l'_a = take 2 l'; 676 l'_b = drop 2 l'; 677 678 l''_a = sortc f_left l'_a; 679 l''_b = sortc f_right l'_b; 680 l'' = join l''_a l''_b; 681 } 682