[comp.sys.next] A Spirograph for the NeXT machine: My first NeXT program.

roy@prism.gatech.EDU (Roy Mongiovi) (04/29/89)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Spiro Spiro/Makefile Spiro/README Spiro/Spiro.m
#   Spiro/SpiroInterface.m Spiro/Spirograph.h Spiro/Spirograph.m
# Wrapped by roy@prism.gatech.edu on Fri Apr 28 13:13:19 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'Spiro' ; then
    echo shar: Creating directory \"'Spiro'\"
    mkdir 'Spiro'
fi
if test -f 'Spiro/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/Makefile'\"
else
echo shar: Extracting \"'Spiro/Makefile'\" \(624 characters\)
sed "s/^X//" >'Spiro/Makefile' <<'END_OF_FILE'
X#
X# Spirograph makefile. 
X#
X
X# Libraries used by this application.
XLIBS=-lappkit_s -ldpsclient_s -lobjc_s -lstreams_s -ldb_s -lm -lc_s -lsoundkit -lsound
X
X# Flags to pass on to the compiler and linker.
XCFLAGS=-gg
XLFLAGS=
X
X# Rules.
XSpiro: Spiro.o SpiroInterface.o Spirograph.o
X	$(CC) $(CFLAGS) $(LFLAGS) -o Spiro Spiro.o SpiroInterface.o Spirograph.o $(LIBS)
X
XSpiro.o: Spiro.m Spirograph.h
XSpiroInterface.o: SpiroInterface.m Spirograph.h
XSpirograph.o: Spirograph.m Spirograph.h
X
Xclean: 
X	-rm -f *.o Spiro core
X
Xhelp: 
X	@echo '  make Spiro - to make the application'
X	@echo '  make clean - to remove all files but the source'
END_OF_FILE
if test 624 -ne `wc -c <'Spiro/Makefile'`; then
    echo shar: \"'Spiro/Makefile'\" unpacked with wrong size!
