mirror of
				https://scm.univ-tours.fr/22107988t/rappaurio-sae501_502.git
				synced 2025-11-04 01:55:21 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			468 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			468 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						||
 * @name CeL function for physical quantities
 | 
						||
 * @fileoverview 本檔案包含了 value of a quantity/物理量/度量衡(長度、時間、重量等)標準/國際單位/國際單位制詞頭
 | 
						||
 *               單位換算用的 functions。<br />
 | 
						||
 * 
 | 
						||
 * TODO:<br />
 | 
						||
 * https://en.wikipedia.org/wiki/Category:Units_of_amount
 | 
						||
 * https://en.wikipedia.org/wiki/Significance_arithmetic
 | 
						||
 * 
 | 
						||
 * http://thdl.ntu.edu.tw/tools/
 | 
						||
 * http://thdl.ntu.edu.tw/weight_measure/transformation8.php<br />
 | 
						||
 * 中國度量衡/中國古代貨幣 http://thdl.ntu.edu.tw/suzhou/
 | 
						||
 * 
 | 
						||
 * @since
 | 
						||
 * @example <code>
 | 
						||
 * CeL.run('data.quantity',function(){
 | 
						||
 * 	// ..
 | 
						||
 * });
 | 
						||
 * </code>
 | 
						||
 * 
 | 
						||
 * @see http://unit.0123456789.tw/
 | 
						||
 * 
 | 
						||
 * @example <code>
 | 
						||
 | 
						||
 | 
						||
cm^2, cm2 視作 cm²
 | 
						||
 | 
						||
CeL.assert([(new CeL.quantity('5.4cm')).toString(),'5.4 cm'],'');
 | 
						||
CeL.assert([(new CeL.quantity('4cm^2')).toString(),'4 cm²'],'');
 | 
						||
CeL.assert([(new CeL.quantity('4cm2')).toString(),'4 cm²'],'');
 | 
						||
CeL.assert([(new CeL.quantity('四千五百六十七公尺')).toString(),'4567 m'],'');
 | 
						||
CeL.assert([(new CeL.quantity('54公尺')).toString(),'54 m'],'');
 | 
						||
CeL.assert([(new CeL.quantity('5公尺')).multiple(4).toString(),'20 m'],'');
 | 
						||
CeL.assert([(new CeL.quantity('54cm')).multiple('8.5公尺').toString(),'4.59 m²'],'');
 | 
						||
CeL.assert([(new CeL.quantity('54cm')).multiple('8.5公尺').toString('繁體中文'),'4.59平方公尺'],'');
 | 
						||
CeL.assert([(new CeL.quantity('500平方公尺')).convert_to('a').toString(),'5 a'],'');
 | 
						||
CeL.assert([(new CeL.quantity('500平方公尺')).convert_to('ha').toString(),'0.05 ha'],'');
 | 
						||
CeL.assert([(new CeL.quantity('500平方公尺')).convert_to('a').toString('繁體中文'),'5 公畝'],'');
 | 
						||
 | 
						||
 | 
						||
°C
 | 
						||
廿分鐘
 | 
						||
三磅
 | 
						||
 | 
						||
 
 | 
						||
 * </code>
 | 
						||
 */
 | 
						||
 | 
						||
'use strict';
 | 
						||
 | 
						||
// --------------------------------------------------------------------------------------------
 | 
						||
 | 
						||
typeof CeL === 'function' && CeL.run({
 | 
						||
	// module name
 | 
						||
	name : 'data.quantity',
 | 
						||
 | 
						||
	// |data.native.
 | 
						||
	require : 'data.|data.numeral.|data.Convert_Pairs.',
 | 
						||
 | 
						||
	// 設定不匯出的子函式。
 | 
						||
	// no_extend : '*',
 | 
						||
 | 
						||
	// 為了方便格式化程式碼,因此將 module 函式主體另外抽出。
 | 
						||
	code : module_code
 | 
						||
});
 | 
						||
 | 
						||
