mirror of
https://github.com/StevenMcLeod/pendulum68k.git
synced 2024-11-28 06:49:53 +00:00
Add files via upload
This commit is contained in:
parent
e96555b3ec
commit
8e530b69a0
30
README
Normal file
30
README
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
Pendulum68k
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Note: Some files in this repository use Classic Mac OS line-endings ('\r').
|
||||||
|
|
||||||
|
About
|
||||||
|
-----
|
||||||
|
|
||||||
|
This program is a pendulum simulator written for Classic Mac OS. It was inspired by this 3Blue1Brown video: (https://www.youtube.com/watch?v=p_di4Zn4wz4). It has been tested in System 4.2 / 6.0.8, but should work for any version of Classic Mac OS that can run 68k applications.
|
||||||
|
|
||||||
|
|
||||||
|
Commands
|
||||||
|
--------
|
||||||
|
|
||||||
|
Simulation parameters can be changed through Edit > Preferences or (Cmd-,). From there, starting angle, angular velocity, pendulum resistance, pendulum length, and gravity can be changed. Values are input in floating-point format. To multiply the input value by pi, suffix the value with "pi".
|
||||||
|
The pendulum simulation can be restarted through File > Restart or (Cmd-R). This is required to activate the changes made in preferences.
|
||||||
|
The program can be quit through File > Quit or (Cmd-Q).
|
||||||
|
|
||||||
|
Building & Installing
|
||||||
|
----------
|
||||||
|
|
||||||
|
To install the application, simply decode the MacBinary file "pendulum.sit.bin", then unstuff the Stuffit Archive "pendulum.sit". The application along with the source files are included in this archive. Mini vMac is recommended to emulate this program on a modern computer (https://www.gryphel.com/c/minivmac/).
|
||||||
|
|
||||||
|
To build the application, compile the source files with any C compiler, and link with the ANSI C library. After an application is created, copy the necessary resource files from "pendulum.π.rsrc" with a program such as ResEdit. This resource file contains the dialogs used in the program as well as Finder settings. This program was compiled with ThinkC 5, and includes the project file "pendulum.π".
|
||||||
|
|
||||||
|
Credits
|
||||||
|
-------
|
||||||
|
|
||||||
|
© 2019, Steven Mcleod. All rights reserved.
|
||||||
|
https://github.com/StevenMcleod/pendulum68k
|
1
common.h
Normal file
1
common.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#ifndef COMMON_H
#define COMMON_H
struct PSTRUCT {
Byte len;
Byte str[];
};
#define HIWORD(x) (((x) & 0xFFFF0000) >> 16)
#define LOWORD(x) ((x) & 0x0000FFFF)
#define TO_PSTRUCT(x) (* (struct PSTRUCT *) (x))
#define PLEN(x) (TO_PSTRUCT(x).len)
#define PSTR(x) (TO_PSTRUCT(x).str)
#ifndef M_PI
#define M_PI 3.1415926535897932
#endif
#endif
|
1
main.c
Normal file
1
main.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include <math.h>
#include "common.h"
#include "pendulum.h"
#include "pendMenu.h"
#include "pendDialog.h"
#define SET_RECT(R,t,l,b,r) \
do { (R).top = (t); (R).left = (l); (R).bottom = (b); (R).right = (r); } while(0)
#define THETA (M_PI / 3)
#define D_THETA (0)
#define MU 0.1
#define LEN 2
#define GRAV 9.8
extern Boolean running = true;
extern Boolean resetting = true;
extern Boolean paused = false;
extern Boolean forceRedraw = false;
extern struct dialog_prefs_t pendPrefs = {
THETA, D_THETA, MU, LEN, GRAV
};
Rect WINDOW_RECT = {50, 50, 250, 250};
char WINDOW_NAME[] = "\pPendulum";
WindowPtr pendWindow;
struct pendulum_t thePend;
static Rect dragRect; /* const */
static void InitMacintosh(void);
static void ExitError(void);
static long HandleEvent(int eventMask);
static void HandleMouseDown(EventRecord *theEvent);
static void WaitTicks(unsigned qty);
static void WindowCreate(void);
static void WindowDrawContent(WindowPtr theWin, double angle, Boolean force);
static int iround(double n) {
double ipart, fpart;
int sign;
fpart = modf(n, &ipart);
sign = (n >= 0 ? 1 : -1);
if(fabs(fpart) >= 0.5) return (int)(ipart) + sign;
return (int) ipart;
}
void InitMacintosh(void) {
MaxApplZone();
InitGraf(&thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(0L);
FlushEvents(everyEvent, 0);
InitCursor();
}
void ExitError(void) {
SysBeep(1);
ExitToShell();
}
void WaitTicks(unsigned qty) {
static long lastTick = 0;
if(qty == 0) return;
while(--qty) {
while(lastTick == Ticks) {
HandleEvent(updateMask);
HandleEvent(everyEvent);
}
}
lastTick = Ticks;
}
long HandleEvent(int eventMask) {
int res;
EventRecord theEvent;
HiliteMenu(0);
SystemTask(); /* Handle desk accessories */
if (!GetNextEvent(eventMask, &theEvent)) return;
switch (theEvent.what) {
case mouseDown:
HandleMouseDown(&theEvent);
break;
case keyDown:
if(theEvent.modifiers & cmdKey) {
MenuEvent(MenuKey(theEvent.message & 0xFF));
}
/* FALLTHROUGH */
case autoKey:
break;
case activateEvt:
if(theEvent.modifiers & activeFlag) {
MenuEditMode(false);
paused = 0;
} else {
MenuEditMode(true);
paused = 1;
}
break;
case updateEvt:
WindowDrawContent((WindowPtr) theEvent.message,
thePend.theta, paused || forceRedraw);
forceRedraw = false;
break;
}
}
void HandleMouseDown(EventRecord *theEvent) {
WindowPtr theWindow;
int windowCode = FindWindow(theEvent->where, &theWindow);
switch (windowCode) {
case inSysWindow:
SystemClick (theEvent, theWindow);
break;
case inDrag:
DragWindow(theWindow, theEvent->where, &dragRect);
break;
case inMenuBar:
MenuEvent(MenuSelect(theEvent->where));
break;
case inContent:
if(theWindow != FrontWindow()) {
SelectWindow(theWindow);
}
break;
case inGoAway:
if(TrackGoAway(theWindow, theEvent->where)) {
HideWindow(theWindow);
running = false;
}
break;
}
}
void WindowCreate(void) {
pendWindow = NewWindow(0L, &WINDOW_RECT, WINDOW_NAME,
true, noGrowDocProc, (WindowPtr) -1L, true, 0);
if(!pendWindow) ExitError();
}
void WindowDrawContent(WindowPtr theWin, double angle, Boolean force) {
static Point lastPoint = {0, 0};
Rect BallRect = {0, 0, 25, 25};
Point lineEnd;
lineEnd.h = 100 + iround(75.0 * sin(angle));
lineEnd.v = 100 + iround(75.0 * cos(angle));
if(!force &&
lastPoint.h == lineEnd.h && lastPoint.v == lineEnd.v) {
return;
}
/*HideCursor();*/
BeginUpdate(theWin);
EraseRect(&theWin->portRect);
MoveTo(100, 100);
LineTo(lineEnd.h, lineEnd.v);
OffsetRect(&BallRect, lineEnd.h - 12, lineEnd.v - 12);
PaintRoundRect(&BallRect, 25, 25);
EndUpdate(theWin);
/*ShowCursor();*/
lastPoint.h = lineEnd.h;
lastPoint.v = lineEnd.v;
}
int main(void) {
InitMacintosh();
WindowCreate();
MenuCreate();
SetPort(pendWindow);
SET_RECT(dragRect, 4, 24, screenBits.bounds.right-4,
screenBits.bounds.bottom-4);
thePend.timestep = 1.0/20.0;
while(running) {
if(resetting) {
thePend.theta = pendPrefs.theta;
thePend.d_theta = pendPrefs.d_theta;
thePend.mu = pendPrefs.mu;
thePend.gl = pendPrefs.grav / pendPrefs.len;
resetting = false;
}
if(!paused) {
InvalRect(&pendWindow->portRect);
WaitTicks(4);
pendulum_iter(&thePend);
} else {
HandleEvent(everyEvent);
}
}
}
|
1
pendDialog.c
Normal file
1
pendDialog.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "pendDialog.h"
#include <errno.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#define DLOG_PREFS 128
#define DLOG_ABOUT 129
#define PREFS_EDIT_START 3
#define PREFS_EDIT_END 7
enum {
PREFS_THETA=0,
PREFS_DTHETA,
PREFS_MU,
PREFS_LEN,
PREFS_GRAV
};
extern Boolean forceRedraw;
static Boolean valid_input(double *n, int no) {
if( *n == HUGE_VAL ||
*n == -HUGE_VAL ||
/*isnan(*n) ||*/ /* Cannot detect NANs */
(*n == 0.0 && errno == ERANGE)) {
return false;
}
switch(no) {
case PREFS_THETA: {
*n = fmod(*n, 2*M_PI);
if(*n > M_PI) *n -= 2*M_PI;
} return true;
case PREFS_LEN:
case PREFS_GRAV:
if(*n == 0.0) return false;
return true;
}
return true;
}
static Boolean parse_pi(char *str) {
return ((tolower(str[0]) == 'p') &&
(tolower(str[1]) == 'i') &&
(str[2] == '\0'));
}
static pascal Boolean aboutFilter(DialogPtr theDialog,
EventRecord *theEvent,
int *itemHit) {
int windowCode;
WindowPtr theWindow;
if(theEvent->what != mouseDown)
return false;
windowCode = FindWindow(theEvent->where, &theWindow);
if(windowCode != inContent || theWindow != theDialog)
return false;
return true;
}
void DialogAbout() {
int itemHit;
DialogPtr dlog = GetNewDialog(DLOG_ABOUT, NULL, (WindowPtr) -1L);
ModalDialog(&aboutFilter, &itemHit);
DisposDialog(dlog);
forceRedraw = true;
}
void DialogPrefs(struct dialog_prefs_t *p) {
union prefs_choose {
struct dialog_prefs_t *rec;
double *arr; /* Size 5 */
};
union prefs_choose args;
double tmpargs[5];
StringPtr dblText;
int itemHit, itemno;
Boolean valid_settings = false;
DialogPtr dlog;
dblText = (StringPtr) NewPtr(sizeof(Str255));
if(!dblText) return;
dlog = GetNewDialog(DLOG_PREFS, NULL, (WindowPtr) -1L);
if(!dlog) goto err_dlog;
args.rec = p;
/* Convert p's fields to EditText fields */
for(itemno = PREFS_EDIT_START; itemno <= PREFS_EDIT_END; ++itemno) {
int arrval = itemno - PREFS_EDIT_START;
int itemType;
ControlHandle itemHandle;
Rect itemBox;
PLEN(dblText) = sprintf((char *) PSTR(dblText), "%.9g", args.arr[arrval]);
GetDItem(dlog, itemno, &itemType, &itemHandle, &itemBox);
SetIText(itemHandle, dblText);
}
while(!valid_settings) {
ModalDialog(NULL, &itemHit);
if(itemHit == cancel) break;
valid_settings = true;
for(itemno = PREFS_EDIT_START; itemno <= PREFS_EDIT_END; ++itemno) {
int arrval = itemno - PREFS_EDIT_START;
int itemType;
ControlHandle itemHandle;
Rect itemBox;
char *endpos;
/* Convert EditText fields to p fields */
GetDItem(dlog, itemno, &itemType, &itemHandle, &itemBox);
GetIText(itemHandle, dblText);
/* If a field was left empty */
if(PLEN(dblText) == 0) {
valid_settings = false;
break;
}
/* Convert to Cstring */
if(PLEN(dblText) == 255)
PSTR(dblText)[255] = '\0';
else
PSTR(dblText)[PLEN(dblText)] = '\0';
/* Convert to double */
tmpargs[arrval] = strtod((char *) PSTR(dblText), &endpos);
if(endpos == (char *) PSTR(dblText)) {
/* Input: "pi" */
if(parse_pi(endpos)) {
tmpargs[arrval] = M_PI;
} else {
valid_settings = false;
break;
}
} else if(*endpos != '\0') {
/* Input: "xx.xxpi" */
if(parse_pi(endpos)) {
tmpargs[arrval] *= M_PI;
} else {
valid_settings = false;
break;
}
}
/* Modifies / Verifies input */
if(!valid_input(&tmpargs[arrval], arrval)) {
valid_settings = false;
break;
}
}
/* Error condition */
if(!valid_settings) {
SelIText(dlog, itemno, 0, 32767);
SysBeep(0);
}
}
if(itemHit == ok)
memcpy(p, tmpargs, sizeof(tmpargs));
DisposDialog(dlog);
err_dlog:
DisposPtr(dblText);
forceRedraw = true;
}
|
1
pendDialog.h
Normal file
1
pendDialog.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#ifndef PEND_DIALOG_H
#define PEND_DIALOG_H
struct dialog_prefs_t {
double theta, d_theta, mu, len, grav;
};
void DialogAbout();
void DialogPrefs(struct dialog_prefs_t *p);
#endif /* PEND_DIALOG_H */
|
1
pendMenu.c
Normal file
1
pendMenu.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "pendMenu.h"
#include "common.h"
#include "pendDialog.h"
#define GET_MENU(mid) menus[(mid)-1]
extern struct dialog_prefs_t pendPrefs;
enum {
appleID=1,
fileID,
editID,
endID
};
enum {
apple_AboutID=1,
apple_EndID
};
enum {
file_RandID=1,
file_RstID=3,
file_QuitID
};
enum {
edit_UndoID=1,
edit_CutID=3,
edit_CopyID,
edit_PasteID,
edit_ClearID,
edit_PrefsID=8
};
extern Boolean resetting, running;
static MenuHandle menus[endID-1];
static void DoAboutMenu(int item);
static void DoFileMenu(int item);
static void DoEditMenu(int item);
void MenuCreate(void) {
int i;
menus[0] = NewMenu(appleID, "\p\024");
menus[1] = NewMenu(fileID, "\pFile");
menus[2] = NewMenu(editID, "\pEdit");
for(i = appleID; i < endID; ++i) {
InsertMenu(GET_MENU(i), 0);
}
DrawMenuBar();
AppendMenu(GET_MENU(appleID), "\pAbout Pendulum...;(-");
AddResMenu(GET_MENU(appleID), 'DRVR');
AppendMenu(GET_MENU(fileID), "\p(Random;(-;Restart/R;Quit/Q");
AppendMenu(GET_MENU(editID),
"\p(Undo/Z;(-;(Cut/X;(Copy/C;(Paste/V;(Clear;(-;Preferences.../,");
}
void MenuEvent(long menuItem) {
GrafPtr oldPort;
Str255 name;
int menuID = HIWORD(menuItem);
int itemID = LOWORD(menuItem);
if(!menuID) return;
switch(menuID) {
case appleID: {
if(itemID < apple_EndID) {
DoAboutMenu(itemID);
} else {
/*StringPtr name = (StringPtr) NewPtr(sizeof(Str255));*/
GetPort(&oldPort);
GetItem(GET_MENU(appleID), itemID, name);
OpenDeskAcc(name);
SetPort(oldPort);
/*DisposPtr(name);*/
}
} break;
case fileID:
DoFileMenu(itemID);
break;
case editID:
DoEditMenu(itemID);
break;
}
}
void MenuEditMode(Boolean active) {
if(active) {
EnableItem(GET_MENU(editID), edit_UndoID);
EnableItem(GET_MENU(editID), edit_CutID);
EnableItem(GET_MENU(editID), edit_CopyID);
EnableItem(GET_MENU(editID), edit_PasteID);
EnableItem(GET_MENU(editID), edit_ClearID);
} else {
DisableItem(GET_MENU(editID), edit_UndoID);
DisableItem(GET_MENU(editID), edit_CutID);
DisableItem(GET_MENU(editID), edit_CopyID);
DisableItem(GET_MENU(editID), edit_PasteID);
DisableItem(GET_MENU(editID), edit_ClearID);
}
}
void DoAboutMenu(int item) {
switch(item) {
case apple_AboutID:
DialogAbout();
break;
}
}
void DoFileMenu(int item) {
switch(item) {
case file_RstID:
resetting = true;
break;
case file_QuitID:
running = false;
break;
}
}
void DoEditMenu(int item) {
if(item <= edit_ClearID) {
SystemEdit(item - 1);
} else {
switch(item) {
case edit_PrefsID:
DialogPrefs(&pendPrefs);
break;
}
}
}
|
1
pendMenu.h
Normal file
1
pendMenu.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#ifndef PEND_MENU_H
#define PEND_MENU_H
void MenuCreate(void);
void MenuEvent(long menuItem);
void MenuEditMode(Boolean active);
#endif /* PEND_MENU_H */
|
1
pendulum.c
Normal file
1
pendulum.c
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "pendulum.h"
#include <math.h>
double pendulum_iter(struct pendulum_t *p) {
double dd_theta = -(p->mu * p->d_theta) - (p->gl * sin(p->theta));
p->d_theta += dd_theta * p->timestep;
p->theta += p->d_theta * p->timestep;
return p->theta;
}
|
1
pendulum.h
Normal file
1
pendulum.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#ifndef PENDULUM_H
#define PENDULUM_H
struct pendulum_t {
double theta, d_theta;
double mu, gl, timestep;
};
double pendulum_iter(struct pendulum_t *p);
#endif /* PENDULUM_H */
|
BIN
pendulum.sit.bin
Normal file
BIN
pendulum.sit.bin
Normal file
Binary file not shown.
BIN
pendulum.π.rsrc.bin
Normal file
BIN
pendulum.π.rsrc.bin
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user