using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; namespace OperationControl.Controls.Pulverizador { class Converters { } public class AlturaBarraToMarginFisicaConverter : IValueConverter { public double GroundTop { get; set; } = 80.0; // Y do solo public double Top80Px { get; set; } = 20.0; // Y da barra quando está a 80 cm public double AlturaMaxCm { get; set; } = 80.0; public double BarraOffset { get; set; } = 13.0; public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double alturaCm) { alturaCm = Math.Clamp(alturaCm, 0, AlturaMaxCm); double distanciaPx = GroundTop - Top80Px; // px que representam 80 cm double pxPorCm = distanciaPx / AlturaMaxCm; // regra de 3 double alturaPx = alturaCm * pxPorCm; double yBarra = GroundTop - alturaPx; // Y da barra na GRID double marginTop = yBarra - BarraOffset; // compensa o offset interno return new Thickness(33, marginTop, 33, 0); } return new Thickness(33, 0, 33, 0); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => System.Windows.Data.Binding.DoNothing; } public class BicoPositionFisicaConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 5) return 0.0; if (values[0] is not double barraWidth || values[1] is not int index || values[2] is not int qtd || values[3] is not double distEntreBicosCm || values[4] is not double comprimentoBarraCm) { return 0.0; } if (qtd <= 0 || barraWidth <= 0 || comprimentoBarraCm <= 0) return 0.0; // garante valores válidos distEntreBicosCm = Math.Max(0.0, distEntreBicosCm); if (qtd == 1) { // 1 bico: centro da barra return barraWidth / 2.0; } // span "ideal" em cm (do primeiro ao último bico) double spanIdealCm = (qtd - 1) * distEntreBicosCm; // Se o span ideal for maior que a barra física, comprimimos double escala = 1.0; if (spanIdealCm > comprimentoBarraCm && qtd > 1) { escala = comprimentoBarraCm / spanIdealCm; } double distEfetivaCm = distEntreBicosCm * escala; double spanEfetivoCm = (qtd - 1) * distEfetivaCm; // centraliza o conjunto de bicos na barra double offsetCm = (comprimentoBarraCm - spanEfetivoCm) / 2.0; // posição em cm do bico atual double xCm = offsetCm + index * distEfetivaCm; // regra de 3: cm → px double pxPorCm = barraWidth / comprimentoBarraCm; double xPx = xCm * pxPorCm; return xPx; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); } public class RuaWidthFisicaConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 3) return 0.0; if (values[0] is not double totalWidth || values[1] is not double larguraRuaCm || values[2] is not double comprimentoBarraCm || totalWidth <= 0 || comprimentoBarraCm <= 0 || larguraRuaCm <= 0) { return 0.0; } // px por cm na faixa do solo (usando comprimento da barra como referência física) double pxPorCm = totalWidth / comprimentoBarraCm; double widthPx = larguraRuaCm * pxPorCm; // não deixa a rua ficar maior que a área disponível if (widthPx > totalWidth) widthPx = totalWidth; return widthPx; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); } public class RuaOffsetFisicaConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length < 4) return new Thickness(0); if (values[0] is not double totalWidth || values[1] is not double erroLateralCm || values[2] is not double larguraRuaCm || values[3] is not double comprimentoBarraCm || totalWidth <= 0 || comprimentoBarraCm <= 0) { return new Thickness(0); } double pxPorCm = totalWidth / comprimentoBarraCm; double widthPx = Math.Min(totalWidth, Math.Max(0, larguraRuaCm * pxPorCm)); double center = totalWidth / 2.0; // erro > 0 = robô desviado pra direita → rua aparece indo pra esquerda double roadCenter = center - erroLateralCm * pxPorCm; double left = roadCenter - widthPx / 2.0; // SEM travas aqui: pode ser negativo ou maior que totalWidth // o Grid com ClipToBounds cuida de cortar o desenho return new Thickness(left, 0, 0, 0); } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); } }