public UnitPathStack PathToSIBaseUnits() { if (this.IsStronglyTyped) { //get the corresponding unit in the SI System Type InnerUnitType = Unit.GetDefaultSIUnitTypeOf(this.QuantityType); if (InnerUnitType == null && this.QuantityType == typeof(PolarLength <>)) { InnerUnitType = Unit.GetDefaultSIUnitTypeOf(typeof(Length <>)); } if (InnerUnitType == null) { //some quantities don't have strongly typed si units //like knot unit there are no corresponding velocity unit in SI // we need to replace the knot unit with mixed unit to be able to do the conversion // first we should reach default unit UnitPathStack path = this.PathToDefaultUnit(); //then test the system of the current unit if it was other than Metric.SI // then we must jump to SI otherwise we are already in default SI if (this.UnitSystem == "Metric.SI" && this.UnitExponent == 1) { //because no unit in SI with exponent = 1 don't have direct unit type throw new NotImplementedException("Impossible reach by logic"); } else { // We should cross the system boundary UnitPathItem DefaultPItem; UnitPathItem RefUPI; DefaultPItem = path.Peek(); if (DefaultPItem.Unit.ReferenceUnit != null) { RefUPI = new UnitPathItem { Numerator = DefaultPItem.Unit.ReferenceUnitNumerator, Denominator = DefaultPItem.Unit.ReferenceUnitDenominator, //Shift = DefaultPItem.Unit.ReferenceUnitShift, Unit = DefaultPItem.Unit.ReferenceUnit }; path.Push(RefUPI); } } return(path); } else { Unit SIUnit = (Unit)Activator.CreateInstance(InnerUnitType); SIUnit.UnitExponent = this.UnitExponent; SIUnit.UnitDimension = this.UnitDimension; UnitPathStack up = this.PathToUnit(SIUnit); if (!SIUnit.IsBaseUnit) { if (SIUnit.UnitDimension.IsDimensionless && SIUnit.IsStronglyTyped) { //for dimensionless units like radian, stradian //do nothing. } else { //expand the unit Unit expandedUnit = ExpandMetricUnit((MetricUnit)SIUnit); UnitPathStack expath = expandedUnit.PathToSIBaseUnits(); while (expath.Count > 0) { up.Push(expath.Pop()); } } } return(up); } } UnitPathStack Pathes = new UnitPathStack(); foreach (Unit un in this.SubUnits) { UnitPathStack up = null; up = un.PathToSIBaseUnits(); while (up.Count > 0) { UnitPathItem upi = up.Pop(); if (un.IsInverted) { upi.Invert(); } Pathes.Push(upi); } } return(Pathes); }
/// <summary> /// Gets the path to the unit starting from current unit. /// </summary> /// <param name="unit"></param> /// <returns></returns> public UnitPathStack PathToUnit(Unit unit) { lock (CachedPaths) { #region Caching //because this method can be a lengthy method we try to check for cached pathes first. UnitPathStack cachedPath; if (EnableUnitsCaching) { if (CachedPaths.TryGetValue(UnitToUnitSymbol(this, unit), out cachedPath)) { return((UnitPathStack)cachedPath.Clone()); //<--- Clone //Why CLONE :D ?? because the unit path is a stack and I use Pop all the time // during the application, and there were hidden error that poping from unit path in the // cached store will not get them back again ;) // I MUST return cloned copy of the UnitPath. } } #endregion #region validity of conversion if (this.UnitDimension.IsDimensionless == true && unit.UnitDimension.IsDimensionless == true) { } else { //why I've tested dimensioless in begining?? // because I want special dimensionless quantities like angle and solid angle to be treated // as normal dimensionless values if (this.UnitDimension.Equals(unit.UnitDimension) == false) { throw new UnitsNotDimensionallyEqualException(); } } #endregion //test if one of the units are not strongly typed // because this needs special treatment. ;) if (this.IsStronglyTyped == false || unit.IsStronglyTyped == false) { #region Complex units //the unit is not strongly typed so we need to make conversion to get its conversion // Source unit ==> SI Base Units // target unit ==> SI BaseUnits UnitPathStack SourcePath = this.PathToSIBaseUnits(); UnitPathStack TargetPath = unit.PathToSIBaseUnits(); UnitPathStack Tito = new UnitPathStack(); while (SourcePath.Count > 0) { Tito.Push(SourcePath.Pop()); } //we have to invert the target while (TargetPath.Count > 0) { UnitPathItem upi = TargetPath.Pop(); upi.Invert(); Tito.Push(upi); } //first location in cache look below for the second location. if (EnableUnitsCaching) { CachedPaths.Add(UnitToUnitSymbol(this, unit), (UnitPathStack)Tito.Clone()); } return(Tito); #endregion } // 1- Get Path default unit to current unit. UnitPathStack FromMeToDefaultUnit = this.PathToDefaultUnit(); // 2- Get Path From Default unit to the passed unit. UnitPathStack FromDefaultUnitToTargetUnit = unit.PathFromDefaultUnit(); // 3- check if the two units are in the same unit system // if the units share the same parent don't jump UnitPathStack SystemsPath = null; bool NoBoundaryCross = false; if (this.UnitSystem == unit.UnitSystem) { NoBoundaryCross = true; } else { //test for that units parents are the same string ThisParent = this.UnitSystem.IndexOf('.') > -1 ? this.UnitSystem.Substring(0, this.UnitSystem.IndexOf('.')) : this.UnitSystem; string TargetParent = unit.UnitSystem.IndexOf('.') > -1 ? unit.UnitSystem.Substring(0, unit.UnitSystem.IndexOf('.')) : unit.UnitSystem; if (ThisParent == TargetParent) { NoBoundaryCross = true; } } if (NoBoundaryCross) { //no boundary cross should occur //if the two units starts with Metric then no need to cross boundaries because //they have common references in metric. } else { //then we must go out side the current unit system //all default units are pointing to the SIUnit system this is a must and not option. //get the default unit of target // to cross the boundary // we should know the non SI system that we will cross it // we have two options // 1- FromMeToDefaultUnit if (Me unit is another system (not SI) // in this case we will take the top unit to get its reference // 2- FromDefaultUnitToTargetUnit (where default unit is not SI) // and in this case we will take the last bottom unit of stack and get its reference SystemsPath = new UnitPathStack(); UnitPathItem DefaultPItem; UnitPathItem RefUPI; Unit SourceDefaultUnit = FromMeToDefaultUnit.Peek().Unit; if (SourceDefaultUnit.UnitSystem != "Metric.SI" && SourceDefaultUnit.GetType() != typeof(Shared.Second)) { //from source default unit to the si DefaultPItem = FromMeToDefaultUnit.Peek(); RefUPI = new UnitPathItem { Numerator = DefaultPItem.Unit.ReferenceUnitNumerator, Denominator = DefaultPItem.Unit.ReferenceUnitDenominator, //Shift = DefaultPItem.Unit.ReferenceUnitShift, Unit = DefaultPItem.Unit.ReferenceUnit }; } else { // from target default unit to si DefaultPItem = FromDefaultUnitToTargetUnit.ElementAt(FromDefaultUnitToTargetUnit.Count - 1); RefUPI = new UnitPathItem { //note the difference here //I made the opposite assignments because we are in reverse manner Numerator = DefaultPItem.Unit.ReferenceUnitDenominator, // <=== opposite Denominator = DefaultPItem.Unit.ReferenceUnitNumerator, // <=== //Shift = 0-DefaultPItem.Unit.ReferenceUnitShift, Unit = DefaultPItem.Unit.ReferenceUnit }; } if (RefUPI.Unit != null) { SystemsPath.Push(RefUPI); } else { //both default units were SI units without references //note: // when define units in unit cloud for quantity // either make all units reference SI units without default unit // or make one default unit and make the rest of units reference it. } } //combine the two paths UnitPathStack Total = new UnitPathStack(); //we are building the conversion stairs // will end like a stack //begin from me unit to default unit for (int i = FromMeToDefaultUnit.Count - 1; i >= 0; i--) { Total.Push(FromMeToDefaultUnit.ElementAt(i)); } Unit One = new Unit(typeof(DimensionlessQuantity <>)); //cross the system if we need to . if (SystemsPath != null) { Total.Push(new UnitPathItem { Denominator = 1, Numerator = 1, Unit = One }); for (int i = SystemsPath.Count - 1; i >= 0; i--) { Total.Push(SystemsPath.ElementAt(i)); } Total.Push(new UnitPathItem { Denominator = 1, Numerator = 1, Unit = One }); } // from default unit to target unit for (int i = FromDefaultUnitToTargetUnit.Count - 1; i >= 0; i--) { Total.Push(FromDefaultUnitToTargetUnit.ElementAt(i)); } //another check if the units are inverted then // go through all items in path and invert it also if (this.IsInverted && unit.IsInverted) { foreach (UnitPathItem upi in Total) { upi.Invert(); } } //Second location in cache look above for the first one in the same function here :D if (EnableUnitsCaching) { CachedPaths.Add(UnitToUnitSymbol(this, unit), (UnitPathStack)Total.Clone()); } return(Total); } }