00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079 #include "utilities.h"
00080 #include "matrix.h"
00081 #include "pad-pin-data.h"
00082
00083 static int replace_footprints(ElementTypePtr new_element);
00084 static Boolean replace_one_footprint(ElementTypePtr old_element,
00085 ElementTypePtr new_element);
00086 static Boolean replace_one_footprint_quick(ElementTypePtr old_element,
00087 ElementTypePtr new_element);
00088 static Boolean replace_one_footprint_expensive(ElementTypePtr old_element,
00089 ElementTypePtr new_element);
00090 static Boolean replace_one_footprint_translate_only(ElementTypePtr old_element,
00091 ElementTypePtr new_element);
00092 static void replace_one_footprint_aux(ElementTypePtr old_element,
00093 PadOrPinType* old1_pp,
00094 PadOrPinType* old2_pp,
00095 ElementTypePtr new_element,
00096 PadOrPinType* new1_pp,
00097 PadOrPinType* new2_pp);
00098 static BYTE calculate_rotation_steps(CheapPointType new1_pt,
00099 CheapPointType new2_pt,
00100 CheapPointType old1_pt,
00101 CheapPointType old2_pt);
00102 static void transfer_text(ElementTypePtr old_element,
00103 ElementTypePtr new_element);
00104 static void transfer_names(ElementTypePtr old_element,
00105 ElementTypePtr new_element);
00106 static void transfer_flags(ElementTypePtr old_element,
00107 ElementTypePtr new_element);
00108
00109 enum {
00110 MATCH_MODE_AUTO,
00111 MATCH_MODE_MANUAL
00112 } match_mode = MATCH_MODE_AUTO;
00113
00114 enum {
00115 STYLE_ALL,
00116 STYLE_SELECTED,
00117 STYLE_NAMED
00118
00119 } style = STYLE_ALL;
00120
00121
00122
00123
00124
00125 int footprint_update(int argc, char **argv, int x, int y);
00126
00127 HID_Action footprint_update_action_list[] = {
00128 { ACTION_NAME, NULL, footprint_update, NULL, NULL }
00129 };
00130
00131 REGISTER_ACTIONS(footprint_update_action_list)
00132
00133 void
00134 pcb_plugin_init()
00135 {
00136 register_footprint_update_action_list();
00137 }
00138
00139 int global_argc = 0;
00140 char **global_argv = NULL;
00141
00142 int
00143 usage()
00144 {
00145 base_log("usage:\n");
00146 base_log(" UpdateFootprintsFromBuffer()\n");
00147 base_log(" UpdateFootprintsFromBuffer(auto|manual)\n");
00148 base_log(" UpdateFootprintsFromBuffer(auto|manual, selected)\n");
00149 base_log(" UpdateFootprintsFromBuffer(auto|manual, named, "
00150 "<name1>[, <name2>...])\n");
00151 base_log("version: %s\n", VERSION);
00152 return 1;
00153 }
00154
00155 int
00156 footprint_update(int argc, char **argv, int x, int y)
00157 {
00158 global_argc = argc;
00159 global_argv = argv;
00160
00161 debug_log("footprint_update\n");
00162 debug_log(" argc: %d\n", argc);
00163 if (argc) {
00164 int i;
00165 for (i = 0; i < argc; i++) {
00166 debug_log(" argv[%d]: %s\n", i, argv[i]);
00167 }
00168 }
00169 if (argc >= 1) {
00170 if (strcasecmp(argv[0], "auto") == 0) {
00171 match_mode = MATCH_MODE_AUTO;
00172 style = STYLE_ALL;
00173 } else if (strcasecmp(argv[0], "manual") == 0) {
00174 match_mode = MATCH_MODE_MANUAL;
00175 style = STYLE_SELECTED;
00176 } else {
00177 base_log("Error: If given, the first argument must be "
00178 "\"auto\" or \"manual\".\n");
00179 return usage();
00180 }
00181 if (argc >= 2) {
00182 if (strcasecmp(argv[1], "selected") == 0) {
00183 style = STYLE_SELECTED;
00184 } else if (strcasecmp(argv[1], "named") == 0) {
00185 style = STYLE_NAMED;
00186 } else {
00187 base_log("Error: If given, the second argument must be "
00188 "\"selected\", or \"named\".\n");
00189 return usage();
00190 }
00191 }
00192
00193 }
00194 debug_log("match_mode: %d\n", match_mode);
00195 debug_log("style: %d\n", style);
00196
00197 if (PASTEBUFFER->Data->ElementN != 1) {
00198 base_log("Error: Paste buffer should contain one element.\n");
00199 return usage();
00200 }
00201
00202 ELEMENT_LOOP(PASTEBUFFER->Data);
00203 {
00204 int replaced = replace_footprints(element);
00205 if (replaced) {
00206 base_log("Replaced %d elements.\n", replaced);
00207 IncrementUndoSerialNumber();
00208 }
00209 }
00210 END_LOOP;
00211
00212 return 0;
00213 }
00214
00215 static int
00216 replace_footprints(ElementTypePtr new_element)
00217 {
00218 int replaced = 0;
00219 int i = 0;
00220
00221 ELEMENT_LOOP (PCB->Data);
00222 {
00223 if (match_mode != MATCH_MODE_AUTO
00224 || strcmp(DESCRIPTION_NAME(new_element),
00225 DESCRIPTION_NAME(element)) == 0) {
00226 Boolean matched = False;
00227
00228 switch (style) {
00229 case STYLE_ALL:
00230 matched = True;
00231 break;
00232 case STYLE_SELECTED:
00233 matched = TEST_FLAG(SELECTEDFLAG, element);
00234 break;
00235 case STYLE_NAMED:
00236 for (i = 0; i < global_argc; i++) {
00237 if (NAMEONPCB_NAME(element)
00238 && strcasecmp(NAMEONPCB_NAME(element), global_argv[i]) == 0) {
00239 matched = True;
00240 break;
00241 }
00242 }
00243 break;
00244 }
00245 if (matched) {
00246 if (TEST_FLAG (LOCKFLAG, element)) {
00247 base_log("Skipping \"%s\". Element locked.\n",
00248 NAMEONPCB_NAME(element));
00249 } else {
00250 debug_log("Considering \"%s\".\n", NAMEONPCB_NAME(element));
00251 if (replace_one_footprint(element, new_element)) {
00252 base_log("Replaced \"%s\".\n", NAMEONPCB_NAME(element));
00253 replaced++;
00254 }
00255 }
00256 }
00257 }
00258 }
00259 END_LOOP;
00260 return replaced;
00261 }
00262
00263 static Boolean
00264 replace_one_footprint(ElementTypePtr old_element,
00265 ElementTypePtr new_element)
00266 {
00267
00268 return (replace_one_footprint_quick(old_element, new_element)
00269 || replace_one_footprint_expensive(old_element, new_element)
00270 || replace_one_footprint_translate_only(old_element, new_element));
00271 }
00272
00273 static Boolean
00274 replace_one_footprint_quick(ElementTypePtr old_element,
00275 ElementTypePtr new_element)
00276 {
00277
00278
00279 if (! have_two_corresponding_unique_non_coincident(old_element,
00280 new_element,
00281 NULL, NULL,
00282 NULL, NULL)) {
00283 return False;
00284 }
00285
00286
00287 ElementTypePtr copy_element =
00288 CopyElementLowLevel(PCB->Data, NULL, new_element, False, 0, 0);
00289
00290 PadOrPinType old1_pp;
00291 PadOrPinType old2_pp;
00292 PadOrPinType copy1_pp;
00293 PadOrPinType copy2_pp;
00294 if (! have_two_corresponding_unique_non_coincident(old_element,
00295 copy_element,
00296 &old1_pp, &old2_pp,
00297 ©1_pp, ©2_pp)) {
00298 base_log("Error: Couldn't find two corresponding, unique, "
00299 "non-coincident element pads/pins.");
00300 return False;
00301 }
00302 replace_one_footprint_aux(old_element, &old1_pp, &old2_pp,
00303 copy_element, ©1_pp, ©2_pp);
00304 debug_log("Used quick replacement.\n");
00305 return True;
00306 }
00307
00308 static Boolean
00309 replace_one_footprint_expensive(ElementTypePtr old_element,
00310 ElementTypePtr new_element)
00311 {
00312
00313
00314
00315
00316
00317
00318
00319
00320 if (! have_two_corresponding_non_coincident(old_element, new_element,
00321 NULL, NULL, NULL, NULL)) {
00322 return False;
00323 }
00324
00325
00326 ElementTypePtr copy_element =
00327 CopyElementLowLevel(PCB->Data, NULL, new_element, False, 0, 0);
00328
00329 int old_ppd_len = 0;
00330 ElementPadPinData* old_ppd =
00331 alloc_pad_pin_data_array(old_element, &old_ppd_len);
00332
00333 int copy_ppd_len = 0;
00334 ElementPadPinData* copy_ppd =
00335 alloc_pad_pin_data_array(copy_element, ©_ppd_len);
00336
00337 int copy_index1 = 0;
00338 int copy_index2 = 0;
00339 if (! find_non_coincident(copy_ppd, copy_ppd_len,
00340 ©_index1, ©_index2)) {
00341 base_log("Error: Couldn't find non-coincident element pads/pins.");
00342 MYFREE(old_ppd);
00343 MYFREE(copy_ppd);
00344 return False;
00345 }
00346
00347 Boolean reflect = IS_REFLECTED(new_element, old_element);
00348 int old_index1 = 0;
00349 int old_index2 = 0;
00350 if (! find_best_corresponding_pads_or_pins(copy_ppd, copy_ppd_len,
00351 copy_index1, copy_index2,
00352 reflect,
00353 old_ppd, old_ppd_len,
00354 &old_index1, &old_index2)) {
00355 MYFREE(old_ppd);
00356 MYFREE(copy_ppd);
00357 return False;
00358 }
00359
00360 replace_one_footprint_aux(old_element,
00361 &old_ppd[old_index1].pp,
00362 &old_ppd[old_index2].pp,
00363 copy_element,
00364 ©_ppd[copy_index1].pp,
00365 ©_ppd[copy_index2].pp);
00366 MYFREE(old_ppd);
00367 MYFREE(copy_ppd);
00368 debug_log("Used expensive replacement.\n");
00369 return True;
00370 }
00371
00372 static Boolean
00373 replace_one_footprint_translate_only(ElementTypePtr old_element,
00374 ElementTypePtr new_element)
00375 {
00376
00377 if (! have_any_corresponding_pad_or_pin(old_element, new_element,
00378 NULL, NULL)) {
00379 return False;
00380 }
00381
00382
00383 ElementTypePtr copy_element =
00384 CopyElementLowLevel(PCB->Data, NULL, new_element, False, 0, 0);
00385
00386 PadOrPinType old_pp = make_pad_or_pin(NULL, NULL);
00387 PadOrPinType copy_pp = make_pad_or_pin(NULL, NULL);
00388 if (! have_any_corresponding_pad_or_pin(old_element, copy_element,
00389 &old_pp, ©_pp)) {
00390 base_log("Error: Couldn't find any corresponding pads or pins.");
00391 return False;
00392 }
00393 replace_one_footprint_aux(old_element, &old_pp, NULL,
00394 copy_element, ©_pp, NULL);
00395 debug_log("Used translation-only replacement.\n");
00396 return True;
00397 }
00398
00399 static void
00400 replace_one_footprint_aux(ElementTypePtr old_element,
00401 PadOrPinType* old1_pp, PadOrPinType* old2_pp,
00402 ElementTypePtr copy_element,
00403 PadOrPinType* copy1_pp, PadOrPinType* copy2_pp)
00404 {
00405 Boolean two_points = (old2_pp && copy2_pp);
00406 Boolean reflect = IS_REFLECTED(copy_element, old_element);
00407
00408 debug_log("Reflect?: %s\n", (reflect ? "yes" : "no"));
00409 if (reflect) {
00410
00411 ChangeElementSide(copy_element, 0);
00412 }
00413
00414 CheapPointType copy1_pt = pad_or_pin_center(copy1_pp);
00415 CheapPointType old1_pt = pad_or_pin_center(old1_pp);
00416
00417 BYTE rot_steps = 0;
00418 if (two_points) {
00419
00420 CheapPointType copy2_pt = pad_or_pin_center(copy2_pp);
00421 CheapPointType old2_pt = pad_or_pin_center(old2_pp);
00422 rot_steps =
00423 calculate_rotation_steps(copy1_pt, copy2_pt, old1_pt, old2_pt);
00424 }
00425 if (rot_steps) {
00426
00427 RotateElementLowLevel(PCB->Data, copy_element, 0, 0, rot_steps);
00428
00429 copy1_pt = pad_or_pin_center(copy1_pp);
00430 }
00431
00432
00433 LocationType dx = old1_pt.X - copy1_pt.X;
00434 LocationType dy = old1_pt.Y - copy1_pt.Y;
00435
00436 MoveElementLowLevel(PCB->Data, copy_element, dx, dy);
00437
00438
00439 transfer_text(old_element, copy_element);
00440 transfer_names(old_element, copy_element);
00441 transfer_flags(old_element, copy_element);
00442 SetElementBoundingBox(PCB->Data, copy_element, &PCB->Font);
00443
00444 AddObjectToCreateUndoList(ELEMENT_TYPE,
00445 copy_element, copy_element, copy_element);
00446
00447 MoveObjectToRemoveUndoList(ELEMENT_TYPE,
00448 old_element, old_element, old_element);
00449 }
00450
00451 static BYTE
00452 calculate_rotation_steps(CheapPointType new1_pt, CheapPointType new2_pt,
00453 CheapPointType old1_pt, CheapPointType old2_pt)
00454 {
00455
00456 LocationType new1_to_origin_dx = -new1_pt.X;
00457 LocationType new1_to_origin_dy = -new1_pt.Y;
00458
00459 CheapPointType new2_translated_pt =
00460 make_point(new2_pt.X + new1_to_origin_dx,
00461 new2_pt.Y + new1_to_origin_dy);
00462 double new2_angle =
00463 (new2_translated_pt.Y || new2_translated_pt.X
00464 ? atan2(new2_translated_pt.Y, new2_translated_pt.X) : 0);
00465
00466
00467 LocationType old1_to_origin_dx = -old1_pt.X;
00468 LocationType old1_to_origin_dy = -old1_pt.Y;
00469
00470 CheapPointType old2_translated_pt =
00471 make_point(old2_pt.X + old1_to_origin_dx,
00472 old2_pt.Y + old1_to_origin_dy);
00473 double old2_angle =
00474 (old2_translated_pt.X || old2_translated_pt.Y
00475 ? atan2(old2_translated_pt.Y, old2_translated_pt.X) : 0);
00476
00477
00478 double angle = old2_angle - new2_angle;
00479 if (angle > M_PI) {
00480 angle -= 2 * M_PI;
00481 } else if (angle < -M_PI) {
00482 angle += 2 * M_PI;
00483 }
00484 debug_log("Rotation: %lf\n", RAD_TO_DEG * angle);
00485
00486 return angle_to_rotation_steps(angle);
00487 }
00488
00489 static void
00490 transfer_text(ElementTypePtr old_element, ElementTypePtr new_element)
00491 {
00492 int i;
00493 for (i = 0; i < MAX_ELEMENTNAMES; i++) {
00494 TextTypePtr old_text = &old_element->Name[i];
00495 TextTypePtr new_text = &new_element->Name[i];
00496 MYFREE(new_text->TextString);
00497 new_text->X = old_text->X;
00498 new_text->Y = old_text->Y;
00499 new_text->Direction = old_text->Direction;
00500 new_text->Flags = old_text->Flags;
00501 new_text->Scale = old_text->Scale;
00502 new_text->TextString =
00503 ((old_text->TextString && *old_text->TextString) ?
00504 MyStrdup(old_text->TextString, "transfer_text()") : NULL);
00505 }
00506 }
00507
00508 static void
00509 transfer_names(ElementTypePtr old_element, ElementTypePtr new_element)
00510 {
00511 PAD_OR_PIN_LOOP_HYG(old_element, _old);
00512 {
00513 const char* old_name = pad_or_pin_name(&pp_old);
00514 PAD_OR_PIN_LOOP_HYG(new_element, _new);
00515 {
00516 if (pad_or_pin_number_cmp(&pp_old, &pp_new) == 0) {
00517 if (pp_new.pad) {
00518 MYFREE(pp_new.pad->Name);
00519 pp_new.pad->Name = MyStrdup((char*)old_name, "transfer_names()");
00520 } else if (pp_new.pin) {
00521 MYFREE(pp_new.pin->Name);
00522 pp_new.pin->Name = MyStrdup((char*)old_name, "transfer_names()");
00523 }
00524 }
00525 }
00526 END_LOOP;
00527 }
00528 END_LOOP;
00529 }
00530
00531 static void
00532 transfer_flags(ElementTypePtr old_element, ElementTypePtr new_element)
00533 {
00534 PAD_OR_PIN_LOOP_HYG(old_element, _old);
00535 {
00536 if (pad_or_pin_test_flag(&pp_old, SELECTEDFLAG)) {
00537 PAD_OR_PIN_LOOP_HYG(new_element, _new);
00538 {
00539 if (pad_or_pin_number_cmp(&pp_old, &pp_new) == 0) {
00540 pad_or_pin_set_flag(&pp_new, SELECTEDFLAG);
00541 }
00542 }
00543 END_LOOP;
00544 }
00545 }
00546 END_LOOP;
00547 if (TEST_FLAG(SELECTEDFLAG, old_element)) {
00548 SET_FLAG(SELECTEDFLAG, new_element);
00549 }
00550 }