/* ProfiGleisFactory.java - jfig.utils.ProfiGleisFactory 
 *
 * utility method to create the "Fleischmann H0 Profigleis" model railroad
 * tracks, including straights, curvers, points, crossings, and turntables.
 * See www.fleischmann.de for details.
 *
 * 08.08.05 - polishing
 * 07.08.05 - avoid double labels and markers, better turntables
 * 04.08.05 - first version for fun
 */

package jfig.demo;


import java.awt.Color;
import java.util.Enumeration;

import jfig.canvas.FigTrafo2D;
import jfig.commands.FigBasicEditor;
import jfig.gui.*;
import jfig.objects.*;


public class ProfiGleisFactory {

  public final static double _2PI = 2 * Math.PI;
  public final static double MM = FigTrafo2D.DOTS_PER_MM;
  public final static double DEGREES = 2*Math.PI/360;

  public final static int START_MARKER_LAYER   = 200;
  public final static int END_MARKER_LAYER     = 201;
  public final static int TRACK_LABEL_LAYER    = 230;
  public final static int TRACK_LAYER          = 240;
  public final static int TRACK_OUTLINE_LAYER  = 250;


  FigBasicEditor editor = null; // the editor we're using


  /**
   * create a small marker to indicate the start point of a piece of track.
   */
  public FigObject startMarker( Point v, double degrees ) {
    FigObject tmp = marker( v, degrees, START_MARKER_LAYER, ColorCache.RED );
    tmp.setComment( "start" );
    return tmp;
  }


  /**
   * create a small marker to indicate the end point of a piece of track.
   */
  public FigObject endMarker( Point v, double degrees ) {
    FigObject tmp = marker( v, degrees+180, END_MARKER_LAYER, ColorCache.BLUE );
    tmp.setComment( "end" );
    return tmp;
  }


  public FigObject marker( Point v, double degrees, int layer, int color ) {
    double len   = 4.0 * MM;
    double angle = _2PI * degrees / 360.0;
    double  wx   = v.getX() + len * Math.cos( angle );
    double  wy   = v.getY() + len * Math.sin( angle );
    
    FigPolyline p = new FigPolyline();
    p.setPoints( new Point[] { v, new Point(wx,wy) } );
    p.setLayer( layer );
    p.setLineColor( ColorCache.getColorCache().get(color) );
    p.getAttributes().parse( "linewidth=6" );
    return p;
  }


  public FigText makeLabel( String s, double x, double y, double degrees ) {
    FigText t = new FigText();
    t.setPoints( new Point[] { new Point(x,y+7*MM) } );
    t.setText( s );
    t.getAttributes().currentLayer = TRACK_LABEL_LAYER;
    t.getAttributes().parse( "textalign=center fontsize=50 font=Helvetica " );
    t.getAttributes().parse( "rotation=" + (-degrees) );
    return t;
  }


  /**
   * create a single piece of track of the given length (straight) 
   * or the given radius and angle (curves).
   * Length and radius are specified in millimeters, angle in degrees.
   * We use a default background color here.
   */
  public FigPolyline makeTrack( double len, double radius, double angle ) {
    return makeTrack( len, radius, angle, "0xe0e0e4" );
  }


