Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        /// <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);
            }
        }