예제 #1
0
파일: main.c 프로젝트: bert/pcb-rnd
/* ---------------------------------------------------------------------- 
 * post-process settings.
 */
     static void settings_post_process ()
{
  char *tmps;

  if (Settings.LineThickness > MAX_LINESIZE
      || Settings.LineThickness < MIN_LINESIZE)
    Settings.LineThickness = MIL_TO_COORD(10);

  if (Settings.ViaThickness > MAX_PINORVIASIZE
      || Settings.ViaThickness < MIN_PINORVIASIZE)
    Settings.ViaThickness = MIL_TO_COORD(40);

  if (Settings.ViaDrillingHole <= 0)
    Settings.ViaDrillingHole =
      DEFAULT_DRILLINGHOLE * Settings.ViaThickness / 100;

  Settings.MaxWidth  = CLAMP (Settings.MaxWidth, MIN_SIZE, MAX_COORD);
  Settings.MaxHeight = CLAMP (Settings.MaxHeight, MIN_SIZE, MAX_COORD);

  if (Settings.Routes != NULL)
    ParseRouteString (Settings.Routes, &Settings.RouteStyle[0], "cmil");

  /*
   * Make sure we have settings for some various programs we may wish
   * to call
   */
  if (Settings.MakeProgram == NULL) {
    tmps = getenv ("PCB_MAKE_PROGRAM");
    if (tmps != NULL)
      Settings.MakeProgram = strdup (tmps);
  }
  if (Settings.MakeProgram == NULL) {
    Settings.MakeProgram = strdup ("make");
  }

  if (Settings.GnetlistProgram == NULL) {
    tmps = getenv ("PCB_GNETLIST");
    if (tmps != NULL)
      Settings.GnetlistProgram = strdup (tmps);
  }
  if (Settings.GnetlistProgram == NULL) {
    Settings.GnetlistProgram = strdup ("gnetlist");
  }

  if (grid_units)
    Settings.grid_unit = get_unit_struct (grid_units);
  if (!grid_units || Settings.grid_unit == NULL)
    Settings.grid_unit = get_unit_struct ("mil");

  Settings.increments = get_increments_struct (Settings.grid_unit->suffix);
}
예제 #2
0
파일: autoplace.c 프로젝트: ee-lco/geda-pcb
/* ---------------------------------------------------------------------------
 * Auto-place selected components.
 */
