You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
506 lines
19 KiB
JavaScript
506 lines
19 KiB
JavaScript
function update() {
|
|
|
|
var size = parseNum($('#INPUT_SIZE').val());
|
|
var unit_select = $('#unit_select input[type="radio"]:checked').val();
|
|
var hres1 = parseInt(parseNum($('#INPUT_HRES').val()));
|
|
var vres1 = parseInt(parseNum($('#INPUT_VRES').val()));
|
|
var ar_options = {
|
|
'p_max': 4,
|
|
'p_min': 2,
|
|
}
|
|
var ar1 = long_division(hres1, vres1, ar_options);
|
|
|
|
var diag = parseFloat(size);
|
|
var width = size * Math.sin(Math.atan(hres1 / vres1));
|
|
var height = size * Math.cos(Math.atan(hres1 / vres1));
|
|
var area = width * height;
|
|
var px_density = hres1 / width;
|
|
var total_px = hres1 * vres1;
|
|
|
|
var hres2 = parseNum($('#INPUT_HRES2').val());
|
|
var vres2 = parseNum($('#INPUT_VRES2').val());
|
|
var ar2 = hres2 / vres2;
|
|
var d_match = Math.sqrt((height * height) * (1 + (ar2 * ar2)));
|
|
var opt_res = parseInt(vres1 * ar2) + ' × ' + vres1;
|
|
|
|
var hres_den = parseInt(parseNum($('#INPUT_HRES_DENSITY').val()));
|
|
var vres_den = parseInt(parseNum($('#INPUT_VRES_DENSITY').val()));
|
|
var ar_den = long_division(hres_den, vres_den, ar_options);
|
|
|
|
var width2 = width * (hres_den / hres1);
|
|
var height2 = height * (vres_den / vres1);
|
|
var size2 = Math.sqrt((width2 * width2) + (height2 * height2));
|
|
|
|
/* Conversion Codes:
|
|
1: Secondary units have normal conversion factor
|
|
2: Secondary units have squared conversion factor
|
|
3: Secondary units have reciprocal conversion factor
|
|
*/
|
|
display(new UNIT(unit_select),
|
|
[
|
|
['RESULT_DIAG', 1, diag.toFixed(3) , (isPositive(size)) ],
|
|
['RESULT_WIDTH', 1, width.toFixed(3) , (isPositive([size, hres1, vres1])) ],
|
|
['RESULT_HEIGHT', 1, height.toFixed(3) , (isPositive([size, hres1, vres1])) ],
|
|
['RESULT_AREA', 2, area.toFixed(3) , (isPositive([size, hres1, vres1])) ],
|
|
['RESULT_PX_DENSITY', 3, px_density.toFixed(3), (isPositive([size, hres1, vres1])) ],
|
|
['RESULT_D_MATCH', 1, d_match.toFixed(3) , (isPositive([size, hres1, vres1, hres2, vres2])) ],
|
|
['RESULT_DENSITY_SIZE', 1, size2.toFixed(3) , (isPositive([size, hres1, vres1, hres_den, vres_den])) ],
|
|
]
|
|
);
|
|
|
|
|
|
if (isNum([size, hres1, vres1, hres2, vres2]) && isPositive([size, hres1, vres1, hres2, vres2])) {
|
|
$('#RESULT_OPT_RES').html(opt_res);
|
|
if (hres2 == '21' && vres2 == '9') {
|
|
$('#21_9_warning').css('display', 'table-row');
|
|
}
|
|
else {
|
|
$('#21_9_warning').css('display', 'none');
|
|
}
|
|
}
|
|
else
|
|
{ $('#RESULT_OPT_RES').html(''); }
|
|
|
|
|
|
//if (hres1 != '' && vres1 != '' && hres1 != 0 && vres1 != 0 && isNaN(hres1) == false && isNaN(vres1) == false) {
|
|
if (isNum([hres1, vres1]) && isPositive([hres1, vres1])) {
|
|
// $('#RESULT_RATIO').html(commas(ar1.toFixed(3)) + ' (' + parseInt(hres1 / GCD(hres1, vres1)) + '<span style="vertical-align:baseline; position:relative; top:-0.05em;">:</span>' + parseInt(vres1 / GCD(hres1, vres1)) + ')');
|
|
$('#RESULT_RATIO').html(ar1 + '∶1 (' + commas(parseInt(hres1 / GCD(hres1, vres1))) + '∶' + commas(parseInt(vres1 / GCD(hres1, vres1))) + ')');
|
|
$('#RESULT_TOTAL_PX').html(commas(total_px) + ' (' + prefixGen(total_px, 2)['num'] + ' ' + prefixGen(total_px, 2)['prefix'] + 'px)');
|
|
}
|
|
else {
|
|
$('#RESULT_RATIO').html('');
|
|
$('#RESULT_TOTAL_PX').html('');
|
|
}
|
|
|
|
|
|
|
|
//!= '' && vres1 != '' && hres1 != 0 && vres1 != 0 && isNaN(hres1) == false && isNaN(vres1) == false
|
|
//DEBUG('isInt', isInt([hres1, vres1, hres_den, vres_den]), 'isPositive', isPositive([hres1, vres1, hres_den, vres_den]));
|
|
//DEBUG('hres1', hres1, isInt(hres1), 'vres1', vres1, isInt(vres1), 'hres_den', hres_den, isInt(hres_den), 'vres_den', vres_den, isInt(vres_den));
|
|
if (isInt([hres1, vres1, hres_den, vres_den]) && isPositive([hres1, vres1, hres_den, vres_den])) {
|
|
// $('#RESULT_DENSITY_RATIO').html(commas(ar_den.toFixed(3)) + ' (' + parseInt(hres_den / GCD(hres_den, vres_den)) + '<span style="vertical-align:baseline; position:relative; top:-0.05em;">:</span>' + parseInt(vres_den / GCD(hres_den, vres_den)) + ')');
|
|
$('#RESULT_DENSITY_RATIO').html(ar_den + '∶1 (' + commas(parseInt(hres_den / GCD(hres_den, vres_den))) + '∶' + commas(parseInt(vres_den / GCD(hres_den, vres_den))) + ')');
|
|
}
|
|
else {
|
|
$('#RESULT_DENSITY_RATIO').html('');
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Returns false if input is a non-integer number or NaN
|
|
function isInt(num) {
|
|
if (Array.isArray(num) == true) {
|
|
for (a = 0; a < num.length; a++) {
|
|
if (Number.isInteger(parseNum(num[a])) == false) {
|
|
//DEBUG('isInt Array False. a:', a, 'num[a]:', num[a], 'Number.isInteger(num[a]):', Number.isInteger(num[a]));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
return Number.isInteger(parseNum(num));
|
|
}
|
|
|
|
|
|
function isFloat(num) {
|
|
if (Array.isArray(num) == true) {
|
|
for (a = 0; a < num.length; a++) {
|
|
if (Number.isInteger(parseNum(num[a])) == true || Number.isNaN(num[a]) == true) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
return !(Number.isInteger(parseNum(num)) || Number.isNaN(parseNum(num)));
|
|
}
|
|
|
|
|
|
// Returns false if input is not a positive number (zero, negative number, or NaN)
|
|
function isPositive(num) {
|
|
if (Array.isArray(num) == true) {
|
|
for (a = 0; a < num.length; a++) {
|
|
if (Number.isNaN(parseNum(num[a])) == true)
|
|
return false;
|
|
else if (num[a] <= 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
if (Number.isNaN(parseNum(num)) == true)
|
|
return false;
|
|
else if (num > 0)
|
|
return true;
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
function isNonNegative(num) {
|
|
if (Array.isArray(num) == true) {
|
|
for (a = 0; a < num.length; a++) {
|
|
if (Number.isNaN(parseNum(num[a])) == true)
|
|
return false;
|
|
else if (num[a] < 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
else {
|
|
if (Number.isNaN(parseNum(num)) == true)
|
|
return false;
|
|
else if (num >= 0)
|
|
return true;
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns false if input is NaN
|
|
function isNum(num) {
|
|
if (Array.isArray(num) == true) {
|
|
for (a = 0; a < num.length; a++) {
|
|
if (Number.isNaN(parseNum(num[a])) == true) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
return !Number.isNaN(parseNum(num));
|
|
}
|
|
}
|
|
|
|
// Converts string to floating point if it has a decimal point, or integer if there is no decimal point. Also strips commas and spaces, and optionally applies absolute value.
|
|
// Cannot handle inputs with negative signs in the wrong position.
|
|
function parseNum(str) {
|
|
if (typeof str === "string") {
|
|
str = str.replace(/[^0-9\. ]/g, ''); // Apply absolute value
|
|
// str = str.replace(/[^0-9\. -]/g, ''); // Allow negative numbers
|
|
|
|
// Return NaN if...
|
|
if (str == '' // input is blank
|
|
|| str.indexOf('.') != str.lastIndexOf('.') // input contains multiple decimal places
|
|
|| str.indexOf('-') != str.lastIndexOf('-') // input contains multiple minus signs
|
|
|| (str.indexOf('-') != -1 && str.indexOf('-') != 0)) { // input contains a minus sign in a position other than the first character
|
|
|
|
return NaN;
|
|
}
|
|
|
|
else {
|
|
if (str.indexOf('.') == -1)
|
|
return parseInt(str);
|
|
else {
|
|
return parseFloat(str);
|
|
}
|
|
}
|
|
}
|
|
else if (Number.isNaN(str))
|
|
return NaN;
|
|
else if (typeof str === "number") {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
function display(units, list) {
|
|
var el;
|
|
for (var x = 0; x < list.length; x++) {
|
|
if (isNaN(list[x][2]) == true || isFinite(list[x][2]) == false || list[x][3] == false) {
|
|
$('#' + list[x][0]).html('');
|
|
}
|
|
else {
|
|
el = $('#' + list[x][0]);
|
|
el.html(commas(list[x][2]));
|
|
if (list[x][1] == 1) {
|
|
el[0].innerHTML += units.sym()[0] + ' (' + commas((list[x][2] * units.conv()).toFixed(3)) + units.sym()[1] + ')';
|
|
}
|
|
else if (list[x][1] == 2) {
|
|
el[0].innerHTML += ' ' + units.abbr()[0] + '<sup>2</sup> (' + commas((list[x][2] * units.conv() * units.conv()).toFixed(3)) + ' ' + units.abbr()[1] + '<sup>2</sup>)';
|
|
}
|
|
else if (list[x][1] == 3) {
|
|
el[0].innerHTML += ' px/' + units.abbr()[0] + ' (' + commas((list[x][2] * (1 / units.conv())).toFixed(3)) + ' px/' + units.abbr()[1] + ')';
|
|
}
|
|
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
function UNIT (mode) {
|
|
this._primary = mode;
|
|
|
|
this.set = function(mode) {
|
|
this._primary = mode;
|
|
}
|
|
|
|
this.constructor = function(mode) {
|
|
this._primary(mode);
|
|
}
|
|
|
|
this.full = function() {
|
|
if (this._primary == 'in') { return ['inches', 'centimeters']; }
|
|
else if (this._primary == 'cm') { return ['centimeters', 'inches']; }
|
|
}
|
|
|
|
this.abbr = function() {
|
|
if (this._primary == 'in') { return ['in', 'cm']; }
|
|
else if (this._primary == 'cm') { return ['cm', 'in']; }
|
|
}
|
|
|
|
this.sym = function() {
|
|
if (this._primary == 'in') { return ['"', ' cm']; }
|
|
else if (this._primary == 'cm') { return [' cm', '"']; }
|
|
}
|
|
|
|
this.conv = function() {
|
|
if (this._primary == 'in') { return 2.54; }
|
|
else if (this._primary == 'cm') { return 1 / 2.54; }
|
|
}
|
|
}
|
|
|
|
|
|
function prefixGen(num, precision) {
|
|
var out_num;
|
|
var out_prefix;
|
|
|
|
var prefixDef = {
|
|
'-8': 'y',
|
|
'-7': 'z',
|
|
'-6': 'a',
|
|
'-5': 'f',
|
|
'-4': 'p',
|
|
'-3': 'n',
|
|
'-2': 'µ',
|
|
'-1': 'm',
|
|
'0': '',
|
|
'1': 'K',
|
|
'2': 'M',
|
|
'3': 'G',
|
|
'4': 'T',
|
|
'5': 'P',
|
|
'6': 'E',
|
|
'7': 'Z',
|
|
'8': 'Y'
|
|
};
|
|
|
|
var magnitude = Math.floor(Math.log(num) / Math.log(1000));
|
|
|
|
if (magnitude >= -8 && magnitude <= 8) {
|
|
out_num = commas(Number(num / Math.pow(1000, magnitude)).toFixed(precision));
|
|
out_prefix = prefixDef[magnitude];
|
|
}
|
|
else {
|
|
out_num = commas(num);
|
|
out_prefix = '';
|
|
}
|
|
|
|
return {
|
|
'num': out_num,
|
|
'prefix': out_prefix
|
|
};
|
|
}
|
|
|
|
|
|
function pxPrefix(num, precision) {
|
|
var x = prefixGen(num, precision);
|
|
return (x['num'] + ' ' + x['prefix'] + 'px');
|
|
}
|
|
|
|
|
|
function GCD(a, b) {
|
|
a = Math.abs(a);
|
|
b = Math.abs(b);
|
|
if (b > a) { var temp = a; a = b; b = temp; }
|
|
while (true) {
|
|
if (b == 0) return a;
|
|
a %= b;
|
|
if (a == 0) return b;
|
|
b %= a;
|
|
}
|
|
}
|
|
|
|
|
|
function commas(input, group, radix) {
|
|
if (!group) { group = ','; }
|
|
if (!radix) { radix = '.'; }
|
|
var parts = input.toString().split(radix);
|
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, group);
|
|
return parts.join(radix);
|
|
}
|
|
|
|
|
|
function long_division(A, B, options) {
|
|
if (isNaN(A / B) || !isFinite(A / B)) { DEBUG('Answer is NaN or Infinity. Function aborted.'); return ''; }
|
|
|
|
var OL_open = '<span style="text-decoration:overline;">'; // Overline markup opening tag
|
|
var OL_close = '</span>'; // Overline markup closing tag. Used in conjunction with OL_open to surround the repeating numbers. May be set to control markup, separate it with parentheses, or simply left blank, etc.
|
|
var p_max = 8; // Maximum number of decimal places
|
|
var p_min = 3; // Minimum number of decimal places
|
|
var Approx = '≈'; // Symbol to be used for "approximately equal to". Can be set blank to turn off this functionality, etc.
|
|
var Radix_Point = '.'; // Character to use for the radix point ("decimal point")
|
|
var Group = ','; // Character to use for digit grouping. Can be set blank to disable digit grouping.
|
|
var Base = 10; // Number base system to use
|
|
var Minus_Sign = '−'; // Character to preceed negative numbers. Default minus sign, can be set blank to output absolute value, or set to hyphen-minus for better compatibility or for other functions to be able to parse as a number correctly.
|
|
var Plus_Sign = ''; // Character to preceed positive numbers. Default blank, but can be set to "+" if explicit signs on both positive and negative numbers is desired.
|
|
var RepeatSinglesFlag = true; // Display single-digit repeating patterns as 1.(33) instead of 1.(3)
|
|
|
|
if (typeof(options) === 'number') { // If 3rd argument is a number rather than a dictionary, use it as the p_max value
|
|
p_max = options;
|
|
}
|
|
else if(options) {
|
|
if ('OL_open' in options) { OL_open = options['OL_open']; }
|
|
if ('OL_close' in options) { OL_close = options['OL_close']; }
|
|
if ('p_max' in options) { p_max = options['p_max']; }
|
|
if ('p_min' in options) { p_min = options['p_min']; }
|
|
if ('radix' in options) { Radix_Point = options['radix']; }
|
|
if ('group' in options) { Group = options['group']; }
|
|
if ('approx' in options) { Approx = options['approx']; }
|
|
if ('minus' in options) { Minus_Sign = options['minus']; }
|
|
if ('plus' in options) { Plus_Sign = options['plus']; }
|
|
if ('base' in options) { Base = options['base']; }
|
|
if ('repeat_singles' in options) { RepeatSinglesFlag = options['repeat_singles']; }
|
|
}
|
|
|
|
var Decimal_Digits = '';
|
|
var Previous_Dividends = {};
|
|
var Prefix = '';
|
|
var Repetend = '';
|
|
var RepeatFlag = false;
|
|
var ApproxFlag = true;
|
|
var Sign = Plus_Sign;
|
|
|
|
// Determine if answer will be negative, then use the absolute value of inputs for the rest of the calculations.
|
|
if ((A < 0) ? !(B < 0) : (B < 0)) { Sign = Minus_Sign; } // If (A is negative) XOR (B is negative) then final result will be negative.
|
|
var Dividend = Math.abs(parseFloat(A));
|
|
var Divisor = Math.abs(parseFloat(B));
|
|
|
|
p_max = parseInt(p_max);
|
|
p_min = parseInt(p_min);
|
|
Base = parseInt(Base);
|
|
if (p_max < 0 || p_min < 0 || p_max < p_min || isNaN(p_max) || isNaN(p_min) || !isFinite(p_max) || !isFinite(p_min)) {
|
|
DEBUG('Invalid p_max and p_min values. Both values must be non-negative numbers, and p_min cannot be greater than p_max. p_max:', p_max, 'p_min', p_min)
|
|
return '';
|
|
}
|
|
if (!isInt(Base)) {
|
|
DEBUG('Invalid Base value. Must be an integer number. Base:', Base);
|
|
return '';
|
|
}
|
|
if (p_max == 0) { // If p_max is 0, then output is integer values only, and the long division section is not necessary.
|
|
var Result = Math.round(Dividend / Divisor).toFixed(0);
|
|
if (Result != (Dividend / Divisor)) { ApproxFlag = true; }
|
|
Result = Sign.concat(Result.toString());
|
|
if (ApproxFlag == true) { Result = Approx.concat(Result); }
|
|
return Result;
|
|
}
|
|
|
|
var Quotient = Dividend / Divisor;
|
|
var Remainder = Dividend % Divisor;
|
|
var Result = Math.floor(Quotient).toString() + Radix_Point; // Use floor division to determine the front part of the number immediately
|
|
Dividend = Remainder * Base;
|
|
|
|
// Use long division for the decimal places, so that repeating decimals can be detected
|
|
var i = 0;
|
|
while (i < p_max + 2) {
|
|
if (!(Dividend in Previous_Dividends)) {
|
|
Previous_Dividends[Dividend] = i;
|
|
|
|
Quotient = Dividend / Divisor;
|
|
Remainder = Dividend % Divisor;
|
|
Dividend = Remainder * Base;
|
|
|
|
//if (i < p_max) {
|
|
Decimal_Digits += Math.floor(Quotient).toString();
|
|
//}
|
|
|
|
//DEBUG('i:', i, 'Quotient:', Quotient, 'Remainder:', Remainder, 'Dividend:', Dividend, 'Result:', Result, 'Decimal_Digits:', Decimal_Digits);
|
|
}
|
|
else {
|
|
RepeatFlag = true;
|
|
ApproxFlag = false;
|
|
Prefix = Decimal_Digits.substring(0, Previous_Dividends[Dividend]);
|
|
Repetend = Decimal_Digits.substring(Previous_Dividends[Dividend], Decimal_Digits.length);
|
|
|
|
if (Repetend == '0') { // A "repeating" dividend of 0 signals a non-repeating result
|
|
Repetend = '';
|
|
RepeatFlag = false;
|
|
Decimal_Digits = Prefix;
|
|
}
|
|
|
|
//Decimal_Digits = Prefix + Repetend;
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
if (RepeatFlag == false) {
|
|
if (Decimal_Digits.length > p_max) {
|
|
//Decimal_Digits = Decimal_Digits.substr(0, p_max);
|
|
Decimal_Digits = Math.round(parseFloat(Decimal_Digits.substr(0, p_max) + '.' + Decimal_Digits.substr(p_max))).toString();
|
|
if (p_max - Decimal_Digits.length >= 0) {
|
|
Decimal_Digits = '0'.repeat(p_max - Decimal_Digits.length) + Decimal_Digits;
|
|
}
|
|
ApproxFlag = true;
|
|
}
|
|
if ((Decimal_Digits.length < p_min) && (p_min - Decimal_Digits.length >= 0)) {
|
|
Decimal_Digits += '0'.repeat(p_min - Decimal_Digits.length);
|
|
}
|
|
}
|
|
|
|
if (RepeatFlag == true) {
|
|
if (Prefix.length + Repetend.length > p_max) {
|
|
if (Prefix.length > p_max) {
|
|
Prefix = Math.round(parseFloat(Prefix.substr(0, p_max) + '.' + Prefix.substr(p_max))).toString();
|
|
if (p_max - Prefix.length >= 0) {
|
|
Prefix = '0'.repeat(p_max - Prefix.length) + Prefix;
|
|
}
|
|
}
|
|
else {
|
|
Prefix = Prefix + Repetend.substr(0, p_max - Prefix.length);
|
|
}
|
|
Decimal_Digits = Prefix;
|
|
RepeatFlag = false;
|
|
ApproxFlag = true;
|
|
}
|
|
if (Prefix.length + Repetend.length < p_min) {
|
|
if ((p_min - (Prefix.length + Repetend.length)) >= Repetend.length) {
|
|
Repetend += Repetend.repeat(Math.floor((p_min - (Prefix.length + Repetend.length)) / Repetend.length));
|
|
}
|
|
while (Prefix.length + Repetend.length < p_min) {
|
|
Prefix = Prefix.concat(Repetend[0]);
|
|
Repetend = Repetend.slice(1).concat(Repetend[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RepeatFlag == true) {
|
|
if (Repetend.length == 1 && (Prefix.length + Repetend.length < p_max) && RepeatSinglesFlag == true) { Repetend = Repetend.repeat(2); } // Single-digit repetitions will be displayed twice, i.e. 4/3 will result in 1.(33) rather than 1.(3)
|
|
Result += Prefix + OL_open + Repetend + OL_close;
|
|
}
|
|
else {
|
|
Result += Decimal_Digits;
|
|
}
|
|
|
|
if (Result[Result.length - 1] == Radix_Point) { Result = Result.replace(Radix_Point, ''); }
|
|
|
|
|
|
Result = commas(Result, Group, Radix_Point);
|
|
|
|
Result = Sign.concat(Result);
|
|
|
|
if (ApproxFlag == true) {
|
|
Result = Approx.concat(Result);
|
|
}
|
|
|
|
return Result;
|
|
|
|
//return Result * Sign;
|
|
} |