  /**
   * create a single piece of track of the given length (straight)
   * or the given radius and angle (curves), and the given background color
   * specified as a 0xrrggbb string.
   * Length and radius are specified in millimeters, angle in degrees.
   */
  public FigPolyline makeTrack( double len_in_mm, 
                                double radius_in_mm, 
                                double angle_in_degrees,
                                String rgb_background ) {
    Point[] pp = null;

    double width  = 32.0 * MM;  // outer width of the ballast road-bed 32 or 33
    double len    = len_in_mm * MM;
    double radius = radius_in_mm * MM;
    double angle  = angle_in_degrees;

    if (angle == 0.0) {
      pp    = new Point[5];     // origin and four corners
      pp[0] = new Point( 0.0, 0.0 );
      pp[1] = new Point( 0.0, -width/2 );
      pp[2] = new Point( len, -width/2 );
      pp[3] = new Point( len, +width/2 );
      pp[4] = new Point( 0.0, +width/2 );
    }
    else { // curve
      int     n = 50;
      double r1 = radius - width/2;
      double r2 = radius + width/2;

      pp    = new Point[1+2*n]; // origin, 50 intermediates upper/lower
      pp[0] = new Point( 0.0, 0.0 );

      for( int i=0; i < n; i++ ) {
        double alpha = (1.0*i/(n-1)) * (angle/360*_2PI) - Math.PI/2;
        double x1 = r1 * Math.cos( alpha );
        double y1 = r1 * Math.sin( alpha );

        pp[1+i] = new Point(x1,y1+radius);

        double x2 = r2 * Math.cos( alpha );
        double y2 = r2 * Math.sin( alpha );

        pp[2*n-i] = new Point(x2,y2+radius);
      }
    }

    FigPolyline p = new FigPolyline();
    p.setPoints( pp );
    p.setIsClosed( true );
    p.getAttributes().parse( "color=black linewidth=1" ); 
    p.getAttributes().parse( "fillstyle=pure fillcolor=" + rgb_background );
    p.setLayer( TRACK_OUTLINE_LAYER );
    return p;
  }


  /**
   * create a straight piece of track with the given length in millimeters
   * and a label using the given name. 
   * This method also automatically adds the start- and end-markers.
   * <p>
   * Example:  FigCompound profi_gleis_6101 = straight( 200.0, "6101" );
   */
  public FigCompound straight( double mm, String name ) {
    return straight( mm, name, "0xe0e0e4" );
  }


  /**
   * create a straight piece of track with the given length in millimeters,
   * a label using the given name, and the given background color.
   * This method also automatically adds the start- and end-markers.
   */
  public FigCompound straight( double mm, String name, String rgb_background ) {
    Point  v = new Point( 0.0*MM, 0.0*MM );
    Point  w = new Point(  mm*MM, 0.0*MM );
    FigCompound tmp = new FigCompound();
    tmp.fastAddMember( makeTrack( mm, 0.0, 0.0, rgb_background ));
    tmp.fastAddMember( startMarker( v, 0.0));
    tmp.fastAddMember( endMarker( w, 0.0 ));
    tmp.fastAddMember( makeLabel( name, w.getX()/2, w.getY(), 0.0 ));
    tmp.update_bbox();
    return tmp;
  }


  /**
   * create a piece of curved track given the radius in millimeters,
   * curve angle in degrees, a label using the given name,
   * and the default background color.
   * This method also automatically adds the start- and end-markers.
   */
  public FigCompound curve( double radius_in_mm, double degrees, String name ) {
    return curve( radius_in_mm, degrees, name, "0xffe086", true );
  }


  /**
   * create a piece of curved track given the radius in millimeters,
   * curve angle in degrees, a label using the given name,
   * and the specified background color.
   * This method also automatically adds the start- and end-markers.
   */
  public FigCompound curve( double radius_in_mm, double degrees,
                            String name, String rgb_background ) {
    return curve( radius_in_mm, degrees, name, rgb_background, true );
  }


  /**
   * create a piece of curved track given the radius in millimeters,
   * curve angle in degrees, a label using the given name,
   * and the specified background color.
   * The 'enable_markers' flag enables the start- and end-markers.
   */
  public FigCompound curve( double radius_in_mm, double degrees,
                            String name, String rgb_background,
                            boolean enable_markers ) 
  {
    Point  v = new Point( 0.0*MM, 0.0*MM );
    Point  w = getCurveEndPoint( radius_in_mm, degrees );
    Point  z = getCurveEndPoint( radius_in_mm, degrees/2 );

    FigCompound tmp = new FigCompound();
    tmp.fastAddMember( 
       makeTrack( 0.0, radius_in_mm, degrees, rgb_background ));

    if (enable_markers) {
      tmp.fastAddMember( startMarker( v, 0.0 ));
      tmp.fastAddMember( endMarker( w, degrees ));
    }
    if ((name != null) && (name.length() > 0)) {
      tmp.fastAddMember( makeLabel( name, z.getX(), z.getY(), degrees/2 ));
    }
    tmp.update_bbox();
    return tmp;
  }


