Merge pull request #59 from antstorm/rates-preview

Rates preview for the PID Tuning tab
10.3.x-maintenance
borisbstyle 2016-06-29 23:38:29 +02:00 committed by GitHub
commit 99106cb409
6 changed files with 311 additions and 188 deletions

View File

@ -232,7 +232,7 @@
"defaultWelcomeIntro": {
"message": "Welcome to <strong>Betaflight - Configurator</strong>, a utility designed to simplify updating, configuring and tuning of your flight controller."
},
"defaultWelcomeText": {
"message": "The application supports all hardware that can run Betaflight (<a href=\"http://seriouslypro.com/spracingf3\" target=\"_blank\">SPRacingF3</a>, <a href=\"http://www.immersionrc.com/fpv-products/vortex-racing-quad/\" target=\"_blank\">Vortex</a>, <a href=\"https://github.com/TauLabs/TauLabs/wiki/Sparky\" target=\"_blank\">Sparky</a>, <a href=\"http://www.readymaderc.com/store/index.php?main_page=product_info&cPath=76_156&products_id=4221\" target=\"_blank\">DoDo</a>, <a href=\"https://www.openpilot.org/product/coptercontrol/\" target=\"_blank\">CC3D/EVO</a>, <a href=\"http://www.multiwiicopter.com/products/paris-air-hero-32-naze\" target=\"_blank\">Air Hero 32</a>, <a href=\"http://www.readytoflyquads.com/flight-controllers/flip-series\" target=\"_blank\">Flip32/+/Deluxe</a>, <a href=\"http://multirotormania.com/129-dragonfly32\" target=\"_blank\">DragonFly32</a>, <a href=\"http://www.goodluckbuy.com/micro-quadcopter-flight-driver-controller-9dof-9-axis-altitude-sensor-stm32f103.html\" target=\"_blank\">CJMCU Microquad</a>, Chebuzz F3, <a href=\"http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF254044\" target=\"_blank\">STM32F3Discovery</a>, <a href=\"http://www.overskyrc.com/hermit-micro-fpv-brushless-quadcopter-145mm-98g-fully-assembled-p-621.html\" target=\"_blank\">Hermit</a>, <a href=\"http://rcexplorer.se/blog/2015/05/introducing-the-naze32-tricopter-frame/\" target=\"_blank\">Naze32 Tricopter Frame</a>, <a href=\"http://www.2dogrc.com/skyline-32-naze-32-bit-flight-controller-acro-version.html\" target=\"_blank\">Skyline32</a>, <a href=\"http://abusemark.com/store/index.php?main_page=index&cPath=1\" target=\"_blank\">Naze/32/Mini/Pro</a>/<a href=\"http://www.massiverc.com/Shop/en/574-massive-acro-blackbox-flight-control-board.html\" target=\"_blank\">Blackbox</a> etc)<br /><br />The firmware source code can be downloaded from <a href=\"https://github.com/Betaflight/Betaflight\" title=\"www.github.com\" target=\"_blank\">here</a><br />The newest binary firmware image is available <a href=\"https://github.com/Betaflight/Betaflight/releases\" title=\"www.github.com\" target=\"_blank\">here</a>, development builds available <a href=\"http://Betaflight.memoryleaks.org/builds/\" target=\"_blank\">here</a><br /><br />Latest <strong>CP210x Drivers</strong> can be downloaded from <a href=\"http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx\" title=\"http://www.silabs.com\" target=\"_blank\">here</a><br />Latest <strong>STM USB VCP Drivers</strong> can be downloaded from <a href=\"http://www.st.com/web/en/catalog/tools/PF257938\" title=\"http://www.st.com\" target=\"_blank\">here</a><br />Latest <strong>Zadig</strong> for Windows DFU flashing can be downloaded from <a href=\"http://zadig.akeo.ie/\" title=\"http://zadig.akeo.ie\" target=\"_blank\">here</a><br />"
},
@ -296,7 +296,7 @@
"defaultSupport5": {
"message": "<a href=\"http://webchat.freenode.net/?channels=Betaflight\" target=\"_blank\">Join via WebClient</a>"
},
"initialSetupBackupAndRestoreApiVersion": {
"message": "<span style=\"color: red\">Backup and restore functionality disabled.</span> You have firmware with API version <span style=\"color: red\">$1</span>, backup and restore requires <span style=\"color: #ffbb00\">$2</span>. Please backup your settings via the CLI, see Betaflight documentation for procedure."
},
@ -532,7 +532,7 @@
"featureAIRMODE": {
"message": "Airmode always enabled!"
},
"configurationFeatureEnabled": {
"message": "Enabled"
},
@ -668,7 +668,7 @@
"configurationLoopTime": {
"message": "Flight Controller Loop Time"
},
"configurationCalculatedCyclesSec": {
"configurationCalculatedCyclesSec": {
"message": "Cycles/Sec (Hz)"
},
"configurationLoopTimeHelp": {
@ -766,6 +766,9 @@
"pidTuningRate": {
"message": "Rate"
},
"pidTuningRatesPreview": {
"message": "Rates Preview"
},
"pidTuningRcExpo": {
"message": "RC Expo"
},
@ -832,7 +835,7 @@
},
"receiverHelpYawDeadband": {
"message": "These are values (in us) by how much RC input can be different before it's considered valid. For transmitters with jitter on outputs, this value can be increased if rc inputs twitch while idle. <strong>This setting is for Yaw only.</strong>"
},
},
"receiverYawDeadband": {
"message": "Yaw Deadband"
},
@ -1078,7 +1081,7 @@
},
"transponderDataInvalid": {
"message": "Transponder data is <span style=\"color: red\">invalid</span>"
},
},
"transponderEepromSaved": {
"message": "EEPROM <span style=\"color: #ffbb00\">saved</span>"
},
@ -1251,14 +1254,14 @@
"blackboxButtonSave": {
"message": "Save and reboot"
},
"serialLoggingSupportedNote": {
"message": "You can log to an external logging device (such as an OpenLog or compatible clone) by using a serial port. Configure the port on the Ports tab."
},
"sdcardNote": {
"message": "Flight logs can be recorded to your flight controller's onboard SD card slot."
},
"dataflashNote": {
"message": "Flight logs can be recorded to your flight controller's onboard dataflash chip."
},
@ -1304,7 +1307,7 @@
"dataflashFileWriteFailed": {
"message": "Failed to write to the file you selected, are the permissions on that folder okay?"
},
"firmwareFlasherReleaseSummaryHead": {
"message": "Release info"
},
@ -1460,7 +1463,7 @@
"ledStripEepromSaved": {
"message": "EEPROM <span style=\"color: #ffbb00\">saved</span>"
},
"controlAxisRoll": {
"message": "Roll"
},
@ -1521,7 +1524,7 @@
"controlAxisAux16": {
"message": "AUX 16"
},
"pidTuningBasic": {
"message": "Basic/Acro"
},
@ -1545,7 +1548,7 @@
},
"pidTuningLevelD": {
"message": "Transition (Horizon)"
},
},
"pidTuningLevelHelp": {
"message": "The values below change the behaviour of the ANGLE and HORIZON flight modes. Different PID controllers handle the LEVEL values differently. Please check the documentation."
},
@ -1669,5 +1672,5 @@
"configurationMagHardware": {
"message": "Magnetometer (if supported)"
}
}

