From ee0ba351cedb15fb3309efaf4a680e688bd470ab Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 6 Mar 2025 19:31:59 +0100 Subject: [PATCH 01/32] Add plotting tools --- addons/common/XEH_PREP.hpp | 2 + .../common/functions/fnc_cartesianProduct.sqf | 47 ++++++++++ addons/common/functions/fnc_zip.sqf | 44 ++++++++++ addons/plotting/$PBOPREFIX$ | 1 + addons/plotting/CfgContext.hpp | 33 +++++++ addons/plotting/CfgEventHandlers.hpp | 17 ++++ addons/plotting/CfgFormatters.hpp | 31 +++++++ addons/plotting/XEH_PREP.hpp | 14 +++ addons/plotting/XEH_postInit.sqf | 12 +++ addons/plotting/XEH_preInit.sqf | 29 ++++++ addons/plotting/XEH_preStart.sqf | 3 + addons/plotting/config.cpp | 21 +++++ addons/plotting/functions/fnc_addPlot.sqf | 28 ++++++ addons/plotting/functions/fnc_clearPlots.sqf | 18 ++++ .../functions/fnc_compileFormatters.sqf | 54 ++++++++++++ addons/plotting/functions/fnc_drawLine.sqf | 49 +++++++++++ addons/plotting/functions/fnc_drawPlots.sqf | 88 +++++++++++++++++++ addons/plotting/functions/fnc_drawRadius.sqf | 59 +++++++++++++ .../plotting/functions/fnc_drawRectangle.sqf | 56 ++++++++++++ addons/plotting/functions/fnc_onDraw.sqf | 26 ++++++ addons/plotting/functions/fnc_onDraw3D.sqf | 29 ++++++ addons/plotting/functions/fnc_onKeyDown.sqf | 26 ++++++ addons/plotting/functions/fnc_onLoad.sqf | 42 +++++++++ .../functions/fnc_onMouseButtonDown.sqf | 52 +++++++++++ addons/plotting/functions/fnc_onUnload.sqf | 18 ++++ .../plotting/functions/fnc_selectPosition.sqf | 34 +++++++ .../plotting/functions/script_component.hpp | 1 + addons/plotting/initKeybinds.inc.sqf | 33 +++++++ addons/plotting/initSettings.inc.sqf | 10 +++ addons/plotting/script_component.hpp | 27 ++++++ addons/plotting/stringtable.xml | 33 +++++++ 31 files changed, 937 insertions(+) create mode 100644 addons/common/functions/fnc_cartesianProduct.sqf create mode 100644 addons/common/functions/fnc_zip.sqf create mode 100644 addons/plotting/$PBOPREFIX$ create mode 100644 addons/plotting/CfgContext.hpp create mode 100644 addons/plotting/CfgEventHandlers.hpp create mode 100644 addons/plotting/CfgFormatters.hpp create mode 100644 addons/plotting/XEH_PREP.hpp create mode 100644 addons/plotting/XEH_postInit.sqf create mode 100644 addons/plotting/XEH_preInit.sqf create mode 100644 addons/plotting/XEH_preStart.sqf create mode 100644 addons/plotting/config.cpp create mode 100644 addons/plotting/functions/fnc_addPlot.sqf create mode 100644 addons/plotting/functions/fnc_clearPlots.sqf create mode 100644 addons/plotting/functions/fnc_compileFormatters.sqf create mode 100644 addons/plotting/functions/fnc_drawLine.sqf create mode 100644 addons/plotting/functions/fnc_drawPlots.sqf create mode 100644 addons/plotting/functions/fnc_drawRadius.sqf create mode 100644 addons/plotting/functions/fnc_drawRectangle.sqf create mode 100644 addons/plotting/functions/fnc_onDraw.sqf create mode 100644 addons/plotting/functions/fnc_onDraw3D.sqf create mode 100644 addons/plotting/functions/fnc_onKeyDown.sqf create mode 100644 addons/plotting/functions/fnc_onLoad.sqf create mode 100644 addons/plotting/functions/fnc_onMouseButtonDown.sqf create mode 100644 addons/plotting/functions/fnc_onUnload.sqf create mode 100644 addons/plotting/functions/fnc_selectPosition.sqf create mode 100644 addons/plotting/functions/script_component.hpp create mode 100644 addons/plotting/initKeybinds.inc.sqf create mode 100644 addons/plotting/initSettings.inc.sqf create mode 100644 addons/plotting/script_component.hpp create mode 100644 addons/plotting/stringtable.xml diff --git a/addons/common/XEH_PREP.hpp b/addons/common/XEH_PREP.hpp index 339b180ce..24a91d4ce 100644 --- a/addons/common/XEH_PREP.hpp +++ b/addons/common/XEH_PREP.hpp @@ -1,4 +1,5 @@ PREP(canFire); +PREP(cartesianProduct); PREP(changeGroupSide); PREP(collapseTree); PREP(createZeus); @@ -73,3 +74,4 @@ PREP(showMessage); PREP(spawnLargeObject); PREP(teleportIntoVehicle); PREP(updateEditableObjects); +PREP(zip); diff --git a/addons/common/functions/fnc_cartesianProduct.sqf b/addons/common/functions/fnc_cartesianProduct.sqf new file mode 100644 index 000000000..bb9a56d53 --- /dev/null +++ b/addons/common/functions/fnc_cartesianProduct.sqf @@ -0,0 +1,47 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Returns the Cartesian product of the given sets. + * Similar to the itertools.product function in Python. + * + * Arguments: + * 0: N-dimensional sets + * + * Return Value: + * Cartesian product + * + * Example: + * [[[-1, 1], [-2, 2], [-3, 3]]] call zen_common_fnc_cartesianProduct + * => [[-1,-2,-3], [-1,-2,3], [-1,2,-3], [-1,2,3], [1,-2,-3], [1,-2,3], [1,2,-3], [1,2,3]] + * + * Public: No + */ + +params [["_sets", [], [[]]]]; + +private _fnc_product = { + params ["_setA", "_setB"]; + + private _result = []; + + { + private _a = _x; + + if !(_a isEqualType []) then { + _a = [_a]; + }; + + { + _result pushBack (_a + [_x]); + } forEach _setB; + } forEach _setA; + + _result +}; + +private _result = _sets select 0; +for "_i" from 1 to (count _sets - 1) do { + _result = [_result, _sets select _i] call _fnc_product; +}; + +_result diff --git a/addons/common/functions/fnc_zip.sqf b/addons/common/functions/fnc_zip.sqf new file mode 100644 index 000000000..d451247e6 --- /dev/null +++ b/addons/common/functions/fnc_zip.sqf @@ -0,0 +1,44 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Aggregate elements from each of the N arrays. The i-th array contains the i-th element from each of the argument arrays. + * Works similar to the Python zip(*iterables) function. + * + * Arguments: + * 0: Arrays to zip each containing a subarray > + * + * Return Value: + * Aggregate elements from each of the arrays. + * + * Example: + * [[[-1, -2, -3], [1, 2, 3]]] call zen_common_fnc_zip + * => [[-1, 1], [-2, 2], [-3, 3]] + * + * Public: No + */ + +params [["_iterables", [], [[]]]]; + +if (_iterables isEqualTo []) exitWith {[]}; + +private _minCount = -1; +{ + if (_minCount < 0 || count _x < _minCount) then { + _minCount = count _x; + }; +} forEach _iterables; + +if (_minCount <= 0) exitWith {[]}; + +private _result = []; + +for "_i" from 0 to (_minCount - 1) do { + private _temp = []; + { + _temp pushBack (_x select _i); + } forEach _iterables; + + _result pushBack _temp; +}; + +_result diff --git a/addons/plotting/$PBOPREFIX$ b/addons/plotting/$PBOPREFIX$ new file mode 100644 index 000000000..d5de66d5e --- /dev/null +++ b/addons/plotting/$PBOPREFIX$ @@ -0,0 +1 @@ +x\zen\addons\plotting diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp new file mode 100644 index 000000000..8a318b862 --- /dev/null +++ b/addons/plotting/CfgContext.hpp @@ -0,0 +1,33 @@ +class EGVAR(context_menu,actions) { + class Plots { + displayName = CSTRING(DisplayName); + icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + statement = QUOTE(call FUNC(selectPosition)); + args = "LINE"; + priority = 15; + + class MeasureDistance { + displayName = "MeasureDistance"; + icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + statement = QUOTE(call FUNC(selectPosition)); + args = "LINE"; + }; + class Radius { + displayName = "Radius"; + icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + statement = QUOTE(call FUNC(selectPosition)); + args = "RADIUS"; + }; + class Rectangle { + displayName = "Rectangle"; + icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + statement = QUOTE(call FUNC(selectPosition)); + args = "RECTANGLE"; + }; + class ClearPlots { + displayName = CSTRING(ClearPlots); + icon = "A3\3den\Data\Displays\Display3DEN\PanelLeft\entityList_delete_ca.paa"; + statement = QUOTE(call FUNC(clearPlots)); + }; + }; +}; diff --git a/addons/plotting/CfgEventHandlers.hpp b/addons/plotting/CfgEventHandlers.hpp new file mode 100644 index 000000000..f6503c247 --- /dev/null +++ b/addons/plotting/CfgEventHandlers.hpp @@ -0,0 +1,17 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; + +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; + +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp new file mode 100644 index 000000000..5a8718dfc --- /dev/null +++ b/addons/plotting/CfgFormatters.hpp @@ -0,0 +1,31 @@ +class GVAR(formatters) { + class Distance { + class Meter { + displayName = CSTRING(FormatterName_Distance_Meter); + formatter = QUOTE(format [ARR_2('%1 m',_value toFixed 1)]); + priority = 100; + }; + class Feet { + displayName = CSTRING(FormatterName_Distance_Feet); + formatter = QUOTE(format [ARR_2('%1 ft',(_value * 3.281) toFixed 1)]); + priority = 90; + }; + class NauticalMile { + displayName = CSTRING(FormatterName_Distance_NauticalMile); + formatter = QUOTE(format [ARR_2('%1 NM',(_value / 1852) toFixed 2)]); + priority = 80; + }; + }; + class Azimuth { + class Degree { + displayName = CSTRING(FormatterName_Azimuth_Degree); + formatter = QUOTE(format [ARR_2('%1°',_value toFixed 1)]); + priority = 100; + }; + class NATOMil { + displayName = CSTRING(FormatterName_Azimuth_NATOMil); + formatter = QUOTE(format [ARR_2('%1 mil',(_value * 17.7778) toFixed 0)]); + priority = 90; + }; + }; +}; diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp new file mode 100644 index 000000000..8b776eecb --- /dev/null +++ b/addons/plotting/XEH_PREP.hpp @@ -0,0 +1,14 @@ +PREP(addPlot); +PREP(clearPlots); +PREP(compileFormatters); +PREP(drawLine); +PREP(drawPlots); +PREP(drawRadius); +PREP(drawRectangle); +PREP(onDraw); +PREP(onDraw3D); +PREP(onKeyDown); +PREP(onLoad); +PREP(onMouseButtonDown); +PREP(onUnload); +PREP(selectPosition); diff --git a/addons/plotting/XEH_postInit.sqf b/addons/plotting/XEH_postInit.sqf new file mode 100644 index 000000000..cf18ef9b4 --- /dev/null +++ b/addons/plotting/XEH_postInit.sqf @@ -0,0 +1,12 @@ +#include "script_component.hpp" + +if (hasInterface) then { + // All plots ordered by creation time, last is newest + GVAR(plots) = []; + + ["zen_curatorDisplayLoaded", LINKFUNC(onLoad)] call CBA_fnc_addEventHandler; + ["zen_curatorDisplayUnloaded", LINKFUNC(onUnload)] call CBA_fnc_addEventHandler; + + [QGVAR(plotAdded), LINKFUNC(addPlot)] call CBA_fnc_addEventHandler; + [QGVAR(plotsCleared), LINKFUNC(clearPlots)] call CBA_fnc_addEventHandler; +}; diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf new file mode 100644 index 000000000..a3d662e63 --- /dev/null +++ b/addons/plotting/XEH_preInit.sqf @@ -0,0 +1,29 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +if (hasInterface) then { + GVAR(activePlot) = []; + GVAR(draw3DAdded) = false; + GVAR(inputHandlersAdded) = false; + + GVAR(plotTypes) = createHashMapFromArray [ + ["LINE", LINKFUNC(drawLine)], + ["RADIUS", LINKFUNC(drawRadius)], + ["RECTANGLE", LINKFUNC(drawRectangle)] + ]; + + GVAR(currentDistanceFormatter) = 0; + GVAR(currentAzimuthFormatter) = 0; +}; + +call FUNC(compileFormatters); + +#include "initSettings.inc.sqf" +#include "initKeybinds.inc.sqf" + +ADDON = true; diff --git a/addons/plotting/XEH_preStart.sqf b/addons/plotting/XEH_preStart.sqf new file mode 100644 index 000000000..022888575 --- /dev/null +++ b/addons/plotting/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/plotting/config.cpp b/addons/plotting/config.cpp new file mode 100644 index 000000000..991023a13 --- /dev/null +++ b/addons/plotting/config.cpp @@ -0,0 +1,21 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"zen_common", "zen_context_menu"}; + author = ECSTRING(main,Author); + authors[] = {"Timi007"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +PRELOAD_ADDONS; + +#include "CfgEventHandlers.hpp" +#include "CfgContext.hpp" +#include "CfgFormatters.hpp" diff --git a/addons/plotting/functions/fnc_addPlot.sqf b/addons/plotting/functions/fnc_addPlot.sqf new file mode 100644 index 000000000..093465527 --- /dev/null +++ b/addons/plotting/functions/fnc_addPlot.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Adds a new plot to be drawn. + * + * Arguments: + * 0: Type of plot + * 1: Start position ASL or attached object + * 2: End position ASL or attached object + * + * Return Value: + * Index of new plot + * + * Example: + * ["RADIUS", player, [100, 100, 10]] call zen_plotting_fnc_addPlot + * + * Public: No + */ + +params [ + ["_type", "LINE", [""]], + ["_startPos", [0, 0, 0], [[], objNull], [3]], + ["_endPos", [0, 0, 0], [[], objNull], [3]] +]; + +if !(_type in GVAR(plotTypes)) exitWith {-1}; + +GVAR(plots) pushBack [_type, _startPos, _endPos]; diff --git a/addons/plotting/functions/fnc_clearPlots.sqf b/addons/plotting/functions/fnc_clearPlots.sqf new file mode 100644 index 000000000..fc1175238 --- /dev/null +++ b/addons/plotting/functions/fnc_clearPlots.sqf @@ -0,0 +1,18 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Clears of plots. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call zen_plotting_fnc_clearPlots + * + * Public: No + */ + +GVAR(plots) = []; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf new file mode 100644 index 000000000..cbb6bf02e --- /dev/null +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -0,0 +1,54 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Compiles formatters from config and saves them in missionNamespace. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call zen_plotting_fnc_compileFormatters + * + * Public: No + */ + +private _fnc_getFormatters = { + params ["_cfgFormatters"]; + + private _formatters = []; + { + private _entryConfig = _x; + + private _formatterName = configName _entryConfig; + private _displayName = getText (_entryConfig >> "displayName"); + + private _formatterString = getText (_entryConfig >> "formatter"); + if (_formatterString isNotEqualTo "") then { + _formatterString = "params ['_value']; " + _formatterString; + }; + private _formatter = compile _formatterString; + + private _priority = getNumber (_entryConfig >> "priority"); + + private _formatterEntry = [ + _formatterName, + _displayName, + _formatter, + _priority + ]; + + _formatters pushBack _formatterEntry; + } forEach configProperties [_cfgFormatters, "isClass _x", true]; + + [_formatters, 3, false] call CBA_fnc_sortNestedArray +}; + +private _cfgFormatters = configFile >> QGVAR(formatters); +private _distanceFormatters = [_cfgFormatters >> "Distance"] call _fnc_getFormatters; +private _azimuthFormatters = [_cfgFormatters >> "Azimuth"] call _fnc_getFormatters; + +missionNamespace setVariable [QGVAR(distanceFormatters), _distanceFormatters]; +missionNamespace setVariable [QGVAR(azimuthFormatters), _azimuthFormatters]; diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf new file mode 100644 index 000000000..dbd9a5949 --- /dev/null +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -0,0 +1,49 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a line plot in 3D or on the map. + * + * Arguments: + * 0: Start position ASL + * 1: End position ASL + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 1: Azimuth formatter + * 3: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {_this toFixed 1}]] call zen_plotting_fnc_drawLine + * + * Public: No + */ + +params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_mapCtrl", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + +private _distance = _startPos vectorDistance _endPos; + +private _azimuthToStart = _endPos getDir _startPos; +private _azimuthToEnd = _startPos getDir _endPos; + +private _startText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToStart call _fnc_azimuthFormatter]; +private _endText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToEnd call _fnc_azimuthFormatter]; + +if (isNull _mapCtrl) then { // 3D + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle, _startText]; + drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _endText]; +} else { // Map + _mapCtrl drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, _startText]; + _mapCtrl drawLine [_startPos, _endPos, _color, _lineWidth]; + _mapCtrl drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _endText]; +}; diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf new file mode 100644 index 000000000..b798e65ae --- /dev/null +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -0,0 +1,88 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws all placed plots and currently active one in 3D or on the map. + * + * Arguments: + * 0: Permanent plots in format [type, startPosASLOrObj, endPosASLOrObj] + * 1: Currently active plot in format [type, startPosASLOrObj] + * 2: Zeus map (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[["LINE", [0, 0, 0], player]], ["LINE", [100, 100, 0]]] call zen_plotting_fnc_drawPlots + * + * Public: No + */ + +params ["_plots", ["_activePlot", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; + +private _drawIn3D = isNull _ctrlMap; + +private _d = 0; +private _scale = 0; +private _screenPos = []; +private _color = GVAR(color); +private _icon = "\a3\ui_f\data\map\markerbrushes\cross_ca.paa"; +private _angle = 0; +private _lineWidth = 5; + +private _formatters = [ + (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 2, + (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 2 +]; + +// Format active plot as permanent plot with mouse position as end position +private _activePlotWithMouseEndPos = []; +if (_activePlot isNotEqualTo []) then { + _activePlot params ["_type", "_startPosOrObj"]; + + private _endPos = if (_drawIn3D) then { + [EGVAR(common,mousePos), 2] call EFUNC(common,getPosFromScreen) + } else { + private _pos2D = _ctrlMap ctrlMapScreenToWorld getMousePosition; + _pos2D set [2, getTerrainHeightASL _pos2D]; + _pos2D + }; + + _activePlotWithMouseEndPos = [[_type, _startPosOrObj, _endPos]]; +}; + +// Draw all plots +{ + _x params ["_type", "_startPosOrObj", "_endPosOrObj"]; + + private _startPos = if (_startPosOrObj isEqualType objNull) then { + if (isNull _startPosOrObj) then {continue}; + + getPosASLVisual _startPosOrObj + } else { + _startPosOrObj + }; + + private _endPos = if (_endPosOrObj isEqualType objNull) then { + if (isNull _endPosOrObj) then {continue}; + + getPosASLVisual _endPosOrObj + } else { + _endPosOrObj + }; + + if (_drawIn3D) then { + _scale = ICON_SCALE; + } else { + _scale = MAP_ICON_SCALE; + }; + + if (_scale < 0.01) then { + continue; + }; + + private _visualProperties = [_icon, _color, _scale, _angle, _lineWidth]; + private _drawArgs = [_startPos, _endPos, _visualProperties, _formatters, _ctrlMap]; + if (_type in GVAR(plotTypes)) then { + _drawArgs call (GVAR(plotTypes) get _type); + }; +} forEach (_activePlotWithMouseEndPos + _plots); diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf new file mode 100644 index 000000000..bf61f20f1 --- /dev/null +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -0,0 +1,59 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a radius circle plot in 3D or on the map. + * + * Arguments: + * 0: Start position ASL + * 1: End position ASL + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 1: Azimuth formatter + * 3: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {_this toFixed 1}]] call zen_plotting_fnc_drawRadius + * + * Public: No + */ + +params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + +private _radius = _startPos vectorDistance _endPos; +private _azimuth = _startPos getDir _endPos; + +private _text = format ["%1 - %2", _radius call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter]; + +if (isNull _ctrlMap) then { // 3D + private _centerAGL = ASLToAGL _startPos; + private _endPosAGL = ASLToAGL _endPos; + drawIcon3D [_icon, _color, _centerAGL, _scale, _scale, _angle]; + drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; + drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, _text]; + + private _count = CIRCLE_DOTS_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_DOTS_SPACING); + private _factor = 360 / _count; + + for "_i" from 0 to (_count - 1) do { + private _phi = _i * _factor; + private _offset = [_radius * cos _phi, _radius * sin _phi, 0]; + + drawIcon3D [_icon, _color, _centerAGL vectorAdd _offset, CIRCLE_DOTS_SCALE, CIRCLE_DOTS_SCALE, 0]; + }; +} else { // Map + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; + _ctrlMap drawLine [_startPos, _endPos, _color, _lineWidth]; + _ctrlMap drawEllipse [_startPos, _radius, _radius, 0, _color, ""]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _text]; +}; diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf new file mode 100644 index 000000000..dcd60115a --- /dev/null +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -0,0 +1,56 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Draws a rectangle plot in 3D or on the map. + * + * Arguments: + * 0: Start position ASL + * 1: End position ASL + * 2: Visual properties + * 0: Icon + * 1: Color RGBA + * 2: Scale + * 3: Angle + * 4: Line width + * 3: Formatters + * 0: Distance formatter + * 3: Map control (default: Draw in 3D) + * + * Return Value: + * None + * + * Example: + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}]] call zen_plotting_fnc_drawRectangle + * + * Public: No + */ + +params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +_visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_formatters params ["_fnc_distanceFormatter"]; + +private _offset = _endPos vectorDiff _startPos; +_offset params ["_a", "_b", "_c"]; + +private _text = format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_distanceFormatter, _b call _fnc_distanceFormatter, _c call _fnc_distanceFormatter]; + +if (isNull _ctrlMap) then { // 3D + private _startPosAGL = ASLToAGL _startPos; + private _endPosAGL = ASLToAGL _endPos; + + drawIcon3D [_icon, _color, _startPosAGL, _scale, _scale, _angle]; + + private _corners = [[[_startPosAGL, _endPosAGL]] call EFUNC(common,zip)] call EFUNC(common,cartesianProduct); + private _count = count _corners; + + for "_i" from 0 to (_count - 1) do { + drawLine3D [_corners select _i, _corners select ((_i + 1) % _count), _color, _lineWidth]; + }; + + drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, _text]; +} else { // Map + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; + private _center = (_startPos vectorAdd _endPos) vectorMultiply 0.5; + _ctrlMap drawRectangle [_center, _a / 2, _b / 2, 0, _color, ""]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _text]; +}; diff --git a/addons/plotting/functions/fnc_onDraw.sqf b/addons/plotting/functions/fnc_onDraw.sqf new file mode 100644 index 000000000..ad71549a0 --- /dev/null +++ b/addons/plotting/functions/fnc_onDraw.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Author: Timi007 + * Handles drawing the plots on the Zeus map. Is only called when map is open. + * + * Arguments: + * 0: Zeus map + * + * Return Value: + * None + * + * Example: + * [_ctrlMap] call zen_plotting_fnc_onDraw + * + * Public: No + */ + +BEGIN_COUNTER(onDraw); + +params ["_ctrlMap"]; + +if (dialog || {call EFUNC(common,isInScreenshotMode)}) exitWith {}; // Dialog is open or HUD is hidden + +[GVAR(plots), GVAR(activePlot), _ctrlMap] call FUNC(drawPlots); + +END_COUNTER(onDraw); diff --git a/addons/plotting/functions/fnc_onDraw3D.sqf b/addons/plotting/functions/fnc_onDraw3D.sqf new file mode 100644 index 000000000..8f6a493dc --- /dev/null +++ b/addons/plotting/functions/fnc_onDraw3D.sqf @@ -0,0 +1,29 @@ +#include "script_component.hpp" +/* + * Author: Timi007 + * Handles drawing the plots in Zeus 3D. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call zen_plotting_fnc_onDraw3D + * + * Public: No + */ + +BEGIN_COUNTER(onDraw3D); + +if ( + isNull (findDisplay IDD_RSCDISPLAYCURATOR) || // We are in not Zeus + {!isNull (findDisplay IDD_INTERRUPT)} || // Pause menu is opened + {dialog} || // We have a dialog open + {call EFUNC(common,isInScreenshotMode)} // HUD is hidden +) exitWith {}; + +[GVAR(plots), GVAR(activePlot)] call FUNC(drawPlots); + +END_COUNTER(onDraw3D); diff --git a/addons/plotting/functions/fnc_onKeyDown.sqf b/addons/plotting/functions/fnc_onKeyDown.sqf new file mode 100644 index 000000000..86117c264 --- /dev/null +++ b/addons/plotting/functions/fnc_onKeyDown.sqf @@ -0,0 +1,26 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles canceling the currently active plot. + * + * Arguments: + * 0: Display or control the EH is attached to (ignored) + * 1: Key code + * + * Return Value: + * Handled + * + * Example: + * [_display, 1] call zen_plotting_fnc_onKeyDown + * + * Public: No + */ + +params ["", "_keyCode"]; + +if (GVAR(activePlot) isEqualTo [] || {_keyCode != DIK_ESCAPE}) exitWith {false}; + +TRACE_1("Cancel adding plot",_this); +GVAR(activePlot) = []; + +true diff --git a/addons/plotting/functions/fnc_onLoad.sqf b/addons/plotting/functions/fnc_onLoad.sqf new file mode 100644 index 000000000..143b1b092 --- /dev/null +++ b/addons/plotting/functions/fnc_onLoad.sqf @@ -0,0 +1,42 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Function triggered every time the Zeus/Curator display is opened. + * Adds the draw event handlers to display static and currently active plots in 3D and on the map. + * + * Arguments: + * 0: Zeus Display + * + * Return Value: + * None + * + * Example: + * [_display] call zen_plotting_fnc_onLoad + * + * Public: No + */ + +params ["_display"]; +TRACE_1("Zeus display opened",_display); + +GVAR(activePlot) = []; + +if (!GVAR(draw3DAdded)) then { + LOG("Adding Draw3D."); + addMissionEventHandler ["Draw3D", {call FUNC(onDraw3D)}]; + GVAR(draw3DAdded) = true; +}; + +if (!GVAR(inputHandlersAdded)) then { + LOG("Adding input handlers."); + _display displayAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; + _display displayAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; + GVAR(inputHandlersAdded) = true; +}; + +// MapDraw EH needs to be added every time the Zeus display is opened. +LOG("Adding map draw."); +private _ctrlMap = _display displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; +_ctrlMap ctrlAddEventHandler ["Draw", {call FUNC(onDraw)}]; +_ctrlMap ctrlAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; +_ctrlMap ctrlAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf new file mode 100644 index 000000000..22db4a28f --- /dev/null +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -0,0 +1,52 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles the mouse button event when user wants to add a plot in 3D or on the map. + * + * Arguments: + * 0: Display or control the EH is attached to + * 1: Mouse button pressen + * + * Return Value: + * None + * + * Example: + * [_display, 1] call zen_plotting_fnc_onMouseButtonDown + * + * Public: No + */ + +params ["_displayOrControl", "_button"]; + +if (GVAR(activePlot) isEqualTo [] || {_button != 0}) exitWith {}; +TRACE_1("onMouseButtonDown",_this); + +if (call EFUNC(common,isCursorOnMouseArea)) then { + curatorMouseOver params ["_type", "_object"]; + curatorSelected params ["_objects"]; + + private _endPosOrObj = switch (true) do { + case (_type isEqualTo "OBJECT"): {_object}; + case (count _objects isEqualTo 1): {_objects select 0}; + case (visibleMap): { + private _ctrlMap = if (_displayOrControl isEqualType controlNull) then { + _displayOrControl + } else { + _displayOrControl displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP + }; + + private _pos2D = _ctrlMap ctrlMapScreenToWorld getMousePosition; + _pos2D set [2, getTerrainHeightASL _pos2D]; + _pos2D + }; + default {[EGVAR(common,mousePos), 2] call EFUNC(common,getPosFromScreen)}; + }; + + GVAR(activePlot) params ["_type", "_startPosOrObj"]; + + // Add current active plot to permanent ones + TRACE_4("Add plot",_type,_startPosOrObj,_endPosOrObj,_this); + [QGVAR(plotAdded), [_type, _startPosOrObj, _endPosOrObj]] call CBA_fnc_localEvent; +}; + +GVAR(activePlot) = []; diff --git a/addons/plotting/functions/fnc_onUnload.sqf b/addons/plotting/functions/fnc_onUnload.sqf new file mode 100644 index 000000000..e49cba9eb --- /dev/null +++ b/addons/plotting/functions/fnc_onUnload.sqf @@ -0,0 +1,18 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Handles unloading the Zeus Display. + * + * Arguments: + * 0: Display + * + * Return Value: + * None + * + * Example: + * [DISPLAY] call zen_plotting_fnc_onUnload + * + * Public: No + */ + +GVAR(activePlot) = []; diff --git a/addons/plotting/functions/fnc_selectPosition.sqf b/addons/plotting/functions/fnc_selectPosition.sqf new file mode 100644 index 000000000..57847590f --- /dev/null +++ b/addons/plotting/functions/fnc_selectPosition.sqf @@ -0,0 +1,34 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Called from context menu to select the start position of the plot. + * + * Arguments: + * 0: Context menu position ASL + * 1: Selected objects + * 2: Selected groups + * 3: Selected waypoints + * 4: Selected markers + * 5: Hovered entity + * 6: Plot type + * + * Return Value: + * None + * + * Example: + * [[100, 100, 5], [], [], [], [], objNull, "LINE"] call zen_plotting_fnc_selectPosition + * + * Public: No + */ + +params ["_position", "_objects", "_groups", "_waypoints", "_markers", "_hoveredEntity", "_type"]; + +private _posOrObj = switch (true) do { + case (_hoveredEntity isEqualType objNull && {!isNull _hoveredEntity}): {_hoveredEntity}; + case (count _objects isEqualTo 1): {_objects select 0}; + default {_position}; +}; + +TRACE_1("start pos",_posOrObj); + +GVAR(activePlot) = [_type, _posOrObj]; diff --git a/addons/plotting/functions/script_component.hpp b/addons/plotting/functions/script_component.hpp new file mode 100644 index 000000000..2680773dd --- /dev/null +++ b/addons/plotting/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\x\zen\addons\plotting\script_component.hpp" diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf new file mode 100644 index 000000000..63f769ea7 --- /dev/null +++ b/addons/plotting/initKeybinds.inc.sqf @@ -0,0 +1,33 @@ +private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; + +[ + _category, + QGVAR(toggleDistanceFormat), + LSTRING(ToggleDistanceFormat), + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(distanceFormatters); + GVAR(currentDistanceFormatter) = (GVAR(currentDistanceFormatter) + 1) % _numFormatters; + + true + }; + }, + {}, + [DIK_R, [false, false, false]] // Default: R +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(toggleAzimuthFormat), + LSTRING(ToggleAzimuthFormat), + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(azimuthFormatters); + GVAR(currentAzimuthFormatter) = (GVAR(currentAzimuthFormatter) + 1) % _numFormatters; + + true + }; + }, + {}, + [DIK_TAB, [false, false, false]] // Default: Tab +] call CBA_fnc_addKeybind; diff --git a/addons/plotting/initSettings.inc.sqf b/addons/plotting/initSettings.inc.sqf new file mode 100644 index 000000000..bbb620817 --- /dev/null +++ b/addons/plotting/initSettings.inc.sqf @@ -0,0 +1,10 @@ +private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; + +[ + QGVAR(color), + "COLOR", + localize "str_3den_marker_attribute_color_displayname", + _category, + [0.9, 0.9, 0, 1], + 0 +] call CBA_fnc_addSetting; diff --git a/addons/plotting/script_component.hpp b/addons/plotting/script_component.hpp new file mode 100644 index 000000000..5822d8ed5 --- /dev/null +++ b/addons/plotting/script_component.hpp @@ -0,0 +1,27 @@ +#define COMPONENT plotting +#define COMPONENT_BEAUTIFIED Plotting +#include "\x\zen\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_PLOTTING + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_PLOTTING + #define DEBUG_SETTINGS DEBUG_SETTINGS_PLOTTING +#endif + +#include "\x\zen\addons\main\script_macros.hpp" + +#include "\a3\ui_f\hpp\defineDIKCodes.inc" +#include "\x\zen\addons\common\defineResinclDesign.inc" + +#define ICON_SCALE 1 +#define MAP_ICON_SCALE 24 + +#define CIRCLE_DOTS_SCALE 0.5 +#define CIRCLE_DOTS_MIN 6 +#define CIRCLE_DOTS_SPACING 5 diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml new file mode 100644 index 000000000..91c71919d --- /dev/null +++ b/addons/plotting/stringtable.xml @@ -0,0 +1,33 @@ + + + + + Plotting + Plotten + + + Meter + Meter + + + Foot + Fuß + + + Nautical mile + Nautische Meile + + + Degree + Grad + + + NATO mil + NATO Strich + + + Clear plots + Plots entfernen + + + From d826afcfe8515f4cb5be8c76198b21c2e0aaf6fb Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 6 Mar 2025 19:36:10 +0100 Subject: [PATCH 02/32] Fix return syntax --- addons/plotting/functions/fnc_addPlot.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/plotting/functions/fnc_addPlot.sqf b/addons/plotting/functions/fnc_addPlot.sqf index 093465527..051e490fc 100644 --- a/addons/plotting/functions/fnc_addPlot.sqf +++ b/addons/plotting/functions/fnc_addPlot.sqf @@ -25,4 +25,4 @@ params [ if !(_type in GVAR(plotTypes)) exitWith {-1}; -GVAR(plots) pushBack [_type, _startPos, _endPos]; +GVAR(plots) pushBack [_type, _startPos, _endPos] From 6b0e3bdd34789d23f418999ea49b2aaa82d39716 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 6 Mar 2025 23:58:46 +0100 Subject: [PATCH 03/32] Fix attaching plots to objects --- addons/plotting/functions/fnc_onDraw3D.sqf | 8 ++++---- addons/plotting/functions/fnc_onMouseButtonDown.sqf | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/plotting/functions/fnc_onDraw3D.sqf b/addons/plotting/functions/fnc_onDraw3D.sqf index 8f6a493dc..4c06ac1f1 100644 --- a/addons/plotting/functions/fnc_onDraw3D.sqf +++ b/addons/plotting/functions/fnc_onDraw3D.sqf @@ -18,10 +18,10 @@ BEGIN_COUNTER(onDraw3D); if ( - isNull (findDisplay IDD_RSCDISPLAYCURATOR) || // We are in not Zeus - {!isNull (findDisplay IDD_INTERRUPT)} || // Pause menu is opened - {dialog} || // We have a dialog open - {call EFUNC(common,isInScreenshotMode)} // HUD is hidden + isNull (findDisplay IDD_RSCDISPLAYCURATOR) // We are in not Zeus + || {!isNull (findDisplay IDD_INTERRUPT)} // Pause menu is opened + || {dialog} // We have a dialog open + || {call EFUNC(common,isInScreenshotMode)} // HUD is hidden ) exitWith {}; [GVAR(plots), GVAR(activePlot)] call FUNC(drawPlots); diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf index 22db4a28f..0ca325b7c 100644 --- a/addons/plotting/functions/fnc_onMouseButtonDown.sqf +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -23,11 +23,9 @@ TRACE_1("onMouseButtonDown",_this); if (call EFUNC(common,isCursorOnMouseArea)) then { curatorMouseOver params ["_type", "_object"]; - curatorSelected params ["_objects"]; private _endPosOrObj = switch (true) do { case (_type isEqualTo "OBJECT"): {_object}; - case (count _objects isEqualTo 1): {_objects select 0}; case (visibleMap): { private _ctrlMap = if (_displayOrControl isEqualType controlNull) then { _displayOrControl From 7b31bb2336efdbd439e412f4cf35a60b1b0197aa Mon Sep 17 00:00:00 2001 From: Timi007 Date: Fri, 7 Mar 2025 00:34:10 +0100 Subject: [PATCH 04/32] Use lines for radius --- addons/plotting/functions/fnc_drawPlots.sqf | 20 +++++++------------- addons/plotting/functions/fnc_drawRadius.sqf | 13 +++++++++---- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf index b798e65ae..d55261a4b 100644 --- a/addons/plotting/functions/fnc_drawPlots.sqf +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -24,10 +24,6 @@ private _drawIn3D = isNull _ctrlMap; private _d = 0; private _scale = 0; private _screenPos = []; -private _color = GVAR(color); -private _icon = "\a3\ui_f\data\map\markerbrushes\cross_ca.paa"; -private _angle = 0; -private _lineWidth = 5; private _formatters = [ (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 2, @@ -54,20 +50,18 @@ if (_activePlot isNotEqualTo []) then { { _x params ["_type", "_startPosOrObj", "_endPosOrObj"]; - private _startPos = if (_startPosOrObj isEqualType objNull) then { + private _startPos = _startPosOrObj; + if (_startPosOrObj isEqualType objNull) then { if (isNull _startPosOrObj) then {continue}; - getPosASLVisual _startPosOrObj - } else { - _startPosOrObj + _startPos = getPosASLVisual _startPosOrObj; }; - private _endPos = if (_endPosOrObj isEqualType objNull) then { + private _endPos = _endPosOrObj; + if (_endPosOrObj isEqualType objNull) then { if (isNull _endPosOrObj) then {continue}; - getPosASLVisual _endPosOrObj - } else { - _endPosOrObj + _endPos = getPosASLVisual _endPosOrObj; }; if (_drawIn3D) then { @@ -80,7 +74,7 @@ if (_activePlot isNotEqualTo []) then { continue; }; - private _visualProperties = [_icon, _color, _scale, _angle, _lineWidth]; + private _visualProperties = [ICON, GVAR(color), _scale, ICON_ANGLE, LINEWIDTH]; private _drawArgs = [_startPos, _endPos, _visualProperties, _formatters, _ctrlMap]; if (_type in GVAR(plotTypes)) then { _drawArgs call (GVAR(plotTypes) get _type); diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index bf61f20f1..4e3151def 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -42,14 +42,19 @@ if (isNull _ctrlMap) then { // 3D drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, _text]; - private _count = CIRCLE_DOTS_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_DOTS_SPACING); + private _count = CIRCLE_EDGES_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_RESOLUTION); private _factor = 360 / _count; + private _offsets = []; for "_i" from 0 to (_count - 1) do { - private _phi = _i * _factor; - private _offset = [_radius * cos _phi, _radius * sin _phi, 0]; + private _phi = _i * _factor - _azimuth; + _offsets pushBack [_radius * cos _phi, _radius * sin _phi, 0]; + }; - drawIcon3D [_icon, _color, _centerAGL vectorAdd _offset, CIRCLE_DOTS_SCALE, CIRCLE_DOTS_SCALE, 0]; + for "_i" from 0 to (_count - 1) do { + private _pos1 = _centerAGL vectorAdd (_offsets select _i); + private _pos2 = _centerAGL vectorAdd (_offsets select ((_i + 1) % _count)); + drawLine3D [_pos1, _pos2, _color, _lineWidth]; }; } else { // Map _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; From ac1e52183fa9528c2e00f8845c9988c3f969ba3c Mon Sep 17 00:00:00 2001 From: Timi007 Date: Fri, 7 Mar 2025 01:12:28 +0100 Subject: [PATCH 05/32] Fix rectangular cuboid --- .../plotting/functions/fnc_drawRectangle.sqf | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index dcd60115a..d27a87436 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -35,19 +35,28 @@ _offset params ["_a", "_b", "_c"]; private _text = format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_distanceFormatter, _b call _fnc_distanceFormatter, _c call _fnc_distanceFormatter]; if (isNull _ctrlMap) then { // 3D - private _startPosAGL = ASLToAGL _startPos; - private _endPosAGL = ASLToAGL _endPos; + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; - drawIcon3D [_icon, _color, _startPosAGL, _scale, _scale, _angle]; + _startPos params ["_x1", "_y1", "_z1"]; + _endPos params ["_x2", "_y2", "_z2"]; - private _corners = [[[_startPosAGL, _endPosAGL]] call EFUNC(common,zip)] call EFUNC(common,cartesianProduct); - private _count = count _corners; + private _edges = [ + [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]], // Rectangle same height as start pos + [[_x1, _y1, _z2], [_x2, _y1, _z2], [_x2, _y2, _z2], [_x1, _y2, _z2], [_x1, _y1, _z2]], // Rectangle same height as end pos + // Connections from start to end height + [[_x1, _y1, _z1], [_x1, _y1, _z2]], + [[_x2, _y1, _z1], [_x2, _y1, _z2]], + [[_x2, _y2, _z1], [_x2, _y2, _z2]], + [[_x1, _y2, _z1], [_x1, _y2, _z2]] + ]; - for "_i" from 0 to (_count - 1) do { - drawLine3D [_corners select _i, _corners select ((_i + 1) % _count), _color, _lineWidth]; - }; + { + for "_i" from 0 to (count _x - 2) do { + drawLine3D [ASLToAGL (_x select _i), ASLToAGL (_x select (_i + 1)), _color, _lineWidth]; + }; + } forEach _edges; - drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, _text]; + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _text]; } else { // Map _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; private _center = (_startPos vectorAdd _endPos) vectorMultiply 0.5; From 32e6f821a2f87056521489cfba937a968e3c31da Mon Sep 17 00:00:00 2001 From: Timi007 Date: Fri, 7 Mar 2025 01:13:13 +0100 Subject: [PATCH 06/32] Remove obsolete files --- addons/common/XEH_PREP.hpp | 2 - .../common/functions/fnc_cartesianProduct.sqf | 47 ------------------- addons/common/functions/fnc_zip.sqf | 44 ----------------- 3 files changed, 93 deletions(-) delete mode 100644 addons/common/functions/fnc_cartesianProduct.sqf delete mode 100644 addons/common/functions/fnc_zip.sqf diff --git a/addons/common/XEH_PREP.hpp b/addons/common/XEH_PREP.hpp index 24a91d4ce..339b180ce 100644 --- a/addons/common/XEH_PREP.hpp +++ b/addons/common/XEH_PREP.hpp @@ -1,5 +1,4 @@ PREP(canFire); -PREP(cartesianProduct); PREP(changeGroupSide); PREP(collapseTree); PREP(createZeus); @@ -74,4 +73,3 @@ PREP(showMessage); PREP(spawnLargeObject); PREP(teleportIntoVehicle); PREP(updateEditableObjects); -PREP(zip); diff --git a/addons/common/functions/fnc_cartesianProduct.sqf b/addons/common/functions/fnc_cartesianProduct.sqf deleted file mode 100644 index bb9a56d53..000000000 --- a/addons/common/functions/fnc_cartesianProduct.sqf +++ /dev/null @@ -1,47 +0,0 @@ -#include "script_component.hpp" -/* - * Authors: Timi007 - * Returns the Cartesian product of the given sets. - * Similar to the itertools.product function in Python. - * - * Arguments: - * 0: N-dimensional sets - * - * Return Value: - * Cartesian product - * - * Example: - * [[[-1, 1], [-2, 2], [-3, 3]]] call zen_common_fnc_cartesianProduct - * => [[-1,-2,-3], [-1,-2,3], [-1,2,-3], [-1,2,3], [1,-2,-3], [1,-2,3], [1,2,-3], [1,2,3]] - * - * Public: No - */ - -params [["_sets", [], [[]]]]; - -private _fnc_product = { - params ["_setA", "_setB"]; - - private _result = []; - - { - private _a = _x; - - if !(_a isEqualType []) then { - _a = [_a]; - }; - - { - _result pushBack (_a + [_x]); - } forEach _setB; - } forEach _setA; - - _result -}; - -private _result = _sets select 0; -for "_i" from 1 to (count _sets - 1) do { - _result = [_result, _sets select _i] call _fnc_product; -}; - -_result diff --git a/addons/common/functions/fnc_zip.sqf b/addons/common/functions/fnc_zip.sqf deleted file mode 100644 index d451247e6..000000000 --- a/addons/common/functions/fnc_zip.sqf +++ /dev/null @@ -1,44 +0,0 @@ -#include "script_component.hpp" -/* - * Authors: Timi007 - * Aggregate elements from each of the N arrays. The i-th array contains the i-th element from each of the argument arrays. - * Works similar to the Python zip(*iterables) function. - * - * Arguments: - * 0: Arrays to zip each containing a subarray > - * - * Return Value: - * Aggregate elements from each of the arrays. - * - * Example: - * [[[-1, -2, -3], [1, 2, 3]]] call zen_common_fnc_zip - * => [[-1, 1], [-2, 2], [-3, 3]] - * - * Public: No - */ - -params [["_iterables", [], [[]]]]; - -if (_iterables isEqualTo []) exitWith {[]}; - -private _minCount = -1; -{ - if (_minCount < 0 || count _x < _minCount) then { - _minCount = count _x; - }; -} forEach _iterables; - -if (_minCount <= 0) exitWith {[]}; - -private _result = []; - -for "_i" from 0 to (_minCount - 1) do { - private _temp = []; - { - _temp pushBack (_x select _i); - } forEach _iterables; - - _result pushBack _temp; -}; - -_result From 1e0e028da55fdda7d78f74474b417ac891a8a543 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Fri, 7 Mar 2025 17:06:09 +0100 Subject: [PATCH 07/32] Add icons and localized text --- addons/plotting/CfgContext.hpp | 18 ++++++------ addons/plotting/CfgFormatters.hpp | 5 ---- .../functions/fnc_compileFormatters.sqf | 2 -- addons/plotting/functions/fnc_drawPlots.sqf | 6 ++-- addons/plotting/stringtable.xml | 26 ++++++------------ addons/plotting/ui/cuboid.paa | Bin 0 -> 5625 bytes addons/plotting/ui/radius.paa | Bin 0 -> 5625 bytes addons/plotting/ui/ruler.paa | Bin 0 -> 2897 bytes 8 files changed, 21 insertions(+), 36 deletions(-) create mode 100644 addons/plotting/ui/cuboid.paa create mode 100644 addons/plotting/ui/radius.paa create mode 100644 addons/plotting/ui/ruler.paa diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp index 8a318b862..4007b0d73 100644 --- a/addons/plotting/CfgContext.hpp +++ b/addons/plotting/CfgContext.hpp @@ -1,26 +1,26 @@ class EGVAR(context_menu,actions) { class Plots { displayName = CSTRING(DisplayName); - icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + icon = QPATHTOF(ui\ruler.paa); statement = QUOTE(call FUNC(selectPosition)); args = "LINE"; priority = 15; class MeasureDistance { - displayName = "MeasureDistance"; - icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + displayName = CSTRING(MeasureDistance); + icon = QPATHTOF(ui\ruler.paa); statement = QUOTE(call FUNC(selectPosition)); args = "LINE"; }; - class Radius { - displayName = "Radius"; - icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + class MeasureRadius { + displayName = CSTRING(MeasureRadius); + icon = QPATHTOF(ui\radius.paa); statement = QUOTE(call FUNC(selectPosition)); args = "RADIUS"; }; - class Rectangle { - displayName = "Rectangle"; - icon = "\a3\ui_f\data\IGUI\Cfg\Actions\autohover_ca.paa"; + class MeasureOffset { + displayName = CSTRING(MeasureOffset); + icon = QPATHTOF(ui\cuboid.paa); statement = QUOTE(call FUNC(selectPosition)); args = "RECTANGLE"; }; diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index 5a8718dfc..b354177f0 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -1,29 +1,24 @@ class GVAR(formatters) { class Distance { class Meter { - displayName = CSTRING(FormatterName_Distance_Meter); formatter = QUOTE(format [ARR_2('%1 m',_value toFixed 1)]); priority = 100; }; class Feet { - displayName = CSTRING(FormatterName_Distance_Feet); formatter = QUOTE(format [ARR_2('%1 ft',(_value * 3.281) toFixed 1)]); priority = 90; }; class NauticalMile { - displayName = CSTRING(FormatterName_Distance_NauticalMile); formatter = QUOTE(format [ARR_2('%1 NM',(_value / 1852) toFixed 2)]); priority = 80; }; }; class Azimuth { class Degree { - displayName = CSTRING(FormatterName_Azimuth_Degree); formatter = QUOTE(format [ARR_2('%1°',_value toFixed 1)]); priority = 100; }; class NATOMil { - displayName = CSTRING(FormatterName_Azimuth_NATOMil); formatter = QUOTE(format [ARR_2('%1 mil',(_value * 17.7778) toFixed 0)]); priority = 90; }; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf index cbb6bf02e..ab3b6df32 100644 --- a/addons/plotting/functions/fnc_compileFormatters.sqf +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -23,7 +23,6 @@ private _fnc_getFormatters = { private _entryConfig = _x; private _formatterName = configName _entryConfig; - private _displayName = getText (_entryConfig >> "displayName"); private _formatterString = getText (_entryConfig >> "formatter"); if (_formatterString isNotEqualTo "") then { @@ -35,7 +34,6 @@ private _fnc_getFormatters = { private _formatterEntry = [ _formatterName, - _displayName, _formatter, _priority ]; diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf index d55261a4b..8a66cbf1f 100644 --- a/addons/plotting/functions/fnc_drawPlots.sqf +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Authors: Timi007 - * Draws all placed plots and currently active one in 3D or on the map. + * Draws all placed plots and currently active one in 3D or on the map. Must be called every frame. * * Arguments: * 0: Permanent plots in format [type, startPosASLOrObj, endPosASLOrObj] @@ -26,8 +26,8 @@ private _scale = 0; private _screenPos = []; private _formatters = [ - (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 2, - (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 2 + (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 1, + (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 1 ]; // Format active plot as permanent plot with mouse position as end position diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index 91c71919d..7b269ed4e 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -5,25 +5,17 @@ Plotting Plotten - - Meter - Meter + + Measure distance + Entfernung messen - - Foot - Fuß + + Measure radius + Radius messen - - Nautical mile - Nautische Meile - - - Degree - Grad - - - NATO mil - NATO Strich + + Measure offset + Versatz messen Clear plots diff --git a/addons/plotting/ui/cuboid.paa b/addons/plotting/ui/cuboid.paa new file mode 100644 index 0000000000000000000000000000000000000000..7f76adfb75cbe66fef1863d3cc99068767c3b258 GIT binary patch literal 5625 zcmeHLO=uHA6n<$dYOtngi%1cprv744kY1{EiG)>XH#@s3G1Q1)hTV3*eKT*q_cL#1 zGmH6rwR^Yv_SF^vKsue?rsIa%9UL=k9Q$gwdpUOC&G8CZHOCV8&;tx0OZ`1D6%uXB)1&DIgc+aIP4 zigPvWH}?z7|A>db$7aa?EY|r+d@2%ba@cidRGKebgcMDjU3?fgO86y>2dFzQ=QDs+ zKbmQ-FWqQs{$qd3dYC!3@I!JIen`&BFRT3g(SECbtbZYE{e!<;|M*cz&aXQA{5JW_ z;_vM0X)fv-_?O>t8zBwnf-$AD?S%=eIJ|b84M$dYbHA zIm}oM`}CBstNUJY3`;uUO#JNne^UQ)THi9)Ph9-i$R5vo{$!MB{{GDE)+O24%g69W z`gHsMbiK-H5mp{!Wtf{`f5V8=skB=~&7?LrskO&{?Lw zxzFz`zJA}|F6)!$2UVFL$BX3Z_+3Z!2G?f7W&N&6wrsAsjjHH;oRZ{V!)l*0&6MC9 zlB<1=Bx{*3>MRNQ6Fwy+QwThc;#9QAMrTH>pL!W*s(DS9C?^VZAmJ;#X z2z5N(!hgI!W|*)C2U+4fQ@kM7g8BUW#}LkiH?rSAxx*#=tJwiNY|9|3V>8Tl9FFp3 zdMipamw%{gY&-HULRcAd?2L_G3z!%V#9|NE3miRtvwzz>u}qY=mGQN~_z_cHzZ|q) zX?&p2tvwRsKR{&n#y#g(QU3#~w^4SpY73(sSKHEH4$J`sf6(aG0>>-wT_m-h2U>5j p(jO!ofrXpyhuTHtrZZnD{ZMv7k{-rkqiHM?tQQ zhZ7_gOaD7 z7n#4+Yxuflr1tAZeH*hw9_EkAE_;4;qhT>FbufL^Ya~gM{mee_u|Pb&qxN&Pryp-{ z;d<_uyod4AkIy%*xiA6oaGTS7e5p#Z#rPY|-FwPb8F@%NM`lB=6no7H?90741Ix znqAI1zAdY;p7>6tk8sVWZNQuA&lK0TPpX>{6rlejo57yB{#4fdPzMU-Go0-9-sZ&{ z6j!v5!Df@Q&Oa@y-Ciq%fa+67LkP0PwFCw`JD;NCM>fY&k+VH4->TlE{mB#;4R(uh z-dj)gH!RvA`EG_N&QxE%`!_^a;_{tqNJj}}(ct;iY%qWOAL_5$teLOF(5{E7N+a6vNG@YRpc6r(Er2I^GtTgm|RuGtLJ| ziOZBFNoA^<_%d)uUeaTQYR*{{KkQ9i6Vd%wS;kNoj1cERzsfbZm79?uu36NTzm zQTEs%@SWQAS9~rheyLfHBkQDeMT_r}P`L&dpCi}DHP|5d@jT7)Wv={o#;Xy?*L?rq z<5|U3aU6bOP-VM@rF6KJT^9;E}-qo zv9Wm1FidmyQ*k^y*@oQ#_lb(uiqSfSpUmh^M_zxdu;nB{7&@`wJ$tOpWeP{ z2Fk1^ekIEgzvn)C+tW$U3ZHg%UA zJkGFb#@BUe8<#1)e`WY75I-`iLc=C3K=%Uz#xNo$OVts#T@2KbB5^1}z>c`WV znc-gEYnBGsvayHZ^ynX$W$g=zH$T_hZiQ8d`B3`nkI>r;)wP4&M(p8FERnyteG3c1 zakr*=3JWKmk7%%-|88zKeijYD?kZapW`Jwj_pIq7=7!^pl5IASs&2r>GikDYX(`KA?m~tJc`o Date: Fri, 7 Mar 2025 17:06:41 +0100 Subject: [PATCH 08/32] Fix line width on map --- addons/plotting/functions/fnc_drawLine.sqf | 4 +-- addons/plotting/functions/fnc_drawRadius.sqf | 4 +-- .../plotting/functions/fnc_drawRectangle.sqf | 30 ++++++++++++------- addons/plotting/script_component.hpp | 10 +++++-- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index dbd9a5949..b20daa5b2 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Authors: Timi007 - * Draws a line plot in 3D or on the map. + * Draws a line plot in 3D or on the map. Must be called every frame. * * Arguments: * 0: Start position ASL @@ -44,6 +44,6 @@ if (isNull _mapCtrl) then { // 3D drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _endText]; } else { // Map _mapCtrl drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, _startText]; - _mapCtrl drawLine [_startPos, _endPos, _color, _lineWidth]; + _mapCtrl drawLine [_startPos, _endPos, _color]; _mapCtrl drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _endText]; }; diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index 4e3151def..da9da0727 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Authors: Timi007 - * Draws a radius circle plot in 3D or on the map. + * Draws a radius circle plot in 3D or on the map. Must be called every frame. * * Arguments: * 0: Start position ASL @@ -58,7 +58,7 @@ if (isNull _ctrlMap) then { // 3D }; } else { // Map _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; - _ctrlMap drawLine [_startPos, _endPos, _color, _lineWidth]; + _ctrlMap drawLine [_startPos, _endPos, _color]; _ctrlMap drawEllipse [_startPos, _radius, _radius, 0, _color, ""]; _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _text]; }; diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index d27a87436..904d06ff1 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Authors: Timi007 - * Draws a rectangle plot in 3D or on the map. + * Draws a rectangle plot in 3D or on the map. Must be called every frame. * * Arguments: * 0: Start position ASL @@ -40,15 +40,25 @@ if (isNull _ctrlMap) then { // 3D _startPos params ["_x1", "_y1", "_z1"]; _endPos params ["_x2", "_y2", "_z2"]; - private _edges = [ - [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]], // Rectangle same height as start pos - [[_x1, _y1, _z2], [_x2, _y1, _z2], [_x2, _y2, _z2], [_x1, _y2, _z2], [_x1, _y1, _z2]], // Rectangle same height as end pos - // Connections from start to end height - [[_x1, _y1, _z1], [_x1, _y1, _z2]], - [[_x2, _y1, _z1], [_x2, _y1, _z2]], - [[_x2, _y2, _z1], [_x2, _y2, _z2]], - [[_x1, _y2, _z1], [_x1, _y2, _z2]] - ]; + private _height = abs (_z2 - _z1); + + private _edges = []; + if (_height > CUBOID_HEIGHT_THRESHOLD) then { + _edges = [ + [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]], // Rectangle same height as start pos + [[_x1, _y1, _z2], [_x2, _y1, _z2], [_x2, _y2, _z2], [_x1, _y2, _z2], [_x1, _y1, _z2]], // Rectangle same height as end pos + // Connections from start to end height + [[_x1, _y1, _z1], [_x1, _y1, _z2]], + [[_x2, _y1, _z1], [_x2, _y1, _z2]], + [[_x2, _y2, _z1], [_x2, _y2, _z2]], + [[_x1, _y2, _z1], [_x1, _y2, _z2]] + ]; + } else { + // Don't draw cuboid if height difference is small + _edges = [ + [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]] + ]; + }; { for "_i" from 0 to (count _x - 2) do { diff --git a/addons/plotting/script_component.hpp b/addons/plotting/script_component.hpp index 5822d8ed5..4898670e8 100644 --- a/addons/plotting/script_component.hpp +++ b/addons/plotting/script_component.hpp @@ -19,9 +19,13 @@ #include "\a3\ui_f\hpp\defineDIKCodes.inc" #include "\x\zen\addons\common\defineResinclDesign.inc" +#define ICON "\a3\ui_f\data\map\markerbrushes\cross_ca.paa" +#define ICON_ANGLE 0 #define ICON_SCALE 1 #define MAP_ICON_SCALE 24 +#define LINEWIDTH 10 -#define CIRCLE_DOTS_SCALE 0.5 -#define CIRCLE_DOTS_MIN 6 -#define CIRCLE_DOTS_SPACING 5 +#define CIRCLE_EDGES_MIN 12 +#define CIRCLE_RESOLUTION 5 + +#define CUBOID_HEIGHT_THRESHOLD 0.1 From 3fcd2548e35faed4cb0e2c5e87d76f3423da5ced Mon Sep 17 00:00:00 2001 From: Timi007 Date: Fri, 7 Mar 2025 17:58:18 +0100 Subject: [PATCH 09/32] Add docs --- docs/_sidebar.md | 1 + docs/user_guide/context_actions.md | 4 ++++ docs/user_guide/plotting.md | 27 +++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 docs/user_guide/plotting.md diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 1529f158f..bd303bdca 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -6,6 +6,7 @@ - [ Compositions](/user_guide/compositions.md) - [ Vehicle Garage](/user_guide/garage.md) - [ Area Markers](/user_guide/area_markers.md) + - [ Plotting](/user_guide/plotting.md) - **Frameworks** - [ Context Menu](/frameworks/context_menu.md) - [ Custom Modules](/frameworks/custom_modules.md) diff --git a/docs/user_guide/context_actions.md b/docs/user_guide/context_actions.md index 803ad90b1..5cddd642d 100644 --- a/docs/user_guide/context_actions.md +++ b/docs/user_guide/context_actions.md @@ -60,6 +60,10 @@ Sub-actions allow for copying and pasting the hovered unit's loadout onto anothe Furthermore, the unit's current weapon can be switched between their rifle, handgun, or binoculars. The main action acts as a shortcut for the "Edit" sub-action. +## Plotting + +Creates a [plot](/user_guide/plotting.md) at the context menu's position or attached object. + ## Remote Control Starts the remote control process on the hovered unit or vehicle. diff --git a/docs/user_guide/plotting.md b/docs/user_guide/plotting.md new file mode 100644 index 000000000..3c1cec898 --- /dev/null +++ b/docs/user_guide/plotting.md @@ -0,0 +1,27 @@ +# Plotting + +This feature helps you measure distances and directions with 3D and map plots. +You can set the starting and ending points to be either fixed locations or attached to an object, allowing the values to update dynamically. + +You can cycle between different distance and azimuth formats using the R and Tab ↹ keys, which can be customized in the CBA Keybinding settings. +You can also change the plot colors in the CBA settings. + +## Measure Distance + +This action creates a simple line. It calculates the 3D distance between the starting and ending points and shows it at both ends of the line. +The azimuth at the end point indicates the direction from the start point to the end point, while the azimuth at the start point shows the direction from the end point back to the start. + +## Measure Radius + +This action draws a circle with the starting point at the center. +The radius of the circle is determined by the 3D distance between the start and end points. +The azimuth at the end point indicates the direction from the center to the end point. + +## Measure Offset + +This action creates a rectangular cuboid, using the starting point as one corner and the ending point as the opposite corner. +The end point displays the offsets in the X, Y, and Z axes relative to the starting point. + +## Clear Plots + +This action removes all measurement plots from the display. From e3ac446664e4bbd68d4337aea451e97b3d301011 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Sun, 9 Mar 2025 17:29:48 +0100 Subject: [PATCH 10/32] Improve icons --- addons/plotting/ui/cuboid.paa | Bin 5625 -> 5625 bytes addons/plotting/ui/ruler.paa | Bin 2897 -> 5625 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/addons/plotting/ui/cuboid.paa b/addons/plotting/ui/cuboid.paa index 7f76adfb75cbe66fef1863d3cc99068767c3b258..b1d997f20a607e76a29a5b82818d8a4b6a04415a 100644 GIT binary patch literal 5625 zcmdUz4NOy46vr=6feI~jNasWb)PhdOj5Dd*tOJy^cIq&5bC|i=7C)9@3yD$Y)Kp+` znxPb;QDVeI+?JRvg2`T7>I7p#A)?cnEgh9%1@VJe3JRv_t8e!e&h6tVd#R3OytGMw zJ@=k_{`ceDd*38RtIhdviFS342qDzl+xtd<^K(|P%;1M*ZO(EH%VGG-@+yrcFB|{f zjlbW-BXnR2LWdF&x`Dr;J=rK5A?#t~VH;$BDr}UgoHEY=F0oM(#2i=hn8o4pxs=4& zkn9BJ`!3_KC}TJ1leuYMR3rTt@I=?gk6+=YOipUU4A587)>E0q_!9`%G+;ZP z68K^3Dv+*PsVgJ%gYR#`ac;J>8wU;g0wS@*u{akkEmLyr6Hf0)z@TsIZFc+(Wc+2G z{4=zdwGNNB^y^Z8lf{z-EFS0aSGb)`$6NaR(4L}(G9}d~bOF!rX!qC|+5sGD|4aL^ zKh?&$P=B`Pv-4`Nm&|wdU~Ar!t3{;!l!4YIJqcOmQ2r&$`1Xg)tugme1)o5B3Y(Up z8Kx09*LFDBS#Nso{JZkD^CjI@IgN}H& zIapu58gB-i`m;MJNrvw$`Df@)=IR%$Z_h7q;w1&~8eQu*ao3k|=s_jzf4B+kTLzf^6s9T#_$mK3 zFMAds`ROV9$RA_Y57GXj`sH1L_4PW$Bk#&TkMqfK;Ml{Kq3Ei#B;ew^-l`P*0UmFC zLjFV5FQj+IVT-4!R6^#%6r{6?bk>ugcjW``p>;cVUjr73Z@le7`a{(#6t8GXn#sXg zFGA(V+x{&5Q^TeW!uas#Gk<(Tj;}v{UwXcD9{5k%cP7~pM@Iqk{gnjv-|A?v4|7@i zXs#ytV%Q!&6YP9{eCF#nJzm`S#2d_4^1yt~B>v@rc*38bttw*@=X@F4x4lKJ5BA3^ zp86GzG&&yQm(>iJ7g7eYo@DMg`nvb$EU@*Xp~pgu#TZh&Fph~EF2*Lo5jGCC>Ap9O=KM_G*TWr&9#uk6&7BKq%2)*RSh zWgU$>+i{lw^UDiyIL=Sp^KCS;9^`Tq!uvPp{QJV(fb>pk(=3QL*B=?t)8_jaSbXz0 z>xJ@Q{*;}<_cN9G4%ii^yk2|O&UV5bcrv()$oWH7!roW?|J(c9JHKk&{?$e@pT#a# zZ}9veG+v?FQ&B2HB9u`<85VNV?zE2TgWa0@kEZ3g&y`qM3Y&;6~^lqo8(eFD4 z>_XxryLHFNwUytV5UaIcR@m_e{6!Yy;5gU91+F3Yq8%R6A2F_LbIvsv9)$8j{)O!7 z;^-^IKz66Fvv{}8Ehq$kvu~&jbG|z|6*&FC=-pUZ zyGG)>XH#@s3G1Q1)hTV3*eKT*q_cL#1 zGmH6rwR^Yv_SF^vKsue?rsIa%9UL=k9Q$gwdpUOC&G8CZHOCV8&;tx0OZ`1D6%uXB)1&DIgc+aIP4 zigPvWH}?z7|A>db$7aa?EY|r+d@2%ba@cidRGKebgcMDjU3?fgO86y>2dFzQ=QDs+ zKbmQ-FWqQs{$qd3dYC!3@I!JIen`&BFRT3g(SECbtbZYE{e!<;|M*cz&aXQA{5JW_ z;_vM0X)fv-_?O>t8zBwnf-$AD?S%=eIJ|b84M$dYbHA zIm}oM`}CBstNUJY3`;uUO#JNne^UQ)THi9)Ph9-i$R5vo{$!MB{{GDE)+O24%g69W z`gHsMbiK-H5mp{!Wtf{`f5V8=skB=~&7?LrskO&{?Lw zxzFz`zJA}|F6)!$2UVFL$BX3Z_+3Z!2G?f7W&N&6wrsAsjjHH;oRZ{V!)l*0&6MC9 zlB<1=Bx{*3>MRNQ6Fwy+QwThc;#9QAMrTH>pL!W*s(DS9C?^VZAmJ;#X z2z5N(!hgI!W|*)C2U+4fQ@kM7g8BUW#}LkiH?rSAxx*#=tJwiNY|9|3V>8Tl9FFp3 zdMipamw%{gY&-HULRcAd?2L_G3z!%V#9|NE3miRtvwzz>u}qY=mGQN~_z_cHzZ|q) zX?&p2tvwRsKR{&n#y#g(QU3#~w^4SpY73(sSKHEH4$J`sf6(aG0>>-wT_m-h2U>5j p(jO!ofrXpyhuTHC|YD99;Tks&bQArFmmY}Fdg&}JpfQHm-h)i6OMCcSU5++{SnRz-keWFA_E%M4ZLrv0Ao7=|&^Luagq<2S5iab}#mTYL{0#Iga$_i(*&Key+4)kpSF zME{OQ&6ON0<#@BOU(M92k|%hrdx4l`ghan&@k=oSk|%h(D*q?{T%G}4e!OTrf3u&N ziVAqi{zmr~z>7Fx>Zu>0)Kg+G*w;g>v`KPZ0deAcHBavw zv-x;YsbxMqG86;1Lpb^y%o@56_v|#WLBB`WQ@!W{k`G>% zb8frZ&7So5R`j>6ufyYCSFAB$?wFf=w!hGz4W7BgHwrk4{_TU#4=QDA|dVJI|`r7Dz0gJTc3;+NC literal 2897 zcmeHJu}Z{15PiBSf(p6D{wiz1R@ej~X%q`Z{2V(wziA<@aDU)81g@|NtZ!!bRL;bB z7)_Ibl5IASs&2r>GikDYX(`KA?m~tJc`o Date: Sun, 9 Mar 2025 19:17:58 +0100 Subject: [PATCH 11/32] Rename var --- addons/plotting/functions/fnc_drawLine.sqf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index b20daa5b2..7eec07849 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -26,7 +26,7 @@ * Public: No */ -params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_mapCtrl", controlNull, [controlNull]]]; +params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; _formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; @@ -38,12 +38,12 @@ private _azimuthToEnd = _startPos getDir _endPos; private _startText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToStart call _fnc_azimuthFormatter]; private _endText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToEnd call _fnc_azimuthFormatter]; -if (isNull _mapCtrl) then { // 3D +if (isNull _ctrlMap) then { // 3D drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle, _startText]; drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _endText]; } else { // Map - _mapCtrl drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, _startText]; - _mapCtrl drawLine [_startPos, _endPos, _color]; - _mapCtrl drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _endText]; + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, _startText]; + _ctrlMap drawLine [_startPos, _endPos, _color]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _endText]; }; From b7b453e1e693d127b503b9ae9d21b42788043b24 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 00:26:57 +0100 Subject: [PATCH 12/32] Add measure distance from camera --- addons/plotting/CfgContext.hpp | 6 ++++++ addons/plotting/stringtable.xml | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp index 4007b0d73..ad3810f4d 100644 --- a/addons/plotting/CfgContext.hpp +++ b/addons/plotting/CfgContext.hpp @@ -12,6 +12,12 @@ class EGVAR(context_menu,actions) { statement = QUOTE(call FUNC(selectPosition)); args = "LINE"; }; + class MeasureDistanceFromCamera { + displayName = CSTRING(MeasureDistanceFromCamera); + icon = QPATHTOF(ui\ruler.paa); + statement = QUOTE(GVAR(activePlot) = [ARR_2(_args,curatorCamera)]); + args = "LINE"; + }; class MeasureRadius { displayName = CSTRING(MeasureRadius); icon = QPATHTOF(ui\radius.paa); diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index 7b269ed4e..27877ed2c 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -9,6 +9,10 @@ Measure distance Entfernung messen + + Measure distance from camera + Entfernung von Kamera messen + Measure radius Radius messen From 49a066df3e784b6fa434ef7bf58770093cf3a1cb Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 00:27:39 +0100 Subject: [PATCH 13/32] Add max render distance --- addons/plotting/XEH_PREP.hpp | 1 + addons/plotting/functions/fnc_drawLine.sqf | 32 ++++++++---- addons/plotting/functions/fnc_drawRadius.sqf | 49 ++++++++++++------- .../plotting/functions/fnc_drawRectangle.sqf | 28 ++++++++--- .../functions/fnc_isPosInCylinder.sqf | 28 +++++++++++ addons/plotting/script_component.hpp | 6 +++ 6 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 addons/plotting/functions/fnc_isPosInCylinder.sqf diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp index 8b776eecb..8d02648af 100644 --- a/addons/plotting/XEH_PREP.hpp +++ b/addons/plotting/XEH_PREP.hpp @@ -5,6 +5,7 @@ PREP(drawLine); PREP(drawPlots); PREP(drawRadius); PREP(drawRectangle); +PREP(isPosInCylinder); PREP(onDraw); PREP(onDraw3D); PREP(onKeyDown); diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index 7eec07849..d6154b3c3 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -15,7 +15,8 @@ * 3: Formatters * 0: Distance formatter * 1: Azimuth formatter - * 3: Map control (default: Draw in 3D) + * 4: Camera position ASL (default: Don't check render distance) + * 5: Map control (default: Draw in 3D) * * Return Value: * None @@ -28,22 +29,35 @@ params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; -_formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; private _distance = _startPos vectorDistance _endPos; private _azimuthToStart = _endPos getDir _startPos; private _azimuthToEnd = _startPos getDir _endPos; -private _startText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToStart call _fnc_azimuthFormatter]; -private _endText = format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuthToEnd call _fnc_azimuthFormatter]; +private _fnc_format = { + params ["_distance", "_azimuth", "_formatters"]; + _formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + + format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter] +}; if (isNull _ctrlMap) then { // 3D - drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle, _startText]; - drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; - drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _endText]; + private _camPos = getPosASL curatorCamera; + + if (CAN_RENDER_ICON(_camPos,_startPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle, [_distance, _azimuthToStart, _formatters] call _fnc_format]; + }; + + if (CAN_RENDER_LINE(_camPos,_startPos,_endPos)) then { + drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; + }; + + if (CAN_RENDER_ICON(_camPos,_endPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_distance, _azimuthToEnd, _formatters] call _fnc_format]; + }; } else { // Map - _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, _startText]; + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, [_distance, _azimuthToStart, _formatters] call _fnc_format]; _ctrlMap drawLine [_startPos, _endPos, _color]; - _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _endText]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_distance, _azimuthToEnd, _formatters] call _fnc_format]; }; diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index da9da0727..38fc9883e 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -4,7 +4,7 @@ * Draws a radius circle plot in 3D or on the map. Must be called every frame. * * Arguments: - * 0: Start position ASL + * 0: Center position ASL * 1: End position ASL * 2: Visual properties * 0: Icon @@ -26,21 +26,33 @@ * Public: No */ -params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +params ["_centerPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; -_formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; -private _radius = _startPos vectorDistance _endPos; -private _azimuth = _startPos getDir _endPos; +private _radius = _centerPos vectorDistance _endPos; +private _azimuth = _centerPos getDir _endPos; -private _text = format ["%1 - %2", _radius call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter]; +private _fnc_format = { + params ["_distance", "_azimuth", "_formatters"]; + _formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + + format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter] +}; if (isNull _ctrlMap) then { // 3D - private _centerAGL = ASLToAGL _startPos; + private _camPos = getPosASL curatorCamera; + + private _centerAGL = ASLToAGL _centerPos; private _endPosAGL = ASLToAGL _endPos; - drawIcon3D [_icon, _color, _centerAGL, _scale, _scale, _angle]; - drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; - drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, _text]; + + if (CAN_RENDER_ICON(_camPos,_centerPos)) then { + drawIcon3D [_icon, _color, _centerAGL, _scale, _scale, _angle]; + }; + + if (_camPos vectorDistance _centerPos <= _radius + MAX_RENDER_DISTANCE) then { + drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; + drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, [_radius, _azimuth, _formatters] call _fnc_format]; + }; private _count = CIRCLE_EDGES_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_RESOLUTION); private _factor = 360 / _count; @@ -52,13 +64,16 @@ if (isNull _ctrlMap) then { // 3D }; for "_i" from 0 to (_count - 1) do { - private _pos1 = _centerAGL vectorAdd (_offsets select _i); - private _pos2 = _centerAGL vectorAdd (_offsets select ((_i + 1) % _count)); - drawLine3D [_pos1, _pos2, _color, _lineWidth]; + private _pos1 = _centerPos vectorAdd (_offsets select _i); + private _pos2 = _centerPos vectorAdd (_offsets select ((_i + 1) % _count)); + + if (CAN_RENDER_LINE(_camPos,_pos1,_pos2)) then { + drawLine3D [ASLToAGL _pos1, ASLToAGL _pos2, _color, _lineWidth]; + }; }; } else { // Map - _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; - _ctrlMap drawLine [_startPos, _endPos, _color]; - _ctrlMap drawEllipse [_startPos, _radius, _radius, 0, _color, ""]; - _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _text]; + _ctrlMap drawIcon [_icon, _color, _centerPos, _scale, _scale, _angle]; + _ctrlMap drawLine [_centerPos, _endPos, _color]; + _ctrlMap drawEllipse [_centerPos, _radius, _radius, 0, _color, ""]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_radius, _azimuth, _formatters] call _fnc_format]; }; diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index 904d06ff1..c4b41ee5f 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -27,15 +27,24 @@ params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; -_formatters params ["_fnc_distanceFormatter"]; private _offset = _endPos vectorDiff _startPos; _offset params ["_a", "_b", "_c"]; -private _text = format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_distanceFormatter, _b call _fnc_distanceFormatter, _c call _fnc_distanceFormatter]; +private _fnc_format = { + params ["_offset", "_formatters"]; + _offset params ["_a", "_b", "_c"]; + _formatters params ["_fnc_distanceFormatter"]; + + format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_distanceFormatter, _b call _fnc_distanceFormatter, _c call _fnc_distanceFormatter] +}; if (isNull _ctrlMap) then { // 3D - drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; + private _camPos = getPosASL curatorCamera; + + if (CAN_RENDER_ICON(_camPos,_startPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; + }; _startPos params ["_x1", "_y1", "_z1"]; _endPos params ["_x2", "_y2", "_z2"]; @@ -62,14 +71,21 @@ if (isNull _ctrlMap) then { // 3D { for "_i" from 0 to (count _x - 2) do { - drawLine3D [ASLToAGL (_x select _i), ASLToAGL (_x select (_i + 1)), _color, _lineWidth]; + private _pos1 = _x select _i; + private _pos2 = _x select (_i + 1); + + if (CAN_RENDER_LINE(_camPos,_pos1,_pos2)) then { + drawLine3D [ASLToAGL _pos1, ASLToAGL _pos2, _color, _lineWidth]; + }; }; } forEach _edges; - drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, _text]; + if (CAN_RENDER_ICON(_camPos,_endPos)) then { + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_offset, _formatters] call _fnc_format]; + }; } else { // Map _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; private _center = (_startPos vectorAdd _endPos) vectorMultiply 0.5; _ctrlMap drawRectangle [_center, _a / 2, _b / 2, 0, _color, ""]; - _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, _text]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_offset, _formatters] call _fnc_format]; }; diff --git a/addons/plotting/functions/fnc_isPosInCylinder.sqf b/addons/plotting/functions/fnc_isPosInCylinder.sqf new file mode 100644 index 000000000..cb7376ed2 --- /dev/null +++ b/addons/plotting/functions/fnc_isPosInCylinder.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Checks if the given point is inside the defined cylinder. + * + * Arguments: + * 0: Start position of cylinder + * 1: End position of cylinder + * 2: Radius of cylinder + * 3: Point to check + * + * Return Value: + * Position is in cylinder + * + * Example: + * [[0, 0, 0], [100, 0, 0], 50, [50, 50, 0]] call zen_plotting_fnc_isPosInCylinder + * + * Public: No + */ + +params ["_p1", "_p2", "_r", "_q"]; +// https://stackoverflow.com/a/47933302 + +private _diff = _p2 vectorDiff _p1; + +((_q vectorDiff _p1) vectorDotProduct _diff >= 0) // First test (q - p1) * (p2 - p1) >= 0 +|| {(_q vectorDiff _p2) vectorDotProduct _diff <= 0} // Second test (q - p2) * (p2 - p1) <= 0 +|| {(vectorMagnitude ((_q vectorDiff _p1) vectorCrossProduct _diff)) <= (_r * vectorMagnitude _diff)} // Third test |(q - p1) x (p2 - p1)| <= r * |p2 - p1| diff --git a/addons/plotting/script_component.hpp b/addons/plotting/script_component.hpp index 4898670e8..e9ea4b298 100644 --- a/addons/plotting/script_component.hpp +++ b/addons/plotting/script_component.hpp @@ -29,3 +29,9 @@ #define CIRCLE_RESOLUTION 5 #define CUBOID_HEIGHT_THRESHOLD 0.1 + +#define MAX_RENDER_DISTANCE 3000 +#define MAX_RENDER_DISTANCE_SQR 9000000 + +#define CAN_RENDER_ICON(CAMPOS,POS) (CAMPOS vectorDistanceSqr POS <= MAX_RENDER_DISTANCE_SQR) +#define CAN_RENDER_LINE(CAMPOS,POS1,POS2) ((CAMPOS vectorDistanceSqr POS1 <= MAX_RENDER_DISTANCE_SQR) || {CAMPOS vectorDistanceSqr POS2 <= MAX_RENDER_DISTANCE_SQR} || {[POS1, POS2, MAX_RENDER_DISTANCE, CAMPOS] call FUNC(isPosInCylinder)}) From 9e82bc91f4ee3bdbff9f41cdcd46718aae08740d Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 00:46:37 +0100 Subject: [PATCH 14/32] Add function to set active plot --- addons/plotting/CfgContext.hpp | 2 +- addons/plotting/XEH_PREP.hpp | 1 + .../plotting/functions/fnc_selectPosition.sqf | 2 +- .../plotting/functions/fnc_setActivePlot.sqf | 22 +++++++++++++++++++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 addons/plotting/functions/fnc_setActivePlot.sqf diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp index ad3810f4d..8b5817734 100644 --- a/addons/plotting/CfgContext.hpp +++ b/addons/plotting/CfgContext.hpp @@ -15,7 +15,7 @@ class EGVAR(context_menu,actions) { class MeasureDistanceFromCamera { displayName = CSTRING(MeasureDistanceFromCamera); icon = QPATHTOF(ui\ruler.paa); - statement = QUOTE(GVAR(activePlot) = [ARR_2(_args,curatorCamera)]); + statement = QUOTE([ARR_2(_args,curatorCamera)] call FUNC(setActivePlot)); args = "LINE"; }; class MeasureRadius { diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp index 8d02648af..74836fd4c 100644 --- a/addons/plotting/XEH_PREP.hpp +++ b/addons/plotting/XEH_PREP.hpp @@ -13,3 +13,4 @@ PREP(onLoad); PREP(onMouseButtonDown); PREP(onUnload); PREP(selectPosition); +PREP(setActivePlot); diff --git a/addons/plotting/functions/fnc_selectPosition.sqf b/addons/plotting/functions/fnc_selectPosition.sqf index 57847590f..8faf08df6 100644 --- a/addons/plotting/functions/fnc_selectPosition.sqf +++ b/addons/plotting/functions/fnc_selectPosition.sqf @@ -31,4 +31,4 @@ private _posOrObj = switch (true) do { TRACE_1("start pos",_posOrObj); -GVAR(activePlot) = [_type, _posOrObj]; +[_type, _posOrObj] call FUNC(setActivePlot); diff --git a/addons/plotting/functions/fnc_setActivePlot.sqf b/addons/plotting/functions/fnc_setActivePlot.sqf new file mode 100644 index 000000000..0fc40e2be --- /dev/null +++ b/addons/plotting/functions/fnc_setActivePlot.sqf @@ -0,0 +1,22 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Sets the currently active plot where the end position is attached to the cursor. + * + * Arguments: + * 0: Type of plot + * 1: Start position ASL or attached object + * + * Return Value: + * None + * + * Example: + * ["LINE", curatorCamera] call zen_plotting_fnc_setActivePlot + * + * Public: No + */ + +params [["_type", "LINE", [""]], ["_startPos", [0, 0, 0], [[], objNull], [3]]]; + +GVAR(activePlot) = [_type, _startPos]; + From a6fa17d86dbee0529459c78f0e6cc00b510f8bb6 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 16:41:05 +0100 Subject: [PATCH 15/32] Cleanup --- addons/plotting/functions/fnc_onMouseButtonDown.sqf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf index 0ca325b7c..45ef81922 100644 --- a/addons/plotting/functions/fnc_onMouseButtonDown.sqf +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -18,14 +18,14 @@ params ["_displayOrControl", "_button"]; -if (GVAR(activePlot) isEqualTo [] || {_button != 0}) exitWith {}; +if (_button != 0 || {GVAR(activePlot) isEqualTo []}) exitWith {}; TRACE_1("onMouseButtonDown",_this); if (call EFUNC(common,isCursorOnMouseArea)) then { - curatorMouseOver params ["_type", "_object"]; + curatorMouseOver params ["_mouseOverType", "_object"]; private _endPosOrObj = switch (true) do { - case (_type isEqualTo "OBJECT"): {_object}; + case (_mouseOverType isEqualTo "OBJECT"): {_object}; case (visibleMap): { private _ctrlMap = if (_displayOrControl isEqualType controlNull) then { _displayOrControl From 1fa239daafebfd3f3a3dc43e63cd5b426660fc83 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 16:58:00 +0100 Subject: [PATCH 16/32] Fix key handlers not always register input --- addons/plotting/XEH_preInit.sqf | 1 - addons/plotting/functions/fnc_onLoad.sqf | 11 +++-------- addons/plotting/functions/fnc_onMouseButtonDown.sqf | 12 ++++-------- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf index a3d662e63..b9fd02bb9 100644 --- a/addons/plotting/XEH_preInit.sqf +++ b/addons/plotting/XEH_preInit.sqf @@ -9,7 +9,6 @@ PREP_RECOMPILE_END; if (hasInterface) then { GVAR(activePlot) = []; GVAR(draw3DAdded) = false; - GVAR(inputHandlersAdded) = false; GVAR(plotTypes) = createHashMapFromArray [ ["LINE", LINKFUNC(drawLine)], diff --git a/addons/plotting/functions/fnc_onLoad.sqf b/addons/plotting/functions/fnc_onLoad.sqf index 143b1b092..e041165a8 100644 --- a/addons/plotting/functions/fnc_onLoad.sqf +++ b/addons/plotting/functions/fnc_onLoad.sqf @@ -27,16 +27,11 @@ if (!GVAR(draw3DAdded)) then { GVAR(draw3DAdded) = true; }; -if (!GVAR(inputHandlersAdded)) then { - LOG("Adding input handlers."); - _display displayAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; - _display displayAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; - GVAR(inputHandlersAdded) = true; -}; +LOG("Adding input handlers."); +_display displayAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; +_display displayAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; // MapDraw EH needs to be added every time the Zeus display is opened. LOG("Adding map draw."); private _ctrlMap = _display displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; _ctrlMap ctrlAddEventHandler ["Draw", {call FUNC(onDraw)}]; -_ctrlMap ctrlAddEventHandler ["MouseButtonDown", {call FUNC(onMouseButtonDown)}]; -_ctrlMap ctrlAddEventHandler ["KeyDown", {call FUNC(onKeyDown)}]; diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf index 45ef81922..96d1ed145 100644 --- a/addons/plotting/functions/fnc_onMouseButtonDown.sqf +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -4,8 +4,8 @@ * Handles the mouse button event when user wants to add a plot in 3D or on the map. * * Arguments: - * 0: Display or control the EH is attached to - * 1: Mouse button pressen + * 0: Zeus display + * 1: Mouse button pressed * * Return Value: * None @@ -16,7 +16,7 @@ * Public: No */ -params ["_displayOrControl", "_button"]; +params ["_display", "_button"]; if (_button != 0 || {GVAR(activePlot) isEqualTo []}) exitWith {}; TRACE_1("onMouseButtonDown",_this); @@ -27,11 +27,7 @@ if (call EFUNC(common,isCursorOnMouseArea)) then { private _endPosOrObj = switch (true) do { case (_mouseOverType isEqualTo "OBJECT"): {_object}; case (visibleMap): { - private _ctrlMap = if (_displayOrControl isEqualType controlNull) then { - _displayOrControl - } else { - _displayOrControl displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP - }; + private _ctrlMap = _display displayCtrl IDC_RSCDISPLAYCURATOR_MAINMAP; private _pos2D = _ctrlMap ctrlMapScreenToWorld getMousePosition; _pos2D set [2, getTerrainHeightASL _pos2D]; From 7896fdfe64218235cd85278f043c59e46ad651bf Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 12 Mar 2025 17:39:14 +0100 Subject: [PATCH 17/32] Add undo keybind --- addons/plotting/initKeybinds.inc.sqf | 32 ++++++++++++++++++++++++++-- addons/plotting/stringtable.xml | 24 +++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf index 63f769ea7..8798abd2e 100644 --- a/addons/plotting/initKeybinds.inc.sqf +++ b/addons/plotting/initKeybinds.inc.sqf @@ -3,7 +3,7 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; [ _category, QGVAR(toggleDistanceFormat), - LSTRING(ToggleDistanceFormat), + [LSTRING(ToggleDistanceFormat), LSTRING(ToggleDistanceFormat_Description)], { if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { private _numFormatters = count GVAR(distanceFormatters); @@ -19,7 +19,7 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; [ _category, QGVAR(toggleAzimuthFormat), - LSTRING(ToggleAzimuthFormat), + [LSTRING(ToggleAzimuthFormat), LSTRING(ToggleAzimuthFormat_Description)], { if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { private _numFormatters = count GVAR(azimuthFormatters); @@ -31,3 +31,31 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; {}, [DIK_TAB, [false, false, false]] // Default: Tab ] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(deleteLastPlot), + [LSTRING(DeleteLastPlot), LSTRING(DeleteLastPlot_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + if (GVAR(plots) isNotEqualTo []) then { + private _lastPlot = GVAR(plots) deleteAt [-1]; + TRACE_1("Removed last plot",_lastPlot); + + // Automatically delete invalid plots (e.g. when attached object does not exist anymore) + while {GVAR(plots) isNotEqualTo []} do { + (GVAR(plots) select -1) params ["", "_startPos", "_endPos"]; + + if ((_startPos isEqualTo objNull) || {_endPos isEqualTo objNull}) then { + _lastPlot = GVAR(plots) deleteAt [-1]; + TRACE_1("Automatically removed next invalid plot",_lastPlot); + }; + }; + }; + + true + }; + }, + {}, + [DIK_Y, [false, true, false]] // Default: Ctrl + Y +] call CBA_fnc_addKeybind; diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index 27877ed2c..30238068b 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -25,5 +25,29 @@ Clear plots Plots entfernen + + Switch Distance Unit + Distanzeinheit durchschalten + + + Switches to the next unit for distances. + Wechselt zur nächsten Einheit für Entfernungen. + + + Switch Azimuth Unit + Richtungseinheit durchschalten + + + Switches to the next unit for directions. + Wechselt zur nächsten Einheit für Richtungen. + + + Delete Last Plot + Letzten Plot entfernen + + + Deletes the last plot created. + Entfernt den zuletzt erstellten Plot. + From 65bddc47ec66da771af5983fd7f52631c285c9d8 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 00:00:51 +0100 Subject: [PATCH 18/32] Fix pos in cylinder --- addons/plotting/functions/fnc_isPosInCylinder.sqf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/plotting/functions/fnc_isPosInCylinder.sqf b/addons/plotting/functions/fnc_isPosInCylinder.sqf index cb7376ed2..16607f42a 100644 --- a/addons/plotting/functions/fnc_isPosInCylinder.sqf +++ b/addons/plotting/functions/fnc_isPosInCylinder.sqf @@ -24,5 +24,5 @@ params ["_p1", "_p2", "_r", "_q"]; private _diff = _p2 vectorDiff _p1; ((_q vectorDiff _p1) vectorDotProduct _diff >= 0) // First test (q - p1) * (p2 - p1) >= 0 -|| {(_q vectorDiff _p2) vectorDotProduct _diff <= 0} // Second test (q - p2) * (p2 - p1) <= 0 -|| {(vectorMagnitude ((_q vectorDiff _p1) vectorCrossProduct _diff)) <= (_r * vectorMagnitude _diff)} // Third test |(q - p1) x (p2 - p1)| <= r * |p2 - p1| +&& {(_q vectorDiff _p2) vectorDotProduct _diff <= 0} // Second test (q - p2) * (p2 - p1) <= 0 +&& {(vectorMagnitude ((_q vectorDiff _p1) vectorCrossProduct _diff)) <= (_r * vectorMagnitude _diff)} // Third test |(q - p1) x (p2 - p1)| <= r * |p2 - p1| From d458b3c8c4d768b587271bfc135eec80fbb55df9 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 00:00:56 +0100 Subject: [PATCH 19/32] Cleanup --- addons/plotting/functions/fnc_drawRectangle.sqf | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index c4b41ee5f..f25f6d93c 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -49,11 +49,8 @@ if (isNull _ctrlMap) then { // 3D _startPos params ["_x1", "_y1", "_z1"]; _endPos params ["_x2", "_y2", "_z2"]; - private _height = abs (_z2 - _z1); - - private _edges = []; - if (_height > CUBOID_HEIGHT_THRESHOLD) then { - _edges = [ + private _edges = if ((abs (_z2 - _z1)) > CUBOID_HEIGHT_THRESHOLD) then { + [ [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]], // Rectangle same height as start pos [[_x1, _y1, _z2], [_x2, _y1, _z2], [_x2, _y2, _z2], [_x1, _y2, _z2], [_x1, _y1, _z2]], // Rectangle same height as end pos // Connections from start to end height @@ -61,12 +58,10 @@ if (isNull _ctrlMap) then { // 3D [[_x2, _y1, _z1], [_x2, _y1, _z2]], [[_x2, _y2, _z1], [_x2, _y2, _z2]], [[_x1, _y2, _z1], [_x1, _y2, _z2]] - ]; + ] } else { // Don't draw cuboid if height difference is small - _edges = [ - [[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]] - ]; + [[[_x1, _y1, _z1], [_x2, _y1, _z1], [_x2, _y2, _z1], [_x1, _y2, _z1], [_x1, _y1, _z1]]] }; { From 17ae98215b6f5b44862f90709486bf633fa6f473 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 00:16:46 +0100 Subject: [PATCH 20/32] Replace nautical miles with yards --- addons/plotting/CfgFormatters.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index b354177f0..0f499e8ee 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -8,8 +8,8 @@ class GVAR(formatters) { formatter = QUOTE(format [ARR_2('%1 ft',(_value * 3.281) toFixed 1)]); priority = 90; }; - class NauticalMile { - formatter = QUOTE(format [ARR_2('%1 NM',(_value / 1852) toFixed 2)]); + class Yards { + formatter = QUOTE(format [ARR_2('%1 yd',(_value * 1.094) toFixed 1)]); priority = 80; }; }; From 681ce901473fcdc3e942d85ef8b94a3a61c7c2b3 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 00:23:44 +0100 Subject: [PATCH 21/32] Add mile --- addons/plotting/CfgFormatters.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index 0f499e8ee..ac3c7420a 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -12,6 +12,10 @@ class GVAR(formatters) { formatter = QUOTE(format [ARR_2('%1 yd',(_value * 1.094) toFixed 1)]); priority = 80; }; + class Mile { + formatter = QUOTE(format [ARR_2('%1 mi',(_value / 1609.344) toFixed 2)]); + priority = 70; + }; }; class Azimuth { class Degree { From f28459b94fb3aef5ef463cd25c99061c27619fe0 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 14:57:50 +0100 Subject: [PATCH 22/32] Fix formatter sorting --- addons/plotting/functions/fnc_compileFormatters.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf index ab3b6df32..622023248 100644 --- a/addons/plotting/functions/fnc_compileFormatters.sqf +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -41,7 +41,7 @@ private _fnc_getFormatters = { _formatters pushBack _formatterEntry; } forEach configProperties [_cfgFormatters, "isClass _x", true]; - [_formatters, 3, false] call CBA_fnc_sortNestedArray + [_formatters, 2, false] call CBA_fnc_sortNestedArray }; private _cfgFormatters = configFile >> QGVAR(formatters); From b12efd94e04daf542e3552124e06f30bfc288959 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 13 Mar 2025 15:00:19 +0100 Subject: [PATCH 23/32] Remove _value --- addons/plotting/CfgFormatters.hpp | 12 ++++++------ addons/plotting/functions/fnc_compileFormatters.sqf | 8 +------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index ac3c7420a..0557bc183 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -1,29 +1,29 @@ class GVAR(formatters) { class Distance { class Meter { - formatter = QUOTE(format [ARR_2('%1 m',_value toFixed 1)]); + formatter = QUOTE(format [ARR_2('%1 m',_this toFixed 1)]); priority = 100; }; class Feet { - formatter = QUOTE(format [ARR_2('%1 ft',(_value * 3.281) toFixed 1)]); + formatter = QUOTE(format [ARR_2('%1 ft',(_this * 3.281) toFixed 1)]); priority = 90; }; class Yards { - formatter = QUOTE(format [ARR_2('%1 yd',(_value * 1.094) toFixed 1)]); + formatter = QUOTE(format [ARR_2('%1 yd',(_this * 1.094) toFixed 1)]); priority = 80; }; class Mile { - formatter = QUOTE(format [ARR_2('%1 mi',(_value / 1609.344) toFixed 2)]); + formatter = QUOTE(format [ARR_2('%1 mi',(_this / 1609.344) toFixed 2)]); priority = 70; }; }; class Azimuth { class Degree { - formatter = QUOTE(format [ARR_2('%1°',_value toFixed 1)]); + formatter = QUOTE(format [ARR_2('%1°',_this toFixed 1)]); priority = 100; }; class NATOMil { - formatter = QUOTE(format [ARR_2('%1 mil',(_value * 17.7778) toFixed 0)]); + formatter = QUOTE(format [ARR_2('%1 mil',(_this * 17.7778) toFixed 0)]); priority = 90; }; }; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf index 622023248..7897db189 100644 --- a/addons/plotting/functions/fnc_compileFormatters.sqf +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -23,13 +23,7 @@ private _fnc_getFormatters = { private _entryConfig = _x; private _formatterName = configName _entryConfig; - - private _formatterString = getText (_entryConfig >> "formatter"); - if (_formatterString isNotEqualTo "") then { - _formatterString = "params ['_value']; " + _formatterString; - }; - private _formatter = compile _formatterString; - + private _formatter = compile getText (_entryConfig >> "formatter"); private _priority = getNumber (_entryConfig >> "priority"); private _formatterEntry = [ From 87b831f03110de16a897a10c804e706a5416b3be Mon Sep 17 00:00:00 2001 From: Timi007 Date: Tue, 25 Mar 2025 16:30:25 +0100 Subject: [PATCH 24/32] Remove yards and miles, change decimals based on distance, add thousands separators --- addons/plotting/CfgFormatters.hpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index 0557bc183..912ce5005 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -1,29 +1,21 @@ class GVAR(formatters) { class Distance { class Meter { - formatter = QUOTE(format [ARR_2('%1 m',_this toFixed 1)]); + formatter = QUOTE(FORMAT_1('%1 m',[ARR_4(_this,1,parseNumber (_this < 100),true)] call CBA_fnc_formatNumber)); priority = 100; }; class Feet { - formatter = QUOTE(format [ARR_2('%1 ft',(_this * 3.281) toFixed 1)]); + formatter = QUOTE(FORMAT_1('%1 ft',[ARR_4(_this * 3.281,1,0,true)] call CBA_fnc_formatNumber)); priority = 90; }; - class Yards { - formatter = QUOTE(format [ARR_2('%1 yd',(_this * 1.094) toFixed 1)]); - priority = 80; - }; - class Mile { - formatter = QUOTE(format [ARR_2('%1 mi',(_this / 1609.344) toFixed 2)]); - priority = 70; - }; }; class Azimuth { class Degree { - formatter = QUOTE(format [ARR_2('%1°',_this toFixed 1)]); + formatter = QUOTE(FORMAT_1('%1°',_this toFixed 0)); priority = 100; }; class NATOMil { - formatter = QUOTE(format [ARR_2('%1 mil',(_this * 17.7778) toFixed 0)]); + formatter = QUOTE(FORMAT_1('%1 mil',(_this * 17.7778) toFixed 0)); priority = 90; }; }; From 6e5015374dadcaa7dac3284f05501f8652c5343c Mon Sep 17 00:00:00 2001 From: Timi007 Date: Tue, 25 Mar 2025 18:09:44 +0100 Subject: [PATCH 25/32] Add travel time to line plot --- addons/plotting/CfgFormatters.hpp | 40 ++++++++++++++++++- addons/plotting/XEH_PREP.hpp | 1 + addons/plotting/XEH_preInit.sqf | 1 + .../functions/fnc_compileFormatters.sqf | 2 + addons/plotting/functions/fnc_drawLine.sqf | 8 +++- addons/plotting/functions/fnc_drawPlots.sqf | 1 + addons/plotting/functions/fnc_drawRadius.sqf | 4 +- .../plotting/functions/fnc_drawRectangle.sqf | 4 +- .../functions/fnc_formatTravelTime.sqf | 33 +++++++++++++++ addons/plotting/initKeybinds.inc.sqf | 20 +++++++++- addons/plotting/stringtable.xml | 8 ++++ 11 files changed, 112 insertions(+), 10 deletions(-) create mode 100644 addons/plotting/functions/fnc_formatTravelTime.sqf diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index 912ce5005..e4e1831be 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -1,14 +1,50 @@ class GVAR(formatters) { + // Formats distance: _this is distance in meters class Distance { class Meter { - formatter = QUOTE(FORMAT_1('%1 m',[ARR_4(_this,1,parseNumber (_this < 100),true)] call CBA_fnc_formatNumber)); + formatter = QUOTE(FORMAT_1('%1 m',[ARR_3(_this,1,parseNumber (abs _this < 100))] call CBA_fnc_formatNumber)); priority = 100; }; class Feet { - formatter = QUOTE(FORMAT_1('%1 ft',[ARR_4(_this * 3.281,1,0,true)] call CBA_fnc_formatNumber)); + formatter = QUOTE(FORMAT_1('%1 ft',[ARR_3(_this * 3.281,1,0)] call CBA_fnc_formatNumber)); priority = 90; }; }; + // Formats speed: _this is distance in meters + class Speed { + class Running { + // Unit with weapon lowered running in fast combat pace: takes ~30 s for 100 m -> 3.333 m/s -> 11.999 km/h + // This is with ACE mod loaded, without ACE it takes about 27 s for 100 m + formatter = QUOTE(FORMAT_1('%1 @ 12 km/h',[ARR_2(_this,3.333)] call FUNC(formatTravelTime))); + priority = 100; + }; + class VehicleSlow { + // Vehicle at 30 km/h -> 8.333 m/s + formatter = QUOTE(FORMAT_1('%1 @ 30 km/h',[ARR_2(_this,8.333)] call FUNC(formatTravelTime))); + priority = 90; + }; + class VehicleTown { + // Vehicle at 50 km/h -> 13.889 m/s + formatter = QUOTE(FORMAT_1('%1 @ 50 km/h',[ARR_2(_this,13.889)] call FUNC(formatTravelTime))); + priority = 80; + }; + class VehicleFast { + // Vehicle at 100 km/h -> 27.778 m/s + formatter = QUOTE(FORMAT_1('%1 @ 100 km/h',[ARR_2(_this,27.778)] call FUNC(formatTravelTime))); + priority = 70; + }; + class Helicopter { + // Helicopter at 250 km/h -> 69.444 m/s + formatter = QUOTE(FORMAT_1('%1 @ 250 km/h',[ARR_2(_this,69.444)] call FUNC(formatTravelTime))); + priority = 60; + }; + class Jet { + // Jet at 600 km/h -> 166.667 m/s + formatter = QUOTE(FORMAT_1('%1 @ 600 km/h',[ARR_2(_this,166.667)] call FUNC(formatTravelTime))); + priority = 50; + }; + }; + // Formats direction: _this is direction in the range 0..360 degrees. class Azimuth { class Degree { formatter = QUOTE(FORMAT_1('%1°',_this toFixed 0)); diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp index 74836fd4c..2f7a7d059 100644 --- a/addons/plotting/XEH_PREP.hpp +++ b/addons/plotting/XEH_PREP.hpp @@ -5,6 +5,7 @@ PREP(drawLine); PREP(drawPlots); PREP(drawRadius); PREP(drawRectangle); +PREP(formatTravelTime); PREP(isPosInCylinder); PREP(onDraw); PREP(onDraw3D); diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf index b9fd02bb9..8b4cf5c75 100644 --- a/addons/plotting/XEH_preInit.sqf +++ b/addons/plotting/XEH_preInit.sqf @@ -17,6 +17,7 @@ if (hasInterface) then { ]; GVAR(currentDistanceFormatter) = 0; + GVAR(currentSpeedFormatter) = 0; GVAR(currentAzimuthFormatter) = 0; }; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf index 7897db189..d99ce718d 100644 --- a/addons/plotting/functions/fnc_compileFormatters.sqf +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -40,7 +40,9 @@ private _fnc_getFormatters = { private _cfgFormatters = configFile >> QGVAR(formatters); private _distanceFormatters = [_cfgFormatters >> "Distance"] call _fnc_getFormatters; +private _speedFormatters = [_cfgFormatters >> "Speed"] call _fnc_getFormatters; private _azimuthFormatters = [_cfgFormatters >> "Azimuth"] call _fnc_getFormatters; missionNamespace setVariable [QGVAR(distanceFormatters), _distanceFormatters]; +missionNamespace setVariable [QGVAR(speedFormatters), _speedFormatters]; missionNamespace setVariable [QGVAR(azimuthFormatters), _azimuthFormatters]; diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index d6154b3c3..8c17ab8ea 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -37,9 +37,13 @@ private _azimuthToEnd = _startPos getDir _endPos; private _fnc_format = { params ["_distance", "_azimuth", "_formatters"]; - _formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + _formatters params ["_fnc_formatDistance", "_fnc_formatSpeed", "_fnc_formatAzimuth"]; - format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter] + format ["%1 (%2) - %3", + _distance call _fnc_formatDistance, + _distance call _fnc_formatSpeed, + _azimuth call _fnc_formatAzimuth + ] }; if (isNull _ctrlMap) then { // 3D diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf index 8a66cbf1f..a6db5e8ba 100644 --- a/addons/plotting/functions/fnc_drawPlots.sqf +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -27,6 +27,7 @@ private _screenPos = []; private _formatters = [ (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 1, + (GVAR(speedFormatters) select GVAR(currentSpeedFormatter)) select 1, (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 1 ]; diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index 38fc9883e..7697e14ec 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -34,9 +34,9 @@ private _azimuth = _centerPos getDir _endPos; private _fnc_format = { params ["_distance", "_azimuth", "_formatters"]; - _formatters params ["_fnc_distanceFormatter", "_fnc_azimuthFormatter"]; + _formatters params ["_fnc_formatDistance", "", "_fnc_formatAzimuth"]; - format ["%1 - %2", _distance call _fnc_distanceFormatter, _azimuth call _fnc_azimuthFormatter] + format ["%1 - %2", _distance call _fnc_formatDistance, _azimuth call _fnc_formatAzimuth] }; if (isNull _ctrlMap) then { // 3D diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index f25f6d93c..a8204a7b0 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -34,9 +34,9 @@ _offset params ["_a", "_b", "_c"]; private _fnc_format = { params ["_offset", "_formatters"]; _offset params ["_a", "_b", "_c"]; - _formatters params ["_fnc_distanceFormatter"]; + _formatters params ["_fnc_formatDistance"]; - format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_distanceFormatter, _b call _fnc_distanceFormatter, _c call _fnc_distanceFormatter] + format ["X: %1 - Y: %2 - Z: %3", _a call _fnc_formatDistance, _b call _fnc_formatDistance, _c call _fnc_formatDistance] }; if (isNull _ctrlMap) then { // 3D diff --git a/addons/plotting/functions/fnc_formatTravelTime.sqf b/addons/plotting/functions/fnc_formatTravelTime.sqf new file mode 100644 index 000000000..16a388ec2 --- /dev/null +++ b/addons/plotting/functions/fnc_formatTravelTime.sqf @@ -0,0 +1,33 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Formats the travel time to an appropriate format given by distance and speed. + * + * Arguments: + * 0: Distance traveled in meters + * 1: Speed in meters per second + * + * Return Value: + * Formatted travel time in format H:MM:SS or M:SS + * + * Example: + * [100, 0.3] call zen_plotting_fnc_formatTravelTime + * + * Public: No + */ + +params ["_distance", "_speed"]; + +private _time = _distance / _speed; + +private _hours = floor (_time / 3600); +private _seconds = _time - (_hours * 3600); +private _minutes = floor (_seconds / 60); +_seconds = _seconds - (_minutes * 60); + +// return +if (_hours > 0) then { + format ["%1:%2:%3", _hours, [_minutes, 2] call CBA_fnc_formatNumber, [floor _seconds, 2] call CBA_fnc_formatNumber] +} else { + format ["%1:%2", _minutes, [floor _seconds, 2] call CBA_fnc_formatNumber] +} diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf index 8798abd2e..2ca4a267f 100644 --- a/addons/plotting/initKeybinds.inc.sqf +++ b/addons/plotting/initKeybinds.inc.sqf @@ -13,7 +13,23 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; }; }, {}, - [DIK_R, [false, false, false]] // Default: R + [DIK_R, [false, true, false]] // Default: CTRL + R +] call CBA_fnc_addKeybind; + +[ + _category, + QGVAR(toggleSpeedFormat), + [LSTRING(ToggleSpeedFormat), LSTRING(ToggleSpeedFormat_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numFormatters = count GVAR(speedFormatters); + GVAR(currentSpeedFormatter) = (GVAR(currentSpeedFormatter) + 1) % _numFormatters; + + true + }; + }, + {}, + [DIK_R, [true, false, false]] // Default: SHIFT + R ] call CBA_fnc_addKeybind; [ @@ -29,7 +45,7 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; }; }, {}, - [DIK_TAB, [false, false, false]] // Default: Tab + [DIK_T, [false, true, false]] // Default: CTRL + T ] call CBA_fnc_addKeybind; [ diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index 30238068b..043d0179f 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -33,6 +33,14 @@ Switches to the next unit for distances. Wechselt zur nächsten Einheit für Entfernungen. + + Switch Speed for Travel Time + Geschwindigkeit für Fahrzeit durchschalten + + + Switches to the next speed which determines travel time. + Wechselt zur nächsten Geschwindigkeit, die die Fahrzeit bestimmt. + Switch Azimuth Unit Richtungseinheit durchschalten From 726f83c7b89e4c83bc66d7112aaf7f4c16a70982 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Tue, 25 Mar 2025 18:23:43 +0100 Subject: [PATCH 26/32] Add docs --- docs/user_guide/plotting.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/user_guide/plotting.md b/docs/user_guide/plotting.md index 3c1cec898..74a37090d 100644 --- a/docs/user_guide/plotting.md +++ b/docs/user_guide/plotting.md @@ -3,12 +3,13 @@ This feature helps you measure distances and directions with 3D and map plots. You can set the starting and ending points to be either fixed locations or attached to an object, allowing the values to update dynamically. -You can cycle between different distance and azimuth formats using the R and Tab ↹ keys, which can be customized in the CBA Keybinding settings. +You can cycle between different distance and azimuth formats using the Ctrl+R and Ctrl+T keys, which can be customized in the CBA Keybinding settings. You can also change the plot colors in the CBA settings. ## Measure Distance This action creates a simple line. It calculates the 3D distance between the starting and ending points and shows it at both ends of the line. +Next to the distance, the travel time is shown in the format M:SS or H:MM:SS, based on the currently selected speed. You can change the speed by pressing Shift+R by default. The azimuth at the end point indicates the direction from the start point to the end point, while the azimuth at the start point shows the direction from the end point back to the start. ## Measure Radius From a889ec4e7300c94ec1fff09905b9f0d204c3befb Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 26 Mar 2025 17:10:02 +0100 Subject: [PATCH 27/32] Show speed of unit if attached to it --- addons/plotting/CfgFormatters.hpp | 46 ++++++---------- addons/plotting/XEH_PREP.hpp | 1 + addons/plotting/XEH_preInit.sqf | 1 + .../functions/fnc_compileFormatters.sqf | 4 +- addons/plotting/functions/fnc_drawLine.sqf | 54 ++++++++++++------- addons/plotting/functions/fnc_drawPlots.sqf | 28 ++++------ addons/plotting/functions/fnc_drawRadius.sqf | 40 ++++++++++---- .../plotting/functions/fnc_drawRectangle.sqf | 19 +++++-- .../plotting/functions/fnc_formatSpeedKmh.sqf | 28 ++++++++++ .../functions/fnc_formatTravelTime.sqf | 2 +- addons/plotting/initKeybinds.inc.sqf | 20 ++++++- addons/plotting/stringtable.xml | 12 ++++- 12 files changed, 168 insertions(+), 87 deletions(-) create mode 100644 addons/plotting/functions/fnc_formatSpeedKmh.sqf diff --git a/addons/plotting/CfgFormatters.hpp b/addons/plotting/CfgFormatters.hpp index e4e1831be..085dd3556 100644 --- a/addons/plotting/CfgFormatters.hpp +++ b/addons/plotting/CfgFormatters.hpp @@ -10,40 +10,26 @@ class GVAR(formatters) { priority = 90; }; }; - // Formats speed: _this is distance in meters + + // Formats custom speeds: _this is [distance (m), speed (m/s)] class Speed { - class Running { - // Unit with weapon lowered running in fast combat pace: takes ~30 s for 100 m -> 3.333 m/s -> 11.999 km/h - // This is with ACE mod loaded, without ACE it takes about 27 s for 100 m - formatter = QUOTE(FORMAT_1('%1 @ 12 km/h',[ARR_2(_this,3.333)] call FUNC(formatTravelTime))); + class kmh { + formatter = QUOTE(call FUNC(formatSpeedKmh)); priority = 100; - }; - class VehicleSlow { - // Vehicle at 30 km/h -> 8.333 m/s - formatter = QUOTE(FORMAT_1('%1 @ 30 km/h',[ARR_2(_this,8.333)] call FUNC(formatTravelTime))); - priority = 90; - }; - class VehicleTown { - // Vehicle at 50 km/h -> 13.889 m/s - formatter = QUOTE(FORMAT_1('%1 @ 50 km/h',[ARR_2(_this,13.889)] call FUNC(formatTravelTime))); - priority = 80; - }; - class VehicleFast { - // Vehicle at 100 km/h -> 27.778 m/s - formatter = QUOTE(FORMAT_1('%1 @ 100 km/h',[ARR_2(_this,27.778)] call FUNC(formatTravelTime))); - priority = 70; - }; - class Helicopter { - // Helicopter at 250 km/h -> 69.444 m/s - formatter = QUOTE(FORMAT_1('%1 @ 250 km/h',[ARR_2(_this,69.444)] call FUNC(formatTravelTime))); - priority = 60; - }; - class Jet { - // Jet at 600 km/h -> 166.667 m/s - formatter = QUOTE(FORMAT_1('%1 @ 600 km/h',[ARR_2(_this,166.667)] call FUNC(formatTravelTime))); - priority = 50; + + // Defines static speeds for this format to cycle through when plot is not attached to object + // Must have at least one entry + speeds[] = { + 3.333, // Unit with weapon lowered running in fast combat pace: takes ~12 km/h -> 3.333 m/s. This is with ACE mod loaded. Without ACE, the unit is a bit faster. + 8.333, // Vehicle at 30 km/h + 13.889, // Vehicle at 50 km/h + 27.778, // Vehicle at 100 km/h + 69.444, // Helicopter at 250 km/h + 166.667, // Jet at 600 km/h + }; }; }; + // Formats direction: _this is direction in the range 0..360 degrees. class Azimuth { class Degree { diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp index 2f7a7d059..5bac644a5 100644 --- a/addons/plotting/XEH_PREP.hpp +++ b/addons/plotting/XEH_PREP.hpp @@ -5,6 +5,7 @@ PREP(drawLine); PREP(drawPlots); PREP(drawRadius); PREP(drawRectangle); +PREP(formatSpeedKmh); PREP(formatTravelTime); PREP(isPosInCylinder); PREP(onDraw); diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf index 8b4cf5c75..b3a49d50a 100644 --- a/addons/plotting/XEH_preInit.sqf +++ b/addons/plotting/XEH_preInit.sqf @@ -18,6 +18,7 @@ if (hasInterface) then { GVAR(currentDistanceFormatter) = 0; GVAR(currentSpeedFormatter) = 0; + GVAR(currentSpeedIndex) = 0; GVAR(currentAzimuthFormatter) = 0; }; diff --git a/addons/plotting/functions/fnc_compileFormatters.sqf b/addons/plotting/functions/fnc_compileFormatters.sqf index d99ce718d..11f94a1c4 100644 --- a/addons/plotting/functions/fnc_compileFormatters.sqf +++ b/addons/plotting/functions/fnc_compileFormatters.sqf @@ -25,11 +25,13 @@ private _fnc_getFormatters = { private _formatterName = configName _entryConfig; private _formatter = compile getText (_entryConfig >> "formatter"); private _priority = getNumber (_entryConfig >> "priority"); + private _speeds = if (isArray (_entryConfig >> "speeds")) then {getArray (_entryConfig >> "speeds")} else {[]}; private _formatterEntry = [ _formatterName, _formatter, - _priority + _priority, + _speeds ]; _formatters pushBack _formatterEntry; diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index 8c17ab8ea..a465fad06 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -4,8 +4,8 @@ * Draws a line plot in 3D or on the map. Must be called every frame. * * Arguments: - * 0: Start position ASL - * 1: End position ASL + * 0: Start position ASL or attached object + * 1: End position ASL or attached object * 2: Visual properties * 0: Icon * 1: Color RGBA @@ -14,34 +14,52 @@ * 4: Line width * 3: Formatters * 0: Distance formatter - * 1: Azimuth formatter - * 4: Camera position ASL (default: Don't check render distance) + * 1: Travel time formatter + * 2: Azimuth formatter + * 4: Additional arguments (default: []) * 5: Map control (default: Draw in 3D) * * Return Value: * None * * Example: - * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {_this toFixed 1}]] call zen_plotting_fnc_drawLine + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {[_this, 3.333] call FUNC(formatTravelTime)}, {_this toFixed 1}]] call zen_plotting_fnc_drawLine * * Public: No */ -params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +params ["_startPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_args params ["_speed"]; -private _distance = _startPos vectorDistance _endPos; +// Don't draw start icon and line in 3D if Zeus camera is start pos +// Also don't use speed of camera +private _startPosIsNotCamera = curatorCamera isNotEqualTo _startPosOrObj; + +private _startPos = _startPosOrObj; +if (_startPosOrObj isEqualType objNull) then { + _startPos = getPosASLVisual _startPosOrObj; + + if (_startPosIsNotCamera) then { + _speed = velocityModelSpace _startPosOrObj select 1; + }; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; -private _azimuthToStart = _endPos getDir _startPos; -private _azimuthToEnd = _startPos getDir _endPos; +private _distance = _startPos vectorDistance _endPos; +private _azimuth = _startPos getDir _endPos; private _fnc_format = { - params ["_distance", "_azimuth", "_formatters"]; - _formatters params ["_fnc_formatDistance", "_fnc_formatSpeed", "_fnc_formatAzimuth"]; + params ["_distance", "_speed", "_azimuth", "_formatters"]; + _formatters params ["_fnc_formatDistance", "_fnc_formatTime", "_fnc_formatAzimuth"]; format ["%1 (%2) - %3", _distance call _fnc_formatDistance, - _distance call _fnc_formatSpeed, + [_distance, _speed] call _fnc_formatTime, _azimuth call _fnc_formatAzimuth ] }; @@ -49,19 +67,19 @@ private _fnc_format = { if (isNull _ctrlMap) then { // 3D private _camPos = getPosASL curatorCamera; - if (CAN_RENDER_ICON(_camPos,_startPos)) then { - drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle, [_distance, _azimuthToStart, _formatters] call _fnc_format]; + if (_startPosIsNotCamera && {CAN_RENDER_ICON(_camPos,_startPos)}) then { + drawIcon3D [_icon, _color, ASLToAGL _startPos, _scale, _scale, _angle]; }; - if (CAN_RENDER_LINE(_camPos,_startPos,_endPos)) then { + if (_startPosIsNotCamera && {CAN_RENDER_LINE(_camPos,_startPos,_endPos)}) then { drawLine3D [ASLToAGL _startPos, ASLToAGL _endPos, _color, _lineWidth]; }; if (CAN_RENDER_ICON(_camPos,_endPos)) then { - drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_distance, _azimuthToEnd, _formatters] call _fnc_format]; + drawIcon3D [_icon, _color, ASLToAGL _endPos, _scale, _scale, _angle, [_distance, _speed, _azimuth, _formatters] call _fnc_format]; }; } else { // Map - _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle, [_distance, _azimuthToStart, _formatters] call _fnc_format]; + _ctrlMap drawIcon [_icon, _color, _startPos, _scale, _scale, _angle]; _ctrlMap drawLine [_startPos, _endPos, _color]; - _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_distance, _azimuthToEnd, _formatters] call _fnc_format]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_distance, _speed, _azimuth, _formatters] call _fnc_format]; }; diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf index a6db5e8ba..6822856a2 100644 --- a/addons/plotting/functions/fnc_drawPlots.sqf +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -25,12 +25,18 @@ private _d = 0; private _scale = 0; private _screenPos = []; +(GVAR(speedFormatters) select GVAR(currentSpeedFormatter)) params ["", "_fnc_formatTime", "", "_speeds"]; + private _formatters = [ (GVAR(distanceFormatters) select GVAR(currentDistanceFormatter)) select 1, - (GVAR(speedFormatters) select GVAR(currentSpeedFormatter)) select 1, + _fnc_formatTime, (GVAR(azimuthFormatters) select GVAR(currentAzimuthFormatter)) select 1 ]; +private _args = [ + _speeds select GVAR(currentSpeedIndex) +]; + // Format active plot as permanent plot with mouse position as end position private _activePlotWithMouseEndPos = []; if (_activePlot isNotEqualTo []) then { @@ -51,19 +57,7 @@ if (_activePlot isNotEqualTo []) then { { _x params ["_type", "_startPosOrObj", "_endPosOrObj"]; - private _startPos = _startPosOrObj; - if (_startPosOrObj isEqualType objNull) then { - if (isNull _startPosOrObj) then {continue}; - - _startPos = getPosASLVisual _startPosOrObj; - }; - - private _endPos = _endPosOrObj; - if (_endPosOrObj isEqualType objNull) then { - if (isNull _endPosOrObj) then {continue}; - - _endPos = getPosASLVisual _endPosOrObj; - }; + if (_startPosOrObj isEqualTo objNull || {_endPosOrObj isEqualTo objNull}) then {continue}; if (_drawIn3D) then { _scale = ICON_SCALE; @@ -71,12 +65,8 @@ if (_activePlot isNotEqualTo []) then { _scale = MAP_ICON_SCALE; }; - if (_scale < 0.01) then { - continue; - }; - private _visualProperties = [ICON, GVAR(color), _scale, ICON_ANGLE, LINEWIDTH]; - private _drawArgs = [_startPos, _endPos, _visualProperties, _formatters, _ctrlMap]; + private _drawArgs = [_startPosOrObj, _endPosOrObj, _visualProperties, _formatters, _args, _ctrlMap]; if (_type in GVAR(plotTypes)) then { _drawArgs call (GVAR(plotTypes) get _type); }; diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index 7697e14ec..00600a8ec 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -4,8 +4,8 @@ * Draws a radius circle plot in 3D or on the map. Must be called every frame. * * Arguments: - * 0: Center position ASL - * 1: End position ASL + * 0: Circle center position ASL or attached object + * 1: End position ASL or attached object * 2: Visual properties * 0: Icon * 1: Color RGBA @@ -14,29 +14,47 @@ * 4: Line width * 3: Formatters * 0: Distance formatter - * 1: Azimuth formatter - * 3: Map control (default: Draw in 3D) + * 1: Travel time formatter (unused) + * 2: Azimuth formatter + * 4: Additional arguments (default: []) + * 5: Map control (default: Draw in 3D) * * Return Value: * None * * Example: - * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {_this toFixed 1}]] call zen_plotting_fnc_drawRadius + * [[0, 0, 0], [100, 100, 0], ["", [1, 0, 0, 1], 1, 0, 5], [{_this toFixed 0}, {[_this, 3.333] call FUNC(formatTravelTime)}, {_this toFixed 1}]] call zen_plotting_fnc_drawRadius * * Public: No */ -params ["_centerPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +params ["_centerPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +_args params ["_speed"]; + +private _centerPos = _centerPosOrObj; +if (_centerPosOrObj isEqualType objNull) then { + _centerPos = getPosASLVisual _centerPosOrObj; + _speed = speed _centerPosOrObj; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; private _radius = _centerPos vectorDistance _endPos; private _azimuth = _centerPos getDir _endPos; private _fnc_format = { - params ["_distance", "_azimuth", "_formatters"]; - _formatters params ["_fnc_formatDistance", "", "_fnc_formatAzimuth"]; + params ["_distance", "_speed", "_azimuth", "_formatters"]; + _formatters params ["_fnc_formatDistance", "_fnc_formatTime", "_fnc_formatAzimuth"]; - format ["%1 - %2", _distance call _fnc_formatDistance, _azimuth call _fnc_formatAzimuth] + format ["%1 (%2) - %3", + _distance call _fnc_formatDistance, + [_distance, _speed] call _fnc_formatTime, + _azimuth call _fnc_formatAzimuth + ] }; if (isNull _ctrlMap) then { // 3D @@ -51,7 +69,7 @@ if (isNull _ctrlMap) then { // 3D if (_camPos vectorDistance _centerPos <= _radius + MAX_RENDER_DISTANCE) then { drawLine3D [_centerAGL, _endPosAGL, _color, _lineWidth]; - drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, [_radius, _azimuth, _formatters] call _fnc_format]; + drawIcon3D [_icon, _color, _endPosAGL, _scale, _scale, _angle, [_radius, _speed, _azimuth, _formatters] call _fnc_format]; }; private _count = CIRCLE_EDGES_MIN max floor (2 * pi * _radius ^ 0.65 / CIRCLE_RESOLUTION); @@ -75,5 +93,5 @@ if (isNull _ctrlMap) then { // 3D _ctrlMap drawIcon [_icon, _color, _centerPos, _scale, _scale, _angle]; _ctrlMap drawLine [_centerPos, _endPos, _color]; _ctrlMap drawEllipse [_centerPos, _radius, _radius, 0, _color, ""]; - _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_radius, _azimuth, _formatters] call _fnc_format]; + _ctrlMap drawIcon [_icon, _color, _endPos, _scale, _scale, _angle, [_radius, _speed, _azimuth, _formatters] call _fnc_format]; }; diff --git a/addons/plotting/functions/fnc_drawRectangle.sqf b/addons/plotting/functions/fnc_drawRectangle.sqf index a8204a7b0..be829fdcb 100644 --- a/addons/plotting/functions/fnc_drawRectangle.sqf +++ b/addons/plotting/functions/fnc_drawRectangle.sqf @@ -4,8 +4,8 @@ * Draws a rectangle plot in 3D or on the map. Must be called every frame. * * Arguments: - * 0: Start position ASL - * 1: End position ASL + * 0: Start position ASL or attached object + * 1: End position ASL or attached object * 2: Visual properties * 0: Icon * 1: Color RGBA @@ -14,7 +14,8 @@ * 4: Line width * 3: Formatters * 0: Distance formatter - * 3: Map control (default: Draw in 3D) + * 4: Additional arguments (default: []) + * 5: Map control (default: Draw in 3D) * * Return Value: * None @@ -25,9 +26,19 @@ * Public: No */ -params ["_startPos", "_endPos", "_visualProperties", "_formatters", ["_ctrlMap", controlNull, [controlNull]]]; +params ["_startPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; +private _startPos = _startPosOrObj; +if (_startPosOrObj isEqualType objNull) then { + _startPos = getPosASLVisual _startPosOrObj; +}; + +private _endPos = _endPosOrObj; +if (_endPosOrObj isEqualType objNull) then { + _endPos = getPosASLVisual _endPosOrObj; +}; + private _offset = _endPos vectorDiff _startPos; _offset params ["_a", "_b", "_c"]; diff --git a/addons/plotting/functions/fnc_formatSpeedKmh.sqf b/addons/plotting/functions/fnc_formatSpeedKmh.sqf new file mode 100644 index 000000000..a877c1035 --- /dev/null +++ b/addons/plotting/functions/fnc_formatSpeedKmh.sqf @@ -0,0 +1,28 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Formats the given distance and speed. + * + * Arguments: + * 0: Distance traveled in meters + * 1: Absolute speed in meters per second + * + * Return Value: + * Formatted travel time + * + * Example: + * [100, 3.3] call zen_plotting_fnc_formatTravelTime + * + * Public: No + */ + +params [["_distance", 0, [0]], ["_speed", 0, [0]]]; + +private _kmh = round (abs _speed * 3.6); + +// return +if (_kmh > 0) then { + format ["%1 @ %2 km/h", [_distance, _speed] call FUNC(formatTravelTime), _kmh toFixed 0] +} else { + "∞ @ 0 km/h" +} diff --git a/addons/plotting/functions/fnc_formatTravelTime.sqf b/addons/plotting/functions/fnc_formatTravelTime.sqf index 16a388ec2..fd80f5378 100644 --- a/addons/plotting/functions/fnc_formatTravelTime.sqf +++ b/addons/plotting/functions/fnc_formatTravelTime.sqf @@ -11,7 +11,7 @@ * Formatted travel time in format H:MM:SS or M:SS * * Example: - * [100, 0.3] call zen_plotting_fnc_formatTravelTime + * [100, 3.3] call zen_plotting_fnc_formatTravelTime * * Public: No */ diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf index 2ca4a267f..51dd97177 100644 --- a/addons/plotting/initKeybinds.inc.sqf +++ b/addons/plotting/initKeybinds.inc.sqf @@ -16,6 +16,22 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; [DIK_R, [false, true, false]] // Default: CTRL + R ] call CBA_fnc_addKeybind; +[ + _category, + QGVAR(toggleSpeed), + [LSTRING(ToggleSpeed), LSTRING(ToggleSpeed_Description)], + { + if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { + private _numSpeeds = count (GVAR(speedFormatters) select GVAR(currentSpeedFormatter) select 3); + GVAR(currentSpeedIndex) = (GVAR(currentSpeedIndex) + 1) % _numSpeeds; + + true + }; + }, + {}, + [DIK_R, [true, false, false]] // Default: SHIFT + R +] call CBA_fnc_addKeybind; + [ _category, QGVAR(toggleSpeedFormat), @@ -25,11 +41,13 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; private _numFormatters = count GVAR(speedFormatters); GVAR(currentSpeedFormatter) = (GVAR(currentSpeedFormatter) + 1) % _numFormatters; + GVAR(currentSpeedIndex) = 0; + true }; }, {}, - [DIK_R, [true, false, false]] // Default: SHIFT + R + [DIK_R, [false, false, true]] // Default: ALT + R ] call CBA_fnc_addKeybind; [ diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index 043d0179f..e2c288992 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -33,14 +33,22 @@ Switches to the next unit for distances. Wechselt zur nächsten Einheit für Entfernungen. - + Switch Speed for Travel Time Geschwindigkeit für Fahrzeit durchschalten - + Switches to the next speed which determines travel time. Wechselt zur nächsten Geschwindigkeit, die die Fahrzeit bestimmt. + + Switch Speed Unit + Geschwindigkeitseinheit durchschalten + + + Switches to the next unit for speeds. + Wechselt zur nächsten Einheit für Geschwindigkeiten. + Switch Azimuth Unit Richtungseinheit durchschalten From 6acd716b0119da7cb3f4dd46ffd2cae4e2bf4619 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 26 Mar 2025 17:17:45 +0100 Subject: [PATCH 28/32] Update docs --- docs/user_guide/plotting.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/docs/user_guide/plotting.md b/docs/user_guide/plotting.md index 74a37090d..839e8eaa1 100644 --- a/docs/user_guide/plotting.md +++ b/docs/user_guide/plotting.md @@ -3,19 +3,24 @@ This feature helps you measure distances and directions with 3D and map plots. You can set the starting and ending points to be either fixed locations or attached to an object, allowing the values to update dynamically. -You can cycle between different distance and azimuth formats using the Ctrl+R and Ctrl+T keys, which can be customized in the CBA Keybinding settings. +You can cycle between different distance, speed, and azimuth formats using the Ctrl+R, Alt+R, and Ctrl+T keys, which can be customized in the CBA Keybinding settings. You can also change the plot colors in the CBA settings. ## Measure Distance -This action creates a simple line. It calculates the 3D distance between the starting and ending points and shows it at both ends of the line. -Next to the distance, the travel time is shown in the format M:SS or H:MM:SS, based on the currently selected speed. You can change the speed by pressing Shift+R by default. -The azimuth at the end point indicates the direction from the start point to the end point, while the azimuth at the start point shows the direction from the end point back to the start. +This action creates a simple line. It calculates the 3D distance between the starting and ending points and shows it at the end of the line. + +Next to the distance, the travel time is shown in the format M:SS or H:MM:SS, based on the currently selected speed. +You can change the speed by pressing Shift+R by default. +If the start point is attached to a unit, the speed of the unit is used instead. + +The azimuth at the end point indicates the direction from the start point to the end point. ## Measure Radius This action draws a circle with the starting point at the center. The radius of the circle is determined by the 3D distance between the start and end points. +Same as in [Measure Distance](#measure-distance), the travel time is shown next to the distance. The azimuth at the end point indicates the direction from the center to the end point. ## Measure Offset From 743e11d8b2926a425abe4bb0d7d2be5f596da18d Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 26 Mar 2025 18:25:22 +0100 Subject: [PATCH 29/32] Limit attaching end points to one unit at a time --- addons/plotting/CfgContext.hpp | 2 +- addons/plotting/XEH_PREP.hpp | 1 + addons/plotting/XEH_postInit.sqf | 3 --- addons/plotting/XEH_preInit.sqf | 10 +++++++- addons/plotting/functions/fnc_addPlot.sqf | 18 ++++++++++--- addons/plotting/functions/fnc_clearPlots.sqf | 3 ++- addons/plotting/functions/fnc_drawLine.sqf | 2 +- addons/plotting/functions/fnc_drawPlots.sqf | 13 +++------- addons/plotting/functions/fnc_drawRadius.sqf | 2 +- addons/plotting/functions/fnc_isValidPlot.sqf | 25 +++++++++++++++++++ addons/plotting/functions/fnc_onDraw.sqf | 2 +- addons/plotting/functions/fnc_onDraw3D.sqf | 2 +- .../functions/fnc_onMouseButtonDown.sqf | 18 ++++++++++--- .../plotting/functions/fnc_setActivePlot.sqf | 2 +- addons/plotting/initKeybinds.inc.sqf | 20 +++++++++------ addons/plotting/script_component.hpp | 2 +- addons/plotting/stringtable.xml | 4 +++ 17 files changed, 92 insertions(+), 37 deletions(-) create mode 100644 addons/plotting/functions/fnc_isValidPlot.sqf diff --git a/addons/plotting/CfgContext.hpp b/addons/plotting/CfgContext.hpp index 8b5817734..5ee677e95 100644 --- a/addons/plotting/CfgContext.hpp +++ b/addons/plotting/CfgContext.hpp @@ -33,7 +33,7 @@ class EGVAR(context_menu,actions) { class ClearPlots { displayName = CSTRING(ClearPlots); icon = "A3\3den\Data\Displays\Display3DEN\PanelLeft\entityList_delete_ca.paa"; - statement = QUOTE(call FUNC(clearPlots)); + statement = QUOTE([QQGVAR(plotsCleared)] call CBA_fnc_localEvent); }; }; }; diff --git a/addons/plotting/XEH_PREP.hpp b/addons/plotting/XEH_PREP.hpp index 5bac644a5..6d70988a2 100644 --- a/addons/plotting/XEH_PREP.hpp +++ b/addons/plotting/XEH_PREP.hpp @@ -8,6 +8,7 @@ PREP(drawRectangle); PREP(formatSpeedKmh); PREP(formatTravelTime); PREP(isPosInCylinder); +PREP(isValidPlot); PREP(onDraw); PREP(onDraw3D); PREP(onKeyDown); diff --git a/addons/plotting/XEH_postInit.sqf b/addons/plotting/XEH_postInit.sqf index cf18ef9b4..10087eff4 100644 --- a/addons/plotting/XEH_postInit.sqf +++ b/addons/plotting/XEH_postInit.sqf @@ -1,9 +1,6 @@ #include "script_component.hpp" if (hasInterface) then { - // All plots ordered by creation time, last is newest - GVAR(plots) = []; - ["zen_curatorDisplayLoaded", LINKFUNC(onLoad)] call CBA_fnc_addEventHandler; ["zen_curatorDisplayUnloaded", LINKFUNC(onUnload)] call CBA_fnc_addEventHandler; diff --git a/addons/plotting/XEH_preInit.sqf b/addons/plotting/XEH_preInit.sqf index b3a49d50a..64fd9570b 100644 --- a/addons/plotting/XEH_preInit.sqf +++ b/addons/plotting/XEH_preInit.sqf @@ -7,7 +7,6 @@ PREP_RECOMPILE_START; PREP_RECOMPILE_END; if (hasInterface) then { - GVAR(activePlot) = []; GVAR(draw3DAdded) = false; GVAR(plotTypes) = createHashMapFromArray [ @@ -20,6 +19,15 @@ if (hasInterface) then { GVAR(currentSpeedFormatter) = 0; GVAR(currentSpeedIndex) = 0; GVAR(currentAzimuthFormatter) = 0; + + // Unique ID for creating plots + GVAR(nextID) = 0; + // Currently active plot shown at cursor in format [type, start] + GVAR(activePlot) = []; + // All plots, keys are IDs, values [type, start, end] + GVAR(plots) = createHashMap; + // All plot IDs ordered by creation time, last is newest + GVAR(history) = []; }; call FUNC(compileFormatters); diff --git a/addons/plotting/functions/fnc_addPlot.sqf b/addons/plotting/functions/fnc_addPlot.sqf index 051e490fc..e13344556 100644 --- a/addons/plotting/functions/fnc_addPlot.sqf +++ b/addons/plotting/functions/fnc_addPlot.sqf @@ -9,7 +9,7 @@ * 2: End position ASL or attached object * * Return Value: - * Index of new plot + * ID of new plot or -1 on error * * Example: * ["RADIUS", player, [100, 100, 10]] call zen_plotting_fnc_addPlot @@ -23,6 +23,18 @@ params [ ["_endPos", [0, 0, 0], [[], objNull], [3]] ]; -if !(_type in GVAR(plotTypes)) exitWith {-1}; +if (!(_type in GVAR(plotTypes)) || {_startPos isEqualTo objNull} || {_endPos isEqualTo objNull}) exitWith {-1}; -GVAR(plots) pushBack [_type, _startPos, _endPos] +private _id = GVAR(nextID); +GVAR(plots) set [_id, [_type, _startPos, _endPos]]; +GVAR(history) pushBack _id; + +GVAR(nextID) = GVAR(nextID) + 1; + +if (_endPos isEqualType objNull) then { + _endPos setVariable [QGVAR(attachedPlot), _id]; +}; + +TRACE_4("Plot added",_id,_type,_startPos,_endPos); + +_id diff --git a/addons/plotting/functions/fnc_clearPlots.sqf b/addons/plotting/functions/fnc_clearPlots.sqf index fc1175238..254590e1f 100644 --- a/addons/plotting/functions/fnc_clearPlots.sqf +++ b/addons/plotting/functions/fnc_clearPlots.sqf @@ -15,4 +15,5 @@ * Public: No */ -GVAR(plots) = []; +GVAR(plots) = createHashMap; +GVAR(history) = []; diff --git a/addons/plotting/functions/fnc_drawLine.sqf b/addons/plotting/functions/fnc_drawLine.sqf index a465fad06..eaa0ad142 100644 --- a/addons/plotting/functions/fnc_drawLine.sqf +++ b/addons/plotting/functions/fnc_drawLine.sqf @@ -30,7 +30,7 @@ params ["_startPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; -_args params ["_speed"]; +_args params [["_speed", 0, [0]]]; // Don't draw start icon and line in 3D if Zeus camera is start pos // Also don't use speed of camera diff --git a/addons/plotting/functions/fnc_drawPlots.sqf b/addons/plotting/functions/fnc_drawPlots.sqf index 6822856a2..76fb99b04 100644 --- a/addons/plotting/functions/fnc_drawPlots.sqf +++ b/addons/plotting/functions/fnc_drawPlots.sqf @@ -21,10 +21,6 @@ params ["_plots", ["_activePlot", [], [[]]], ["_ctrlMap", controlNull, [controlN private _drawIn3D = isNull _ctrlMap; -private _d = 0; -private _scale = 0; -private _screenPos = []; - (GVAR(speedFormatters) select GVAR(currentSpeedFormatter)) params ["", "_fnc_formatTime", "", "_speeds"]; private _formatters = [ @@ -57,16 +53,13 @@ if (_activePlot isNotEqualTo []) then { { _x params ["_type", "_startPosOrObj", "_endPosOrObj"]; + // Skip invalid plots if (_startPosOrObj isEqualTo objNull || {_endPosOrObj isEqualTo objNull}) then {continue}; - if (_drawIn3D) then { - _scale = ICON_SCALE; - } else { - _scale = MAP_ICON_SCALE; - }; - + private _scale = [MAP_ICON_SCALE, ICON_SCALE] select _drawIn3D; private _visualProperties = [ICON, GVAR(color), _scale, ICON_ANGLE, LINEWIDTH]; private _drawArgs = [_startPosOrObj, _endPosOrObj, _visualProperties, _formatters, _args, _ctrlMap]; + if (_type in GVAR(plotTypes)) then { _drawArgs call (GVAR(plotTypes) get _type); }; diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index 00600a8ec..aec542db2 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -30,7 +30,7 @@ params ["_centerPosOrObj", "_endPosOrObj", "_visualProperties", "_formatters", ["_args", [], [[]]], ["_ctrlMap", controlNull, [controlNull]]]; _visualProperties params ["_icon", "_color", "_scale", "_angle", "_lineWidth"]; -_args params ["_speed"]; +_args params [["_speed", 0, [0]]]; private _centerPos = _centerPosOrObj; if (_centerPosOrObj isEqualType objNull) then { diff --git a/addons/plotting/functions/fnc_isValidPlot.sqf b/addons/plotting/functions/fnc_isValidPlot.sqf new file mode 100644 index 000000000..09420cb94 --- /dev/null +++ b/addons/plotting/functions/fnc_isValidPlot.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Authors: Timi007 + * Checks if the given plot identified by an ID is still valid. + * + * Arguments: + * 0: Plot ID + * + * Return Value: + * Plot is valid + * + * Example: + * [1] call zen_plotting_fnc_isValidPlot + * + * Public: No + */ + +params ["_id"]; + +if !(_id in GVAR(plots)) exitWith {false}; + +(GVAR(plots) get _id) params ["", "_startPos", "_endPos"]; + +(_startPos isNotEqualTo objNull) +&& {_endPos isNotEqualTo objNull} diff --git a/addons/plotting/functions/fnc_onDraw.sqf b/addons/plotting/functions/fnc_onDraw.sqf index ad71549a0..19ab03139 100644 --- a/addons/plotting/functions/fnc_onDraw.sqf +++ b/addons/plotting/functions/fnc_onDraw.sqf @@ -21,6 +21,6 @@ params ["_ctrlMap"]; if (dialog || {call EFUNC(common,isInScreenshotMode)}) exitWith {}; // Dialog is open or HUD is hidden -[GVAR(plots), GVAR(activePlot), _ctrlMap] call FUNC(drawPlots); +[values GVAR(plots), GVAR(activePlot), _ctrlMap] call FUNC(drawPlots); END_COUNTER(onDraw); diff --git a/addons/plotting/functions/fnc_onDraw3D.sqf b/addons/plotting/functions/fnc_onDraw3D.sqf index 4c06ac1f1..65eb8d046 100644 --- a/addons/plotting/functions/fnc_onDraw3D.sqf +++ b/addons/plotting/functions/fnc_onDraw3D.sqf @@ -24,6 +24,6 @@ if ( || {call EFUNC(common,isInScreenshotMode)} // HUD is hidden ) exitWith {}; -[GVAR(plots), GVAR(activePlot)] call FUNC(drawPlots); +[values GVAR(plots), GVAR(activePlot)] call FUNC(drawPlots); END_COUNTER(onDraw3D); diff --git a/addons/plotting/functions/fnc_onMouseButtonDown.sqf b/addons/plotting/functions/fnc_onMouseButtonDown.sqf index 96d1ed145..8f392efd6 100644 --- a/addons/plotting/functions/fnc_onMouseButtonDown.sqf +++ b/addons/plotting/functions/fnc_onMouseButtonDown.sqf @@ -36,11 +36,21 @@ if (call EFUNC(common,isCursorOnMouseArea)) then { default {[EGVAR(common,mousePos), 2] call EFUNC(common,getPosFromScreen)}; }; - GVAR(activePlot) params ["_type", "_startPosOrObj"]; + // Check if end point already has a valid plot attached to it + private _attachedPlot = -1; + if (_endPosOrObj isEqualType objNull) then { + _attachedPlot = _endPosOrObj getVariable [QGVAR(attachedPlot), -1]; + }; + + if (_attachedPlot isNotEqualTo -1 && {[_attachedPlot] call FUNC(isValidPlot)}) then { + [LSTRING(ErrorCannotAttachPlot)] call EFUNC(common,showMessage); + } else { + // Add current active plot to permanent ones + GVAR(activePlot) params ["_type", "_startPosOrObj"]; - // Add current active plot to permanent ones - TRACE_4("Add plot",_type,_startPosOrObj,_endPosOrObj,_this); - [QGVAR(plotAdded), [_type, _startPosOrObj, _endPosOrObj]] call CBA_fnc_localEvent; + TRACE_4("Add plot",_type,_startPosOrObj,_endPosOrObj,_this); + [QGVAR(plotAdded), [_type, _startPosOrObj, _endPosOrObj]] call CBA_fnc_localEvent; + }; }; GVAR(activePlot) = []; diff --git a/addons/plotting/functions/fnc_setActivePlot.sqf b/addons/plotting/functions/fnc_setActivePlot.sqf index 0fc40e2be..eefb648fb 100644 --- a/addons/plotting/functions/fnc_setActivePlot.sqf +++ b/addons/plotting/functions/fnc_setActivePlot.sqf @@ -19,4 +19,4 @@ params [["_type", "LINE", [""]], ["_startPos", [0, 0, 0], [[], objNull], [3]]]; GVAR(activePlot) = [_type, _startPos]; - +[QGVAR(selectingEndPos), [_type, _startPos]] call CBA_fnc_localEvent; diff --git a/addons/plotting/initKeybinds.inc.sqf b/addons/plotting/initKeybinds.inc.sqf index 51dd97177..92c7bc5db 100644 --- a/addons/plotting/initKeybinds.inc.sqf +++ b/addons/plotting/initKeybinds.inc.sqf @@ -72,17 +72,21 @@ private _category = [ELSTRING(main,DisplayName), LSTRING(DisplayName)]; [LSTRING(DeleteLastPlot), LSTRING(DeleteLastPlot_Description)], { if (!isNull curatorCamera && {!GETMVAR(RscDisplayCurator_search,false)}) then { - if (GVAR(plots) isNotEqualTo []) then { - private _lastPlot = GVAR(plots) deleteAt [-1]; - TRACE_1("Removed last plot",_lastPlot); + if (GVAR(history) isNotEqualTo []) then { + private _lastPlotId = (GVAR(history) deleteAt [-1]) select 0; + private _lastPlot = GVAR(plots) deleteAt _lastPlotId; + [QGVAR(plotDeleted), [_lastPlotId] + _lastPlot] call CBA_fnc_localEvent; + TRACE_2("Removed last plot",_lastPlotId,_lastPlot); // Automatically delete invalid plots (e.g. when attached object does not exist anymore) - while {GVAR(plots) isNotEqualTo []} do { - (GVAR(plots) select -1) params ["", "_startPos", "_endPos"]; + while {GVAR(history) isNotEqualTo []} do { + _lastPlotId = GVAR(history) select -1; - if ((_startPos isEqualTo objNull) || {_endPos isEqualTo objNull}) then { - _lastPlot = GVAR(plots) deleteAt [-1]; - TRACE_1("Automatically removed next invalid plot",_lastPlot); + if !([_lastPlotId] call FUNC(isValidPlot)) then { + GVAR(history) deleteAt [-1]; + _lastPlot = GVAR(plots) deleteAt _lastPlotId; + [QGVAR(plotDeleted), [_lastPlotId] + _lastPlot] call CBA_fnc_localEvent; + TRACE_2("Automatically removed next invalid plot",_lastPlotId,_lastPlot); }; }; }; diff --git a/addons/plotting/script_component.hpp b/addons/plotting/script_component.hpp index e9ea4b298..bcb73cdbf 100644 --- a/addons/plotting/script_component.hpp +++ b/addons/plotting/script_component.hpp @@ -23,7 +23,7 @@ #define ICON_ANGLE 0 #define ICON_SCALE 1 #define MAP_ICON_SCALE 24 -#define LINEWIDTH 10 +#define LINEWIDTH 5 #define CIRCLE_EDGES_MIN 12 #define CIRCLE_RESOLUTION 5 diff --git a/addons/plotting/stringtable.xml b/addons/plotting/stringtable.xml index e2c288992..1386b2eb3 100644 --- a/addons/plotting/stringtable.xml +++ b/addons/plotting/stringtable.xml @@ -65,5 +65,9 @@ Deletes the last plot created. Entfernt den zuletzt erstellten Plot. + + Cannot attach more than one plot endpoint to a unit + Einer Einheit kann nicht mehr als ein Plot-Endpunkt angehängt werden + From dde0706e6ec4705a8dbb20f7274189767c1c8726 Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 26 Mar 2025 18:28:52 +0100 Subject: [PATCH 30/32] Update docs --- docs/user_guide/plotting.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/user_guide/plotting.md b/docs/user_guide/plotting.md index 839e8eaa1..6d538e6c5 100644 --- a/docs/user_guide/plotting.md +++ b/docs/user_guide/plotting.md @@ -2,6 +2,7 @@ This feature helps you measure distances and directions with 3D and map plots. You can set the starting and ending points to be either fixed locations or attached to an object, allowing the values to update dynamically. +A unit can only have one end point connected to it at a time. You can cycle between different distance, speed, and azimuth formats using the Ctrl+R, Alt+R, and Ctrl+T keys, which can be customized in the CBA Keybinding settings. You can also change the plot colors in the CBA settings. From 746d92189dc44e9bd10bfadaaa49470b9c20ccab Mon Sep 17 00:00:00 2001 From: Timi007 Date: Wed, 26 Mar 2025 18:49:00 +0100 Subject: [PATCH 31/32] Fix speed for radius --- addons/plotting/functions/fnc_drawRadius.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index aec542db2..2066a4bcf 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -35,7 +35,7 @@ _args params [["_speed", 0, [0]]]; private _centerPos = _centerPosOrObj; if (_centerPosOrObj isEqualType objNull) then { _centerPos = getPosASLVisual _centerPosOrObj; - _speed = speed _centerPosOrObj; + _speed = velocityModelSpace _startPosOrObj select 1; }; private _endPos = _endPosOrObj; From 24411f2424fc5969ffc9991f17ae6a474aecd11f Mon Sep 17 00:00:00 2001 From: Timi007 Date: Thu, 9 Oct 2025 14:00:59 +0200 Subject: [PATCH 32/32] Fix speed for radius --- addons/plotting/functions/fnc_drawRadius.sqf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/plotting/functions/fnc_drawRadius.sqf b/addons/plotting/functions/fnc_drawRadius.sqf index 2066a4bcf..803d697c3 100644 --- a/addons/plotting/functions/fnc_drawRadius.sqf +++ b/addons/plotting/functions/fnc_drawRadius.sqf @@ -35,7 +35,7 @@ _args params [["_speed", 0, [0]]]; private _centerPos = _centerPosOrObj; if (_centerPosOrObj isEqualType objNull) then { _centerPos = getPosASLVisual _centerPosOrObj; - _speed = velocityModelSpace _startPosOrObj select 1; + _speed = velocityModelSpace _centerPosOrObj select 1; }; private _endPos = _endPosOrObj;