#INCLUDE JsGraph.inc #INCLUDE ControlPanel.inc function ModelObj() { this.barWidth = 1; this.alpha = 0.5; this.dalpha = Math.PI / 500; this.alphaSpeed = 0.2; this.lastTime = 0; this.r = 8; this.d = 10; this.a = 20; this.r_last = 0; this.d_last = 0; this.a_last = 0; this.beta = 0; this.x = 0; this.y = 0; this.dx = 0; this.dy = 0; this.ddx = 0; this.ddy = 0; this.delta = 0.00001; this.R = 0; this.Zx = 0; this.Zy = 0; this.xpoly = []; this.ypoly = []; this.xmin = 0; this.xmax = 0; this.ymax = 0; this.Update(); } ModelObj.prototype.xy = function( alpha ) { var c = this.r * Math.cos( alpha ); var h = this.r * Math.sin( alpha ); var b = this.d - c; var f = Math.sqrt( b*b + h*h ); var g = f - this.a; var e = b * g / f; this.x = this.d - e; this.y = g * h / f; this.beta = (f == 0) ? 0 : Math.acos( b / f ); if (h < 0) this.beta *= -1; } ModelObj.prototype.diffxy = function( a ) { this.xy( a + this.delta ); var dx = this.x; var dy = this.y; var ddx = dx; var ddy = dy; this.xy( a - this.delta ); dx -= this.x; dy -= this.y; ddx += this.x; ddy += this.y; this.xy( a ); ddx -= 2 * this.x; ddy -= 2 * this.y; var d2 = 2 * this.delta; var dd2 = this.delta * this.delta; this.dx = dx / d2; this.dy = dy / d2; this.ddx = ddx / dd2; this.ddy = ddy / dd2; } ModelObj.prototype.CompCirc = function() { this.R = 0; this.diffxy( this.alpha ); var tlen2 = this.dx * this.dx + this.dy * this.dy; var nom = Math.pow( tlen2, 1.5 ); var denom = this.dx * this.ddy - this.ddx * this.dy; this.R = nom / denom; if (denom == 0) return; var tlen = Math.sqrt( tlen2 ); this.Zx = this.x - this.R * this.dy / tlen; this.Zy = this.y + this.R * this.dx / tlen; } ModelObj.prototype.Update = function() { // Update alpha if (this.alphaSpeed > 0) { if (this.lastTime == 0) this.lastTime = xTimeMS(); var nowTime = xTimeMS(); var deltaTime = nowTime - this.lastTime; this.lastTime = nowTime; this.alpha += this.alphaSpeed * deltaTime * Math.PI / 1000; if (this.alpha > 4 * Math.PI || this.alpha < 0) this.alpha = 0; if (this.alpha > 2 * Math.PI) this.alpha -= 2 * Math.PI; } else { this.lastTime = 0; } // prevent d = r var rd = Math.abs( this.d - this.r ); if (rd < this.barWidth) { this.d = (this.d > this.r) ? this.r+this.barWidth : this.r-this.barWidth; } if (this.d != this.d_last || this.a != this.a_last || this.r != this.r_last) { this.ComputePoly(); this.d_last = this.d; this.a_last = this.a; this.r_last = this.r; } this.CompCirc(); this.xy( this.alpha ); } ModelObj.prototype.ComputePoly = function() { var px = []; var py = []; this.xmin = 0; this.xmax = 0; this.ymax = 0; var max = Math.max; var limit = Math.PI - this.dalpha / 2; for (var ang = 0; ang <= limit; ang += this.dalpha) { this.xy( ang ); px.push( this.x ); py.push( this.y ); if (this.x < this.xmin) this.xmin = this.x; if (this.x > this.xmax) this.xmax = this.x; if (Math.abs(this.y) > this.ymax) this.ymax = Math.abs(this.y); } this.xy( Math.PI ); px.push( this.x ); py.push( this.y ); if (this.x < this.xmin) this.xmin = this.x; if (this.x > this.xmax) this.xmax = this.x; if (Math.abs(this.y) > this.ymax) this.ymax = Math.abs(this.y); // mirror poly for (var i = px.length-1; i >= 0; i--) { px.push( px[i] ); py.push( -py[i] ); } this.xpoly = px; this.ypoly = py; } Model = new ModelObj(); var ModelAnimation = NewGraph2D( { Width: '100%', Height: '66%', DrawFunc: DrawModel, AutoReset: false } ); function DrawModel( g ) { var barWidth = Model.barWidth; var circPadding = 1; var winPadding = 5; var max = Math.max; var barLen = max( Model.d + Model.r, Model.a ) + 1.5 * barWidth; var winXmin = Model.r; if (Model.d < Model.r) winXmin = barLen - Model.r; winXmin = max( winXmin, -Model.xmin ) + winPadding; var winXmax = max( barLen + Model.r, Model.xmax ) + winPadding; var winY = winXmin; winY = max( winY, Model.ymax ) + winPadding; var winW = winXmin + winXmax; var winH = 2 * winY; var vpRatio = g.VpInnerWidth / g.VpInnerHeight; var gRatio = winW / winH; if (gRatio > vpRatio) { winH = winW / vpRatio; winY = winH / 2; } else { winW = winH * vpRatio; var pad = (winW - (winXmin + winXmax)) / 2; winXmin += pad; winXmax += pad; } g.Reset(); g.SetAngleMeasure( 'rad' ); g.SetWindow( -winXmin, -winY, winXmax, winY ); var markerSize = 0.5 * barWidth * g.ScaleWinX(); g.SetMarkerSize( markerSize ); g.SetLineAttr( '#ccc', 1 ); g.Axes(); g.SetLineAttr( '#ddd', 1 ); g.Grid( 10, 10 ); g.Circle( 0, 0, Model.r ); var x = Model.r * Math.cos( Model.alpha ); var y = Model.r * Math.sin( Model.alpha ); // draw radial bar g.TransRotate( Model.alpha ); g.SetAreaAttr( 'white', 'black', 2 ); g.RectWH( -barWidth/2, -barWidth/2, Model.r+barWidth, barWidth, 3 ); g.ResetTrans(); g.SetAreaAttr( 'gray', 'gray', 2 ); g.Marker( 0, 0 ); // draw other bar g.TransRotate( -Model.beta ); g.TransMove( x, y ); g.SetAreaAttr( 'white', 'blue', 2 ); g.RectWH( -barWidth/2, -barWidth/2, barLen, barWidth, 3 ); g.Line( Model.a, -barWidth/2, Model.a, barWidth/2 ); g.Line( 0, 0, Model.a, 0 ); g.SetAreaAttr( 'gray', 'gray', 2 ); g.Marker( 0, 0 ); g.ResetTrans(); g.TransRotate( -Model.beta ); g.TransMove( Model.d, 0 ); g.SetAreaAttr( 'white', 'blue', 2 ); g.RectWH( -0.75*barWidth, -0.75*barWidth, 1.5*barWidth, 1.5*barWidth, 3 ); // draw anchor point g.ResetTrans(); g.SetAreaAttr( 'blue', 'blue', 2 ); g.Marker( Model.d, 0 ); // draw green curve g.SetAreaAttr( 'white', 'green', 1 ); g.SetMarkerSize( 8 ); g.Marker( Model.x, Model.y, 3 ); g.SetMarkerSize( markerSize ); g.SetLineAttr( 'green', 2 ); g.Polygon( Model.xpoly, Model.ypoly ); // draw osculating circle if (Model.R != 0) { var zdx = Model.x - Model.Zx; var zdy = Model.y - Model.Zy; var cosrot = zdx / Math.abs(Model.R); if (cosrot > 1) cosrot = 1; if (cosrot < -1) cosrot = -1; var rot = Math.acos( cosrot ); if (zdy < 0) rot *= -1; g.SetLineAttr( 'red', 1 ); g.Line( Model.Zx, Model.Zy, Model.x, Model.y ); g.SetMarkerSymbol( 'Plus' ); g.SetMarkerSize( 16 ); g.Marker( Model.Zx, Model.Zy ); g.SetLineAttr( 'red', 2 ); g.Circle( Model.Zx, Model.Zy, Math.abs( Model.R ), 1, rot ); } } function UpdateModel() { Model.Update(); ModelSliders.Update(); DrawModel( ModelAnimation ); NextFrame(); } var AnimationFrame = null; function NextFrame() { if (Model.alphaSpeed == 0) return; AnimationFrame = requestAnimationFrame( UpdateModelFromAnimationFrame ); } function UpdateModelFromAnimationFrame() { cancelAnimationFrame( AnimationFrame ); AnimationFrame = null; UpdateModel(); } NextFrame(); var ModelSliders = ControlPanels.NewSliderPanel( { ModelRef: 'Model', OnModelChange: UpdateModel, Format: 'fix0', Digits: 2 } ); ModelSliders.AddValueSliderField( { Name: 'a', Min: 0, Max: 50 } ); ModelSliders.AddValueSliderField( { Name: 'd', Min: 0, Max: 30 } ); ModelSliders.AddValueSliderField( { Name: 'r', Min: 0, Max: 15 } ); ModelSliders.AddValueSliderField( { Name: 'alpha', Label: 'α', Min: 0, Max: 3*Math.PI } ); ModelSliders.AddValueSliderField( { Name: 'alphaSpeed', Label: 'v<sub>α</sub>', Min: 0, Max: 1 } ); ModelSliders.Render();