View File

@ -2,30 +2,145 @@
// generate mixer
var mixerList = [
{name: 'Tricopter', model: 'tricopter', image: 'tri'},
{name: 'Quad +', model: 'quad_x', image: 'quad_p'},
{name: 'Quad X', model: 'quad_x', image: 'quad_x'},
{name: 'Bicopter', model: 'custom', image: 'bicopter'},
{name: 'Gimbal', model: 'custom', image: 'custom'},
{name: 'Y6', model: 'y6', image: 'y6'},
{name: 'Hex +', model: 'hex_plus', image: 'hex_p'},
{name: 'Flying Wing', model: 'custom', image: 'flying_wing'},
{name: 'Y4', model: 'y4', image: 'y4'},
{name: 'Hex X', model: 'hex_x', image: 'hex_x'},
{name: 'Octo X8', model: 'custom', image: 'octo_x8'},
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p'},
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x'},
{name: 'Airplane', model: 'custom', image: 'airplane'},
{name: 'Heli 120', model: 'custom', image: 'custom'},
{name: 'Heli 90', model: 'custom', image: 'custom'},
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad'},
{name: 'Hex H', model: 'custom', image: 'custom'},
{name: 'PPM to SERVO', model: 'custom', image: 'custom'},
{name: 'Dualcopter', model: 'custom', image: 'custom'},
{name: 'Singlecopter', model: 'custom', image: 'custom'},
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad'},
{name: 'Custom', model: 'custom', image: 'custom'},
{name: 'Custom Airplane', model: 'custom', image: 'custom'},
{name: 'Custom Tricopter', model: 'custom', image: 'custom'}
{name: 'Tricopter', model: 'tricopter', image: 'tri'},
{name: 'Quad +', model: 'quad_x', image: 'quad_p'},
{name: 'Quad X', model: 'quad_x', image: 'quad_x'},
{name: 'Bicopter', model: 'custom', image: 'bicopter'},
{name: 'Gimbal', model: 'custom', image: 'custom'},
{name: 'Y6', model: 'y6', image: 'y6'},
{name: 'Hex +', model: 'hex_plus', image: 'hex_p'},
{name: 'Flying Wing', model: 'custom', image: 'flying_wing'},
{name: 'Y4', model: 'y4', image: 'y4'},
{name: 'Hex X', model: 'hex_x', image: 'hex_x'},
{name: 'Octo X8', model: 'custom', image: 'octo_x8'},
{name: 'Octo Flat +', model: 'custom', image: 'octo_flat_p'},
{name: 'Octo Flat X', model: 'custom', image: 'octo_flat_x'},
{name: 'Airplane', model: 'custom', image: 'airplane'},
{name: 'Heli 120', model: 'custom', image: 'custom'},
{name: 'Heli 90', model: 'custom', image: 'custom'},
{name: 'V-tail Quad', model: 'quad_vtail', image: 'vtail_quad'},
{name: 'Hex H', model: 'custom', image: 'custom'},
{name: 'PPM to SERVO', model: 'custom', image: 'custom'},
{name: 'Dualcopter', model: 'custom', image: 'custom'},
{name: 'Singlecopter', model: 'custom', image: 'custom'},
{name: 'A-tail Quad', model: 'quad_atail', image: 'atail_quad'},
{name: 'Custom', model: 'custom', image: 'custom'},
{name: 'Custom Airplane', model: 'custom', image: 'custom'},
{name: 'Custom Tricopter', model: 'custom', image: 'custom'}
];
// 3D model
var Model = function (wrapper, canvas) {
var useWebGLRenderer = this.canUseWebGLRenderer();
this.wrapper = wrapper;
this.canvas = canvas;
if (useWebGLRenderer) {
this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas[0], alpha: true, antialias: true });
} else {
this.renderer = new THREE.CanvasRenderer({ canvas: this.canvas[0], alpha: true });
}
// initialize render size for current canvas size
this.renderer.setSize(this.wrapper.width() * 2, this.wrapper.height() * 2);
// load the model including materials
var model_file = useWebGLRenderer ? mixerList[CONFIG.multiType - 1].model : 'fallback';
// Temporary workaround for 'custom' model until akfreak's custom model is merged.
if (model_file == 'custom') { model_file = 'fallback'; }
// setup scene
this.scene = new THREE.Scene();
// modelWrapper adds an extra axis of rotation to avoid gimbal lock with the euler angles
this.modelWrapper = new THREE.Object3D();
// stationary camera
this.camera = new THREE.PerspectiveCamera(60, this.wrapper.width() / this.wrapper.height(), 1, 10000);
// move camera away from the model
this.camera.position.z = 125;
// some light
var light = new THREE.AmbientLight(0x404040);
var light2 = new THREE.DirectionalLight(new THREE.Color(1, 1, 1), 1.5);
light2.position.set(0, 1, 0);
// add camera, model, light to the foreground scene
this.scene.add(light);
this.scene.add(light2);
this.scene.add(this.camera);
this.scene.add(this.modelWrapper);
// Load model file, add to scene and render it
this.loadJSON(model_file, (function (model) {
this.model = model;
this.modelWrapper.add(model);
this.scene.add(this.modelWrapper);
this.render();
}).bind(this));
};
Model.prototype.loadJSON = function (model_file, callback) {
var loader = new THREE.JSONLoader();
loader.load('./resources/models/' + model_file + '.json', function (geometry, materials) {
var modelMaterial = new THREE.MeshFaceMaterial(materials),
model = new THREE.Mesh(geometry, modelMaterial);
model.scale.set(15, 15, 15);
callback(model);
});
};
Model.prototype.canUseWebGLRenderer = function () {
// webgl capability detector
// it would seem the webgl "enabling" through advanced settings will be ignored in the future
// and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one
var detector_canvas = document.createElement('canvas');
return window.WebGLRenderingContext && (detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'))
};
Model.prototype.rotateTo = function (x, y, z) {
if (!this.model) { return; }
this.model.rotation.x = x;
this.modelWrapper.rotation.y = y;
this.model.rotation.z = z;
this.render();
};
Model.prototype.rotateBy = function (x, y, z) {
if (!this.model) { return; }
this.model.rotateX(x);
this.model.rotateY(y);
this.model.rotateZ(z);
this.render();
};
Model.prototype.render = function () {
if (!this.model) { return; }
// draw
this.renderer.render(this.scene, this.camera);
};
// handle canvas resize
Model.prototype.resize = function () {
this.renderer.setSize(this.wrapper.width() * 2, this.wrapper.height() * 2);
this.camera.aspect = this.wrapper.width() / this.wrapper.height();
this.camera.updateProjectionMatrix();
this.render();
};

View File

@ -177,11 +177,6 @@
border-top-right-radius: 3px;
}
.tab-pid_tuning .tpa {
/*border: 0px solid #ccc; */
margin-bottom: 10px;
}
.tab-pid_tuning .rc_curve {
float: right;
width: calc(100% - 2px); /* - ( "virtual" margin) */
@ -580,3 +575,21 @@
margin-top: -28px;
margin-left: 8px;
}
.tab-pid_tuning .rates_preview_cell {
position: relative;
width: 100%;
height: 0;
padding-bottom: 100%;
}
.tab-pid_tuning .rates_preview {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url(../images/paper.jpg);
background-size: 100%;
background-position: center;
}

