Exemple #1
0
/* ---------------------------------------------------------------------------
 * 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);
}
Exemple #2
0
/* ---------------------------------------------------------------------------
 * 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);
}
Exemple #3
0
/* ---------------------------------------------------------------------------
 * 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);
}
Exemple #4
0
/*!
 * \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;
  }
}
Exemple #5
0
/*!
 * \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;
}
Exemple #6
0
/*
 * 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;
}
Exemple #7
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;
}
Exemple #8
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;
}