SIM_contact, for creating dynamic event scenarios #972 #936

This commit is contained in:
Penn, John M 047828115 2020-03-25 13:07:15 -05:00
parent 120b9c74c9
commit c8ccfff15e
16 changed files with 717 additions and 0 deletions

View File

@ -0,0 +1,10 @@
trick.real_time_enable()
trick.exec_set_software_frame(0.1)
trick.itimer_enable()
trick.exec_set_enable_freeze(True)
trick.exec_set_freeze_command(True)
simControlPanel = trick.SimControlPanel()
trick.add_external_application(simControlPanel)

View File

@ -0,0 +1,25 @@
#SIM_contact
SIM_contact simulates the motion, and the collisions of any arbitrary numbers of moving balls.
Each ball is defined by a position, velocity, radius, and mass.
Collisions are purely elastic. In addition to conserving momentum, they also conserve kinetic energy.
A graphics client allows you to see the balls in motion:
![Graphics CLient showing two balls](images/TwoBalls.png)
##Examples
The configuration for the above display is:
```
dyn.contact.nballs = 2
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
dyn.contact.balls[0] = trick.make_Ball(0.0, 0.0, 0.5, 0.0, 0.5, 1.0)
dyn.contact.balls[1] = trick.make_Ball(3.0, 0.0, -0.5, 0.0, 0.5, 1.0)
```
The initial state of the first ball is pos=<0.0, 0.0>, vel=<0.5,0.0>, radius=0.5, and mass=1.0.
The initial state of the second ball is pos=<3.0, 0.0>, vel=<-0.5,0.0>, radius=0.5, and mass=1.0.

View File