fi
# end of 'Spiro/Makefile'
fi
if test -f 'Spiro/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/README'\"
else
echo shar: Extracting \"'Spiro/README'\" \(2736 characters\)
sed "s/^X//" >'Spiro/README' <<'END_OF_FILE'
XThis is my first excursion into programming on the NeXT.  It uses vector
Xaddition to draw a "spirograph."  Since I'm not terribly familiar with
Xthe NeXT and it's objects, I can't say whether or not I'm doing things
Xin the politicaly correct manner, but the program seems to work (with
Xa caveat or two that I'll mention in a moment).
X
XThis application was developed under 0.8, and doesn't use a .nib file.
XI haven't gotten 0.9 yet, so I have no idea if it will work with it or
Xnot.  At least it won't cause problems with the new interface builder.
XI didn't use a .nib file for two reasons.  First, I really don't think
Xthat the interface builder gives me enough control over objects.  I'd
Xrather create a window with the right background color in its content
Xview than have the interface builder create a window with a light grey
Xbackground and then change the color during initialization.  the second
Xreason is that I'm a purist.  I don't like having part of my program
Xnot under my control.
X
XSo anyway, I decompiled the entire application and then went into the
Xinterface code with my machete and fixed it up.  It seems to draw the
Xright stuff, but I'm not sure if I'm doing it the right way.  There are
Xso many methods available through the application kit objects that I
Xjust sort of flailed around until I got the behavior I wanted.
X
XThere's one thing I couldn't figure out how to straighten out.  When
Xyou enter a number in the matrix of forms that specify the vector
Xlengths and rotation values, you must press the return key to cause
Xthe final number to be recognized.  Mousing or tabbing to another
Xentry in the matrix is also good enough, but after you enter the
Xlast number in the matrix you have to hit return or it won't see the
Xnumber.  There's another form (not in a matrix) where you enter the
Xnumber of points to display.  It is not necessary to press return
Xafter entering a number there.  Clicking the "draw" button seems to
Xbe good enough to cause it to see that number.  Only the matrix gives
Xme grief.
X
XBoth the vector lengths and rotation values may be positive or negative
Xfloating point numbers.  If you click the "Vectors" switch it will use
Xinstance drawing to show you the vectors that are being summed to make
Xthe graph.  This is useful if you want to know why the spirograph looks
Xlike it does.  It can also dump/load the spirograph to a little text file.
XIf you can tell me ways to improve it, please do.  Don't blame me if
Xyour spirographs are ugly, though.
X--
XRoy J. Mongiovi     Systems Support Specialist     Office of Computing Services
XGeorgia Institute of Technology	  Atlanta, Georgia  30332-0275   (404) 894-4660
X	uucp: ...!{allegra,amd,hplabs,ut-ngp}!gatech!prism!roy
X	ARPA: roy@prism.gatech.edu
END_OF_FILE
if test 2736 -ne `wc -c <'Spiro/README'`; then
    echo shar: \"'Spiro/README'\" unpacked with wrong size!
fi
# end of 'Spiro/README'
fi
if test -f 'Spiro/Spiro.m' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/Spiro.m'\"
else
echo shar: Extracting \"'Spiro/Spiro.m'\" \(447 characters\)
sed "s/^X//" >'Spiro/Spiro.m' <<'END_OF_FILE'
X
X/* Generated by the NeXT Interface Builder */
X
X#import <appkit/appkit.h>
X#import <dpsclient/dpsclient.h>
X#import "Spirograph.h"
X
Xextern id buildInterface(id);
Xextern graph();
X
Xmain(argc, argv)
Xint   argc;
Xchar *argv[];
X{
X   DPSTimedEntry handler;
X
X   NXApp = [Spirograph new];
X   buildInterface(NXApp);
X
X   handler = DPSAddTimedEntry(0.0, (DPSTimedEntryProc *) &graph, NXApp, NX_BASETHRESHOLD);
X   [NXApp run];
X   DPSRemoveTimedEntry(handler);
X}
END_OF_FILE
if test 447 -ne `wc -c <'Spiro/Spiro.m'`; then
    echo shar: \"'Spiro/Spiro.m'\" unpacked with wrong size!
fi
# end of 'Spiro/Spiro.m'
fi
if test -f 'Spiro/SpiroInterface.m' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/SpiroInterface.m'\"
else
echo shar: Extracting \"'Spiro/SpiroInterface.m'\" \(9536 characters\)
sed "s/^X//" >'Spiro/SpiroInterface.m' <<'END_OF_FILE'
X#import <appkit/appkit.h>
X#import "Spirograph.h"
X
Xid Spiro;		/* Spirograph application */
X
Xid SpiroInfo;		/* Spirograph info panel */
Xid SpiroControl;	/* Spirograph control window */
X
Xid buildSpiro_SpiroInfo_Button()
X{
X   NXRect theFrame;
X   id	  theButton;
X
X   NXSetRect(&theFrame, 19.0, 30.0, 64.0, 64.0);
X   theButton = [Button newFrame: &theFrame
X		       title: "SPIRO"
X		       tag: 0
X		       target: nil
X		       action: NULL
X		       key: 0
X		       enabled: NO];
X   [theButton setType: NX_MOMENTARYPUSH];
X
X   return theButton;
X}
X
Xid buildSpiro_SpiroInfo_Field1()
X{
X   NXRect theFrame;
X   id	  theTextField;
X
X   NXSetRect(&theFrame, 108.0, 65.0, 99.0, 21.0);
X   theTextField = [TextField newFrame: &theFrame];
X   [theTextField setStringValueNoCopy: "Spirograph"];
X   [theTextField setFont: [Font newFont: "Helvetica" size: 18.0]];
X   [theTextField setBackgroundGray: NX_LTGRAY];
X   [theTextField setTextGray: NX_BLACK];
X   [theTextField setAlignment: NX_LEFTALIGNED];
X   [theTextField setBordered: NO];
X   [theTextField setSelectable: NO];
X
X   return theTextField;
X}
X
Xid buildSpiro_SpiroInfo_Field2()
X{
X   NXRect theFrame;
X   id	  theTextField;
X
X   NXSetRect(&theFrame, 108.0, 40.0, 148.0, 21.0);
X   theTextField = [TextField newFrame: &theFrame];
X   [theTextField setStringValueNoCopy: "by Roy Mongiovi"];
X   [theTextField setFont: [Font newFont: "Helvetica" size: 18.0]];
X   [theTextField setBackgroundGray: NX_LTGRAY];
X   [theTextField setTextGray: NX_BLACK];
X   [theTextField setAlignment: NX_LEFTALIGNED];
X   [theTextField setBordered: NO];
X   [theTextField setSelectable: NO];
X
X   return theTextField;
X}
X
Xid buildSpiro_SpiroInfo_VersionNumber()
X{
X   NXRect theFrame;
X   id	  theTextField;
X
X   NXSetRect(&theFrame, 23.0, 12.0, 62.0, 13.0);
X   theTextField = [TextField newFrame: &theFrame];
X   [theTextField setStringValueNoCopy: "Version 1.0"];
X   [theTextField setFont: [Font newFont: "Helvetica" size: 10.0]];
X   [theTextField setBackgroundGray: NX_LTGRAY];
X   [theTextField setTextGray: NX_DKGRAY];
X   [theTextField setAlignment: NX_LEFTALIGNED];
X   [theTextField setBordered: NO];
X   [theTextField setSelectable: NO];
X
X   return theTextField;
X}
X
Xid buildSpiro_SpiroInfo()
X{
X   NXRect theFrame;
X   id	  theView;
X
X   NXSetRect(&theFrame, 377.0, 644.0, 272.0, 111.0);
X   SpiroInfo = [Panel newContent: &theFrame
X		      style: NX_TITLEDSTYLE
X		      backing: NX_BUFFERED
X		      buttonMask: NX_CLOSEBUTTONMASK
X		      defer: NO];
X   [SpiroInfo setTitle: "Spirograph Info"];
X
X   theView = [SpiroInfo contentView];
X   [theView addSubview:buildSpiro_SpiroInfo_Button()];
X   [theView addSubview:buildSpiro_SpiroInfo_Field1()];
X   [theView addSubview:buildSpiro_SpiroInfo_Field2()];
X   [theView addSubview:buildSpiro_SpiroInfo_VersionNumber()];
X
X   [SpiroInfo display];
X   return SpiroInfo;
X}
X
Xid buildSpiro_SpiroMenu()
X{
X   id theMenu;
X
X   theMenu = [Menu newTitle: "Spiro"];
X
X   [[theMenu addItem: "Info..."
X	     action: @selector(orderFront:)
X	     keyEquivalent: 0]
X	     setTarget: SpiroInfo];
X
X   [[theMenu addItem: "Open..."
X	     action: @selector(open:)
X	     keyEquivalent: 'o']
X	     setTarget: Spiro];
X
X   [[theMenu addItem: "Save..."
X	     action: @selector(save:)
X	     keyEquivalent: 's']
X	     setTarget: Spiro];
X
X   [[theMenu addItem: "Clear"
X	     action: @selector(clear:)
X	     keyEquivalent: 'c']
X	     setTarget: Spiro];
X
X    [[theMenu addItem: "Hide"
X	      action: @selector(hide:)
X	      keyEquivalent: 'h']
X	      setTarget: Spiro];
X
X    [[theMenu addItem: "Quit"
X	      action: @selector(terminate:)
X	      keyEquivalent: 'q']
X	      setTarget: Spiro];
X
X   [theMenu display];
X   return theMenu;
X}
X
Xid buildSpiro_DisplayWindow()
X{
X   NXRect theFrame;
X   id	  theWindow;
X   id	  theView;
X
X   NXSetRect(&theFrame, 500.0, 200.0, 520.0, 520.0);
X   theWindow = [Window newContent: &theFrame
X		       style: NX_TITLEDSTYLE
X		       backing: NX_RETAINED
X		       buttonMask: NX_RESIZEBUTTONMASK
X		       defer: NO];
X   [theWindow setTitle:"Spirograph Display Window"];
X   [theWindow removeFromEventMask: NX_KEYDOWNMASK | NX_KEYUPMASK];
X   [theWindow setBackgroundGray: NX_WHITE];
X
X   [theWindow display];
X
X   theView = [theWindow contentView];
X   [theView setClipping: NO];
X   [Spiro setGraphics: theView];
X   return theWindow;
X}
X
Xid buildSpiro_ControlWindow_Field1()
X{
X   NXRect theFrame;
X   id	  theTextField;
X
X   NXSetRect(&theFrame, 39.0, 284.0, 66.0, 21.0);
X   theTextField = [TextField newFrame: &theFrame];
X   [theTextField setStringValueNoCopy: "Length"];
X   [theTextField setFont: [Font newFont: "Helvetica" size: 18.0]];
X   [theTextField setBackgroundGray: NX_LTGRAY];
X   [theTextField setTextGray: NX_BLACK];
X   [theTextField setAlignment: NX_CENTERED];
X   [theTextField setBordered: NO];
X   [theTextField setSelectable: NO];
X
X   return theTextField;
X}
X
Xid buildSpiro_ControlWindow_Field2()
X{
X   id	  theTextField;
X   NXRect theFrame;
X
X   NXSetRect(&theFrame, 116.0, 284.0, 89.0, 21.0);
X   theTextField = [TextField newFrame: &theFrame];
X   [theTextField setStringValueNoCopy: "Rotations"];
X   [theTextField setFont: [Font newFont: "Helvetica" size: 18.0]];
X   [theTextField setBackgroundGray: NX_LTGRAY];
X   [theTextField setTextGray: NX_BLACK];
X   [theTextField setAlignment: NX_CENTERED];
X   [theTextField setBordered: NO];
X   [theTextField setSelectable: NO];
X
X   return theTextField;
X}
X
Xid buildSpiro_ControlWindow_Indices()
X{
X   id	  theMatrix, theCell, theProtoCell;
X   NXRect theFrame;
X   NXSize theSize;
X
X   theProtoCell = [TextFieldCell new];
X
X   NXSetRect(&theFrame, 6.0, 88.0, 21.0,188.0);
X   theMatrix = [Matrix newFrame: &theFrame
X		       mode: NX_TRACKMODE
X		       prototype: theProtoCell
X		       numRows: 9
X		       numCols: 1];
X   theSize.width  = 21.0;
X   theSize.height = 21.0;
X   [theMatrix setCellSize: &theSize];
X   theSize.width  = 0.0;
X   theSize.height = 0.0;
X   [theMatrix setIntercell: &theSize];
X   [theMatrix setFont: [Font newFont: "Helvetica" size: 14.0]];
X
X   [[theMatrix cellAt: 0: 0] setStringValue: "1."];
X   [[theMatrix cellAt: 1: 0] setStringValue: "2."];
X   [[theMatrix cellAt: 2: 0] setStringValue: "3."];
X   [[theMatrix cellAt: 3: 0] setStringValue: "4."];
X   [[theMatrix cellAt: 4: 0] setStringValue: "5."];
X   [[theMatrix cellAt: 5: 0] setStringValue: "6."];
X   [[theMatrix cellAt: 6: 0] setStringValue: "7."];
X   [[theMatrix cellAt: 7: 0] setStringValue: "8."];
X   [[theMatrix cellAt: 8: 0] setStringValue: "9."];
X
X   return theMatrix;
X}
X
Xid buildSpiro_ControlWindow_Values()
X{
X   id	  theMatrix, theCell, theProtoCell;
X   NXRect theFrame;
X   NXSize theSize;
X   int	  i;
X
X   theProtoCell = [TextFieldCell new];
X   [theProtoCell setBezeled: YES];
X   [theProtoCell setEditable: YES];
X
X   NXSetRect(&theFrame, 27.0, 89.0,174.0,189.0);
X   theMatrix = [Matrix newFrame: &theFrame
X		       mode: NX_TRACKMODE
X		       prototype: theProtoCell
X		       numRows: 9
X		       numCols: 2];
X   theSize.width  = 87.0;
X   theSize.height = 21.0;
X   [theMatrix setCellSize: &theSize];
X   theSize.width  = 0.0;
X   theSize.height = 0.0;
X   [theMatrix setIntercell: &theSize];
X
X   for (i = 0; i < 9; i++)
X   {
X      [[theMatrix cellAt: i: 0] setStringValue: ""];
X      [[theMatrix cellAt: i: 1] setStringValue: ""];
X   }
X
X   [Spiro setValues: theMatrix];
X   return theMatrix;
X}
X
Xid buildSpiro_ControlWindow_Form()
X{
X   NXRect theFrame;
X   id	  theForm;
X
X   NXSetRect(&theFrame, 48.0, 51.0, 127.0, 21.0);
X   theForm = [Form newFrame: &theFrame];
X
X   [theForm addEntry: "Points"];
X
X   [Spiro setPoints: theForm];
X   return theForm;
X}
X
Xid buildSpiro_ControlWindow_Vectors()
X{
X   NXRect theFrame;
X   id	  theButton;
X
X   NXSetRect(&theFrame, 39.0, 16.0, 64.0, 15.0);
X   theButton = [Button newFrame: &theFrame
X		       title: "Vectors"
X		       tag: 0
X		       target: Spiro
X		       action: @selector(vectors:)
X		       key: 0
X		       enabled: YES];
X   [theButton setType: NX_SWITCH];
X
X   return theButton;
X}
X
Xid buildSpiro_ControlWindow_Draw()
X{
X   NXRect theFrame;
X   id	  theButton;
X
X   NXSetRect(&theFrame, 129.0, 12.0, 63.0, 24.0);
X   theButton = [Button newFrame: &theFrame
X		       title: "Draw"
X		       tag: 0
X		       target: Spiro
X		       action: @selector(draw:)
X		       key: 0
X		       enabled: YES];
X   [theButton setType:NX_TOGGLE];
X   [theButton setAltTitle: "Stop"];
X
X   [Spiro setSwitch: theButton];
X   return theButton;
X}
X
Xid buildSpiro_ControlWindow()
X{
X   NXRect theFrame;
X   id	  theView;
X
X   NXSetRect(&theFrame, 185.0, 295.0, 224.0, 310.0);
X   SpiroControl = [Window newContent: &theFrame
X			  style: NX_TITLEDSTYLE
X			  backing: NX_BUFFERED
X			  buttonMask: 0
X			  defer: NO];
X   [SpiroControl setTitle:"Spirograph Control Window"];
X
X   theView = [SpiroControl contentView];
X   [theView addSubview:buildSpiro_ControlWindow_Field1()];
X   [theView addSubview:buildSpiro_ControlWindow_Field2()];
X   [theView addSubview:buildSpiro_ControlWindow_Indices()];
X   [theView addSubview:buildSpiro_ControlWindow_Values()];
X   [theView addSubview:buildSpiro_ControlWindow_Form()];
X   [theView addSubview:buildSpiro_ControlWindow_Vectors()];
X   [theView addSubview:buildSpiro_ControlWindow_Draw()];
X
X   [SpiroControl display];
X   return SpiroControl;
X}
X
Xid buildInterface(owner)
Xid owner;
X{
X   Spiro = owner;
X
X   [buildSpiro_SpiroInfo() orderOut: nil];
X   [Spiro setOpen: [OpenPanel new]];
X   [Spiro setSave: [SavePanel new]];
X   [NXApp setMainMenu: buildSpiro_SpiroMenu()];
X   [buildSpiro_DisplayWindow() orderFront: nil];
X   [buildSpiro_ControlWindow() orderFront: nil];
X
X   [Spiro activateSelf: YES];
X   [SpiroControl makeKeyWindow];
X
X   return owner;
X}
END_OF_FILE
if test 9536 -ne `wc -c <'Spiro/SpiroInterface.m'`; then
    echo shar: \"'Spiro/SpiroInterface.m'\" unpacked with wrong size!
fi
# end of 'Spiro/SpiroInterface.m'
fi
if test -f 'Spiro/Spirograph.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/Spirograph.h'\"
else
echo shar: Extracting \"'Spiro/Spirograph.h'\" \(601 characters\)
sed "s/^X//" >'Spiro/Spirograph.h' <<'END_OF_FILE'
X#import <appkit/appkit.h>
X#import <appkit/Application.h>
X
X#define TWO_PI	(6.283185307179586476925287)
X
Xtypedef struct
X	{
X	   double l;	/* Vector length */
X	   double v;	/* Vector velocity */
X	} VECTOR;
X
Xtypedef struct
X	{
X	   double s;	/* Scaled length */
X	   double a;	/* Vector angle */
X	   double i;	/* Angle increment */
X	} POINT;
X
X@interface Spirograph: Application
X{
X}
X
X- setGraphics: anObject;
X- setPoints: anObject;
X- setValues: anObject;
X- setSwitch: anObject;
X- setOpen: anObject;
X- setSave: anObject;
X- open: sender;
X- save: sender;
X- clear: sender;
X- draw: sender;
X- vectors: sender;
X
X@end
END_OF_FILE
if test 601 -ne `wc -c <'Spiro/Spirograph.h'`; then
    echo shar: \"'Spiro/Spirograph.h'\" unpacked with wrong size!
fi
# end of 'Spiro/Spirograph.h'
fi
if test -f 'Spiro/Spirograph.m' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Spiro/Spirograph.m'\"
else
echo shar: Extracting \"'Spiro/Spirograph.m'\" \(6407 characters\)
sed "s/^X//" >'Spiro/Spirograph.m' <<'END_OF_FILE'
X#import <dpsclient/dpsclient.h>
X#import <math.h>
X#import <stdio.h>
X#import "Spirograph.h"
X
Xchar *Types[] = {"", NULL};
X
Xid     Graphics;		/* Id of View for Graphics */
Xid     Points;			/* Id of Number of Points Form */
Xid     Values;			/* Id of Matrix of Forms */
Xid     Switch;			/* Id of Draw/Stop Button */
Xid     Open;			/* Id of OpenPanel */
Xid     Save;			/* Id of SavePanel */
X
Xchar   Path[256];		/* Path name returned by Open/Save */
Xchar   Name[256];		/* Filename returned by Open/Save */
Xchar   File[265];		/* Concatenated file name to Open/Save */
X
XVECTOR V[9];			/* Vector values read from Values */
Xint    Nv;			/* Number of non-zero vectors encountered */
Xint    Np;			/* Number of points from Points */
Xdouble Length;			/* Scaled length of vector summation */
X
Xdouble Xcenter, Ycenter;	/* Offset of center of Graphics View */
X
Xdouble X, Y, Row, Col;		/* Current and next coordinates */
X
XPOINT  P[9];			/* Current values of vector endpoints */
X
Xint    Drawing;			/* True if currently drawing */
Xint    Current;			/* Current point being drawn */
Xchar   Showing;			/* True if showing vectors */
X
X@implementation Spirograph
X
X- setGraphics: anObject
X{
X   Graphics = anObject;
X   return self;
X}
X
X- setPoints: anObject
X{
X   Points = anObject;
X   return self;
X}
X
X- setValues: anObject
X{
X   Values = anObject;
X   return self;
X}
X
X- setSwitch: anObject
X{
X   Switch = anObject;
X   return self;
X}
X
X- setOpen: anObject
X{
X   Open = anObject;
X   return self;
X}
X
X- setSave: anObject
X{
X   Save = anObject;
X   return self;
X}
X
X- open: sender
X{
X   FILE *fd;
X   int   i;
X
X   if ([Open runModalForTypes: Types])
X   {
X      strcpy(Path, [Open dirPath]);
X      strcpy(Name, [Open filename]);
X      sprintf(File, "%s/%s", Path, Name);
X
X      if (fd = fopen(File, "r"))
X      {
X	 fscanf(fd, "%d %d\n", &Nv, &Np);
X	 for (i = 0; i < Nv; i++)
X	    fscanf(fd, "%lf %lf\n", &V[i].l, &V[i].v);
X	 fclose(fd);
X
X	 [Points setIntValue: Np at: 0];
X	 for (i = 0; i < Nv; i++)
X	 {
X	    [[Values cellAt: i: 0] setDoubleValue: V[i].l];
X	    [[Values cellAt: i: 1] setDoubleValue: V[i].v];
X	 }
X      }
X   }
X   return self;
X}
X
X- save: sender
X{
X   FILE *fd;
X   int   i;
X
X   if ([Save runModalForDirectory: Path file: Name])
X   {
X      strcpy(Path, [Save dirPath]);
X      strcpy(Name, [Save filename]);
X      sprintf(File, "%s/%s", Path, Name);
X
X      if (fd = fopen(File, "w"))
X      {
X	 for (Nv = i = 0; i < 9; i++)
X	 {
X	    if ((V[i].l = [[Values cellAt: i: 0] doubleValue]) != 0.0)
X	       Nv = i + 1;
X	    V[i].v = [[Values cellAt: i: 1] doubleValue];
X	 }
X	 Np = [Points intValueAt: 0];
X
X	 fprintf(fd, "%d %d\n", Nv, Np);
X	 for (i = 0; i < Nv; i++)
X	    fprintf(fd, "%f %f\n", V[i].l, V[i].v);
X	 fclose(fd);
X      }
X   }
X   return self;
X}
X
X- clear: sender
X{
X   int	  i;
X   NXRect bounds;
X
X   if (!Drawing)
X   {
X      for (i = 0; i < 9; i++)
X      {
X	 [[Values cellAt: i: 0] setStringValue: ""];
X	 [[Values cellAt: i: 1] setStringValue: ""];
X      }
X      [Points setStringValue: ""];
X
X      [Graphics lockFocus];
X	 [Graphics getBounds: &bounds];
X	 NXEraseRect(&bounds);
X      [Graphics unlockFocus];
X   }
X   return self;
X}
X
X- vectors:sender
X{
X   Showing = [sender state];
X   return self;
X}
X
X- draw:sender
X{
X   int    i;
X   NXRect bounds;
X   double factor;
X
X   if ([Switch state])
X   {
X      for (Nv = i = 0; i < 9; i++)
X      {
X	 if ((V[i].l = [[Values cellAt: i: 0] doubleValue]) != 0.0)
X	    Nv = i + 1;
X	 V[i].v = [[Values cellAt: i: 1] doubleValue];
X      }
X      Np = [Points intValueAt: 0];
X
X      [Graphics lockFocus];
X	 [Graphics getBounds: &bounds];
X	 NXEraseRect(&bounds);
X
X	 Xcenter = bounds.size.width  / 2.0;
X	 Ycenter = bounds.size.height / 2.0;
X      [Graphics unlockFocus];
X
X      for (i = 0, Length = 0.0; i < Nv; i++)
X	 Length += fabs(V[i].l);
X      Length = (MIN(Xcenter, Ycenter) - 2.0) / Length;
X
X      factor = TWO_PI / ((double) Np - 1.0);
X
X      for (X = Y = 0.0, i = 0; i < Nv; i++)
X      {
X	 Y += (P[i].s = V[i].l * Length);
X	 P[i].a = P[i].i = V[i].v * factor;
X      }
X
X      Current = 1;
X      Drawing = YES;
X   }
X   else
X      Drawing = NO;
X   return self;
X}
X
Xgraph(te, now, app)
XDPSTimedEntry te;
Xdouble	      now;
Xchar	     *app;
X{
X   typedef struct
X	   {
X	      DPSTokenType     tokenType;
X	      DPSTopLevelCount topLevelCount;
X	      DPSCard16        nBytes;
X
X	      DPSBinObjGeneric obj0;
X	      DPSBinObjReal    obj1;
X	      DPSBinObjReal    obj2;
X	      DPSBinObjGeneric obj3;
X	      DPSBinObjReal    obj4;
X	      DPSBinObjReal    obj5;
X	      DPSBinObjGeneric obj6;
X	      DPSBinObjGeneric obj7;
X	   } _dpsQ;
X
X   static _dpsQ _dpsF =
X   {
X       DPS_DEF_TOKENTYPE, 8, sizeof(_dpsQ),
X      {DPS_EXEC    | DPS_NAME, 0, -1, 111},	/* newpath */
X      {DPS_LITERAL | DPS_REAL, 0,  0,   0},	/* param: X */
X      {DPS_LITERAL | DPS_REAL, 0,  0,   0},	/* param: Y */
X      {DPS_EXEC    | DPS_NAME, 0, -1, 107},	/* moveto */
X      {DPS_LITERAL | DPS_REAL, 0,  0,   0},	/* param: Col */
X      {DPS_LITERAL | DPS_REAL, 0,  0,   0},	/* param: Row */
X      {DPS_EXEC    | DPS_NAME, 0, -1,  99},	/* lineto */
X      {DPS_EXEC    | DPS_NAME, 0, -1, 167},	/* stroke */
X   };
X   register DPSContext    _dpsCurCtxt = DPSPrivGetContext();
X   register DPSBinObjRec *_dpsP       = (DPSBinObjRec *) &_dpsF.obj0;
X
X   char     showing;
X   NXEvent  event;
X   int	    i;
X
X   if (!Drawing)
X      return;
X
X   showing = Showing;
X
X   [Graphics lockFocus];
X      [Graphics translate: Xcenter: Ycenter];
X
X      while (![(id) app peekNextEvent: NX_ALLEVENTS into: &event] && Current++ < Np)
X      {
X	 if (showing)
X	 {
X	    PSsetinstance(1);
X	    PSnewpath();
X	    PSmoveto(0.0, 0.0);
X	 }
X	 for (Row = Col = 0.0, i = 0; i < Nv; i++)
X	 {
X	    Row += P[i].s * cos(P[i].a);
X	    Col += P[i].s * sin(P[i].a);
X
X	    if (showing)
X	       PSlineto(Col, Row);
X
X	    P[i].a += P[i].i;
X	    while (P[i].a >= TWO_PI)
X	       P[i].a -= TWO_PI;
X	 }
X	 if (showing)
X	 {
X	    PSstroke();
X	    PSsetinstance(0);
X	    DPSFlush();
X
X	    PSnewinstance();
X	 }
X
X/*	 PSnewpath(); */
X/*	 PSmoveto(X, Y); */
X/*	 PSlineto(X = Col, Y = Row); */
X/*	 PSstroke(); */
X
X	 _dpsP[1].val.realVal = X;
X	 _dpsP[2].val.realVal = Y;
X	 _dpsP[4].val.realVal = Col;
X	 _dpsP[5].val.realVal = Row;
X	 DPSBinObjSeqWrite(_dpsCurCtxt,(char *) &_dpsF,sizeof(_dpsQ));
X
X	 X = Col;
X	 Y = Row;
X      }
X      if (Current >= Np)
X      {
X	 [Switch performClick: (id) app];
X         DPSFlush();
X      }
X
X      [Graphics translate: -Xcenter: -Ycenter];
X   [Graphics unlockFocus];
X}
X
X@end
END_OF_FILE
if test 6407 -ne `wc -c <'Spiro/Spirograph.m'`; then
    echo shar: \"'Spiro/Spirograph.m'\" unpacked with wrong size!
fi
# end of 'Spiro/Spirograph.m'
fi
echo shar: End of shell archive.
exit 0
-- 
Roy J. Mongiovi     Systems Support Specialist     Office of Computing Services
Georgia Institute of Technology	  Atlanta, Georgia  30332-0275   (404) 894-4660
	uucp: ...!{allegra,amd,hplabs,ut-ngp}!gatech!prism!roy
	ARPA: roy@prism.gatech.edu

ali@polya.Stanford.EDU (Ali T. Ozer) (04/29/89)

In article <569@hydra.gatech.EDU> roy@prism.gatech.EDU (Roy Mongiovi) writes:
>This is my first excursion into programming on the NeXT.  It uses vector
>addition to draw a "spirograph."  Since I'm not terribly familiar with
>the NeXT and it's objects, I can't say whether or not I'm doing things
>in the politicaly correct manner, but the program seems to work (with
>a caveat or two that I'll mention in a moment).

Thanks for the program! I haven't had a chance to play with it
yet (not near a NeXT machine), but will soon... Hope you don't mind some
comments in the meantime.

>This application was developed under 0.8, and doesn't use a .nib file.
>I haven't gotten 0.9 yet, so I have no idea if it will work with it or
>not.  At least it won't cause problems with the new interface builder.
>I didn't use a .nib file for two reasons.  First, I really don't think
>that the interface builder gives me enough control over objects.  I'd
>rather create a window with the right background color in its content
>view than have the interface builder create a window with a light grey
>background and then change the color during initialization.  

One way to control any object in any way you want it to make it an outlet
of something; say your application object. Then, when the setxxx: method
is called, with a handle to that freshly created object, you can go ahead
and change any parameters you want. This should give you full control
over any objects created by Interface Builder.

This might seem inefficient (why create the window with a light gray
background and then have it change color at runtime) --- but it isn't.
Windows (in the server) are not created until they are actually brought on
screen, and this does not happen until your application's "run" method is
invoked. Thus at the time the setxxx: methods are being called, only the 
window object exists, not the actual window memory. So changing the color 
(and other parameters, like size, contentView, etc), is not expensive --- 
you are just changing some instance variables in the window object.

Keeping your interface in .nib files gives you great flexibility. You
needn't worry about your 0.8 .nib file not working in 0.9; although
0.9 .nib format is different than the 0.8 one, 0.8 .nib files can still
be read in by the loadNibFile: method. Interface Builder can also
read 0.8 .nib files (as well as 0.9, of course); it saves 0.9 format.

If you are worried about having to keep one or more .nib files around
with the executable (admittedly a pain when you copy/move the 
application around) 0.9 provides a solution to that: You
can now include .nib files (and any other data file you might need)
in Mach-O segments of your executable. It's very easy to open a stream
bound to data stored in a Mach-O segment; thus you can read any random
data from the executable itself. Of course, there are "convenience" 
methods such as "loadNibSection:," "newFromMachO:," provided to
make it real easy to read common forms of data (such as .nib files,
TIFF bitmaps, sound data, etc) out. 

On another note... If you wish to refer to instance variables in a 
timed entry function, you can simply invoke a method from the timed entry:
When installing the timed entry, pass "self" (or whatever the id is) to
DPSAddTimedEntry as your "user data." Then, in your timed entry function, 
graph() in your case, you can invoke a method as shown below:

void graph (DPSTimedEntry te, double now, id obj)
{
  [obj someMethod];
}

This would allow you to access "obj"s instance variables in "someMethod,"
making it unnecessary to have the globals you have in Spirograph.m. (Objects
whose state is contained totally within their instance variables are a lot
nicer to deal with...)

In the graph() function, it seems like you used code from a C function 
generated by pswrap; is that the case? Such generated code might not be very
portable (and not very pretty either); you probably want to keep the original 
pswraps around... 

Anyway, thanks again --- Can't wait to try the program out.

Ali

roy@prism.gatech.EDU (Roy Mongiovi) (05/02/89)

In article <8827@polya.Stanford.EDU>, ali@polya.Stanford.EDU (Ali T. Ozer) writes:
> of something; say your application object. Then, when the setxxx: method
> is called, with a handle to that freshly created object, you can go ahead
> and change any parameters you want. This should give you full control
> over any objects created by Interface Builder.
> ...
> On another note... If you wish to refer to instance variables in a 
> timed entry function, you can simply invoke a method from the timed entry:
> When installing the timed entry, pass "self" (or whatever the id is) to
> DPSAddTimedEntry as your "user data."

Thanks for the comments, Ali.  The program isn't worth a whole lot, but
it does click buttons, use postscript to plot, and use timed entries,
so I thought it might be of interest to some of the folks out there.

I decompiled the interface mostly because I didn't want separate .nib
files floating around.  It doesn't seem like a good way to keep a
finished "product."  I agree that it isn't easy to modify once you do
that (plus, it's a hassle because of how badly the 0.8 IB botches the
interface decompilation).  Being able to include the .nib file in the
executable will be great.

However, it seems to me that once you decompile and start adding code
to the stubs that IB gives you, it's impossible to cleanly change the
interface except for cosmetics.  If I add any new methods or outlets
I have to edit in the appropriate code to the already decompiled source
since decompiling again will destroy my code.  But that's a relatively
minor hassle since I should do a good job of designing the interface
before I start coding anyway.

I did things the way I did in Spiro (with globals, etc.) because I don't
like having to use gratuitous function calls.  I suppose it shouldn't
bother me because they aren't nested in loops, but I'd be a lot happier
if objective-c methods and c function fit together better.  I was
SERIOUSLY disappointed to discover that I cannot use those nice inline
functions in math.h from objective c.  It seems a real shame that the
GNU c compiler allows direct access to the math coprocessor instructions
but the objective c prevents it.
								Roy
-- 
Roy J. Mongiovi     Systems Support Specialist     Office of Computing Services
Georgia Institute of Technology	  Atlanta, Georgia  30332-0275   (404) 894-4660
	uucp: ...!{allegra,amd,hplabs,ut-ngp}!gatech!prism!roy
	ARPA: roy@prism.gatech.edu