  public Point getCurveEndPoint( double radius_in_mm, double degrees ) {
    double r     = radius_in_mm * MM;
    double theta = degrees * DEGREES;
    Point      w = new Point( r * Math.cos( theta-Math.PI/2 ),
                              r * (1 + Math.sin( theta-Math.PI/2 )));
    return w;
  }


  public FigCompound make6101() {  // Gerade 200 mm
    return straight( 200, "6101" ); 
  }

  public FigCompound make6102() { // halbe Gerade fuer Diagonale 105 mm
    return straight( 105, "6102" );
  }

  public FigCompound make6103() { // halbe Gerade 100 mm
    return straight( 100, "6103" );
  }

  public FigCompound make6106() { // Flexgleis 800 mm
    // FIXME: not flexible at all, yet
    return straight( 800, "6106", "0xe0e0e4" );
  }

  public FigCompound make6107() { // Distanzstueck 10 mm
    return straight(  10, "6107" );
  }


  public FigCompound make6110() { // Ausgleichsstueck 80..120 mm
    FigCompound   tmp = straight( 120, "6110", "0xc0c0c0" );
    tmp.fastAddMember( endMarker( new Point( 80*MM,0.0), 0.0 ));
    tmp.fastAddMember( endMarker( new Point( 90*MM,0.0), 0.0 ));
    tmp.fastAddMember( endMarker( new Point(100*MM,0.0), 0.0 ));
    tmp.fastAddMember( endMarker( new Point(110*MM,0.0), 0.0 ));

    FigPolyline flex = new FigPolyline();
    flex.setPoints( 
      new Point[] {
        new Point(  80*MM,   0*MM ),
        new Point(  80*MM, -16*MM ),
        new Point( 120*MM, -16*MM ),
        new Point( 120*MM, +16*MM ),
        new Point(  80*MM, +16*MM ),
        new Point(  80*MM,   0*MM ),
      });
    flex.getAttributes().parse( 
      "linewidth=1 fillstyle=pure color=black fillcolor=0xc0ffff layer=231" );
    tmp.fastAddMember( flex );
    tmp.update_bbox();
    return tmp;
  }


  public FigCompound make6111() { // Entkupplungsgleis, elektr.
    FigCompound tmp = straight( 100, "6111", "0xc0c0c0" );
    FigPolyline lever = new FigPolyline();
    lever.setPoints( 
      new Point[] {
        new Point(  50*MM, 16*MM ),
        new Point(  70*MM, 16*MM ),
        new Point(  60*MM, 26*MM ),
        new Point(  40*MM, 26*MM ),
        new Point(  30*MM, 16*MM ),
        new Point(  50*MM, 16*MM ),
      });
    lever.getAttributes().parse( 
      "linewidth=1 fillstyle=solid color=black layer=231" );
    tmp.fastAddMember( lever );
    tmp.update_bbox();
    return tmp;
  }


  public FigCompound make6114() { // Entkupplungsgleis, manuell
    FigCompound tmp = straight( 100, "6114", "0xc0c0c0" );
    FigPolyline lever = new FigPolyline();
    lever.setPoints( 
      new Point[] {
        new Point(  50*MM, 16*MM ),
        new Point(  60*MM, 16*MM ),
        new Point(  50*MM, 26*MM ),
        new Point(  40*MM, 16*MM ),
        new Point(  50*MM, 16*MM ),
      });
    lever.getAttributes().parse( 
      "linewidth=1 fillstyle=solid color=black layer=231" );
    tmp.fastAddMember( lever );
    tmp.update_bbox();
    return tmp;
  }




