diff --git a/src/lua/recognizeProfile.lua b/src/lua/recognizeProfile.lua index 36c05e6..57e6605 100644 --- a/src/lua/recognizeProfile.lua +++ b/src/lua/recognizeProfile.lua @@ -1,6 +1,89 @@ -local paths = Metrostroi.GetTrackPaths() -if not paths then return end -local trackPath = paths[14] +local function getPlan(path) + local LocalCurvatureThreshold = 0.000063 + local lastMode = "straight" + local plan = {} + local startIndex = 1 + local ordinate = 0 + local planElement = { + ordinate = 0, + startIndex = startIndex, + elementType = lastMode + } + + for k, v in pairs(path) do + if k < 3 then continue end + local start = path[k - 2] + local fin = path[k - 1] + local dir = path[k] + local startFlat = Vector(start.x, start.y) + local finFlat = Vector(fin.x, fin.y) + local dirFlat = Vector(dir.x, dir.y) + local directionInit = finFlat - startFlat + local directionNew = dirFlat - finFlat + local dir1 = directionInit:GetNormalized() + local dir2 = directionNew:GetNormalized() + local angleCos = dir2:Dot(dir1) + local angleRad = math.acos(math.Clamp(angleCos, -1, 1)) + local angle = math.deg(angleRad) + local cross = dir1.x * dir2.y - dir1.y * dir2.x + local localCurvature = angleRad / (dirFlat - finFlat):Length() + local isCurve = localCurvature > LocalCurvatureThreshold + local isRight = cross < 0 + if isRight then angle = -angle end + local mode = Either(isCurve, Either(isRight, "right", "left"), "straight") + if mode == "right" and lastMode == "left" then mode = "straight" end + if mode == "left" and lastMode == "right" then mode = "straight" end + if mode ~= "straight" and lastMode == "straight" then + planElement.endIndex = k - 2 + if planElement.length then ordinate = ordinate + planElement.length end + if planElement.startIndex ~= planElement.endIndex then table.insert(plan, planElement) end + planElement = {} + planElement.ordinate = ordinate + planElement.startIndex = k - 2 + planElement.elementType = mode + end + + if mode == "straight" and lastMode ~= "straight" then + planElement.curveLen = planElement.curveLen or 0 + planElement.curveLen = planElement.curveLen + directionInit:Length() * 0.01905 -- Convert to meters + planElement.endIndex = k - 1 + planElement.radius = math.abs(planElement.curveLen / planElement.angle) + ordinate = ordinate + planElement.curveLen + if planElement.startIndex ~= planElement.endIndex then table.insert(plan, planElement) end + planElement = {} + planElement.ordinate = ordinate + planElement.startIndex = k - 1 + planElement.elementType = mode + end + + if mode == "straight" and lastMode == "straight" then + planElement.length = planElement.length or 0 + planElement.length = planElement.length + directionInit:Length() * 0.01905 -- Convert to meters + end + + if mode ~= "straight" then + planElement.angle = planElement.angle or 0 + planElement.angle = planElement.angle + angleRad + planElement.angleDeg = planElement.angleDeg or 0 + planElement.angleDeg = planElement.angleDeg + angle + planElement.curveLen = planElement.curveLen or 0 + planElement.curveLen = planElement.curveLen + directionInit:Length() * 0.01905 -- Convert to meters + end + + if k == #path then + planElement.endIndex = k + if planElement.startIndex ~= planElement.endIndex then table.insert(plan, planElement) end + if mode == "straight" and lastMode == "straight" then + planElement.length = planElement.length or 0 + planElement.length = planElement.length + directionNew:Length() * 0.01905 -- Convert to meters + end + end + + lastMode = mode + end + return plan +end + local function getProfile(path) local step = 11 ------------------------------------------------------------------ @@ -58,8 +141,8 @@ local function getProfile(path) length = accLen, ordinate = ordinate, }) - ordinate = ordinate + accLen + ordinate = ordinate + accLen -- начинаем новую curStart = p.startIndex curEnd = p.endIndex @@ -77,9 +160,46 @@ local function getProfile(path) length = accLen, ordinate = ordinate, }) - return filteredProfile end -local profile = getProfile(trackPath) -PrintTable(profile) \ No newline at end of file +local function generatePlanProfile(trackNum) + trackNum = tonumber(trackNum) or 1 + + local paths = Metrostroi.GetTrackPaths() + if not paths then + print("[plan_profile] Metrostroi.GetTrackPaths() returned nil") + return + end + + local trackPath = paths[trackNum] + if not trackPath then + print(string.format("[plan_profile] Track %d not found", trackNum)) + return + end + + local plan = getPlan(trackPath) + local profile = getProfile(trackPath) + local map = game.GetMap() + + if not file.Exists("metrostroi_data", "DATA") then + file.CreateDir("metrostroi_data") + end + + local planFileName = string.format("metrostroi_data/plan_%s_%d.json", map, trackNum) + local profFileName = string.format("metrostroi_data/prof_%s_%d.json", map, trackNum) + + file.Write(planFileName, util.TableToJSON(plan, true)) + file.Write(profFileName, util.TableToJSON(profile, true)) + + print(string.format("[plan_profile] Saved plan to %s", planFileName)) + print(string.format("[plan_profile] Saved profile to %s", profFileName)) +end + +concommand.Add("plan_profile", function(ply, cmd, args, argStr) + local argTrack = args and args[1] + if not argTrack then + print("[plan_profile] Usage: plan_profile (default 1)") + end + generatePlanProfile(argTrack) +end) \ No newline at end of file