bool
AutoPlaceSelected (void)
{
  NetListType *Nets;
  PointerListType Selected = { 0, 0, NULL };
  PerturbationType pt;
  double C0, T0;
  bool changed = false;

  /* (initial netlist processing copied from AddAllRats) */
  /* the netlist library has the text form
   * ProcNetlist fills in the Netlist
   * structure the way the final routing
   * is supposed to look
   */
  Nets = ProcNetlist (&PCB->NetlistLib);
  if (!Nets)
    {
      Message (_("Can't add rat lines because no netlist is loaded.\n"));
      goto done;
    }

  Selected = collectSelectedElements ();
  if (Selected.PtrN == 0)
    {
      Message (_("No elements selected to autoplace.\n"));
      goto done;
    }

  /* simulated annealing */
  {				/* compute T0 by doing a random series of moves. */
    const int TRIALS = 10;
    const double Tx = MIL_TO_COORD (300), P = 0.95;
    double Cs = 0.0;
    int i;
    C0 = ComputeCost (Nets, Tx, Tx);
    for (i = 0; i < TRIALS; i++)
      {
	pt = createPerturbation (&Selected, INCH_TO_COORD (1));
	doPerturb (&pt, false);
	Cs += fabs (ComputeCost (Nets, Tx, Tx) - C0);
	doPerturb (&pt, true);
      }
    T0 = -(Cs / TRIALS) / log (P);
    printf ("Initial T: %f\n", T0);
  }
  /* now anneal in earnest */
  {
    double T = T0;
    long steps = 0;
    int good_moves = 0, moves = 0;
    const int good_move_cutoff = CostParameter.m * Selected.PtrN;
    const int move_cutoff = 2 * good_move_cutoff;
    printf ("Starting cost is %.0f\n", ComputeCost (Nets, T0, 5));
    C0 = ComputeCost (Nets, T0, T);
    while (1)
      {
	double Cprime;
	pt = createPerturbation (&Selected, T);
	doPerturb (&pt, false);
	Cprime = ComputeCost (Nets, T0, T);
	if (Cprime < C0)
	  {			/* good move! */
	    C0 = Cprime;
	    good_moves++;
	    steps++;
	  }
	else if ((random () / (double) RAND_MAX) <
		 exp (MIN (MAX (-20, (C0 - Cprime) / T), 20)))
	  {
	    /* not good but keep it anyway */
	    C0 = Cprime;
	    steps++;
	  }
	else
	  doPerturb (&pt, true);	/* undo last change */
	moves++;
	/* are we at the end of a stage? */
	if (good_moves >= good_move_cutoff || moves >= move_cutoff)
	  {
	    printf ("END OF STAGE: COST %.0f\t"
		    "GOOD_MOVES %d\tMOVES %d\t"
		    "T: %.1f\n", C0, good_moves, moves, T);
	    /* is this the end? */
	    if (T < 5 || good_moves < moves / CostParameter.good_ratio)
	      break;
	    /* nope, adjust T and continue */
	    moves = good_moves = 0;
	    T *= CostParameter.gamma;
	    /* cost is T dependent, so recompute */
	    C0 = ComputeCost (Nets, T0, T);
	  }
      }
    changed = (steps > 0);
  }
done:
  if (changed)
    {
      DeleteRats (false);
      AddAllRats (false, NULL);
      Redraw ();
    }
  FreePointerListMemory (&Selected);
  return (changed);
}
예제 #3
0
파일: autoplace.c 프로젝트: ee-lco/geda-pcb
/* ---------------------------------------------------------------------------
 * Perturb:
 *  1) flip SMD from solder side to component side or vice-versa.
 *  2) rotate component 90, 180, or 270 degrees.
 *  3) shift component random + or - amount in random direction.
 *     (magnitude of shift decreases over time)
 *  -- Only perturb selected elements (need count/list of selected?) --
 */