  public FigCompound make6116() { // Prellbock
    Point  v = new Point(   0*MM, 0*MM );
    Point  w = new Point( 100*MM, 0*MM );
    FigCompound tmp = new FigCompound();
    tmp.fastAddMember( makeTrack( 100, 0.0, 0.0, "0xc0c0c0" ));
    tmp.fastAddMember( makeLabel( "6116" , w.getX()/2, w.getY(), 0.0 ));
    tmp.fastAddMember( startMarker( v, 0.0));
    // no end marker for this track :-)

    FigPolyline tail = new FigPolyline();
    tail.setPoints( 
      new Point[] {
        new Point(  90*MM,   0*MM ),
        new Point(  90*MM, -27*MM ),
        new Point( 100*MM, -27*MM ),
        new Point( 100*MM, +27*MM ),
        new Point(  90*MM, +27*MM ),
        new Point(  90*MM,   0*MM ),
      });
    tail.getAttributes().parse( 
      "linewidth=1 fillstyle=solid color=black layer=231" );
    tmp.fastAddMember( tail );
    tmp.update_bbox();
    return tmp;
  }

    
  public FigCompound make6120() { // R1 36 degrees
    return curve( 356.5, 36.0,  "6120", "0xffff00" );
  }

  public FigCompound make6122() { // R1 18 degrees
    return curve( 356.5, 18.0,  "6122", "0xffff00" );
  }

  public FigCompound make6125() { // R2 36 degrees
    return curve( 420.0, 36.0,  "6125", "0xff5050" );
  }

  public FigCompound make6127() { // R2 18 degrees
    return curve( 420.0, 18.0,  "6127", "0xff5050" );
  }

  public FigCompound make6131() { // R3 18 degrees
    return curve( 483.5, 18.0,  "6131", "0xa0ff60" );
  }

  public FigCompound make6133() { // R4 18 degrees
    return curve( 547.0, 18.0,  "6133", "0x00df00" );
  }

  public FigCompound make6138() { // Weichenbogen 18 degrees
    return curve( 647.0, 18.0,  "6138", "0xffc000" );
  }

  public FigCompound make6139() { // Adapter fuer Drehscheibe 7.5 degrees
    return curve( 788.0, 7.5,  "6139" );
  }


