# verifica_grila.ps1 - citeste hartile de geometrie recursiva geom_grila_.tsv (scrise de # DumpGrilaRecursiv/ColectGrila din test_s3b_dupa_ui.prg) si raporteaza incalcarile din # docs\nota_s3b_reasezare_grila.md sectiunea 6 + addendumul A1.5. Nu ruleaza VFP, doar text. # # Rulare: powershell -ExecutionPolicy Bypass -File verifica_grila.ps1 [-Dir ] [-OutFile ] # implicit -Dir = .\out , -OutFile = .\out\raport_grila.txt param( [string]$Dir = (Join-Path $PSScriptRoot 'out'), [string]$OutFile = (Join-Path (Join-Path $PSScriptRoot 'out') 'raport_grila.txt') ) $ErrorActionPreference = 'Stop' # controale admise in banda de titlu (Top 0..29), pe Name complet $TitleBandAllow = @('_shape1', '_shape2', 'Lb_titlu_alb_b121', 'BUT_TERMIN1', 'But_renunt1') function Read-GeomTsv { param([string]$Path) $lines = Get-Content -Path $Path -Encoding Default if ($lines.Count -lt 2) { return @() } $header = $lines[0] -split "`t" $rows = @() for ($i = 1; $i -lt $lines.Count; $i++) { if ([string]::IsNullOrEmpty($lines[$i])) { continue } $f = $lines[$i] -split "`t" # linia poate avea Caption gol la coada -> -split scurteaza array-ul; completeaza while ($f.Count -lt $header.Count) { $f += '' } $rows += [pscustomobject]@{ Name = $f[0] Class = $f[1] Left = [double]$f[2] Top = [double]$f[3] Width = [double]$f[4] Height = [double]$f[5] Visible = ($f[6] -eq '.T.') AbsLeft = [double]$f[7] AbsTop = [double]$f[8] Caption = $f[9] } } return $rows } function Is-ShapeOrGrid { param($row) if ($row.Class -match '^_shape') { return $true } if ($row.Class -eq '_grdrow') { return $true } if ($row.Name -match '(^|\.)[Gg]rd_') { return $true } return $false } function Is-AncestorOf { param([string]$a, [string]$b) return $b.StartsWith($a + '.') } # --- verificarile pe o singura stare ------------------------------------------------- function Check-TitleBand { param($rows) $hits = @() foreach ($r in $rows) { if (-not $r.Visible) { continue } if ($TitleBandAllow -icontains $r.Name) { continue } if ($r.AbsTop -lt 29 -and ($r.AbsTop + $r.Height) -gt 0) { $hits += $r } } return $hits } function Check-Overlaps { param($rows) $cand = @($rows | Where-Object { $_.Visible -and -not (Is-ShapeOrGrid $_) }) $hits = @() for ($i = 0; $i -lt $cand.Count; $i++) { for ($j = $i + 1; $j -lt $cand.Count; $j++) { $a = $cand[$i]; $b = $cand[$j] if ((Is-AncestorOf $a.Name $b.Name) -or (Is-AncestorOf $b.Name $a.Name)) { continue } $aR = $a.AbsLeft + $a.Width; $aB = $a.AbsTop + $a.Height $bR = $b.AbsLeft + $b.Width; $bB = $b.AbsTop + $b.Height $ovX = [Math]::Min($aR, $bR) - [Math]::Max($a.AbsLeft, $b.AbsLeft) $ovY = [Math]::Min($aB, $bB) - [Math]::Max($a.AbsTop, $b.AbsTop) if ($ovX -gt 2 -and $ovY -gt 2) { $hits += [pscustomobject]@{ A = $a; B = $b; OvX = $ovX; OvY = $ovY RectLeft = [Math]::Max($a.AbsLeft, $b.AbsLeft) RectTop = [Math]::Max($a.AbsTop, $b.AbsTop) } } } } return $hits } # Zonele nu sunt etichetate in harta - se reconstituie geometric: randurile (AbsTop grupat +/-3px) # se ordoneaza pe verticala, iar un gol de peste 40px intre bottom-ul unui rand si top-ul # urmatorului marcheaza granita intre doua zone (antet / grid / acordeoane / totaluri). $ZoneGapPx = 40 $RowTolerancePx = 3 function Group-Rows { param($rows) $cand = @($rows | Where-Object { $_.Visible -and -not (Is-ShapeOrGrid $_) } | Sort-Object AbsTop) $rowGroups = @() foreach ($r in $cand) { $g = $rowGroups | Where-Object { [Math]::Abs($_.Top - $r.AbsTop) -le $RowTolerancePx } | Select-Object -First 1 if ($null -eq $g) { $rowGroups += [pscustomobject]@{ Top = $r.AbsTop; Items = @($r); Bottom = ($r.AbsTop + $r.Height) } } else { $g.Items += $r if (($r.AbsTop + $r.Height) -gt $g.Bottom) { $g.Bottom = $r.AbsTop + $r.Height } } } return @($rowGroups | Sort-Object Top) } function Group-Zones { param($rowGroups) $zones = @() $cur = @() $prevBottom = $null foreach ($rg in $rowGroups) { if ($null -ne $prevBottom -and ($rg.Top - $prevBottom) -gt $ZoneGapPx) { $zones += , $cur $cur = @() } $cur += $rg if ($null -eq $prevBottom -or $rg.Bottom -gt $prevBottom) { $prevBottom = $rg.Bottom } } if ($cur.Count -gt 0) { $zones += , $cur } return $zones } function Check-Alignment { param($rows) $rowGroups = Group-Rows $rows $zones = Group-Zones $rowGroups $report = @() $zoneIdx = 0 foreach ($zone in $zones) { $zoneIdx++ if ($zone.Count -lt 2) { continue } $leftCounts = @{} foreach ($rg in $zone) { foreach ($it in $rg.Items) { $key = [string][int]$it.AbsLeft if ($leftCounts.ContainsKey($key)) { $leftCounts[$key] += 1 } else { $leftCounts[$key] = 1 } } } $distinct = $leftCounts.Keys | Sort-Object { [int]$_ } $suspects = $leftCounts.GetEnumerator() | Where-Object { $_.Value -eq 1 } | ForEach-Object { $_.Key } $report += [pscustomobject]@{ ZoneIdx = $zoneIdx TopRange = "$([int]$zone[0].Top)..$([int]($zone[$zone.Count-1].Top))" NumRows = $zone.Count Distinct = ($distinct -join ', ') Suspects = @($suspects | Sort-Object { [int]$_ }) } } return $report } function Get-MaxRight { param($rows) $cand = @($rows | Where-Object { $_.Visible }) if ($cand.Count -eq 0) { return 0 } return ([Math]::Round(($cand | ForEach-Object { $_.AbsLeft + $_.Width } | Measure-Object -Maximum).Maximum)) } # --- rulare pe toate starile ----------------------------------------------------------- if (-not (Test-Path $Dir)) { "Directorul $Dir nu exista." | Set-Content -Path $OutFile -Encoding UTF8 Write-Host "Director inexistent: $Dir" exit 1 } $files = Get-ChildItem -Path $Dir -Filter 'geom_grila_*.tsv' | Sort-Object Name if ($files.Count -eq 0) { "Niciun fisier geom_grila_*.tsv gasit in $Dir." | Set-Content -Path $OutFile -Encoding UTF8 Write-Host "Niciun fisier geom_grila_*.tsv in $Dir" exit 1 } $allTitleHits = @{} $allOverlapHits = @{} $allAlignHits = @{} $allMaxRight = @{} $allVisCount = @{} $detailLines = @() foreach ($f in $files) { $stare = $f.BaseName -replace '^geom_grila_', '' $rows = Read-GeomTsv -Path $f.FullName $visCount = @($rows | Where-Object { $_.Visible }).Count $titleHits = Check-TitleBand $rows $overlapHits = Check-Overlaps $rows $alignReport = Check-Alignment $rows $alignSuspectZones = @($alignReport | Where-Object { $_.Suspects.Count -gt 0 }) $maxRight = Get-MaxRight $rows $allTitleHits[$stare] = $titleHits.Count $allOverlapHits[$stare] = $overlapHits.Count $allAlignHits[$stare] = ($alignSuspectZones | ForEach-Object { $_.Suspects.Count } | Measure-Object -Sum).Sum if ($null -eq $allAlignHits[$stare]) { $allAlignHits[$stare] = 0 } $allMaxRight[$stare] = $maxRight $allVisCount[$stare] = $visCount $detailLines += "" $detailLines += "=== STARE: $stare ($($rows.Count) randuri in harta, $visCount vizibile) ===" $detailLines += "--- Banda de titlu (0..29): $($titleHits.Count) incalcari ---" foreach ($h in $titleHits) { $detailLines += (" {0} ({1}) AbsLeft={2} AbsTop={3} W={4} H={5}" -f $h.Name, $h.Class, [int]$h.AbsLeft, [int]$h.AbsTop, [int]$h.Width, [int]$h.Height) } $detailLines += "--- Suprapuneri eticheta/caseta: $($overlapHits.Count) ---" foreach ($h in $overlapHits) { $detailLines += (" {0} ({1}) x {2} ({3}) suprapunere {4}x{5}px la ({6},{7}) captionA='{8}' captionB='{9}'" -f ` $h.A.Name, $h.A.Class, $h.B.Name, $h.B.Class, [int]$h.OvX, [int]$h.OvY, [int]$h.RectLeft, [int]$h.RectTop, $h.A.Caption, $h.B.Caption) } $detailLines += "--- Aliniere pe coloane (zone reconstituite din goluri >$($ZoneGapPx)px, randuri grupate +/-$($RowTolerancePx)px) ---" foreach ($z in $alignReport) { $flag = if ($z.Suspects.Count -gt 0) { " <-- POSIBILA DEZALINIERE: AbsLeft=$($z.Suspects -join ', ')" } else { "" } $detailLines += (" zona {0} (Top {1}, {2} randuri): AbsLeft distincte = {3}{4}" -f $z.ZoneIdx, $z.TopRange, $z.NumRows, $z.Distinct, $flag) } $detailLines += "--- Latime folosita: max(AbsLeft+Width) peste vizibile = $maxRight px ---" $detailLines += "--- Numar total controale vizibile: $visCount ---" } $sumTitle = ($allTitleHits.Values | Measure-Object -Sum).Sum $sumOverlap = ($allOverlapHits.Values | Measure-Object -Sum).Sum $sumAlign = ($allAlignHits.Values | Measure-Object -Sum).Sum $widthSummary = ($allMaxRight.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '; ' $widthWarn = @($allMaxRight.GetEnumerator() | Where-Object { $_.Value -lt 1400 } | ForEach-Object { $_.Key }) $widthFlag = if ($widthWarn.Count -gt 0) { " -- SUB 1400px (criteriul 9): $($widthWarn -join ', ')" } else { " -- OK (>=1400px peste tot)" } $countSummary = ($allVisCount.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '; ' $summary = @( "REZUMAT (5 linii, citeste doar astea pentru verdict rapid):" "1. Incalcari banda de titlu (0..29px): $sumTitle" "2. Suprapuneri eticheta/caseta: $sumOverlap" "3. Posibile dezalinieri de coloana: $sumAlign" "4. Latime folosita per stare: $widthSummary$widthFlag" "5. Controale vizibile per stare: $countSummary" ) $out = @() $out += $summary $out += "" $out += "=" * 90 $out += $detailLines $out -join "`r`n" | Set-Content -Path $OutFile -Encoding UTF8 Write-Host "Raport scris in $OutFile" Write-Host ($summary -join "`n")