function module_code(library_namespace) {
 | 
						||
 | 
						||
	/**
 | 
						||
	 * 
 | 
						||
	 * @param {RegExp}pattern
 | 
						||
	 * @param {String}flag
 | 
						||
	 * 
 | 
						||
	 * @returns {RegExp}
 | 
						||
	 */
 | 
						||
	function convert_pattern(pattern, flag) {
 | 
						||
		return new RegExp(pattern.source.replace(/N/, PATTERN_NUMBER_SOURCE)
 | 
						||
				.replace(/S/, SUPERSCRIPT_NUMBER), flag);
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 定義基本常數。
 | 
						||
 | 
						||
	var SUPERSCRIPT_NUMBER =
 | 
						||
	// @ data.native
 | 
						||
	// Number.prototype.to_super.digits,
 | 
						||
	'⁰¹²³⁴⁵⁶⁷⁸⁹',
 | 
						||
 | 
						||
	//
 | 
						||
	PATTERN_NUMBER_SOURCE =
 | 
						||
	// /[+\-]?(?:\d+(?:\.\d+)?|\.\d+)(?:E[+\-]?\d+)?/i
 | 
						||
	/[+\-]?(?:\d+(?:\.\d+)?|\.\d+)/i.source,
 | 
						||
 | 
						||
	// [ , number, units, 分母 units ]
 | 
						||
	PATTERN_QUANTITY = convert_pattern(
 | 
						||
	// /^\s*(N)\s*([^\/\d][^\/]*)(?:\/(.+))?$/
 | 
						||
	/^\s*(N)\s*([^\d\s\^].*)$/, 'i'),
 | 
						||
 | 
						||
	// kg/(m·s2) and kg·m−1·s−2 are acceptable, but kg/m/s2 is ambiguous
 | 
						||
	// and unacceptable.
 | 
						||
	PATTERN_UNIT = convert_pattern(
 | 
						||
	// [ , unit symbol, exponent, exponent, superscript exponent ]
 | 
						||
	/([^\d\s\^⋅·×()]+)([+\-]?\d|\^\(?([+\-]?\d)\)?|([⁺⁻]?[S]))?/
 | 
						||
	//
 | 
						||
	, 'gi'),
 | 
						||
 | 
						||
	// exponent_to_prefix.pair_Map.get(6) === 'M'
 | 
						||
	exponent_to_prefix = new library_namespace.data.Convert_Pairs(
 | 
						||
	// https://en.wikipedia.org/wiki/Metric_prefix
 | 
						||
	'30=Q,27=R,24=Y,21=Z,18=E,15=P,12=T,9=G,6=M,3=k,2=h,1=da,'
 | 
						||
	//
 | 
						||
	+ '-1=d,-2=c,-3=m,-6=µ,-9=n,-12=p,-15=f,-18=a,-21=z,-24=y,-27=r,-30=q', {
 | 
						||
		key_is_number : true
 | 
						||
	}),
 | 
						||
	// 數量級
 | 
						||
	exponent_of = exponent_to_prefix.clone().reverse({
 | 
						||
		key_is_number : true
 | 
						||
	}),
 | 
						||
	//
 | 
						||
	prefix_pattern = new RegExp('^\\s*(' + exponent_of.pattern({
 | 
						||
		get_normal_keys : true
 | 
						||
	}).join('|') + ')([^\\s]+)$'),
 | 
						||
 | 
						||
	// prefix name. prefix_name.pair_Map.get('M') === 'mega'.
 | 
						||
	prefix_name = new library_namespace.data.Convert_Pairs(
 | 
						||
	//
 | 
						||
	'Q=quetta,R=ronna,Y=yotta,Z=zetta,E=exa,P=peta,'
 | 
						||
	//
 | 
						||
	+ 'T=tera,G=giga,M=mega,k=kilo,h=hecto,da=deca,'
 | 
						||
	//
 | 
						||
	+ 'd=deci,c=centi,m=milli,µ=micro,n=nano,p=pico,'
 | 
						||
	//
 | 
						||
	+ 'f=femto,a=atto,z=zepto,y=yocto,r=ronto,q=quecto');
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 初始調整並規範基本常數。
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// private 工具函數。
 | 
						||
 | 
						||
	// Prefixes may not be used in combination.
 | 
						||
	function parse_prefix(unit) {
 | 
						||
		library_namespace.debug('parse_prefix(' + unit + ')', 3);
 | 
						||
		var matched = String(unit).match(prefix_pattern);
 | 
						||
		// [ unit symbol, prefix exponent ]
 | 
						||
		return matched ? [ matched[2], exponent_of.pair_Map.get(matched[1]) ]
 | 
						||
				: [ unit, 0 ];
 | 
						||
	}
 | 
						||
 | 
						||
	// 度量衡單位 : 數量級 (prefix exponent / integer powers of 10)
 | 
						||
	// 度量衡單位 : [ 數量級, 單位數量級 (unit power) ]
 | 
						||
	// e.g., m : 0 → m
 | 
						||
	// e.g., m : 3 → km
 | 
						||
	// e.g., m : [ , 2 ] → m^2
 | 
						||
	// e.g., m : [ 3 , 2 ] → km^2
 | 
						||
	function multiple_unit(quantity, unit_symbol, prefix_exponent, unit_power) {
 | 
						||
 | 
						||
		if (unit_power === '' || isNaN(unit_power))
 | 
						||
			unit_power = 1;
 | 
						||
 | 
						||
		var units = quantity.units;
 | 
						||
		if (!units)
 | 
						||
			quantity.units = units = Object.create(null);
 | 
						||
 | 
						||
		if (unit_symbol in units) {
 | 
						||
			var array = units[unit_symbol];
 | 
						||
			if (!Array.isArray(array))
 | 
						||
				// assert: !isNaN(units[unit_symbol])
 | 
						||
				units[unit_symbol] = array = [ array, 1 ];
 | 
						||
			// 若是 Array,則使用原 Array。
 | 
						||
 | 
						||
			if (prefix_exponent !== array[0]) {
 | 
						||
				// km^3 * cm^-2
 | 
						||
				// = 10^(3*3 - (-2)*2) m^(3 + -2)
 | 
						||
				// = 10^13 m
 | 
						||
				// = 10 Tm
 | 
						||
				prefix_exponent = array[0] * array[1] + unit_power
 | 
						||
						* (prefix_exponent || 0);
 | 
						||
				array[1] += unit_power;
 | 
						||
				// The "non-three" prefixes (hecto-, deca-, deci-, and
 | 
						||
				// centi-) are however more commonly used for everyday
 | 
						||
				// purposes than in science.
 | 
						||
				if (array[0] = prefix_exponent / 3 / array[1] | 0)
 | 
						||
					prefix_exponent -= 3 * array[0] * array[1];
 | 
						||
				// assert: Math.abs(prefix_exponent) = 0, 1, 2
 | 
						||
 | 
						||
				// 微調:盡量取到不會 overflow 或 underflow 的 prefix。
 | 
						||
				if (prefix_exponent < 0) {
 | 
						||
					if (Math.abs(quantity.amount) < 1)
 | 
						||
						array[0]--, prefix_exponent += 3;
 | 
						||
 | 
						||
				} else if (prefix_exponent > 0
 | 
						||
						&& Math.abs(quantity.amount) > 1e6
 | 
						||
						&& quantity.amount % 10 === 0)
 | 
						||
					array[0]++, prefix_exponent -= 3;
 | 
						||
 | 
						||
				// 把剩餘乘進 quantity.amount。
 | 
						||
				if (prefix_exponent)
 | 
						||
					quantity.amount *= Math.pow(10, prefix_exponent);
 | 
						||
				array[0] *= 3;
 | 
						||
 | 
						||
			} else
 | 
						||
				// e.g., km * km = km^2
 | 
						||
				array[1] += unit_power;
 | 
						||
 | 
						||
			if (!array[1])
 | 
						||
				delete units[unit_symbol];
 | 
						||
			else if (array[1] === 1)
 | 
						||
				units[unit_symbol] = array[0];
 | 
						||
 | 
						||
		} else
 | 
						||
			units[unit_symbol] = unit_power !== 1 ? [ prefix_exponent,
 | 
						||
					unit_power ] : prefix_exponent;
 | 
						||
 | 
						||
		return units;
 | 
						||
	}
 | 
						||
 | 
						||
	function parse_unit(unit, quantity, is_division) {
 | 
						||
		library_namespace.debug('parse_unit(' + unit + ')', 2);
 | 
						||
		if (library_namespace.is_Object(unit))
 | 
						||
			return unit;
 | 
						||
 | 
						||
		var matched = String(unit).match(/([^\/]*)\/([^\/]+)/);
 | 
						||
		if (matched) {
 | 
						||
			return Object.assign(parse_unit(matched[1], quantity), parse_unit(
 | 
						||
					matched[2], quantity, true));
 | 
						||
		}
 | 
						||
 | 
						||
		is_division = is_division ? -1 : 1;
 | 
						||
		// var units = Object.create(null);
 | 
						||
		// reset pattern
 | 
						||
		PATTERN_UNIT.lastIndex = 0;
 | 
						||
		while (matched = PATTERN_UNIT.exec(unit)) {
 | 
						||
			library_namespace.debug('parse_unit: [' + matched + ']', 3);
 | 
						||
			if (matched[4])
 | 
						||
				matched[2] = matched[4].length > 1 ? (matched[4].charAt(0) === '⁻' ? 1
 | 
						||
						: -1)
 | 
						||
						* SUPERSCRIPT_NUMBER.indexOf(matched[4].charAt(1))
 | 
						||
						: SUPERSCRIPT_NUMBER.indexOf(matched[4]);
 | 
						||
			// return [ unit symbol, prefix exponent ]
 | 
						||
			matched[1] = parse_prefix(normalize_name(matched[1]));
 | 
						||
			multiple_unit(quantity, matched[1][0], matched[1][1], matched[3]
 | 
						||
					|| matched[2]);
 | 
						||
		}
 | 
						||
 | 
						||
		return quantity.units;
 | 
						||
	}
 | 
						||
 | 
						||
	function unit_to_String(unit_symbol, prefix_exponent, unit_power) {
 | 
						||
		if (Array.isArray(prefix_exponent)
 | 
						||
		// 處理 [ 數量級, 單位數量級 (unit power) ]
 | 
						||
		&& unit_power === undefined) {
 | 
						||
			unit_power = prefix_exponent[1];
 | 
						||
			prefix_exponent = prefix_exponent[0];
 | 
						||
		}
 | 
						||
 | 
						||
		var unit = (prefix_exponent ?
 | 
						||
		//
 | 
						||
		exponent_to_prefix.pair_Map.get(+prefix_exponent)
 | 
						||
				|| ('10^' + prefix_exponent) : '')
 | 
						||
				+ unit_symbol;
 | 
						||
		if (unit_power) {
 | 
						||
			if (unit_power < 0)
 | 
						||
				unit += '⁻', unit_power = -unit_power;
 | 
						||
			unit += SUPERSCRIPT_NUMBER[unit_power];
 | 
						||
		}
 | 
						||
 | 
						||
		return unit;
 | 
						||
	}
 | 
						||
 | 
						||
	var unit_name_all, unit_name = {
 | 
						||
 | 
						||
		// 繁體中文
 | 
						||
		'cmn-Hant-TW' : {
 | 
						||
			公里 : 'km',
 | 
						||
			公尺 : 'm',
 | 
						||
			公分 : 'cm',
 | 
						||
			厘米 : 'cm',
 | 
						||
			公釐 : 'mm',
 | 
						||
			毫米 : 'mm'
 | 
						||
		},
 | 
						||
 | 
						||
		// https://zh.wikipedia.org/wiki/%E5%B8%82%E5%88%B6
 | 
						||
		'市制' : {
 | 
						||
			市尺 : '1/3m'
 | 
						||
		},
 | 
						||
 | 
						||
		// https://zh.wikipedia.org/wiki/%E5%8F%B0%E5%88%B6
 | 
						||
		'臺制' : {
 | 
						||
			臺尺 : '10/33m'
 | 
						||
		},
 | 
						||
 | 
						||
		// 英制單位
 | 
						||
		'imperial' : {},
 | 
						||
 | 
						||
		// default, 公制
 | 
						||
		'' : {
 | 
						||
			'㎝' : 'cm',
 | 
						||
			'㎠' : 'cm^2',
 | 
						||
			'㎤' : 'cm^3',
 | 
						||
 | 
						||
			'㎜' : 'mm',
 | 
						||
			'㎟' : 'mm^2',
 | 
						||
			'㎣' : 'mm^3'
 | 
						||
		}
 | 
						||
	};
 | 
						||
 | 
						||
	// normalize unit name
 | 
						||
	function normalize_name(unit, locale) {
 | 
						||
		var matched = unit.match(/^([平立])方(.+)$/),
 | 
						||
		//
 | 
						||
		prefix_exponent, unit_power;
 | 
						||
 | 
						||
		if (matched) {
 | 
						||
			unit_power = matched[1] === '平' ? 2 : 3;
 | 
						||
			unit = matched[2];
 | 
						||
		}
 | 
						||
 | 
						||
		if (!unit_name_all) {
 | 
						||
			// initialization.
 | 
						||
			unit_name_all = Object.create(null);
 | 
						||
			for ( var l in unit_name)
 | 
						||
				Object.assign(unit_name_all, unit_name[l]);
 | 
						||
		}
 | 
						||
 | 
						||
		// TODO
 | 
						||
		if (false)
 | 
						||
			if (unit in unit_name_all)
 | 
						||
				return [ unit_name_all[unit],
 | 
						||
				//
 | 
						||
				prefix_exponent, unit_power ];
 | 
						||
 | 
						||
		if (unit in unit_name_all)
 | 
						||
			return unit_name_all[unit];
 | 
						||
 | 
						||
		return unit;
 | 
						||
	}
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 定義基本常數。
 | 
						||
 | 
						||
	function Quantity(amount, units, options) {
 | 
						||
		if (!units && isNaN(amount)) {
 | 
						||
			units = String(amount)
 | 
						||
			// 先期處理。
 | 
						||
			.replace(/^\s*[〇一二三四五六七八九十百千萬億兆]+/,
 | 
						||
			//
 | 
						||
			library_namespace.from_Chinese_numeral);
 | 
						||
 | 
						||
			// e.g., new Quantity('2m', null, { type : 'length' })
 | 
						||
			if (units = units.match(PATTERN_QUANTITY)) {
 | 
						||
				amount = units[1];
 | 
						||
				units = units[2];
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		// 數值/量值的大小 / number / amount/count of base unit /
 | 
						||
		// numerical value / factor
 | 
						||
		// {Number} or [{Number}積]
 | 
						||
		if (typeof amount === 'string' && !isNaN(amount))
 | 
						||
			amount = +amount;
 | 
						||
		this.amount = amount;
 | 
						||
 | 
						||
		// unit symbol
 | 
						||
		this.units = parse_unit(units, this);
 | 
						||
 | 
						||
		if (options) {
 | 
						||
			// 度量衡單位分類 type. e.g., 'length' (長度)
 | 
						||
			if (options.type)
 | 
						||
				this.type = options.type;
 | 
						||
 | 
						||
			// 有效數字/the appropriate number of significant
 | 
						||
			// figures
 | 
						||
			if ('significant_figures' in options)
 | 
						||
				this.significant_figures
 | 
						||
				//
 | 
						||
				= options.significant_figures | 0;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 數值單位換算。
 | 
						||
	function convert_to(units) {
 | 
						||
		;
 | 
						||
	}
 | 
						||
 | 
						||
	function multiple(quantity) {
 | 
						||
		if (quantity)
 | 
						||
			if (isNaN(quantity)) {
 | 
						||
				if (quantity.constructor !== Quantity)
 | 
						||
					quantity = new Quantity(quantity);
 | 
						||
				this.amount *= quantity.amount;
 | 
						||
				var units = quantity.units;
 | 
						||
				for ( var unit in units)
 | 
						||
					multiple_unit(this, unit, units[unit]);
 | 
						||
			} else
 | 
						||
				this.amount *= quantity;
 | 
						||
		else
 | 
						||
			this.amount = 0;
 | 
						||
		return this;
 | 
						||
	}
 | 
						||
 | 
						||
	// https://en.wikipedia.org/wiki/International_System_of_Units#Unit_symbols_and_the_values_of_quantities
 | 
						||
	// The value of a quantity is written as a number followed
 | 
						||
	// by a space
 | 
						||
	// (representing a multiplication sign) and a unit symbol
 | 
						||
	function to_String(locale) {
 | 
						||
		var result = [], units = this.units;
 | 
						||
		for ( var unit in units)
 | 
						||
			result.push(unit_to_String(unit, units[unit]));
 | 
						||
		// intervening space ' ', dot operator '⋅'.
 | 
						||
		return this.amount + ' ' + result.join('⋅');
 | 
						||
	}
 | 
						||
 | 
						||
	// http://en.wikipedia.org/wiki/SI_base_unit
 | 
						||
	// http://en.wikipedia.org/wiki/International_System_of_Units#Units_and_prefixes
 | 
						||
	// http://en.wikipedia.org/wiki/List_of_thermodynamic_properties
 | 
						||
	// Quantity.type_of('m')==='length';
 | 
						||
	function type_of(unit) {
 | 
						||
		;
 | 
						||
	}
 | 
						||
 | 
						||
	function name_of(unit, locale) {
 | 
						||
		;
 | 
						||
	}
 | 
						||
 | 
						||
	// Quantity.multiple(Quantity, Quantity);
 | 
						||
 | 
						||
	function parse_factor() {
 | 
						||
		;
 | 
						||
	}
 | 
						||
 | 
						||
	// 同一個物理量的兩種不同的單位之間,是靠著轉換因子(conversion
 | 
						||
	// factor)從一個單位轉換到另一個單位。例如,1 in = 2.54
 | 
						||
	// cm,注意到在這裡「2.54 cm/in」是轉換因子,不具有因次,其數值等於1。
 | 
						||
	parse_factor('min=60s,hr=60min,a=100m2,Newton=kg*m/s2');
 | 
						||
 | 
						||
	// 常數/constants/factors
 | 
						||
	var constants = {
 | 
						||
		PI : Math.PI,
 | 
						||
		c : 299792458
 | 
						||
	};
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 應用功能。
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// 網頁應用功能。
 | 
						||
 | 
						||
	// ---------------------------------------------------------------------//
 | 
						||
	// export.
 | 
						||
 | 
						||
	library_namespace.set_method(Quantity, {
 | 
						||
		type_of : type_of,
 | 
						||
		name_of : name_of,
 | 
						||
		parse_factor : parse_factor,
 | 
						||
		constants : constants
 | 
						||
	});
 | 
						||
 | 
						||
	library_namespace.set_method(Quantity.prototype, {
 | 
						||
		convert_to : convert_to,
 | 
						||
		multiple : multiple
 | 
						||
	});
 | 
						||
	Quantity.prototype.toString = to_String;
 | 
						||
 | 
						||
	return (Quantity// JSDT:_module_
 | 
						||
	);
 | 
						||
}
 |