PerturbationType
createPerturbation (PointerListType *selected, double T)
{
  PerturbationType pt = { 0 };
  /* pick element to perturb */
  pt.element = (ElementType *) selected->Ptr[random () % selected->PtrN];
  /* exchange, flip/rotate or shift? */
  switch (random () % ((selected->PtrN > 1) ? 3 : 2))
    {
    case 0:
      {				/* shift! */
	Coord grid;
	double scaleX = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxWidth / 3);
	double scaleY = CLAMP (sqrt (T), MIL_TO_COORD (2.5), PCB->MaxHeight / 3);
	pt.which = SHIFT;
	pt.DX = scaleX * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
	pt.DY = scaleY * 2 * ((((double) random ()) / RAND_MAX) - 0.5);
	/* snap to grid. different grids for "high" and "low" T */
	grid = (T > MIL_TO_COORD (10)) ? CostParameter.large_grid_size :
	  CostParameter.small_grid_size;
	/* (round away from zero) */
	pt.DX = ((pt.DX / grid) + SGN (pt.DX)) * grid;
	pt.DY = ((pt.DY / grid) + SGN (pt.DY)) * grid;
	/* limit DX/DY so we don't fall off board */
	pt.DX = MAX (pt.DX, -pt.element->VBox.X1);
	pt.DX = MIN (pt.DX, PCB->MaxWidth - pt.element->VBox.X2);
	pt.DY = MAX (pt.DY, -pt.element->VBox.Y1);
	pt.DY = MIN (pt.DY, PCB->MaxHeight - pt.element->VBox.Y2);
	/* all done but the movin' */
	break;
      }
    case 1:
      {				/* flip/rotate! */
	/* only flip if it's an SMD component */
	bool isSMD = pt.element->PadN != 0;
	pt.which = ROTATE;
	pt.rotate = isSMD ? (random () & 3) : (1 + (random () % 3));
	/* 0 - flip; 1-3, rotate. */
	break;
      }
    case 2:
      {				/* exchange! */
	pt.which = EXCHANGE;
	pt.other = (ElementType *)
	  selected->Ptr[random () % (selected->PtrN - 1)];
	if (pt.other == pt.element)
	  pt.other = (ElementType *) selected->Ptr[selected->PtrN - 1];
	/* don't allow exchanging a solderside-side SMD component
	 * with a non-SMD component. */
	if ((pt.element->PinN != 0 /* non-SMD */  &&
	     TEST_FLAG (ONSOLDERFLAG, pt.other)) ||
	    (pt.other->PinN != 0 /* non-SMD */  &&
	     TEST_FLAG (ONSOLDERFLAG, pt.element)))
	  return createPerturbation (selected, T);
	break;
      }
    default:
      assert (0);
    }
  return pt;
}
예제 #4
0
파일: autoplace.c 프로젝트: ee-lco/geda-pcb
CostParameter =
{
  3e3,				/* via cost */
    2e-2,			/* congestion penalty */
    1e-2,			/* initial overlap penalty */
    1e2,			/* final overlap penalty */
    1e3,			/* out of bounds penalty */
    1e0,			/* penalty for total area used */
    1e0,			/* subtract 1000 from cost for every same-type neighbor */
    1e0,			/* subtract 1000 from cost for every aligned neighbor */
    1e0,			/* subtract 1000 from cost for every same-rotation neighbor */
    20,				/* move on when each module has been profitably moved 20 times */
    0.75,			/* annealing schedule constant: 0.85 */
    40,				/* halt when there are 60 times as many moves as good moves */
    false,			/* don't ignore SMD/pin conflicts */
    MIL_TO_COORD (100),		/* coarse grid is 100 mils */
    MIL_TO_COORD (10),		/* fine grid is 10 mils */
};

typedef struct
{
  ElementType **element;
  Cardinal elementN;
}
ElementPtrListType;

enum ewhich
  { SHIFT, ROTATE, EXCHANGE };

