using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using Tango.BL.Interfaces; using Tango.Core; using Tango.Core.Threading; namespace Tango.BL.Entities { public partial class Segment : SegmentBase, ISegment { private double _lastLength; private LinearGradientBrush _brush; private ActionTimer _brushStopCollectionChangedActionTimer; #region Properties private TimeSpan _remainingTime; /// /// Gets or sets the remaining time for this segment to complete. /// [NotMapped] [JsonIgnore] public TimeSpan RemainingTime { get { return _remainingTime; } set { _remainingTime = value; RaisePropertyChangedAuto(); } } private TimeSpan _estimatedDuration; /// /// Gets or sets the estimated duration for this segment to complete. /// [NotMapped] [JsonIgnore] public TimeSpan EstimatedDuration { get { return _estimatedDuration; } set { _estimatedDuration = value; } } private double _progress; /// /// Gets or sets the segment progress within a running job. /// [NotMapped] [JsonIgnore] public double Progress { get { return _progress; } set { _progress = value; RaisePropertyChangedAuto(); } } private bool _started; /// /// Gets or sets a value indicating whether this segment has started. /// [NotMapped] [JsonIgnore] public bool Started { get { return _started; } set { _started = value; RaisePropertyChangedAuto(); } } private bool _completed; /// /// Gets or sets a value indicating whether this segment has completed. /// [NotMapped] [JsonIgnore] public bool Completed { get { return _completed; } set { _completed = value; RaisePropertyChangedAuto(); } } /// /// Gets the segment brush. /// [NotMapped] [JsonIgnore] public Brush SegmentBrush { get { return GetSegmentBrush(); } } private bool _isInterSegment; /// /// Gets or sets a value indicating whether this segment is an inter segment. /// [NotMapped] [JsonIgnore] public bool IsInterSegment { get { return _isInterSegment; } set { _isInterSegment = value; RaisePropertyChangedAuto(); } } /// /// Gets a value indicating whether this segment has any out of gamut brush stops. /// [NotMapped] [JsonIgnore] public bool HasOutOfGamutBrushStop { get { return BrushStops.Any(x => x.IsOutOfGamut); } } /// /// Gets the length of this segment including the job length factor . /// [NotMapped] [JsonIgnore] public double LengthWithFactor { get { return Job != null && !IsInterSegment ? (Length + Length * (Job.LengthPercentageFactor / 100)) : Length; } } [NotMapped] [JsonIgnore] public String DisplayLengthWithFactor { get { double length = Job != null && !IsInterSegment ? (Length + Length * (Job.LengthPercentageFactor / 100)) : Length; return length.ToString("N0") + "m"; } } #endregion #region Constructors /// /// Initializes a new instance of the class. /// public Segment() : base() { } #endregion #region Brush Stops Collection Changed private void BrushStops_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (_brushStopCollectionChangedActionTimer == null) { _brushStopCollectionChangedActionTimer = new ActionTimer(TimeSpan.FromMilliseconds(100)); } _brushStopCollectionChangedActionTimer.ResetReplace(() => { foreach (var stop in BrushStops.ToList()) { stop.RaiseOffsetChanged(); } //Item#7059 - //if (BrushStops.Count > 0) //{ // BrushStops.First().OffsetPercent = 0; //} //if (BrushStops.Count > 1) //{ // BrushStops.Last().OffsetPercent = 100; //} RaiseSegmentBrushChanged(); }); } #endregion #region Properties Changed /// /// Called when the Length has changed. /// /// protected override void OnLengthChanged(double length) { base.OnLengthChanged(length); if (_lastLength != length) { BrushStops.ToList().ForEach(x => x.RaiseOffsetChanged()); _lastLength = Length; RaisePropertyChanged(nameof(LengthWithFactor)); } } /// /// Called when the BrushStops has changed. /// /// protected override void OnBrushStopsChanged(SynchronizedObservableCollection brushstops) { base.OnBrushStopsChanged(brushstops); if (brushstops != null) { brushstops.CollectionChanged -= BrushStops_CollectionChanged; brushstops.CollectionChanged += BrushStops_CollectionChanged; foreach (var stop in brushstops.ToList()) { stop.RaiseOffsetChanged(); } RaiseSegmentBrushChanged(); } } #endregion #region Public Methods /// /// Gets the segment brush. /// /// public LinearGradientBrush GetSegmentBrush() { if (_brush == null || _brush.GradientStops.Count != BrushStops.Count) { GradientStopCollection stops = new GradientStopCollection(); foreach (var stop in BrushStops.ToList().OrderBy(x => x.StopIndex).ToList()) { stops.Add(new GradientStop(stop.IsTransparent ? Colors.Transparent : stop.Color, stop.OffsetPercent / 100d)); } LinearGradientBrush brush = new LinearGradientBrush(); brush.StartPoint = new Point(0, 0); brush.EndPoint = new Point(1, 0); brush.GradientStops = stops; _brush = brush; return brush; } else { for (int i = 0; i < BrushStops.Count; i++) { _brush.GradientStops[i].Color = BrushStops[i].IsTransparent ? Colors.Transparent : BrushStops[i].Color; _brush.GradientStops[i].Offset = BrushStops[i].OffsetPercent / 100d; } return _brush; } } public void UpdateMiddleColorBrush() { if (BrushStops.Count == 5) { BrushStops[2].Color = GetRelativeRGB(BrushStops[1].Color, BrushStops[3].Color, 0, 1, 0.5); } } public BrushStop FirstBrushStop { get { if (BrushStops.Count == 1)//Intersegment?? - null return BrushStops[0]; else if(BrushStops.Count >= 5) return BrushStops[1]; else return null; } } public static Color GetRelativeRGB(Color first, Color second, double firstOffset, double secondOffset, double offset) { var color = new Color(); var range = (secondOffset - firstOffset); var realoffset = offset - firstOffset; color.ScA = (float)(realoffset * (second.ScA - first.ScA) / range + first.ScA); color.ScR = (float)(realoffset * (second.ScR - first.ScR) / range + first.ScR); color.ScG = (float)(realoffset * (second.ScG - first.ScG) / range + first.ScG); color.ScB = (float)(realoffset * (second.ScB - first.ScB) / range + first.ScB); return color; } /// /// Raises the property changed event. /// public void RaiseSegmentBrushChanged() { RaisePropertyChanged(nameof(SegmentBrush)); } /// /// Raises the property changed event. /// public void RaiseLengthWithFactorChanged() { RaisePropertyChanged(nameof(LengthWithFactor)); } /// /// Adds a new brush stop to this segment. /// /// public BrushStop AddBrushStop() { BrushStop stop = new BrushStop(); var lastStop = BrushStops.OrderBy(x => x.StopIndex).LastOrDefault(); if (lastStop != null && lastStop.ColorSpace != null) { stop.ColorSpace = lastStop.ColorSpace; } else if (Job != null) { if (Job.ColorSpace != null) { stop.ColorSpace = Job.ColorSpace; } else { stop.ColorSpaceGuid = Job.ColorSpaceGuid; } } if (BrushStops.Count > 0) { stop.StopIndex = BrushStops.Max(x => x.StopIndex) + 1; stop.OffsetPercent = 100; } else { stop.StopIndex = 1; } stop.Segment = this; stop.Color = Colors.White; if (stop.ColorSpace != null) { if (stop.BrushColorSpace == Enumerations.ColorSpaces.LAB) { stop.L = 100; stop.A = 0; stop.B = 0; } } BrushStops.Add(stop); return stop; } /// /// Gets the next segment. /// /// public Segment GetNextSegment() { return Job.OrderedSegments.FirstOrDefault(x => x.SegmentIndex > SegmentIndex); } /// /// Gets the previous segment. /// /// public Segment GetPreviousSegment() { return Job.OrderedSegments.LastOrDefault(x => x.SegmentIndex < SegmentIndex); } /// /// Gets the collection of previous segments. /// /// public List GetPreviousSegments() { return Job.OrderedSegments.Where(x => x.SegmentIndex < SegmentIndex).ToList(); } /// /// Gets the estimated duration for this segment by the specified process parameters. /// /// The process parameters. /// /// Process parameters dying speed cannot be zero. public TimeSpan GetEstimatedDuration(ProcessParametersTable processParameters) { if (processParameters.DyeingSpeed == 0) { throw new ArgumentException("Process parameters dying speed cannot be zero."); } return TimeSpan.FromSeconds(Length / (processParameters.DyeingSpeed / 100d)); } /// /// Creates a GDI version of this segment brush. /// /// The width. /// The height. /// public System.Drawing.Brush CreateGdiBrush(int width, int height) { if (BrushStops.Count > 1) { System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new System.Drawing.PointF(0, 0), new System.Drawing.Point(width, height), System.Drawing.Color.Black, System.Drawing.Color.Black); System.Drawing.Drawing2D.ColorBlend blend = new System.Drawing.Drawing2D.ColorBlend(); List colors = new List(); List offsets = new List(); foreach (var stop in BrushStops.ToList().OrderBy(x => x.OffsetPercent)) { colors.Add(stop.Color.ToGdiColor()); offsets.Add((float)stop.OffsetPercent / 100f); } blend.Colors = colors.ToArray(); blend.Positions = offsets.ToArray(); brush.InterpolationColors = blend; return brush; } else if (BrushStops.Count == 1) { return new System.Drawing.SolidBrush(BrushStops.First().Color.ToGdiColor()); } else { return System.Drawing.Brushes.White; } } #endregion #region Internal Methods /// /// Raises the property changed event. /// internal void RaiseHasOutOfGamutBrushStop() { RaisePropertyChanged(nameof(HasOutOfGamutBrushStop)); } #endregion #region Cloning /// /// Clones this segment with its brush stops. /// /// public override Segment Clone() { Segment cloned = base.Clone(); cloned.BrushStops = BrushStops.Select(x => x.Clone()).ToSynchronizedObservableCollection(); foreach (var stop in cloned.BrushStops) { stop.SegmentGuid = cloned.Guid; stop.Segment = cloned; } return cloned; } /// /// Clones this segment and assigns it to the specified job. /// /// The job. /// public Segment Clone(Job job) { Segment cloned = base.Clone(); cloned.BrushStops = BrushStops.Select(x => x.Clone(cloned)).ToSynchronizedObservableCollection(); cloned.Job = job; cloned.JobGuid = job.Guid; return cloned; } /// /// Clones this segment with its brush stops. /// /// ISegment ISegment.Clone() { return Clone() as ISegment; } /// /// Clones this segment and assigns it to the specified job. /// /// The job. /// ISegment ISegment.Clone(Job job) { return Clone(job) as ISegment; } #endregion #region Delete /// /// Removes this entity and all dependent entities from the specified db context. /// /// The context. public override void Delete(ObservablesContext context) { base.Delete(context); if (BrushStops != null && BrushStops.Count > 0) { context.BrushStops.RemoveRange(BrushStops); } context.Segments.Remove(this); } #endregion } }