import java.applet.Applet; import java.awt.*; import java.awt.event.*; /* APPLET TO STUDY WAVES ON A STRING (MODELED AS POINT MASSES AND ELASTIC BANDS) AUTHOR: JEFF JENSEN ORIGINALLY BASED ON nm5x.java BY JACK ORD [ http://www.kw.igs.net/~jackord/ ] THE GENERAL EQUATION FOR EACH MASS POINT IS: m[i]* y"[i] = -k*( y[i] - y[i+1]) + -k*( y[i] - y[i-1]) WE HAVE MASSES AT NODES 1,2,...,n; 0 AND n+1 ARE FIXED BOUNDARY PTS. m[i] = 1 FOR ALL i ON LEFT HALF OF STRING, AND THEN m[i] = 2.0 SAY, ON RIGHT HALF STRING. AT i=n ( y[n+1] = 0 ): m * y"[n] = k*( 0 - 2*y[n] + y[n-1] ) [STILL 2 ELASTIC BANDS ON MASS PT]. FOR A FREE END PT, THERE IS NO RIGHT BAND: m[n]*y"[n] = -k*( y[n] - y[n-1] ) */ public class JeffString extends Applet implements ActionListener, Runnable { int kk = 0; String b1s = " Run "; Button b1 = new Button(b1s); String b2s = "Step"; Button b2 = new Button(b2s); String b3s = "Freeze"; Button b3 = new Button(b3s); String b4s = "Reset"; Button b4 = new Button(b4s); Choice chSpeed = new Choice(); Choice chMP = new Choice(); Choice chSideF = new Choice(); Choice chClear = new Choice(); Choice chNum = new Choice(); Choice chRedClosest = new Choice(); ChoiceState cs = new ChoiceState(); Image bufferImage; Graphics bufferGraphics; Thread animatorThread; boolean animation_is_set_to_stop; boolean first_time_painting = true; String fmt = "00.00"; java.text.DecimalFormat dFmt = new java.text.DecimalFormat(fmt); String fmt1 = "0.00"; java.text.DecimalFormat dFmt1 = new java.text.DecimalFormat(fmt1); // final int X_LENGTH = 500; final int Y_LENGTH = 540; final int TOP_MARGIN_LEN = 55; final int TOP_MARGIN_YCOORD = TOP_MARGIN_LEN; final int Y_DIVIDER_COORD = 290; //320; final int X_DIVIDER = 384; //32*12 OR 64*6 int numSegmentsVisible = 0; //JUST SO IT WILL BE RESET IN REINITIALIZE() int totalNumPts = 33; int numMassPtsVisible; int firstVisiblePtIndex = 0; int stringSegmentLength = 12; // 384/32 = 12 int lastLeftNodeIndex = 20; int watchedNode; int relativeLastLeftNodeIndex = lastLeftNodeIndex - firstVisiblePtIndex; int n; int j; //GLOBAL PAINTING COUNTER AND A MEASURE OF TIME int maxNumberOfSteps = 20*totalNumPts + 5; final int stringAxis = TOP_MARGIN_YCOORD + 69; final int velocityAxis = stringAxis + 70; final int forceAxis = velocityAxis + 50; int logicalNumPts; int pict2XleftEdge = X_DIVIDER - 220; int pict2TopY = Y_DIVIDER_COORD + 24; //4; int pict2Width = 154; int pict2Height = 168; int pict2Ycenter = pict2TopY + pict2Height/2 + 30; int pict2Xold, pict2Xnew; int pict2YoldF, pict2YnewF, p2rightYold, p2rightYnew, p2leftYold, p2leftYnew; int pict2YoldY, pict2YnewY, pict2YoldV, pict2YnewV; double extraDrawTime = 14.0; boolean first_time_Pict2 = true; boolean cycleInProgressP2, firstCycleEndedP2, writeExtremaP2; boolean backLiftInProgressP2; int fOldP2, fMaxP2, fMinP2, fBLMaxP2; double vOldP2, vMaxP2, vMinP2, vBLMaxP2; double tP2Start = 0.0, tEndIL, tEndPD, tEndBL; int[] xx; int[] yy; int[] vv; int[] ff; double[] y; double[] v; double[] f; double[][] s; //FOR ANALYTICAL COMPUTATION double[] w; double[] a; double[] b; int[] xst1; //SIMPLE TRANSLATION CALCULATION int[] yst1; int[] vst1; int[] fst1; double[] ya; //SIMPLE TRANS 2 double[] va; double[] fa; double[] yb; double[] vb; double[] fb; boolean boundaryPointIsFree = false; double totalEnergy; //double waveSpeed; double t; //TIME PARAMETER OF INTEGRATION -- REALLY FICTIOUS, SINCE //ODE IS HAS NO EXPLICIT T DEPENDENCE; t = 0.1*(j-2) double tStart = 0.0; //VARIABLES FOR STOERMER double totalStep = 0.1; int numSubsteps = 10; int neqns; int k, nn; boolean first_time_stoermer = true; double[] ytemp; double[] vtemp; //double h = totalStep / (double)numSubsteps; double h = 0.01; double halfh = 0.5*h; double h2 = h*h; double piOver8 = Math.PI / 8.0; double ccc = 13.0 * Math.PI / 4.0; double arg, COS, SIN; double amplitude = 40.0; double ws = 1.0; //WAVESPEED = PHASE VELOCITY final int halfPulseWidth = 4; final int centerOfPulse = 6; //---------------------------------------------------------------------------- public void init() { setLayout(new FlowLayout()); setBackground(Color.white); chMP.addItem("1 pulse; homogeneous string"); chMP.addItem("1 pulse; heavier right string 1:4"); chMP.addItem("1 pulse; heavier right string 1:40"); chMP.addItem("1 pulse; lighter right string 1:.25"); chMP.addItem("1 pulse; free end point; mass 1:1"); chMP.addItem("1 pulse; free end point; mass 1:4"); chMP.addItem("1 pulse; free end point; mass 1:100"); chMP.addItem("2 opposite pulses" ); chMP.addItem("2 same side pulses" ); chMP.addItem("Analytically: 1 pulse; homog string"); chMP.addItem("Analytically: 2 opposite pulses" ); chMP.addItem("Analytically: 2 same side pulses" ); chMP.addItem("Simple Translation: 2 opp pulses" ); chMP.addItem("Simple Translation: 2 same side" ); add(chMP); chSpeed.addItem("fast"); chSpeed.addItem("slow"); add(chSpeed); //add(new Label(" Lower graphs:")); chSideF.addItem("Posit, vel, force"); chSideF.addItem("left,right forces"); add(chSideF); chClear.addItem("clear lower graphs"); chClear.addItem("do not clear"); add(chClear); chRedClosest.addItem("Red Fixed"); chRedClosest.addItem("Red Closest"); add(chRedClosest); chNum.addItem("32 masses"); chNum.addItem("64 masses"); chNum.addItem("16 masses"); chNum.addItem("24 masses"); chNum.addItem("48 masses"); add(chNum); add(b1); add(b3); add(b2); add(b4); b1.addActionListener(this); b2.addActionListener(this); b3.addActionListener(this); b4.addActionListener(this); //ANIMATION BUFFER bufferImage = createImage( X_DIVIDER, Y_DIVIDER_COORD ); bufferGraphics = bufferImage.getGraphics(); reInitialize(); } //---------------------------------------------------------------------------- public void paint( Graphics g ) { g.setColor(Color.white); if( j == 0 ) { if( cs.fullRepaint ) g.fillRect( 0,0, X_LENGTH, Y_LENGTH ); else g.fillRect( 0,0, X_DIVIDER, Y_DIVIDER_COORD ); } if( j == 0 ) //NECESSARY BECAUSE NT SEEMS TO DO AN EXTRA { //PAINT; THIS DOESN'T HAPPEN ON SOLARIS first_time_painting = false; g.setColor(Color.black); g.drawRect( pict2XleftEdge, pict2TopY, pict2Width, pict2Height ); g.drawLine( pict2XleftEdge, pict2Ycenter, pict2XleftEdge + pict2Width, pict2Ycenter ); g.drawString( "Forces on red point mass", pict2XleftEdge, pict2TopY + pict2Height + 15); g.drawLine( X_DIVIDER +1, TOP_MARGIN_YCOORD, //RIGHT BOUNDARY X_DIVIDER +1, Y_DIVIDER_COORD ); g.drawString( "String", X_DIVIDER +7, stringAxis); g.drawString( "Velocity of string", X_DIVIDER +7, velocityAxis); g.drawString( "elements", X_DIVIDER +7, velocityAxis + 10); g.drawString( "Accel on string ", X_DIVIDER +7, forceAxis); g.drawString( "elements", X_DIVIDER +7, forceAxis + 10); if( cs.simpleTrans2 ) { g.drawString( "(Translation speed is", pict2XleftEdge-150, pict2TopY + 15 ); g.drawString( "slightly different)", pict2XleftEdge-150, pict2TopY + 15 + 12 ); } if( cs.drawSideForces ) { g.drawString( "MAGENTA = force", pict2XleftEdge-113, pict2TopY + 15 + 12 + 20 + 12 + 20); g.drawString( "from left band", pict2XleftEdge-113, pict2TopY + 15 + 12 + 20 + 12 + 20 + 12); g.drawString( "ORANGE = force", pict2XleftEdge-113, pict2TopY + 15 + 12 + 20 + 12 + 20 + 12 + 20); g.drawString( "from right band", pict2XleftEdge-113, pict2TopY + 15 + 12 + 20 + 12 + 20 + 12 + 20 + 12); } } //g.setColor(Color.red); bufferGraphics.setColor( getBackground() ); //CLEAR PLOT BUFFER OR STRING SMEARS bufferGraphics.fillRect( 0,TOP_MARGIN_YCOORD, X_DIVIDER, Y_DIVIDER_COORD - TOP_MARGIN_LEN ); bufferGraphics.setColor( Color.blue); for( int i=0; i < numMassPtsVisible; i=i+1 ) { yy[i] = stringAxis - (int)y[ i + firstVisiblePtIndex]; vv[i] = velocityAxis - (int)v[ i + firstVisiblePtIndex]; ff[i] = forceAxis - (int)f[ i + firstVisiblePtIndex]; } if( boundaryPointIsFree ) logicalNumPts = relativeLastLeftNodeIndex+1; else logicalNumPts = numMassPtsVisible; if( j < 280 && cs.simpleTrans) //DON'T KEEP DRAWING AFTER ITS OFF THE SCREEN { bufferGraphics.setColor(Color.cyan); bufferGraphics.drawPolyline(xst1, yst1,totalNumPts -1 ); bufferGraphics.drawPolyline(xst1, vst1,totalNumPts -1 ); bufferGraphics.drawPolyline(xst1, fst1,totalNumPts -1 ); for( int i=1; i < totalNumPts -1; i++ ) xst1[i] -= 1; } bufferGraphics.setColor(Color.blue); //DRAW BLUE STRING bufferGraphics.drawPolyline(xx, yy, logicalNumPts); if( cs.numMassPts <= 64 ) //DRAW POINT MASSES { for( int i=0; i <= relativeLastLeftNodeIndex; i++ ) bufferGraphics.fillArc(xx[i]-cs.lshift, yy[i]-cs.lshift, 2*cs.lr, 2*cs.lr, 0, 360); } bufferGraphics.setColor(Color.red); bufferGraphics.fillArc(xx[ watchedNode ]-cs.lshift, yy[ watchedNode ]-cs.lshift, 2*cs.lr, 2*cs.lr, 0, 360); if( cs.numMassPts <= 64 ) { bufferGraphics.setColor(Color.black); //RIGHT BLACK STRING for( int i=relativeLastLeftNodeIndex+1; i<=numSegmentsVisible; i++ ) bufferGraphics.fillArc(xx[i]-cs.rshift, yy[i]-cs.rshift, 2*cs.rr, 2*cs.rr, 0, 360); } if( cs.simpleTrans2 ) { bufferGraphics.setColor(Color.black); bufferGraphics.drawLine( xx[relativeLastLeftNodeIndex+1],TOP_MARGIN_YCOORD, xx[relativeLastLeftNodeIndex+1],Y_DIVIDER_COORD); } bufferGraphics.setColor(Color.red); bufferGraphics.drawPolyline(xx, vv, logicalNumPts); bufferGraphics.setColor(Color.green); bufferGraphics.drawPolyline(xx, ff, logicalNumPts); for( int i=0; i < logicalNumPts; i++ ) //MAKE LINES THICKER { vv[i] = vv[i] -1; ff[i] = ff[i] -1; } bufferGraphics.setColor(Color.red); bufferGraphics.drawPolyline(xx, vv, logicalNumPts); bufferGraphics.setColor(Color.green); bufferGraphics.drawPolyline(xx, ff, logicalNumPts); bufferGraphics.setColor(Color.black); bufferGraphics.drawLine( xx[1], stringAxis, xx[numSegmentsVisible],stringAxis ); bufferGraphics.drawLine( xx[1], velocityAxis, xx[numSegmentsVisible],velocityAxis ); bufferGraphics.drawLine( xx[1], forceAxis, xx[numSegmentsVisible],forceAxis ); bufferGraphics.fillArc( xx[numSegmentsVisible]-2, yy[numSegmentsVisible]-2, 6, 6, 0, 360 ); pict2Xnew = pict2XleftEdge + (int)(11.2*(t-tP2Start)); pict2YnewF = pict2Ycenter - (int)(2.0*f[ watchedNode ]); pict2YnewY = pict2Ycenter - (int)(2.0*y[ watchedNode ]); pict2YnewV = pict2Ycenter - (int)(2.0*v[ watchedNode ]); //RIGHT STRING SEGMENT my'' = -k(y[i]-y[i+1]) if( boundaryPointIsFree ) p2rightYnew = pict2Ycenter; else p2rightYnew = pict2Ycenter - (int)(2.0*cs.numPtsMassFactor*cs.kappa*( y[ watchedNode + 1 ] - y[ watchedNode ]) ); //LEFT STRING SEGMENT my'' = -k(y[i]-y[i-1]) p2leftYnew = pict2Ycenter - (int)(2.0*cs.numPtsMassFactor*cs.kappa*( y[ watchedNode - 1] - y[ watchedNode ]) ); if( j>0 ) { if( firstCycleHappeningForPict2( (int)f[watchedNode], v[watchedNode]) || (firstCycleEndedP2 && ( t < (tP2Start + extraDrawTime) ) ) ) { if( t > tP2Start + 0.1 ) //COMPENSATING FOR WIERD DRAWING BUG { g.setColor(Color.green); //NET FORCE g.drawLine(pict2Xold, pict2YoldF, pict2Xnew, pict2YnewF); g.drawLine(pict2Xold, pict2YoldF-1, pict2Xnew, pict2YnewF-1); if( cs.drawSideForces ) { g.setColor(Color.orange); //RIGHT FORCE g.drawLine(pict2Xold, p2rightYold, pict2Xnew, p2rightYnew); g.drawLine(pict2Xold, p2rightYold-1, pict2Xnew, p2rightYnew-1); g.setColor(Color.magenta); //LEFT FORCE g.drawLine(pict2Xold, p2leftYold, pict2Xnew, p2leftYnew); g.drawLine(pict2Xold, p2leftYold-1, pict2Xnew, p2leftYnew-1); } else { g.setColor(Color.blue); //POSITION g.drawLine(pict2Xold, pict2YoldY, pict2Xnew, pict2YnewY); g.drawLine(pict2Xold, pict2YoldY-1, pict2Xnew, pict2YnewY-1); g.setColor(Color.red); //VELOCITY g.drawLine(pict2Xold, pict2YoldV, pict2Xnew, pict2YnewV); g.drawLine(pict2Xold, pict2YoldV-1, pict2Xnew, pict2YnewV-1); } g.setColor(Color.red); } //WIERD DRAWING BUG } } pict2Xold = pict2Xnew; pict2YoldF = pict2YnewF; pict2YoldY = pict2YnewY; pict2YoldV = pict2YnewV; p2rightYold = p2rightYnew; p2leftYold = p2leftYnew; if( writeExtremaP2 && cs.fullRepaint ) //DON'T OVERWRITE NUMBERS { writeExtremaP2 = false; //WRITE IT ONLY ONCE String e = dFmt.format( vMaxP2 ); String ee = dFmt1.format( tEndIL - tP2Start ); g.drawString( "I.L. = " + e + " time = " + ee, pict2XleftEdge + pict2Width + 5, pict2TopY + 25); e = dFmt.format( vMinP2 - vMaxP2 ); ee = dFmt1.format( tEndPD - tEndIL ); g.drawString( "P.D. = " + e + " time = " + ee, pict2XleftEdge + pict2Width + 5, pict2TopY + 55); e = dFmt.format( vBLMaxP2 - vMinP2 ); ee = dFmt1.format( tEndBL - tEndPD ); g.drawString( "B.L. = " + e + " time = " + ee, pict2XleftEdge + pict2Width + 5, pict2TopY + 85); e = dFmt1.format( tP2Start ); g.drawString( "tStart = " + e, pict2XleftEdge - 110, pict2TopY + 45 ) ; } g.drawImage(bufferImage, 1, 1, null); //SHOW PLOT BUFFER } //-------------------------------------------------------------------------- public void update( Graphics g) //WE OVERRIDE update() TO AVOID CLEARING { //THE BACKGROUND UNNECESSARILY paint(g); } //-------------------------------------------------------------------------- public void run() { Thread.currentThread().setPriority( Thread.MIN_PRIORITY ); Thread currentThread = Thread.currentThread(); long startTime = System.currentTimeMillis(); while( currentThread == animatorThread && j <= maxNumberOfSteps ) { j++; repaint(0,TOP_MARGIN_YCOORD, X_LENGTH, Y_LENGTH - TOP_MARGIN_LEN ); try { startTime += cs.delay; Thread.sleep( Math.max( 0, startTime-System.currentTimeMillis() )); } catch( InterruptedException e ) { break; } //if( j % 25 == 0) // computeTotalEnergy(); if( cs.analyticSoln ) computeStepAnalytically(); //COMPUTE NEW y,v,f else if(cs.simpleTrans2 ) computeSimpleTrans2(); else computeStepWithStoermersRule(); //COMPUTE NEW y,v,f if( kk == 2 ) { animation_is_set_to_stop = true; stop(); } } } //---------------------------------------------------------------------------- public void start() { if( animation_is_set_to_stop ) { } else //START ANIMATING { if( animatorThread == null ) { animatorThread = new Thread( this ); } animatorThread.start(); //THIS STARTS run() ON ANIMATOR THREAD } } //---------------------------------------------------------------------------- public void stop() { animatorThread = null; } //---------------------------------------------------------------------------- public void actionPerformed( ActionEvent e ) { // BUTTONS String tst = e.getActionCommand(); if( b1s.equals(tst) ) //RUN { kk = 0; loadChoiceBoxData(); animation_is_set_to_stop = false; } if( b2s.equals(tst) ) //STEP { animation_is_set_to_stop = false; kk=2; } if( b3s.equals(tst) ) //STOP { animation_is_set_to_stop = true; kk = 0; stop(); } if( b4s.equals(tst) ) //RESET { animation_is_set_to_stop = true; kk = 0; stop(); loadChoiceBoxData(); reInitialize( ); } start(); } //---------------------------------------------------------------------------- void reInitialize( ) { j = 0; t = 0.0; tStart = 0.0; animation_is_set_to_stop = true; first_time_stoermer = true; first_time_painting = true; first_time_Pict2 = true; cycleInProgressP2 = firstCycleEndedP2 = false; writeExtremaP2 = false; pict2Xold = pict2XleftEdge; pict2YoldF = p2rightYold = p2leftYold = pict2Ycenter; pict2YoldY = pict2YoldV = pict2Ycenter; if( cs.numMassPts != numSegmentsVisible ) //RE-INIT EVERYTHING! { numSegmentsVisible = cs.numMassPts; totalNumPts = numSegmentsVisible + 1; numMassPtsVisible = numSegmentsVisible + 1; firstVisiblePtIndex = 0; stringSegmentLength = X_DIVIDER / numSegmentsVisible; //384 / 12 or 24 lastLeftNodeIndex = (numSegmentsVisible / 2) - 1 ; relativeLastLeftNodeIndex = lastLeftNodeIndex - firstVisiblePtIndex; xx = new int[numMassPtsVisible]; for( int i=0; i<=numSegmentsVisible; i=i+1 ) xx[i] = stringSegmentLength * i; yy = new int[numMassPtsVisible]; //MASS PT DIST FROM REST POSIT vv = new int[numMassPtsVisible]; ff = new int[numMassPtsVisible]; y = new double[totalNumPts]; //ABSOLUTE Y CO-ORD OF MASS PTS v = new double[totalNumPts]; f = new double[totalNumPts]; neqns = totalNumPts-2; //NO EQ FOR y[0] AND y[totalNumPts-1] ytemp = new double[neqns+1]; vtemp = new double[neqns+1]; } if( !cs.redClosest ) //SAME POINT ON LOGICAL STRING watchedNode = (int)(15*cs.numPtsFactor); else watchedNode = lastLeftNodeIndex; n = numSegmentsVisible - 1; if( cs.analyticSoln ) //I DON'T WANT TO RE-DO THIS EVERYTIME { //UNLESS numMassPts CHANGES!! s = new double[n+1][n+1]; w = new double[n+1]; a = new double[n+1]; b = new double[n+1]; double dn = (double)n; double dN; double pi = Math.PI; w[0] = Math.sqrt( cs.kappa * cs.numPtsMassFactor ); //sqrt( k/m ) for( int N=1; N<=n; N++ ) //FOWLES AM P.304 NOTATION { dN = (double)N; w[N] = 2.0 * w[0] * Math.sin( dN*pi/(2.0*(dn+1.0)) ); for( int jj=1; jj<=n; jj++ ) s[jj][N] = Math.sin( (double)jj * dN * pi /( dn + 1.0) ); } } else //HOPEFULLY FREE UP A LOT OF MEMORY { s = new double[1][1]; w = new double[1]; a = new double[1]; b = new double[1]; } for( int i=0; i < totalNumPts; i++ ) y[i] = v[i] = f[i] = 0.0; //INIT PULSE /* WE KNOW THAT WAVE EQ d^2y/dt^2 = w^2 d^2y/dx^2 HAS THE SOLN y(x,t) = f(x-wt) FOR ARBITRARY FUNCTION f THUS WE CAN FIND VELOCITY IC v = dy/dt(x,0) = f'(x)*(-w) FOR OUR PULSE; FOR US w=1 */ for( int i = (int)(cs.numPtsFactor*(centerOfPulse - halfPulseWidth)); i <= (int)(cs.numPtsFactor*(centerOfPulse + halfPulseWidth)); i++ ) { arg = piOver8 * i / cs.numPtsFactor + ccc; COS = Math.cos( arg ); SIN = Math.sin( arg ); y[i] = amplitude *COS *COS*COS; v[i] = 3.0* piOver8* ws* amplitude* COS*COS* SIN; } if( cs.numPulsesAndMore == 2 ) { for( int i=0; i < (totalNumPts-1)/2; i++ ) { y[ totalNumPts-1 - i ] = -y[i]; v[ totalNumPts-1 - i ] = -v[i]; } } if( cs.numPulsesAndMore == 3 ) //NOTE THE ASYMMETRY OF ADDITIONAL i-1 { for( int i=0; i < (totalNumPts-1)/2; i++ ) { y[ totalNumPts-1 - i-1 ] = y[i]; v[ totalNumPts-1 - i-1 ] = v[i]; } } if( cs.analyticSoln ) { //S^{T} * S = factor*I double[] notOrthoNormalFactor = new double[n+1]; for( int pp=1; pp<=n; pp++ ) { notOrthoNormalFactor[pp] = 0.0; for( int N=1; N<=n; N++ ) notOrthoNormalFactor[pp] += s[N][pp]*s[N][pp]; } for( int jj=1; jj<=n; jj++ ) // y(t=0) = Sa; S^{-1} = c*S^{T} { a[jj] = 0.0; b[jj] = 0.0; for( int N=1; N<=n; N++ ) { a[jj] = a[jj] + s[N][jj]*y[N]; b[jj] = b[jj] + s[N][jj]*v[N]; } a[jj] = a[jj]/notOrthoNormalFactor[jj]; b[jj] = b[jj]/( w[jj] * notOrthoNormalFactor[jj] ); } } if( cs.simpleTrans2) initSimpleTrans2(); if( cs.analyticSoln ) computeStepAnalytically(); //COMPUTE NEW y,v,f else if( cs.simpleTrans2 ) computeSimpleTrans2(); else computeStepWithStoermersRule(); //COMPUTE NEW y,v,f if( cs.simpleTrans ) //OVERLAY SIMPLE TRANSLATION OF PULSE { xst1 = new int[totalNumPts]; yst1 = new int[totalNumPts]; vst1 = new int[totalNumPts]; fst1 = new int[totalNumPts]; for( int i=0; i < totalNumPts ; i++ ) { xst1[i] = xx[i];; yst1[i] = stringAxis - (int)y[i]; vst1[i] = velocityAxis - (int)v[i]; fst1[i] = forceAxis - (int)f[i]; } } yy[0] = stringAxis; //FIXED END PTS yy[numSegmentsVisible] = yy[0]; if( cs.fullRepaint) //DON'T KEEP OVERWRITING BOTTOM PART OF PICTURE repaint(); else repaint(0,TOP_MARGIN_YCOORD, X_LENGTH, Y_LENGTH - TOP_MARGIN_LEN ); } //---------------------------------------------------------------------------- boolean firstCycleHappeningForPict2( int f, double v ) { if( first_time_Pict2 ) { first_time_Pict2 = false; if( f != 0 ) System.out.println("WARNING: not starting cycle2 at beginning;j =" + j); fOldP2 = f; vOldP2 = v; backLiftInProgressP2 = false; fMaxP2 = fMinP2 = fBLMaxP2 = 0; vMaxP2 = vMinP2 = vBLMaxP2 = 0.0; return false; } if( firstCycleEndedP2 ) return false; if( fOldP2 < f && !cycleInProgressP2 ) //HAPPENS ONCE AT START { cycleInProgressP2 = true; fOldP2 = f; vOldP2 = v; tP2Start = t; //System.out.println( "tP2Start = " + tP2Start ); //waveSpeed = (double)(sp*stringSegmentLength)/(tP2Start-tP1Start); //System.out.println( "waveSpeed = " + waveSpeed ); return true; } if( backLiftInProgressP2 ) //CHECK IF BACKLIFT HAS STARTED { if( f <= 0 ) //CYCLE FINISHED { firstCycleEndedP2 = true; writeExtremaP2 = true; return false; } if( v > vBLMaxP2 ) { vBLMaxP2 = v; tEndBL = t; } } else //PRE-BACKLIFT { if( fOldP2 < 0 && fOldP2 < f && f >= 0 ) backLiftInProgressP2 = true; if( v < vOldP2 ) { vMinP2 = v; tEndPD = t; } else { if( v > 0.0 && v > vOldP2 ) { vMaxP2 = v; tEndIL = t; } } } fOldP2 = f; vOldP2 = v; return cycleInProgressP2; } //---------------------------------------------------------------------------- void compute_d2y( double z[] ) //SEE TOP OF FILE FOR DESCRIPTION { f[1] = cs.numPtsMassFactor* cs.kappa*(z[2] - 2.0*z[1] ); for( k=2; k < lastLeftNodeIndex; k++ ) f[k] = cs.numPtsMassFactor* cs.kappa* (z[k+1] - 2.0*z[k] + z[k-1] ); if( boundaryPointIsFree ) { f[lastLeftNodeIndex] = cs.numPtsMassFactor*cs.endPtMassFactor * cs.kappa* (z[lastLeftNodeIndex-1] -z[lastLeftNodeIndex]); for( k=lastLeftNodeIndex+1; k<=neqns; k++ ) f[k] = 0.0; return; } k = lastLeftNodeIndex; f[k] = cs.numPtsMassFactor* cs.kappa* (z[k+1] - 2.0*z[k] + z[k-1] ); //TRANSITION TO DIFFERENT MASS STRING for( k=lastLeftNodeIndex+1; k<=neqns-1; k++ ) f[k] = cs.rightStringMassFactor * cs.kappa* cs.numPtsMassFactor* (z[k+1] - 2.0*z[k] + z[k-1]); f[neqns] = cs.rightStringMassFactor * cs.kappa* cs.numPtsMassFactor* (-2.0*z[neqns] + z[neqns-1] ); } //---------------------------------------------------------------------------- void computeStepWithStoermersRule( ) { //INSPIRED BY "NUMERICAL RECIPIES IN C" P.733 int i; if( first_time_stoermer) { first_time_stoermer = false; compute_d2y( y ); return; } for( i=1; i<=neqns; i++) //AVOID y[0] AND y[totalNumPts-1] { vtemp[i] = h*( v[i] + halfh * f[i] ); ytemp[i] = y[i] + vtemp[i]; } compute_d2y( ytemp ); for(nn=2; nn<=numSubsteps; nn++) { for( i=1; i<=neqns; i++ ) { vtemp[i] += h2*f[i]; ytemp[i] += vtemp[i]; } compute_d2y( ytemp ); } for( i=1; i<=neqns; i++) { v[i] = vtemp[i]/h + halfh*f[i]; y[i] = ytemp[i]; } f[totalNumPts-1] = y[ (totalNumPts-1) -1 ]; t = tStart; tStart = tStart + totalStep; /* for( int N=0; N