/* --------------------------------------------------------------------------- * moves the object identified by its data pointers and the type * as well as all attached rubberband lines */ void * MoveObjectAndRubberband (int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY) { RubberbandTypePtr ptr; void *ptr2; /* setup offset */ DeltaX = DX; DeltaY = DY; if (DX == 0 && DY == 0) return (NULL); /* move all the lines... and reset the counter */ ptr = Crosshair.AttachedObject.Rubberband; while (Crosshair.AttachedObject.RubberbandN) { /* first clear any marks that we made in the line flags */ CLEAR_FLAG (RUBBERENDFLAG, ptr->Line); AddObjectToMoveUndoList (LINEPOINT_TYPE, ptr->Layer, ptr->Line, ptr->MovedPoint, DX, DY); MoveLinePoint (ptr->Layer, ptr->Line, ptr->MovedPoint); Crosshair.AttachedObject.RubberbandN--; ptr++; } AddObjectToMoveUndoList (Type, Ptr1, Ptr2, Ptr3, DX, DY); ptr2 = ObjectOperation (&MoveFunctions, Type, Ptr1, Ptr2, Ptr3); IncrementUndoSerialNumber (); return (ptr2); }
/* --------------------------------------------------------------------------- * inserts a point into a line */ static void * InsertPointIntoLine (LayerTypePtr Layer, LineTypePtr Line) { LineTypePtr line; LocationType X, Y; if (((Line->Point1.X == InsertX) && (Line->Point1.Y == InsertY)) || ((Line->Point2.X == InsertX) && (Line->Point2.Y == InsertY))) return (NULL); X = Line->Point2.X; Y = Line->Point2.Y; AddObjectToMoveUndoList (LINEPOINT_TYPE, Layer, Line, &Line->Point2, InsertX - X, InsertY - Y); EraseLine (Line); r_delete_entry (Layer->line_tree, (BoxTypePtr) Line); Line->Point2.X = InsertX; Line->Point2.Y = InsertY; SetLineBoundingBox (Line); r_insert_entry (Layer->line_tree, (BoxTypePtr) Line, 0); DrawLine (Layer, Line, 0); /* we must create after playing with Line since creation may * invalidate the line pointer */ if ((line = CreateDrawnLineOnLayer (Layer, InsertX, InsertY, X, Y, Line->Thickness, Line->Clearance, Line->Flags))) { AddObjectToCreateUndoList (LINE_TYPE, Layer, line, line); DrawLine (Layer, line, 0); /* creation call adds it to the rtree */ } Draw (); return (line); }
/* --------------------------------------------------------------------------- * moves the object identified by its data pointers and the type * not we don't bump the undo serial number */ void * MoveObject (int Type, void *Ptr1, void *Ptr2, void *Ptr3, Coord DX, Coord DY) { void *result; /* setup offset */ DeltaX = DX; DeltaY = DY; AddObjectToMoveUndoList (Type, Ptr1, Ptr2, Ptr3, DX, DY); result = ObjectOperation (&MoveFunctions, Type, Ptr1, Ptr2, Ptr3); return (result); }
/*! * \brief Place one element. * * Must initialize statics above before calling for the first time. * * This is taken almost entirely from ActionDisperseElements, with cleanup */ static void place (ElementType *element) { Coord dx, dy; /* figure out how much to move the element */ dx = minx - element->BoundingBox.X1; dy = miny - element->BoundingBox.Y1; /* snap to the grid */ dx -= (element->MarkX + dx) % (long) (PCB->Grid); dx += (long) (PCB->Grid); dy -= (element->MarkY + dy) % (long) (PCB->Grid); dy += (long) (PCB->Grid); /* * and add one grid size so we make sure we always space by GAP or * more */ dx += (long) (PCB->Grid); /* Figure out if this row has room. If not, start a new row */ if (minx != GAP && GAP + element->BoundingBox.X2 + dx > PCB->MaxWidth) { miny = maxy + GAP; minx = GAP; place(element); /* recurse can't loop, now minx==GAP */ return; } /* move the element */ MoveElementLowLevel (PCB->Data, element, dx, dy); /* and add to the undo list so we can undo this operation */ AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy); /* keep track of how tall this row is */ minx += element->BoundingBox.X2 - element->BoundingBox.X1 + GAP; if (maxy < element->BoundingBox.Y2) { maxy = element->BoundingBox.Y2; } }
/*! * \brief Move everything. * * Call our own 'MyMove*LowLevel' where they don't exist in move.c. * This gets very slow if there are large polygons present, since every * element move re-clears the poly, followed by the polys moving and * re-clearing everything again. */ static void MoveAll(Coord dx, Coord dy) { ELEMENT_LOOP (PCB->Data); { MoveElementLowLevel (PCB->Data, element, dx, dy); AddObjectToMoveUndoList (ELEMENT_TYPE, NULL, NULL, element, dx, dy); } END_LOOP; VIA_LOOP (PCB->Data); { MyMoveViaLowLevel (PCB->Data, via, dx, dy); AddObjectToMoveUndoList (VIA_TYPE, NULL, NULL, via, dx, dy); } END_LOOP; ALLLINE_LOOP (PCB->Data); { MyMoveLineLowLevel (PCB->Data, layer, line, dx, dy); AddObjectToMoveUndoList (LINE_TYPE, NULL, NULL, line, dx, dy); } ENDALL_LOOP; ALLARC_LOOP (PCB->Data); { MyMoveArcLowLevel (PCB->Data, layer, arc, dx, dy); AddObjectToMoveUndoList (ARC_TYPE, NULL, NULL, arc, dx, dy); } ENDALL_LOOP; ALLTEXT_LOOP (PCB->Data); { MyMoveTextLowLevel (layer, text, dx, dy); AddObjectToMoveUndoList (TEXT_TYPE, NULL, NULL, text, dx, dy); } ENDALL_LOOP; ALLPOLYGON_LOOP (PCB->Data); { /* * XXX MovePolygonLowLevel does not mean "no gui" like * XXX MoveElementLowLevel, it doesn't even handle layer * XXX tree activity. */ MyMovePolygonLowLevel (PCB->Data, layer, polygon, dx, dy); AddObjectToMoveUndoList (POLYGON_TYPE, NULL, NULL, polygon, dx, dy); } ENDALL_LOOP; }
/* * DistributeText(X, [Lefts/Rights/Centers/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) * DistributeText(Y, [Tops/Bottoms/Centers/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) * * As with align, plus: * * Gaps - Make gaps even rather than spreading points evenly * First, Last, * Crosshair - Two arguments specifying both ends of the * distribution, they can't both be the same. * * Defaults are Lefts/Tops, First, Last * * Distributed texts always retain the same relative order they had * before they were distributed. */ static int distributetext(int argc, char **argv, Coord x, Coord y) { int dir; int point; int refa, refb; int gridless; Coord s, e, slack; int divisor; int changed = 0; int i; if (argc < 1 || argc == 3 || argc > 4) { AFAIL(distributetext); } /* parse direction arg */ switch ((dir = keyword(ARG(0)))) { case K_X: case K_Y: break; default: AFAIL(distributetext); } /* parse point (within each element) which will be distributed */ switch ((point = keyword(ARG(1)))) { case K_Centers: case K_Gaps: break; case K_Lefts: case K_Rights: if (dir == K_Y) { AFAIL(distributetext); } break; case K_Tops: case K_Bottoms: if (dir == K_X) { AFAIL(distributetext); } break; case K_none: /* default value */ if (dir == K_X) { point = K_Lefts; } else { point = K_Tops; } break; default: AFAIL(distributetext); } /* parse reference which will determine first distribution coordinate */ switch ((refa = keyword(ARG(2)))) { case K_First: case K_Last: case K_Average: case K_Crosshair: break; case K_none: refa = K_First; /* default value */ break; default: AFAIL(distributetext); } /* parse reference which will determine final distribution coordinate */ switch ((refb = keyword(ARG(3)))) { case K_First: case K_Last: case K_Average: case K_Crosshair: break; case K_none: refb = K_Last; /* default value */ break; default: AFAIL(distributetext); } if (refa == refb) { AFAIL(distributetext); } /* optionally work off the grid (solar cells!) */ switch (keyword(ARG(4))) { case K_Gridless: gridless = 1; break; case K_none: gridless = 0; break; default: AFAIL(distributetext); } SaveUndoSerialNumber(); /* build list of texts in orthogonal axis order */ sort_texts_by_pos(K_distributetext, dir, point); /* find the endpoints given the above options */ s = reference_coord(K_distributetext, x, y, dir, point, refa); e = reference_coord(K_distributetext, x, y, dir, point, refb); slack = e - s; /* use this divisor to calculate spacing (for 1 elt, avoid 1/0) */ divisor = (ntexts_by_pos > 1) ? (ntexts_by_pos - 1) : 1; /* even the gaps instead of the edges or whatnot */ /* find the "slack" in the row */ if (point == K_Gaps) { Coord w; /* subtract all the "widths" from the slack */ for (i = 0; i < ntexts_by_pos; ++i) { TextType *text = texts_by_pos[i].text; /* coord doesn't care if I mix Lefts/Tops */ w = texts_by_pos[i].width = coord(text, dir, K_Rights) - coord(text, dir, K_Lefts); /* Gaps distribution is on centers, so half of * first and last text don't count */ if (i == 0 || i == ntexts_by_pos - 1) { w /= 2; } slack -= w; } /* slack could be negative */ } /* move all selected texts to the new coordinate */ for (i = 0; i < ntexts_by_pos; ++i) { TextType *text = texts_by_pos[i].text; int type = texts_by_pos[i].type; Coord p, q, dp, dx, dy; /* find reference point for this text */ q = s + slack * i / divisor; /* find delta from reference point to reference point */ p = coord(text, dir, point); dp = q - p; /* ...but if we're gridful, keep the mark on the grid */ /* TODO re-enable grid if (! gridless) { dp -= (coord(text, dir, K_Marks) + dp) % (long) (PCB->Grid); } */ if (dp) { /* move from generic to X or Y */ dx = dy = dp; if (dir == K_X) dy = 0; else dx = 0; /* need to know if the text is part of an element, * all are TEXT_TYPE, but text associated with an * element is also ELEMENTNAME_TYPE. For undo, this is * significant in search.c: SearchObjectByID. * * MoveObject() is better as in aligntext(), but we * didn't keep the element reference when sorting. * */ MOVE_TEXT_LOWLEVEL(text, dx, dy); AddObjectToMoveUndoList(type, NULL, NULL, text, dx, dy); changed = 1; } /* in gaps mode, accumulate part widths */ if (point == K_Gaps) { /* move remaining half of our text */ s += texts_by_pos[i].width / 2; /* move half of next text */ if (i < ntexts_by_pos - 1) s += texts_by_pos[i + 1].width / 2; } } if (changed) { RestoreUndoSerialNumber(); IncrementUndoSerialNumber(); Redraw(); SetChangedFlag(true); } free_texts_by_pos(); return 0; }
/* * Distribute(X, [Lefts/Rights/Centers/Marks/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) * Distribute(Y, [Tops/Bottoms/Centers/Marks/Gaps, [First/Last/Crosshair, First/Last/Crosshair[, Gridless]]]) * * As with align, plus: * * Gaps - Make gaps even rather than spreading points evenly * First, Last, * Crosshair - Two arguments specifying both ends of the * distribution, they can't both be the same. * * Defaults are Marks, First, Last * * Distributed elements always retain the same relative order they had * before they were distributed. */ static int distribute(int argc, char **argv, Coord x, Coord y) { int dir; int point; int refa, refb; int gridless; Coord s, e, slack; int divisor; int changed = 0; int i; if (argc < 1 || argc == 3 || argc > 4) { AFAIL(distribute); } /* parse direction arg */ switch ((dir = keyword(ARG(0)))) { case K_X: case K_Y: break; default: AFAIL(distribute); } /* parse point (within each element) which will be distributed */ switch ((point = keyword(ARG(1)))) { case K_Centers: case K_Marks: case K_Gaps: break; case K_Lefts: case K_Rights: if (dir == K_Y) { AFAIL(distribute); } break; case K_Tops: case K_Bottoms: if (dir == K_X) { AFAIL(distribute); } break; case K_none: point = K_Marks; /* default value */ break; default: AFAIL(distribute); } /* parse reference which will determine first distribution coordinate */ switch ((refa = keyword(ARG(2)))) { case K_First: case K_Last: case K_Average: case K_Crosshair: break; case K_none: refa = K_First; /* default value */ break; default: AFAIL(distribute); } /* parse reference which will determine final distribution coordinate */ switch ((refb = keyword(ARG(3)))) { case K_First: case K_Last: case K_Average: case K_Crosshair: break; case K_none: refb = K_Last; /* default value */ break; default: AFAIL(distribute); } if (refa == refb) { AFAIL(distribute); } /* optionally work off the grid (solar cells!) */ switch (keyword(ARG(4))) { case K_Gridless: gridless = 1; break; case K_none: gridless = 0; break; default: AFAIL(distribute); } /* build list of elements in orthogonal axis order */ sort_elements_by_pos(K_distribute, dir, point); /* find the endpoints given the above options */ s = reference_coord(K_distribute, x, y, dir, point, refa); e = reference_coord(K_distribute, x, y, dir, point, refb); slack = e - s; /* use this divisor to calculate spacing (for 1 elt, avoid 1/0) */ divisor = (nelements_by_pos > 1) ? (nelements_by_pos - 1) : 1; /* even the gaps instead of the edges or whatnot */ /* find the "slack" in the row */ if (point == K_Gaps) { Coord w; /* subtract all the "widths" from the slack */ for (i = 0; i < nelements_by_pos; ++i) { ElementType *element = elements_by_pos[i].element; /* coord doesn't care if I mix Lefts/Tops */ w = elements_by_pos[i].width = coord(element, dir, K_Rights) - coord(element, dir, K_Lefts); /* Gaps distribution is on centers, so half of * first and last element don't count */ if (i == 0 || i == nelements_by_pos - 1) { w /= 2; } slack -= w; } /* slack could be negative */ } /* move all selected elements to the new coordinate */ for (i = 0; i < nelements_by_pos; ++i) { ElementType *element = elements_by_pos[i].element; Coord p, q, dp, dx, dy; /* find reference point for this element */ q = s + slack * i / divisor; /* find delta from reference point to reference point */ p = coord(element, dir, point); dp = q - p; /* ...but if we're gridful, keep the mark on the grid */ if (! gridless) { dp -= (coord(element, dir, K_Marks) + dp) % (long) (PCB->Grid); } if (dp) { /* move from generic to X or Y */ dx = dy = dp; if (dir == K_X) dy = 0; else dx = 0; MoveElementLowLevel(PCB->Data, element, dx, dy); AddObjectToMoveUndoList(ELEMENT_TYPE, NULL, NULL, element, dx, dy); changed = 1; } /* in gaps mode, accumulate part widths */ if (point == K_Gaps) { /* move remaining half of our element */ s += elements_by_pos[i].width / 2; /* move half of next element */ if (i < nelements_by_pos - 1) s += elements_by_pos[i + 1].width / 2; } } if (changed) { IncrementUndoSerialNumber(); Redraw(); SetChangedFlag(1); } free_elements_by_pos(); return 0; }
/* * Align(X, [Lefts/Rights/Centers/Marks, [First/Last/Crosshair/Average[, Gridless]]]) * Align(Y, [Tops/Bottoms/Centers/Marks, [First/Last/Crosshair/Average[, Gridless]]]) * * X or Y - Select which axis will move, other is untouched * Lefts, Rights, * Tops, Bottoms, * Centers, Marks - Pick alignment point within each element * First, Last, * Crosshair, * Average - Alignment reference, First=Topmost/Leftmost, * Last=Bottommost/Rightmost, Average or Crosshair point * Gridless - Do not force results to align to prevailing grid * * Defaults are Marks, First */ static int align(int argc, char **argv, Coord x, Coord y) { int dir; int point; int reference; int gridless; Coord q; int changed = 0; if (argc < 1 || argc > 4) { AFAIL(align); } /* parse direction arg */ switch ((dir = keyword(ARG(0)))) { case K_X: case K_Y: break; default: AFAIL(align); } /* parse point (within each element) which will be aligned */ switch ((point = keyword(ARG(1)))) { case K_Centers: case K_Marks: break; case K_Lefts: case K_Rights: if (dir == K_Y) { AFAIL(align); } break; case K_Tops: case K_Bottoms: if (dir == K_X) { AFAIL(align); } break; case K_none: point = K_Marks; /* default value */ break; default: AFAIL(align); } /* parse reference which will determine alignment coordinates */ switch ((reference = keyword(ARG(2)))) { case K_First: case K_Last: case K_Average: case K_Crosshair: break; case K_none: reference = K_First; /* default value */ break; default: AFAIL(align); } /* optionally work off the grid (solar cells!) */ switch (keyword(ARG(3))) { case K_Gridless: gridless = 1; break; case K_none: gridless = 0; break; default: AFAIL(align); } /* find the final alignment coordinate using the above options */ q = reference_coord(K_align, Crosshair.X, Crosshair.Y, dir, point, reference); /* move all selected elements to the new coordinate */ ELEMENT_LOOP(PCB->Data); { Coord p, dp, dx, dy; if (! TEST_FLAG (SELECTEDFLAG, element)) continue; /* find delta from reference point to reference point */ p = coord(element, dir, point); dp = q - p; /* ...but if we're gridful, keep the mark on the grid */ if (! gridless) { dp -= (coord(element, dir, K_Marks) + dp) % (long) (PCB->Grid); } if (dp) { /* move from generic to X or Y */ dx = dy = dp; if (dir == K_X) dy = 0; else dx = 0; MoveElementLowLevel(PCB->Data, element, dx, dy); AddObjectToMoveUndoList(ELEMENT_TYPE, NULL, NULL, element, dx, dy); changed = 1; } } END_LOOP; if (changed) { IncrementUndoSerialNumber(); Redraw(); SetChangedFlag(1); } free_elements_by_pos(); return 0; }