typedef struct
{
예제 #5
0
파일: main.c 프로젝트: bert/pcb-rnd
    Nanometer
   @item in
    Inch (1in = 0.0254m)
   @item mil
    Mil (1000mil = 1in)
   @item cmil
    Centimil (1/100 mil)
@end table

@ftable @code
@item --via-thickness <num>
Default diameter of vias. Default value is @code{60mil}.
@end ftable
%end-doc
*/
  CSET (ViaThickness, MIL_TO_COORD(60), "via-thickness",
  "default diameter of vias in 1/100 mil"),

/* %start-doc options "5 Sizes"
@ftable @code
@item --via-drilling-hole <num>
Default diameter of holes. Default value is @code{28mil}.
@end ftable
%end-doc
*/
  CSET (ViaDrillingHole, MIL_TO_COORD(28), "via-drilling-hole",
  "default diameter of holes"),

/* %start-doc options "5 Sizes"
@ftable @code
@item --line-thickness <num>
예제 #6
0
int
ActionLoadVendorFrom (int argc, char **argv, Coord x, Coord y)
{
  int i;
  char *fname = NULL;
  static char *default_file = NULL;
  char *sval;
  Resource *res, *drcres, *drlres;
  int type;
  bool free_fname = false;

  cached_drill = -1;

  fname = argc ? argv[0] : 0;

  if (!fname || !*fname)
    {
      fname = gui->fileselect (_("Load Vendor Resource File..."),
			       _("Picks a vendor resource file to load.\n"
				 "This file can contain drc settings for a\n"
				 "particular vendor as well as a list of\n"
				 "predefined drills which are allowed."),
			       default_file, ".res", "vendor",
			       HID_FILESELECT_READ);
      if (fname == NULL)
	AFAIL (load_vendor);

      free_fname = true;

      free (default_file);
      default_file = NULL;

      if (fname && *fname)
	default_file = strdup (fname);
    }

  /* Unload any vendor table we may have had */
  n_vendor_drills = 0;
  n_refdes = 0;
  n_value = 0;
  n_descr = 0;
  FREE (vendor_drills);
  FREE (ignore_refdes);
  FREE (ignore_value);
  FREE (ignore_descr);


  /* load the resource file */
  res = resource_parse (fname, NULL);
  if (res == NULL)
    {
      Message (_("Could not load vendor resource file \"%s\"\n"), fname);
      return 1;
    }

  /* figure out the vendor name, if specified */
  vendor_name = (char *)UNKNOWN (resource_value (res, "vendor"));

  /* figure out the units, if specified */
  sval = resource_value (res, "units");
  if (sval == NULL)
    {
      sf = MIL_TO_COORD(1);
    }
  else if ((NSTRCMP (sval, "mil") == 0) || (NSTRCMP (sval, "mils") == 0))
    {
      sf = MIL_TO_COORD(1);
    }
  else if ((NSTRCMP (sval, "inch") == 0) || (NSTRCMP (sval, "inches") == 0))
    {
      sf = INCH_TO_COORD(1);
    }
  else if (NSTRCMP (sval, "mm") == 0)
    {
      sf = MM_TO_COORD(1);
    }
  else
    {
      Message ("\"%s\" is not a supported units.  Defaulting to inch\n",
	       sval);
      sf = INCH_TO_COORD(1);
    }


  /* default to ROUND_UP */
  rounding_method = ROUND_UP;

  /* extract the drillmap resource */
  drlres = resource_subres (res, "drillmap");
  if (drlres == NULL)
    {
      Message (_("No drillmap resource found\n"));
    }
  else
    {
      sval = resource_value (drlres, "round");
      if (sval != NULL)
	{
	  if (NSTRCMP (sval, "up") == 0)
	    {
	      rounding_method = ROUND_UP;
	    }
	  else if (NSTRCMP (sval, "nearest") == 0)
	    {
	      rounding_method = CLOSEST;
	    }
	  else
	    {
	      Message (_
		       ("\"%s\" is not a valid rounding type.  Defaulting to up\n"),
		       sval);
	      rounding_method = ROUND_UP;
	    }
	}

      process_skips (resource_subres (drlres, "skips"));

      for (i = 0; i < drlres->c; i++)
	{
	  type = resource_type (drlres->v[i]);
	  switch (type)
	    {
	    case 10:
	      /* just a number */
	      add_to_drills (drlres->v[i].value);
	      break;

	    default:
	      break;
	    }
	}
    }

  /* Extract the DRC resource */
  drcres = resource_subres (res, "drc");

  sval = resource_value (drcres, "copper_space");
  if (sval != NULL)
    {
      PCB->Bloat = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum copper spacing to %.2f mils\n"),
	       0.01 * PCB->Bloat);
    }

  sval = resource_value (drcres, "copper_overlap");
  if (sval != NULL)
    {
      PCB->Shrink = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum copper overlap to %.2f mils\n"),
	       0.01 * PCB->Shrink);
    }

  sval = resource_value (drcres, "copper_width");
  if (sval != NULL)
    {
      PCB->minWid = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum copper spacing to %.2f mils\n"),
	       0.01 * PCB->minWid);
    }

  sval = resource_value (drcres, "silk_width");
  if (sval != NULL)
    {
      PCB->minSlk = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum silk width to %.2f mils\n"),
	       0.01 * PCB->minSlk);
    }

  sval = resource_value (drcres, "min_drill");
  if (sval != NULL)
    {
      PCB->minDrill = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum drill diameter to %.2f mils\n"),
	       0.01 * PCB->minDrill);
    }

  sval = resource_value (drcres, "min_ring");
  if (sval != NULL)
    {
      PCB->minRing = floor (sf * atof (sval) + 0.5);
      Message (_("Set DRC minimum annular ring to %.2f mils\n"),
	       0.01 * PCB->minRing);
    }

  Message (_("Loaded %d vendor drills from %s\n"), n_vendor_drills, fname);
  Message (_("Loaded %d RefDes skips, %d Value skips, %d Descr skips\n"),
	   n_refdes, n_value, n_descr);

  vendorMapEnable = true;
  apply_vendor_map ();
  if (free_fname)
    free (fname);
  return 0;
}
예제 #7
0
파일: crosshair.c 프로젝트: jaredcasper/pcb
/* ---------------------------------------------------------------------------
 * recalculates the passed coordinates to fit the current grid setting
 */
