myspace / CommonLua /Editor /XEditor /XMeasureTool.lua
sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
9.58 kB
if FirstLoad then
EditorMeasureLines = false
end
function AddEditorMeasureLine(line)
EditorMeasureLines = EditorMeasureLines or {}
EditorMeasureLines[#EditorMeasureLines + 1] = line
end
function DestroyEditorMeasureLines()
for _, line in ipairs(EditorMeasureLines or empty_table) do
line:Done()
end
EditorMeasureLines = false
end
function UpdateEditorMeasureLines()
for _, line in ipairs(EditorMeasureLines or empty_table) do
line:Move(line.point0, line.point1)
end
end
OnMsg.EditorHeightChanged = UpdateEditorMeasureLines
OnMsg.EditorPassabilityChanged = UpdateEditorMeasureLines
OnMsg.ChangeMap = DestroyEditorMeasureLines
----- XMeasureTool
DefineClass.XMeasureTool = {
__parents = { "XEditorTool" },
properties = {
persisted_setting = true,
{ id = "CamDist", editor = "help", default = false, persisted_setting = false,
help = function(self)
local frac = self.cam_dist % guim
return string.format("Distance to screen: %d.%0".. (#tostring(guim) - 1) .."dm", self.cam_dist / guim, frac)
end
},
{ id = "Slope", editor = "help", default = false, persisted_setting = false,
help = function(self)
return string.format("Terrain slope: %.1f°", self.slope / 60.0)
end
},
{ id = "MeasureInSlabs", name = "Measure in slabs", editor = "bool", default = false, no_edit = not const.SlabSizeZ },
{ id = "FollowTerrain", name = "Follow terrain", editor = "bool", default = false, },
{ id = "IgnoreWalkables", name = "Ignore walkables", editor = "bool", default = false, },
{ id = "MeasurePath", name = "Measure path", editor = "bool", default = false, },
{ id = "StayOnScreen", name = "Stay on screen", editor = "bool", default = false, },
},
ToolTitle = "Measure",
Description = {
"Measures distance between two points.",
"The path found using the pathfinder and slope in degrees are also displayed.",
},
ActionSortKey = "1",
ActionIcon = "CommonAssets/UI/Editor/Tools/MeasureTool.tga",
ActionShortcut = "Alt-M",
ToolSection = "Misc",
UsesCodeRenderables = true,
measure_line = false,
measure_cam_dist_thread = false,
cam_dist = 0,
slope = 0,
}
function XMeasureTool:Init()
self.measure_cam_dist_thread = CreateRealTimeThread(function()
while true do
local mouse_pos = terminal.GetMousePos()
if mouse_pos:InBox2D(terminal.desktop.box) then
RequestPixelWorldPos(mouse_pos)
WaitNextFrame(6)
self.cam_dist = camera.GetEye():Dist2D(ReturnPixelWorldPos())
self.slope = terrain.GetTerrainSlope(GetTerrainCursor())
ObjModified(self)
end
Sleep(50)
end
end)
end
function XMeasureTool:Done()
if not self:GetStayOnScreen() then
DestroyEditorMeasureLines()
end
DeleteThread(self.measure_cam_dist_thread)
end
function XMeasureTool:OnMouseButtonDown(pt, button)
if button == "L" then
local terrain_cursor = GetTerrainCursor()
if self.measure_line then
self.measure_line = false
else
if not self:GetStayOnScreen() then
DestroyEditorMeasureLines()
end
self.measure_line = PlaceObject("MeasureLine", {
measure_in_slabs = self:GetMeasureInSlabs(),
follow_terrain = self:GetFollowTerrain(),
ignore_walkables = self:GetIgnoreWalkables(),
show_path = self:GetMeasurePath()
})
self.measure_line:Move(terrain_cursor, terrain_cursor)
AddEditorMeasureLine(self.measure_line)
end
return "break"
end
return XEditorTool.OnMouseButtonDown(self, pt, button)
end
function XMeasureTool:UpdatePoints()
local obj = self.measure_line
if obj and IsValid(obj) then
local pt = GetTerrainCursor()
obj:Move(obj.point0, pt)
obj:UpdatePath()
end
end
function XMeasureTool:OnMousePos(pt, button)
self:UpdatePoints()
end
function XMeasureTool:OnEditorSetProperty(prop_id, old_value, ged)
if prop_id == "MeasureInSlabs" then
for _, line in ipairs(EditorMeasureLines or empty_table) do
line.measure_in_slabs = self:GetMeasureInSlabs()
line:UpdateText()
end
elseif prop_id == "FollowTerrain" then
for _, line in ipairs(EditorMeasureLines or empty_table) do
line.follow_terrain = self:GetFollowTerrain()
line:Move(line.point0, line.point1)
end
elseif prop_id == "IgnoreWalkables" then
for _, line in ipairs(EditorMeasureLines or empty_table) do
line.ignore_walkables = self:GetIgnoreWalkables()
line:Move(line.point0, line.point1)
end
elseif prop_id == "MeasurePath" then
for _, line in ipairs(EditorMeasureLines or empty_table) do
line.show_path = self:GetMeasurePath()
line:UpdatePath()
end
end
if prop_id == "StayOnScreen" and not self:GetStayOnScreen() then
DestroyEditorMeasureLines()
self.measure_line = false
end
end
----- MeasureLine
DefineClass.MeasureLine = {
__parents = { "Object" },
point0 = point30,
point1 = point30,
path_distance = -1,
line_distance = -1,
horizontal_distance = -1,
vertical_distance = -1,
measure_in_slabs = false,
show_path = false,
follow_terrain = true,
}
function MeasureLine:Init()
self.line = PlaceObject("Polyline")
self.path = PlaceObject("Polyline")
self.label = PlaceObject("Text")
end
function MeasureLine:Done()
DoneObject(self.line)
DoneObject(self.path)
DoneObject(self.label)
end
function MeasureLine:DistanceToString(dist, slab_size, skip_slabs)
dist = Max(0, dist)
if self.measure_in_slabs then
local whole = dist / slab_size
return string.format(skip_slabs and "%d.%d" or "%d.%d slabs", whole, dist * 10 / slab_size - whole * 10)
else
local frac = dist % guim
return string.format("%d.%0".. (#tostring(guim) - 1) .."dm", dist / guim, frac)
end
end
function MeasureLine:UpdateText()
local dist_string
if self.measure_in_slabs then
local x = self:DistanceToString(abs(self.point0:x() - self.point1:x()), const.SlabSizeX, true)
local y = self:DistanceToString(abs(self.point0:y() - self.point1:y()), const.SlabSizeY, true)
local z = self:DistanceToString(self.vertical_distance, const.SlabSizeZ, true)
dist_string = string.format("x%s, y%s, z%s", x, y, z)
else
local h = self:DistanceToString(self.horizontal_distance)
local v = self:DistanceToString(self.vertical_distance)
dist_string = string.format("h%s, v%s", h, v)
end
local angle = atan(self.vertical_distance, self.horizontal_distance) / 60.0
local l = self:DistanceToString(self.line_distance, const.SlabSizeX)
if self.show_path then
local p = "No path"
if self.show_path and self.path_distance ~= -1 then
p = self:DistanceToString(self.path_distance, const.SlabSizeX)
end
self.label:SetText(string.format("%s (%s, %.1f°) : %s", l, dist_string, angle, p))
else
self.label:SetText(string.format("%s (%s, %.1f°)", l, dist_string, angle))
end
end
local function _GetZ(pt, ignore_walkables)
if ignore_walkables then
return terrain.GetHeight(pt)
else
return Max(GetWalkableZ(pt), terrain.GetSurfaceHeight(pt))
end
end
local function SetLineMesh(line, p_pstr)
line:SetMesh(p_pstr)
return line
end
function MeasureLine:Move(point0, point1)
self.point0 = point0:SetInvalidZ()
self.point1 = point1:SetInvalidZ()
local point0t = point(point0:x(), point0:y(), _GetZ(point0))
local point1t = point(point1:x(), point1:y(), _GetZ(point1))
local len = (point0t - point1t):Len()
local points_pstr = pstr("")
points_pstr:AppendVertex(point0t)
points_pstr:AppendVertex(point0t + point(0, 0, 5 * guim))
points_pstr:AppendVertex(point0t + point(0, 0, guim))
local steps = len / (guim / 2)
steps = steps > 0 and steps or 1
local distance = 0
local prev_point = point0t + point(0, 0, guim)
for i = 0, steps do
local pt = point0t + (point1t - point0t) * i / steps
if self.follow_terrain then
pt = point(pt:x(), pt:y(), _GetZ(pt, self.ignore_walkables))
distance = distance + (prev_point - point(0, 0, guim)):Dist(pt)
end
prev_point = pt + point(0, 0, guim)
points_pstr:AppendVertex(prev_point)
end
points_pstr:AppendVertex(point1t + point(0, 0, guim))
points_pstr:AppendVertex(point1t + point(0, 0, 5 * guim))
points_pstr:AppendVertex(point1t)
self.line = SetLineMesh(self.line, points_pstr)
-- update text label
local middlePoint = (point0 + point1) / 2
self.line:SetPos(middlePoint)
self.path:SetPos(middlePoint)
self.label:SetPos(middlePoint + point(0, 0, 4 * guim))
self.label:SetTextStyle("EditorTextBold")
self.line_distance = self.follow_terrain and distance or len
self.horizontal_distance = (point0t - point1t):Len2D()
self.vertical_distance = abs(point0t:z() - point1t:z())
self:UpdateText()
end
local function SetWalkableHeight(pt)
return pt:SetZ(_GetZ(pt))
end
-- will create a red line if *delayed* == true and a green line if *delayed* == false
function MeasureLine:SetPath(path, delayed)
local v_points_pstr = pstr("")
if path and #path > 0 then
local v_prev = {}
v_points_pstr:AppendVertex(SetWalkableHeight(self.point0), delayed and const.clrRed or const.clrGreen)
v_points_pstr:AppendVertex(SetWalkableHeight(self.point0))
local dist = 0
for i = 1, #path do
v_points_pstr:AppendVertex(SetWalkableHeight(path[i]))
if i > 1 then
dist = dist + path[i]:Dist(path[i - 1])
end
end
self.path_distance = dist
else
v_points_pstr:AppendVertex(self.point0, delayed and const.clrRed or const.clrGreen)
v_points_pstr:AppendVertex(self.point0)
self.path_distance = -1
end
self.path = SetLineMesh(self.path, v_points_pstr)
self:UpdateText()
end
function MeasureLine:UpdatePath()
if self.show_path then
local pts, delayed = pf.GetPosPath(self.point0, self.point1)
self:SetPath(pts, delayed)
self.path:SetEnumFlags(const.efVisible)
else
self:UpdateText()
self.path:ClearEnumFlags(const.efVisible)
end
end