agrobot_base/AgroBase/OperationControl/Controls/Pulverizador/Converters.cs

169 lines
5.9 KiB
C#

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();
}
}