The Curvature App source code is split in 3 parts:
The Source Code shown on this page is stored in the private page Special: Curvature App Code.
The description of the graphic module JsGraphX3D and the ControlPanels module can be found here:
This code is part of the following blog pages:
#INCLUDE JsGraphX3D.inc #INCLUDE ControlPanel.inc #INCLUDE Tabs.inc #INCLUDE DataX.inc #INCLUDE TextControl.inc #INCLUDE ModelAnimation.inc <style> .Wiki .ControlPanel { margin: 0; } textarea.ListingDisplay { font-family: Courier; margin-bottom: 1em; } #MoreAnimationInfos { margin-top: 0.25em; margin-bottom: 0.25em; } #JsGraph1 { margin-top: 0.25em; } #ResetButton { } #UnitsTab { margin-left: 1.2em; } #BackButton { margin-left: 1.2em; } #Options { border: 2px solid #fa6; } #ControlPanel1-Height { background: #ff8; } #Object-Sliders0-NObjects, #Object-Sliders0-ObjDist, #Object-Sliders0-ObjSize { background: #ff8; } #Object-Sliders1-NObjects, #Object-Sliders0-ObjDist, #Object-Sliders0-ObjSize { background: #ff8; } #Refraction-Panel-refractionCoeff { background: #ff8; } #Image-Panel-OverlayImage, #Image-Panel-DemoText, #Image-Panel-Description { text-align: left; width: 100% !important; } </style> <a id="App"></a> {{TabSelectorsTop| CurveDemosTabs | TopMargin }} {{TabSelButton| Curve | CurveButton }} {{TabSelButton| Causew | CausewayButton }} {{TabSelButton| Soundly | SoundlyButton }} {{TabSelButton| TrnsmLine | TrnsmLineButton }} {{TabSelButton| Rainy | RainyButton }} {{TabSelButton| Tensas | TensasButton }} {{TabSelButton| Grace | GraceButton }} {{TabSelButton| Chicago | ChicagoButton }} {{TabSelButton| Canigou | CanigouButton }} {{TabSelButton| Bedford | BedfordButton }} {{TabSelButton| Custom | CustomButton }} {{TabSelButton| Reset | ResetButton }} {{EndTabSelectors}} {{div|id=MoreAnimationInfos}} Click a Button to Start a Demo. Click again to skip one step. Click the Animation to start/stop. See also Controls below. {{end div}} <jscript> xAddClass( 'CustomButton', 'TabHide' ); OnLayoutMaxRemoveLimitWidth = true; var DefaultDigits = 6; var DeviceRatioOff = 1.5; var PI90 = Math.PI / 2; function toRad( a ) { return a * Math.PI / 180; } function toDeg( a ) { return a * 180 / Math.PI; } // meta data for Json generation and loading var CurveAppMetaData = { Compact: false, DefaultPrec: 8, Properties: [ { Name: 'DemoText', Type: 'str', Default: '' }, { Name: 'Description', Type: 'str', Default: '' }, { Name: 'Height', Type: 'num', Default: 100 }, { Name: 'FocalLengthField', Type: 'num', Default: 33.9 }, { Name: 'Roll', Type: 'num', Default: 0 }, { Name: 'Tilt', Type: 'num', Default: 0 }, { Name: 'Pan', Type: 'num', Default: 0 }, { Name: 'nLines', Type: 'int', Default: 45 }, { Name: 'showModel', Type: 'int', Default: 1 }, { Name: 'showGrid', Type: 'int', Default: 1 }, { Name: 'showData', Type: 'bool', Default: false }, { Name: 'showTangent', Type: 'bool', Default: false }, { Name: 'showEyeLevel', Type: 'bool', Default: true }, { Name: 'deviceRatio', Type: 'num', Default: DeviceRatioOff }, { Name: 'viewcenterHorizon', Type: 'int', Default: 0 }, { Name: 'ObjType', Type: 'arr', Size: 2, ArrayType: 'int', Default: [ 3, 3 ] }, { Name: 'NObjects', Type: 'arr', Size: 2, ArrayType: 'int', Default: [ 0, 0 ] }, { Name: 'ObjDist', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 500, 600 ] }, { Name: 'ObjDeltaDist', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 300, 300 ] }, { Name: 'ObjSideType', Type: 'arr', Size: 2, ArrayType: 'int', Default: [ 0, 0 ] }, { Name: 'ObjSidePos', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 0, 0 ] }, { Name: 'ObjSideVar', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 0, 0 ] }, { Name: 'ObjSizeType', Type: 'arr', Size: 2, ArrayType: 'int', Default: [ 0, 0 ] }, { Name: 'ObjSize', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 65, 100 ] }, { Name: 'ObjSizeVar', Type: 'arr', Size: 2, ArrayType: 'num', Default: [ 0, 0 ] }, { Name: 'refractionCoeff', Type: 'num', Default: 0 }, { Name: 'tempGradient', Type: 'num', Default: -0.0065 }, { Name: 'refractionSync', Type: 'int', Default: 1 }, { Name: 'pressure', Type: 'num', Default: 1013.25 }, { Name: 'temperatureC', Type: 'num', Default: 15 }, { Name: 'refractionFactMin', Type: 'num', Default: 0.5 }, { Name: 'refractionFactMax', Type: 'num', Default: 10000 }, { Name: 'rEarth', Type: 'num', Default: 6371000 }, { Name: 'rFEarth', Type: 'num', Default: 10007543 }, { Name: 'showTheodolite', Type: 'bool', Default: false }, { Name: 'OverlayImage', Type: 'str', Default: '' }, { Name: 'OverlayImageAlpha', Type: 'num', Default: 0.5 }, { Name: 'Credit', Type: 'str', Default: '' }, { Name: 'Flerspective', Type: 'int', Default: 0 }, // feature removed { Name: 'RefDistance', Type: 'num', Default: 10000 }, // feature removed { Name: 'showRefraction', Type: 'bool', Default: true }, { Name: 'showLRDrop', Type: 'bool', Default: false }, { Name: 'UnitsType', Type: 'int', Default: 0 }, { Name: 'ImageOnBackground', Type: 'bool', Default: false }, { Name: 'TextBackground', Type: 'bool', Default: true }, ], }; // Curve App function CurveAppClass() { // choosen units tables this.UnitsType = 0; // 0 -> m, 1 -> mi/ft, 2 -> ft this.LengthUnits = { Selection: '.UnitsType', Units: [ 'm', 'mi', 'ft' ], Mults: [ 1, 1609.344, 0.3048 ] }; this.BigLengthUnits = { Selection: '.UnitsType', Units: [ 'km', 'mi', 'ft' ], Mults: [ 1000, 1609.344, 0.3048 ] }; this.HeightUnits = { Selection: '.UnitsType', Units: [ 'm', 'ft', 'ft' ], Mults: [ 1, 0.3048, 0.3048 ] }; this.GradientUnits = { Selection: '.UnitsType', Units: [ '°C/m', '°C/ft', '°C/ft' ], Mults: [ 1, 1/0.3048, 1/0.3048 ] }; this.AngleFormat = 0; // 0 -> deg, 1 -> DM, 2 -> DMS this.AngleFormats = { Selection: '.AngleFormat', Units: [ '°', '', '' ], Formats: [ 'prec', 'dm', 'dms' ], }; this.DemoText = ''; this.Description = ''; this.Credit = ''; this.LastCredit = ''; this.AllStatesChanged = false; this.OverlayImage = ''; this.OverlayImageAlpha = 0.5; this.ImageOnBackground = false; this.TextBackground = true; this.AlphaOpaque = 1; this.pause = 0; // dummy for animation delays this.rEarth = 6371000; this.rFEarth = 10007543; // this.rEarth * PI90; this.nLines = 45; this.showModel = 1; // 1 -> globe, 2 -> flat, 3 -> both mirrored, 4 -> both side by side this.showGrid = 1; // 0 -> none, 1 -> on, 3 -> projection of globe to flat this.showData = false; this.showRefraction = true; this.showLRDrop = false; this.showTangent = false; this.showEyeLevel = true; this.showTheodolite = false; this.viewcenterHorizon = 0; // 0 -> globe, 1 -> flat earth, 2 -> between, 3 -> eye level this.Diagonal35mmEquivalent = 43.2666153; // refraction this.BaroModel = null; // refractionSync = 0 -> off, 1 -> T,P, 2 -> std, 3 -> k=0.13, 4 -> k=0.17, 5 -> a=7/6, 6 -> a=7/2 this.refractionSync = 1; this.refractionFactMax = 10000; this.refractionFactMin = 0.5; this.pressure = 1013.25; // mbar this.pressureLast = 1013.25; this.temperature = 288.15; // K this.temperatureC = 15; // Celsius this.temperatureCLast = 15; this.tempGradient = -0.0065; // K/m this.tempGradientLast = -0.0065; this.refractionCoeff = 0; this.refractionCoeffLast = 0; this.refractionFact = 1; this.refractionFactLast = 1; this.refractionRadius = this.rEarth; this.refractionRadiusLast = this.rEarth; this.refractionSlider = 0; this.refractionSliderLast = 0; this.HeightSlider = 0; this.HeightSliderLast = 0; this.Height = 100; this.ViewAngle = 0; // viewAngle in deg this.ViewAngleField = 0; this.ViewAngleSlider = 0; this.ViewAngleSliderLast = 0; this.Roll = 0; this.Tilt = 0; this.LastTilt = 0; this.TiltSlider = 0; this.LastTiltSlider = 0; this.Pan = 0; this.LastPan = 0; this.PanSlider = 0; this.LastPanSlider = 0; this.PanRad = 0; this.FocalLength = 0; this.FocalLengthField = 33.9; this.FocalLengthSlider = 0; this.FocalLengthSliderLast = 0; this.rDisk = 0; // d this.dHorizon = 0; // s this.zDisk = 0; // p this.hDisk = 0; // R - b this.hDip = 0; // b this.aDip = 0; // alpha this.aEarth = 0; // 180 - 2 * alpha this.dView = 0; // v this.aDelta = 0; // about aDip / nLines this.posEarth = [ 0, 0, -this.refractionRadius ]; this.deviceRatio = 3 / 2; // width / height of device screen this.sceneWidth = 0; this.sceneHeight = 0; this.horizonLftRgtWidth = 0; this.horizonLftRgtWidthAngle = 0; this.horizonLftRgtDist = 0; this.horizonLftRgtDropAngle = 0; this.horizonLftRgtDrop = 0; this.camViewAngle = 0.1; // rad this.camPos = [ 0, 0, 0 ]; this.camUp = [ 0, 0, 1 ]; this.camViewCenter = [ 0, 1, 0 ]; this.camSceneSize = 1; this.TheodoliteTilt = 0; // special additional flat earth perspective transformations (flerspective) this.Flerspective = 0; // (0 -> normal, 1 -> flerspective) feature removed this.RefDistance = 10000; // feature removed // object settings this.ObjType = [ 3, 3 ]; // 0 = Bedford, 1 = scale, 2 = ship, 3 = T-Tower, 4 = city, 5 = mountain, 6 = bridge, 7 = tensas bayou, 8 = Causew-S, 9 = Causew-N, 10 = Wind Turbine, 11 = Rainy Lake, 12 = Cloud this.ObjSideType = [ 0, 0 ]; // 0 = lin, 1 = 2col, 2 = rand, 3 = cos, 4 = sin this.ObjSizeType = [ 0, 0 ]; // 0 = lin, 1 = 2col, 2 = rand, 3 = cos, 4 = sin this.ObjDist = [ 500, 600 ]; this.Slider_ObjDist_Log = [ 0, 0 ]; this.Slider_ObjDist_Log_Last = [ 0, 0 ]; this.ObjSidePos = [ 0, 0 ]; this.Slider_ObjSidePos_Log = [ 0, 0 ]; this.Slider_ObjSidePos_Log_Last = [ 0, 0 ]; this.ObjSize = [ 65, 100 ]; this.Slider_ObjSize_Log = [ 0, 0 ]; this.Slider_ObjSize_Log_Last = [ 0, 0 ]; this.ObjDeltaDist = [ 300, 300 ]; this.Slider_ObjDeltaDist_Log = [ 0, 0 ]; this.Slider_ObjDeltaDist_Log_Last = [ 0, 0 ]; this.ObjSideVar = [ 0, 0 ]; this.Slider_ObjSideVar_Log = [ 0, 0 ]; this.Slider_ObjSideVar_Log_Last = [ 0, 0 ]; this.ObjSizeVar = [ 0, 0 ]; this.NObjects = [ 0, 0 ]; // distance offsets in meters for some objects like Rainy Lake poles, from back to front this.ObjDistOffsets = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, [ 0, 0, 0, 122.81, 178.59, 159.18, 50.90, 66.46, 43.70 ], 0 ]; // object draw loop variables this.aObjDelta = [ 0, 0 ]; this.aObjFirst = [ 0, 0 ]; this.aObjLast = [ 0, 0 ]; this.NObjectsDrawn = [ 0, 0 ]; this.MaxNObjectsToDraw = [ 0, 0 ]; this.aObjCurr = [ 0, 0 ]; this.isHiddenObj = [ false, false ]; this.LastPosValid = [ false, false ]; // computed object values this.HorizonRefrAng = 0; this.ObjRefrAng = 0; this.ObjLiftAbs = 0; this.ObjLiftRel = 0; this.HorizonLift = 0; this.ObjNearSize = 0; this.ObjNearTilt = 0; this.ObjAngSize = 0; this.ObjRealDist = 0; // distance along surface taking refraction, ObjDist and ObjSidePos into account this.ObjHidden = 0; this.ObjVisi = 0; // colors; for some elements '' -> do not draw element this.EyeLvlCol = 'magenta'; this.FEEqCol = 'black'; this.FEGridCol = 'black'; this.GlobeGridCol = 'blue'; this.GlobeFGridCol = 'red'; this.TangentCol = 'black'; this.FrameCol = 'black'; this.Update(); } CurveAppClass.prototype.IsShowGlobe = function() { return (this.showModel & 1) || this.showModel == 4; } CurveAppClass.prototype.IsShowFlatEarth = function() { return (this.showModel & 2) || this.showModel == 4; } CurveAppClass.prototype.IsShowBothModels = function() { return this.showModel >= 3; } CurveAppClass.prototype.IsShowBothModelsMirror = function() { return this.showModel == 3; } CurveAppClass.prototype.IsNearestObject = function( objIx ) { // returns true if the current object drawn is the nearest object to the observer return this.NObjectsDrawn[objIx] == this.NObjects[objIx]-1; } CurveAppClass.prototype.IsFurthestObject = function( objIx ) { // returns true if the current object drawn is the furthest object to the observer return this.NObjectsDrawn[objIx] == 0; } CurveAppClass.prototype.GetObjectSizeVar = function( objIx ) { // some object sizes like M-Rod are dependent on ObjSizeVar var sizeVar = 1; if (this.ObjType[objIx] == 1) { // M-Rod make size: var 0.1..11 for values of -1..+1 where 0 -> 1 sizeVar = this.ObjSizeVar[objIx]; if (sizeVar < 0) { sizeVar = 1 + sizeVar; if (sizeVar < 0.1) sizeVar = 0.1; } else { sizeVar = 10 * sizeVar + 1; } } return sizeVar; } CurveAppClass.prototype.IsVariableSizeObject = function( objIx ) { // M-Rod is never variable, all the same size, but ObjSizeVar defines its size too, see GetObjectSizeVar() return this.ObjType[objIx] != 1 && Math.abs(this.ObjSizeVar[objIx]) > 0.01; } function LVal( x ) { return x / CurveApp.LengthUnits.Mults[CurveApp.UnitsType]; } function LUnit() { return ' ' + CurveApp.LengthUnits.Units[CurveApp.UnitsType]; } function HVal( x ) { return x / CurveApp.HeightUnits.Mults[CurveApp.UnitsType]; } function HUnit() { return ' ' + CurveApp.HeightUnits.Units[CurveApp.UnitsType]; } function BVal( x ) { return x / CurveApp.BigLengthUnits.Mults[CurveApp.UnitsType]; } function BUnit() { return ' ' + CurveApp.BigLengthUnits.Units[CurveApp.UnitsType]; } function AVal( x ) { return x / CurveApp.GradientUnits.Mults[CurveApp.UnitsType]; } function AUnit() { return CurveApp.GradientUnits.Units[CurveApp.UnitsType]; } function FormatAngle( ang ) { if (CurveApp.AngleFormat == 0) { return NumFormatter.Format( ang, 'prec', 6, '°' ); } else if (CurveApp.AngleFormat == 1) { return NumFormatter.Format( ang, 'dm', 5, '' ); } else { return NumFormatter.Format( ang, 'dms', 5, '' ); } } CurveAppClass.prototype.Update = function() { // input validation if ( this.rEarth < 100000 ) this.rEarth = 100000; if ( this.rFEarth < 100000 ) this.rFEarth = 100000; if (this.nLines > 200) this.nLines = 200; if (this.nLines < 0) this.nLines = 0; this.showModel = Math.floor(this.showModel); if (this.showModel < 1 || this.showModel > 4) this.showModel = 1; this.showGrid = Math.floor(this.showGrid); if (this.showGrid < 0 || this.showGrid > 3) this.showGrid = 1; if (this.deviceRatio <= 0) this.deviceRatio = DeviceRatioOff; this.viewcenterHorizon = Math.floor(this.viewcenterHorizon); if (this.viewcenterHorizon < 0 || this.viewcenterHorizon > 3) this.viewcenterHorizon = 0; // handle height changes if ( this.HeightSliderLast != this.HeightSlider ) { this.Height = Math.pow( 10, 0 + 9 * this.HeightSlider ); } if ( this.Height < 0.001 ) this.Height = 0.001; if ( this.Height > 1000000000 ) this.Height = 1000000000; this.HeightSlider = ( Math.log10( this.Height ) - 0 ) / 9; this.HeightSliderLast = this.HeightSlider; if (this.BaroModel) { // BaroModel depends on Height this.BaroModel.Update(); } // handle refraction changes // refractionSync: 0 -> off, 1 -> T,P, 2 -> std, 3 -> k=0.13, 4 -> k=0.17, 5 -> a=7/6, 6 -> a=7/2 this.refractionSync = Math.floor(this.refractionSync); if (this.refractionSync < 0 || this.refractionSync > 6) this.refractionSync = 1; if (this.refractionFactMin < 0.1) this.refractionFactMin = 0.1; if (this.refractionFactMin > 1) this.refractionFactMin = 1; if (this.refractionFactMax < 10000) this.refractionFactMax = 10000; var k_min = 1 - 1 / this.refractionFactMin; var k_max = 1 - 1 / this.refractionFactMax; if (this.refractionCoeff < k_min) this.refractionCoeff = k_min; if (this.refractionCoeff > k_max) this.refractionCoeff = k_max; if (this.pressure < 0) this.pressure = 0; if (this.pressure > 1200) this.pressure = 1200; if (this.temperatureC < -100) this.temperatureC = -100; if (this.temperatureC > 100) this.temperatureC = 100; // revert sync mode to T,P or off if some refraction values have changed manually if (this.AllStatesChanged) { // refractionsSync has priority if multiple values have changed if (this.refractionSync == 2 || this.refractionSync == 0) { // ensure that tempGradient has priority this.tempGradientLast = this.tempGradient + 1; this.refractionCoeffLast = this.refractionCoeff; } else { // ensure that refractionCoeff has priority this.refractionCoeffLast = this.refractionCoeff + 1; this.tempGradientLast = this.tempGradient; } } else { if (this.refractionSync >= 2 && (this.refractionSlider != this.refractionSliderLast || this.tempGradient != this.tempGradientLast || this.refractionCoeff != this.refractionCoeffLast || this.refractionFact != this.refractionFactLast || this.refractionRadius != this.refractionRadiusLast) ) { this.refractionSync = 1; } else if (this.refractionSync > 0 && (this.temperatureC != this.temperatureCLast || this.pressure != this.pressureLast) ) { this.refractionSync = 0; } } // synchronize refraction pressure, temp and gradient with baro settings if (this.refractionSync >= 1) { if (this.Height > 84852) { this.temperatureC = -86.204; this.pressure = 0.00373383; if (this.refractionSync == 2) { this.tempGradient = -0.002; this.tempGradientLast = this.tempGradient - 1; // indicate a change this.refractionCoeffLast = this.refractionCoeff; } } else if (this.BaroModel) { var baroModel = this.BaroModel; this.temperatureC = baroModel.T_C; this.pressure = baroModel.p / 100; if (this.refractionSync == 2) { this.tempGradient = baroModel.alpha; this.tempGradientLast = this.tempGradient - 1; // indicate a change this.refractionCoeffLast = this.refractionCoeff; } } } // handle sync modes if (this.refractionSync == 3) { this.refractionCoeff = 0.13; this.refractionCoeffLast = this.refractionCoeff + 1; // indicate change } else if (this.refractionSync == 4) { this.refractionCoeff = 0.17; this.refractionCoeffLast = this.refractionCoeff + 1; // indicate change } else if (this.refractionSync == 5) { this.refractionFact = 7 / 6; this.refractionFactLast = this.refractionFact + 1; // indicate change } else if (this.refractionSync == 6) { this.refractionFact = 7 / 2; this.refractionFactLast = this.refractionFact + 1; // indicate change } // handle changes of refraction input fields and sliders this.temperature = this.temperatureC + 273.15; if (this.refractionSlider != this.refractionSliderLast) { // slider changed if (Math.abs(this.refractionSlider) < 0.01) this.refractionSlider = 0; // snap to 0 this.refractionCoeff = k_max * this.refractionSlider; } else if (this.refractionCoeff != this.refractionCoeffLast) { // k changed } else if (this.tempGradient != this.tempGradientLast) { // dT/dh changed if (this.temperature < 3) this.temperature = 3; this.refractionCoeff = 503 * (this.pressure / (this.temperature*this.temperature)) * (0.0343 + this.tempGradient); } else if (this.refractionFact != this.refractionFactLast) { // a changed if (this.refractionFact < this.refractionFactMin) this.refractionFact = this.refractionFactMin; if (this.refractionFact > this.refractionFactMax) this.refractionFact = this.refractionFactMax; this.refractionCoeff = 1 - 1 / this.refractionFact; } else if (this.refractionRadius != this.refractionRadiusLast) { // R' changed this.refractionFact = this.refractionRadius / this.rEarth; if (this.refractionFact < this.refractionFactMin) this.refractionFact = this.refractionFactMin; if (this.refractionFact > this.refractionFactMax) this.refractionFact = this.refractionFactMax; this.refractionCoeff = 1 - 1 / this.refractionFact; } // limit some inputs if (this.refractionCoeff < k_min) this.refractionCoeff = k_min; if (this.refractionCoeff > k_max) this.refractionCoeff = k_max; if (this.temperature < 3) this.temperature = 3; if (this.temperature > 10000) this.temperature = 10000; this.temperatureC = this.temperature - 273.15; if (this.pressure < 0.001) this.pressure = 0.001; if (this.pressure > 10000) this.pressure = 10000; if (Math.abs(this.refractionCoeff) < 0.000002) this.refractionCoeff = 0; // compute refraction values this.tempGradient = (this.refractionCoeff * this.temperature * this.temperature) / (503 * this.pressure) - 0.0343; if (Math.abs(this.tempGradient) < 0.000001) this.tempGradient = 0; this.refractionFact = 1 / (1 - this.refractionCoeff); this.refractionRadius = this.rEarth * this.refractionFact; this.refractionSlider = this.refractionCoeff / k_max; // store current refration values for detecting user changes this.refractionSliderLast = this.refractionSlider; this.refractionCoeffLast = this.refractionCoeff; this.tempGradientLast = this.tempGradient; this.refractionFactLast = this.refractionFact; this.refractionRadiusLast = this.refractionRadius; this.temperatureCLast = this.temperatureC; this.pressureLast = this.pressure; // handle ViewAngle and FocalLength changes if ( this.FocalLengthSlider != this.FocalLengthSliderLast ) { var f = (8000-21) * Math.pow(this.FocalLengthSlider,2) + 21; // f = 21..8000 this.ViewAngle = toDeg( 2 * Math.atan( this.Diagonal35mmEquivalent / 2 / f ) ); } else if ( this.FocalLengthField != this.FocalLength ) { this.ViewAngle = toDeg( 2 * Math.atan( this.Diagonal35mmEquivalent / 2 / this.FocalLengthField ) ); } else if ( this.ViewAngleSlider != this.ViewAngleSliderLast ) { this.ViewAngle = this.ViewAngleSlider; } else if ( this.ViewAngleField != this.ViewAngle ) { this.ViewAngle = this.ViewAngleField; } if ( this.ViewAngle < 0.1 ) this.ViewAngle = 0.1; if ( this.ViewAngle > 160 ) this.ViewAngle = 160; this.camViewAngle = toRad( this.ViewAngle ); this.FocalLength = this.Diagonal35mmEquivalent / ( 2 * Math.tan( this.camViewAngle / 2 ) ); this.FocalLengthField = this.FocalLength; var f = this.FocalLength - 21; if (f < 0) f = 0; this.FocalLengthSlider = Math.pow(f/(8000-21),1/2); this.FocalLengthSliderLast = this.FocalLengthSlider; this.ViewAngleField = this.ViewAngle; this.ViewAngleSlider = this.ViewAngle; this.ViewAngleSliderLast = this.ViewAngle; this.UpdateObjectInput(0); this.UpdateObjectInput(1); // compute diverse values this.aDip = Math.acos( this.refractionRadius / (this.refractionRadius + this.Height) ); this.aEarth = Math.PI - 2 * this.aDip; this.rDisk = this.refractionRadius * Math.sin( this.aDip ); this.dHorizon = this.refractionRadius * this.aDip; this.hDisk = this.refractionRadius * Math.cos( this.aDip ); this.hDip = this.refractionRadius - this.hDisk; this.dView = ( this.Height + this.refractionRadius ) * Math.sin( this.aDip ); var exp2 = Math.floor( Math.log( PI90 / this.aDip ) / Math.LN2 ); this.aDelta = (PI90 / Math.pow( 2, exp2 ) / this.nLines); this.dDelta = this.IsShowGlobe() ? this.aDelta * this.refractionRadius : 0; this.posEarth = [ 0, 0, -(this.refractionRadius + this.Height) ]; this.zDisk = (this.refractionRadius + this.Height) - ( this.refractionRadius * Math.cos( this.aDip ) ); // Tilt and Pan slider synchronization if (this.Tilt != this.LastTilt) { if (this.Tilt < -85) this.Tilt = -85; if (this.Tilt > 45) this.Tilt = 45; if (this.Tilt > 0) { this.TiltSlider = Math.sqrt( this.Tilt / 45 ); } else { this.TiltSlider = - Math.sqrt( -this.Tilt / 85 ); } } else if (this.TiltSlider != this.LastTiltSlider) { if (this.TiltSlider > 0) { this.Tilt = Math.pow( this.TiltSlider, 2 ) * 45; } else { this.Tilt = - Math.pow( this.TiltSlider, 2 ) * 85; } } this.LastTilt = this.Tilt; this.LastTiltSlider = this.TiltSlider; if (this.Pan != this.LastPan) { if (this.Pan < -180) this.Pan = -180; if (this.Pan > 180) this.Pan = 180; if (this.Pan > 0) { this.PanSlider = Math.sqrt( this.Pan / 90 ); } else { this.PanSlider = - Math.sqrt( -this.Pan / 90 ); } } else if (this.PanSlider != this.LastPanSlider) { if (this.PanSlider > 0) { this.Pan = Math.pow( this.PanSlider, 2 ) * 90; } else { this.Pan = - Math.pow( this.PanSlider, 2 ) * 90; } } this.PanRad = toRad( this.Pan ); this.LastPan = this.Pan; this.LastPanSlider = this.PanSlider; // compute camViewCenter from panning if (this.Roll < -180) this.Roll = -180; if (this.Roll > 180) this.Roll = 180; this.CompCameraParams( this.PanRad, this.Tilt, this.Roll ); // compute scene size taking device ratio into account var vpRatio = 3 / 2; var diag = 2 * this.dView * Math.tan( this.camViewAngle / 2 ); this.sceneHeight = diag / Math.sqrt( 1 + this.deviceRatio*this.deviceRatio ); this.sceneWidth = this.deviceRatio * this.sceneHeight; if ( this.deviceRatio > vpRatio ) { // device is wider then viewport this.camSceneSize = this.sceneWidth / vpRatio; } else { this.camSceneSize = this.sceneHeight; } // compute horizon left right drop (0 = whole earth in view, can't calculate left right drop) var yFieldOfView2 = toRad( this.ViewAngle ) / Math.sqrt( 1 + 1 / (this.deviceRatio*this.deviceRatio) ) / 2; var width2HorLRDrop = this.dView * Math.sin( yFieldOfView2 ); if (width2HorLRDrop < this.rDisk) { this.horizonLftRgtWidth = width2HorLRDrop * 2; this.horizonLftRgtWidthAngle = toDeg( 2 * Math.asin( this.horizonLftRgtWidth / (2 * this.dView) ) ); this.horizonLftRgtDist = Math.sqrt( this.rDisk*this.rDisk - width2HorLRDrop*width2HorLRDrop ); var angVertHorLRDrop = Math.atan( this.horizonLftRgtDist / this.zDisk ); var angHorLRDropRad = Math.PI/2 - this.aDip - angVertHorLRDrop; this.horizonLftRgtDropAngle = toDeg( angHorLRDropRad ); this.horizonLftRgtDrop = this.dView * angHorLRDropRad; } else { this.horizonLftRgtDist = 0; this.horizonLftRgtWidth = 0; this.horizonLftRgtWidthAngle = 0; this.horizonLftRgtDropAngle = 0; this.horizonLftRgtDrop = 0; } function compObjVect( dist, side, size, rad, h ) { var R = rad + size; var a1 = side / rad; var a2 = dist / rad; var r = R * Math.cos( a1 ); var vx = R * Math.sin( a1 ); var vy = r * Math.sin( a2 ); var vz = r * Math.cos( a2 ) - (rad + h); return [ vx, vy, vz ]; } function compViewDist( v ) { return Math.sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] ); } function compVectAng( v1, v2 ) { var sp = JsgVect3.ScalarProd( JsgVect3.Norm(v1), JsgVect3.Norm(v2) ); if (sp > 1) sp = 1; // handle rounding errors if (sp < -1) sp = -1; // handle rounding errors return Math.acos( sp ); } // horizon refraction if (Math.abs(this.rEarth - this.refractionRadius) > 1e-5) { var aDip_norm = Math.acos( this.rEarth / (this.rEarth + this.Height) ); var dView_norm = ( this.Height + this.rEarth ) * Math.sin( aDip_norm ); var v_norm = compObjVect( dView_norm, 0, 0, this.rEarth, this.Height ); var v_refr = compObjVect( this.dView, 0, 0, this.refractionRadius, this.Height ); this.HorizonRefrAng = toDeg( compVectAng( v_norm, v_refr ) ); if (this.refractionCoeff < 0) this.HorizonRefrAng *= -1; } else { this.HorizonRefrAng = 0; } // some computed object values var objIx = 0; if (this.NObjects[0] == 0) objIx = 1; if (this.NObjects[0] > 0 && this.NObjects[1] > 0 && this.ObjDist[0] > this.ObjDist[1]) objIx = 1; if (this.NObjects[objIx] > 0) { this.ObjNearSize = this.GetObjectSizeVar(objIx) * this.ObjSize[objIx]; var v_base_refr = compObjVect( this.ObjDist[objIx], this.ObjSidePos[objIx], 0, this.refractionRadius, this.Height ); var v_top_refr = compObjVect( this.ObjDist[objIx], this.ObjSidePos[objIx], this.ObjNearSize, this.refractionRadius, this.Height ); var v_base_norm = compObjVect( this.ObjDist[objIx], this.ObjSidePos[objIx], 0, this.rEarth, this.Height ); var objUpDir = JsgVect3.Sub( v_top_refr, v_base_refr ); this.ObjNearTilt = toDeg( compVectAng( [0,0,1], objUpDir ) ); this.ObjAngSize = toDeg( compVectAng( v_base_refr, v_top_refr ) ); if (Math.abs(this.ObjAngSize) < 1e-5) this.ObjAngSize = 0; this.ObjRefrAng = toDeg( compVectAng( v_base_norm, v_base_refr ) ); if (Math.abs(this.ObjRefrAng) < 1e-5) this.ObjRefrAng = 0; if (this.refractionCoeff < 0) this.ObjRefrAng *= -1; this.ObjLiftAbs = 0; this.ObjLiftRel = 0; this.HorizonLift = 0; if (this.ObjAngSize != 0) { this.ObjLiftAbs = this.ObjNearSize * this.ObjRefrAng / this.ObjAngSize; this.HorizonLift = this.ObjNearSize * this.HorizonRefrAng / this.ObjAngSize; this.ObjLiftRel = this.ObjLiftAbs - this.HorizonLift; } // distance from origin to object base along surface var v_pos = compObjVect( this.ObjDist[objIx], this.ObjSidePos[objIx], 0, this.refractionRadius, -this.refractionRadius ); var aDist = compVectAng( v_pos, [ 0, 0, this.refractionRadius ] ); this.ObjRealDist = aDist * this.refractionRadius; // compute hidden part if (aDist > this.aDip) { // object lies behind horizon var cosaHorObj = Math.cos( aDist - this.aDip ); this.ObjHidden = this.refractionRadius * ( 1 - cosaHorObj ) / cosaHorObj; this.ObjVisi = this.ObjNearSize - this.ObjHidden; if (this.ObjVisi < 0) this.ObjVisi = 0; } else { // object lies in front of horizon this.ObjHidden = 0; this.ObjVisi = this.ObjNearSize; } } else { this.ObjRefrAng = this.HorizonRefrAng; this.ObjLiftAbs = 0; this.HorizonLift = 0; this.ObjLiftRel = 0; this.ObjAngSize = 0; this.ObjHidden = 0; this.ObjVisi = 0; } this.AllStatesChanged = false; // display credit if (this.Credit != this.LastCredit) { ShowCredit( this.Credit ); this.LastCredit = this.Credit; } } CurveAppClass.prototype.CompCameraParams = function( pan, tilt, roll ) { var dvc, avc; dvc = Math.sqrt( this.rDisk * this.rDisk + this.zDisk * this.zDisk ); if (this.viewcenterHorizon == 0) { // view center is globe horizon avc = this.aDip; } else if (this.viewcenterHorizon == 1) { // view center is flat earth equator avc = Math.atan( this.Height / this.rFEarth ); } else if (this.viewcenterHorizon == 2) { // view center is between globe horizon and flat earth equator avc = (Math.atan( this.Height / this.rFEarth ) + this.aDip) / 2; } else { // view center is eye level avc = 0; } avc -= toRad( tilt ); if ( avc > 0.9999*PI90 ) avc = 0.9999*PI90; if ( avc < -0.9999*PI90 ) avc = -0.9999*PI90; this.TheodoliteTilt = -avc * 180 / Math.PI; var zvc = - dvc * Math.sin( avc ); var rvc = dvc * Math.cos( avc ); var xvc = rvc * Math.sin( pan ); var yvc = rvc * Math.cos( pan ); this.camViewCenter = [ xvc, yvc, zvc ]; var nvc = JsgVect3.Norm( this.camViewCenter ); var v = JsgVect3.Norm( [ xvc, yvc, 0 ] ); var n = JsgVect3.Mult( v, [ 0, 0, 1 ] ); var up = JsgVect3.Mult( n, nvc ); // compute camera up and pos var a = toRad( roll ); this.camUp = JsgVect3.Add( JsgVect3.Scale( n, Math.sin(a) ), JsgVect3.Scale( up, Math.cos(a) ) ); this.camPos = [ 0, 0, 0 ]; } CurveAppClass.prototype.UpdateObjectInput = function( objIx ) { // object settings this.NObjects[objIx] = Math.round(this.NObjects[objIx]); if (this.NObjects[objIx] < 0) this.NObjects[objIx] = 0; if (this.NObjects[objIx] > 500) this.NObjects[objIx] = 500; if (this.Slider_ObjDist_Log_Last[objIx] != this.Slider_ObjDist_Log[objIx]) { var distSign = this.Slider_ObjDist_Log[objIx] < 0 ? -1 : 1; var distVal = Math.abs( this.Slider_ObjDist_Log[objIx] ); if (distVal < 1) { this.ObjDist[objIx] = 100 * distVal; } else { this.ObjDist[objIx] = 10 * Math.pow( 10, this.Slider_ObjDist_Log[objIx] ); } this.ObjDist[objIx] *= distSign; } var distLimit = this.refractionRadius * Math.PI / 2; if (this.ObjDist[objIx] < -distLimit) this.ObjDist[objIx] = -distLimit; if (this.ObjDist[objIx] > distLimit) this.ObjDist[objIx] = distLimit; var distSign = this.ObjDist[objIx] < 0 ? -1 : 1; var distVal = Math.abs( this.ObjDist[objIx] ); if (distVal < 100) { this.Slider_ObjDist_Log[objIx] = distVal / 100; } else { this.Slider_ObjDist_Log[objIx] = Math.log10( distVal / 10 ); } this.Slider_ObjDist_Log[objIx] *= distSign; this.Slider_ObjDist_Log_Last[objIx] = this.Slider_ObjDist_Log[objIx]; if (this.Slider_ObjSidePos_Log[objIx] != this.Slider_ObjSidePos_Log_Last[objIx]) { var sidePosSign = this.Slider_ObjSidePos_Log[objIx] < 0 ? -1 : 1; var sidePosVal = Math.abs( this.Slider_ObjSidePos_Log[objIx] ); if (sidePosVal < 1) { this.ObjSidePos[objIx] = 100 * sidePosVal; } else { this.ObjSidePos[objIx] = 10 * Math.pow( 10, sidePosVal ); } this.ObjSidePos[objIx] *= sidePosSign; } var sidePosLimit = this.refractionRadius * Math.PI / 4; if (this.ObjSidePos[objIx] < -sidePosLimit) this.ObjSidePos[objIx] = -sidePosLimit; if (this.ObjSidePos[objIx] > sidePosLimit) this.ObjSidePos[objIx] = sidePosLimit; var sidePosSign = this.ObjSidePos[objIx] < 0 ? -1 : 1; var sidePosVal = Math.abs( this.ObjSidePos[objIx] ); if (sidePosVal < 100) { this.Slider_ObjSidePos_Log[objIx] = sidePosVal / 100; } else { this.Slider_ObjSidePos_Log[objIx] = Math.log10( sidePosVal / 10 ); } this.Slider_ObjSidePos_Log[objIx] *= sidePosSign; this.Slider_ObjSidePos_Log_Last[objIx] = this.Slider_ObjSidePos_Log[objIx]; if (this.Slider_ObjSideVar_Log[objIx] != this.Slider_ObjSideVar_Log_Last[objIx]) { var sideVarSign = this.Slider_ObjSideVar_Log[objIx] < 0 ? -1 : 1; var sideVarVal = Math.abs( this.Slider_ObjSideVar_Log[objIx] ); if (sideVarVal < 1) { this.ObjSideVar[objIx] = 10 * sideVarVal; } else { this.ObjSideVar[objIx] = Math.pow( 10, sideVarVal ); } this.ObjSideVar[objIx] *= sideVarSign; } var sideVarLimit = this.refractionRadius * Math.PI / 4; if (this.ObjSideVar[objIx] < -sideVarLimit) this.ObjSideVar[objIx] = -sideVarLimit; if (this.ObjSideVar[objIx] > sideVarLimit) this.ObjSideVar[objIx] = sideVarLimit; var sideVarSign = this.ObjSideVar[objIx] < 0 ? -1 : 1; var sideVarVal = Math.abs( this.ObjSideVar[objIx] ); if (sideVarVal < 10) { this.Slider_ObjSideVar_Log[objIx] = sideVarVal / 10; } else { this.Slider_ObjSideVar_Log[objIx] = Math.log10( sideVarVal ); } this.Slider_ObjSideVar_Log[objIx] *= sideVarSign; this.Slider_ObjSideVar_Log_Last[objIx] = this.Slider_ObjSideVar_Log[objIx]; if (this.Slider_ObjSize_Log[objIx] != this.Slider_ObjSize_Log_Last[objIx]) { this.ObjSize[objIx] = Math.pow( 10, this.Slider_ObjSize_Log[objIx] ); } if (this.ObjSize[objIx] < 0.001) this.ObjSize[objIx] = 0.001; if (this.ObjSize[objIx] > 1e9) this.ObjSize[objIx] = 1e9; this.Slider_ObjSize_Log[objIx] = Math.log10( this.ObjSize[objIx] ); this.Slider_ObjSize_Log_Last[objIx] = this.Slider_ObjSize_Log[objIx]; if (this.Slider_ObjDeltaDist_Log[objIx] != this.Slider_ObjDeltaDist_Log_Last[objIx]) { this.ObjDeltaDist[objIx] = 10 * Math.pow( 10, this.Slider_ObjDeltaDist_Log[objIx] ); } var deltaDistLimit = this.refractionRadius * PI90; if (this.ObjDeltaDist[objIx] < 0.001) this.ObjDeltaDist[objIx] = 0.001; if (this.ObjDeltaDist[objIx] > deltaDistLimit) this.ObjDeltaDist[objIx] = deltaDistLimit; this.Slider_ObjDeltaDist_Log[objIx] = Math.log10( this.ObjDeltaDist[objIx] / 10 ); this.Slider_ObjDeltaDist_Log_Last[objIx] = this.Slider_ObjDeltaDist_Log[objIx]; } var CurveApp = new CurveAppClass(); DataX.AssignApp( 'CurveApp', CurveApp, CurveAppMetaData, function(){ResetApp();}, function(){UpdateAll(false);} ); DataX.AssignSaveRestoreDomObj( 'SaveRestorePanel' ); DataX.SetupUrlStateHandler( 'App' ); var UpdateAllRunning = false; function UpdateAll( stopAnimation ) { if (UpdateAllRunning) return; UpdateAllRunning = true; try { stopAnimation = xDefBool( stopAnimation, true ); if (stopAnimation) { Demos.Reset(); //CurveApp.DemoText = ''; //CurveApp.Description = ''; ShowMoreInfos(); } CurveApp.Update(); ControlPanels.Update(); graph.Redraw(); } finally { UpdateAllRunning = false; } } xOnLoad( function() { HandleUrlCommands(); UpdateAll( false ); } ); var graph = NewGraphX3D( { Id: 'JsGraph1', Width: '100%', Height: '66.67%', DrawFunc: DrawModel, OnClick: function(e){ if (CurveApp.OverlayImage == '') { Demos.PlayStop(); } else { CurveApp.OverlayImageAlpha = e.offsetX / (0.9*graph.CanvasWidth) - 0.05; if (CurveApp.OverlayImageAlpha > 1) CurveApp.OverlayImageAlpha = 1; if (CurveApp.OverlayImageAlpha < 0) CurveApp.OverlayImageAlpha = 0; UpdateAll(); } }, AutoReset: true, AutoClear: true, AutoScalePix: true } ); function DrawModel( g ) { var m = CurveApp; g.MaxCurveSegments = 256; g.SetAngleMeasure( 'rad' ); g.SetViewport( 0, 1, -0.5, -2 ); g.SetGraphClipping( true, '', 0 ); g.SetCameraClipping( 0.001 ); g.SetWindowToCameraScreen(); g.SetCamera( { SceneSize: m.camSceneSize, CamPos: m.camPos, CamUp: m.camUp, CamViewCenter: m.camViewCenter, } ); g.SetCameraZoom( 1 ); var specialLineWidth = 1.5; // draw overlay image in background if (m.ImageOnBackground && m.OverlayImage != '' && m.OverlayImageAlpha > 0) { g.SetAlpha( m.OverlayImageAlpha ); var imgUrl = m.OverlayImage; if (imgUrl.indexOf('http') != 0) imgUrl = MEDIA_FOLDER + imgUrl; g.DrawImage( imgUrl, 'canvas-in' ); g.SetAlpha( m.AlphaOpaque ) } // draw eye level var rotz = JsgMat3.RotatingZ( -m.PanRad ); if ( m.showEyeLevel || m.showTheodolite ) { g.SetLineAttr( m.EyeLvlCol, 1 ); g.SetAlpha( m.AlphaOpaque ); g.Line3D( JsgMat3.Trans( rotz, [ -2*m.sceneWidth, m.rDisk, 0 ] ), JsgMat3.Trans( rotz, [ 2*m.sceneWidth, m.rDisk, 0 ] ) ); var ta = 'center'; if ( m.showTheodolite ) { ta = 'left'; } g.SetTextAttr( 'Arial', 12, m.EyeLvlCol, 'normal', 'normal', ta, 'bottom', 6 ); g.SetTextRotation( toRad(-m.Roll) ); g.Text3D( 'Eye-Level', JsgMat3.Trans( rotz, [ 0, m.rDisk, 0 ] ) ); g.SetTextRotation( 0 ); } // Flat Earth var objIx = 0; if (m.NObjects[0] == 0) objIx = 1; if ( m.IsShowFlatEarth() ) { // set clipping if both models are shown to restrict graphic to one half of the screen if (m.IsShowBothModels()) { var xpos = 0; g.SetClipRect( xpos, 0, g.CanvasWidth/2, g.CanvasHeight, 'canvas' ); } if (m.IsShowBothModelsMirror()) { m.CompCameraParams( -m.PanRad, m.Tilt, m.Roll ); g.SetCamera( { CamPos: m.camPos, CamUp: m.camUp, CamViewCenter: m.camViewCenter, } ); } // draw flat earth horizon and equator g.SetAlpha( m.AlphaOpaque ); g.SetPlane( [ 0, 0, -m.Height ], [ 1, 0, 0 ], [ 0, 1, 0 ] ); g.SetLineAttr( m.FEEqCol, 1 ); g.CircleOnPlane( 0, 0, m.rFEarth, 1 ); g.SetLineWidth( 2 ); g.CircleOnPlane( 0, 0, 2*m.rFEarth, 1 ); var aMax = 2 * Math.PI; var alpha = m.AlphaOpaque * (0.6 - 0.5 * (Math.log10( m.Height ) / 9)); g.SetAlpha( alpha ); g.SetLineAttr( m.FEGridCol, 1 ); if (m.showGrid > 0) { // circle lines var crDelta = m.rFEarth / 12; var crMax = 2 * m.rFEarth - crDelta / 2; g.SetPlane( [ 0, 0, -m.Height ], [ 1, 0, 0 ], [ 0, 1, 0 ] ); for ( var cr = crDelta; cr < crMax; cr += crDelta ) { g.ArcOnPlane( 0, 0, cr, 0, aMax, 1 ); } if (m.Height < 700000) { crMax = crDelta; crDelta /= 10; crMax -= crDelta / 2; for ( var cr = crDelta; cr < crMax; cr += crDelta ) { g.ArcOnPlane( 0, 0, cr, 0, aMax, 1 ); } } if (m.Height < 30000) { crMax = crDelta; crDelta /= 10; crMax -= crDelta / 2; for ( var cr = crDelta; cr < crMax; cr += crDelta ) { g.ArcOnPlane( 0, 0, cr, 0, aMax, 1 ); } } if (m.Height < 3000) { crMax = crDelta; crDelta /= 10; crMax -= crDelta / 2; for ( var cr = crDelta; cr < crMax; cr += crDelta ) { g.ArcOnPlane( 0, 0, cr, 0, aMax, 1 ); } } if (m.Height < 300) { crMax = crDelta; crDelta /= 10; crMax -= crDelta / 2; for ( var cr = crDelta; cr < crMax; cr += crDelta ) { g.ArcOnPlane( 0, 0, cr, 0, aMax, 1 ); } } // ray lines g.SetAlpha( alpha ); g.SetLineAttr( m.FEGridCol, 1 ); var caDelta = Math.PI / 12; var caMax = 2 * Math.PI; caMax -= caDelta / 2; var r = 2 * m.rFEarth; for ( var ca = 0; ca < caMax; ca += caDelta ) { var c = Math.cos( ca ); var s = Math.sin( ca ); g.LineOnPlane( r * c, r * s, 0, 0 ); } } var drawObjIx = 1; if (m.ObjDist[0] > m.ObjDist[1]) drawObjIx = 0; m.LastPosValid[0] = false; m.LastPosValid[1] = false; DrawObjectsBeforeHorizon( drawObjIx, g, false ); drawObjIx = (drawObjIx + 1) % 2; DrawObjectsBeforeHorizon( drawObjIx, g, false ); // reset inverse panning if (m.IsShowBothModelsMirror()) { m.CompCameraParams( m.PanRad, m.Tilt, m.Roll ); g.SetCamera( { CamPos: m.camPos, CamUp: m.camUp, CamViewCenter: m.camViewCenter, } ); } // reset clipping if (m.IsShowBothModels()) { g.SetClipping( 'canvas' ); } } // end nLines > 0 // Globe Earth if (m.IsShowGlobe()) { // set clipping if both models are shown to restrict graphic to one half of the screen if (m.IsShowBothModels()) { var xpos = g.CanvasWidth / 2; g.SetClipRect( xpos, 0, g.CanvasWidth/2, g.CanvasHeight, 'canvas' ); } var drawObjIx = 1; if (m.ObjDist[0] > m.ObjDist[1]) drawObjIx = 0; m.LastPosValid[0] = false; m.LastPosValid[1] = false; DrawObjectsBehindHorizon( drawObjIx, g ); drawObjIx = (drawObjIx + 1) % 2; DrawObjectsBehindHorizon( drawObjIx, g ); var alpha = m.AlphaOpaque * (0.6 - 0.5 * (Math.log10( m.Height ) / 9) ); g.SetAlpha( alpha ); g.SetLineAttr( m.GlobeGridCol, 1 ); // show globe grid if ( m.showGrid & 1 ) { // latitude lines var latMax = m.aDip; var latStart = -( Math.floor( latMax / m.aDelta ) * m.aDelta ); for ( var lat = latStart; lat < latMax; lat += m.aDelta ) { var dLatPlaneDisk = m.hDisk / Math.cos( lat ); var longMax = Math.acos( dLatPlaneDisk / m.refractionRadius ); var longStart = -( Math.floor( longMax / m.aDelta ) * m.aDelta ); g.NewPoly3D(); for ( var long = longStart; long < longMax; long += m.aDelta ) { g.AddPointToPoly3D( PointOnEarth( lat, long ) ); } g.AddPointToPoly3D( PointOnEarth( lat, longMax ) ); g.DrawPoly3D( 1 ); } // longitude lines var longMax = m.aDip; var longStart = -( Math.floor( longMax / m.aDelta ) * m.aDelta ); for ( var long = longStart; long < longMax; long += m.aDelta ) { var rLong = m.refractionRadius * Math.cos( long ); var latMax = Math.acos( m.hDisk / rLong ); var latStart = -( Math.floor( latMax / m.aDelta ) * m.aDelta ); g.NewPoly3D(); g.AddPointToPoly3D( PointOnEarth( -latMax, long ) ); for ( var lat = latStart; lat < latMax; lat += m.aDelta ) { g.AddPointToPoly3D( PointOnEarth( lat, long ) ); } g.AddPointToPoly3D( PointOnEarth( latMax, long ) ); g.DrawPoly3D( 1 ); } } // end show globe grid g.SetLineAttr( m.GlobeFGridCol, 1 ); // show flat grid if ( m.showGrid & 2 ) { // latitude lines on flat model var latMax = m.aDip; var latStart = -( Math.floor( latMax / m.aDelta ) * m.aDelta ); for ( var lat = latStart; lat < latMax; lat += m.aDelta ) { var dLatPlaneDisk = m.hDisk / Math.cos( lat ); var longMax = Math.acos( dLatPlaneDisk / m.refractionRadius ); var longStart = -( Math.floor( longMax / m.aDelta ) * m.aDelta ); g.NewPoly3D(); for ( var long = longStart; long < longMax; long += m.aDelta ) { g.AddPointToPoly3D( PointOnPlane( lat, long ) ); } g.AddPointToPoly3D( PointOnPlane( lat, longMax ) ); g.DrawPoly3D( 1 ); } // longitude lines on flat model var longMax = m.aDip; var longStart = -( Math.floor( longMax / m.aDelta ) * m.aDelta ); for ( var long = longStart; long < longMax; long += m.aDelta ) { var rLong = m.refractionRadius * Math.cos( long ); var latMax = Math.acos( m.hDisk / rLong ); var latStart = -( Math.floor( latMax / m.aDelta ) * m.aDelta ); g.NewPoly3D(); g.AddPointToPoly3D( PointOnPlane( -latMax, long ) ); for ( var lat = latStart; lat < latMax; lat += m.aDelta ) { g.AddPointToPoly3D( PointOnPlane( lat, long ) ); } g.AddPointToPoly3D( PointOnPlane( latMax, long ) ); g.DrawPoly3D( 1 ); } } // end flat grid if ( m.showGrid & 2 ) { // horizon on flat model g.SetPlane( [ 0, 0, -m.Height ], [ 1, 0, 0 ], [ 0, 1, 0 ] ); g.SetAlpha( m.AlphaOpaque ); g.SetLineAttr( m.GlobeFGridCol, specialLineWidth ); g.CircleOnPlane( 0, 0, m.rDisk, 1 ); } // Globe Horizon g.SetAlpha( m.AlphaOpaque ); g.SetLineAttr( m.GlobeGridCol, specialLineWidth ); g.SetPlane( [ 0, 0, -m.zDisk ], [ 1, 0, 0 ], [ 0, 1, 0 ] ); g.CircleOnPlane( 0, 0, m.rDisk, 1 ); // show tangent line to globe horizon if ( m.showTangent || m.showData || m.showLRDrop ) { g.SetLineAttr( m.TangentCol, 1 ); g.Line3D( JsgMat3.Trans( rotz, [ -2*m.sceneWidth, m.rDisk, -m.zDisk ] ), JsgMat3.Trans( rotz, [ 2*m.sceneWidth, m.rDisk, -m.zDisk ] ) ); } // show left-right drop line if ( m.showLRDrop && m.horizonLftRgtWidth > 0) { g.SetLineAttr( 'red', 1 ); g.Line3D( JsgMat3.Trans( rotz, [ -m.horizonLftRgtWidth/2, m.horizonLftRgtDist, -m.zDisk ] ), JsgMat3.Trans( rotz, [ m.horizonLftRgtWidth/2, m.horizonLftRgtDist, -m.zDisk ] ) ); g.SetMarkerAttr( 'ArrowUp', g.ScalePix(10), 'black', 'white', 1 ); g.Marker3D( JsgMat3.Trans( rotz, [ -m.horizonLftRgtWidth/2, m.horizonLftRgtDist, -m.zDisk ] ) ); g.Marker3D( JsgMat3.Trans( rotz, [ m.horizonLftRgtWidth/2, m.horizonLftRgtDist, -m.zDisk ] ) ); } // draw all objects in the foreground var drawObjIx = 1; if (m.ObjDist[0] > m.ObjDist[1]) drawObjIx = 0; DrawObjectsBeforeHorizon( drawObjIx, g, true ); drawObjIx = (drawObjIx + 1) % 2; DrawObjectsBeforeHorizon( drawObjIx, g, true ); // reset clipping if (m.IsShowBothModels()) { g.SetClipping( 'canvas' ); } } // end model globe if ( m.showTheodolite ) { var xDir = JsgVect3.Mult( g.Camera.ViewDir, g.Camera.CamUp ); var yDir = JsgVect3.Mult( g.Camera.ViewDir, xDir ); var diag = Math.sqrt( m.sceneWidth*m.sceneWidth + m.sceneHeight*m.sceneHeight ); g.SetPlane( g.Camera.CamViewCenter, xDir, yDir, true ); // measurements var numFormat = { Mode: 'fix0', Precision: 4, UsePrefix: false, Units: '' }; var txt = FormatAngle( m.TheodoliteTilt ); g.SetBgColor( 'white' ); g.SetTextAttr( 'Arial', 12, 'black', 'normal', 'normal', 'left', 'bottom', 5, 2 ); g.SetAlpha( 0.8 * m.AlphaOpaque ); if (m.TextBackground) { g.Rect( g.GetTextBoxOnPlane( txt, 0.1*m.sceneWidth, 0 ), 2 ); } g.SetAlpha( m.AlphaOpaque ); g.TextOnPlane( txt, 0.1*m.sceneWidth, 0 ); var txt = FormatAngle( m.Pan ); g.SetTextAttr( 'Arial', 12, 'black', 'normal', 'normal', 'left', 'top', 5, 2 ); g.SetAlpha( 0.8 * m.AlphaOpaque ); if (m.TextBackground) { g.Rect( g.GetTextBoxOnPlane( txt, 0, -0.35*m.sceneHeight ), 2 ); } g.SetAlpha( m.AlphaOpaque ); g.TextOnPlane( txt, 0, -0.35*m.sceneHeight ); // crosshair g.SetAlpha( 0.8 * m.AlphaOpaque ); g.SetLineAttr( 'white', 3 ); g.LineOnPlane( -0.7*m.sceneWidth/2, 0, -0.05*m.sceneWidth/2, 0 ); g.LineOnPlane( 0.05*m.sceneWidth/2, 0, 0.7*m.sceneWidth/2, 0 ); g.LineOnPlane( 0, -0.7*m.sceneHeight/2, 0, -0.05*m.sceneHeight/2 ); g.LineOnPlane( 0, 0.7*m.sceneHeight/2, 0, 0.05*m.sceneHeight/2 ); g.SetAlpha( m.AlphaOpaque ); g.SetLineAttr( 'black', 1 ); g.LineOnPlane( -0.7*m.sceneWidth/2, 0, -0.02*m.sceneWidth/2, 0 ); g.LineOnPlane( 0.7*m.sceneWidth/2, 0, 0.02*m.sceneWidth/2, 0 ); g.LineOnPlane( 0, -0.7*m.sceneHeight/2, 0, -0.02*m.sceneHeight/2 ); g.LineOnPlane( 0, 0.7*m.sceneHeight/2, 0, 0.02*m.sceneHeight/2 ); // theodolite scope border g.SetAreaAttr( 'gray', 'gray', 2 ); g.SetAlpha( 0.8 * m.AlphaOpaque ); g.OpenPath3D(); g.RectOnPlane( -diag, -diag, diag, diag ); g.CircleOnPlane( 0, 0, -0.35*diag ); g.Path3D( 3 ); g.SetAlpha( m.AlphaOpaque ); } // label split screen if (m.IsShowBothModels()) { var oldTrans = g.SelectTrans( 'viewport' ); // label left var txt = 'Flat Earth'; g.SetTextAttr( 'Arial', 16, 'black', 'bold', 'normal', 'left', 'top', 10 ); g.SetAreaAttr( 'yellow', 'white', 1 ); if (m.TextBackground) { g.TextBox( txt, 0, 0, 3 ); } g.Text( txt, 0, 0 ); // label right var txt = 'Globe'; g.SetTextAttr( 'Arial', 16, 'black', 'bold', 'normal', 'right', 'top', 10 ); g.SetAreaAttr( 'yellow', 'white', 1 ); if (m.TextBackground) { g.TextBox( txt, g.VpInnerWidth, 0, 3 ); } g.Text( txt, g.VpInnerWidth, 0 ); // split line g.SetLineAttr( '#ddd', 1 ); g.Line( g.VpInnerWidth/2, 0, g.VpInnerWidth/2, g.VpInnerHeight ); g.SelectTrans( oldTrans ); } // draw frame if (m.deviceRatio != DeviceRatioOff && m.FrameCol != '') { g.SetAlpha( m.AlphaOpaque ); g.SetLineAttr( m.FrameCol, 2 ); var xDir = JsgVect3.Mult( g.Camera.ViewDir, g.Camera.CamUp ); var yDir = JsgVect3.Mult( g.Camera.ViewDir, xDir ); g.SetPlane( g.Camera.CamViewCenter, xDir, yDir, true ); g.RectOnPlane( -m.sceneWidth/2, -m.sceneHeight/2, m.sceneWidth/2, m.sceneHeight/2, 1 ); } var numFormat = { Mode: 'std', Precision: 4, UsePrefix: false, Units: '', }; var numFormat4 = { Mode: 'std', Precision: 4, UsePrefix: false, Units: '', }; // draw left-right drop data var textSize = 12; var lineHeight = g.ScalePix(17); var tm = g.ScalePix(5); // text margin if ( m.showLRDrop && m.IsShowGlobe() && m.horizonLftRgtWidth > 0) { var posDropLine, align, offset; posDropLine = JsgMat3.Trans( rotz, [ 0.46 * m.horizonLftRgtWidth, m.horizonLftRgtDist, -m.zDisk ] ); align = 'right'; offset = g.ScalePix(40); var posDropLineWin = g.VTransPoint3D( posDropLine ); var posDropLineVp = g.TransXY( posDropLineWin[0], posDropLineWin[1] ); var posDropLineX = posDropLineVp.x; var posDropLineY = posDropLineVp.y - g.ScalePix(1); var posTextX = posDropLineX + offset; var posTextY = posDropLineY + g.ScalePix(30); if (posTextX < tm) posTextX = tm; if (posTextX > g.VpInnerWidth-tm) posTextX = g.VpInnerWidth-tm; // show left-right drop data g.SetTextAttr( 'Arial', textSize, 'black', 'normal', 'normal', align, 'bottom', 4 ); g.SetMarkerAttr( 'Arrow1', 6, 'black', 'black', 1 ); var oldTrans = g.SelectTrans( 'viewport' ); g.Arrow( posDropLineX, posTextY, posDropLineX, posDropLineY, 9 ); g.SetBgColor( 'white' ); var txt = 'Left-Right Drop Angle = ' + FormatAngle( m.horizonLftRgtDropAngle ); var ty = posTextY + lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Left-Right Width Angle = ' + FormatAngle( m.horizonLftRgtWidthAngle ); ty += lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Left-Right Drop = ' + NumFormatter.NumToString( HVal(m.horizonLftRgtDrop), numFormat ) + HUnit(); ty += lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Left-Right Width = ' + NumFormatter.NumToString( LVal(m.horizonLftRgtWidth), numFormat ) + LUnit(); ty += lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); g.SelectTrans( oldTrans ); } // draw data if (m.showData && m.IsShowGlobe()) { var posHorizon, align, offset; posHorizon = JsgMat3.Trans( rotz, [ 0.44 * m.sceneWidth, m.rDisk, -m.zDisk ] ); align = 'right'; offset = g.ScalePix(40); var posHorizonWin = g.VTransPoint3D( posHorizon ); var posHorizonVp = g.TransXY( posHorizonWin[0], posHorizonWin[1] ); var posHorizonX = posHorizonVp.x; var posHorizonY = posHorizonVp.y - g.ScalePix(2); var posTextX = posHorizonX + offset; var posTextY = posHorizonY - g.ScalePix(30); if (posTextX < tm) posTextX = tm; if (posTextX > g.VpInnerWidth-tm) posTextX = g.VpInnerWidth-tm; // show horizon data g.SetTextAttr( 'Arial', textSize, 'black', 'normal', 'normal', align, 'bottom', 4 ); g.SetMarkerAttr( 'Arrow1', 6, 'black', 'black', 1 ); var oldTrans = g.SelectTrans( 'viewport' ); g.Arrow( posHorizonX, posTextY, posHorizonX, posHorizonY, 9 ); g.SetBgColor( 'white' ); var txt = 'Grid Spacing = ' + NumFormatter.NumToString( LVal(m.dDelta), numFormat ) + LUnit(); var ty = posTextY; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Dip from Surface b = ' + NumFormatter.NumToString( HVal(m.hDip), numFormat ) + HUnit(); ty -= lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Dip from Eye-Level p = ' + NumFormatter.NumToString( HVal(m.zDisk), numFormat ) + HUnit(); ty -= lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Dip Angle alpha = ' + FormatAngle( toDeg( m.aDip ) ); ty -= lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var txt = 'Distance on Surface s = ' + NumFormatter.NumToString( LVal(m.dHorizon), numFormat ) + LUnit(); ty -= lineHeight; if (m.TextBackground) { g.TextBox( txt, posTextX, ty, 2 ); } g.Text( txt, posTextX, ty ); var ty = g.VpInnerHeight - 3.25 * lineHeight; if (m.NObjects[objIx] > 0) { // show object data g.SetTextAttr( 'Arial', 12, 'black', 'normal', 'normal', 'center', 'bottom', 4 ); g.SetBgColor( 'white' ); ty -= 5.5 * lineHeight; var tx = g.VpInnerWidth / 2; if (m.IsShowBothModels()) { tx *= 1.5; } var txt = 'Object Visible = ' + NumFormatter.NumToString( HVal(m.ObjVisi), numFormat4 ) + HUnit() + '; Hidden = ' + NumFormatter.NumToString( HVal(m.ObjHidden), numFormat4 ) + HUnit(); g.SetBgColor( '#ffddff' ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; var eq = ' = '; var a = m.ObjAngSize; if (a < 1e-5) a = 0; if (m.IsVariableSizeObject(objIx)) eq = ' <= '; var txt = 'Object Size ' + eq + NumFormatter.NumToString( HVal(m.ObjNearSize), numFormat4 ) + HUnit() + '; ' + FormatAngle( a ); g.SetBgColor( 'white' ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; var txt = 'Object Distance = ' + NumFormatter.NumToString( LVal(m.ObjRealDist), numFormat4 ) + LUnit() + '; Tilt = ' + FormatAngle( m.ObjNearTilt ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; // show refraction data ty += 0.5 * lineHeight; var txt = 'Object Lift Relativ to Horizon = ' + NumFormatter.NumToString( HVal(m.ObjLiftRel), numFormat4 ) + HUnit(); g.SetBgColor( '#ffddff' ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; var txt = 'Object Lift Absolute = ' + NumFormatter.NumToString( HVal(m.ObjLiftAbs), numFormat4 ) + HUnit(); g.SetBgColor( 'white' ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; var txt = 'Horizon Lift = ' + NumFormatter.NumToString( HVal(m.HorizonLift), numFormat4 ) + HUnit(); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; g.SetTextColor( 'darkred' ); var txt = 'Refraction Angle = ' + FormatAngle( m.ObjRefrAng ); if (m.TextBackground) { g.TextBox( txt, tx, ty, 2 ); } g.Text( txt, tx, ty ); ty += lineHeight; } g.SelectTrans( oldTrans ); } // show refraction data if ((m.IsShowGlobe()) && (Math.abs(m.refractionCoeff) > 0.001 || (m.showData && m.NObjects[objIx] > 0))) { function refractionClass( k ) { var r_class = ''; if (k >= 0.78) { r_class = 'extreme'; } else if (k >= 0.58) { r_class = 'severe'; } else if (k >= 0.38) { r_class = 'strong'; } else if (k >= 0.18) { r_class = 'medium'; } else if (k >= 0.12) { r_class = 'standard'; } else if (k > 0.0001) { r_class = 'weak'; } else if (k < 0) { r_class = 'negative'; } if (r_class != '') r_class = '(' + r_class + ')'; return r_class; } function tempGradClass( dt ) { var t_class = ''; if (dt < -0.01) { t_class = 'instable Layer'; } else { t_class = 'stable Layer'; } if (dt > 0) { t_class += '; Inversion'; } if (t_class != '') t_class = '(' + t_class + ')'; return t_class; } var tx = g.VpInnerWidth / 2; if (m.IsShowBothModels()) { tx *= 1.5; } var oldTrans = g.SelectTrans( 'viewport' ); g.SetTextAttr( 'Arial', 12, 'darkred', 'normal', 'normal', 'center', 'bottom', 4 ); if (m.refractionCoeff < -1e-5) { g.SetBgColor( '#ffeeaa' ); } else if (m.refractionCoeff > 1e-5) { g.SetBgColor( '#ffff80' ); } else { g.SetBgColor( 'white' ); } var oldFormatMode = numFormat.Mode; var oldPrecision = numFormat.Precision; numFormat.Mode = 'fix0'; if (m.showRefraction) { var txt = 'Refraction k = ' + NumFormatter.NumToString( m.refractionCoeff, numFormat ); if (m.refractionSync == 2) { txt += ' (Standard Atmosphere)'; } else { txt += ' ' + refractionClass( m.refractionCoeff ); } if (m.TextBackground) { g.TextBox( txt, tx, g.VpInnerHeight - g.ScalePix(21), 2 ); } g.Text( txt, tx, g.VpInnerHeight - g.ScalePix(21) ); if (m.Height < 84852) { numFormat.Precision = 5; var txt = ''; if (m.tempGradient < 1) { txt = 'Temp. Gradient dT/dh = ' + NumFormatter.NumToString( AVal(m.tempGradient), numFormat ) + AUnit(); } txt += tempGradClass( m.tempGradient ); if (m.TextBackground) { g.TextBox( txt, tx, g.VpInnerHeight - g.ScalePix(5), 2 ); } g.Text( txt, tx, g.VpInnerHeight - g.ScalePix(5) ); } } numFormat.Mode = oldFormatMode; numFormat.Precision = oldPrecision; g.SelectTrans( oldTrans ); } // draw overlay image in foreground if (!m.ImageOnBackground && m.OverlayImage != '' && m.OverlayImageAlpha > 0) { g.SetAlpha( m.OverlayImageAlpha ); var imgUrl = m.OverlayImage; if (imgUrl.indexOf('http') != 0) imgUrl = MEDIA_FOLDER + imgUrl; g.DrawImage( imgUrl, 'canvas-in' ); g.SetAlpha( m.AlphaOpaque ) } // draw demo text and description if (m.DemoText != '') { var oldTrans = g.SelectTrans( 'viewport' ); g.SetTextAttr( 'Arial', 20, 'black', 'normal', 'normal', 'center', 'top', 10 ); g.SetBgColor( 'white' ); //if (m.OverlayImage != '') { if (m.TextBackground) { g.TextBox( m.DemoText, g.VpInnerWidth/2, 0, 2 ); } //} g.Text( m.DemoText, g.VpInnerWidth/2, 0 ); if (m.Description != '') { g.SetTextAttr( 'Arial', 16, 'black', 'normal', 'normal', 'center', 'top', 10 ); //if (m.OverlayImage != '') { if (m.TextBackground) { g.TextBox( m.Description, g.VpInnerWidth/2, g.ScalePix(30), 2 ); } //} g.Text( m.Description, g.VpInnerWidth/2, g.ScalePix(30) ); } g.SelectTrans( oldTrans ); } } function DrawObjectsBehindHorizon( objIx, g ) { var m = CurveApp; // init some loop variables used in DrawObjectsBeforeHorizon too if (m.NObjects[objIx] == 0) return; m.aObjDelta[objIx] = m.ObjDeltaDist[objIx] / m.refractionRadius; m.aObjFirst[objIx] = m.ObjDist[objIx] / m.refractionRadius; m.aObjLast[objIx] = (m.NObjects[objIx]-1) * m.aObjDelta[objIx] + m.aObjFirst[objIx]; m.MaxNObjectsToDraw[objIx] = m.NObjects[objIx]; m.NObjectsDrawn[objIx] = 0; if (m.aObjLast[objIx] > Math.PI) { m.MaxNObjectsToDraw[objIx] = Math.floor( Math.PI / m.aObjDelta[objIx] ); m.aObjLast[objIx] = (m.MaxNObjectsToDraw[objIx]-1) * m.aObjDelta[objIx] + m.aObjFirst[objIx]; m.NObjectsDrawn[objIx] = m.NObjects[objIx] - m.MaxNObjectsToDraw[objIx]; } m.aObjCurr[objIx] = m.aObjLast[objIx]; // if only objects before horizon exist skip this function var aSide = m.ObjSidePos[objIx] / m.refractionRadius; var sideOnDisk = (m.refractionRadius - m.hDip) * Math.tan( aSide ); var aClip = 0; if (m.rDisk > Math.abs(sideOnDisk)) { // not all objects are behind the horizon, so find the first object that is behind var distOnDisk = Math.sqrt( m.rDisk * m.rDisk - sideOnDisk * sideOnDisk ); aClip = Math.asin( distOnDisk / m.refractionRadius ); } if (m.aObjCurr[objIx] <= aClip) return; m.isHiddenObj[objIx] = true; // draw all objects behind horizon only and clip then at horizon g.SetAlpha( m.AlphaOpaque ); g.SetAreaAttr( '#ffa', '#666', 1 ); g.SaveTrans3D(); var aLimit = m.aObjFirst[objIx] - m.aObjDelta[objIx]/2; while (m.aObjCurr[objIx] > aClip && m.aObjCurr[objIx] > aLimit ) { DrawShapeVariants( objIx, g, m, true ); m.NObjectsDrawn[objIx]++; m.aObjCurr[objIx] -= m.aObjDelta[objIx]; } g.RestoreTrans3D(); // clip at horizon if (m.NObjectsDrawn[objIx] > 0) { g.SetPlane( [0,0,-m.zDisk], [1,0,0], [0,1,0] ); g.SetBgColor( 'white' ); g.CircleOnPlane( 0, 0, m.rDisk, 2 ); } } function DrawObjectsBeforeHorizon( objIx, g, bOnGlobe ) { var m = CurveApp; if (m.NObjects[objIx] == 0) return; // Init parameters for flat earth here, because DrawObjectsBehindHorizon is not caller in that case if (!bOnGlobe) { m.aObjDelta[objIx] = m.ObjDeltaDist[objIx] / m.refractionRadius; m.aObjFirst[objIx] = m.ObjDist[objIx] / m.refractionRadius; m.aObjLast[objIx] = (m.NObjects[objIx]-1) * m.aObjDelta[objIx] + m.aObjFirst[objIx]; m.MaxNObjectsToDraw[objIx] = m.NObjects[objIx]; m.aObjCurr[objIx] = m.aObjLast[objIx]; m.NObjectsDrawn[objIx] = 0; } var aLimit = m.aObjFirst[objIx] - m.aObjDelta[objIx]/2; if (m.aObjCurr[objIx] < aLimit) return; m.isHiddenObj[objIx] = false; g.SetAlpha( m.AlphaOpaque ); g.SetAreaAttr( 'yellow', 'black', 1 ); g.SaveTrans3D(); while (m.aObjCurr[objIx] >= aLimit) { DrawShapeVariants( objIx, g, m, bOnGlobe ); m.aObjCurr[objIx] -= m.aObjDelta[objIx]; m.NObjectsDrawn[objIx]++; } g.RestoreTrans3D(); } function SetTrans( objIx, g, m, lng, lat, size, alt, bOnGlobe ) { var objType = m.ObjType[objIx]; var lngOffset = m.ObjDistOffsets[objType]; if (xArray(lngOffset)) { var nDraw = m.NObjectsDrawn[objIx]; lngOffset = lngOffset[nDraw % lngOffset.length] / m.refractionRadius; } var d = 0; var s = 0; var xs = m.IsShowBothModelsMirror() && !bOnGlobe ? -m.ObjSize[objIx] : m.ObjSize[objIx]; g.ResetTrans3D(); g.TransScale3D( xs, m.ObjSize[objIx], m.ObjSize[objIx] ); g.TransMove3D( 0, 0, m.refractionRadius+alt ); if (bOnGlobe) { g.TransRotateY3D( lat ); g.TransRotateX3D( -(lng + lngOffset) ); } else { d = lng * m.refractionRadius; s = lat * m.refractionRadius; if (m.IsShowBothModelsMirror()) s *= -1; } g.TransMove3D( s, d, -(m.refractionRadius+m.Height) ); } function DrawShapeVariants( objIx, g, m, bOnGlobe ) { // size: 0 -> lin, 1 -> alt, 2 -> rand, 3 -> cos, 4 -> sin var size = 1; var sizeType = m.ObjSizeType[objIx]; var objType = m.ObjType[objIx]; if (objType == 1 || objType == 12) { // M-Rod and clouds only scale linearly with size, size type is ignored and ObjSizeVar defines the scale or cloud size (num of divisions of scale) } else if (sizeType == 0) { // lin if (m.MaxNObjectsToDraw[objIx] > 1) { size = 1 + m.ObjSizeVar[objIx] * (2 / (m.MaxNObjectsToDraw[objIx] - 1) * (m.MaxNObjectsToDraw[objIx]-1 - m.NObjectsDrawn[objIx]) - 1); } } else if (sizeType == 1) { // alt size = 1 + m.ObjSizeVar[objIx] * Math.cos( Math.PI * m.NObjectsDrawn[objIx] ); } else if (sizeType == 2) { // rand size = 1 + m.ObjSizeVar[objIx] * ( (64894678.9798467 * Math.sqrt(m.NObjectsDrawn[objIx])) % 2 - 1 ); } else if (sizeType == 3) { // cos if (m.MaxNObjectsToDraw[objIx] > 1) { size = 1 - m.ObjSizeVar[objIx] * Math.cos( 2 * Math.PI * m.NObjectsDrawn[objIx]/(m.MaxNObjectsToDraw[objIx]-1) ); } else { size = 1 + m.ObjSizeVar[objIx]; } } else if (sizeType == 4) { // sin if (m.MaxNObjectsToDraw[objIx] > 1) { size = 1 - m.ObjSizeVar[objIx] * Math.sin( 2 * Math.PI * m.NObjectsDrawn[objIx]/(m.MaxNObjectsToDraw[objIx]-1) ); } else { size = 1; } } // make size varying from 0..1 size = (size + 1 - Math.abs(m.ObjSizeVar[objIx])) / 2; if (size < 0) size *= -1; if (objType == 1 || objType == 12) { // dont scale M-Rod and clouds by sizeVar size = 1; } var sizeHor = 1; if (objType == 0 || objType == 1 || objType == 3 || objType == 12) { // Bedford, M-Rod, T-Tower, cloud: scale width proportionaly sizeHor = size; } else if (objType == 2) { // ship: scale width inverse proportional to size within some limits sizeHor = (1 - 0.75) * size + 0.75; size = (1 - 0.5) * size + 0.5; } else if (objType == 4) { // city: scale only height } else if (objType == 6) { // bridge: scale only height } else if (objType == 7) { // tensas bayou: no scale size = 1; sizeHor = 1; } else if (objType == 8 || objType == 11) { // Causew-S, Rainy Lake: no scale size = 1; sizeHor = 1; } else if (objType == 9) { // Causew-N: no scale, horizontally mirrored size = 1; sizeHor = -1; } var sideVar = m.ObjSideVar[objIx] / m.refractionRadius / 2; var side = m.ObjSidePos[objIx] / m.refractionRadius; var sideType = m.ObjSideType[objIx]; if (objType == 11) { // Rainy Lake size var controls side offsets, so sideVar is ignored here SetTrans( objIx, g, m, m.aObjCurr[objIx], side, m.ObjSize[objIx], 0, bOnGlobe ); g.SetPlane( [0,0,0], [sizeHor,0,0], [0,0,size] ); DrawShape( objIx, g, m, bOnGlobe ); } else if (sideType == 0) { // lin if (m.MaxNObjectsToDraw[objIx] > 1) { sideVar *= (2 / (m.MaxNObjectsToDraw[objIx] - 1) * (m.MaxNObjectsToDraw[objIx]-1 - m.NObjectsDrawn[objIx]) - 1); } SetTrans( objIx, g, m, m.aObjCurr[objIx], side+sideVar, m.ObjSize[objIx], 0, bOnGlobe ); g.SetPlane( [0,0,0], [sizeHor,0,0], [0,0,size] ); DrawShape( objIx, g, m, bOnGlobe ); } else if (sideType == 1) { // 2col var pos1 = side - sideVar; var pos2 = side + sideVar; if (Math.abs(pos1) < Math.abs(pos2)) { // invert drawing sequence var tmp = pos1; pos1 = pos2; pos2 = tmp; } // assert: pos1 is farther to the side than pos2 -> |pos1| >= |pos2| m.Col = 1; g.SetPlane( [0,0,0], [sizeHor,0,0], [0,0,size] ); SetTrans( objIx, g, m, m.aObjCurr[objIx], pos1, m.ObjSize[objIx], 0, bOnGlobe ); DrawShape( objIx, g, m, bOnGlobe ); m.Col = 2; SetTrans( objIx, g, m, m.aObjCurr[objIx], pos2, m.ObjSize[objIx], 0, bOnGlobe ); g.SetPlane( [0,0,0], [sizeHor,0,0], [0,0,size] ); DrawShape( objIx, g, m, bOnGlobe ); } else if (sideType == 2) { // rand sideVar *= (186573.6498496 * Math.sqrt(m.NObjectsDrawn[objIx]) % 2) - 1; g.SetPlane( [0,0,0], [sizeHor,0,0], [0,0,size] ); SetTrans( objIx, g, m, m.aObjCurr[objIx], side+sideVar, m.ObjSize[objIx], 0, bOnGlobe ); DrawShape( objIx, g, m, bOnGlobe ); } else if (sideType == 3) { // cos if (m.MaxNObjectsToDraw[objIx] > 1) { sideVar *= Math.cos( 2 * Math.PI * m.NObjectsDrawn[objIx]/(m.MaxNObjectsToDraw[objIx]-1) ); } g.SetPlane( [side,0,0], [sizeHor,0,0], [0,0,size] ); SetTrans( objIx, g, m, m.aObjCurr[objIx], side+sideVar, m.ObjSize[objIx], 0, bOnGlobe ); DrawShape( objIx, g, m, bOnGlobe ); } else if (sideType == 4) { // sin if (m.MaxNObjectsToDraw[objIx] > 1) { sideVar *= Math.sin( 2 * Math.PI * m.NObjectsDrawn[objIx]/(m.MaxNObjectsToDraw[objIx]-1) ); } else { sideVar = 0; } g.SetPlane( [side,0,0], [sizeHor,0,0], [0,0,size] ); SetTrans( objIx, g, m, m.aObjCurr[objIx], side+sideVar, m.ObjSize[objIx], 0, bOnGlobe ); DrawShape( objIx, g, m, bOnGlobe ); } } function DrawShape( objIx, g, m, bOnGlobe ) { // plane and transformations are set function setBgColor( normalColor, hiddenColor ) { if (m.isHiddenObj[objIx]) { g.SetBgColor( hiddenColor ); } else { g.SetBgColor( normalColor ); } } // objType: 0 = Bedford, 1 = M-Rod, 2 = ship, 3 = T-Tower, 4 = city, 5 = mountain, 6 = bridge, 7 = tensas bayou, 8 = Causew-S, 9 = Causew-N, 10 = Wind Turbine, 11 = Rainy Lake, 12 = Clouds var objType = m.ObjType[objIx]; if (objType == 0) { // Bedford if (m.IsFurthestObject(objIx)) { // draw bridge with bar setBgColor( '#aaa', '#ccc' ); g.OpenPath3D(); g.ArcOnPlane( 0, 0, -0.8, Math.PI, 0 ); g.PolygonOnPlane( [ 0.8, 1.2, 1.2, -1.2, -1.2, -0.8 ], [ 0, 0, 1.1, 1.1, 0., 0 ] ); g.Path3D( 3 ); setBgColor( 'white', 'white' ); g.RectOnPlane( -0.3, 0.85, 0.3, 1.15, 3 ); setBgColor( 'black', '#444' ); g.RectOnPlane( -0.3, 0.95, 0.3, 1.05, 3 ); } else if (m.NObjects[objIx] > 2 && m.IsNearestObject(objIx)) { // draw scope g.SetAlpha( 0.3 * m.AlphaOpaque ); setBgColor( 'black', 'black' ); g.RectOnPlane( -0.01, 0, 0.01, 0.955, 3 ); g.OpenPath3D(); g.CircleOnPlane( 0, 1, 0.05 ); g.CircleOnPlane( 0, 1, -0.04 ); g.Path3D( 3 ); g.SetAlpha( m.AlphaOpaque ); g.SetColor( 'orange' ); g.LineOnPlane( -0.04, 1, 0.04, 1 ); g.LineOnPlane( -0.02, 1-0.02, -0.005, 1-0.005 ); g.LineOnPlane( 0.02, 1-0.02, 0.005, 1-0.005 ); g.LineOnPlane( -0.02, 1+0.02, -0.005, 1+0.005 ); g.LineOnPlane( 0.02, 1+0.02, 0.005, 1+0.005 ); } else { // draw rods setBgColor( 'yellow', '#ff4' ); g.RectOnPlane( -0.025, 0, 0.025, 1, 3 ); setBgColor( 'red', '#f44' ); g.CircleOnPlane( 0, 1, 0.06, 3 ); //g.CircleOnPlane( 0, 0.698, 0.06, 3 ); } } else if (objType == 1) { // M-Rod var sizeVar = m.GetObjectSizeVar( objIx ); setBgColor( 'yellow', '#ff4' ); g.RectOnPlane( -0.1, 0, 0.1, sizeVar, 2 ); setBgColor( 'red', '#f66' ); for (var z = 0.5; z < sizeVar; z += 1) { var t = z + 0.5; if (t > sizeVar) t = sizeVar; g.RectOnPlane( -0.1, z, 0, t, 2 ); } for (var z = 0.1; z < sizeVar; z += 0.2 ) { var t = z + 0.1; if (t > sizeVar) t = sizeVar; g.RectOnPlane( 0, z, 0.1, t, 2 ); } g.RectOnPlane( -0.1, 0, 0.1, sizeVar, 1 ); if (m.showData && m.IsNearestObject(objIx)) { // label object size var x1 = 0.12; var x2 = 0.22; if (m.IsShowBothModelsMirror() && !bOnGlobe) { x1 = -0.12; x2 = -0.22; } g.LineOnPlane( x1, sizeVar, x2, sizeVar ); g.SetTextAttr( 'Arial', 12, 'black', 'normal', 'normal', 'left', 'bottom', 0 ); var numFormat = { Mode: 'fix', Precision: 2, UsePrefix: false, Units: '' }; var txt = NumFormatter.NumToString( HVal(m.ObjSize[objIx]*sizeVar), numFormat ) + HUnit(); g.TextOnPlane( txt, x2, sizeVar ); } } else if (objType == 2) { // ship setBgColor( '#654130', '#937162' ); g.PolygonOnPlane( [-0.15, 0.15, 0.2, -0.2, -0.15], [0, 0, 0.15, 0.15, 0], 3 ); g.LineOnPlane( 0, 0, 0, 1.05 ); setBgColor( '#7b899a', '#a9b6c6' ); g.PolygonOnPlane( [-0.2, -0.1, 0.1, 0.2, 0.3, -0.3, -0.2], [0.15, 0.2, 0.2, 0.15, 0.4, 0.4, 0.15], 3 ); setBgColor( '#b5bdc3', '#d4d9dd' ); g.PolygonOnPlane( [-0.3, -0.1, 0.1, 0.3, 0.25, -0.25, -0.3], [0.4, 0.45, 0.45, 0.4, 0.6, 0.6, 0.4], 3 ); setBgColor( '#dfe0df', '#eff0ef' ); g.PolygonOnPlane( [-0.25, -0.1, 0.1, 0.25, 0.2, -0.2, -0.25], [0.6, 0.65, 0.65, 0.6, 0.8, 0.8, 0.6], 3 ); g.PolygonOnPlane( [-0.2, -0.05, 0.05, 0.2, 0.1, -0.1, -0.2], [0.8, 0.85, 0.85, 0.8, 1, 1, 0.8], 3 ); } else if (objType == 3) { // T-Tower if (objIx == 0) { // Tower Type 1 g.PolygonOnPlane( [ 0.09, -0.12, -0.06, 0.14, 0.12, 0.12, 0.16, 0.16, 0.14, 0.06, -0.06, -0.14, -0.12, -0.12, -0.16, -0.16, -0.14, 0.06, 0.12, -0.09 ], [0.31, 0.14, 0.48, 0.82, 0.89, 0.93, 1, 0.89, 0.82, 0.48, 0.48, 0.82, 0.89, 0.93, 1, 0.89, 0.82, 0.48, 0.14, 0.31 ], 1 ); g.PolygonOnPlane( [ 0.06, -0.09, 0.09, -0.06 ], [ 0.48, 0.31, 0.31, 0.48 ], 1 ); g.PolygonOnPlane( [ -0.28, 0.28, 0.16, -0.16, -0.28 ], [ 0.89, 0.89, 0.93, 0.93, 0.89 ], 1 ); setBgColor( '#a38d60', '#dcc493' ); g.PolygonOnPlane( [ -0.145, -0.095, -0.095, 0.095, 0.095, 0.145, 0.145, -0.145, -0.145 ], [ 0, 0, 0.105, 0.105, 0, 0, 0.137, 0.137, 0 ], 3 ); } else { // Tower Type 2 g.PolygonOnPlane( [ -0.12, -0.02, -0.02, 0.02, 0.02, 0.12 ], [ 0.08, 0.76, 1, 1, 0.76, 0.08 ], 1 ); g.PolygonOnPlane( [ -0.02, -0.08, 0.08, 0.02 ], [ 1, 0.98, 0.98, 1 ], 1 ); g.PolygonOnPlane( [ -0.02, -0.07, 0.07, 0.02, -0.02 ], [ 0.94, 0.92, 0.92, 0.94, 0.94 ], 1 ); g.PolygonOnPlane( [ -0.02, -0.07, 0.07, 0.02, -0.02 ], [ 0.86, 0.84, 0.84, 0.86, 0.86 ], 1 ); g.PolygonOnPlane( [ -0.02, -0.07, 0.07, 0.02, -0.02 ], [ 0.78, 0.76, 0.76, 0.78, 0.78 ], 1 ); g.PolygonOnPlane( [ -0.12, 0.0906, -0.0685, 0.0494, -0.02 ], [ 0.08, 0.28, 0.43, 0.56, 0.76 ], 1 ); g.PolygonOnPlane( [ 0.12, -0.0906, 0.0685, -0.0494, 0.02 ], [ 0.08, 0.28, 0.43, 0.56, 0.76 ], 1 ); g.LineOnPlane( -0.0906, 0.28, 0.0906, 0.28 ); g.LineOnPlane( -0.0685, 0.43, 0.0685, 0.43 ); g.LineOnPlane( -0.0494, 0.56, 0.0494, 0.56 ); setBgColor( '#a38d60', '#dcc493' ); g.PolygonOnPlane( [ -0.14, -0.1, -0.1, 0.1, 0.1, 0.14, 0.14, -0.14, -0.14 ], [ 0, 0, 0.06, 0.06, 0, 0, 0.08, 0.08, 0 ], 3 ); } } else if (objType == 4) { // city var mi = m.NObjectsDrawn[objIx] % 3; var mr = m.NObjectsDrawn[objIx] % 6; if (mr <= 2) { // mirror x axes g.Plane.XDir[0] *= -1; } if (mi == 0) { setBgColor( '#eca992', '#ecc3b4' ); g.RectOnPlane( -1.5, 0, -1.2, 0.4, 3 ); setBgColor( '#ffeacc', '#fff4e5' ); g.RectOnPlane( -1.45, 0.4, -1.25, 0.7, 3 ); g.RectOnPlane( -1.4, 0.7, -1.3, 0.75, 3 ); g.LineOnPlane( -1.5, 0.3, -1.2, 0.3 ); g.LineOnPlane( -1.45, 0.6, -1.25, 0.6 ); setBgColor( '#586a78', '#7d868d' ); g.RectOnPlane( -0.7, 0, -0.4, 0.5, 3 ); g.RectOnPlane( -0.65, 0.5, -0.45, 0.8, 3 ); g.RectOnPlane( -0.6, 0, -0.5, 1, 3 ); g.LineOnPlane( -0.56, 1, -0.56, 1.2 ); g.LineOnPlane( -0.53, 1, -0.53, 1.1 ); setBgColor( '#f4caa9', '#f4e3d5' ); g.PolygonOnPlane( [ -0.05, -0.05, 0, 0, 0.1, 0.2, 0.2, 0.25, 0.25, -0.05 ], [ 0, 0.45, 0.5, 0.6, 0.75, 0.6, 0.5, 0.45, 0, 0 ], 3 ); g.LineOnPlane( 0, 0, 0, 0.5 ); g.LineOnPlane( 0.2, 0, 0.2, 0.5 ); setBgColor( '#ec7266', '#f3b0a9' ); g.RectOnPlane( 0.4, 0, 0.6, 0.9, 3 ); setBgColor( '#586a78', '#7d868d' ); g.PolygonOnPlane( [ 1.1, 1.2, 1.35, 1.45, 1.1 ], [ 0, 0.8, 0.8, 0, 0 ], 3 ); g.LineOnPlane( 1.4, 0, 1.3, 0.8 ); g.LineOnPlane( 1.25, 0.8, 1.25, 1 ); g.LineOnPlane( 1.3, 0.8, 1.3, 1 ); } else if (mi == 1) { setBgColor( '#fbd9c6', '#fbebe2' ); g.RectOnPlane( -1.7, 0, -1.5, 0.5, 3 ); setBgColor( '#fbebe2', '#fbf5f1' ); g.PolygonOnPlane( [ -1.68, -1.68, -1.6, -1.52, -1.52, -1.68 ], [ 0.5, 0.6, 0.7, 0.6, 0.5, 0.5 ], 3 ); g.LineOnPlane( -1.68, 0.6, -1.52, 0.6 ); setBgColor( '#6d6870', '#9a939e' ); g.RectOnPlane( -1.1, 0, -0.9, 0.55, 3 ); setBgColor( '#dfbb97', '#dfcfbf' ); g.RectOnPlane( -1, 0, -0.8, 0.45, 3 ); g.RectOnPlane( -1.05, 0.55, -0.95, 0.6, 3 ); setBgColor( '#b7997c', '#cdbdad' ); g.RectOnPlane( -0.3, 0, 0.2, 0.4, 3 ); setBgColor( '#ffeac6', '#fff5e5' ); g.RectOnPlane( -0.25, 0.3, 0.15, 0.35, 3 ); setBgColor( '#f3dca7', '#f3e5c5' ); g.RectOnPlane( 0.6, 0, 1, 0.4, 3 ); setBgColor( '#c19f89', '#e2bfa9' ); g.PolygonOnPlane( [ 0.7, 0.7, 0.8, 0.9, 0.9, 0.7 ], [ 0, 0.5, 0.7, 0.5, 0, 0 ], 3 ); setBgColor( '#4b5866', '#7b8591' ); g.RectOnPlane( 1.6, 0, 1.9, 0.6, 3 ); setBgColor( '#012a46', '#3b5465' ); g.RectOnPlane( 1.7, 0, 1.8, 0.65, 3 ); } else { setBgColor( '#eacfae', '#eaddce' ); g.RectOnPlane( -2, 0, -1.8, 0.4, 3 ); setBgColor( '#525462', '#8a8c98' ); g.RectOnPlane( -1.8, 0, -1.7, 0.4, 3 ); setBgColor( '#b59776', '#dac2a8' ); g.RectOnPlane( -1.4, 0, -1, 0.3, 3 ); setBgColor( '#9c504d', '#c18d8b' ); g.RectOnPlane( -1.35, 0.3, -1.05, 0.35, 3 ); setBgColor( '#46352d', '#827067' ); g.RectOnPlane( -0.7, 0, -0.5, 0.3, 3 ); setBgColor( '#e5c9a5', '#efe2d1' ); g.PolygonOnPlane( [ -0.7, -0.6, -0.5 ], [ 0.2, 0.3, 0.2 ], 1 ); setBgColor( '#f5d9b4', '#f5e9d9' ); g.RectOnPlane( -0.3, 0, 0.1, 0.2, 3 ); setBgColor( '#39596b', '#738c9a' ); g.PolygonOnPlane( [ 0.3, 0.4, 0.5, 0.4, 0.3 ], [ 0.4, 0.6, 0.4, 0.2, 0.4 ], 3 ); setBgColor( '#878b8e', '#a9aeb1' ); g.PolygonOnPlane( [ 0.3, 0.3, 0.4, 0.5, 0.5, 0.3 ], [ 0, 0.4, 0.2, 0.4, 0, 0 ], 3 ); g.LineOnPlane( 0.4, 0, 0.4, 0.2 ); setBgColor( '#f5d9b4', '#f5e9d9' ); g.RectOnPlane( 0.6, 0, 0.8, 0.2, 3 ); setBgColor( '#dccace', '#f0dde1' ); g.RectOnPlane( 0.9, 0, 1.2, 0.5, 3 ); setBgColor( '#d3b79e', '#e3d7cc' ); g.RectOnPlane( 1, 0, 1.15, 0.6, 3 ); setBgColor( '#a17268', '#c7a8a2' ); g.PolygonOnPlane( [ 1.4, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2, 2, 1.4 ], [ 0, 0.15, 0.2, 0.15, 0.2, 0.15, 0.2, 0.15, 0, 0 ], 3 ); g.LineOnPlane( 1.6, 0, 1.6, 0.15 ); g.LineOnPlane( 1.8, 0, 1.8, 0.15 ); } } else if (objType == 5) { // mountain var mi = m.NObjectsDrawn[objIx] % 3; var mr = m.NObjectsDrawn[objIx] % 6; mi = (mi + objIx) % 3; if (mr > 2) { // mirror x axes g.Plane.XDir[0] *= -1; } if (mi == 0) { setBgColor( '#dbe2ed', '#e9f0fc' ); g.PolygonOnPlane( [ -1.3, -0.8, -0.6, -0.3, -0.1, 0.1, 0.2, 0.4, 0.5, 0.6, 1.3, -1.3 ], [ 0, 0.6, 0.6, 1, 0.9, 0.7, 0.7, 0.9, 0.8, 0.85, 0, 0 ], 3 ); g.PolygonOnPlane( [ -0.3, 0, 0.3, 0.5, 0.8 ], [ 0.1, 0.2, 0.5, 0.5, 0.4 ], 1 ); g.PolygonOnPlane( [ -0.6, -0.3, 0.1 ], [ 0.6, 0.4, 0.3 ], 1 ); g.PolygonOnPlane( [ -0.2, -0.1, 0.1 ], [ 0.5, 0.6, 0.7 ], 1 ); g.PolygonOnPlane( [ 0.1, 0.2, 0.4, 0.5 ], [ 0.5, 0.6, 0.7, 0.8 ], 1 ); } else if (mi == 1) { setBgColor( '#92aace', '#c4d2e8' ); g.PolygonOnPlane( [ -1.3, -0.8, -0.7, -0.2, 0.1, 0.2, 0.5, 0.6, 0.7, 1.3, -1.3 ], [ 0, 0.5, 0.5, 1, 0.8, 0.7, 0.7, 0.6, 0.6, 0, 0 ], 3 ); g.PolygonOnPlane( [ -0.7, -0.6, -0.3 ], [ 0.5, 0.5, 0.2 ], 1 ); g.LineOnPlane( 0.2, 0.7, 0.7, 0.2 ); g.LineOnPlane( 0.6, 0.6, 1.1, 0.1 ); } else { setBgColor( '#abacb1', '#c8c9cf' ); g.PolygonOnPlane( [ -1.3, -0.7, -0.6, -0.3, -0.3, -0.2, -0.1, 0.1, 0.3, 0.5, 0.6, 0.8, 1, 1.1, 1.3, -1.3 ], [ 0, 0.6, 0.6, 0.8, 0.9, 1, 1, 0.8, 0.5, 0.4, 0.5, 0.5, 0.4, 0.2, 0, 0 ], 3 ); g.PolygonOnPlane( [ -1, -0.6, -0.4, -0.2, 0 ], [ 0.1, 0.4, 0.5, 0.5, 0.3 ], 1 ); g.PolygonOnPlane( [ -0.6, -0.5, -0.4 ], [ 0.6, 0.6, 0.5 ], 1 ); g.PolygonOnPlane( [ -0.2, -0.1, 0 ], [ 1, 0.8, 0.4 ], 1 ); g.PolygonOnPlane( [ 0, 0.3, 0.6, 0.8, 0.9 ], [ 0.1, 0.2, 0.5, 0.4, 0.3 ], 1 ); } } else if (objType == 6) { // bridge // draw road except on SideVarType = rand var p1 = JsgVect3.Null(); var p2 = JsgVect3.Null(); g.GetTransPointOnPlane( -1, 1, p1 ); g.GetTransPointOnPlane( 1, 1, p2 ); if (m.NObjectsDrawn[objIx] > 0 && m.ObjSideType[objIx] != 2) { g.SaveTrans3D( true ); g.NewPoly3D(); if (m.Col == 1) { g.AddPointToPoly3D( m.LastRoadP11 ); g.AddPointToPoly3D( m.LastRoadP12 ); } else { g.AddPointToPoly3D( m.LastRoadP21 ); g.AddPointToPoly3D( m.LastRoadP22 ); } g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); setBgColor( '#cdcac3', '#e8e4dc' ); g.DrawPoly3D( 7 ); g.RestoreTrans3D(); } if (m.Col == 1) { m.LastRoadP11 = JsgVect3.Copy( p1 ); m.LastRoadP12 = JsgVect3.Copy( p2 ); } else { m.LastRoadP21 = JsgVect3.Copy( p1 ); m.LastRoadP22 = JsgVect3.Copy( p2 ); } // draw pilows setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -0.8, 0, -0.55, 0.55, 3 ); g.RectOnPlane( 0.55, 0, 0.8, 0.55, 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -1, 0.55, 1, 0.7, 3 ); setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -1, -1, -0.95, -0.95, 0.95, 0.95, 1, 1, -1 ], [ 1, 0.9, 0.9, 0.7, 0.7, 0.9, 0.9, 1, 1, 1 ], 3 ); } else if (objType == 7) { // tensas bayou var p1 = JsgVect3.Null(); var p2 = JsgVect3.Null(); var nDraw = m.NObjectsDrawn[objIx]; var roadH = 9.5; var roadP = 25.1; var roadW = 6.1; // draw ground if (nDraw <= 8) { var groundH = 4.2 * (m.ObjSizeVar[objIx] + 1); if (nDraw == 8) groundH = 0; g.GetTransPointOnPlane( -100, groundH, p1 ); g.GetTransPointOnPlane( 100, groundH, p2 ); if (m.LastPosValid[objIx]) { g.SaveTrans3D( true ); g.NewPoly3D(); g.AddPointToPoly3D( m.LastGroundP1 ); g.AddPointToPoly3D( m.LastGroundP2 ); g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); if (nDraw == 8) { setBgColor( '#687e28', '#6d8527' ); g.SetColor( '#687e28' ); } else { setBgColor( '#49884b', '#5e9b60' ); g.SetColor( '#49884b' ); } g.DrawPoly3D( 7 ); g.SetColor( 'black' ); g.RestoreTrans3D(); } m.LastGroundP1 = JsgVect3.Copy( p1 ); m.LastGroundP2 = JsgVect3.Copy( p2 ); } // draw roads var roadS = 1; if (m.ObjSidePos[objIx] < 0) roadS = -1; if (nDraw < 2) { roadH = 25.5; roadP = 5.1; roadW = 5; } else if (nDraw < 4) { roadH = nDraw == 2 ? 23.3 : 17.3; roadP = 5.1; roadW = 5; } else if (nDraw < 6) { if (nDraw == 4) roadH = 11.4; roadP = nDraw == 4 ? 11.7 : 18.4; roadW = nDraw == 4 ? 5.4 : 5.8; } roadH *= (m.ObjSizeVar[objIx] + 1); g.GetTransPointOnPlane( roadS*roadP-(roadW-0.4), roadH, p1 ); g.GetTransPointOnPlane( roadS*roadP+(roadW-0.4), roadH, p2 ); g.SetColor( '#838795' ); if (m.LastPosValid[objIx]) { g.SaveTrans3D( true ); g.NewPoly3D(); g.AddPointToPoly3D( m.LastRoadP11 ); g.AddPointToPoly3D( m.LastRoadP12 ); g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); setBgColor( '#999ba2', '#adb1bc' ); g.DrawPoly3D( 7 ); g.RestoreTrans3D(); } m.LastRoadP11 = JsgVect3.Copy( p1 ); m.LastRoadP12 = JsgVect3.Copy( p2 ); roadS *= -1; g.GetTransPointOnPlane( roadS*roadP-(roadW-0.4), roadH, p1 ); g.GetTransPointOnPlane( roadS*roadP+(roadW-0.4), roadH, p2 ); if (m.LastPosValid[objIx]) { g.SaveTrans3D( true ); g.NewPoly3D(); g.AddPointToPoly3D( m.LastRoadP21 ); g.AddPointToPoly3D( m.LastRoadP22 ); g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); setBgColor( '#999ba2', '#adb1bc' ); g.DrawPoly3D( 7 ); g.RestoreTrans3D(); } g.SetColor( 'black' ); m.LastRoadP21 = JsgVect3.Copy( p1 ); m.LastRoadP22 = JsgVect3.Copy( p2 ); m.LastPosValid[objIx] = true; // draw pilows setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -roadP-(roadW-1.3), 0, -roadP-(roadW-2.7), roadH-2.4, 3 ); g.RectOnPlane( -roadP+(roadW-2.7), 0, -roadP+(roadW-1.3), roadH-2.4, 3 ); g.RectOnPlane( +roadP-(roadW-1.3), 0, +roadP-(roadW-2.7), roadH-2.4, 3 ); g.RectOnPlane( +roadP+(roadW-2.7), 0, +roadP+(roadW-1.3), roadH-2.4, 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -roadP-roadW, roadH-2.4, -roadP+roadW, roadH-1.2, 3 ); g.RectOnPlane( +roadP-roadW, roadH-2.4, +roadP+roadW, roadH-1.2, 3 ); setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -roadP-roadW, -roadP-roadW, -roadP-(roadW-1.3), -roadP-(roadW-1.3), -roadP+(roadW-1.3), -roadP+(roadW-1.3), -roadP+roadW, -roadP+roadW, -roadP+(roadW-0.4), -roadP+(roadW-0.4), -roadP-(roadW-0.4), -roadP-(roadW-0.4), -roadP-roadW ], [ roadH+0.8, roadH-0.2, roadH-0.2, roadH-1.2, roadH-1.2, roadH-0.2, roadH-0.2, roadH+0.8, roadH+0.8, roadH+0.0, roadH+0.0, roadH+0.8, roadH+0.8 ], 3 ); g.PolygonOnPlane( [ +roadP-roadW, +roadP-roadW, +roadP-(roadW-1.3), +roadP-(roadW-1.3), +roadP+(roadW-1.3), +roadP+(roadW-1.3), +roadP+roadW, +roadP+roadW, +roadP+(roadW-0.4), +roadP+(roadW-0.4), +roadP-(roadW-0.4), +roadP-(roadW-0.4), +roadP-roadW ], [ roadH+0.8, roadH-0.2, roadH-0.2, roadH-1.2, roadH-1.2, roadH-0.2, roadH-0.2, roadH+0.8, roadH+0.8, roadH+0.0, roadH+0.0, roadH+0.8, roadH+0.8 ], 3 ); if (nDraw == 47) { // draw bridge setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -roadP+roadW, -roadP+roadW, -roadP+20.1, roadP-20.1, roadP-roadW, roadP-roadW, roadP-20.1, -roadP+20.1, -roadP+roadW ], [ roadH+0.8, roadH-0.2, roadH-0.0, roadH-0.0, roadH-0.2, roadH+0.8, roadH+1.0, roadH+1.0, roadH+0.8 ], 3 ); setBgColor( '#adadab', '#d2d2cf' ); g.PolygonOnPlane( [ -roadP+roadW, -roadP+roadW, -roadP+20.1, roadP-20.1, roadP-roadW, roadP-roadW, roadP-20.1, -roadP+20.1, -roadP+roadW ], [ roadH-0.2, roadH-1.0, roadH-0.8, roadH-0.8, roadH-1.0, roadH-0.2, roadH-0.0, roadH-0.0, roadH-0.2 ], 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -roadP+(roadW+0.1), roadH-1.8, -roadP+(roadW+1.3), roadH-1.0, 3 ); g.RectOnPlane( +roadP-(roadW+0.1), roadH-1.8, +roadP-(roadW+1.3), roadH-1.0, 3 ); g.RectOnPlane( -roadP+18.5, roadH-1.6, -roadP+19.7, roadH-0.8, 3 ); g.RectOnPlane( +roadP-18.5, roadH-1.6, +roadP-19.7, roadH-0.8, 3 ); setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -roadP+(roadW+0.1), -roadP+(roadW-0.5), -roadP+(roadW+0.1), -roadP+(roadW+0.7), -roadP+(roadW+0.1) ], [ roadH-1.8, 0.0, 0.0, roadH-1.8, roadH-1.8 ], 3 ); g.PolygonOnPlane( [ +roadP-(roadW+0.1), +roadP-(roadW-0.5), +roadP-(roadW+0.1), +roadP-(roadW+0.7), +roadP-(roadW+0.1) ], [ roadH-1.8, 0.0, 0.0, roadH-1.8, roadH-1.8 ], 3 ); g.PolygonOnPlane( [ -roadP+(roadW+0.7), -roadP+(roadW+1.3), -roadP+(roadW+1.9), -roadP+(roadW+1.3), -roadP+(roadW+0.7) ], [ roadH-1.8, 0.0, 0.0, roadH-1.8, roadH-1.8 ], 3 ); g.PolygonOnPlane( [ +roadP-(roadW+0.7), +roadP-(roadW+1.3), +roadP-(roadW+1.9), +roadP-(roadW+1.3), +roadP-(roadW+0.7) ], [ roadH-1.8, 0.0, 0.0, roadH-1.8, roadH-1.8 ], 3 ); g.PolygonOnPlane( [ -roadP+18.5, -roadP+17.9, -roadP+18.5, -roadP+19.1, -roadP+18.5 ], [ roadH-1.6, 0.0, 0.0, roadH- 1.6, roadH- 1.6 ], 3 ); g.PolygonOnPlane( [ +roadP-18.5, +roadP-17.9, +roadP-18.5, +roadP-19.1, +roadP-18.5 ], [ roadH-1.6, 0.0, 0.0, roadH- 1.6, roadH- 1.6 ], 3 ); g.PolygonOnPlane( [ -roadP+19.1, -roadP+19.7, -roadP+20.3, -roadP+19.7, -roadP+19.1 ], [ roadH-1.6, 0.0, 0.0, roadH- 1.6, roadH -1.6 ], 3 ); g.PolygonOnPlane( [ +roadP-19.1, +roadP-19.7, +roadP-20.3, +roadP-19.7, +roadP-19.1 ], [ roadH-1.6, 0.0, 0.0, roadH- 1.6, roadH -1.6 ], 3 ); } } else if (objType == 10) { // Wind Turbine // blade 1 setBgColor( '#b3b3b3', '#d3d3d3' ); g.PolygonOnPlane( [ -0.074, -0.031, -0.019, -0.033, -0.074 ], [ 1.025, 1.025, 1.686, 1.686, 1.025 ], 3 ); // blade 2 g.PolygonOnPlane( [ -0.060, -0.019, -0.050, 0.235, 0.248, 0.043, -0.034, -0.060 ], [ 0.974, 0.930, 0.900, 0.627, 0.638, 0.916, 0.991, 0.974 ], 3 ); // rotor hut g.PolygonOnPlane( [ -0.097, -0.080, -0.057, -0.057, -0.080, -0.097, -0.097 ], [ 0.989, 0.977, 0.977, 1.025, 1.025, 1.011, 0.989 ], 3 ); // blade 3 g.PolygonOnPlane( [ -0.493, -0.484, -0.140, -0.057, -0.057, -0.062, -0.079, -0.493 ], [ 0.718, 0.697, 0.908, 0.977, 0.988, 1.000, 1.000, 0.718 ], 3 ); // pole setBgColor( '#aa4400', '#cc5500' ); g.RectOnPlane( -0.028, 0.000, 0.028, 0.139, 3 ); setBgColor( '#333333', '#555555' ); g.RectOnPlane( -0.048, 0.139, 0.048, 0.158, 3 ); setBgColor( '#999999', '#bbbbbb' ); g.RectOnPlane( -0.028, 0.158, 0.028, 0.963, 3 ); // hub g.RectOnPlane( -0.057, 0.963, 0.091, 1.033, 3 ); setBgColor( '#cccccc', '#e0e0e0' ); g.RectOnPlane( 0.091, 0.963, 0.134, 1.033, 3 ); //setBgColor( '#666666', '#888888' ); //g.RectOnPlane( 0.060, 1.033, 0.149, 1.055, 3 ); } else if (objType == 8 || objType == 9) { // Soundly demo: Causew-S and Causew-N var p1 = JsgVect3.Null(); var p2 = JsgVect3.Null(); var nDraw = m.NObjectsDrawn[objIx]; if (objType == 9) { // draw backward nDraw = m.NObjects[objIx] - 1 - nDraw; } var roadH = 5 * (m.ObjSizeVar[objIx] + 1); var roadP = 17; var roadW = 5; var roadN = 0; // narrowing of east road // draw roads var roadS = 1; if (m.ObjSidePos[objIx] < 0) roadS = -1; // compute ship passages if (nDraw == 106 || nDraw == 107 || nDraw == 64 || nDraw == 63 || nDraw == 21 || nDraw == 20) { roadH += 3.0; } if (nDraw == 86 || nDraw == 87 ) { // 4 mile bridge roadH += 12.1; } if (nDraw == 41 || nDraw == 42) { // 8 mile bridge roadH += 9.1; } // compute narrowing of roads if (nDraw >= 121) { roadN = 18 * (nDraw - 121) / 7 } if (nDraw <= 7) { roadN = 18 * (7-nDraw) / 7; } var underH = roadH - 3; var underL = 24.1; var underR = 26.8; function DrawTower( base, center, height, basew, topw, nbars ) { g.SetAlpha( 0.3 * m.AlphaOpaque ); setBgColor( '#888888', '#aaaaaa' ); g.PolygonOnPlane( [ center-basew/2, center+basew/2, center+topw/2, center-topw/2, center-basew/2 ], [ base, base, base+height, base+height, base ], 3 ); g.NewPoly(); var dh = height / nbars; var hlimit = height + dh/2; var slope = (topw-basew) / 2 / height; var s = 1; for (var hoff = 0; hoff < hlimit; hoff += dh) { g.AddPointToPoly( center+s*(hoff*slope+basew/2), hoff+base ); s *= -1; } g.PolygonOnPlane( g.Poly, 1 ); g.NewPoly(); var s = -1; for (var hoff = 0; hoff < hlimit; hoff += dh) { g.AddPointToPoly( center+s*(hoff*slope+basew/2), hoff+base ); s *= -1; } g.PolygonOnPlane( g.Poly, 1 ); g.SetAlpha( m.AlphaOpaque ); } g.SetColor( '#838795' ); var roadCorrLeftH = 4; var w = roadN; if (roadS < 0) w = 0; // under passage correction if (nDraw == 80 && roadS < 0) { //DrawUnderPassageRamp( m.LastRoadP11 ); roadH += roadCorrLeftH; } g.GetTransPointOnPlane( roadS*roadP-w-(roadW-0.4), roadH, p1 ); g.GetTransPointOnPlane( roadS*roadP-w+(roadW-0.4), roadH, p2 ); if (m.LastPosValid[objIx]) { g.SaveTrans3D( true ); g.NewPoly3D(); g.AddPointToPoly3D( m.LastRoadP11 ); g.AddPointToPoly3D( m.LastRoadP12 ); g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); setBgColor( '#999ba2', '#adb1bc' ); g.DrawPoly3D( 7 ); g.RestoreTrans3D(); } m.LastRoadP11 = JsgVect3.Copy( p1 ); m.LastRoadP12 = JsgVect3.Copy( p2 ); // undo under passage correction if (nDraw == 80 && roadS < 0) roadH -= roadCorrLeftH; roadS *= -1; // under passage correction if (nDraw == 80 && roadS < 0) { //DrawUnderPassageRamp( m.LastRoadP21 ); roadH += roadCorrLeftH; } w = roadN; if (roadS < 0) w = 0; g.GetTransPointOnPlane( roadS*roadP-w-(roadW-0.4), roadH, p1 ); g.GetTransPointOnPlane( roadS*roadP-w+(roadW-0.4), roadH, p2 ); if (m.LastPosValid[objIx]) { g.SaveTrans3D( true ); g.NewPoly3D(); g.AddPointToPoly3D( m.LastRoadP21 ); g.AddPointToPoly3D( m.LastRoadP22 ); g.AddPointToPoly3D( p2 ); g.AddPointToPoly3D( p1 ); setBgColor( '#999ba2', '#adb1bc' ); g.DrawPoly3D( 7 ); g.RestoreTrans3D(); } // undo under passage correction if (nDraw == 80 && roadS < 0) roadH -= roadCorrLeftH; g.SetColor( 'black' ); m.LastRoadP21 = JsgVect3.Copy( p1 ); m.LastRoadP22 = JsgVect3.Copy( p2 ); m.LastPosValid[objIx] = true; // draw under passage if (nDraw == 80) { // pillows setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -roadP-(underL-1.3), 0, -roadP-(underL-2.7), underH, 3 ); g.RectOnPlane( -roadP-(underL-7.3), 0, -roadP-(underL-8.7), underH, 3 ); g.RectOnPlane( -roadP+(underR-1.3), 0, -roadP+(underR-2.7), underH, 3 ); g.RectOnPlane( -roadP+(underR-7.3), 0, -roadP+(underR-8.7), underH, 3 ); // hut setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -roadP-underL, -roadP-underL, -roadP-(underL-1.3), -roadP-(underL-1.3), -roadP+(underR-1.3), -roadP+(underR-1.3), -roadP+underR, -roadP+underR, -roadP+(underR-0.4), -roadP+(underR-0.4), -roadP-(underL-0.4), -roadP-(underL-0.4), -roadP-underL ], [ underH+0.8, underH-0.2, underH-0.2, underH-1.2, underH-1.2, underH-0.2, underH-0.2, underH+0.8, underH+0.8, underH+0.0, underH+0.0, underH+0.8, underH+0.8 ], 3 ); // base setBgColor( '#888888', '#aaaaaa' ); g.SetAlpha( 0.5 * m.AlphaOpaque ); g.RectOnPlane( -roadP-roadW-13.5, underH, -roadP-roadW, underH+4, 3 ); g.RectOnPlane( -roadP+roadW, underH, -roadP+roadW+18.6, underH+4, 3 ); // towers var towerOffset = 5.5; var c = -roadP - roadW - towerOffset; var th = 21.4; var bh = underH + 3.2; // 8; //DrawTower( bh, c, th, 3.8, 3.8, 5 ); g.RectOnPlane( c-2.4, underH+4, c+2.4, underH+8, 3 ); var c = -roadP + roadW + towerOffset; var th = 14.3; DrawTower( bh, c, th, 3.8, 3.8, 5 ); g.RectOnPlane( c-2.4, underH+4, c+2.4, underH+8, 3 ); // boxes setBgColor( '#8c8678', '#b9b19e' ); var c = -roadP - roadW - towerOffset; g.RectOnPlane( c-1.8, underH+4, c+1.8, underH+7.6, 3 ); var c = -roadP + roadW + towerOffset; g.RectOnPlane( c-1.8, underH+4, c+1.8, underH+7.6, 3 ); setBgColor( '#cccccc', '#dddddd' ); } // draw hump stuff if (nDraw == 41) { // control buildings setBgColor( '#8c8678', '#b9b19e' ); var x = roadP - roadW; var z = roadH; g.PolygonOnPlane( [ x-2.26, x-2.26, x, x, x-2.51, x-2.51, x-2.01, x-7.28, x-6.78, x-6.78, x-2.26 ], [ z-5.52, 0, 0, z, z, z+2.51, z+5.52, z+5.52, z+2.51, z-4.27, z-5.52 ], 3 ); setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( x-8.79, z+2, x-2.25, z+2.5, 3 ); g.RectOnPlane( x-8.28, z, x-7.28, z+2, 3 ); // radar g.RectOnPlane( x-5.27, z+5.52, x-4.77, z+9.29, 3 ); g.RectOnPlane( x-6.90, z+8.54, x-3.12, z+9.04, 3 ); // window setBgColor( '#444444', '#666666' ); g.PolygonOnPlane( [ x-6.53, x-2.76, x-2.51, x-6.78, x-6.53 ], [ z+3.51, z+3.51, z+4.77, z+4.77, z+3.51 ], 3 ); // building west var x = -roadP - roadW; setBgColor( '#8c8678', '#b9b19e' ); g.PolygonOnPlane( [ x-2.26, x-2.26, x, x, x-2.51, x-2.51, x-6.78, x-6.78, x-2.26 ], [ z-5.52, 0, 0, z, z, z+7.03, z+7.03, z-4.27, z-5.52 ], 3 ); DrawTower( z, x-3.94, 7.53, 6.28, 6.28, 2 ); // DrawTower( base, center, height, basew, topw, nbars ) { // bridge setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -roadP+roadW, roadH-1.13, roadP-roadW, roadH, 3 ); // left pillow setBgColor( '#8c8678', '#b9b19e' ); var x = -roadP + roadW; g.RectOnPlane( x, 0, x+1.76, z, 3 ); // tower oeast var towerOffset = 1.9; var c = +roadP + roadW + towerOffset; var th = 10; DrawTower( roadH, c, th, 3.8, 3.8, 3 ); g.RectOnPlane( roadP + roadW, roadH-4, roadP + roadW + 4, roadH, 3 ); // needle tower var towerOffset = 1.25; var c = +roadP - roadW - towerOffset; var th = 14; DrawTower( roadH, c, th, 1.5, 0, 6 ); } // draw gerust if (nDraw == 42) { // DrawTower( base, center, height, basew, topw, nbars ) { DrawTower( 0, -roadP-roadW-5, roadH, 10, 10, 4 ); } // draw ship gide if (nDraw == 86 || nDraw == 87) { // 4 mile bridge setBgColor( '#8c8678', '#b9b19e' ); g.SetAlpha( 0.7 * m.AlphaOpaque ); g.RectOnPlane( -roadP-roadW-33.2, 0, +roadP+roadW+40.5, 3.4, 3 ); g.SetAlpha( m.AlphaOpaque ); g.RectOnPlane( -roadP-roadW-39.7, 0, -roadP-roadW-33.2, 2.7, 3 ); g.RectOnPlane( +roadP+roadW+40.5, 0, +roadP+roadW+47.0, 2.7, 3 ); } if (nDraw == 41 || nDraw == 42) { // 8 mile bridge setBgColor( '#8c8678', '#b9b19e' ); g.SetAlpha( 0.7 * m.AlphaOpaque ); g.RectOnPlane( -roadP-roadW-40, 0, +roadP+roadW+37, 3.4, 3 ); g.SetAlpha( m.AlphaOpaque ); g.RectOnPlane( -roadP-roadW-46.5, 0, -roadP-roadW-40, 2.7, 3 ); g.RectOnPlane( +roadP+roadW+37, 0, +roadP+roadW+43.5, 2.7, 3 ); } // draw pilows w = roadN; var hCorrLeft = 0; if (nDraw == 80) hCorrLeft = roadCorrLeftH; // under passage bridge correction var roadHC = roadH + hCorrLeft; setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -roadP-(roadW-1.3), 0, -roadP-(roadW-2.7), roadHC-2.4, 3 ); g.RectOnPlane( -roadP+(roadW-2.7), 0, -roadP+(roadW-1.3), roadHC-2.4, 3 ); g.RectOnPlane( +roadP-w-(roadW-1.3), 0, +roadP-w-(roadW-2.7), roadH-2.4, 3 ); g.RectOnPlane( +roadP-w+(roadW-2.7), 0, +roadP-w+(roadW-1.3), roadH-2.4, 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -roadP-roadW, roadHC-2.4, -roadP+roadW, roadHC-1.2, 3 ); g.RectOnPlane( +roadP-w-roadW, roadH-2.4, +roadP-w+roadW, roadH-1.2, 3 ); setBgColor( '#565a56', '#858b85' ); g.PolygonOnPlane( [ -roadP-roadW, -roadP-roadW, -roadP-(roadW-1.3), -roadP-(roadW-1.3), -roadP+(roadW-1.3), -roadP+(roadW-1.3), -roadP+roadW, -roadP+roadW, -roadP+(roadW-0.4), -roadP+(roadW-0.4), -roadP-(roadW-0.4), -roadP-(roadW-0.4), -roadP-roadW ], [ roadHC+0.8, roadHC-0.2, roadHC-0.2, roadHC-1.2, roadHC-1.2, roadHC-0.2, roadHC-0.2, roadHC+0.8, roadHC+0.8, roadHC+0.0, roadHC+0.0, roadHC+0.8, roadHC+0.8 ], 3 ); g.PolygonOnPlane( [ +roadP-w-roadW, +roadP-w-roadW, +roadP-w-(roadW-1.3), +roadP-w-(roadW-1.3), +roadP-w+(roadW-1.3), +roadP-w+(roadW-1.3), +roadP-w+roadW, +roadP-w+roadW, +roadP-w+(roadW-0.4), +roadP-w+(roadW-0.4), +roadP-w-(roadW-0.4), +roadP-w-(roadW-0.4), +roadP-w-roadW ], [ roadH+0.8, roadH-0.2, roadH-0.2, roadH-1.2, roadH-1.2, roadH-0.2, roadH-0.2, roadH+0.8, roadH+0.8, roadH+0.0, roadH+0.0, roadH+0.8, roadH+0.8 ], 3 ); // draw big transmission towers if (nDraw == 84) { var c = +roadP - roadW - 6.7; DrawTower( roadH, c, 32, 7.7, 4.8, 5 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -roadP+roadW+2.2, roadH, -roadP+roadW+6.8, roadH+3.8, 3 ); } // double towers from north view if (nDraw == 39 && objType == 9) { // DrawTower( base, center, height, basew, topw, nbars ); DrawTower( roadH, -6.25, 9.5, 1.79, 1.79, 5 ); DrawTower( roadH, 6.25, 9.5, 1.79, 1.79, 5 ); } // draw bridges if (nDraw == 18 || nDraw == 39 || nDraw == 44 || nDraw == 61 || nDraw == 84 || nDraw == 89 || nDraw == 110) { setBgColor( '#565a56', '#858b85' ); g.RectOnPlane( -roadP+roadW, roadH-0.2, roadP-roadW, roadH+0.8, 3 ); setBgColor( '#adadab', '#d2d2cf' ); g.RectOnPlane( -roadP+roadW, roadH-1.4, roadP-roadW, roadH-0.2, 3 ); g.RectOnPlane( -roadP+(roadW+0.1), roadH-2.8, -roadP+(roadW+1.0), roadH-1.4, 3 ); g.RectOnPlane( +roadP-(roadW+0.1), roadH-2.8, +roadP-(roadW+1.0), roadH-1.4, 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.RectOnPlane( -roadP+(roadW+0.2), 0, -roadP+(roadW+0.9), roadH-2.8, 3 ); g.RectOnPlane( +roadP-(roadW+0.2), 0, +roadP-(roadW+0.9), roadH-2.8, 3 ); // signs if (objType == 8) { if (nDraw != 89 && nDraw != 44) { setBgColor( '#cccccc', '#dddddd' ); g.RectOnPlane( -roadP+(roadW+1.0+7.5), roadH+1.8, -roadP+(roadW+1.0), roadH+1.8+2.2, 3 ); } if (nDraw != 84 && nDraw != 39) { setBgColor( '#444444', '#666666' ); g.RectOnPlane( +roadP-(roadW+1.0+7.5), roadH+1.8, +roadP-(roadW+1.0), roadH+1.8+2.2, 3 ); } setBgColor( '#666666', '#888888' ); if (nDraw != 89 && nDraw != 44) { g.LineOnPlane( -roadP+(roadW+1.0+7.5), roadH+0.9, -roadP+(roadW+1.0+7.5), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0+5 ), roadH+0.9, -roadP+(roadW+1.0+5 ), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0+2.5), roadH+0.9, -roadP+(roadW+1.0+2.5), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0 ), roadH+0.9, -roadP+(roadW+1.0 ), roadH+1.8 ); } if (nDraw != 84 && nDraw != 39) { g.LineOnPlane( +roadP-(roadW+1.0+7.5), roadH+0.9, +roadP-(roadW+1.0+7.5), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0+5 ), roadH+0.9, +roadP-(roadW+1.0+5 ), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0+2.5), roadH+0.9, +roadP-(roadW+1.0+2.5), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0 ), roadH+0.9, +roadP-(roadW+1.0 ), roadH+1.8 ); } } else { if (nDraw != 89 && nDraw != 44) { setBgColor( '#444444', '#666666' ); g.RectOnPlane( -roadP+(roadW+1.0+7.5), roadH+1.8, -roadP+(roadW+1.0), roadH+1.8+2.2, 3 ); } if (nDraw != 84 && nDraw != 39) { setBgColor( '#cccccc', '#dddddd' ); g.RectOnPlane( +roadP-(roadW+1.0+7.5), roadH+1.8, +roadP-(roadW+1.0), roadH+1.8+2.2, 3 ); } setBgColor( '#666666', '#888888' ); if (nDraw != 89 && nDraw != 44) { g.LineOnPlane( -roadP+(roadW+1.0+7.5), roadH+0.9, -roadP+(roadW+1.0+7.5), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0+5 ), roadH+0.9, -roadP+(roadW+1.0+5 ), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0+2.5), roadH+0.9, -roadP+(roadW+1.0+2.5), roadH+1.8 ); g.LineOnPlane( -roadP+(roadW+1.0 ), roadH+0.9, -roadP+(roadW+1.0 ), roadH+1.8 ); } if (nDraw != 84 && nDraw != 39) { g.LineOnPlane( +roadP-(roadW+1.0+7.5), roadH+0.9, +roadP-(roadW+1.0+7.5), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0+5 ), roadH+0.9, +roadP-(roadW+1.0+5 ), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0+2.5), roadH+0.9, +roadP-(roadW+1.0+2.5), roadH+1.8 ); g.LineOnPlane( +roadP-(roadW+1.0 ), roadH+0.9, +roadP-(roadW+1.0 ), roadH+1.8 ); } } // lights setBgColor( '#aaaaaa', '#bbbbbb' ); g.RectOnPlane( -roadP-roadW-0.14, roadH, -roadP-roadW+0.14, roadH+18.0, 2 ); g.RectOnPlane( -roadP-roadW+0.14, roadH+17.3, -roadP-roadW+1.14, roadH+17.7, 2 ); g.SetAreaAttr( '#eeeeee', '#aaaaaa', 1 ); g.RectOnPlane( -roadP-roadW+0.63, roadH+16.8, -roadP-roadW+1.13, roadH+17.3, 3 ); setBgColor( '#aaaaaa', '#bbbbbb' ); g.RectOnPlane( -0.07, roadH, 0.07, roadH+9.5, 2 ); g.SetAreaAttr( '#eeeeee', '#aaaaaa', 1 ); g.RectOnPlane( -0.35, roadH+9.05, 0.35, roadH+9.4, 3 ); g.SetColor( 'black' ); } // double towers from south view if (nDraw == 39 && objType == 8) { // DrawTower( base, center, height, basew, topw, nbars ); DrawTower( roadH, -6.25, 9.5, 1.79, 1.79, 5 ); DrawTower( roadH, 6.25, 9.5, 1.79, 1.79, 5 ); } // draw huts function isHatPos( n ) { var hatPos = [ 6, 12, 18, 25, 32, 39, 44, 49, 55, 61, 67, 73, 84, 89, 96, 103, 110, 116, 122 ]; for (var i = 0; i < hatPos.length; i++) { if (n == hatPos[i]) return true; } return false; } if (isHatPos( nDraw )) { setBgColor( '#888888', '#aaaaaa' ); g.RectOnPlane( -roadP-roadW-13.5, roadH-0.4, -roadP-roadW, roadH, 3 ); setBgColor( '#8c8678', '#b9b19e' ); g.PolygonOnPlane( [ -roadP-roadW-13.5, -roadP-roadW-12.9, -roadP-roadW-9.1, -roadP-roadW-8.5, -roadP-roadW-13.5 ], [ roadH-0.4, roadH-0.65, roadH-0.65, roadH-0.4, roadH-0.4 ], 3 ); setBgColor( '#cccccc', '#dddddd' ); var c = -roadP - roadW - 9.1 - 1.9; var b = roadH; g.PolygonOnPlane( [ c-2.3, c-2.3, c+2.3, c+2.3, c, c-2.3 ], [ b+2.8, b, b, b+2.8, b+3.1, b+2.8 ], 3 ); g.RectOnPlane( c-1.8, 0, c-0.9, b-0.65, 3 ); g.RectOnPlane( c+0.9, 0, c+1.8, b-0.65, 3 ); } } else if (objType == 11) { // Rainy Lake var commonSize = 0.8; function getSize( size ) { // adjusts target size according to SizeVar slider var sizeVar = m.ObjSizeVar[0]; // -1...1 if (sizeVar > 0) sizeVar = 0; if (sizeVar < 0) sizeVar = -sizeVar; return sizeVar * commonSize + (1-sizeVar) * size; } function getSidePos( sidePos ) { // adjusts target side offset according to SideVar var val = Math.abs( m.Slider_ObjSideVar_Log[0] / 5 ); if (val > 1 ) val = 1; return (1-val) * sidePos; } function getHeight( hCalc, hRef, hWlvl, hRef2 ) { // adjusts height according to SizeVar slider of object2 and SizeVar slider of obect1 // 0 -> returns hCalc, -1 returns hWlvl, 1 -> returns hRef // hRef: height normalized to water level at observer // hWlvl: height normalized to water level at target location hRef2 = xDefNum( hRef2, hRef ); var sizeVar = 2 * m.ObjSizeVar[0] - 1; // -3...1 if (sizeVar < 0) sizeVar = 0; val = m.ObjSizeVar[1]; hRef = sizeVar * hRef2 + (1-sizeVar) * hRef; if (val >= 0) { return val * hRef + (1-val) * hCalc; } else { return -val * hWlvl + (1+val) * hCalc; } } function drawPlate1( height, size, sidePos, oversize ) { oversize = xDefNum( oversize, 1 ); var os2 = (oversize - 1) * 2.5 + 1; size = getSize( size ); sidePos = getSidePos( sidePos ); var w2 = 0.65 * size / 2; var h2 = 0.5; setBgColor( '#eeeeee', '#ffffff' ); g.RectOnPlane( -w2 * os2 + sidePos, -h2 * oversize * size + height, w2 * os2 + sidePos, h2 * oversize * size + height, 3 ); setBgColor( '#000000', '#444444' ); g.PolygonOnPlane( [-w2+sidePos, sidePos, w2+sidePos, -w2+sidePos], [h2*size+height, height, h2*size+height, h2*size+height], 2 ); g.PolygonOnPlane( [-w2+sidePos, sidePos, w2+sidePos, -w2+sidePos], [-h2*size+height, height, -h2*size+height, -h2*size+height], 2 ); g.RectOnPlane( -w2 + sidePos, 0.0476 * size + height, w2 + sidePos, 0.0732 * size + height, 2 ); g.RectOnPlane( -w2 + sidePos, -0.0476 * size + height, w2 + sidePos, -0.0732 * size + height, 2 ); setBgColor( '#0000ff', '#4444ff' ); g.PolygonOnPlane( [-w2/2+sidePos, sidePos, w2/2+sidePos, -w2/2+sidePos], [h2*size+height, h2/2*size+height, h2*size+height, h2*size+height], 2 ); g.PolygonOnPlane( [-w2/2+sidePos, sidePos, w2/2+sidePos, -w2/2+sidePos], [-h2*size+height, -h2/2*size+height, -h2*size+height, -h2*size+height], 2 ); } function drawPlate2( height, size, sidePos ) { size = getSize( size ); sidePos = getSidePos( sidePos ); setBgColor( '#cc6400', '#dc7410' ); g.RectOnPlane( -size + sidePos, 0.09 * size + height, size + sidePos, 0.35 * size + height, 3 ); g.RectOnPlane( -size + sidePos, -0.35 * size + height, size + sidePos, -0.09 * size + height, 3 ); } function drawPlate0( height, size, sidePos ) { size = getSize( size ); sidePos = getSidePos( sidePos ); var ph = 0.2; setBgColor( '#eeeeee', '#ffffff' ); g.RectOnPlane( -0.5*size+sidePos, -ph*size+height, 0.5*size+sidePos, ph*size+height, 3 ); setBgColor( '#000000', '#444444' ); g.PolygonOnPlane( [-0.1*size+sidePos, sidePos, 0.1*size+sidePos, -0.1*size+sidePos], [ph*size+height, height, ph*size+height, ph*size+height], 2 ); g.PolygonOnPlane( [-0.1*size+sidePos, sidePos, 0.1*size+sidePos, -0.1*size+sidePos], [-ph*size+height, height, -ph*size+height, -ph*size+height], 2 ); g.RectOnPlane( -0.5*size+sidePos, 0.04*size+height, 0.5*size+sidePos, 0.12*size+height, 2 ); g.RectOnPlane( -0.5*size+sidePos, -0.12*size+height, 0.5*size+sidePos, -0.04*size+height, 2 ); } var lineHeightAngle = 0.05; function drawPole( id, targetHeight, poleHeight, sidePos, dist ) { sidePos = getSidePos( sidePos ); setBgColor( '#88dddd', '#aaffff' ); g.RectOnPlane( -0.05 + sidePos, 0, 0.05 + sidePos, poleHeight, 3 ); if (dist > 0) { var textHeight = lineHeightAngle * dist; var showText = Math.floor( 3 * Math.abs( m.ObjSideVar[1] ) ); if (showText < 2) { if (m.FocalLengthField >= 5000) { if (showText < 1) { g.TextOnPlane( 'd:'+dist.toFixed(1), sidePos, poleHeight + textHeight ); if (targetHeight > 0) g.TextOnPlane( 'h:'+targetHeight.toFixed(2), sidePos, poleHeight + 3 * textHeight ); } if (showText < 2) { if (id > 0) g.TextOnPlane( '('+id+')', sidePos, poleHeight + 5 * textHeight ); } } else if (m.FocalLengthField >= 3000 && showText < 1) { g.TextOnPlane( dist.toFixed(1), sidePos, poleHeight + textHeight ); if (id > 0) g.TextOnPlane( '('+id+')', sidePos, poleHeight + 3 * textHeight ); } } } } function drawPoleSlanted( id, targetHeight, poleHeight, sidePos, dist, slantOffset ) { // drawPoleSlanted( 6, h, h+0.6, side, 6.4, 0.7 ); if (m.NObjects[0] == 0) slantOffset = 0; sidePos = getSidePos( sidePos ); var sizeVar = 2 * m.ObjSizeVar[0] - 1; // -3...1 if (sizeVar < 0) sizeVar = 0; slantOffset = getSidePos( slantOffset ); slantOffset *= (1-sizeVar); setBgColor( '#88dddd', '#aaffff' ); g.RectOnPlane( -0.05 + sidePos, 0, 0.05 + sidePos, 2.3, 3 ); var showText = Math.floor( 3 * Math.abs( m.ObjSideVar[1] ) ); if (poleHeight > 2.3) { g.PolygonOnPlane( [-0.05+sidePos, 0.05+sidePos, 0.05+slantOffset+sidePos, -0.05+sidePos+slantOffset, -0.05+sidePos], [2.3, 2.3, poleHeight, poleHeight, 2.3], 3 ); if (dist > 0 && showText < 2) { var textHeight = lineHeightAngle * dist; if (m.FocalLengthField >= 5000) { if (showText < 1) { g.TextOnPlane( 'd:'+dist.toFixed(1), sidePos+slantOffset, poleHeight + textHeight ); if (targetHeight > 0) g.TextOnPlane( 'h:'+targetHeight.toFixed(2), sidePos+slantOffset, poleHeight + 3 * textHeight ); } if (id > 0 && showText < 2) g.TextOnPlane( '('+id+')', sidePos+slantOffset, poleHeight + 5 * textHeight ); } else if (m.FocalLengthField >= 3000) { if (showText < 1) { g.TextOnPlane( dist.toFixed(1), sidePos+slantOffset, poleHeight + textHeight ); } if (id > 0 && showText < 2) g.TextOnPlane( '('+id+')', sidePos+slantOffset, poleHeight + 3 * textHeight ); } } } else { if (dist > 0 && showText < 2) { var textHeight = lineHeightAngle * dist; if (m.FocalLengthField >= 5000) { if (showText < 1) { g.TextOnPlane( 'd:'+dist.toFixed(1), sidePos, poleHeight + textHeight ); if (targetHeight > 0) g.TextOnPlane( 'h:'+targetHeight.toFixed(2), sidePos, poleHeight + 3 * textHeight ); } if (id > 0 && showText < 2) g.TextOnPlane( '('+id+')', sidePos, poleHeight + 5 * textHeight ); } else if (m.FocalLengthField >= 3000) { if (showText < 1) { g.TextOnPlane( dist.toFixed(1), sidePos, poleHeight + textHeight ); } if (id > 0 && showText < 2) g.TextOnPlane( '('+id+')', sidePos, poleHeight + 3 * textHeight ); } } } } var altObjIx = (objIx + 1) % 2; var altObjType = m.ObjType[altObjIx]; var nDraw = m.NObjectsDrawn[objIx] % 9; var isBothTargets = (altObjType == 11 && m.NObjects[altObjIx] > nDraw); // if both objects are Rainy Lake then draw both here and ignore the second draw with objIx == 1 if ((isBothTargets && objIx == 0) || !isBothTargets) { g.SetTextAttr( 'Arial', 12, 'black', 'normal', 'normal', 'center', 'bottom', 0 ); var minPoleSize = 2.8; if (nDraw == 8) { // first target at 1094.72 m var side = 1.09; if (objIx == 0) { var h = getHeight( 1.854, 1.815, 1.842 ); drawPole( 1, h, minPoleSize, side, 1.1 ); drawPlate0( h, 0.55, side ); } else { drawPole( 1, 0, 0.5, side, 1.1 ); } } else if (nDraw == 7) { // first tangent target at 2168.50 m var side = 1.46; if (isBothTargets) { var h = getHeight( 4.218, 4.267, 4.280 ); drawPole( 2, h, h+0.28, side, 2.2 ); drawPlate1( h, 0.4064, side, 1.15 ); var h = getHeight( 1.854, 1.841, 1.854 ); drawPlate1( h, 0.37, side ); } else { if (objIx == 0) { var h = getHeight( 1.854, 1.841, 1.854 ); drawPole( 2, h, minPoleSize, side, 2.2 ); drawPlate1( h, 0.37, side ); } else { var h = getHeight( 4.218, 4.267, 4.280 ); drawPole( 2, h, h+0.28, side, 2.2 ); drawPlate1( h, 0.4064, side, 1.15 ); } } } else if (nDraw == 6) { // target at 3233.97 m var side = -3.22; if (objIx == 0) { var h = getHeight( 1.854, 1.812, 1.845 ); drawPole( 3, h, minPoleSize, side, 3.2 ); drawPlate1( h, 0.55, side ); } else { drawPole( 3, 0, 0.5, side, 3.2 ); } } else if (nDraw == 5) { // target at 4363.27 m var side = -2.72; if (isBothTargets) { //var h = getHeight( 5.151, 5.123, 5.162 ); var h = getHeight( 5.151, 5.24, 5.162 ); drawPole( 4, h, h+0.47, side, 4.4 ); drawPlate1( h, 0.8636, side ); var h = getHeight( 1.854, 1.824, 1.863 ); drawPlate1( h, 0.74, side ); } else { if (objIx == 0) { var h = getHeight( 1.854, 1.824, 1.863 ); drawPole( 4, h, minPoleSize, side, 4.4 ); drawPlate1( h, 0.74, side ); } else { //var h = getHeight( 5.151, 5.123, 5.162 ); var h = getHeight( 5.151, 5.24, 5.162 ); drawPole( 4, h, h+0.47, side, 4.4 ); drawPlate1( h, 0.8636, side ); } } } else if (nDraw == 4) { // target at 5433.70 m var side = -2.48; if (objIx == 0) { var h = getHeight( 1.854, 1.732, 1.842 ); drawPole( 5, h, minPoleSize, side, 5.4 ); drawPlate1( h, 0.92, side ); } else { drawPole( 5, 0, 0.5, side, 5.4 ); } } else if (nDraw == 3) { // target at 6428.94 m // Note: the SizeVar slider of the Objects 1 panel fades between slanted and upright version var sideVar = 2 * m.ObjSizeVar[0]; // -1...1 var slantVar = sideVar - 1; if (sideVar > 1) sideVar = 1; if (sideVar < 0) sideVar = 0; if (slantVar < 0) slantVar = 0; var side = (1-sideVar) * (-0.3); var targetOffset = (1-slantVar) * 0.65; if (m.NObjects[0] == 0) targetOffset = 0; if (isBothTargets) { //var h = getHeight( 6.602, 6.696, 6.877 ); var h = getHeight( 6.602, 6.2, 6.877, 6.5 ); drawPoleSlanted( 6, h, h+0.6, side, 6.4, 0.7 ); drawPlate2( h, 0.65, side+targetOffset ); var h = getHeight( 1.854, 1.673, 1.854 ); drawPlate2( h, 0.65, side ); } else { if (objIx == 0) { var h = getHeight( 1.854, 1.673, 1.854 ); drawPole( 6, h, minPoleSize, side, 6.4, 1.1 ); drawPlate2( h, 0.65, side ); } else { //var h = getHeight( 6.602, 6.696, 6.877 ); var h = getHeight( 6.602, 6.2, 6.877, 6.696 ); drawPoleSlanted( 6, h, h+0.6, side, 6.4, 0.7 ); drawPlate2( h, 0.65, side+targetOffset ); } } } else if (nDraw == 2) { // no target at this position var side = 0; drawPole( 0, 0, 0.5, side, 0 ); } else if (nDraw == 1) { // no target at this position var side = 0; drawPole( 0, 0, 0.5, side, 0 ); } else if (nDraw == 0) { // last tangent target at 9459.20 m var side = -1.76; if (m.NObjects[0] == 0) side = -2.26; // -2; ? if (isBothTargets || objIx == 1) { var h = getHeight( 9.740, 10.535, 10.773 ); drawPole( 7, h, h+0.9, side, 9.5 ); drawPlate1( h, 1.6, side ); } else { drawPole( 7, 0, 0.5, side, 9.5 ); } } } // end Rainy Lake } else if (objType == 12) { // Cloud var color1 = [ '#ccc', '#ddd', '#bbb', '#ddd' ]; var color2 = [ '#ddd', '#eee', '#ccc', '#eee' ]; var sizeVar = 1 + 0.9 * m.ObjSizeVar[objIx]; var nDraw = m.NObjectsDrawn[objIx] % 4; setBgColor( color1[nDraw], color2[nDraw] ); g.SetColor( '#999' ); g.RectOnPlane( -0.7, 1, 0.7, 1+0.3*sizeVar, 3 ); g.EllipseOnPlane( -0.7, 1+0.3*sizeVar, 0.3, 0.3*sizeVar, 0, 2 ); g.EllipseOnPlane( 0.7, 1+0.3*sizeVar, 0.3, 0.3*sizeVar, 0, 2 ); g.EllipseOnPlane( -0.3, 1+0.5*sizeVar, 0.5, 0.5*sizeVar, 0, 2 ); g.EllipseOnPlane( 0.3, 1+0.4*sizeVar, 0.5, 0.4*sizeVar, 0, 2 ); } } function PointOnEarth( lat, long ) { var x = CurveApp.refractionRadius * Math.sin( long ); var rr = CurveApp.refractionRadius * Math.cos( long ); var y = rr * Math.sin( lat ); var z = rr * Math.cos( lat ); return [ x, y, z - (CurveApp.refractionRadius + CurveApp.Height) ]; } function PointOnPlane( lat, long ) { var x = CurveApp.refractionRadius * Math.sin( long ); var rr = CurveApp.refractionRadius * Math.cos( long ); var y = rr * Math.sin( lat ); return [ x, y, -CurveApp.Height ]; } </jscript> {{TabSelectorsTop| CurveSettingsTabs | MarginTop }} {{TabSel| Views | ViewsTab }} {{TabSel| Objects 1 | Obj1Tab }} {{TabSel| Objects 2 | Obj2Tab }} {{TabSel| Refraction | RefractionTab }} {{TabSel| Image | ImageTab }} {{TabSel| Units | UnitsTab }} {{TabSel| Save/Restore | SaveRestoreTab }} {{TabSelButton| << | BackButton }} {{TabSelButton| Play | PlayButton }} {{TabSelButton| >> | ForwButton }} {{TabSelButton| 0 | CountButton }} {{EndTabSelectors}} {{TabBoxes| CurveSettingsTabs }} <comment> Views Tab </comment> {{scroll|600px}} <jscript> ControlPanels.NewSliderPanel( { ModelRef: 'CurveApp', OnModelChange: UpdateAll, Format: 'std', Digits: DefaultDigits, ReadOnly: false, PanelFormat: 'InputMediumWidth' } ).AddValueSliderField( { Name: 'Height', ValueRef: 'Height', SliderValueRef: 'HeightSlider', UnitsData: 'HeightUnits', Color: 'blue', Min: 0, Max: 1, Inc: 0.1, } ).AddValueSliderField( { Name: 'FocalLength', ValueRef: 'FocalLengthField', SliderValueRef: 'FocalLengthSlider', Label: 'Zoom f', Units: 'mm', Color: 'black', Min: 0, Max: 1, Inc: 1, } ).AddValueSliderField( { Name: 'ViewAngle', Label: 'Diag FOV', ValueRef: 'ViewAngleField', SliderValueRef: 'ViewAngleSlider', Units: '°', Color: 'black', Min: 0.309, Max: 91.6, Inc: 0.1, } ).AddValueSliderField( { Name: 'Pan', ValueRef: 'Pan', SliderValueRef: 'PanSlider', Format: 'std', Digits: 5, Units: '°', Color: 'green', Min: -1, Max: 1, Inc: 0.1, } ).AddValueSliderField( { Name: 'Tilt', ValueRef: 'Tilt', SliderValueRef: 'TiltSlider', Format: 'std', Digits: 5, Units: '°', Color: 'green', Min: -1, Max: 1, Inc: 0.1, } ).AddValueSliderField( { Name: 'Roll', Format: 'std', Digits: 5, Units: '°', Color: 'green', Min: -45, Max: 45, Inc: 0.1, } ).Render(); </jscript> {{end scroll}} {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'Options', ModelRef: 'CurveApp', NCols: 2, OnModelChange: UpdateAll } ).AddRadiobuttonField( { Name: 'showModel', Label: 'Model', ValueType: 'int', Items: [ { Name: 'Globe', Value: 1 }, { Name: 'FE', Value: 2 }, { Name: 'G+FE X', Value: 3 }, { Name: 'G+FE ||', Value: 4 } ] } ).AddRadiobuttonField( { Name: 'viewcenterHorizon', Label: 'CameraAim', ValueType: 'int', Items: [ { Name: 'Horizon', Value: 0 }, { Name: 'Betwn', Value: 2 }, { Name: 'FE-Eq', Value: 1 }, { Name: 'Eye-Lvl', Value: 3 } ] } ).AddCheckboxField( { Name: 'Show', Label: 'Show', ColSpan: 3, Items: [ { Name: 'showData', Text: 'Data', }, { Name: 'showEyeLevel', Text: 'Eye Level', }, { Name: 'showTangent', Text: 'Tangent', }, { Name: 'showLRDrop', Text: 'Left-Right Drop', }, { Name: 'showTheodolite', Text: 'Theodolite', } ] } ).AddRadiobuttonField( { Name: 'deviceRatio', Label: 'AspectRatio', ValueType: 'num', ColSpan: 3, Items: [ { Name: 'off', Value: DeviceRatioOff // off has the value 1.5 }, { Name: '1:1', Value: 1/1 }, { Name: '3:2', Value: DeviceRatioOff+0.0001 // to distinguish from off -> +0.0001 }, { Name: '2:3', Value: 2/3 }, { Name: '4:3', Value: 4/3 }, { Name: '3:4', Value: 3/4 }, { Name: '16:9', Value: 16/9 }, { Name: '9:16', Value: 9/16 } ] } ).AddRadiobuttonField( { Name: 'showGrid', Label: 'Grid', ValueType: 'int', Items: [ { Name: 'Off', Value: 0 }, { Name: 'On', Value: 1 }, { Name: 'Projected', Value: 3 } ] } ).AddRadiobuttonField( { Name: 'nLines', Label: 'Lines', ValueType: 'int', Items: [ { Name: '15', Value: 15 }, { Name: '30', Value: 30 }, { Name: '45', Value: 45 }, { Name: '60', Value: 60 }, { Name: '90', Value: 90 }, { Name: '180', Value: 180 } ] } ).Render(); </jscript> {{end scroll}} {{NextTabBox}} <comment> Objects-1 Tab </comment> {{scroll|600px}} <jscript> ControlPanels.NewSliderPanel( { Name: 'Object-Sliders0', ModelRef: 'CurveApp', OnModelChange: UpdateAll, Format: 'std', Digits: DefaultDigits, ReadOnly: false, PanelFormat: 'InputMediumWidth' } ).AddValueSliderField( { Name: 'NObjects', ValueRef: 'NObjects[0]', Label: 'NObjects', Color: 'black', Format: 'fix', Digits: 0, Min: 0, Max: 200, Steps: 200, Inc: 1, } ).AddValueSliderField( { Name: 'ObjDist', Label: 'Dist', ValueRef: 'ObjDist[0]', SliderValueRef: 'Slider_ObjDist_Log[0]', UnitsData: 'LengthUnits', Color: 'blue', Min: 0, Max: 5, Inc: 100, } ).AddValueSliderField( { Name: 'ObjSize', ValueRef: 'ObjSize[0]', SliderValueRef: 'Slider_ObjSize_Log[0]', UnitsData: 'HeightUnits', Color: 'red', Min: 0, Max: 4, Inc: 1, } ).AddValueSliderField( { Name: 'ObjDeltaDist', Label: 'DeltaDist', ValueRef: 'ObjDeltaDist[0]', SliderValueRef: 'Slider_ObjDeltaDist_Log[0]', UnitsData: 'LengthUnits', Color: 'blue', Min: 0, Max: 4, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSidePos', Label: 'SidePos', ValueRef: 'ObjSidePos[0]', SliderValueRef: 'Slider_ObjSidePos_Log[0]', UnitsData: 'LengthUnits', Color: 'green', Min: -3, Max: 3, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSideVar', Label: 'SideVar', ValueRef: 'ObjSideVar[0]', SliderValueRef: 'Slider_ObjSideVar_Log[0]', UnitsData: 'LengthUnits', Color: 'green', Min: -5, Max: 5, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSizeVar', ValueRef: 'ObjSizeVar[0]', Label: 'SizeVar', Units: '%', Mult: 0.01, Color: 'red', Min: -1, Max: 1, Inc: 0.01, } ).Render(); </jscript> {{end scroll}} <style> #UnitsPanel { border: 2px solid #fa6; } #Object-Sliders0 .Row6 { background-color: #dfd; } #Object-Sliders0 .Row7 { background-color: #fdd; } #Object-Options0 .Row2 { background-color: #dfd; } #Object-Options0 .Row2 .FieldGrid { background-color: #dfd; } #Object-Options0 .Row2 .FieldCell { border-color: #dfd; } #Object-Options0 .Row3 { background-color: #fdd; } #Object-Options0 .Row3 .FieldGrid { background-color: #fdd; } #Object-Options0 .Row3 .FieldCell { border-color: #fdd; } </style> {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'Object-Options0', ModelRef: 'CurveApp', NCols: 1, OnModelChange: UpdateAll } ).AddRadiobuttonField( { Name: 'ObjType[0]', Label: 'ObjType', ValueType: 'int', NCols: 7, Items: [ { Name: 'T-Tower', Value: 3 }, { Name: 'City', Value: 4 }, { Name: 'Mountain', Value: 5 }, { Name: 'Bridge', Value: 6 },{ Name: 'Ship', Value: 2 },{ Name: 'Cloud', Value: 12 }, { Name: 'M-Rod', Value: 1 }, { Name: 'Wnd-Trb', Value: 10 }, { Name: 'Bedford', Value: 0 }, { Name: 'Tensas B', Value: 7 }, { Name: 'Causew-S', Value: 8 }, { Name: 'Causew-N', Value: 9 }, { Name: 'Rainy Lake', Value: 11 } ] } ).AddRadiobuttonField( { Name: 'ObjSideType[0]', Label: 'SideVar', ValueType: 'int', Items: [ { Name: 'Lin', Value: 0 }, { Name: '2-Col', Value: 1 }, { Name: 'Rand', Value: 2 }, { Name: 'Cos', Value: 3 }, { Name: 'Sin', Value: 4 } ] } ).AddRadiobuttonField( { Name: 'ObjSizeType[0]', Label: 'SizeVar', ValueType: 'int', Items: [ { Name: 'Lin', Value: 0 }, { Name: 'Alt', Value: 1 }, { Name: 'Rand', Value: 2 }, { Name: 'Cos', Value: 3 }, { Name: 'Sin', Value: 4 } ] } ).Render(); </jscript> {{end scroll}} {{NextTabBox}} <comment> Objects-2 Tab </comment> {{scroll|600px}} <jscript> ControlPanels.NewSliderPanel( { Name: 'Object-Sliders1', ModelRef: 'CurveApp', OnModelChange: UpdateAll, Format: 'std', Digits: DefaultDigits, ReadOnly: false, PanelFormat: 'InputMediumWidth' } ).AddValueSliderField( { Name: 'NObjects', ValueRef: 'NObjects[1]', Label: 'NObjects', Color: 'black', Format: 'fix', Digits: 0, Min: 0, Max: 200, Steps: 200, Inc: 1, } ).AddValueSliderField( { Name: 'ObjDist', Label: 'Dist', ValueRef: 'ObjDist[1]', SliderValueRef: 'Slider_ObjDist_Log[1]', UnitsData: 'LengthUnits', Color: 'blue', Min: 0, Max: 5, Inc: 100, } ).AddValueSliderField( { Name: 'ObjSize', ValueRef: 'ObjSize[1]', SliderValueRef: 'Slider_ObjSize_Log[1]', UnitsData: 'HeightUnits', Color: 'red', Min: 0, Max: 4, Inc: 1, } ).AddValueSliderField( { Name: 'ObjDeltaDist', Label: 'DeltaDist', ValueRef: 'ObjDeltaDist[1]', SliderValueRef: 'Slider_ObjDeltaDist_Log[1]', UnitsData: 'LengthUnits', Color: 'blue', Min: 0, Max: 4, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSidePos', Label: 'SidePos', ValueRef: 'ObjSidePos[1]', SliderValueRef: 'Slider_ObjSidePos_Log[1]', UnitsData: 'LengthUnits', Color: 'green', Min: -3, Max: 3, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSideVar', Label: 'SideVar', ValueRef: 'ObjSideVar[1]', SliderValueRef: 'Slider_ObjSideVar_Log[1]', UnitsData: 'LengthUnits', Color: 'green', Min: -5, Max: 5, Inc: 10, } ).AddValueSliderField( { Name: 'ObjSizeVar', ValueRef: 'ObjSizeVar[1]', Label: 'SizeVar', Units: '%', Mult: 0.01, Color: 'red', Min: -1, Max: 1, Inc: 0.01, } ).Render(); </jscript> {{end scroll}} <style> #Object-Sliders1 .Row6 { background-color: #dfd; } #Object-Sliders1 .Row7 { background-color: #fdd; } #Object-Options1 .Row2 { background-color: #dfd; } #Object-Options1 .Row2 .FieldGrid { background-color: #dfd; } #Object-Options1 .Row2 .FieldCell { border-color: #dfd; } #Object-Options1 .Row3 { background-color: #fdd; } #Object-Options1 .Row3 .FieldGrid { background-color: #fdd; } #Object-Options1 .Row3 .FieldCell { border-color: #fdd; } </style> {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'Object-Options1', ModelRef: 'CurveApp', NCols: 1, OnModelChange: UpdateAll } ).AddRadiobuttonField( { Name: 'ObjType[1]', Label: 'ObjType', ValueType: 'int', NCols: 7, Items: [ { Name: 'T-Tower', Value: 3 }, { Name: 'City', Value: 4 }, { Name: 'Mountain', Value: 5 }, { Name: 'Bridge', Value: 6 },{ Name: 'Ship', Value: 2 },{ Name: 'Cloud', Value: 12 }, { Name: 'M-Rod', Value: 1 }, { Name: 'Wnd-Trb', Value: 10 }, { Name: 'Bedford', Value: 0 }, { Name: 'Tensas B', Value: 7 }, { Name: 'Causew-S', Value: 8 }, { Name: 'Causew-N', Value: 9 }, { Name: 'Rainy Lake', Value: 11 } ] } ).AddRadiobuttonField( { Name: 'ObjSideType[1]', Label: 'SideVar', ValueType: 'int', Items: [ { Name: 'Lin', Value: 0 }, { Name: '2-Col', Value: 1 }, { Name: 'Rand', Value: 2 }, { Name: 'Cos', Value: 3 }, { Name: 'Sin', Value: 4 } ] } ).AddRadiobuttonField( { Name: 'ObjSizeType[1]', Label: 'SizeVar', ValueType: 'int', Items: [ { Name: 'Lin', Value: 0 }, { Name: 'Alt', Value: 1 }, { Name: 'Rand', Value: 2 }, { Name: 'Cos', Value: 3 }, { Name: 'Sin', Value: 4 } ] } ).Render(); </jscript> {{end scroll}} {{NextTabBox}} <comment> Refraction Tab </comment> {{scroll|600px}} <jscript> var BaroConst = { hIx: 0, // air level data of standard atmosphere model hLimitTab: [ 11000, 20000, 32000, 47000, 51000, 71000, 84852, 0 ], hRefTab: [ 0, 11000, 20000, 32000, 47000, 51000, 71000, NaN ], alphaTab: [ -0.0065, 0, 0.001, 0.0028, 0, -0.0028, -0.002, NaN ], TRefTab: [ 288.15, 216.65, 216.65, 228.65, 270.65, 270.65, 214.65, NaN ], rhoRefTab: [ 1.225, 0.363918, 0.0880348, 0.013225, 0.00142753, 0.000861605, 0.000064211, NaN ], pRefTab: [ 101325, 22632.1, 5474.89, 868.019, 110.906, 66.9389, 3.95642, NaN ], // some general constants g: 9.80665, R: 8.31446, RS: 287.058, kappa: 1.4, M: 28.9644, // private function defIx: function(i) { return xDefNum( i, this.hIx ); }, // set altitude range for following functions SetAltRange: function( h ) { for (var i = 0; i < this.hLimitTab.length; i++) { if (h <= this.hLimitTab[i]) { this.hIx = i; return; } } this.hIx = this.hLimitTab.length - 1; }, // query level dependent constants // default for i is this.hIx, see SetAltRange(h) hRef: function(i) { return this.hRefTab[this.defIx(i)]; }, alpha: function(i) { return this.alphaTab[this.defIx(i)]; }, TRef: function(i) { return this.TRefTab[this.defIx(i)]; }, rhoRef: function(i) { return this.rhoRefTab[this.defIx(i)]; }, pRef: function(i) { return this.pRefTab[this.defIx(i)]; } }; var ConvertUnit = { ms_kt: function( v ) { return v * 1.944; }, kt_ms: function( v ) { return v / 1.944; }, ft_m: function( h ) { return h * 0.3048; }, m_ft: function( h ) { return h / 0.3048; }, K_C: function( t ) { return t - 273.15; }, C_K: function( t ) { return t + 273.15; }, K_F: function( t ) { return t * 1.8 - 459.67; }, F_K: function( t ) { return (t + 459.67) / 1.8; } } var BaroModel = { h: -1, // used for optimisation, CurveApp.Height is used instead, T_C: 0, T: 0, p: 0, rho: 0, alpha: 0, TempOfH: function( h ) { return BaroConst.TRef() + BaroConst.alpha() * (h - BaroConst.hRef()); }, PressureOfH: function( h ) { var alpha = BaroConst.alpha(); if (alpha == 0) { // isoterm var hs = BaroConst.RS * BaroConst.TRef() / BaroConst.g; var p = BaroConst.pRef() * Math.exp( -(h - BaroConst.hRef()) / hs ); return p; } else { var beta = BaroConst.g / BaroConst.RS / alpha; var p = BaroConst.pRef() * Math.pow( 1 + alpha * (h - BaroConst.hRef()) / BaroConst.TRef(), -beta ); return p; } }, DensityOfH: function( h ) { var alpha = BaroConst.alpha(); if (alpha == 0) { // isoterm var hs = BaroConst.RS * BaroConst.TRef() / BaroConst.g; var r = BaroConst.rhoRef() * Math.exp( -(h - BaroConst.hRef()) / hs ); return r; } else { var beta = BaroConst.g / BaroConst.RS / alpha; var r = BaroConst.rhoRef() * Math.pow( 1 + alpha * (h - BaroConst.hRef()) / BaroConst.TRef(), -beta-1 ); return r; } }, Update: function() { if (this.h == CurveApp.Height) return; BaroConst.SetAltRange( CurveApp.Height ); this.alpha = BaroConst.alpha(); this.T = this.TempOfH( CurveApp.Height ); this.T_C = this.T - 273.15; this.p = this.PressureOfH( CurveApp.Height ); //this.rho = this.DensityOfH( CurveApp.Height ); not used this.h = CurveApp.Height; } }; CurveApp.BaroModel = BaroModel; ControlPanels.NewPanel( { Name: 'Refraction-Panel', ModelRef: 'CurveApp', OnModelChange: UpdateAll, NCols: 2, ReadOnly: false, Format: 'std', Digits: DefaultDigits } ).AddSliderField( { Name: 'refractionSlider', Label: 'Refraction', Color: 'red', ColSpan: 3, Min: -1, Max: 1, } ).AddTextField( { Name: 'refractionCoeff', Label: 'Coeff. k', Inc: 0.01, } ).AddTextField( { Name: 'tempGradient', Label: 'dT/dh', UnitsData: 'GradientUnits', Inc: 0.0001, } ).AddTextField( { Name: 'refractionFact', Label: 'Factor a', Inc: 0.001, } ).AddTextField( { Name: 'temperatureC', Label: 'Temp. T', Units: '°C', Inc: 0.1, } ).AddTextField( { Name: 'refractionRadius', Label: 'Radius R\'', Mult: 1000, UnitsData: 'BigLengthUnits', Inc: 100000, } ).AddTextField( { Name: 'pressure', Label: 'Press. P', Units: 'mbar', Inc: 10, } ).AddTextField( { Name: 'ObjRefrAng', Label: 'ObjRefr∠', ReadOnly: true, UnitsData: 'AngleFormats', } ).AddTextField( { Name: 'ObjAngSize', Label: 'ObjSize∠', ReadOnly: true, UnitsData: 'AngleFormats', } ).AddTextField( { Name: 'ObjLiftAbs', Label: 'ObjLiftAbs', ReadOnly: true, UnitsData: 'HeightUnits', } ).AddTextField( { Name: 'ObjLiftRel', Label: 'ObjLiftRel', ReadOnly: true, UnitsData: 'HeightUnits', } ).AddRadiobuttonField( { Name: 'refractionSync', Label: 'BaroLink', ColSpan: 3, ValueType: 'int', Items: [ { Name: 'off', Value: 0 }, { Name: 'T,P', Value: 1 }, { Name: 'Std-Atm', Value: 2 }, { Name: 'k=0.13', Value: 3 }, { Name: 'k=0.17', Value: 4 }, { Name: 'a=7/6', Value: 5 }, { Name: 'a=7/2', Value: 6 } ] } ).Render(); function ResetRefr() { CurveApp.refractionSync = 1; CurveApp.refractionCoeff = 0; UpdateAll(); } ControlPanels.NewPanel( { Name: 'RefractionBaro-Panel', ModelRef: 'CurveApp', OnModelChange: UpdateAll, NCols: 2, ReadOnly: true, Format: 'std', Digits: DefaultDigits } ).AddHeader( { Text: 'Std-Atmosphere Barometer: use BaroLink to link with Refraction settings' + ControlPanels.SmallButtonR( 'Refr k=0', 'ResetRefr()', 'Red' ), ColSpan: 4, } ).AddSliderField( { Name: 'HeightSlider', Label: 'Height h', Color: 'blue', ColSpan: 3, Min: 0, Max: 1, } ).AddTextField( { Name: 'Height', Label: 'Height h', UnitsData: 'HeightUnits', ReadOnly: false, Inc: 0.1, } ).AddTextField( { Name: 'BaroModel.T_C', Label: 'T(h)', Units: '°C', } ).AddTextField( { Name: 'BaroModel.alpha', Label: 'Td/dh(h)', UnitsData: 'GradientUnits', } ).AddTextField( { Name: 'BaroModel.p', Label: 'P(h)', Units: 'mbar', Mult: 100, } ).Render(); </jscript> {{end scroll}} Please read the paragraph on [[Finding the curvature of the Earth#Refraction|Refraction]] to get familiar with this panel. {{NextTabBox}} <comment> Image Tab </comment> {{scroll}} <jscript> ControlPanels.NewSliderPanel( { Name: 'Image-Panel', ModelRef: 'CurveApp', OnModelChange: UpdateAll, Format: 'std', Digits: DefaultDigits, ReadOnly: false, PanelFormat: 'InputMediumWidth', } ).AddValueSliderField( { Name: 'OverlayImageAlpha', Label: 'Alpha', Color: 'black', Format: 'fix0', Digits: 2, Units: '%', Min: 0, Max: 1, Steps: 200, Inc: 0.05, Mult: 0.01, } ).AddTextField( { Name: 'OverlayImage', Label: 'Image', Mult: 0, ColSpan: 3, } ).AddTextField( { Name: 'DemoText', Label: 'Header', Mult: 0, ColSpan: 3, } ).AddTextField( { Name: 'Description', Label: 'Description', Mult: 0, ColSpan: 3, } ).AddCheckboxField( { Name: 'ImageOnBackground', Label: 'Image', Items: [ { Name: 'ImageOnBackgroundCB', ValueRef: 'ImageOnBackground', Text: 'On Background', } ] } ).AddCheckboxField( { Name: 'TextBackground', Label: 'Text', Items: [ { Name: 'TextBackgroundCB', ValueRef: 'TextBackground', Text: 'Draw Background', } ] } ).Render(); </jscript> {{end scroll}} If you want to upload and overlay your own image, please contact me and send me your image. Email is on the bottom of the page. I will upload the image and give you instructions of how to use it. {{NextTabBox}} <comment> Units-Calculator Tab </comment> <jscript> function CLengthModel() { this.m = 0; this.km = 0; this.sm = 0; this.mile = 0; this.in = 0; this.cm = 0; this.ft = 0; this.fl = 0; this.mLast = 0; this.kmLast = 0; this.smLast = 0; this.mileLast = 0; this.inLast = 0; this.cmLast = 0; this.ftLast = 0; this.flLast = 0; } CLengthModel.prototype.calcOthersFromMeter = function() { this.km = this.m / 1000; this.sm = this.m / 1852; this.mile = this.m / 1609.344; this.in = this.m / 0.0254; this.cm = this.m * 100; this.ft = this.m / 0.3048; this.fl = this.ft / 100; } CLengthModel.prototype.Update = function() { if (this.m != this.mLast) { this.calcOthersFromMeter(); } else if (this.km != this.kmLast) { this.m = this.km * 1000; this.calcOthersFromMeter(); } else if (this.sm != this.smLast) { this.m = this.sm * 1852; this.calcOthersFromMeter(); } else if (this.mile != this.mileLast) { this.m = this.mile * 1609.344; this.calcOthersFromMeter(); } else if (this.in != this.inLast) { this.m = this.in * 0.0254; this.calcOthersFromMeter(); } else if (this.cm != this.cmLast) { this.m = this.cm / 100; this.calcOthersFromMeter(); } else if (this.ft != this.ftLast) { this.m = this.ft * 0.3048; this.calcOthersFromMeter(); } else if (this.fl != this.flLast) { this.m = this.fl * 100 * 0.3048; this.calcOthersFromMeter(); } this.mLast = this.m; this.kmLast = this.km; this.smLast = this.sm; this.mileLast = this.mile; this.inLast = this.in; this.cmLast = this.cm; this.ftLast = this.ft; this.flLast = this.fl; } var LengthModel = new CLengthModel(); function UpdateLengthModel() { LengthModel.Update(); ControlPanels.Update( 'Units-Calculator' ); } xOnLoad( UpdateLengthModel ); </jscript> {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'Units-Calculator', ModelRef: 'LengthModel', OnModelChange: UpdateLengthModel, NCols: 2, Format: 'std', Digits: 10 } ).AddTextField( { Name: 'm', Label: 'Meter', Units: 'm', Inc: 1, } ).AddTextField( { Name: 'km', Label: 'Kilometer', Units: 'km', Inc: 1, } ).AddTextField( { Name: 'sm', Label: 'Nautical Mile', Units: 'sm', Inc: 1, } ).AddTextField( { Name: 'mile', Label: 'Statute Mile', Units: 'mi', Inc: 1, } ).AddTextField( { Name: 'in', Label: 'Inch', Units: 'in', Inc: 1, } ).AddTextField( { Name: 'cm', Label: 'Centimeter', Units: 'cm', Inc: 1, } ).AddTextField( { Name: 'ft', Label: 'Feet', Units: 'ft', Inc: 1, } ).AddTextField( { Name: 'fl', Label: 'Flight Level FL', Units: '', Inc: 10, } ).Render(); </jscript> {{end scroll}} Use this Formular to convert between different lengh units. You can Copy/Paste the results into input fields in the other Forms. {{NextTabBox}} <comment> Save/Restore Tab </comment> {{form textarea|SaveRestorePanel|12|spellcheck=false|class=ListingDisplay|}} [javascript:void DataX.GetAppState()|{{ButtonText|Get App State|blue}}] [javascript:void DataX.GetAppStateUrl(ThisPageUrl)|{{ButtonText|Get App Url|blue}}] [javascript:void DataX.SetAppState()|{{ButtonText|Set App State|green}}] [javascript:void DataX.CompactSaveRestoreDomObj()|{{ButtonText|Compact|red}}] [javascript:void DataX.ClearSaveRestoreDomObj()|{{ButtonText|Clear|red}}] Use this panel to save a certain App state by '''Get App State''' and use Copy/Paste to save the state in an external text file. Use Copy/Paste to copy a saved state from an external text file into this panel and click '''Set App State''' to activate this state. You can change the Parameters in this panel and apply them by pressing the RETURN button on the keyboard. Use '''Get App Url''' to get an URL containing the current App State. Click '''Set App State''' oder copy the URL into any browser address field to go to this page and display the current App State. <jscript> function HandleUrlCommands() { var dataStr = DataX.GetUrlStr( 'tab' ); if (dataStr != '') { if (dataStr == 'View') { Tabs.Select( 'CurveSettingsTabs', 0 ); } else if (dataStr == 'Obj1') { Tabs.Select( 'CurveSettingsTabs', 1 ); } else if (dataStr == 'Obj2') { Tabs.Select( 'CurveSettingsTabs', 2 ); } else if (dataStr == 'Refr') { Tabs.Select( 'CurveSettingsTabs', 3 ); } else if (dataStr == 'Unit') { Tabs.Select( 'CurveSettingsTabs', 4 ); } else if (dataStr == 'SaveRest') { Tabs.Select( 'CurveSettingsTabs', 5 ); } } Animations.TimeStrech = 1 / DataX.GetUrlNum( 'speed', 1 ); if (Animations.TimeStrech < 0.01) Animations.TimeStrech = 0.01; if (Animations.TimeStrech > 100) Animations.TimeStrech = 100; var dataStr = DataX.GetUrlStr( 'demo' ); if (dataStr != '') { location.hash = "#App"; var play = false; var pos = DataX.GetUrlInt( 'pos', 0 ); if (pos == 0) { pos = DataX.GetUrlInt( 'play', 0 ); play = pos > 0; } if (pos > 0) { pos--; setTimeout( function() { Demos.SetDemo( dataStr, play, pos ); }, 1000 ); } else { setTimeout( function() { Demos.Play( dataStr, true ); }, 1000 ); } return; } } function SetupUrlState( state ) { DataX.StreamToAppState( state ); UpdateAll(false); } function SetupForMeasurements() { SetupUrlState( '--23-13000-430-13-21-51-21-212000-21000-430-2~20-410-40.17060685-1~0.0064999992-21012.8897-114.9805-8' ); } function SetupTeslaInSpace4k() { SetupUrlState( '-14-1Tesla--in--Space-135-1Altitude--4000--km;--FocalLength--32--mm-14000000-132-1~61.8-1~3.5-1~4.1326531-9-9-9-3~0.0343-10-10.00373383-1~86.204-620-1PRTeslaAt4000km0.jpg-2' ); } function SetupTeslaInSpace7k() { SetupUrlState( '-14-1Tesla--in--Space-135-1Altitude--7000--km;--FocalLength--32--mm-17000000-132-1~83.7-1~6.023-1~0.031248794-9-9-9-3~0.0343-10-10.00373383-1~86.204-620-1PRTeslaAt7000km0.jpg-2' ); } </jscript> {{EndTabBoxes}} {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'UnitsPanel', ModelRef: 'CurveApp', NCols: 2, OnModelChange: UpdateAll } ).AddRadiobuttonField( { Name: 'UnitsType', Label: 'Units', ValueType: 'int', Items: [ { Name: 'm (Metric)', Value: 0 }, { Name: 'mi/ft (Imp)', Value: 1 }, { Name: 'ft/ft (Imp)', Value: 2 } ] } ).AddRadiobuttonField( { Name: 'AngleFormat', Label: 'AngleFormat', ValueType: 'int', Items: [ { Name: 'deg.', Value: 0 }, { Name: 'DM', Value: 1 }, { Name: 'DMS', Value: 2 } ] } ).Render(); </jscript> {{end scroll}} {{scroll}} <jscript> ControlPanels.NewPanel( { Name: 'Output-Panel', ModelRef: 'CurveApp', OnModelChange: UpdateAll, NCols: 2, ReadOnly: true, Format: 'std', Digits: DefaultDigits } ).AddTextField( { Name: 'hDip', Label: 'DipHeight b', UnitsData: 'HeightUnits', } ).AddTextField( { Name: 'aDip', Label: 'DipAngle α', Mult: Math.PI / 180, UnitsData: 'AngleFormats', } ).AddTextField( { Name: 'dHorizon', Label: 'HorDist s', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'aEarth', Label: 'AngDiameter', Mult: Math.PI / 180, UnitsData: 'AngleFormats', } ).AddTextField( { Name: 'dView', Label: 'HorDistView v', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'dDelta', Label: 'GridSpacing', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'rDisk', Label: 'HorDistX d', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'zDisk', Label: 'HorDistZ p', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'horizonLftRgtWidth', Label: 'HorLftRgtWidth', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'sceneWidth', Label: 'FrameWidth', UnitsData: 'LengthUnits', } ).AddTextField( { Name: 'horizonLftRgtDrop', Label: 'HorLftRgtDrop', UnitsData: 'HeightUnits', } ).AddTextField( { Name: 'horizonLftRgtDropAngle', Label: 'HorLftRgtDrop∠', UnitsData: 'AngleFormats', } ).AddTextField( { Name: 'ObjHidden', Label: 'ObjHidden', UnitsData: 'HeightUnits', } ).AddTextField( { Name: 'ObjRealDist', Label: 'ObjDist', UnitsData: 'LengthUnits', } ).Render(); </jscript> {{end scroll}}