  public FigCompound make6170() { // Weiche links
    FigCompound master = straight( 200, "6170", "0xffc000" );
    FigObject        c = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject        m = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );
    master.fastAddMember( c );
    master.fastAddMember( m );
    master.update_bbox();
    return master;
  }


  public FigCompound make6171() { // Weiche rechts
    FigCompound master = straight( 200, "6171", "0xffc000" );
    FigObject        c = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject        m = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );
    c.mirrorY( 0.0, 0.0 );
    m.mirrorY( 0.0, 0.0 );
    master.fastAddMember( c );
    master.fastAddMember( m );
    master.update_bbox();
    return master;
  }


  /**
   * create the right 'curved point' (Bogenweiche rechts).
   * <p>
   * While the Fleischmann documentation claims a perfect geometry
   * for two setups (single-point R1 inner and R1 outer tracks, and
   * the two-opposite-points R1-to-R2 crossing), this is impossible.
   * The actual device is constructed so that the R1+R1 setup is
   * slightly short, while the R1+R2 setup is slightly wide, with
   * about 4mm error in both cases.
   */
  public FigCompound make6174() { // Bogenweiche rechts
    FigCompound inner = curve( 356.5, 36.0, "6174",  "0xffcc00" );
    FigObject   outer = makeTrack( 0.0, 356.5, 36.0, "0xffcc00" );
    FigObject   extra = makeTrack( 63.50, 0.0, 0.0,  "0xffcc00" );
    FigObject   markr = endMarker( getCurveEndPoint(356.5,36.0), 36.0 );

    // theory
    // outer.move( 63.5*MM, 0.0 );
    // markr.move( 63.5*MM, 0.0 );

    // actual measurement 
    outer.move( 60*MM, 0*MM );
    markr.move( 60*MM, 0*MM );

    inner.fastAddMember( outer );
    inner.fastAddMember( extra );
    inner.fastAddMember( markr );
    inner.update_bbox();
    return inner;
  }


  /**
   * create the left 'curved point' (Bogenweiche links).
   * See notes for make6174 above.
   */
  public FigCompound make6175() { // Bogenweiche links
    FigCompound inner = curve( 356.5, 36.0, null,  "0xffcc00" );
    FigObject   outer = makeTrack( 0.0, 356.5, 36.0, "0xffcc00" );
    FigObject   extra = makeTrack( 63.50, 0.0, 0.0,  "0xffcc00" );
    FigObject   markr = endMarker( getCurveEndPoint(356.5,36.0), 36.0 );

    Point           p = getCurveEndPoint( 356.5, 18.0 );
    FigObject   label = makeLabel( "6175", p.getX(), -p.getY(), -18.0 );

    // theory
    // outer.move( 63.5*MM, 0.0 );
    // markr.move( 63.5*MM, 0.0 );

    // actual measurement
    outer.move( 60*MM, 0*MM );
    markr.move( 60*MM, 0*MM );

    inner.mirrorY( 0.0, 0.0 );
    outer.mirrorY( 0.0, 0.0 );
    markr.mirrorY( 0.0, 0.0 );

    inner.fastAddMember( outer );
    inner.fastAddMember( extra );
    inner.fastAddMember( markr );
    inner.fastAddMember( label );
    inner.update_bbox();
    return inner;
  }



  /**
   * create the 6157 three-way point.
   */
  public FigCompound make6157() { // Dreiwegweiche
    FigCompound master = straight( 200, "6157", "0xffc000" );
    FigObject    right = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject     left = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject    rmark = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );
    FigObject    lmark = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );

    left.mirrorY( 0.0, 0.0 );
    lmark.mirrorY( 0.0, 0.0 );
    master.fastAddMember( right );
    master.fastAddMember( rmark );
    master.fastAddMember( left  );
    master.fastAddMember( lmark );
    master.update_bbox();
    return master;
  }


  /**
   * create the 6178 high-speed point.
   */
  public FigCompound make6178() { // Schnellfahrweiche rechts
    FigCompound master = straight( 300, "6178", "0xffc000" );
    FigObject    right = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject    rmark = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );

    right.move( 100*MM, 0.0 );
    rmark.move( 100*MM, 0.0 );
    master.fastAddMember( right );
    master.fastAddMember( rmark );
    master.update_bbox();
    return master;
  }


  /** 
   * create the 6179 high-speed point.
   */
  public FigCompound make6179() { // Schnellfahrweiche links
    FigCompound master = straight( 300, "6179", "0xffc000" );
    FigObject     left = makeTrack( 0.0, 647.0, 18.0, "0xffc000" );
    FigObject    lmark = endMarker( getCurveEndPoint(647.0,18.0), 18.0 );

    left.mirrorY( 0.0, 0.0 );
    left.move(  100*MM, 0.0 );
    lmark.mirrorY( 0.0, 0.0 );
    lmark.move( 100*MM, 0.0 );
    master.fastAddMember( left );
    master.fastAddMember( lmark );
    master.update_bbox();
    return master;
  }


  public FigCompound make6160() { // Kreuzung 36 Grad
    FigCompound master = straight( 105, "6160", "0xe0e0e4" );
    FigCompound slave  = straight( 105, "", "0xe0e0e4" );
    try {
      slave.rotate( new Point( 52.5*MM, 0.0 ), 36.0*DEGREES );
    }
    catch( Exception e ) {
      e.printStackTrace();
    }
    for( Enumeration e=slave.getMembers().elements(); e.hasMoreElements(); ) {
      master.fastAddMember( (FigObject) e.nextElement() ); 
    }
    master.update_bbox();
    return master;
  }


  public FigCompound make6162() { // Kreuzung 18 Grad links?
    FigCompound master = straight( 200, "6162", "0xe0e0e4" );
    FigCompound slave  = straight( 210, "", "0xe0e0e4" );
    try {
      slave.move( -5*MM, 0*MM );
      slave.rotate( new Point( 100*MM, 0.0 ), 18.0*DEGREES );
    }
    catch( Exception e ) {
      e.printStackTrace();
    }
    for( Enumeration e=slave.getMembers().elements(); e.hasMoreElements(); ) {
      master.fastAddMember( (FigObject) e.nextElement() ); 
    }
    master.update_bbox();
    return master;
  }


  public FigCompound make6163() { // Kreuzung 18 Grad rechts?
    FigCompound master = straight( 200, "6163", "0xe0e0e4" );
    FigCompound slave  = straight( 210, "", "0xe0e0e4" );
    try {
      slave.move( -5*MM, 0*MM );
      slave.rotate( new Point( 100*MM, 0.0 ), -18.0*DEGREES );
    }
    catch( Exception e ) {
      e.printStackTrace();
    }
    for( Enumeration e=slave.getMembers().elements(); e.hasMoreElements(); ) {
      master.fastAddMember( (FigObject) e.nextElement() ); 
    }
    master.update_bbox();
    return master;
  }


  /**
   * create the 6150 manual turntable.
   * The turntable has up to 24 tracks at 15 degrees spacing.
   * Use the 6139 curve to adapt the 15 degrees geometry to the 18 degrees
   * geometry used by the standard 'ProfiGleisFactory' tracks.
   */
  public FigCompound make6150() { // Handdrehscheibe, 24 Gleisadapter
    FigCompound tmp = new FigCompound();
    try {
      tmp.fastAddMember( makeLabel( "6150", 0.0, 0.0, 0.0 ) );

      FigObject buehne = makeTrack( 165.0, 0.0, 0.0, "0xb0b0b0" );
      buehne.move( -165.0/2*MM, 0.0 );
      tmp.fastAddMember( buehne );

      FigEllipse outer = new FigEllipse();
      outer.setCenterAndRadius(
        new Point(0.0,0.0), 
        new Point(124.0*MM,124.0*MM) );
      outer.getAttributes()
        .parse( "fillstyle=solid fillcolor=0xe0e0e4 layer=260" );
      tmp.fastAddMember( outer );

      FigEllipse inner = new FigEllipse();
      inner.setCenterAndRadius(
        new Point(0.0,0.0), 
        new Point(84.5*MM,84.5*MM) );
      inner.getAttributes().parse( "fillstyle=none layer=255" );
      tmp.fastAddMember( inner );

      for( int i=0; i < 24; i++ ) {
        FigPolyline limit = new FigPolyline();
        limit.setPoints( 
          new Point[] { new Point(84.5*MM,0.0), new Point(124.0*MM,0.0) } );
        limit.getAttributes().parse( "fillstyle=none layer=255" );
        limit.rotate( new Point(0.0,0.0), (i+0.5)*15*DEGREES );
        tmp.fastAddMember( limit );

        FigObject marker = endMarker( new Point(124*MM,0.0), 0.0 );
        marker.rotate( new Point(0.0,0.0), i*15*DEGREES );
        tmp.fastAddMember( marker );
      }
    }
    catch( Exception e ) {
      e.printStackTrace();
    } 
    tmp.update_bbox();
    return tmp;
  }


  /**
   * create the 6152 electrical turntable.
   * The turntable has up to 48 tracks at 7.5 degrees spacing.
   * Use the 6139 curve to adapt the 7.5 degrees geometry to the 18 degrees
   * geometry used by the standard 'ProfiGleisFactory' tracks.
   */
  public FigCompound make6152() { // Supermodell-Drehscheibe, 48 Gleise
    FigCompound tmp = new FigCompound();
    try {
      tmp.fastAddMember( makeLabel( "6152", 0.0, 0.0, 0.0 ) );

      FigObject buehne = makeTrack( 310.0, 0.0, 0.0, "0xb0b0b0" );
      buehne.move( -310.0/2*MM, 0.0 );
      tmp.fastAddMember( buehne );

      FigEllipse outer = new FigEllipse();
      outer.setCenterAndRadius(
        new Point(0.0,0.0), 
        new Point(232.5*MM,232.5*MM) );
      outer.getAttributes()
        .parse( "fillstyle=solid fillcolor=0xe0e0e4 layer=260" );
      tmp.fastAddMember( outer );

      FigEllipse inner = new FigEllipse();
      inner.setCenterAndRadius(
        new Point(0.0,0.0), 
        new Point(157.0*MM,157.0*MM) );
      inner.getAttributes().parse( "fillstyle=none layer=255" );
      tmp.fastAddMember( inner );

      for( int i=0; i < 48; i++ ) {
        FigPolyline limit = new FigPolyline();
        limit.setPoints(
          new Point[] { new Point(157.0*MM,0.0), new Point(232.5*MM,0.0) } );
        limit.getAttributes().parse( "fillstyle=none layer=255" );
        limit.rotate( new Point(0.0,0.0), (i+0.5)*7.5*DEGREES );
        tmp.fastAddMember( limit );

        FigObject marker = endMarker( new Point(232.5*MM,0.0), 0.0 );
        marker.rotate( new Point(0.0,0.0), i*7.5*DEGREES );
        tmp.fastAddMember( marker );
      }
    }
    catch( Exception e ) {
      e.printStackTrace();
    } 
    
    tmp.update_bbox();
    return tmp;
  }
  

  /**
   * selftest. 
   * Creates a drawing that includes one piece of all available tracks.
   */
  public static void main( String argv[] ) {
    ProfiGleisFactory MPC   = new ProfiGleisFactory();

    // create a jfig editor, this automatically loads the global and user
    // property settings, if any. We are not interested in verbose error
    // messages.
    //
    jfig.utils.ExceptionTracer.setEnabled( false );
    MPC.editor = new jfig.gui.JModularEditor();

    FigObject tmp = null; int i=1;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6101() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6102() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6103() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6107() );
