Physics sim by Achim Dahlhoff.

(see the GNU general public license for details of copyright)



Usage of the 2D physics simulation.

To use, include the header 'Struct2D.h'.

The main object defined there is a 'WORLD'. It contains all data and structures for a running simulation.

The simulation can contain bars and wheels (also called granules). The bars are connected at joints. Another possible object is a motor, which can apply a momentum to a wheel.

These objects are of the types 'JOINT', 'BAR', 'GRANULE' and 'MOTOR'. They are handled and maintained by the instance of the WORLD.

The object defines a number of methods for adding and removing objects. Do not try to create, remove, copy move or relink any of the objects present in the simulation. Use only the methods provided. Most operations require some relinking of other objects or hidden internal structures. Note that the constructors and destructors of the objects are declared private.

The arrays containing the objects are only in the 'public' part of the class declaration to allow quick read access to them. This is typically needed for graphics output. Some properties of the objects may also be changed directly, such as mass, momentum of inertia, damping values.

While the simulation does allocate some memory for running, those blocks of memory are only allocated the first time 'simulate()' is called. Until then, only memory for the objects is allocated.
Therefore, multiple instances of WORLD can be created for purposes like editor local storage, undo buffers, clipboard buffer, etc. Objects can be moved around using the copy oerator and the methods 'add_from()' and 'remove_selected'.
Note also that the local arrays for the objects are resized if they are too small, but they are never reduced. The only way to free memory allocated by an instance of WORLD is to deallocate the whole simulation object with the destructor.

Size of objects:

The simulation does set some limits to the size of objects.
The stability of the elastic simulation depends on the bars' lengths and masses. If the simulation vibrates or explodes, try reducing the elastic parameters E, D, I, or BD. If the simulation does not stop oscillating, try increasing D and BD. Besides this, a bar should not be shorter than 1.0 but can be any length. I have added a check on these parameters that will reduce E and D if a bar is too short, too light or too stiff. Currently, there is no way of checking if this kicks in other than using a breakpoint in there (method BAR::prepare).
Due to the inner workings of the collision calculation, the radius of a wheel should not exceed 1.5 and no part should be moving faster than 30 (length-units per second). Do not make structures too small. Use the sample 'testprog.cpp' as a reference.

Constants of the material:

E Elastic force relative to the relative change in length. ex: F = (l/l0)*E
D Damping force relative to the relative change rate in length ex: F = ((dl/dt)/l0)*D
I Elastic momentum at node relative to bending angle. ex: M=a*I
BD Damping momentum at node relative to angular velocity at node. ex: M=(da/dt)*BD
mass Mass per length

The elastic properties for collision and friction constants are defines and cannot be changed dynamically.

Texture and color:

The struct P_MATERIAL used to define the material when adding parts contains two fields called 'color' and 'textureID'. These are provided to be used with graphics output. They are stored in the elements BAR, GRANULE and TRIANGLE in fields with the same name. They are not used at all by the simulation and may be read and changed freely. textureID is a 8 byte integer so it can be used to hold (short) texture filenames.

Methods of WORLD:

WORLD& operator=(WORLD const &org);
copy operator. Makes a copy of the whole simulation. Can be used for cut/copy/paste operation.
unsigned int find_joint(float x,float y,float radius,int allow_create);
find or create a joint. If param allow_create is set to TRUE, a new joint will be created if not joint was found in the search radius.
unsigned int add_bar(float x0,float y0,float x1,float y1,const P_MATERIAL *mat,float connect_radius,int fix);
add a bar using coordinates. Joints will be added where needed. Returns index of new bar.
unsigned int add_bar(unsigned int idx1,unsigned int idx2,const P_MATERIAL *mat,int fix);
add a bar using indices of existing joints. Returns index of new bar.
unsigned int add_granule(float x,float y,const P_MATERIAL *mat,float radius,float mass,unsigned int bar_index);
add a wheel. If the last parameter 'bar_index' is a valid index to a bar, the wheel is attached to that bar. Returns index of new wheel.
unsigned int add_motor(unsigned int granule_index,float F0,float v_max);
add a motor to an existing wheel. Returns index of new motor.
unsigned int add_triangle_jt(unsigned int jt_index0,unsigned int jt_index1,unsigned int jt_index2,float tex_align,__int64 texID,unsigned int color);
add a triangle using indices of three joints. Returns index of new triangle. Set parameter tex_align to zero to set a texture that closely fits the triangle. Set tex_align to a positive value to use absolute panning texture coordinates. In this case, the tex_align value is the panning gridsize.
unsigned int add_triangle_bar(unsigned int br_index0,unsigned int br_index1,unsigned int br_index2,float tex_align,__int64 texID,unsigned int color);
Same as add_triangle_jt, but uses two or three bars as reference instead of joints. One bar index may be set to invalid.
int add_from(const WORLD *from_here,int selected_only,float dx,float dy,float merge_radius);
Copy a number of objects from one WORLD instance into an other. When 'selected_only' is true, only those objects with the selected flag set will be copied. The destination objects will be moved by the offsets 'dx' and 'dy'. Can be used for cut/copy/paste operation.
void remove_bar(unsigned int index);
remove a bar. Assiciated wheels will be removed. Will not remove Joints. See 'remove_unused_joints()'
void remove_granule(unsigned int index);
remove a wheel. If a motor is associated, it will be removed, too.
void remove_triangle(unsigned int index,int remove_joints);
remove a triangle.
void remove_motor(unsigned int index);
remove a motor. The wheel associated is not removed.
void remove_unused_joints();
remove all joints not connected to any bar. Should be used after removing bars.
void remove_selected();
remove all objects who's selected flag is set. Can be used for cut/copy/paste operation.
void remove_all();
clear all objects.
void set_relaxed();
Changes the length and angles of the joints and bars so they will be free of elastic stress in the current position.
unsigned int find_nearest(float x,float y,unsigned int *type,float max_dist);
find object nearest the point specified. The type defines what should be looked for:
bit 0 find joints
bit 1 find bars
bit 2 find wheels
The function will return the index of the object found and indicate the type in the type field. If nothing was found within the range max_dist, INVALID_INDEX is returned.
void transform(float dx1,float dy1,float rot_angle,float size_factor,float dx2,float dy2);
Move, rotate and stretch all objects in the WORLD. The first pair of offsets is applied before the rotate and scale, the second pair of offsets is added afterwards.
void set_flags(unsigned int type,unsigned int flag_mask,unsigned int flag_value,unsigned int toggle_mask,unsigned int *old_anded=0L,unsigned int *old_ored=0L);
Modify object's flags. The 'flag_mask' defines which flags are to be changed, the 'flag_value' contains the new value for those flags. The 'toggle_mask' is applied independently of the 'flag_mask' to flip bits. The last two params can be used to get the old state of all flags affected ored or anded.
void move_selected(float dx,float dy,int relaxed,float snap_dist);
Move all selected objects (flag OF_SELECTED set) the specified amount.
If a joint is moved with in close range of another (distance defined by snap_dist), the joints are merged into one. To disable the merging feature, set snap_dist to -1 .
float get_sim_timestep();
Return the simulation timestep. This is the time that passes in the simulation each time 'simulate()' is called.
void simulate();
Run the simulation one timestep.
int store(const char *filename);
Save the entire state to a file.
int store(int(*writefunc)(const char*,unsigned int,void*),void *data);
Save the entire state using an arbitrary data output function. (like the file)
int restore(const char *filename);
drop current state and load state from a file.
int restore(int(*readfunc)(char*,unsigned int,void*),void *data);
drop current state and restore state using an arbitrary data input function to get the data saved store().


by Achim Dahlhoff.