void
FitCrosshairIntoGrid (Coord X, Coord Y)
{
    Coord nearest_grid_x, nearest_grid_y;
    void *ptr1, *ptr2, *ptr3;
    struct snap_data snap_data;
    int ans;

    Crosshair.X = CLAMP (X, Crosshair.MinX, Crosshair.MaxX);
    Crosshair.Y = CLAMP (Y, Crosshair.MinY, Crosshair.MaxY);

    if (PCB->RatDraw)
    {
        nearest_grid_x = -MIL_TO_COORD (6);
        nearest_grid_y = -MIL_TO_COORD (6);
    }
    else
    {
        nearest_grid_x = GridFit (Crosshair.X, PCB->Grid, PCB->GridOffsetX);
        nearest_grid_y = GridFit (Crosshair.Y, PCB->Grid, PCB->GridOffsetY);

        if (Marked.status && TEST_FLAG (ORTHOMOVEFLAG, PCB))
        {
            Coord dx = Crosshair.X - Marked.X;
            Coord dy = Crosshair.Y - Marked.Y;
            if (ABS (dx) > ABS (dy))
                nearest_grid_y = Marked.Y;
            else
                nearest_grid_x = Marked.X;
        }

    }

    snap_data.crosshair = &Crosshair;
    snap_data.nearest_sq_dist =
        crosshair_sq_dist (&Crosshair, nearest_grid_x, nearest_grid_y);
    snap_data.nearest_is_grid = true;
    snap_data.x = nearest_grid_x;
    snap_data.y = nearest_grid_y;

    ans = NO_TYPE;
    if (!PCB->RatDraw)
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    ELEMENT_TYPE, &ptr1, &ptr2, &ptr3);

    if (ans & ELEMENT_TYPE)
    {
        ElementType *el = (ElementType *) ptr1;
        check_snap_object (&snap_data, el->MarkX, el->MarkY, false);
    }

    ans = NO_TYPE;
    if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    PAD_TYPE, &ptr1, &ptr2, &ptr3);

    /* Avoid self-snapping when moving */
    if (ans != NO_TYPE &&
            Settings.Mode == MOVE_MODE &&
            Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
            ptr1 == Crosshair.AttachedObject.Ptr1)
        ans = NO_TYPE;

    if (ans != NO_TYPE &&
            ( Settings.Mode == LINE_MODE ||
              (Settings.Mode == MOVE_MODE &&
               Crosshair.AttachedObject.Type == LINEPOINT_TYPE)))
    {
        PadTypePtr pad = (PadTypePtr) ptr2;
        LayerType *desired_layer;
        Cardinal desired_group;
        Cardinal SLayer, CLayer;
        int found_our_layer = false;

        desired_layer = CURRENT;
        if (Settings.Mode == MOVE_MODE &&
                Crosshair.AttachedObject.Type == LINEPOINT_TYPE)
        {
            desired_layer = (LayerType *)Crosshair.AttachedObject.Ptr1;
        }

        /* find layer groups of the component side and solder side */
        SLayer = GetLayerGroupNumberByNumber (solder_silk_layer);
        CLayer = GetLayerGroupNumberByNumber (component_silk_layer);
        desired_group = TEST_FLAG (ONSOLDERFLAG, pad) ? SLayer : CLayer;

        GROUP_LOOP (PCB->Data, desired_group);
        {
            if (layer == desired_layer)
            {
                found_our_layer = true;
                break;
            }
        }
        END_LOOP;

        if (found_our_layer == false)
            ans = NO_TYPE;
    }

    if (ans != NO_TYPE)
    {
        PadType *pad = (PadType *)ptr2;
        check_snap_object (&snap_data, (pad->Point1.X + pad->Point2.X) / 2,
                           (pad->Point1.Y + pad->Point2.Y) / 2,
                           true);
    }

    ans = NO_TYPE;
    if (PCB->RatDraw || TEST_FLAG (SNAPPINFLAG, PCB))
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    PIN_TYPE, &ptr1, &ptr2, &ptr3);

    /* Avoid self-snapping when moving */
    if (ans != NO_TYPE &&
            Settings.Mode == MOVE_MODE &&
            Crosshair.AttachedObject.Type == ELEMENT_TYPE &&
            ptr1 == Crosshair.AttachedObject.Ptr1)
        ans = NO_TYPE;

    if (ans != NO_TYPE)
    {
        PinType *pin = (PinType *)ptr2;
        check_snap_object (&snap_data, pin->X, pin->Y, true);
    }

    ans = NO_TYPE;
    if (TEST_FLAG (SNAPPINFLAG, PCB))
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    VIA_TYPE, &ptr1, &ptr2, &ptr3);

    /* Avoid snapping vias to any other vias */
    if (Settings.Mode == MOVE_MODE &&
            Crosshair.AttachedObject.Type == VIA_TYPE &&
            (ans & PIN_TYPES))
        ans = NO_TYPE;

    if (ans != NO_TYPE)
    {
        PinType *pin = (PinType *)ptr2;
        check_snap_object (&snap_data, pin->X, pin->Y, true);
    }

    ans = NO_TYPE;
    if (TEST_FLAG (SNAPPINFLAG, PCB))
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    LINEPOINT_TYPE, &ptr1, &ptr2, &ptr3);

    if (ans != NO_TYPE)
    {
        PointType *pnt = (PointType *)ptr3;
        check_snap_object (&snap_data, pnt->X, pnt->Y, true);
    }

    check_snap_offgrid_line (&snap_data, nearest_grid_x, nearest_grid_y);

    ans = NO_TYPE;
    if (TEST_FLAG (SNAPPINFLAG, PCB))
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    POLYGONPOINT_TYPE, &ptr1, &ptr2, &ptr3);

    if (ans != NO_TYPE)
    {
        PointType *pnt = (PointType *)ptr3;
        check_snap_object (&snap_data, pnt->X, pnt->Y, true);
    }

    if (snap_data.x >= 0 && snap_data.y >= 0)
    {
        Crosshair.X = snap_data.x;
        Crosshair.Y = snap_data.y;
    }

    if (Settings.Mode == ARROW_MODE)
    {
        ans = SearchScreenGridSlop (Crosshair.X, Crosshair.Y,
                                    LINEPOINT_TYPE, &ptr1, &ptr2, &ptr3);
        if (ans == NO_TYPE)
            hid_action("PointCursor");
        else if (!TEST_FLAG(SELECTEDFLAG, (LineType *)ptr2))
            hid_actionl("PointCursor","True", NULL);
    }

    if (Settings.Mode == LINE_MODE
            && Crosshair.AttachedLine.State != STATE_FIRST
            && TEST_FLAG (AUTODRCFLAG, PCB))
        EnforceLineDRC ();

    gui->set_crosshair (Crosshair.X, Crosshair.Y, HID_SC_DO_NOTHING);
}
예제 #8
0
파일: fontmode.c 프로젝트: majenkotech/pcb
static int
FontEdit (int argc, char **argv, Coord Ux, Coord Uy) {
    FontType *font;
    SymbolType *symbol;
    LayerType *lfont, *lorig, *lwidth, *lgrid;
    int s, l;

    if (hid_actionl ("New", "Font", 0)) {
        return 1;
    }

    Settings.grid_unit = get_unit_struct("mil");
    Settings.Bloat = PCB->Bloat = 1;
    Settings.Shrink = PCB->Shrink = 1;
    Settings.minWid = PCB->minWid = 1;
    Settings.minSlk = PCB->minSlk = 1;
    MoveLayerToGroup (max_copper_layer + TOP_SILK_LAYER, 0);
    MoveLayerToGroup (max_copper_layer + BOTTOM_SILK_LAYER, 1);

    while (PCB->Data->LayerN > 4) {
        MoveLayer (4, -1);
    }

    for (l = 0; l < 4; l++) {
        MoveLayerToGroup (l, l);
    }

    PCB->MaxWidth = CELL_SIZE * 18;
    PCB->MaxHeight = CELL_SIZE * ((MAX_FONTPOSITION + 15) / 16 + 2);
    PCB->Grid = MIL_TO_COORD (5);
    PCB->Data->Layer[0].Name = strdup ("Font");
    PCB->Data->Layer[1].Name = strdup ("OrigFont");
    PCB->Data->Layer[2].Name = strdup ("Width");
    PCB->Data->Layer[3].Name = strdup ("Grid");
    hid_action ("PCBChanged");
    hid_action ("LayersChanged");
    lfont = PCB->Data->Layer + 0;
    lorig = PCB->Data->Layer + 1;
    lwidth = PCB->Data->Layer + 2;
    lgrid = PCB->Data->Layer + 3;
    font = &PCB->Font;

    for (s = 0; s <= MAX_FONTPOSITION; s++) {
        Coord ox = (s % 16 + 1) * CELL_SIZE;
        Coord oy = (s / 16 + 1) * CELL_SIZE;
        Coord w, miny, maxy, maxx = 0;
        symbol = &font->Symbol[s];
        miny = MIL_TO_COORD (5);
        maxy = font->MaxHeight;

        for (l = 0; l < symbol->LineN; l++) {
            CreateDrawnLineOnLayer (lfont,
                                    symbol->Line[l].Point1.X + ox,
                                    symbol->Line[l].Point1.Y + oy,
                                    symbol->Line[l].Point2.X + ox,
                                    symbol->Line[l].Point2.Y + oy,
                                    symbol->Line[l].Thickness,
                                    symbol->Line[l].Thickness, NoFlags ());
            CreateDrawnLineOnLayer (lorig, symbol->Line[l].Point1.X + ox,
                                    symbol->Line[l].Point1.Y + oy,
                                    symbol->Line[l].Point2.X + ox,
                                    symbol->Line[l].Point2.Y + oy,
                                    symbol->Line[l].Thickness,
                                    symbol->Line[l].Thickness, NoFlags ());

            if (maxx < symbol->Line[l].Point1.X) {
                maxx = symbol->Line[l].Point1.X;
            }

            if (maxx < symbol->Line[l].Point2.X) {
                maxx = symbol->Line[l].Point2.X;
            }
        }

        w = maxx + symbol->Delta + ox;
        CreateDrawnLineOnLayer (lwidth,
                                w, miny + oy,
                                w, maxy + oy, MIL_TO_COORD (1), MIL_TO_COORD (1), NoFlags ());
    }

    for (l = 0; l < 16; l++) {
        int x = (l + 1) * CELL_SIZE;
        CreateDrawnLineOnLayer (lgrid, x, 0, x, PCB->MaxHeight, MIL_TO_COORD (1),
                                MIL_TO_COORD (1), NoFlags ());
    }

    for (l = 0; l <= MAX_FONTPOSITION / 16 + 1; l++) {
        int y = (l + 1) * CELL_SIZE;
        CreateDrawnLineOnLayer (lgrid, 0, y, PCB->MaxWidth, y, MIL_TO_COORD (1),
                                MIL_TO_COORD (1), NoFlags ());
    }

    return 0;
}