View File

@ -199,50 +199,74 @@
</tr>
</table>
</div>
<div class="cf_column half topspacer">
<div>
<table class="rc_curve cf">
<thead>
<tr>
<th i18n="rcCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="pitch_roll_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="clear-both"></div>
</div>
<div class="topspacer">
<table class="rc_yaw_curve cf">
<thead>
<tr>
<th i18n="rcYawCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="yaw_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div class="clear-both"></div>
</div>
</div>
<div class="cf_column half topspacer">
<table class="tpa cf">
<thead>
<tr>
<th i18n="pidTuningRatesPreview"></th>
</tr>
</thead>
<tbody>
<tr>
<td class="rates_preview_cell">
<div class="rates_preview">
<canvas id="canvas"></canvas>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="cf_column third_right">
<div class="spacer_left">
<table class="rc_curve cf">
<thead>
<tr>
<th i18n="rcCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="pitch_roll_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left topspacer">
<table class="rc_yaw_curve cf">
<thead>
<tr>
<th i18n="rcYawCurve"></th>
</tr>
</thead>
</tbody>
<tr>
<td>
<div class="spacer" style="margin-top: 10px; margin-bottom: 10px;">
<div class="yaw_curve">
<canvas height="120px" style="width: 100%; height: 100%"></canvas>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left throttle topspacer">
<div class="spacer_left throttle">
<table class="cf">
<thead>
<tr>
@ -259,15 +283,23 @@
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="spacer_left topspacer">
<table class="tpa cf">
<thead>
<tr>
<th i18n="receiverThrottleMid"></th>
<th i18n="receiverThrottleExpo"></th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="number" name="mid" step="0.01" min="0" max="1" /></td>
<td><input type="number" name="expo" step="0.01" min="0" max="1" /></td>
</tr>
</tbody>
</tbody>
</table>
</div>
<div class="spacer_left topspacer">