@ -0,0 +1,30 @@
execfile("Modified_data/realtime.py")
# Variable Server Data should be copied at top of frame.
trick.var_set_copy_mode(2)
dyn.contact.nballs = 2
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
dyn.contact.balls[0] = trick.make_Ball(0.0, 0.0, 0.5, 0.0, 0.5, 1.0)
dyn.contact.balls[1] = trick.make_Ball(3.0, 0.0, -0.5, 0.0, 0.5, 1.0)
dyn_integloop.getIntegrator(trick.Euler, 2*dyn.contact.nballs)
#==========================================
# Start the Satellite Graphics Client
#==========================================
varServerPort = trick.var_server_get_port();
BallDisplay_path = "models/graphics/dist/BallDisplay.jar"
if (os.path.isfile(BallDisplay_path)) :
BallDisplay_cmd = "java -jar " \
+ BallDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BallDisplay_cmd)
os.system( BallDisplay_cmd);
else :
print('==================================================================================')
print('BallDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,35 @@
execfile("Modified_data/realtime.py")
# Variable Server Data should be copied at top of frame.
trick.var_set_copy_mode(2)
dyn.contact.nballs = 5
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
# x, y, vx, vy, r, m
dyn.contact.balls[0] = trick.make_Ball( 0.0, 0.0, 0.67, 0.0, 2.0, 4.0)
dyn.contact.balls[1] = trick.make_Ball( 5.0, 0.0,-0.67, 0.0, 0.5, 1.0)
dyn.contact.balls[2] = trick.make_Ball( 7.0, 0.3,-0.67, 0.0, 0.5, 1.0)
dyn.contact.balls[3] = trick.make_Ball(-7.0, 0.0, 0.67, 0.1, 0.5, 1.0)
dyn.contact.balls[4] = trick.make_Ball( 9.0, 0.0,-0.67, 0.0, 0.5, 1.0)
dyn_integloop.getIntegrator(trick.Euler, 2*dyn.contact.nballs)
# dyn.contact.nbounds = 5
# dyn.contact.bounds = trick.TMM_declare_var_1d("Boundary*", dyn.contact.nbounds)
#==========================================
# Start the Satellite Graphics Client
#==========================================
varServerPort = trick.var_server_get_port();
BallDisplay_path = "models/graphics/dist/BallDisplay.jar"
if (os.path.isfile(BallDisplay_path)) :
BallDisplay_cmd = "java -jar " \
+ BallDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BallDisplay_cmd)
os.system( BallDisplay_cmd);
else :
print('==================================================================================')
print('BallDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,35 @@
execfile("Modified_data/realtime.py")
# Variable Server Data should be copied at top of frame.
trick.var_set_copy_mode(2)
dyn.contact.nballs = 5
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
# x, y, vx, vy, r
dyn.contact.balls[0] = trick.make_Ball(0.0, 0.0, 0.67, 0.0, 0.5, 5.0)
dyn.contact.balls[1] = trick.make_Ball(1.5, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[2] = trick.make_Ball(3.0, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[3] = trick.make_Ball(4.5, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[4] = trick.make_Ball(6.0, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn_integloop.getIntegrator(trick.Euler, 2*dyn.contact.nballs)
# dyn.contact.nbounds = 5
# dyn.contact.bounds = trick.TMM_declare_var_1d("Boundary*", dyn.contact.nbounds)
#==========================================
# Start the Satellite Graphics Client
#==========================================
varServerPort = trick.var_server_get_port();
BallDisplay_path = "models/graphics/dist/BallDisplay.jar"
if (os.path.isfile(BallDisplay_path)) :
BallDisplay_cmd = "java -jar " \
+ BallDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BallDisplay_cmd)
os.system( BallDisplay_cmd);
else :
print('==================================================================================')
print('BallDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,37 @@
execfile("Modified_data/realtime.py")
# Variable Server Data should be copied at top of frame.
trick.var_set_copy_mode(2)
dyn.contact.nballs = 7
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
# x, y, vx, vy, r
dyn.contact.balls[0] = trick.make_Ball(-5.0, 0.1, 0.5, 0.0, 0.5, 1.0)
dyn.contact.balls[1] = trick.make_Ball( 0.0, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[2] = trick.make_Ball( 1.2, 0.6, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[3] = trick.make_Ball( 1.2,-0.6, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[4] = trick.make_Ball( 2.4, 1.2, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[5] = trick.make_Ball( 2.4, 0.0, 0.0, 0.0, 0.5, 1.0)
dyn.contact.balls[6] = trick.make_Ball( 2.4,-1.2, 0.0, 0.0, 0.5, 1.0)
dyn_integloop.getIntegrator(trick.Euler, 2*dyn.contact.nballs)
# dyn.contact.nbounds = 5
# dyn.contact.bounds = trick.TMM_declare_var_1d("Boundary*", dyn.contact.nbounds)
#==========================================
# Start the Satellite Graphics Client
#==========================================
varServerPort = trick.var_server_get_port();
BallDisplay_path = "models/graphics/dist/BallDisplay.jar"
if (os.path.isfile(BallDisplay_path)) :
BallDisplay_cmd = "java -jar " \
+ BallDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BallDisplay_cmd)
os.system( BallDisplay_cmd);
else :
print('==================================================================================')
print('BallDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

View File

@ -0,0 +1,33 @@
execfile("Modified_data/realtime.py")
# Variable Server Data should be copied at top of frame.
trick.var_set_copy_mode(2)
dyn.contact.nballs = 3
dyn.contact.balls = trick.TMM_declare_var_1d("Ball*", dyn.contact.nballs)
# x, y, vx, vy, r, mass
dyn.contact.balls[0] = trick.make_Ball( -5.0, 2.0, 1.0, 0.0, 0.5, 1.0)
dyn.contact.balls[1] = trick.make_Ball( 5.0, 10.2, 0.0, 0.0, 10.0, 1000.0)
dyn.contact.balls[2] = trick.make_Ball( 5.0,-10.2, 0.0, 0.0, 10.0, 1000.0)
dyn_integloop.getIntegrator(trick.Euler, 2*dyn.contact.nballs)
# dyn.contact.nbounds = 5
# dyn.contact.bounds = trick.TMM_declare_var_1d("Boundary*", dyn.contact.nbounds)
#==========================================
# Start the Satellite Graphics Client
#==========================================
varServerPort = trick.var_server_get_port();
BallDisplay_path = "models/graphics/dist/BallDisplay.jar"
if (os.path.isfile(BallDisplay_path)) :
BallDisplay_cmd = "java -jar " \
+ BallDisplay_path \
+ " " + str(varServerPort) + " &" ;
print(BallDisplay_cmd)
os.system( BallDisplay_cmd);
else :
print('==================================================================================')
print('BallDisplay needs to be built. Please \"cd\" into ../models/graphics and type \"make\".')
print('==================================================================================')

23
trick_sims/SIM_contact/S_define Executable file
View File

@ -0,0 +1,23 @@
/************************************************************
PURPOSE:
( Simulation of Ball Contact. )
LIBRARY DEPENDENCIES:
((contact/src/Contact.cpp))
*************************************************************/
#include "sim_objects/default_trick_sys.sm"
##include "contact/include/Contact.hh"
class ContactSimObject : public Trick::SimObject {
public:
Contact contact;
ContactSimObject() {
("default_data") contact.default_data() ;
("initialization") contact.state_init() ;
("derivative") contact.state_deriv() ;
("integration") trick_ret = contact.state_integ() ;
("dynamic_event") contact.collision() ;
}
};
ContactSimObject dyn;
IntegLoop dyn_integloop(0.1) dyn;

View File

@ -0,0 +1,2 @@
TRICK_CFLAGS += -Imodels
TRICK_CXXFLAGS += -Imodels

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

View File

@ -0,0 +1,21 @@
/********************************* TRICK HEADER *******************************
PURPOSE: ( Simulate balls contacting boundaries. )
LIBRARY DEPENDENCY:
((Ball.o))
*******************************************************************************/
#ifndef _ball_hh_
#define _ball_hh_
class Ball {
public:
Ball(){}
double pos[2];
double vel[2];
double mass;
double radius;
Ball(double x, double y, double vx, double vy, double r, double m);
};
Ball* make_Ball(double x, double y, double vx, double vy, double r, double m);
#endif

View File

@ -0,0 +1,27 @@
/************************************************************************
PURPOSE: (Simulate a contact.)
LIBRARY DEPENDENCIES:
((contact/src/Contact.o))
**************************************************************************/
#ifndef _contact_hh_
#define _contact_hh_
#include "trick/regula_falsi.h"
#include "Ball.hh"
class Contact {
public:
Contact(){}
Ball ** balls;
unsigned int nballs;
REGULA_FALSI rf ;
void ballCollision(Ball &b1, Ball &b2);
int default_data();
int state_init();
int state_deriv();
int state_integ();
double collision();
};
#endif

View File

@ -0,0 +1,22 @@
/********************************* TRICK HEADER *******************************
PURPOSE: ( Simulate balls contacting boundaries. )
LIBRARY DEPENDENCY:
((Ball.o))
*******************************************************************************/
#include "../include/Ball.hh"
#include "trick/memorymanager_c_intf.h"
#include <new>
Ball::Ball(double x, double y, double vx, double vy, double r, double m) {
pos[0] = x;
pos[1] = y;
vel[0] = vx;
vel[1] = vy;
radius = r;
mass = m;
}
Ball* make_Ball(double x, double y, double vx, double vy, double r, double m) {
Ball* b = (Ball*)TMM_declare_var_s("Ball");
return (new (b) Ball(x,y,vx,vy,r,m));
}

View File

@ -0,0 +1,101 @@
/********************************* TRICK HEADER *******************************
PURPOSE: ( Simulate balls contacting boundaries. )
LIBRARY DEPENDENCY:
((Contact.o)
(Ball.o))
*******************************************************************************/
#include <math.h>
#include <iostream>
#include "trick/integrator_c_intf.h"
#include "../include/Contact.hh"
int Contact::default_data() {
balls = NULL;
nballs = 0;
return (0);
}
int Contact::state_init() {
rf.mode = Decreasing;
rf.error_tol = 0.001;
return (0);
}
int Contact::state_deriv() {
return(0);
}
int Contact::state_integ() {
int integration_step;
for (unsigned int ii = 0; ii < nballs; ii++) {
// Be sure to fix load_indexed_state() so it only loads @ intermediate_step == 0
load_indexed_state( 2*ii, balls[ii]->pos[0]);
load_indexed_state( 2*ii+1, balls[ii]->pos[1]);
}
for (unsigned int ii = 0; ii < nballs; ii++) {
load_indexed_deriv( 2*ii, balls[ii]->vel[0]);
load_indexed_deriv( 2*ii+1, balls[ii]->vel[1]);
}
integration_step = integrate();
for (unsigned int ii = 0; ii < nballs; ii++) {
balls[ii]->pos[0] = unload_indexed_state( 2*ii );
balls[ii]->pos[1] = unload_indexed_state( 2*ii+1 );
}
return(integration_step);
}
void Contact::ballCollision(Ball &b1, Ball &b2) {
double unorm[2];
double utang[2];
if ((b1.mass > 0.0) && (b2.mass > 0.0)) {
unorm[0] = ( b2.pos[0] - b1.pos[0] ) / ( b2.radius + b1.radius );
unorm[1] = ( b2.pos[1] - b1.pos[1] ) / ( b2.radius + b1.radius );
utang[0] = -unorm[1];
utang[1] = unorm[0];
double b1vn = unorm[0] * b1.vel[0] + unorm[1] * b1.vel[1];
double b1vt = utang[0] * b1.vel[0] + utang[1] * b1.vel[1];
double b2vn = unorm[0] * b2.vel[0] + unorm[1] * b2.vel[1];
double b2vt = utang[0] * b2.vel[0] + utang[1] * b2.vel[1];
double b1vtAfter = b1vt;
double b2vtAfter = b2vt;
double b1vnAfter = ( b1vn * (b1.mass - b2.mass) + 2.0 * b2.mass * b2vn ) / ( b1.mass + b2.mass );
double b2vnAfter = ( b2vn * (b2.mass - b1.mass) + 2.0 * b1.mass * b1vn ) / ( b1.mass + b2.mass );
b1.vel[0] = b1vnAfter * unorm[0] + b1vtAfter * utang[0];
b1.vel[1] = b1vnAfter * unorm[1] + b1vtAfter * utang[1];
b2.vel[0] = b2vnAfter * unorm[0] + b2vtAfter * utang[0];
b2.vel[1] = b2vnAfter * unorm[1] + b2vtAfter * utang[1];
} else {
std::cout << "ERROR: Balls must have positive mass." << std::endl;
}
}
double Contact::collision() {
double tgo ; /* time-to-go */
double now ; /* current integration time. */
int first, second;
double min_dist = 1000000.0;
for (unsigned int ii = 0; ii < nballs; ii++) {
for (unsigned int jj = ii+1; jj < nballs; jj++) {
double xdiff = balls[ii]->pos[0] - balls[jj]->pos[0];
double ydiff = balls[ii]->pos[1] - balls[jj]->pos[1];
double dist = sqrt( xdiff * xdiff + ydiff * ydiff) - (balls[ii]->radius + balls[jj]->radius);
if (dist < min_dist) {
min_dist = dist;
first = ii;
second = jj;
}
}
}
rf.error = min_dist;
now = get_integ_time() ; /* Get the current integration time */
tgo = regula_falsi( now, &rf ) ; /* Estimate remaining integration time. */
if (tgo == 0.0) { /* If we are at the event, it's action time! */
now = get_integ_time() ;
ballCollision(*balls[first], *balls[second]);
std::cout << "Ball["<<first<<"] and Ball["<<second<<"] collide at t = "<<now<<"." << std::endl;
reset_regula_falsi( now, &rf );
}
return (tgo) ;
}

View File

@ -0,0 +1,36 @@
SHELL = /bin/sh
PROJECT_NAME = BallDisplay
SRC_DIR = src
BUILD_DIR = build
CLASSES_DIR = $(BUILD_DIR)/classes
JAR_DIR = dist
MAIN_CLASS = BallDisplay
all: jar
clean:
rm -rf $(BUILD_DIR)
rm -f manifest
spotless: clean
rm -rf dist
$(CLASSES_DIR):
@ mkdir -p $(CLASSES_DIR)
compile: | $(CLASSES_DIR)
javac -sourcepath $(SRC_DIR) -d $(CLASSES_DIR) $(SRC_DIR)/BallDisplay.java
manifest:
@ echo "Main-Class: $(MAIN_CLASS)" > $@
$(JAR_DIR):
@ mkdir -p $(JAR_DIR)
jar: compile manifest | $(JAR_DIR)
jar cvfm $(JAR_DIR)/$(PROJECT_NAME).jar manifest -C $(CLASSES_DIR) .
@ echo "-------------------------------------------------------------------------------"
@ echo " BUILD COMPLETE"
@ echo "The Java jar file (the Java Executable) is located at: $(JAR_DIR)/$(PROJECT_NAME).jar"
@ echo "-------------------------------------------------------------------------------"

View File

@ -0,0 +1,280 @@
/*
* Trick
* 2020 (c) National Aeronautics and Space Administration (NASA)
*
* @author penn
*/
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Graphics;
import java.awt.RenderingHints;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Ball {
public Color color;
public double x;
public double y;
public double radius;
public int identity;
public Ball (int id) {
identity = id;
x = 0.0;
y = 0.0;
radius = 0.5;
if (id == 0) {
color = Color.GREEN;
} else {
color = Color.LIGHT_GRAY;
}
}
}
class RangeView extends JPanel {
private int scale;
private Color backGroundColor;
// Origin of world coordinates in jpanel coordinates.
private int worldOriginX;
private int worldOriginY;
public Ball[] balls;
/**
* Class constructor.
*/
public RangeView( int mapScale, int numberOfBalls) {
setScale(mapScale);
backGroundColor = Color.WHITE;
balls = new Ball[numberOfBalls];
for (int ii=0 ; ii<numberOfBalls ; ii++) {
balls[ii] = new Ball(ii);
}
}
public void setScale (int mapScale) {
if (mapScale < 4) {
scale = 4;
} else if (mapScale > 128) {
scale = 128;
} else {
scale = mapScale;
}
repaint();
}
public int getScale() {
return scale;
}
public void drawCenteredCircle(Graphics2D g, int x, int y, int d) {
x = x-(d/2);
y = y-(d/2);
g.fillOval(x,y,d,d);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
RenderingHints rh = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
int width = getWidth();
int height = getHeight();
worldOriginX = (width/2);
worldOriginY = (height/2);
// Draw Background
g2d.setPaint(backGroundColor);
g2d.fillRect(0, 0, width, height);
// Draw balls
for (int ii = 0; ii < balls.length ; ii++) {
g2d.setPaint(balls[ii].color);
int bx = (int)(worldOriginX + scale * balls[ii].x);
int by = (int)(worldOriginY - scale * balls[ii].y);
drawCenteredCircle(g2d, bx, by, (int)(scale * 2 * balls[ii].radius));
g2d.setPaint(Color.BLACK);
g2d.drawString ( String.format("%d",ii), bx,by);
}
g2d.drawString ( String.format("SCALE: %d pixels/meter",scale), 20,20);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public class BallDisplay extends JFrame {
private RangeView rangeView;
private BufferedReader in;
private DataOutputStream out;
private JPanel panelGroup0;
private JPanel panelGroup1;
public BallDisplay() {
rangeView = null;
setTitle("Lander Range");
setSize(800, 500);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setFocusable(true);
}
public void connectToServer(String host, int port ) throws IOException {
Socket socket = new Socket(host, port);
in = new BufferedReader( new InputStreamReader( socket.getInputStream()));
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
}
public void setRangeView (RangeView arena) {
rangeView = arena;
panelGroup1 = new JPanel();
panelGroup1.setLayout(new BoxLayout(panelGroup1, BoxLayout.X_AXIS));
panelGroup1.add(rangeView);
add(panelGroup1);
}
private static void printHelpText() {
System.out.println(
"----------------------------------------------------------------------\n"
+ "usage: java jar BallDisplay.jar <port-number>\n"
+ "----------------------------------------------------------------------\n"
);
}
public static void main(String[] args) throws IOException, InterruptedException {
String host = "localHost";
int port = 0;
boolean boom = false;
// Handle program arguments.
int ii = 0;
while (ii < args.length) {
switch (args[ii]) {
case "-help" :
case "--help" : {
printHelpText();
System.exit(0);
} break;
default : {
port = (Integer.parseInt(args[ii]));
} break;
}
++ii;
}
boolean go = true;
double dt = 0.100; // Time between updates (seconds).
int mapScale = 16 ; // pixels per meter.
int nballs = 7;
if (port == 0) {
System.out.println("No variable server port specified.");
printHelpText();
System.exit(0);
}
BallDisplay ballDisplay = new BallDisplay();
// Connect to the Trick simulation's variable server.
System.out.println("Connecting to: " + host + ":" + port);
ballDisplay.connectToServer(host, port);
ballDisplay.out.writeBytes("trick.var_set_client_tag(\"BallDisplay\") \n");
ballDisplay.out.flush();
// Get the number of balls.
ballDisplay.out.writeBytes(
"trick.var_add(\"dyn.contact.nballs\")\n" +
"trick.var_send() \n" +
"trick.var_clear() \n");
ballDisplay.out.flush();
try {
String line;
String field[];
line = ballDisplay.in.readLine();
field = line.split("\t");
nballs = Integer.parseInt( field[1]);
} catch (IOException | NullPointerException e ) {
go = false;
}
RangeView rangeView = new RangeView(mapScale, nballs);
ballDisplay.setRangeView(rangeView);
ballDisplay.setVisible(true);
// Get the Radii of the balls.
for ( ii = 0; ii < nballs; ii ++) {
ballDisplay.out.writeBytes( String.format("trick.var_add(\"dyn.contact.balls[%d][0].radius\")\n", ii));
}
ballDisplay.out.flush();
ballDisplay.out.writeBytes(
"trick.var_send() \n" +
"trick.var_clear() \n");
ballDisplay.out.flush();
try {
String line;
String field[];
line = ballDisplay.in.readLine();
field = line.split("\t");
for ( ii=0; ii < nballs; ii++) {
rangeView.balls[ii].radius = Double.parseDouble( field[ii+1]);
}
} catch (IOException | NullPointerException e ) {
go = false;
}
// Get the Positions of the balls, and update the display, periodically.
ballDisplay.out.writeBytes( "trick.var_pause() \n");
for ( ii = 0; ii < nballs; ii ++) {
ballDisplay.out.writeBytes(
String.format("trick.var_add(\"dyn.contact.balls[%d][0].pos[0]\")\n", ii)
+ String.format("trick.var_add(\"dyn.contact.balls[%d][0].pos[1]\")\n", ii)
);
}
ballDisplay.out.writeBytes("trick.var_ascii() \n" +
String.format("trick.var_cycle(%.3f)\n", dt) +
"trick.var_unpause() \n" );
ballDisplay.out.flush();
while (go) {
try {
String line;
String field[];
line = ballDisplay.in.readLine();
// System.out.println("Sim->Client:" + line);
field = line.split("\t");
for ( ii=0; ii < nballs; ii++) {
rangeView.balls[ii].x = Double.parseDouble( field[2*ii+1]);
rangeView.balls[ii].y = Double.parseDouble( field[2*ii+2]);
}
} catch (IOException | NullPointerException e ) {
go = false;
}
// Update the scene.
rangeView.repaint();
} // while
} // main
} // class