tmp.move( 0*4800, i*4800 ); i++;

    // Flexgleis 80mm
    MPC.editor.insertIntoObjectList( tmp=MPC.make6106() );
    tmp.move( 0*4800, -1*4800 ); 

    // Ausgleich, Entkupplung, Prellbock
    MPC.editor.insertIntoObjectList( tmp=MPC.make6110() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6111() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6114() );
tmp.move( 0*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6116() );
tmp.move( 0*4800, i*4800 ); i++;

i = 0;
    // R1 R2
    MPC.editor.insertIntoObjectList( tmp=MPC.make6120() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6122() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6125() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6127() );
tmp.move( 8*4800, i*4800 ); i++;

    // R3 R4 Weichenbogen
    MPC.editor.insertIntoObjectList( tmp=MPC.make6131() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6133() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6138() );
tmp.move( 8*4800, i*4800 ); i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6139() );
tmp.move( 8*4800, i*4800 ); i++;

    // normal points, three-way point, high-speed points
i = 0;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6170() );
tmp.move( 16*4800, i*4800 ); i++; i++;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6171() );
tmp.move( 16*4800, i*4800 ); i+=2;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6157() );
tmp.move( 16*4800, i*4800 ); i+=2;

    MPC.editor.insertIntoObjectList( tmp=MPC.make6178() );
tmp.move( 16*4800, i*4800 ); i+=2;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6179() );
tmp.move( 16*4800, i*4800 ); i+=2;

    // left and right curved points
    MPC.editor.insertIntoObjectList( tmp=MPC.make6174() );
tmp.move( 16*4800, i*4800 ); i+=4;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6175() );
tmp.move( 16*4800, i*4800 ); i+=4;

    // crossings (crossovers?)
i = 0;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6162() );
tmp.move( 24*4800, i*4800 ); i+=3;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6163() );
tmp.move( 24*4800, i*4800 ); i+=3;
    MPC.editor.insertIntoObjectList( tmp=MPC.make6160() );
tmp.move( 24*4800, i*4800 ); i+=3;

    // turntables
    MPC.editor.insertIntoObjectList( tmp=MPC.make6150() );
tmp.move( 2400, 30*2400 );
    MPC.editor.insertIntoObjectList( tmp=MPC.make6152() );
tmp.move( 18*2400, 30*2400 );

    MPC.editor.doRedraw();
  } // main

} // end of jfig.utils.ProfiGleisFactory
