Wie das unten gezeigte Modell berechnet und programmiert wird, kann unter Animation des Krüger-Flaps Mechanismus erklärt nachgelesen werden.
Klicke auf die Animation, um sie automtisch ablaufen zu lassen oder zu stoppen, oder verwende den Schieberegler.
Im folgenden JavaScript ist am Ende ersichtlich, wie die automatische Animation programmiert wird.
// Include some JavaScript modules (Wiki-Syntax) #INCLUDE JsGraph.inc #INCLUDE ControlPanel.inc #INCLUDE ModelAnimation.inc function PointFromAngleLength( P0, Pref, ang, len ) { // returns a point at distance len from P0 at angle ang from ray P0 to Pref var eref = JsgVect2.Norm( JsgVect2.Sub( Pref, P0 ) ); var ep = JsgVect2.Rotate( eref, ang ); return JsgVect2.Add( P0, JsgVect2.Scale( ep, len ) ); } function Intersect2Circles( A, a, B, b ) { // A, B = [ x, y ] // return = [ Q1, Q2 ] or [ Q ] or [] where Q = [ x, y ] var AB0 = B[0] - A[0]; var AB1 = B[1] - A[1]; var c = Math.sqrt( AB0 * AB0 + AB1 * AB1 ); if (c == 0) { // same center: A = B return []; } var x = (a*a + c*c - b*b) / (2*c); var y = a*a - x*x; if (y < 0) { // no intersection return []; } if (y > 0) y = Math.sqrt( y ); // compute unit vectors ex and ey var ex0 = AB0 / c; var ex1 = AB1 / c; var ey0 = -ex1; var ey1 = ex0; var Q1x = A[0] + x * ex0; var Q1y = A[1] + x * ex1; if (y == 0) { // one touch point return [ [ Q1x, Q1y ] ]; } // two intersections var Q2x = Q1x - y * ey0; var Q2y = Q1y - y * ey1; Q1x += y * ey0; Q1y += y * ey1; return [ [ Q1x, Q1y ], [ Q2x, Q2y ] ]; } function FlapsModelObject() { // Parameters this.AnimationTime = 5; // Variable this.Alpha = 0; // Model Geometry this.StartAngle = 10.2 / 180 * Math.PI; this.EndAngle = -135 / 180 * Math.PI; this.P1 = [ 0, 0 ]; this.P2 = [ -200, -15 ]; this.Omega2 = -0.32108; this.Omega3 = 0.056018; this.Len13 = 180.4; this.Len34 = 159.0; this.Len36 = 234.9; this.Len24 = 237.8; this.Len25 = 352.6; this.Len57 = 78.8; this.Len67 = 119.1; // Computed Points -> Update this.P3 = [ 0, 0 ]; this.P4 = [ 0, 0 ]; this.P5 = [ 0, 0 ]; this.P6 = [ 0, 0 ]; this.P7 = [ 0, 0 ]; this.Update(); } FlapsModelObject.prototype.Update = function() { var Pref = JsgVect2.Add( this.P1, [ 1, 0 ] ); this.P3 = PointFromAngleLength( this.P1, Pref, this.StartAngle-this.Alpha, this.Len13 ); var Q = Intersect2Circles( this.P2, this.Len24, this.P3, this.Len34 ); if (Q.length < 2) return; this.P4 = Q[1]; this.P5 = PointFromAngleLength( this.P2, this.P4, this.Omega2, this.Len25 ); this.P6 = PointFromAngleLength( this.P3, this.P4, this.Omega3, this.Len36 ); var Q = Intersect2Circles( this.P5, this.Len57, this.P6, this.Len67 ); if (Q.length < 2) return; this.P7 = Q[1]; } function DrawFlapsModel( g ) { g.SetAngleMeasure( 'rad' ); g.SetWindow( -466.5, -492.1, 307.2, 163.3 ); DrawWing( g, FlapsModel ); DrawPart3( g, FlapsModel ); DrawPart1( g, FlapsModel ); DrawPart4( g, FlapsModel ); DrawPart2( g, FlapsModel ); DrawPart5( g, FlapsModel ); } function DrawWing( g, mod ) { g.SetAreaAttr( '#dff', '#000', 3 ); g.TransMove( mod.P1 ); // draw skeleton g.OpenPath(); g.BezierCurve( 210.4, 133.0, 40.4, 133.0, -20.3, 123.2, -87.8, 107.8 ); g.BezierCurveTo( -174.9, 88.3, -262.6, 17.7, -262.6, -15.0 ); g.BezierCurveTo( -262.6, -54.1, -220.3, -63.3, -184.7, -79.2 ); g.LineTo( 139.4, -191.8 ); g.LineTo( 210.4, -212.6 ); g.LineTo( 210.4, 133.0 ); g.Path( 2 ); // draw outline g.OpenPath(); g.BezierCurve( 210.4, 133.0, 40.4, 133.0, -20.3, 123.2, -87.8, 107.8 ); g.BezierCurveTo( -174.9, 88.3, -262.6, 17.7, -262.6, -15.0 ); g.BezierCurveTo( -262.6, -54.1, -220.3, -63.3, -184.7, -79.2 ); g.Path( 1 ); g.Polygon( [ 130.2, 142.4, 139.4, 210.4 ], [ -178.3, -183.2, -191.8, -212.6 ] ); g.ResetTrans(); } function DrawPart1( g, mod ) { g.SetAreaAttr( '#aaf', '#00f', 2 ); var u = JsgVect2.Norm( [ 179.7, 11.1 ] ); var v = JsgVect2.Norm( JsgVect2.Sub( mod.P3, mod.P1 ) ); g.TransRotate( JsgVect2.Angle( u, v ) ); g.TransMove( mod.P1 ); // draw outline g.OpenPath(); g.Line( 0, 33.9, 179.7, 33.9 ); g.LineTo( 189.2, 16.1 ); g.ArcTo( 172.4, 2.9, -11.1 ); g.LineTo( 157.6, 16.1 ); g.LineTo( 68.5, -8.1 ); g.LineTo( 13.4, -31.2 ); g.ArcTo( 0, 33.9, -33.9, true ); g.Path( 3 ); // draw skeleton g.Line( 146.3, 33.9, 157.6, 16.1 ); g.Polygon( [ 24.6, 28.4, 143.8, 148.4, 65.7, 36.3, 31.9 ], [ 23.2, 27.6, 27.6, 19.5, -1.8, -14.6, -12.2 ] ); g.ArcPt( 31.9, -12.2, 24.6, 23.2, 33.9 ); g.Circle( 0, 0, 16.1 ); g.Circle( 0, 0, 27.6 ); g.Circle( 179.7, 11.1, 8.0 ); g.SetBgColor( 'white' ); g.Circle( 0, 0, 11.1, 3 ); g.Circle( 179.7, 11.1, 5.0, 3 ); g.ResetTrans(); g.SetBgColor( 'white' ); g.SetLineWidth( 2 ); g.Circle( mod.P1, 11.1, 3 ); g.Circle( mod.P3, 5, 3 ); } function DrawPart2( g, mod ) { g.SetAreaAttr( '#8d8', '#0a0', 2 ); var u = JsgVect2.Norm( [ 159.5, 6.2 ] ); var v = JsgVect2.Norm( JsgVect2.Sub( mod.P4, mod.P3 ) ); g.TransRotate( JsgVect2.Angle( u, v ) ); g.TransMove( mod.P3 ); // draw outline g.OpenPath(); g.Line( 0.0, -11.3, 159.5, -11.3 ); g.ArcTo( 164.8, -10.3, 17.5 ); g.LineTo( 236.4, 12.3 ); g.ArcTo( 232.1, 31.9, 10.2 ); g.LineTo( 158.4, 23.4 ); g.ArcTo( 151.9, 21.7, 17.5 ); g.Polygon( [ 151.9, 116.2, 5.4, 5.4, -2.5 ], [ 21.7, 8.3, 25.7, 14.9, 11.1 ] ); g.ArcTo( 0.0, -11.3, 11.3 ); g.Path( 3 ); // draw skeleton g.ArcPt( 0.0, -11.3, -2.5, 11.1, 11.3, true ); g.ArcPt( 232.1, 31.9, 236.4, 12.3, 10.2, true ); g.Circle( 159.5, 6.2, 11.5 ); g.Polygon( [ 142.3, 137.0, 116.2, 24.4, 18.6, 18.6, 24.4, 139.7, 144.2 ], [ 8.3, 9.6, 1.3, 16.1, 11.8, 0.0, -5.4, -5.4, -2.1 ] ); g.ArcPt( 144.2, -2.1, 142.3, 8.3, -17.5 ); g.Polygon( [ 176.7, 182.2, 215.0, 216.9, 215.5, 211.8, 177.9, 174.2 ], [ 3.1, 1.3, 11.8, 15.3, 21.7, 24.2, 20.3, 16.1 ] ); g.ArcPt( 174.2, 16.1, 176.7, 3.1, -17.5 ); g.SetBgColor( 'white' ); g.Circle( 0, 0, 5.4, 3 ); g.Circle( 159.5, 6.2, 8.3, 3 ); g.Circle( 234.3, 22.0, 4.7, 3 ); g.ResetTrans(); g.SetLineWidth( 2 ); g.SetBgColor( 'white' ); g.Circle( mod.P3, 5.4, 3 ); g.Circle( mod.P4, 8.3, 3 ); g.Circle( mod.P6, 4.7, 3 ); } function DrawPart3( g, mod ) { g.SetAreaAttr( '#d88', '#a00', 2 ); var u = JsgVect2.Norm( [ 231.9, 51.6 ] ); var v = JsgVect2.Norm( JsgVect2.Sub( mod.P4, mod.P2 ) ); g.TransRotate( JsgVect2.Angle( u, v ) ); g.TransMove( mod.P2 ); // draw outline g.OpenPath(); g.Line( 312.9, -46.1, 351.4, -46.1 ); g.ArcTo( 354.7, -28.9, 8.6 ); g.LineTo( 312.9, -15.6 ); g.LineTo( 312.9, -46.1 ); g.Path( 3 ); g.OpenPath(); g.Line( 17.5, 0.0, 17.5, 29.9 ); g.ArcTo( 79.8, 38.0, -31.6 ); g.LineTo( 97.6, -16.6 ); g.LineTo( 102.5, -50.9 ); g.LineTo( 312.9, -50.9 ); g.LineTo( 312.9, -15.6 ); g.LineTo( 306.3, -13.6 ); g.LineTo( 248.5, -5.0 ); g.LineTo( 248.5, 51.6 ); g.ArcTo( 220.9, 63.4, 16.1 ); g.LineTo( 172.8, 7.1 ); g.LineTo( 96.0, 77.6 ); g.ArcTo( -17.5, 29.9, 66.9 ); g.LineTo( -17.5, 0.0 ); g.ArcTo( 17.5, 0.0, 17.5 ); g.Path( 3 ); g.Polygon( [ 69.7, 338.8, 328.3, 87.1, 69.7 ], [ -57.5, -57.5, -50.9, -50.9, -57.5 ], 3 ); g.Line( 69.7, -57.5, 37.7, -57.5 ); g.Line( 69.7, -57.5, 54.3, -50.6 ); g.Circle( 54.3, -50.6, 4.5, 3 ); g.Rect( 361.8, -50.9, 374.3, -57.5, 3 ); g.Line( 338.8, -57.5, 374.3, -57.5 ); // draw skeleton g.Polygon( [ 104.1, 108.7, 152.7, 170.9, 104.1 ], [ -17.5, -46.1, -46.1, 0.4, -17.5 ] ); g.Polygon( [ 161.6, 240.3, 240.3, 179.1, 161.6 ], [ -46.1, -46.1, -9.8, -0.8, -46.1 ] ); g.Polygon( [ 248.5, 306.3, 306.3, 248.5, 248.5 ], [ -46.1, -46.1, -19.5, -11.1, -46.1 ] ); g.OpenPath(); g.Line( -9.7, 29.9, 11.4, 29.9 ); g.ArcTo( 85.5, 40.7, -37.8 ); g.LineTo( 102.5, -10.9 ); g.LineTo( 165.1, 5.6 ); g.LineTo( 91.0, 72.4 ); g.ArcTo( -9.7, 29.9, 59.7 ); g.Path( 1 ); g.OpenPath(); g.Polygon( [ 216.7, 181.6, 240.3, 240.3 ], [ 46.0, 5.7, -3.4, 37.6 ] ); g.ArcTo( 216.7, 46.0, -16.1 ); g.Path( 1 ); g.ResetTrans(); g.SetAreaAttr( 'white', '#a00', 2 ); g.Circle( mod.P2, 10.1, 3 ); g.Circle( mod.P5, 4.3, 3 ); g.Circle( mod.P4, 8.3, 3 ); } function DrawPart4( g, mod ) { g.SetAreaAttr( '#dc8', '#a50', 2 ); var u = JsgVect2.Norm( [ -71.0, 34.8 ] ); var v = JsgVect2.Norm( JsgVect2.Sub( mod.P5, mod.P7 ) ); g.TransRotate( JsgVect2.Angle( u, v ) ); g.TransMove( mod.P7 ); g.OpenPath(); g.Line( -63.0, -9.6, 14.9, -9.6 ); g.LineTo( 8.8, 3.8 ); g.ArcTo( -2.8, 9.2, 9.6 ); g.LineTo( -63.0, -9.6 ); g.Path( 3 ); g.OpenPath(); g.Line( -22.4, 3.2, -64.0, 41.5 ); g.ArcTo( -79.1, 29.7, 9.6 ); g.LineTo( -55.9, -7.3 ); g.LineTo( -22.4, 3.2 ); g.Path( 3 ); g.OpenPath(); g.BezierCurve( -71.0, -0.7, -71.0, -0.7, -71.0, -2.0, -53.1, -20.2 ); g.BezierCurveTo( -35.0, -38.8, -22.4, -44.3, -6.6, -44.3 ); g.BezierCurveTo( 9.8, -44.3, 22.6, -44.3, 31.4, -35.6 ); g.BezierCurveTo( 42.9, -24.4, 35.7, -9.6, 24.2, -9.6 ); g.LineTo( -63.0, -9.6 ); g.LineTo( -71.0, -0.7 ); g.Path( 3 ); // draw skeleton g.Circle( 0.0, 0.0, 9.6 ); g.Circle( -71.0, 34.8, 9.6 ); g.ResetTrans(); g.SetAreaAttr( 'white', '#a50', 2 ); g.Circle( mod.P7, 5.1, 3 ); g.Circle( mod.P5, 5.1, 3 ); } function DrawPart5( g, mod ) { g.SetAreaAttr( '#dad', '#d0d', 2 ); var u = JsgVect2.Norm( [ 119.8, 0.0 ] ); var v = JsgVect2.Norm( JsgVect2.Sub( mod.P7, mod.P6 ) ); g.TransRotate( JsgVect2.Angle( u, v ) ); g.TransMove( mod.P6 ); // draw outline g.RectWH( 0.0, -4.5, 119.8, 9.0, 3 ); g.Circle( 0.0, 0.0, 8.0, 3 ); g.Circle( 119.8, 0.0, 8.0, 3 ); g.ResetTrans(); g.SetBgColor( 'white' ); g.SetLineWidth( 2 ); g.Circle( mod.P6, 4.5, 3 ); g.Circle( mod.P7, 4.5, 3 ); } var FlapsModel = new FlapsModelObject(); function HandleFlapsModelChange() { FlapsModel.Update(); FlapsModelSlider.Update(); FlapsModelGraph.Redraw(); } var FlapsModelGraph = NewGraph2D( { Id: 'FlapsModelCanvas', Width: '100%', Height: '84.71%', DrawFunc: DrawFlapsModel, AutoReset: true, ScaleRef: 800, AutoScalePix: true } ); function HandleFlapsModelSliderMove() { FlapsModelAnimation.Stop(); // if Alpha is near start position if (Math.abs(FlapsModel.Alpha) < (5/180*Math.PI)) { FlapsModelAnimation.ResetToState(0); } // if Alpha is near end position if (Math.abs(FlapsModel.Alpha-(FlapsModel.StartAngle-FlapsModel.EndAngle)) < (5/180*Math.PI)) { FlapsModelAnimation.ResetToState(1); } HandleFlapsModelChange(); } var FlapsModelSlider = ControlPanels.NewPanel( { Name: 'FlapsSetting', ModelRef: 'FlapsModel', OnModelChange: HandleFlapsModelSliderMove } ); FlapsModelSlider.AddSliderField( { Name: 'Alpha', Label: 'Position', Min: 0, Max: -(FlapsModel.EndAngle - FlapsModel.StartAngle) } ); FlapsModelSlider.Render(); var FlapsModelAnimation = NewModelAnimation( { ModelRef: FlapsModel, OnModelChange: HandleFlapsModelChange } ); FlapsModelAnimation.AnimationToState( 1, { TaskList: [ { ValueRef: 'Alpha', EndValue: FlapsModel.StartAngle - FlapsModel.EndAngle, Speed: -(FlapsModel.StartAngle - FlapsModel.EndAngle) / FlapsModel.AnimationTime, Sweep: 'smooth' } ] } ); FlapsModelAnimation.AnimationToState( 0, { TaskList: [ { ValueRef: 'Alpha', EndValue: 0, Speed: -(FlapsModel.StartAngle - FlapsModel.EndAngle) / FlapsModel.AnimationTime, Sweep: 'smooth' } ] } ); xAddEvent( 'FlapsModelCanvas', 'click', function(e) { if (FlapsModelAnimation.IsRunning) { FlapsModelAnimation.Stop(); } else { FlapsModelAnimation.Next(true); } } );