fix(markarbejde): markens omrids fra getDensityMapPolygon (FS25 har ingen polygonPoints) — flere prøvepunkter, markør-afstand til kanten; F108 majs sås som bar jord og F<nr>-markører 'manglede' på store marker (v1.28.6.0)

This commit is contained in:
2026-09-26 12:28:27 +02:00
parent 9be8fa98f0
commit 36f381fc36
3 changed files with 123 additions and 18 deletions
+62 -17
View File
@@ -151,13 +151,63 @@ local function isOnField(x, z)
return FSDensityMapUtil.getFieldDataAtWorldPosition(x, y, z) and true or false return FSDensityMapUtil.getFieldDataAtWorldPosition(x, y, z) and true or false
end end
local function findMarker(adEnv, fieldId, x, z) ADFields.SAMPLE_DIRECTIONS = 16
-- Markens omrids som {{x, z}, ...}. FS25: field:getDensityMapPolygon():getVerticesList() (flad liste
-- x1, z1, x2, z2 ... — som Courseplay); ældre: polygonPoints (noder). Tom liste hvis intet findes.
function ADFields.readPolygon(field)
local verts = {}
local okList, list = pcall(function()
return field.getDensityMapPolygon ~= nil and field:getDensityMapPolygon():getVerticesList() or nil
end)
if okList and type(list) == "table" and #list >= 6 then
for index = 1, #list - 1, 2 do
table.insert(verts, {x = list[index], z = list[index + 1]})
end
return verts
end
for _, node in ipairs(field.polygonPoints or {}) do
if type(node) == "number" then
local x, _, z = getWorldTranslation(node)
table.insert(verts, {x = x, z = z})
end
end
return verts
end
-- Hjørnernes gennemsnit (nil uden hjørner).
function ADFields.polygonCenter(verts)
if verts == nil or #verts == 0 then return nil end
local cx, cz = 0, 0
for _, v in ipairs(verts) do cx, cz = cx + v.x, cz + v.z end
return cx / #verts, cz / #verts
end
-- Afstand fra (x, z) til omridsets nærmeste kant; nil uden omrids.
function ADFields.distanceToPolygon(verts, x, z)
if verts == nil or #verts == 0 then return nil end
local best = math.huge
for index = 1, #verts do
local a, b = verts[index], verts[index % #verts + 1]
local dx, dz = b.x - a.x, b.z - a.z
local len2 = dx * dx + dz * dz
local t = len2 > 0 and math.max(0, math.min(1, ((x - a.x) * dx + (z - a.z) * dz) / len2)) or 0
local d = math.sqrt((a.x + dx * t - x) ^ 2 + (a.z + dz * t - z) ^ 2)
if d < best then best = d end
end
return best
end
-- Markøren "F<nr>" nærmest marken: afstand til omridset (markører står ved kanten; midten kan
-- ligge 900 m væk på store kort), ellers til midten.
local function findMarker(adEnv, fieldId, x, z, verts)
local best, bestDistance = nil, math.huge local best, bestDistance = nil, math.huge
for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do for _, marker in pairs(adEnv.ADGraphManager:getMapMarkers()) do
if ADFields.parseMarkerName(marker.name) == fieldId then if ADFields.parseMarkerName(marker.name) == fieldId then
local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id) local wayPoint = adEnv.ADGraphManager:getWayPointById(marker.id)
if wayPoint ~= nil then if wayPoint ~= nil then
local distance = math.sqrt((wayPoint.x - x) ^ 2 + (wayPoint.z - z) ^ 2) local distance = ADFields.distanceToPolygon(verts, wayPoint.x, wayPoint.z)
or math.sqrt((wayPoint.x - x) ^ 2 + (wayPoint.z - z) ^ 2)
if distance < bestDistance then best, bestDistance = marker, distance end if distance < bestDistance then best, bestDistance = marker, distance end
end end
end end
@@ -199,24 +249,19 @@ local function newScan(adEnv, entry)
scan.info = info scan.info = info
-- 1) polygon og kandidatpunkter (centroid + halvvejs mod op til 8 hjørner) -- 1) polygon og kandidatpunkter (centroid + halvvejs mod op til 8 hjørner)
push(scan, function() push(scan, function()
local verts = {} local verts = ADFields.readPolygon(field)
for _, node in ipairs(field.polygonPoints or {}) do
if type(node) == "number" then
local x, _, z = getWorldTranslation(node)
table.insert(verts, {x = x, z = z})
end
end
info.verts = verts info.verts = verts
local cx, cz = field.posX, field.posZ local cx, cz = ADFields.polygonCenter(verts)
if (cx == nil or cz == nil) and #verts > 0 then if cx == nil then cx, cz = field.posX, field.posZ end
cx, cz = 0, 0
for _, v in ipairs(verts) do cx, cz = cx + v.x, cz + v.z end
cx, cz = cx / #verts, cz / #verts
end
info.x, info.z = cx, cz info.x, info.z = cx, cz
if cx == nil then return end if cx == nil then return end
-- midten, markskiltet og halvvejs mod op til SAMPLE_DIRECTIONS hjørner: store marker (Dakota
-- 16x: 1,7 km) har striber uden afgrøde, så ét punkt kan ramme forkert
local candidates = {{x = cx, z = cz}} local candidates = {{x = cx, z = cz}}
local step = math.max(1, math.floor(#verts / 8)) if field.posX ~= nil and field.posZ ~= nil and (field.posX - cx) ^ 2 + (field.posZ - cz) ^ 2 > 1 then
table.insert(candidates, {x = field.posX, z = field.posZ})
end
local step = math.max(1, math.floor(#verts / ADFields.SAMPLE_DIRECTIONS))
for index = 1, #verts, step do for index = 1, #verts, step do
table.insert(candidates, {x = (cx + verts[index].x) / 2, z = (cz + verts[index].z) / 2}) table.insert(candidates, {x = (cx + verts[index].x) / 2, z = (cz + verts[index].z) / 2})
end end
@@ -318,7 +363,7 @@ local function finishScan(adEnv, scan)
if previous ~= nil and previous.markerCheckedAt ~= nil and ADFields.now - previous.markerCheckedAt < ADFields.MARKER_REFRESH_MS then if previous ~= nil and previous.markerCheckedAt ~= nil and ADFields.now - previous.markerCheckedAt < ADFields.MARKER_REFRESH_MS then
info.markerId, info.markerName, info.markerDistance, info.markerCheckedAt = previous.markerId, previous.markerName, previous.markerDistance, previous.markerCheckedAt info.markerId, info.markerName, info.markerDistance, info.markerCheckedAt = previous.markerId, previous.markerName, previous.markerDistance, previous.markerCheckedAt
elseif info.x ~= nil then elseif info.x ~= nil then
local marker, distance = findMarker(adEnv, info.id, info.x, info.z) local marker, distance = findMarker(adEnv, info.id, info.x, info.z, info.verts)
info.markerId = marker ~= nil and marker.id or nil info.markerId = marker ~= nil and marker.id or nil
info.markerName = marker ~= nil and marker.name or nil info.markerName = marker ~= nil and marker.name or nil
info.markerDistance = distance info.markerDistance = distance
+1 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<modDesc descVersion="107"> <modDesc descVersion="107">
<author>masterdraco</author> <author>masterdraco</author>
<version>1.28.5.0</version> <version>1.28.6.0</version>
<title> <title>
<en>AD Smart Pickup</en> <en>AD Smart Pickup</en>
</title> </title>
+60
View File
@@ -0,0 +1,60 @@
-- Kør: luajit tests/test_adFieldsPolygon.lua (fra repo-roden)
-- FS25-marker har ingen polygonPoints — omridset hentes med field:getDensityMapPolygon():getVerticesList().
-- In-game 26/9 (Dakota 16x, marker på 1,7 x 0,6 km): uden omrids blev kun markskiltet (posX/posZ) prøvet;
-- F108 med majs viste "bar jord" og "mangler markør" (markøren stod ved kanten, 890 m fra skiltet).
local failures = 0
local function check(name, actual, expected)
if actual == expected then print("OK " .. name) else
failures = failures + 1
print(string.format("FAIL %s: forventede %s, fik %s", name, tostring(expected), tostring(actual)))
end
end
Logging = {warning = function() end, info = function() end}
local MAIZE = 10
function getWorldTranslation() return 0, 0, 0 end
function getTerrainHeightAtWorldPos() return 0 end
-- markskiltet står i marken, men ved en stribe uden afgrøde (x > 1400)
local function cropAt(x) return x <= 1400 and MAIZE or nil end
FSDensityMapUtil = {
getFieldDataAtWorldPosition = function() return 1, 2 end,
getFruitTypeIndexAtWorldPos = function(x, z) return cropAt(x) end,
getFruitArea = function() return 0, 10 end,
}
FieldDensityMap = {SPRAY_LEVEL = 4, LIME_LEVEL = 5, PLOW_LEVEL = 6}
DensityCoordType = {POINT_VECTOR_VECTOR = 1}
DensityMapModifier = {new = function() return {setParallelogramWorldCoords = function() end, executeGet = function() return 1, 1 end} end}
g_fruitTypeManager = {
getFruitTypeByIndex = function() return {name = "MAIZE", fillType = {index = 60, title = "Majs"}, minHarvestingGrowthState = 7, maxHarvestingGrowthState = 7, cutState = 9} end,
getConverterDataByName = function() return {} end,
}
g_fillTypeManager = {getConverterDataByName = function() return {} end, getFillTypeNameByIndex = function() return "X" end}
g_farmlandManager = {getFarmlandOwner = function() return 1 end}
local polygon = {getVerticesList = function() return {0, 0, 1600, 0, 1600, 600, 0, 600} end}
local field = {getId = function() return 108 end, farmland = {id = 108}, posX = 1500, posZ = 50,
getDensityMapPolygon = function() return polygon end}
g_fieldManager = {getFields = function() return {field} end}
g_currentMission = {terrainRootNode = 1, missionInfo = {}, fieldGroundSystem = {
getDensityMapData = function() return 1, 0, 2 end, getMaxValue = function() return 2 end}}
local adEnv = {ADGraphManager = {
getMapMarkers = function() return {{id = 900, name = "F108"}} end,
-- markøren står 5 m uden for marken ved den modsatte kant: 1505 m fra skiltet
getWayPointById = function() return {x = -5, z = 300} end,
}}
dofile("FS25_ADSmartPickup/adFields.lua")
local now = 0
for _ = 1, 1000 do
if ADFields.getInfo(108) ~= nil then break end
ADFields.scanStep(adEnv, 1, now)
now = now + 16
end
local info = ADFields.getInfo(108)
check("PG1 marken er scannet", info ~= nil, true)
check("PG2 omrids fra getDensityMapPolygon", info and #info.verts, 4)
check("PG3 majsen findes trods skiltet på bar stribe", info and info.fruitName, "MAIZE")
check("PG4 ikke bar mark", info and info.kind, "harvest")
check("PG5 markør ved kanten findes (afstand til omridset)", info and info.markerId, 900)
check("PG6 afstand = til kanten", info and info.markerDistance, 5)
print(failures == 0 and "\nALLE TESTS OK" or ("\n" .. failures .. " FEJL"))
os.exit(failures == 0 and 0 or 1)