View File

@ -315,7 +315,6 @@ TABS.pid_tuning.initialize = function (callback) {
}
});
pid_and_rc_to_form();
var pidController_e = $('select[name="controller"]');
@ -339,12 +338,12 @@ TABS.pid_tuning.initialize = function (callback) {
{ name: "Float"},
]
}
for (var i = 0; i < pidControllerList.length; i++) {
pidController_e.append('<option value="' + (i) + '">' + pidControllerList[i].name + '</option>');
}
var form_e = $('#pid-tuning');
if (GUI.canChangePidController) {
@ -532,6 +531,13 @@ TABS.pid_tuning.initialize = function (callback) {
}
});
// Setup model for rates preview
self.initRatesPreview();
self.renderModel();
// enable RC data pulling for rates preview
GUI.interval_add('receiver_pull', self.getRecieverData, true);
// status data pulled via separate timer with static speed
GUI.interval_add('status_pull', function status_pull() {
MSP.send_message(MSP_codes.MSP_STATUS);
@ -541,8 +547,42 @@ TABS.pid_tuning.initialize = function (callback) {
}
};
TABS.pid_tuning.cleanup = function (callback) {
if (callback) {
callback();
TABS.pid_tuning.getRecieverData = function () {
MSP.send_message(MSP_codes.MSP_RC, false, false);
};
TABS.pid_tuning.initRatesPreview = function () {
this.keepRendering = true;
this.model = new Model($('.rates_preview'), $('.rates_preview canvas'));
var scale = d3.scale.linear().domain([900, 2100]);
this.rollScale = scale.range([Math.PI * 2, -Math.PI * 2]);
this.pitchScale = scale.range([Math.PI * 2, -Math.PI * 2]);
this.yawScale = scale.range([Math.PI * 2, -Math.PI * 2]);
$(window).on('resize', $.proxy(this.model.resize, this.model));
};
TABS.pid_tuning.renderModel = function () {
if (this.keepRendering) { requestAnimationFrame(this.renderModel.bind(this)); }
if (!this.clock) { this.clock = new THREE.Clock(); }
if (RC.channels[0] && RC.channels[1] && RC.channels[2]) {
var delta = this.clock.getDelta(),
roll = delta * this.rollScale(RC.channels[0]),
pitch = delta * this.pitchScale(RC.channels[1]),
yaw = delta * this.yawScale(RC.channels[2]);
this.model.rotateBy(pitch, yaw, roll);
}
};
TABS.pid_tuning.cleanup = function (callback) {
$(window).off('resize', $.proxy(this.model.resize, this.model));
this.keepRendering = false;
if (callback) callback();
};

View File

@ -43,8 +43,9 @@ TABS.setup.initialize = function (callback) {
GUI.log(chrome.i18n.getMessage('initialSetupBackupAndRestoreApiVersion', [CONFIG.apiVersion, CONFIGURATOR.backupRestoreMinApiVersionAccepted]));
}
// initialize 3D
self.initialize3D();
// initialize 3D Model
self.initModel();
// set roll in interactive block
$('span.roll').text(chrome.i18n.getMessage('initialSetupAttitude', [0]));
@ -191,7 +192,8 @@ TABS.setup.initialize = function (callback) {
roll_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[0]]));
pitch_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[1]]));
heading_e.text(chrome.i18n.getMessage('initialSetupAttitude', [SENSOR_DATA.kinematics[2]]));
self.render3D();
self.renderModel();
self.updateInstruments();
});
}
@ -215,104 +217,22 @@ TABS.setup.initializeInstruments = function() {
};
};
TABS.setup.initialize3D = function (compatibility) {
var self = this,
loader, canvas, wrapper, renderer, camera, scene, light, light2, modelWrapper, model, model_file,
useWebGlRenderer = false;
TABS.setup.initModel = function () {
this.model = new Model($('.model-and-info #canvas_wrapper'), $('.model-and-info #canvas'));
canvas = $('.model-and-info #canvas');
wrapper = $('.model-and-info #canvas_wrapper');
$(window).on('resize', $.proxy(this.model.resize, this.model));
};
// webgl capability detector
// it would seem the webgl "enabling" through advanced settings will be ignored in the future
// and webgl will be supported if gpu supports it by default (canary 40.0.2175.0), keep an eye on this one
var detector_canvas = document.createElement('canvas');
if (window.WebGLRenderingContext && (detector_canvas.getContext('webgl') || detector_canvas.getContext('experimental-webgl'))) {
renderer = new THREE.WebGLRenderer({canvas: canvas.get(0), alpha: true, antialias: true});
useWebGlRenderer = true;
} else {
renderer = new THREE.CanvasRenderer({canvas: canvas.get(0), alpha: true});
}
// initialize render size for current canvas size
renderer.setSize(wrapper.width()*2, wrapper.height()*2);
TABS.setup.renderModel = function () {
var x = (SENSOR_DATA.kinematics[1] * -1.0) * 0.017453292519943295,
y = ((SENSOR_DATA.kinematics[2] * -1.0) - this.yaw_fix) * 0.017453292519943295,
z = (SENSOR_DATA.kinematics[0] * -1.0) * 0.017453292519943295;
// // modelWrapper adds an extra axis of rotation to avoid gimbal lock with the euler angles
modelWrapper = new THREE.Object3D()
//
// load the model including materials
if (useWebGlRenderer) {
model_file = mixerList[CONFIG.multiType - 1].model;
} else {
model_file = 'fallback'
}
// Temporary workaround for 'custom' model until akfreak's custom model is merged.
var useLegacyCustomModel = false;
if (model_file == 'custom') {
model_file = 'fallback';
useLegacyCustomModel = true;
}
// setup scene
scene = new THREE.Scene();
loader = new THREE.JSONLoader();
loader.load('./resources/models/' + model_file + '.json', function (geometry, materials) {
var modelMaterial = new THREE.MeshFaceMaterial(materials);
model = new THREE.Mesh(geometry, modelMaterial);
model.scale.set(15, 15, 15);
modelWrapper.add(model);
scene.add(modelWrapper);
});
// stationary camera
camera = new THREE.PerspectiveCamera(50, wrapper.width() / wrapper.height(), 1, 10000);
// some light
light = new THREE.AmbientLight(0x404040);
light2 = new THREE.DirectionalLight(new THREE.Color(1, 1, 1), 1.5);
light2.position.set(0, 1, 0);
// move camera away from the model
camera.position.z = 125;
// add camera, model, light to the foreground scene
scene.add(light);
scene.add(light2);
scene.add(camera);
scene.add(modelWrapper);
this.render3D = function () {
if (!model) {
return;
}
// compute the changes
model.rotation.x = (SENSOR_DATA.kinematics[1] * -1.0) * 0.017453292519943295;
modelWrapper.rotation.y = ((SENSOR_DATA.kinematics[2] * -1.0) - self.yaw_fix) * 0.017453292519943295;
model.rotation.z = (SENSOR_DATA.kinematics[0] * -1.0) * 0.017453292519943295;
// draw
renderer.render(scene, camera);
};
// handle canvas resize
this.resize3D = function () {
renderer.setSize(wrapper.width()*2, wrapper.height()*2);
camera.aspect = wrapper.width() / wrapper.height();
camera.updateProjectionMatrix();
self.render3D();
};
$(window).on('resize', this.resize3D);
this.model.rotateTo(x, y, z);
};
TABS.setup.cleanup = function (callback) {
$(window).off('resize', this.resize3D);
$(window).off('resize', $.proxy(this.model.resize, this.model));
if (callback) callback();
};