COM Visualizer
by th3uiguy
HTML
<script src="http://knockoutjs.com/downloads/knockout-2.3.0.js"></script>
<h1>COM Visualizer</h1>
<div class="weightPlacement">
<div class="carInput">
<div class="row">
<div class="label">units</div>
<select data-bind="options: ['g','oz'], value: changeWeightUnits"></select>
</div>
<div class="row">
<div class="title">Car body</div>
</div>
<div class="row">
<div class="label">width</div>
<input data-bind="value: car.body.attemptedWidth, valueUpdate: 'afterkeydown', returnKey: function () {}" /> in</div>
<div class="row">
<div class="label">length</div>
<input data-bind="value: car.body.attemptedHeight, valueUpdate: 'afterkeydown', returnKey: function () {}" /> in</div>
<div class="row">
<div class="title">Wheels</div>
</div>
<div class="row">
<div class="label">wheel width</div>
<div class="slider" data-bind="slider: car.attemptedWheelWidth, sliderOptions: {min: 1, max: 7.5, range: 'min', step: 0.1}"></div>
<input data-bind="value: car.wheelWidth, visible: car.wheelWidthEdit, hasFocus: car.wheelWidthEdit, returnKey: function () {}" />
<div class="resultHint" data-bind="visible: !car.wheelWidthEdit(), click: car.wheelWidthEditClick">
<span data-bind="text: car.wheelWidth()"></span> mm
</div>
</div>
<!-- ko foreach: { data: car.wheels, as: 'wheel' } -->
<div class="row">
<div class="title" data-bind="text: wheel.title"></div>
</div>
<!-- ko if: wheel.name == 'DFW' -->
<div class="row">
<div class="label">side</div>
<select data-bind="options: ['left','right'], value: wheel.side"> </select>
</div>
<div class="row">
<div class="label">wheel base</div>
<div class="slider"...
CSS
h1 {
text-align: center;
}
.weightPlacement {
min-width: 610px;
position: relative;
text-align: center;
}
.weightPlacement .carInput {
display: inline-block;
width: 310px;
text-align: left;
vertical-align: top;
}
.weightPlacement .carInput .row {
padding-bottom: 5px;
}
.weightPlacement .carInput .row .title {
font-weight: 700;
padding-top: 5px;
}
.weightPlacement .carInput .row .label {
width: 125px;
display: inline-block;
font-weight: normal;
}
.weightPlacement .carInput .row input, .weightPlacement .carInput .row select {
width: 70px;
}
.weightPlacement .carInput .row .slider {
width: 90px;
display: inline-block;
margin-top: 5px;
margin-right: 10px
}
.weightPlacement .carInput .row .resultHint {
cursor: pointer;
display: inline-block;
}
.weightPlacement .carInput .row .resultHint:hover {
text-decoration: underline;
}
.weightPlacement .car {
display: inline-block;
position: relative;
width: 300px;
}
.weightPlacement .car .carBody {
position: absolute;
border: 2px solid #000;
}
.weightPlacement .car .wheel {
position: absolute;
background-color: #111;
z-index: 102;
}
.weightPlacement .car .wheelContact {
position: absolute;
background-color: #F33;
z-index: 103;
width: 1px;
}
.weightPlacement .car .axle {
position: absolute;
background-color: #999;
z-index: 101;
}
.weightPlacement .car .carBodyCenter {
position: absolute;
border-left: 1px dashed gray;
width: 0px;
}
.weightPlacement .car .carBodyBackAxle {
position: absolute;
border-top: 1px dashed gray;
height: 0px;
}
.weightPlacement .car .carBodyComX {
position: absolute;
border-left: 1px dashed red;
width: 0px;
}
.weightPlacement .car .carBodyComY {
position: absolute;
border-top: 1px dashed red;
height: 0px;
}
.weightPlacement .car .carBodyComLegend {
position: absolute;
padding: 5px;
width: 150px;
...
JavaScript
function round(value, precision) { return +(value.toFixed(precision || 4)); }
function gToOz(value) { return round(value * 0.035274); }
function ozToG(value) { return round(value * 28.3495); }
function mmToIn(value) { return round(value * 0.0393701); }
function inToMM(value) { return round(value * 25.4); }
function screenSettings() {
var self = this;
self.dpi = ko.observable(96);
self.scale = ko.observable(1);
self.inToPx = function (inches) {
return self.dpi() * self.scale() * inches;
};
}
function weightPlacementViewModel() {
var self = this;
self.screenSettings = new screenSettings();
self.weightUnits = ko.observable("oz");
self.changeWeightUnits = ko.computed({
read: self.weightUnits,
write: function (value) {
var convert = value == "g" ? ozToG : gToOz,
length = self.car.wheels().length;
for (var i = 0; i < length; i++) {
var wheel = self.car.wheels()[i];
wheel.force(convert(wheel.force()));
}
self.weightUnits(value);
}
});
self.car = new carViewModel({
screenSettings: self.screenSettings
});
self.reset = function () {
self.weightUnits("oz");
self.car.reset();
};
}
function carViewModel(manifest) {
var self = this;
self.manifest = manifest;
self.width = ko.observable(2.75);
self.height = ko.observable(8);
self.wheelWidth = ko.observable(7.5);
self.wheelWidthEdit = ko.observable(false);
self.wheelWidthEditClick = function () {
self.wheelWidthEdit(true);
};
self.attemptedWheelWidth = ko.computed({
read: self.wheelWidth,
write: function (value) {
if (value && !isNaN(value) && value <= 10 && value > 0) {
self.wheelWidth(parseFloat(value));
}
}
});
self.pxWidth = ko.computed(function () {
return...