diff --git a/.github/workflows/test-indicators-tick.yml b/.github/workflows/test-indicators-tick.yml new file mode 100644 index 000000000..25eabe43c --- /dev/null +++ b/.github/workflows/test-indicators-tick.yml @@ -0,0 +1,65 @@ +--- +name: Test Indicators (Tick) + +# yamllint disable-line rule:truthy +on: + pull_request: + paths: + - 'Indicator**' + - 'Indicators/Tick/**' + - '.github/workflows/test-indicators-tick.yml' + push: + paths: + - 'Indicator**' + - 'Indicators/Tick/**' + - '.github/workflows/test-indicators-tick.yml' + +jobs: + + Compile: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + uses: fx31337/mql-compile-action@master + with: + init-platform: true + path: 'Indicators/Tick/tests' + verbose: true + - name: Print compiled files + run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname' + shell: powershell + - name: Upload artifacts (MQL4) + uses: actions/upload-artifact@v2 + with: + name: files-ex4 + path: '**/*.ex4' + - name: Upload artifacts (MQL5) + uses: actions/upload-artifact@v2 + with: + name: files-ex5 + path: '**/*.ex5' + + Indicators-Tests-MQL4: + defaults: + run: + shell: bash + working-directory: Indicators/tests + needs: Compile + runs-on: ubuntu-latest + strategy: + matrix: + test: + - Indi_Tick.test + steps: + - uses: actions/download-artifact@v2 + with: + name: files-ex4 + - name: Run ${{ matrix.test }} + uses: fx31337/mql-tester-action@master + with: + BtDays: 4-8 + BtMonths: 1 + BtYears: 2020 + TestExpert: ${{ matrix.test }} + timeout-minutes: 10 diff --git a/.github/workflows/test-task.yml b/.github/workflows/test-task.yml new file mode 100644 index 000000000..faccb308a --- /dev/null +++ b/.github/workflows/test-task.yml @@ -0,0 +1,67 @@ +--- +name: Test Task + +# yamllint disable-line rule:truthy +on: + pull_request: + paths: + - 'Task/**' + - '.github/workflows/test-task.yml' + push: + paths: + - 'Task/**' + - '.github/workflows/test-task.yml' + +jobs: + + Compile: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + uses: fx31337/mql-compile-action@master + with: + init-platform: true + path: 'Task/tests' + verbose: true + - name: Print compiled files + run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname' + shell: powershell + - name: Upload artifacts (MQL4) + uses: actions/upload-artifact@v2 + with: + name: files-ex4 + path: '**/*.ex4' + - name: Upload artifacts (MQL5) + uses: actions/upload-artifact@v2 + with: + name: files-ex5 + path: '**/*.ex5' + + Task-Tests-MQL4: + defaults: + run: + shell: bash + working-directory: Task/tests + needs: Compile + runs-on: ubuntu-latest + strategy: + matrix: + test: + - Task.test + - TaskAction.test + - TaskCondition.test + - TaskGetter.test + - TaskManager.test + - TaskObject.test + - TaskSetter.test + - Taskable.car.test + - Taskable.test + steps: + - uses: actions/download-artifact@v2 + with: + name: files-ex4 + - name: Run ${{ matrix.test }} + uses: fx31337/mql-tester-action@master + with: + Script: ${{ matrix.test }} diff --git a/.github/workflows/test-tick.yml b/.github/workflows/test-tick.yml new file mode 100644 index 000000000..941fe371a --- /dev/null +++ b/.github/workflows/test-tick.yml @@ -0,0 +1,58 @@ +--- +name: Test Tick + +# yamllint disable-line rule:truthy +on: + pull_request: + paths: + - 'Tick/**.h' + - '.github/workflows/test-tick.yml' + push: + paths: + - 'Tick/**.h' + - '.github/workflows/test-tick.yml' + +jobs: + + Compile: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + uses: fx31337/mql-compile-action@master + with: + path: 'Tick/tests' + verbose: true + - name: Print compiled files + run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname' + shell: powershell + - name: Upload artifacts (MQL4) + uses: actions/upload-artifact@v2 + with: + name: files-ex4 + path: '**/*.ex4' + - name: Upload artifacts (MQL5) + uses: actions/upload-artifact@v2 + with: + name: files-ex5 + path: '**/*.ex5' + + Tick-Tests-MQL4: + defaults: + run: + shell: bash + working-directory: Tick/tests + needs: Compile + runs-on: ubuntu-latest + strategy: + matrix: + test: + - TickManager.test + steps: + - uses: actions/download-artifact@v2 + with: + name: files-ex4 + - name: Run ${{ matrix.test }} + uses: fx31337/mql-tester-action@master + with: + Script: ${{ matrix.test }} diff --git a/.github/workflows/test-trade.yml b/.github/workflows/test-trade.yml index 4ab779efd..a3a9091ec 100644 --- a/.github/workflows/test-trade.yml +++ b/.github/workflows/test-trade.yml @@ -6,10 +6,12 @@ on: pull_request: paths: - 'Trade/**.h' + - 'Trade/**.mq?' - '.github/workflows/test-trade.yml' push: paths: - 'Trade/**.h' + - 'Trade/**.mq?' - '.github/workflows/test-trade.yml' jobs: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7e9f32e28..7040970bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,7 +27,7 @@ jobs: - name: Compile uses: fx31337/mql-compile-action@master with: - init-platform: true + mt-version: 4.0.0.1349 verbose: true - name: Print compiled files run: '(Get-ChildItem -Recurse -Path . -Include *.ex[45]).fullname' @@ -54,12 +54,10 @@ jobs: matrix: test: - AccountTest - - ActionTest - BufferStructTest - BufferTest - ChartTest - CompileIndicatorsTest - - ConditionTest - DatabaseTest - DrawIndicatorTest - EATest @@ -76,7 +74,6 @@ jobs: - StrategyTest-RSI - SymbolInfoTest - SummaryReportTest - - TaskTest - TickerTest - TradeTest steps: @@ -89,6 +86,7 @@ jobs: BtDays: 1-8 BtMonths: 1 BtYears: 2020 + MtVersion: 4.0.0.1349 TestExpert: ${{ matrix.test }} timeout-minutes: 10 @@ -102,6 +100,7 @@ jobs: strategy: matrix: test: + # - 3DTest - CollectionTest - ConfigTest - ConvertTest diff --git a/3D/Chart3D.h b/3D/Chart3D.h new file mode 100644 index 000000000..aedfdbe05 --- /dev/null +++ b/3D/Chart3D.h @@ -0,0 +1,225 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * 3D Chart. + */ + +#include "../Bar.struct.h" +#include "../Indicators/Indi_MA.mqh" +#include "../Instances.h" +#include "../Refs.mqh" +#include "../SerializerConverter.mqh" +#include "../SerializerJson.mqh" +#include "Chart3DCandles.h" +#include "Chart3DType.h" +#include "Cube.h" +#include "Device.h" +#include "Interface.h" + +#ifdef __MQL5__ +// Resource variables. +#resource "Shaders/chart3d_vs.hlsl" as string Chart3DShaderSourceVS; +#resource "Shaders/chart3d_ps.hlsl" as string Chart3DShaderSourcePS; +#endif + +typedef BarOHLC (*Chart3DPriceFetcher)(ENUM_TIMEFRAMES, int); + +// Type of the currently rendered 3d chart. +enum ENUM_CHART3D_TYPE { + CHART3D_TYPE_BARS, + CHART3D_TYPE_CANDLES, + CHART3D_TYPE_LINES, +}; + +class Chart3D; + +void chart3d_interface_listener(InterfaceEvent& _event, void* _target) { + Chart3D* chart3d = (Chart3D*)_target; + chart3d.OnInterfaceEvent(_event); +} + +/** + * 3D chart renderer. + */ +class Chart3D : public Dynamic { + // Camera offset. Z component indicates number of bars per screen's width. + DXVector3 offset; + + // Current chart type. + ENUM_CHART3D_TYPE type; + + // References to chart type renderers. + Ref renderers[3]; + + // OHLC prices fetcher callback. + Chart3DPriceFetcher price_fetcher; + + // Whether graphics were initialized. + bool initialized; + + // Shaders. + Ref shader_vs; + Ref shader_ps; + + Chart3DType* current_renderer; + + Instances instances; + + public: + /** + * Constructor. + */ + Chart3D(Chart3DPriceFetcher _price_fetcher, ENUM_CHART3D_TYPE _type = CHART3D_TYPE_CANDLES) : instances(&this) { + price_fetcher = _price_fetcher; + type = _type; + offset.x = offset.y = 0.0f; + offset.z = 25.0f; + initialized = false; +#ifdef __MQL5__ + Interface::AddListener(chart3d_interface_listener, &this); +#endif + } + + void OnInterfaceEvent(InterfaceEvent& _event) { + if (GetCurrentRenderer() == NULL) { + return; + } + + Device* _gfx = GetCurrentRenderer().GetDevice(); + + _gfx.DrawText(10, 10, "Event!"); + } + + Shader* GetShaderVS() { return shader_vs.Ptr(); } + + Shader* GetShaderPS() { return shader_ps.Ptr(); } + + Chart3DType* GetCurrentRenderer() { return current_renderer; } + + Chart3DType* GetRenderer(Device* _device) { + if (!initialized) { + // shader_vs = _device.VertexShader(Chart3DShaderSourceVS, Vertex::Layout); + // shader_ps = _device.PixelShader(Chart3DShaderSourcePS); + initialized = true; + } + + if (!renderers[type].IsSet()) { + switch (type) { + case CHART3D_TYPE_BARS: + // renderers[type] = new Chart3DBars(_device); + break; + case CHART3D_TYPE_CANDLES: + renderers[type] = new Chart3DCandles(&this, _device); + break; + case CHART3D_TYPE_LINES: + // renderers[type] = new Chart3DLines(_device); + break; + default: + Alert("Internal error: Wrong type for Chart3D in Chart3D::GetRenderer()!"); + DebugBreak(); + return NULL; + } + } + + current_renderer = renderers[type].Ptr(); + + return renderers[type].Ptr(); + } + + /** + * Returns given bar's OHLC. + */ + BarOHLC GetPrice(ENUM_TIMEFRAMES _tf, int _shift) { + BarOHLC _ohlc; + return _ohlc; + // return price_fetcher(_tf, _shift); // @fixme: 'price_fetcher' - internal error #%d + } + + /** + * Return first shift that are visible on the screen. Values is away from 0. + */ + int GetBarsVisibleShiftStart() { return 80; } + + /** + * Return last shift that are visible on the screen. Value is closer to 0. + */ + int GetBarsVisibleShiftEnd() { return 0; } + + /** + * Returns lowest price of bars on the screen. + */ + float GetMinBarsPrice() { + return (float)ChartStatic::iLow( + Symbol(), PERIOD_CURRENT, + ChartStatic::iLowest(Symbol(), PERIOD_CURRENT, MODE_LOW, GetBarsVisibleShiftStart() - GetBarsVisibleShiftEnd(), + GetBarsVisibleShiftEnd())); + } + + /** + * Returns highest price of bars on the screen. + */ + float GetMaxBarsPrice() { + return (float)ChartStatic::iHigh( + Symbol(), PERIOD_CURRENT, + ChartStatic::iHighest(Symbol(), PERIOD_CURRENT, MODE_HIGH, + GetBarsVisibleShiftStart() - GetBarsVisibleShiftEnd(), GetBarsVisibleShiftEnd())); + } + + /** + * Returns number of bars that are visible on te screen. + */ + int GetBarsVisibleCount() { return GetBarsVisibleShiftStart() - GetBarsVisibleShiftEnd() + 1; } + + /** + * Returns absolute x coordinate of bar on the screen. Must not be affected by camera's x offset. + */ + float GetBarPositionX(int _shift) { return -(float)GetBarsVisibleCount() * 1.35f / 2.0f + 1.35f * _shift; } + + /** + * Returns y coordinate of price on the screen. Takes into consideration zoom and min/max prices on the screen. + */ + float GetPriceScale(float price) { + float _scale_y = 40.0f; + float _price_min = GetMinBarsPrice(); + float _price_max = GetMaxBarsPrice(); + float _result = 1.0f / (_price_max - _price_min) * (price - _price_min) * _scale_y - (_scale_y / 2); + return _result; + } + + /** + * Renders chart. + */ + void Render(Device* _device) { + Chart3DType* _type_renderer = GetRenderer(_device); + + BarOHLC _ohlc; + // BarOHLC _ohlc = price_fetcher(PERIOD_CURRENT, 0); // @fixme: 'price_fetcher' - internal error #%d + +#ifdef __debug__ + Print(SerializerConverter::FromObject(_ohlc).ToString()); +#endif + + _type_renderer.Render(_device); + } +}; diff --git a/3D/Chart3DCandles.h b/3D/Chart3DCandles.h new file mode 100644 index 000000000..1340d9be5 --- /dev/null +++ b/3D/Chart3DCandles.h @@ -0,0 +1,103 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * 3D chart candles renderer. + */ + +#include "Chart3DType.h" +#include "Cube.h" +#include "Device.h" +#include "Vertex.h" + +class Chart3D; + +/** + * 3D chart candles renderer. + */ +class Chart3DCandles : public Chart3DType { + Ref> cube1; + Ref> cube2; + Ref> cube3; + + public: + /** + * Constructor. + */ + Chart3DCandles(Chart3D* _chart3d, Device* _device) : Chart3DType(_chart3d, _device) { + cube1 = new Cube(1.0f, 1.0f, 1.0f); + cube2 = new Cube(0.10f, 1.0f, 0.10f); + cube3 = new Cube(1.0f, 0.075f, 0.075f); + } + + /** + * Renders chart. + */ + virtual void Render(Device* _device) { + TSR _tsr; + + for (int _shift = chart3d.GetBarsVisibleShiftStart(); _shift != chart3d.GetBarsVisibleShiftEnd(); --_shift) { + BarOHLC _ohlc = chart3d.GetPrice(PERIOD_CURRENT, _shift); + + float _height = chart3d.GetPriceScale(_ohlc.GetMaxOC()) - chart3d.GetPriceScale(_ohlc.GetMinOC()); + float higher = _ohlc.IsBear(); + + cube1.Ptr().GetTSR().translation.x = chart3d.GetBarPositionX(_shift); + cube1.Ptr().GetTSR().translation.y = chart3d.GetPriceScale(_ohlc.GetMinOC()) + _height / 2; + cube1.Ptr().GetTSR().scale.y = _height; + + // Print(cube1.Ptr().GetTSR().translation.y); + + cube1.Ptr().GetMaterial().SetColor(higher ? 0x22FF11 : 0xFF1122); + _device.Render(cube1.Ptr()); + + cube2.Ptr().GetTSR().translation.x = chart3d.GetBarPositionX(_shift); + float _line_height = chart3d.GetPriceScale(_ohlc.GetHigh()) - chart3d.GetPriceScale(_ohlc.GetLow()); + cube2.Ptr().GetTSR().translation.y = chart3d.GetPriceScale(_ohlc.GetLow()) + _line_height / 2; + cube2.Ptr().GetTSR().scale.y = _line_height; + cube2.Ptr().GetMaterial().SetColor(higher ? 0x22FF11 : 0xFF1122); + _device.Render(cube2.Ptr()); + } + + int _digits = (int)MarketInfo(Symbol(), MODE_DIGITS); + float _pip_pow = (float)MathPow(10, _digits); + float _pip_size = 1.0f / (float)MathPow(10, _digits); + float _pip_size_m1 = 1.0f / (float)MathPow(10, _digits - 1); + float _start = float(int(chart3d.GetMinBarsPrice() * _pip_pow) * _pip_size); + float _end = float(int(chart3d.GetMaxBarsPrice() * _pip_pow) * _pip_size); + + // Rendering price lines. + for (double _s = _start; _s < _end + _pip_size_m1; _s += _pip_size * 10) { + float _y = chart3d.GetPriceScale((float)_s); + + cube3.Ptr().GetTSR().translation.y = _y; + cube3.Ptr().GetTSR().scale.x = 200.0f; + + _device.DrawText(5, _y, StringFormat("%." + IntegerToString(_digits) + "f", _s), 0x90FFFFFF, TA_LEFT | TA_VCENTER, + GFX_DRAW_TEXT_FLAG_2D_COORD_X); + + cube3.Ptr().GetMaterial().SetColor(0x333333); + _device.Render(cube3.Ptr()); + } + } +}; diff --git a/3D/Chart3DType.h b/3D/Chart3DType.h new file mode 100644 index 000000000..9e49737f7 --- /dev/null +++ b/3D/Chart3DType.h @@ -0,0 +1,54 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * 3D chart type renderer. + */ + +#include "../Refs.mqh" +#include "Device.h" + +class Chart3D; +class Device; + +/** + * 3D chart type renderer. + */ +class Chart3DType : public Dynamic { + protected: + Chart3D* chart3d; + Device* device; + + public: + /** + * Constructor. + */ + Chart3DType(Chart3D* _chart3d, Device* _device) : chart3d(_chart3d), device(_device) {} + + Device* GetDevice() { return device; } + + /** + * Renders chart. + */ + virtual void Render(Device* _device) {} +}; diff --git a/3D/Cube.h b/3D/Cube.h new file mode 100644 index 000000000..0bc9038ff --- /dev/null +++ b/3D/Cube.h @@ -0,0 +1,84 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Cube mesh. + */ + +#include "Face.h" +#include "Mesh.h" + +#ifdef __MQL5__ +// Resource variables. +#resource "Shaders/cube_ps.hlsl" as string ShaderCubeSourcePS; +#resource "Shaders/cube_vs.hlsl" as string ShaderCubeSourceVS; +#endif + +/** + * Cube mesh. + */ +template +class Cube : public Mesh { + public: + Cube(float size_x, float size_y, float size_z, float x = 0.0f, float y = 0.0f, float z = 0.0f) + : Mesh(MESH_TYPE_SEPARATE_POINTS) { + float half_x = size_x / 2; + float half_y = size_y / 2; + float half_z = size_z / 2; + + Face f1(x - half_x, y - half_y, z - half_z, x - half_x, y + half_y, z - half_z, x + half_x, y + half_y, + z - half_z, x + half_x, y - half_y, z - half_z); + + Face f2(x + half_x, y - half_y, z + half_z, x + half_x, y + half_y, z + half_z, x - half_x, y + half_y, + z + half_z, x - half_x, y - half_y, z + half_z); + + Face f3(x - half_x, y - half_y, z + half_z, x - half_x, y + half_y, z + half_z, x - half_x, y + half_y, + z - half_z, x - half_x, y - half_y, z - half_z); + + Face f4(x + half_x, y - half_y, z - half_z, x + half_x, y + half_y, z - half_z, x + half_x, y + half_y, + z + half_z, x + half_x, y - half_y, z + half_z); + + Face f5(x - half_x, y - half_y, z + half_z, x - half_x, y - half_y, z - half_z, x + half_x, y - half_y, + z - half_z, x + half_x, y - half_y, z + half_z); + + Face f6(x - half_x, y + half_y, z - half_z, x - half_x, y + half_y, z + half_z, x + half_x, y + half_y, + z + half_z, x + half_x, y + half_y, z - half_z); + + AddFace(f1); + AddFace(f2); + AddFace(f3); + AddFace(f4); + AddFace(f5); + AddFace(f6); + } + +#ifdef __MQL5__ + /** + * Initializes graphics device-related things. + */ + virtual void Initialize(Device* _device) { + SetShaderVS(_device.VertexShader(ShaderCubeSourceVS, T::Layout)); + SetShaderPS(_device.PixelShader(ShaderCubeSourcePS)); + } +#endif +}; diff --git a/3D/Device.h b/3D/Device.h new file mode 100644 index 000000000..d7b64f94f --- /dev/null +++ b/3D/Device.h @@ -0,0 +1,312 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics device. + */ + +#include "../Refs.mqh" +#include "../Util.h" +#include "Frontend.h" +#include "IndexBuffer.h" +#include "Material.h" +#include "Math.h" +#include "Mesh.h" +#include "Shader.h" +#include "VertexBuffer.h" + +enum GFX_DRAW_TEXT_FLAGS { GFX_DRAW_TEXT_FLAG_NONE, GFX_DRAW_TEXT_FLAG_2D_COORD_X, GFX_DRAW_TEXT_FLAG_2D_COORD_Y }; + +enum ENUM_CLEAR_BUFFER_TYPE { CLEAR_BUFFER_TYPE_COLOR, CLEAR_BUFFER_TYPE_DEPTH }; + +/** + * Graphics device. + */ +class Device : public Dynamic { + protected: + int context; + Ref frontend; + DXMatrix mtx_stack[]; + DXMatrix mtx_world; + DXMatrix mtx_view; + DXMatrix mtx_projection; + DXVector3 lightdir; + Material material; + + public: + /** + * Initializes graphics device. + */ + bool Start(Frontend* _frontend) { + frontend = _frontend; + DXMatrixIdentity(mtx_world); + DXMatrixIdentity(mtx_view); + DXMatrixIdentity(mtx_projection); + TSR _identity; + PushTransform(_identity); + lightdir = DXVector3(-0.2f, 0.2f, 1.0f); + return Init(_frontend); + } + + void PushTransform(const TSR& tsr) { + Util::ArrayPush(mtx_stack, mtx_world); + DXMatrixMultiply(mtx_world, tsr.ToMatrix(), mtx_world); + } + + void PopTransform() { mtx_world = Util::ArrayPop(mtx_stack); } + + /** + * Begins render loop. + */ + Device* Begin(unsigned int clear_color = 0) { + frontend.Ptr().RenderBegin(context); + Clear(clear_color); + ClearDepth(); + RenderBegin(); + return &this; + } + + /** + * Ends render loop. + */ + Device* End() { + RenderEnd(); + frontend.Ptr().RenderEnd(context); + frontend.Ptr().ProcessDrawText(); + return &this; + } + + /** + * Deinitializes graphics device. + */ + Device* Stop() { + Deinit(); + return &this; + } + + /** + * Clears scene's color buffer. + */ + Device* Clear(unsigned int _color = 0xFF000000) { + ClearBuffer(CLEAR_BUFFER_TYPE_COLOR, _color); + return &this; + } + + /** + * Begins scene's depth buffer. + */ + Device* ClearDepth() { + ClearBuffer(CLEAR_BUFFER_TYPE_DEPTH, 0); + return &this; + } + + /** + * Returns current material. + */ + Material GetMaterial() { return material; } + + /** + * Assigns material for later rendering. + */ + void SetMaterial(Material& _material) { material = _material; } + + /** + * Returns graphics device context as integer. + */ + int Context() { return context; } + + /** + * Creates vertex shader to be used by current graphics device. + */ + virtual Shader* VertexShader(string _source_code, const ShaderVertexLayout& _layout[], + string _entry_point = "main") = NULL; + + /** + * Creates pixel shader to be used by current graphics device. + */ + virtual Shader* PixelShader(string _source_code, string _entry_point = "main") = NULL; + + /** + * Creates vertex buffer to be used by current graphics device. + */ + template + VertexBuffer* VertexBuffer(T& data[]) { + VertexBuffer* _buff = VertexBuffer(); + // Unfortunately we can't make this method virtual. + if (dynamic_cast(_buff) != NULL) { +// MT5's DirectX. +#ifdef __debug__ + Print("Filling vertex buffer via MTDXVertexBuffer"); +#endif + ((MTDXVertexBuffer*)_buff).Fill(data); + } else { + Alert("Unsupported vertex buffer device target"); + } + return _buff; + } + + /** + * Creates vertex buffer to be used by current graphics device. + */ + virtual VertexBuffer* VertexBuffer() = NULL; + + /** + * Creates index buffer to be used by current graphics device. + */ + virtual IndexBuffer* IndexBuffer(unsigned int& _indices[]) = NULL; + + /** + * Renders vertex buffer with optional point indices. + */ + void Render(VertexBuffer* _vertices, IndexBuffer* _indices = NULL) { RenderBuffers(_vertices, _indices); } + + /** + * Renders vertex buffer with optional point indices. + */ + virtual void RenderBuffers(VertexBuffer* _vertices, IndexBuffer* _indices = NULL) = NULL; + + /** + * Renders given mesh. + */ + template + void Render(Mesh* _mesh, Shader* _vs = NULL, Shader* _ps = NULL) { +#ifdef __debug__ + Print("Rendering mesh"); +#endif + VertexBuffer* _vertices; + IndexBuffer* _indices; + _mesh.GetBuffers(&this, _vertices, _indices); + + SetMaterial(_mesh.GetMaterial()); + + PushTransform(_mesh.GetTSR()); + + SetShader(_vs != NULL ? _vs : _mesh.GetShaderVS()); + SetShader(_ps != NULL ? _ps : _mesh.GetShaderPS()); + + Render(_vertices, _indices); + + PopTransform(); + } + + /** + * Activates shader for rendering. + */ + void SetShader(Shader* _shader) { _shader.Select(); } + + /** + * Activates shaders for rendering. + */ + void SetShader(Shader* _shader1, Shader* _shader2) { + _shader1.Select(); + _shader2.Select(); + } + + /** + * Returns front-end's viewport width. + */ + int Width() { return frontend.Ptr().Width(); } + + /** + * Returns front-end's viewport height. + */ + int Height() { return frontend.Ptr().Height(); } + + void SetCameraOrtho3D(float _pos_x = 0.0f, float _pos_y = 0.0f, float _pos_z = 0.0f) { + DXMatrixOrthoLH(mtx_projection, 1.0f * _pos_z, 1.0f / Width() * Height() * _pos_z, -10000, 10000); + } + + DXMatrix GetWorldMatrix() { return mtx_world; } + + void SetWorldMatrix(DXMatrix& _matrix) { mtx_world = _matrix; } + + DXMatrix GetViewMatrix() { return mtx_view; } + + void SetViewMatrix(DXMatrix& _matrix) { mtx_view = _matrix; } + + DXMatrix GetProjectionMatrix() { return mtx_projection; } + + void SetProjectionMatrix(DXMatrix& _matrix) { mtx_projection = _matrix; } + + DXVector3 GetLightDirection() { return lightdir; } + + void SetLightDirection(float x, float y, float z) { + lightdir.x = x; + lightdir.y = y; + lightdir.z = z; + } + + /** + * Enqueues text to be drawn directly into the pixel buffer. Queue will be processed in the Device::End() method. + */ + void DrawText(float _x, float _y, string _text, unsigned int _color = 0xFFFFFFFF, unsigned int _align = 0, + unsigned int _flags = 0) { + DViewport _viewport; + _viewport.x = 0; + _viewport.y = 0; + _viewport.width = frontend.Ptr().Width(); + _viewport.height = frontend.Ptr().Height(); + _viewport.minz = -10000.0f; + _viewport.maxz = 10000.0f; + + DXVector3 _vec3_in(_x, _y, 0.0f); + DXVector3 _vec3_out; + DXVec3Project(_vec3_out, _vec3_in, _viewport, GetProjectionMatrix(), GetViewMatrix(), GetWorldMatrix()); + + if ((_flags & GFX_DRAW_TEXT_FLAG_2D_COORD_X) == GFX_DRAW_TEXT_FLAG_2D_COORD_X) { + _vec3_out.x = _x; + } + + if ((_flags & GFX_DRAW_TEXT_FLAG_2D_COORD_Y) == GFX_DRAW_TEXT_FLAG_2D_COORD_Y) { + _vec3_out.y = _y; + } + + frontend.Ptr().DrawText(_vec3_out.x, _vec3_out.y, _text, _color, _align); + } + + protected: + /** + * Initializes graphics device. + */ + virtual bool Init(Frontend*) = NULL; + + /** + * Deinitializes graphics device. + */ + virtual bool Deinit() = NULL; + + /** + * Starts rendering loop. + */ + virtual bool RenderBegin() = NULL; + + /** + * Ends rendering loop. + */ + virtual bool RenderEnd() = NULL; + + /** + * Clears color buffer. + */ + virtual void ClearBuffer(ENUM_CLEAR_BUFFER_TYPE _type, unsigned int _color) = NULL; +}; diff --git a/3D/Devices/MTDX/MTDXDevice.h b/3D/Devices/MTDX/MTDXDevice.h new file mode 100644 index 000000000..7bb1c93b8 --- /dev/null +++ b/3D/Devices/MTDX/MTDXDevice.h @@ -0,0 +1,157 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * MetaTrader DX-targeted graphics device. + */ + +#include "../../Device.h" + +class MTDXDevice : public Device { + public: + /** + * Initializes graphics device. + */ + bool Init(Frontend* _frontend) { +#ifdef __debug__ + Print("MTDXDevice: DXContextCreate: width = ", _frontend.Width(), ", height = ", _frontend.Height()); +#endif + context = DXContextCreate(_frontend.Width(), _frontend.Height()); +#ifdef __debug__ + Print("LastError: ", GetLastError()); + Print("MTDXDevice: context = ", context); +#endif + _frontend.Init(); + return true; + } + + /** + * Deinitializes graphics device. + */ + bool Deinit() { + DXRelease(context); + return true; + } + + /** + * Starts rendering loop. + */ + virtual bool RenderBegin() { return true; } + + /** + * Ends rendering loop. + */ + virtual bool RenderEnd() { return true; } + + /** + * Returns DX context's id. + */ + int Context() { return context; } + + /** + * Clears color buffer. + */ + /** + * Clears color buffer. + */ + virtual void ClearBuffer(ENUM_CLEAR_BUFFER_TYPE _type, unsigned int _color = 0x000000) { + if (_type == CLEAR_BUFFER_TYPE_COLOR) { + DXVector _dx_color; + _dx_color.x = 1.0f / 255.0f * ((_color & 0x00FF0000) >> 16); + _dx_color.y = 1.0f / 255.0f * ((_color & 0x0000FF00) >> 8); + _dx_color.z = 1.0f / 255.0f * ((_color & 0x000000FF) >> 0); + _dx_color.w = 1.0f / 255.0f * ((_color & 0xFF000000) >> 24); + DXContextClearColors(context, _dx_color); +#ifdef __debug__ + Print("DXContextClearColors: LastError: ", GetLastError()); +#endif + } else if (_type == CLEAR_BUFFER_TYPE_DEPTH) { + DXContextClearDepth(context); +#ifdef __debug__ + Print("DXContextClearDepth: LastError: ", GetLastError()); +#endif + } + } + + /** + * Creates index buffer to be used by current graphics device. + */ + IndexBuffer* IndexBuffer() { return NULL; } + + /** + * Creates vertex shader to be used by current graphics device. + */ + virtual Shader* VertexShader(string _source_code, const ShaderVertexLayout& _layout[], string _entry_point = "main") { + MTDXShader* _shader = new MTDXShader(&this); + _shader.Create(SHADER_TYPE_VS, _source_code, _entry_point); + _shader.SetDataLayout(_layout); + return _shader; + } + + /** + * Creates pixel shader to be used by current graphics device. + */ + virtual Shader* PixelShader(string _source_code, string _entry_point = "main") { + MTDXShader* _shader = new MTDXShader(&this); + _shader.Create(SHADER_TYPE_PS, _source_code, _entry_point); + return _shader; + } + + /** + * Creates vertex buffer to be used by current graphics device. + */ + VertexBuffer* VertexBuffer() { return new MTDXVertexBuffer(&this); } + + /** + * Creates index buffer to be used by current graphics device. + */ + virtual IndexBuffer* IndexBuffer(unsigned int& _indices[]) { + IndexBuffer* _buffer = new MTDXIndexBuffer(&this); + _buffer.Fill(_indices); + return _buffer; + } + + /** + * + */ + virtual void RenderBuffers(VertexBuffer* _vertices, IndexBuffer* _indices = NULL) { + DXPrimiveTopologySet(context, DX_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + _vertices.Select(); + if (_indices == NULL) { + if (!DXDraw(context)) { +#ifdef __debug__ + Print("Can't draw!"); +#endif + } +#ifdef __debug__ + Print("DXDraw: LastError: ", GetLastError()); +#endif + } else { + _indices.Select(); + DXDrawIndexed(context); +#ifdef __debug__ + Print("DXDrawIndexed: LastError: ", GetLastError()); +#endif + } + } +}; diff --git a/3D/Devices/MTDX/MTDXIndexBuffer.h b/3D/Devices/MTDX/MTDXIndexBuffer.h new file mode 100644 index 000000000..6662aacab --- /dev/null +++ b/3D/Devices/MTDX/MTDXIndexBuffer.h @@ -0,0 +1,69 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * MetaTrader DX-targeted graphics device's index buffer. + */ + +#include "../../IndexBuffer.h" + +class MTDXIndexBuffer : public IndexBuffer { + public: + MTDXIndexBuffer(Device* _device) : IndexBuffer(_device) {} + + protected: + int handle; + + /** + * Creates index buffer. + */ + virtual bool Create(void*& _data[]) { + // handle = DXBufferCreate(Device().Context(), DX_BUFFER_INDEX, &_data); + return handle != INVALID_HANDLE; + } + + /** + * Destructor; + */ + ~MTDXIndexBuffer() { DXRelease(handle); } + + /** + * Fills index buffer with indices. + */ + virtual void Fill(unsigned int& _indices[]) { + handle = DXBufferCreate(GetDevice().Context(), DX_BUFFER_INDEX, _indices); + } + + /** + * Activates index buffer for rendering. + */ + virtual void Select() { +#ifdef __debug__ + Print("Selecting indices ", handle); +#endif + DXBufferSet(GetDevice().Context(), handle); +#ifdef __debug__ + Print("Select: LastError: ", GetLastError()); +#endif + } +}; diff --git a/3D/Devices/MTDX/MTDXShader.h b/3D/Devices/MTDX/MTDXShader.h new file mode 100644 index 000000000..c3dad254a --- /dev/null +++ b/3D/Devices/MTDX/MTDXShader.h @@ -0,0 +1,201 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * MetaTrader DX-targeted unversal graphics shader. + */ + +#include "../../Shader.h" + +class MTDXShader : public Shader { + // DX context handle. + int handle; + + // DX MVP's C-Buffer handle (register b0). + int cbuffer_mvp_handle; + + MVPBuffer mvp_buffer; + + // DX C-Buffer handle. (register b1). + int cbuffer_handle; + + public: + /** + * Constructor. + */ + MTDXShader(Device* _device) : Shader(_device) {} + + /** + * Destructor. + */ + ~MTDXShader() { + DXRelease(cbuffer_handle); + DXRelease(cbuffer_mvp_handle); + DXRelease(handle); + } + + /** + * Creates a shader. + */ + bool Create(ENUM_SHADER_TYPE _type, string _source_code, string _entry_point = "main") { + string error_text; + + handle = DXShaderCreate(GetDevice().Context(), _type == SHADER_TYPE_VS ? DX_SHADER_VERTEX : DX_SHADER_PIXEL, + _source_code, _entry_point, error_text); + +#ifdef __debug__ + Print("DXShaderCreate: LastError: ", GetLastError(), ", ErrorText: ", error_text); +#endif + + cbuffer_handle = 0; + + // Creating MVP buffer. + cbuffer_mvp_handle = DXInputCreate(GetDevice().Context(), sizeof(MVPBuffer)); +#ifdef __debug__ + Print("DXInputCreate (mvp): LastError: ", GetLastError()); +#endif + + return true; + } + + /** + * Sets vertex/pixel data layout to be used by shader. + */ + virtual void SetDataLayout(const ShaderVertexLayout& _layout[]) { + // Converting generic layout into MT5 DX's one. + + DXVertexLayout _target_layout[]; + ArrayResize(_target_layout, ArraySize(_layout)); + +#ifdef __debug__ + Print("ArrayResize: LastError: ", GetLastError()); +#endif + + int i; + + for (i = 0; i < ArraySize(_layout); ++i) { + _target_layout[i].semantic_name = _layout[i].name; + _target_layout[i].semantic_index = _layout[i].index; + _target_layout[i].format = ParseFormat(_layout[i]); + } + +#ifdef __debug__ + for (i = 0; i < ArraySize(_target_layout); ++i) { + Print(_target_layout[i].semantic_name, ", ", _target_layout[i].semantic_index, ", ", + EnumToString(_target_layout[i].format)); + } + + Print("before DXShaderSetLayout: LastError: ", GetLastError()); +#endif + + DXShaderSetLayout(handle, _target_layout); + +#ifdef __debug__ + Print("DXShaderSetLayout: LastError: ", GetLastError()); +#endif + + ResetLastError(); + } + +#ifdef __MQL5__ + /** + * Converts vertex layout's item into required DX's color format. + */ + ENUM_DX_FORMAT ParseFormat(const ShaderVertexLayout& _layout) { + if (_layout.type == GFX_VAR_TYPE_FLOAT) { + switch (_layout.num_components) { + case 1: + return DX_FORMAT_R32_FLOAT; + case 2: + return DX_FORMAT_R32G32_FLOAT; + case 3: + return DX_FORMAT_R32G32B32_FLOAT; + case 4: + return DX_FORMAT_R32G32B32A32_FLOAT; + default: + Alert("Too many components in vertex layout!"); + } + } + + Alert("Wrong vertex layout!"); + return (ENUM_DX_FORMAT)0; + } +#endif + + /** + * Sets custom input buffer for shader. + */ + template + void SetCBuffer(const X& data) { + if (cbuffer_handle == 0) { + cbuffer_handle = DXInputCreate(GetDevice().Context(), sizeof(X)); +#ifdef __debug__ + Print("DXInputCreate: LastError: ", GetLastError()); +#endif + + int _input_handles[1]; + _input_handles[0] = cbuffer_handle; + + DXShaderInputsSet(handle, _input_handles); +#ifdef __debug__ + Print("DXShaderInputsSet: LastError: ", GetLastError()); +#endif + } + + DXInputSet(cbuffer_handle, data); +#ifdef __debug__ + Print("DXInputSet: LastError: ", GetLastError()); +#endif + } + + /** + * Selectes shader to be used by graphics device for rendering. + */ + virtual void Select() { + // Setting MVP transform and material information. + + DXMatrixTranspose(mvp_buffer.world, GetDevice().GetWorldMatrix()); + DXMatrixTranspose(mvp_buffer.view, GetDevice().GetViewMatrix()); + DXMatrixTranspose(mvp_buffer.projection, GetDevice().GetProjectionMatrix()); + mvp_buffer.lightdir = GetDevice().GetLightDirection(); + mvp_buffer.mat_color = GetDevice().GetMaterial().Color; + + if (cbuffer_handle == 0) { + int _input_handles[1]; + _input_handles[0] = cbuffer_mvp_handle; + DXShaderInputsSet(handle, _input_handles); + } else { + int _input_handles[2]; + _input_handles[0] = cbuffer_mvp_handle; + _input_handles[1] = cbuffer_handle; + DXShaderInputsSet(handle, _input_handles); + } + +#ifdef __debug__ + Print("DXShaderInputsSet: LastError: ", GetLastError()); +#endif + + DXInputSet(cbuffer_mvp_handle, mvp_buffer); + DXShaderSet(GetDevice().Context(), handle); + } +}; diff --git a/3D/Devices/MTDX/MTDXVertexBuffer.h b/3D/Devices/MTDX/MTDXVertexBuffer.h new file mode 100644 index 000000000..e96b9c7b7 --- /dev/null +++ b/3D/Devices/MTDX/MTDXVertexBuffer.h @@ -0,0 +1,63 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * MetaTrader DX-targeted graphics vertex buffer. + */ + +#include "../../VertexBuffer.h" + +class MTDXVertexBuffer : public VertexBuffer { + int handle; + + public: + MTDXVertexBuffer(Device* _device) : VertexBuffer(_device) {} + + ~MTDXVertexBuffer() { + // DXRelease(handle); + } + + public: + /** + * Creates vertex buffer. + */ + template + bool Fill(X& _data[]) { + handle = DXBufferCreate(GetDevice().Context(), DX_BUFFER_VERTEX, _data); +#ifdef __debug__ + Print("Created vb ", handle); + Print("Fill: LastError: ", GetLastError()); +#endif + return true; + } + + virtual void Select() { +#ifdef __debug__ + Print("Selecting vb ", handle); +#endif + DXBufferSet(GetDevice().Context(), handle); +#ifdef __debug__ + Print("Select: LastError: ", GetLastError()); +#endif + } +}; diff --git a/3D/Face.h b/3D/Face.h new file mode 100644 index 000000000..558c0bc4e --- /dev/null +++ b/3D/Face.h @@ -0,0 +1,96 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics face object. + */ + +#include "Math.h" + +// Face flags. +enum ENUM_FACE_FLAGS { FACE_FLAGS_NONE, FACE_FLAGS_TRIANGLE, FACE_FLAGS_QUAD }; + +// Face (3 or 4 vertices). +template +struct Face { + // Flags. + ENUM_FACE_FLAGS flags; + + // 3 or 4 points. + T points[4]; + + /** + * Constructor. + */ + Face() { flags = FACE_FLAGS_NONE; } + + /** + * Constructor. + */ + Face(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3) { + flags = FACE_FLAGS_TRIANGLE; + points[0].Position.x = x1; + points[0].Position.y = y1; + points[0].Position.z = z1; + points[1].Position.x = x2; + points[1].Position.y = y2; + points[1].Position.z = z2; + points[2].Position.x = x3; + points[2].Position.y = y3; + points[2].Position.z = z3; + } + + /** + * Constructor. + */ + Face(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, + float z4) { + flags = FACE_FLAGS_QUAD; + points[0].Position.x = x1; + points[0].Position.y = y1; + points[0].Position.z = z1; + points[1].Position.x = x2; + points[1].Position.y = y2; + points[1].Position.z = z2; + points[2].Position.x = x3; + points[2].Position.y = y3; + points[2].Position.z = z3; + points[3].Position.x = x4; + points[3].Position.y = y4; + points[3].Position.z = z4; + } + + void UpdateNormal() { + DXVector3 _normal, _v1, _v2; + + DXVec3Subtract(_v1, points[1].Position, points[0].Position); + DXVec3Subtract(_v2, points[2].Position, points[0].Position); + + DXVec3Cross(_normal, _v1, _v2); + DXVec3Normalize(_normal, _normal); + + for (int i = 0; i < 4; ++i) { + points[i].Normal = _normal; + } + } +}; diff --git a/3D/Frontend.h b/3D/Frontend.h new file mode 100644 index 000000000..8080c65d4 --- /dev/null +++ b/3D/Frontend.h @@ -0,0 +1,112 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics front-end (display buffer target). + */ + +#include "../Refs.mqh" + +struct DrawTextQueueItem { + float x; + float y; + string text; + unsigned int rgb; + unsigned int align; +}; + +/** + * Represents visual target (OS window/canvas for rendering). + */ +class Frontend : public Dynamic { + protected: + DrawTextQueueItem draw_text_queue[]; + + public: + /** + * Initializes canvas. + */ + bool Start() { return Init(); } + + /** + * Deinitializes canvas. + */ + bool End() { return Deinit(); } + + /** + * Initializes canvas. + */ + virtual bool Init() = NULL; + + /** + * Deinitializes canvas. + */ + virtual bool Deinit() = NULL; + + /** + * Executed before render starts. + */ + virtual void RenderBegin(int context) = NULL; + + /** + * Executed after render ends. + */ + virtual void RenderEnd(int context) = NULL; + + /** + * Returns canvas' width. + */ + virtual int Width() = NULL; + + /** + * Returns canvas' height. + */ + virtual int Height() = NULL; + + /** + * Enqueues text to be drawn directly into the pixel buffer. Queue will be processed in the Device::End() method. + */ + virtual void DrawText(float _x, float _y, string _text, unsigned int _color = 0xFFFFFFFF, unsigned int _align = 0) { + DrawTextQueueItem _item; + _item.x = _x; + _item.y = _y; + _item.text = _text; + _item.rgb = _color; + _item.align = _align; + Util::ArrayPush(draw_text_queue, _item); + } + + void ProcessDrawText() { + for (int i = 0; i < ArraySize(draw_text_queue); ++i) { + DrawTextQueueItem _item = draw_text_queue[i]; + DrawTextNow((int)_item.x, (int)_item.y, _item.text, _item.rgb, _item.align); + } + ArrayResize(draw_text_queue, 0); + } + + protected: + /** + * Draws text directly into the pixel buffer. Should be executed after all 3d drawing. + */ + virtual void DrawTextNow(int _x, int _y, string _text, unsigned int _color = 0xFFFFFFFF, unsigned int _align = 0) {} +}; diff --git a/3D/Frontends/MT5Frontend.h b/3D/Frontends/MT5Frontend.h new file mode 100644 index 000000000..9bcec9e73 --- /dev/null +++ b/3D/Frontends/MT5Frontend.h @@ -0,0 +1,173 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * MT5 graphics front-end (display buffer target). + */ + +#include "../Frontend.h" + +/** + * MetaTrader 5 chart target. + */ +class MT5Frontend : public Frontend { + // Target image pixel buffer. + unsigned int image[]; + + // Previous size of the window. + int last_width, last_height; + + // Target image's resource name. + string resname; + + // Target image's object name. + string objname; + + public: + /** + * Initializes canvas. + */ + virtual bool Init() { + // Hiding 2D chart. + ChartSetInteger(0, CHART_SHOW, false); + ChartRedraw(); + +#ifdef __debug__ + Print("MT5 Frontend: LastError: ", GetLastError()); +#endif + + objname = "MT5_Frontend_" + IntegerToString(ChartID()); + resname = "::MT5_Frontend" + IntegerToString(ChartID()); + ObjectCreate(0, objname, OBJ_BITMAP_LABEL, 0, 0, 0); + ObjectSetInteger(0, objname, OBJPROP_XDISTANCE, 0); + ObjectSetInteger(0, objname, OBJPROP_YDISTANCE, 0); +#ifdef __debug__ + Print("MT5 Frontend: ObjectCreate/Set: LastError: ", GetLastError()); + Print("ResourceCreate: width = ", Width(), ", height = ", Height()); +#endif + ObjectSetString(ChartID(), objname, OBJPROP_BMPFILE, resname); +#ifdef __debug__ + Print("LastError: ", GetLastError()); +#endif + return true; + } + + /** + * Deinitializes canvas. + */ + virtual bool Deinit() { + ResourceFree(resname); + ObjectDelete(0, objname); + ChartSetInteger(0, CHART_SHOW, true); + ChartRedraw(); + return true; + } + + /** + * Resizes target image buffer if needed. + */ + bool Resize() { + if (Width() == last_width && Height() == last_height) { + return false; + } + + ArrayResize(image, Width() * Height()); +#ifdef __debug__ + Print("resname = ", resname, ", image_size = ", ArraySize(image), ", width = ", Width(), ", height = ", Height()); +#endif + ResourceCreate(resname, image, Width(), Height(), 0, 0, Width(), COLOR_FORMAT_ARGB_NORMALIZE); +#ifdef __debug__ + Print("ResourceCreate: LastError: ", GetLastError()); +#endif + + last_width = Width(); + last_height = Height(); + + return true; + } + + /** + * Executed before render starts. + */ + virtual void RenderBegin(int context) { +#ifdef __debug__ + Print("MT5Frontend: RenderBegin()"); + Print("Image resize: width = ", Width(), ", height = ", Height()); +#endif + + if (Resize()) { + DXContextSetSize(context, Width(), Height()); + } + +#ifdef __debug__ + Print("DXContextSetSize: LastError: ", GetLastError()); +#endif + } + + /** + * Executed after render ends. + */ + virtual void RenderEnd(int context) { +#ifdef __debug__ + Print("MT5Frontend: RenderEnd()"); + Print("ResourceCreate: width = ", Width(), ", height = ", Height()); + Print("MT5Frontend: DXContextGetColors()"); +#endif + DXContextGetColors(context, image); + ProcessDrawText(); +#ifdef __debug__ + Print("DXContextGetColors: LastError: ", GetLastError()); +#endif + ResourceCreate(resname, image, Width(), Height(), 0, 0, Width(), COLOR_FORMAT_ARGB_NORMALIZE); +#ifdef __debug__ + Print("ResourceCreate: LastError: ", GetLastError()); +#endif + ChartRedraw(); + Sleep(1); + } + + /** + * Returns canvas' width. + */ + virtual int Width() { return (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); } + + /** + * Returns canvas' height. + */ + virtual int Height() { return (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); } + + /** + * Draws text directly into the pixel buffer. Should be executed after all 3d drawing. + */ + virtual void DrawTextNow(int _x, int _y, string _text, unsigned int _color = 0xFFFFFFFF, unsigned int _align = 0) { + TextSetFont("Arial", -80, FW_EXTRABOLD, 0); +#ifdef __debug__ + Print("TextSetFont: LastError = ", GetLastError()); +#endif + + TextOut(_text, _x, _y, _align, image, Width(), Height(), _color, COLOR_FORMAT_ARGB_NORMALIZE); +#ifdef __debug__ + Print("TextOut: LastError = ", GetLastError()); +#endif + } +}; diff --git a/3D/IndexBuffer.h b/3D/IndexBuffer.h new file mode 100644 index 000000000..fdd83d91e --- /dev/null +++ b/3D/IndexBuffer.h @@ -0,0 +1,63 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics index buffer. + */ + +#include "../Refs.mqh" + +class Device; + +/** + * Vertices' index buffer. + */ +class IndexBuffer : public Dynamic { + WeakRef device; + + public: + /** + * Constructor. + */ + IndexBuffer(Device* _device) { device = _device; } + + /** + * Returns base graphics device. + */ + Device* GetDevice() { return device.Ptr(); } + + /** + * Creates index buffer. + */ + virtual bool Create(void*& _data[]) = NULL; + + /** + * Fills index buffer with indices. + */ + virtual void Fill(unsigned int& _indices[]) = NULL; + + /** + * Activates index buffer for rendering. + */ + virtual void Select() = NULL; +}; diff --git a/3D/Interface.h b/3D/Interface.h new file mode 100644 index 000000000..87015f3a4 --- /dev/null +++ b/3D/Interface.h @@ -0,0 +1,131 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Chart events. + */ + +#include "../Util.h" + +enum ENUM_INTERFACE_EVENT { + INTERFACE_EVENT_NONE, + INTERFACE_EVENT_MOUSE_MOVE, + INTERFACE_EVENT_MOUSE_DOWN, + INTERFACE_EVENT_MOUSE_UP +}; + +struct InterfaceEvent { + ENUM_INTERFACE_EVENT type; + struct EventMouse { + int x; + int y; + datetime dt; + }; + + union EventData { + EventMouse mouse; + } data; +}; + +#ifdef __MQL5__ +/** + * "OnChart" event handler function (MQL5 only). + * + * Invoked when the ChartEvent event occurs. + */ +void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { + datetime _dt; + double _mp; + int _window = 0; + InterfaceEvent _event; + + if (id == CHART_EVENT_MOUSE_MOVE) { + Interface::mouse_pos_x = (int)lparam; + Interface::mouse_pos_y = (int)dparam; + ChartXYToTimePrice(0, Interface::mouse_pos_x, Interface::mouse_pos_y, _window, _dt, _mp); + _event.type = INTERFACE_EVENT_MOUSE_MOVE; + _event.data.mouse.x = Interface::mouse_pos_x; + _event.data.mouse.y = Interface::mouse_pos_y; + Interface::FireEvent(_event); + } +} +#endif + +typedef void (*InterfaceListener)(InterfaceEvent&, void*); + +class Interface { + public: + struct Installation { + InterfaceListener listener; + void* target; + }; + + static Installation installations[]; + + static bool mouse_was_down; + static int mouse_pos_x; + static int mouse_pos_y; + static bool initialized; + +#ifdef __MQL5__ + static void AddListener(InterfaceListener _listener, void* _target) { + if (!initialized) { + ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); + ChartRedraw(); + initialized = true; + } + + for (int i = 0; i < ArraySize(installations); ++i) { + if (installations[i].listener == _listener) { + // Listener already added. + return; + } + } + + Installation _installation; + _installation.listener = _listener; + _installation.target = _target; + + Util::ArrayPush(installations, _installation); + } + + static void FireEvent(InterfaceEvent& _event) { + for (int i = 0; i < ArraySize(installations); ++i) { + Installation _installation = installations[i]; + _installation.listener(_event, _installation.target); + } + } + + static int GetMouseX() { return mouse_pos_x; } + + static int GetMouseY() { return mouse_pos_y; } +#endif +}; + +#ifdef __MQL5__ +Interface::Installation Interface::installations[]; +bool Interface::mouse_was_down = false; +int Interface::mouse_pos_x = 0; +int Interface::mouse_pos_y = 0; +bool Interface::initialized = false; +#endif diff --git a/3D/Material.h b/3D/Material.h new file mode 100644 index 000000000..1cb05704b --- /dev/null +++ b/3D/Material.h @@ -0,0 +1,18 @@ +#include "Math.h" + +/** + * Generic vertex to be used by meshes. + */ +class Material { + public: + DXColor Color; + + Material(unsigned int _color = 0xFFFFFFFF) { Color = DXColor(_color); } + + Material(const Material& _r) { Color = _r.Color; } + + Material* SetColor(unsigned int _color) { + Color = DXColor(_color); + return &this; + } +}; diff --git a/3D/Math.h b/3D/Math.h new file mode 100644 index 000000000..e631a214a --- /dev/null +++ b/3D/Math.h @@ -0,0 +1,3225 @@ +//+------------------------------------------------------------------+ +//| DXMath.mqh | +//| Copyright 2019,MetaQuotes Software Corp. | +//| https://www.mql5.com | +//+------------------------------------------------------------------+ +#property copyright "Copyright 2019,MetaQuotes Software Corp." +#property link "https://www.mql5.com" +//+------------------------------------------------------------------+ +//| DirectX Math Routines | +//+------------------------------------------------------------------+ +//| Ported from C++ code of ReactOS, written by David Adam | +//| and Tony Wasserka | +//| | +//| https://doxygen.reactos.org/de/d57/ | +//| dll_2directx_2wine_2d3dx9__36_2math_8c_source.html | +//| | +//| Copyright (C) 2007 David Adam | +//| Copyright (C) 2007 Tony Wasserka | +//+------------------------------------------------------------------+ +#define DX_PI 3.1415926535897932384626f +#define DX_PI_DIV2 1.5707963267948966192313f +#define DX_PI_DIV3 1.0471975511965977461542f +#define DX_PI_DIV4 0.7853981633974483096156f +#define DX_PI_DIV6 0.5235987755982988730771f +#define DX_PI_MUL2 6.2831853071795864769253f +#define DXSH_MINORDER 2 +#define DXSH_MAXORDER 6 +//+------------------------------------------------------------------+ +//| Preliminary declarations | +//+------------------------------------------------------------------+ +//+------------------------------------------------------------------+ +//| DXColor | +//+------------------------------------------------------------------+ +struct DXColor; +struct DXPlane; +struct DXVector2; +struct DXVector3; +struct DXVector4; +struct DXMatrix; +struct DXQuaternion; +struct DViewport; +//+------------------------------------------------------------------+ +//| DXColor | +//+------------------------------------------------------------------+ +struct DXColor { + float r; + float g; + float b; + float a; + //--- constructors + DXColor(void) { + r = 0.0; + g = 0.0; + b = 0.0; + a = 1.0; + } + DXColor(float red, float green, float blue, float alpha) { + r = red; + g = green; + b = blue; + a = alpha; + } + DXColor(const DXVector4 &v) { + r = v.x; + g = v.y; + b = v.z; + a = v.w; + } + DXColor(const DXVector3 &v) { + r = v.x; + g = v.y; + b = v.z; + a = 1.0; + } + DXColor(unsigned int _color) { + a = 1.0f / 255.0f * ((_color & 0xFF000000) >> 24); + r = 1.0f / 255.0f * ((_color & 0x00FF0000) >> 16); + g = 1.0f / 255.0f * ((_color & 0x0000FF00) >> 8); + b = 1.0f / 255.0f * ((_color & 0x000000FF) >> 0); + } +}; +//+------------------------------------------------------------------+ +//| DXPlane | +//+------------------------------------------------------------------+ +struct DXPlane { + float a; + float b; + float c; + float d; +}; +//+------------------------------------------------------------------+ +//| DXVector2 | +//+------------------------------------------------------------------+ +struct DXVector2 { + float x; + float y; + //--- constructors + DXVector2(void) { + x = 0.0; + y = 0.0; + } + DXVector2(float v) { + x = v; + y = v; + } + DXVector2(float vx, float vy) { + x = vx; + y = vy; + } + DXVector2(const DXVector3 &v) { + x = v.x; + y = v.y; + } + DXVector2(const DXVector4 &v) { + x = v.x; + y = v.y; + } +}; +//+------------------------------------------------------------------+ +//| DXVector3 | +//+------------------------------------------------------------------+ +struct DXVector3 { + float x; + float y; + float z; + //--- constructors + DXVector3(void) { + x = 0.0; + y = 0.0; + z = 0.0; + } + DXVector3(float v) { + x = v; + y = v; + z = v; + } + DXVector3(float vx, float vy, float vz) { + x = vx; + y = vy; + z = vz; + } + DXVector3(const DXVector2 &v) { + x = v.x; + y = v.y; + z = 0.0; + } + DXVector3(const DXVector4 &v) { + x = v.x; + y = v.y; + z = v.z; + } +}; +//+------------------------------------------------------------------+ +//| DXVector4 | +//+------------------------------------------------------------------+ +struct DXVector4 { + float x; + float y; + float z; + float w; + //--- constructors + DXVector4(void) { + x = 0.0; + y = 0.0; + z = 0.0; + w = 1.0; + } + DXVector4(float v) { + x = v; + y = v; + z = v; + w = v; + } + DXVector4(float vx, float vy, float vz, float vw) { + x = vx; + y = vy; + z = vz; + w = vw; + } + DXVector4(const DXVector2 &v) { + x = v.x; + y = v.y; + z = 0.0; + w = 1.0; + } + DXVector4(const DXVector3 &v) { + x = v.x; + y = v.y; + z = v.z; + w = 1.0; + } +}; +//+------------------------------------------------------------------+ +//| DXMatrix | +//+------------------------------------------------------------------+ +struct DXMatrix { + float m[4][4]; +}; +//+------------------------------------------------------------------+ +//| DXQuaternion | +//+------------------------------------------------------------------+ +struct DXQuaternion { + float x; + float y; + float z; + float w; +}; +//+------------------------------------------------------------------+ +//| DViewport | +//+------------------------------------------------------------------+ +struct DViewport { + ulong x; + ulong y; + ulong width; + ulong height; + float minz; + float maxz; +}; + +/* +//--- DXColor functions +void DXColorAdd(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +void DXColorAdjustContrast(DXColor &pout,const DXColor &pc,float s); +void DXColorAdjustSaturation(DXColor &pout,const DXColor &pc,float s); +void DXColorLerp(DXColor &pout,const DXColor &pc1,const DXColor &pc2,float s); +void DXColorModulate(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +void DXColorNegative(DXColor &pout,const DXColor &pc); +void DXColorScale(DXColor &pout,const DXColor &pc,float s); +void DXColorSubtract(DXColor &pout,const DXColor &pc1,const DXColor &pc2); +float DXFresnelTerm(float costheta,float refractionindex); + +//--- DXVector2 functions +void DXVec2Add(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2BaryCentric(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 &pv3,float f,float g); +void DXVec2CatmullRom(DXVector2 &pout,const DXVector2 &pv0,const DXVector2 &pv1,const DXVector2 &pv2,const DXVector2 +&pv3,float s); float DXVec2CCW(const DXVector2 &pv1,const DXVector2 &pv2); float DXVec2Dot(const DXVector2 &pv1,const +DXVector2 &pv2); void DXVec2Hermite(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pt1,const DXVector2 +&pv2,const DXVector2 &pt2,float s); float DXVec2Length(const DXVector2 &v); float DXVec2LengthSq(const DXVector2 &v); +void DXVec2Lerp(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2,float s); +void DXVec2Maximize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Minimize(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Normalize(DXVector2 &pout,const DXVector2 &pv); +void DXVec2Scale(DXVector2 &pout,const DXVector2 &pv,float s); +void DXVec2Subtract(DXVector2 &pout,const DXVector2 &pv1,const DXVector2 &pv2); +void DXVec2Transform(DXVector4 &pout,const DXVector2 &pv,const DXMatrix &pm); +void DXVec2TransformCoord(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm); +void DXVec2TransformNormal(DXVector2 &pout,const DXVector2 &pv,const DXMatrix &pm); + +//--- DXVector3 functions +void DXVec3Add(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); +void DXVec3BaryCentric(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3,float f,float g); +void DXVec3CatmullRom(DXVector3 &pout,const DXVector3 &pv0,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 +&pv3,float s); void DXVec3Cross(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); float DXVec3Dot(const +DXVector3 &pv1,const DXVector3 &pv2); void DXVec3Hermite(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 +&pt1,const DXVector3 &pv2,const DXVector3 &pt2,float s); float DXVec3Length(const DXVector3 &pv); float +DXVec3LengthSq(const DXVector3 &pv); void DXVec3Lerp(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2,float +s); void DXVec3Maximize(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); void DXVec3Minimize(DXVector3 +&pout,const DXVector3 &pv1,const DXVector3 &pv2); void DXVec3Normalize(DXVector3 &pout,const DXVector3 &pv); void +DXVec3Project(DXVector3 &pout,const DXVector3 &pv,const DViewport &pviewport,const DXMatrix &pprojection,const DXMatrix +&pview,const DXMatrix &pworld); void DXVec3Scale(DXVector3 &pout,const DXVector3 &pv,float s); void +DXVec3Subtract(DXVector3 &pout,const DXVector3 &pv1,const DXVector3 &pv2); void DXVec3Transform(DXVector4 &pout,const +DXVector3 &pv,const DXMatrix &pm); void DXVec3TransformCoord(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm); +void DXVec3TransformNormal(DXVector3 &pout,const DXVector3 &pv,const DXMatrix &pm); +void DXVec3Unproject(DXVector3 &out,const DXVector3 &v,const DViewport &viewport,const DXMatrix &projection,const +DXMatrix &view,const DXMatrix &world); + +//--- DXVector4 vector functions +void DXVec4Add(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); +void DXVec4BaryCentric(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3,float f,float g); +void DXVec4CatmullRom(DXVector4 &pout,const DXVector4 &pv0,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 +&pv3,float s); void DXVec4Cross(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,const DXVector4 &pv3); float +DXVec4Dot(const DXVector4 &pv1,const DXVector4 &pv2); void DXVec4Hermite(DXVector4 &pout,const DXVector4 &pv1,const +DXVector4 &pt1,const DXVector4 &pv2,const DXVector4 &pt2,float s); float DXVec4Length(const DXVector4 &pv); float +DXVec4LengthSq(const DXVector4 &pv); void DXVec4Lerp(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2,float +s); void DXVec4Maximize(DXVector4 &pout,const DXVector4 &pv1,const DXVector4 &pv2); void DXVec4Minimize(DXVector4 +&pout,const DXVector4 &pv1,const DXVector4 &pv2); void DXVec4Normalize(DXVector4 &pout,const DXVector4 &pv); void +DXVec4Scale(DXVector4 &pout,const DXVector4 &pv,float s); void DXVec4Subtract(DXVector4 &pout,const DXVector4 +&pv1,const DXVector4 &pv2); void DXVec4Transform(DXVector4 &pout,const DXVector4 &pv,const DXMatrix &pm); + +//---DXQuaternion functions +void DXQuaternionBaryCentric(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,float f,float g); +void DXQuaternionConjugate(DXQuaternion &pout,const DXQuaternion &pq); +float DXQuaternionDot(DXQuaternion &a,DXQuaternion &b); +void DXQuaternionExp(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionIdentity(DXQuaternion &out); +bool DXQuaternionIsIdentity(DXQuaternion &pq); +float DXQuaternionLength(const DXQuaternion &pq); +float DXQuaternionLengthSq(const DXQuaternion &pq); +void DXQuaternionInverse(DXQuaternion &pout,const DXQuaternion &pq); +void DXQuaternionLn(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionMultiply(DXQuaternion &pout,const DXQuaternion &pq1,const DXQuaternion &pq2); +void DXQuaternionNormalize(DXQuaternion &out,const DXQuaternion &q); +void DXQuaternionRotationAxis(DXQuaternion &out,const DXVector3 &v,float angle); +void DXQuaternionRotationMatrix(DXQuaternion &out,const DXMatrix &m); +void DXQuaternionRotationYawPitchRoll(DXQuaternion &out,float yaw,float pitch,float roll); +void DXQuaternionSlerp(DXQuaternion &out,DXQuaternion &q1,DXQuaternion &q2,float t); +void DXQuaternionSquad(DXQuaternion &pout,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3,DXQuaternion &pq4,float +t); void DXQuaternionSquadSetup(DXQuaternion &paout,DXQuaternion &pbout,DXQuaternion &pcout,DXQuaternion +&pq0,DXQuaternion &pq1,DXQuaternion &pq2,DXQuaternion &pq3); void DXQuaternionToAxisAngle(const DXQuaternion +&pq,DXVector3 &paxis,float &pangle); DXQuaternion add_diff(const DXQuaternion &q1,const DXQuaternion &q2,const float +add); + +//--- DXMatrix functions +void DXMatrixIdentity(DXMatrix &out); +bool DXMatrixIsIdentity(DXMatrix &pm); +void DXMatrixAffineTransformation(DXMatrix &out,float scaling,const DXVector3 &rotationcenter,const DXQuaternion +&rotation,const DXVector3 &translation); void DXMatrixAffineTransformation2D(DXMatrix &out,float scaling,const +DXVector2 &rotationcenter,float rotation,const DXVector2 &translation); int DXMatrixDecompose(DXVector3 +&poutscale,DXQuaternion &poutrotation,DXVector3 &pouttranslation,const DXMatrix &pm); float DXMatrixDeterminant(const +DXMatrix &pm); void DXMatrixInverse(DXMatrix &pout,float &pdeterminant,const DXMatrix &pm); void +DXMatrixLookAtLH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up); void +DXMatrixLookAtRH(DXMatrix &out,const DXVector3 &eye,const DXVector3 &at,const DXVector3 &up); void +DXMatrixMultiply(DXMatrix &pout,const DXMatrix &pm1,const DXMatrix &pm2); void DXMatrixMultiplyTranspose(DXMatrix +&pout,const DXMatrix &pm1,const DXMatrix &pm2); void DXMatrixOrthoLH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixOrthoOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixOrthoOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixOrthoRH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixPerspectiveFovLH(DXMatrix &pout,float fovy,float aspect,float zn,float zf); +void DXMatrixPerspectiveFovRH(DXMatrix &pout,float fovy,float aspect,float zn,float zf); +void DXMatrixPerspectiveLH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixPerspectiveOffCenterLH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixPerspectiveOffCenterRH(DXMatrix &pout,float l,float r,float b,float t,float zn,float zf); +void DXMatrixPerspectiveRH(DXMatrix &pout,float w,float h,float zn,float zf); +void DXMatrixReflect(DXMatrix &pout,const DXPlane &pplane); +void DXMatrixRotationAxis(DXMatrix &out,const DXVector3 &v,float angle); +void DXMatrixRotationQuaternion(DXMatrix &pout,const DXQuaternion &pq); +void DXMatrixRotationX(DXMatrix &pout,float angle); +void DXMatrixRotationY(DXMatrix &pout,float angle); +void DXMatrixRotationYawPitchRoll(DXMatrix &out,float yaw,float pitch,float roll); +void DXMatrixRotationZ(DXMatrix &pout,float angle); +void DXMatrixScaling(DXMatrix &pout,float sx,float sy,float sz); +void DXMatrixShadow(DXMatrix &pout,const DXVector4 &plight,const DXPlane &pplane); +void DXMatrixTransformation(DXMatrix &pout,const DXVector3 &pscalingcenter,const DXQuaternion &pscalingrotation,const +DXVector3 &pscaling,const DXVector3 &protationcenter,const DXQuaternion &protation,const DXVector3 &ptranslation); void +DXMatrixTransformation2D(DXMatrix &pout,const DXVector2 &pscalingcenter,float scalingrotation,const DXVector2 +&pscaling,const DXVector2 &protationcenter,float rotation,const DXVector2 &ptranslation); void +DXMatrixTranslation(DXMatrix &pout,float x,float y,float z); void DXMatrixTranspose(DXMatrix &pout,const DXMatrix &pm); + +//--- DXPlane functions | +float DXPlaneDot(const DXPlane &p1,const DXVector4 &p2); +float DXPlaneDotCoord(const DXPlane &pp,const DXVector4 &pv); +float DXPlaneDotNormal(const DXPlane &pp,const DXVector4 &pv); +void DXPlaneFromPointNormal(DXPlane &pout,const DXVector3 &pvpoint,const DXVector3 &pvnormal); +void DXPlaneFromPoints(DXPlane &pout,const DXVector3 &pv1,const DXVector3 &pv2,const DXVector3 &pv3); +void DXPlaneIntersectLine(DXVector3 &pout,const DXPlane &pp,const DXVector3 &pv1,const DXVector3 &pv2); +void DXPlaneNormalize(DXPlane &out,const DXPlane &p); +void DXPlaneScale(DXPlane &pout,const DXPlane &p,float s); +void DXPlaneTransform(DXPlane &pout,const DXPlane &pplane,const DXMatrix &pm); + +//---- spherical harmonic functions +void DXSHAdd(float &out[],int order,const float &a[],const float &b[]); +float DXSHDot(int order,const float &a[],const float &b[]); +int DXSHEvalConeLight(int order,const DXVector3 &dir,float radius,float Rintensity,float Gintensity,float +Bintensity,float &rout[],float &gout[],float &bout[]); void DXSHEvalDirection(float &out[],int order,const DXVector3 +&dir); int DXSHEvalDirectionalLight(int order,const DXVector3 &dir,float Rintensity,float Gintensity,float +Bintensity,float &rout[],float &gout[],float &bout[]); int DXSHEvalHemisphereLight(int order,const DXVector3 +&dir,DXColor &top,DXColor &bottom,float &rout[],float &gout[],float &bout[]); int DXSHEvalSphericalLight(int +order,const DXVector3 &dir,float radius,float Rintensity,float Gintensity,float Bintensity,float &rout[],float +&gout[],float &bout[]); void DXSHMultiply2(float &out[],const float &a[],const float &b[]); void DXSHMultiply3(float +&out[],const float &a[],const float &b[]); void DXSHMultiply4(float &out[],const float &a[],const float &b[]); void +DXSHRotate(float &out[],int order,const DXMatrix &_matrix,const float &in[]); void DXSHRotateZ(float &out[],int +order,float angle,const float &in[]); void DXSHScale(float &out[],int order,const float &a[],const float scale); + +//--- scalar functions +float DXScalarLerp(const float val1,const float val2,float s) +float DXScalarBiasScale(const float val,const float bias,const float scale) +*/ + +//+------------------------------------------------------------------+ +//| Adds two color values together to create a new color value. | +//+------------------------------------------------------------------+ +void DXColorAdd(DXColor &pout, const DXColor &pc1, const DXColor &pc2) { + pout.r = pc1.r + pc2.r; + pout.g = pc1.g + pc2.g; + pout.b = pc1.b + pc2.b; + pout.a = pc1.a + pc2.a; +} +//+------------------------------------------------------------------+ +//| Adjusts the contrast value of a color. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, | +//| to the output alpha channel. | +//| | +//| This function interpolates the red,green,and blue color | +//| components of a DXColor structure between fifty percent gray | +//| and a specified contrast value,as shown in the following example.| +//| | +//| pout.r = 0.5f + s*(pc.r - 0.5f); | +//| | +//| If s is greater than 0 and less than 1,the contrast is decreased.| +//| If s is greater than 1, the contrast is increased. | +//+------------------------------------------------------------------+ +void DXColorAdjustContrast(DXColor &pout, const DXColor &pc, float s) { + pout.r = 0.5f + s * (pc.r - 0.5f); + pout.g = 0.5f + s * (pc.g - 0.5f); + pout.b = 0.5f + s * (pc.b - 0.5f); + pout.a = pc.a; +} +//+------------------------------------------------------------------+ +//| Adjusts the saturation value of a color. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, | +//| to the output alpha channel. | +//| | +//| This function interpolates the red, green, and blue color | +//| components of a DXColor structure between an unsaturated color | +//| and a color, as shown in the following example. | +//| | +//| Approximate values for each component's contribution to | +//| luminance. Based upon the NTSC standard described in | +//| ITU-R Recommendation BT.709. | +//| float grey = pc.r*0.2125f + pc.g*0.7154f + pc.b*0.0721f; | +//| | +//| pout.r = grey + s*(pc.r - grey); | +//| If s is greater than 0 and less than 1, the saturation is | +//| decreased. If s is greater than 1, the saturation is increased. | +//| | +//| The grayscale color is computed as: | +//| r = g = b = 0.2125*r + 0.7154*g + 0.0721*b | +//+------------------------------------------------------------------+ +void DXColorAdjustSaturation(DXColor &pout, const DXColor &pc, float s) { + float grey = pc.r * 0.2125f + pc.g * 0.7154f + pc.b * 0.0721f; + pout.r = grey + s * (pc.r - grey); + pout.g = grey + s * (pc.g - grey); + pout.b = grey + s * (pc.b - grey); + pout.a = pc.a; +} +//+------------------------------------------------------------------+ +//| Uses linear interpolation to create a color value. | +//+------------------------------------------------------------------+ +//| This function interpolates the red, green, blue, and alpha | +//| components of a DXColor structure between two colors, as shown | +//| in the following example. | +//| | +//| pout.r = pC1.r + s * (pC2.r - pC1.r); | +//| | +//| If you are linearly interpolating between the colors A and B, | +//| and s is 0, the resulting color is A. | +//| If s is 1, the resulting color is color B. | +//+------------------------------------------------------------------+ +void DXColorLerp(DXColor &pout, const DXColor &pc1, const DXColor &pc2, float s) { + pout.r = (1 - s) * pc1.r + s * pc2.r; + pout.g = (1 - s) * pc1.g + s * pc2.g; + pout.b = (1 - s) * pc1.b + s * pc2.b; + pout.a = (1 - s) * pc1.a + s * pc2.a; +} +//+------------------------------------------------------------------+ +//| Blends two colors. | +//+------------------------------------------------------------------+ +//| This function blends together two colors by multiplying matching | +//| color components, as shown in the following example. | +//| pout.r = pC1.r * pC2.r; | +//+------------------------------------------------------------------+ +void DXColorModulate(DXColor &pout, const DXColor &pc1, const DXColor &pc2) { + pout.r = pc1.r * pc2.r; + pout.g = pc1.g * pc2.g; + pout.b = pc1.b * pc2.b; + pout.a = pc1.a * pc2.a; +} +//+------------------------------------------------------------------+ +//| Creates the negative color value of a color value. | +//+------------------------------------------------------------------+ +//| The input alpha channel is copied, unmodified, to the output | +//| alpha channel. | +//| This function returns the negative color value by subtracting 1.0| +//| from the color components of the DXColor structure, | +//| as shown in the following example. | +//| pout.r = 1.0f - pc.r; | +//+------------------------------------------------------------------+ +void DXColorNegative(DXColor &pout, const DXColor &pc) { + pout.r = 1.0f - pc.r; + pout.g = 1.0f - pc.g; + pout.b = 1.0f - pc.b; + pout.a = pc.a; +} +//+------------------------------------------------------------------+ +//| Scales a color value. | +//+------------------------------------------------------------------+ +//| This function computes the scaled color value by multiplying | +//| the color components of the DXColor structure by the specified | +//| scale factor, as shown in the following example. | +//| pOut.r = pC.r*s; | +//+------------------------------------------------------------------+ +void DXColorScale(DXColor &pout, const DXColor &pc, float s) { + pout.r = s * pc.r; + pout.g = s * pc.g; + pout.b = s * pc.b; + pout.a = s * pc.a; +} +//+------------------------------------------------------------------+ +//| Subtracts two color values to create a new color value. | +//+------------------------------------------------------------------+ +void DXColorSubtract(DXColor &pout, const DXColor &pc1, const DXColor &pc2) { + pout.r = pc1.r - pc2.r; + pout.g = pc1.g - pc2.g; + pout.b = pc1.b - pc2.b; + pout.a = pc1.a - pc2.a; +} +//+------------------------------------------------------------------+ +//| Calculate the Fresnel term. | +//+------------------------------------------------------------------+ +//| To find the Fresnel term (F): | +//| If A is angle of incidence and B is the angle of refraction, | +//| then | +//| F = 0.5*[tan2(A - B)/tan2(A + B) + sin2(A - B)/sin2(A + B)] | +//| = 0.5*sin2(A - B)/sin2(A + B)*[cos2(A + B)/cos2(A - B) + 1] | +//| | +//| Let r = sina(A)/sin(B) (the relative refractive index) | +//| Let c = cos(A) | +//| Let g = (r2 + c2 - 1)1/2 | +//| | +//| Then,expanding using the trig identities and simplifying, | +//| you get: | +//| F = 0.5*(g + c)2/(g - c)2*([c(g + c)-1]2/[c(g - c) + 1]2 + 1) | +//+------------------------------------------------------------------+ +float DXFresnelTerm(float costheta, float refractionindex) { + float g = (float)sqrt(refractionindex * refractionindex + costheta * costheta - 1.0f); + float a = g + costheta; + float d = g - costheta; + float result = (costheta * a - 1.0f) * (costheta * a - 1.0f) / ((costheta * d + 1.0f) * (costheta * d + 1.0f)) + 1.0f; + result *= 0.5f * d * d / (a * a); + //--- + return (result); +} +//+------------------------------------------------------------------+ +//| Adds two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Add(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2) { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; +} +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2BaryCentric(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2, const DXVector2 &pv3, float f, + float g) { + pout.x = (1.0f - f - g) * (pv1.x) + f * (pv2.x) + g * (pv3.x); + pout.y = (1.0f - f - g) * (pv1.y) + f * (pv2.y) + g * (pv3.y); +} +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2CatmullRom(DXVector2 &pout, const DXVector2 &pv0, const DXVector2 &pv1, const DXVector2 &pv2, + const DXVector2 &pv3, float s) { + pout.x = 0.5f * (2.0f * pv1.x + (pv2.x - pv0.x) * s + (2.0f * pv0.x - 5.0f * pv1.x + 4.0f * pv2.x - pv3.x) * s * s + + (pv3.x - 3.0f * pv2.x + 3.0f * pv1.x - pv0.x) * s * s * s); + pout.y = 0.5f * (2.0f * pv1.y + (pv2.y - pv0.y) * s + (2.0f * pv0.y - 5.0f * pv1.y + 4.0f * pv2.y - pv3.y) * s * s + + (pv3.y - 3.0f * pv2.y + 3.0f * pv1.y - pv0.y) * s * s * s); +} +//+------------------------------------------------------------------+ +//| Returns the z-component by taking the cross product | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +float DXVec2CCW(const DXVector2 &pv1, const DXVector2 &pv2) { return (pv1.x * pv2.y - pv1.y * pv2.x); } +//+------------------------------------------------------------------+ +//| Determines the dot product of two 2D vectors. | +//+------------------------------------------------------------------+ +float DXVec2Dot(const DXVector2 &pv1, const DXVector2 &pv2) { return (pv1.x * pv2.x + pv1.y * pv2.y); } +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Hermite(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pt1, const DXVector2 &pv2, + const DXVector2 &pt2, float s) { + //--- prepare coefficients + float h1 = 2.0f * s * s * s - 3.0f * s * s + 1.0f; + float h2 = s * s * s - 2.0f * s * s + s; + float h3 = -2.0f * s * s * s + 3.0f * s * s; + float h4 = s * s * s - s * s; + //--- calculate interpolated point + pout.x = h1 * pv1.x + h2 * pt1.x + h3 * pv2.x + h4 * pt2.x; + pout.y = h1 * pv1.y + h2 * pt1.y + h3 * pv2.y + h4 * pt2.y; +} +//+------------------------------------------------------------------+ +//| Returns the length of a 2D vector. | +//+------------------------------------------------------------------+ +float DXVec2Length(const DXVector2 &v) { return ((float)sqrt(v.x * v.x + v.y * v.y)); } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 2D vector. | +//+------------------------------------------------------------------+ +float DXVec2LengthSq(const DXVector2 &v) { return ((float)(v.x * v.x + v.y * v.y)); } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Lerp(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2, float s) { + pout.x = (1.0f - s) * pv1.x + s * pv2.x; + pout.y = (1.0f - s) * pv1.y + s * pv2.y; +} +//+------------------------------------------------------------------+ +//| Returns a 2D vector that is made up of the largest components | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Maximize(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2) { + pout.x = (float)fmax(pv1.x, pv2.x); + pout.y = (float)fmax(pv1.y, pv2.y); +} +//+------------------------------------------------------------------+ +//| Returns a 2D vector that is made up of the smallest components | +//| of two 2D vectors. | +//+------------------------------------------------------------------+ +void DXVec2Minimize(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2) { + pout.x = (float)fmin(pv1.x, pv2.x); + pout.y = (float)fmin(pv1.y, pv2.y); +} +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 2D vector. | +//+------------------------------------------------------------------+ +void DXVec2Normalize(DXVector2 &pout, const DXVector2 &pv) { + //--- calculate length + float norm = DXVec2Length(pv); + if (!norm) { + pout.x = 0.0f; + pout.y = 0.0f; + } else { + pout.x = pv.x / norm; + pout.y = pv.y / norm; + } +} +//+------------------------------------------------------------------+ +//| Scales a 2D vector. | +//+------------------------------------------------------------------+ +void DXVec2Scale(DXVector2 &pout, const DXVector2 &pv, float s) { + pout.x = s * pv.x; + pout.y = s * pv.y; +} +//+------------------------------------------------------------------+ +//| DXVec3Subtract | +//+------------------------------------------------------------------+ +void DXVec2Subtract(DXVector2 &pout, const DXVector2 &pv1, const DXVector2 &pv2) { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; +} +//+------------------------------------------------------------------+ +//| Transforms a 2D vector by a given _matrix. | +//| This function transforms the vector pv(x,y,0,1) by the _matrix pm.| +//+------------------------------------------------------------------+ +void DXVec2Transform(DXVector4 &pout, const DXVector2 &pv, const DXMatrix &pm) { + DXVector4 out; + out.x = pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[3][0]; + out.y = pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[3][1]; + out.z = pm.m[0][2] * pv.x + pm.m[1][2] * pv.y + pm.m[3][2]; + out.w = pm.m[0][3] * pv.x + pm.m[1][3] * pv.y + pm.m[3][3]; + pout = out; +} +//+------------------------------------------------------------------+ +//| Transforms a 2D vector by a given _matrix, | +//| projecting the result back into w = 1. | +//| This function transforms the vector pv(x,y,0,1) by the _matrix pm.| +//+------------------------------------------------------------------+ +void DXVec2TransformCoord(DXVector2 &pout, const DXVector2 &pv, const DXMatrix &pm) { + float norm = pm.m[0][3] * pv.x + pm.m[1][3] * pv.y + pm.m[3][3]; + if (norm) { + pout.x = (pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[3][0]) / norm; + pout.y = (pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[3][1]) / norm; + } else { + pout.x = 0.0f; + pout.y = 0.0f; + } +} +//+------------------------------------------------------------------+ +//| Transforms the 2D vector normal by the given _matrix. | +//+------------------------------------------------------------------+ +void DXVec2TransformNormal(DXVector2 &pout, const DXVector2 &pv, const DXMatrix &pm) { + pout.x = pm.m[0][0] * pv.x + pm.m[1][0] * pv.y; + pout.y = pm.m[0][1] * pv.x + pm.m[1][1] * pv.y; +} +//+------------------------------------------------------------------+ +//| Adds two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Add(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2) { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; + pout.z = pv1.z + pv2.z; +} +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3BaryCentric(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2, const DXVector3 &pv3, float f, + float g) { + pout.x = (1.0f - f - g) * pv1.x + f * pv2.x + g * pv3.x; + pout.y = (1.0f - f - g) * pv1.y + f * pv2.y + g * pv3.y; + pout.z = (1.0f - f - g) * pv1.z + f * pv2.z + g * pv3.z; +} +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3CatmullRom(DXVector3 &pout, const DXVector3 &pv0, const DXVector3 &pv1, const DXVector3 &pv2, + const DXVector3 &pv3, float s) { + pout.x = 0.5f * (2.0f * pv1.x + (pv2.x - pv0.x) * s + (2.0f * pv0.x - 5.0f * pv1.x + 4.0f * pv2.x - pv3.x) * s * s + + (pv3.x - 3.0f * pv2.x + 3.0f * pv1.x - pv0.x) * s * s * s); + pout.y = 0.5f * (2.0f * pv1.y + (pv2.y - pv0.y) * s + (2.0f * pv0.y - 5.0f * pv1.y + 4.0f * pv2.y - pv3.y) * s * s + + (pv3.y - 3.0f * pv2.y + 3.0f * pv1.y - pv0.y) * s * s * s); + pout.z = 0.5f * (2.0f * pv1.z + (pv2.z - pv0.z) * s + (2.0f * pv0.z - 5.0f * pv1.z + 4.0f * pv2.z - pv3.z) * s * s + + (pv3.z - 3.0f * pv2.z + 3.0f * pv1.z - pv0.z) * s * s * s); +} +//+------------------------------------------------------------------+ +//| Determines the cross-product of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Cross(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2) { + pout.x = pv1.y * pv2.z - pv1.z * pv2.y; + pout.y = pv1.z * pv2.x - pv1.x * pv2.z; + pout.z = pv1.x * pv2.y - pv1.y * pv2.x; +} +//+------------------------------------------------------------------+ +//| Determines the dot product of two 3D vectors. | +//+------------------------------------------------------------------+ +float DXVec3Dot(const DXVector3 &pv1, const DXVector3 &pv2) { return (pv1.x * pv2.x + pv1.y * pv2.y + pv1.z * pv2.z); } +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Hermite(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pt1, const DXVector3 &pv2, + const DXVector3 &pt2, float s) { + float h1 = 2.0f * s * s * s - 3.0f * s * s + 1.0f; + float h2 = s * s * s - 2.0f * s * s + s; + float h3 = -2.0f * s * s * s + 3.0f * s * s; + float h4 = s * s * s - s * s; + //--- calculate interpolated coordinates + pout.x = h1 * pv1.x + h2 * pt1.x + h3 * pv2.x + h4 * pt2.x; + pout.y = h1 * pv1.y + h2 * pt1.y + h3 * pv2.y + h4 * pt2.y; + pout.z = h1 * pv1.z + h2 * pt1.z + h3 * pv2.z + h4 * pt2.z; +} +//+------------------------------------------------------------------+ +//| Returns the length of a 3D vector. | +//+------------------------------------------------------------------+ +float DXVec3Length(const DXVector3 &pv) { return ((float)sqrt(pv.x * pv.x + pv.y * pv.y + pv.z * pv.z)); } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 3D vector. | +//+------------------------------------------------------------------+ +float DXVec3LengthSq(const DXVector3 &pv) { return ((float)(pv.x * pv.x + pv.y * pv.y + pv.z * pv.z)); } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Lerp(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2, float s) { + pout.x = (1.0f - s) * pv1.x + s * pv2.x; + pout.y = (1.0f - s) * pv1.y + s * pv2.y; + pout.z = (1.0f - s) * pv1.z + s * pv2.z; +} +//+------------------------------------------------------------------+ +//| Returns a 3D vector that is made up of the largest components | +//| of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Maximize(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2) { + pout.x = (float)fmax(pv1.x, pv2.x); + pout.y = (float)fmax(pv1.y, pv2.y); + pout.z = (float)fmax(pv1.z, pv2.z); +} +//+------------------------------------------------------------------+ +//| Returns a 3D vector that is made up of the smallest components | +//| of two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Minimize(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2) { + pout.x = (float)fmin(pv1.x, pv2.x); + pout.y = (float)fmin(pv1.y, pv2.y); + pout.z = (float)fmin(pv1.z, pv2.z); +} +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 3D vector. | +//+------------------------------------------------------------------+ +void DXVec3Normalize(DXVector3 &pout, const DXVector3 &pv) { + //--- calculate length + float norm = DXVec3Length(pv); + if (!norm) { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + } else { + pout.x = pv.x / norm; + pout.y = pv.y / norm; + pout.z = pv.z / norm; + } +} +//+------------------------------------------------------------------+ +//| Projects a 3D vector from object space into screen space. | +//+------------------------------------------------------------------+ +void DXVec3Project(DXVector3 &pout, const DXVector3 &pv, const DViewport &pviewport, const DXMatrix &pprojection, + const DXMatrix &pview, const DXMatrix &pworld) { + DXMatrix m; + DXMatrixIdentity(m); + //--- pworld + DXMatrixMultiply(m, m, pworld); + //--- pview + DXMatrixMultiply(m, m, pview); + //--- pprojection + DXMatrixMultiply(m, m, pprojection); + DXVec3TransformCoord(pout, pv, m); + //---pviewport + pout.x = pviewport.x + (1.0f + pout.x) * pviewport.width / 2.0f; + pout.y = pviewport.y + (1.0f - pout.y) * pviewport.height / 2.0f; + pout.z = pviewport.minz + pout.z * (pviewport.maxz - pviewport.minz); +} +//+------------------------------------------------------------------+ +//| Scales a 3D vector. | +//+------------------------------------------------------------------+ +void DXVec3Scale(DXVector3 &pout, const DXVector3 &pv, float s) { + pout.x = s * pv.x; + pout.y = s * pv.y; + pout.z = s * pv.z; +} +//+------------------------------------------------------------------+ +//| Subtracts two 3D vectors. | +//+------------------------------------------------------------------+ +void DXVec3Subtract(DXVector3 &pout, const DXVector3 &pv1, const DXVector3 &pv2) { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; + pout.z = pv1.z - pv2.z; +} +//+------------------------------------------------------------------+ +//| Transforms vector (x,y,z,1) by a given _matrix. | +//+------------------------------------------------------------------+ +void DXVec3Transform(DXVector4 &pout, const DXVector3 &pv, const DXMatrix &pm) { + DXVector4 out; + //--- + out.x = pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[2][0] * pv.z + pm.m[3][0]; + out.y = pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[2][1] * pv.z + pm.m[3][1]; + out.z = pm.m[0][2] * pv.x + pm.m[1][2] * pv.y + pm.m[2][2] * pv.z + pm.m[3][2]; + out.w = pm.m[0][3] * pv.x + pm.m[1][3] * pv.y + pm.m[2][3] * pv.z + pm.m[3][3]; + pout = out; +} +//+------------------------------------------------------------------+ +//| Transforms a 3D vector by a given _matrix, | +//| projecting the result back into w = 1. | +//+------------------------------------------------------------------+ +void DXVec3TransformCoord(DXVector3 &pout, const DXVector3 &pv, const DXMatrix &pm) { + float norm = pm.m[0][3] * pv.x + pm.m[1][3] * pv.y + pm.m[2][3] * pv.z + pm.m[3][3]; + //--- + if (norm) { + pout.x = (pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[2][0] * pv.z + pm.m[3][0]) / norm; + pout.y = (pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[2][1] * pv.z + pm.m[3][1]) / norm; + pout.z = (pm.m[0][2] * pv.x + pm.m[1][2] * pv.y + pm.m[2][2] * pv.z + pm.m[3][2]) / norm; + } else { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + } +} +//+------------------------------------------------------------------+ +//| Transforms the 3D vector normal by the given _matrix. | +//+------------------------------------------------------------------+ +void DXVec3TransformNormal(DXVector3 &pout, const DXVector3 &pv, const DXMatrix &pm) { + pout.x = pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[2][0] * pv.z; + pout.y = pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[2][1] * pv.z; + pout.z = pm.m[0][2] * pv.x + pm.m[1][2] * pv.y + pm.m[2][2] * pv.z; +} +//+------------------------------------------------------------------+ +//| Projects a vector from screen space into object space. | +//+------------------------------------------------------------------+ +void DXVec3Unproject(DXVector3 &out, const DXVector3 &v, const DViewport &viewport, const DXMatrix &projection, + const DXMatrix &view, const DXMatrix &world) { + DXMatrix m; + DXMatrixIdentity(m); + //--- world + DXMatrixMultiply(m, m, world); + //--- view + DXMatrixMultiply(m, m, view); + //--- projection + DXMatrixMultiply(m, m, projection); + //--- calculate inverse _matrix + float det = 0.0f; + DXMatrixInverse(m, det, m); + out = v; + //--- viewport + out.x = 2.0f * (out.x - viewport.x) / viewport.width - 1.0f; + out.y = 1.0f - 2.0f * (out.y - viewport.y) / viewport.height; + out.z = (out.z - viewport.minz) / (viewport.maxz - viewport.minz); + //--- + DXVec3TransformCoord(out, out, m); +} +//+------------------------------------------------------------------+ +//| Adds two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Add(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2) { + pout.x = pv1.x + pv2.x; + pout.y = pv1.y + pv2.y; + pout.z = pv1.z + pv2.z; + pout.w = pv1.w + pv2.w; +} +//+------------------------------------------------------------------+ +//| Returns a point in Barycentric coordinates, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4BaryCentric(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2, const DXVector4 &pv3, float f, + float g) { + pout.x = (1.0f - f - g) * (pv1.x) + f * (pv2.x) + g * (pv3.x); + pout.y = (1.0f - f - g) * (pv1.y) + f * (pv2.y) + g * (pv3.y); + pout.z = (1.0f - f - g) * (pv1.z) + f * (pv2.z) + g * (pv3.z); + pout.w = (1.0f - f - g) * (pv1.w) + f * (pv2.w) + g * (pv3.w); +} +//+------------------------------------------------------------------+ +//| Performs a Catmull-Rom interpolation, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4CatmullRom(DXVector4 &pout, const DXVector4 &pv0, const DXVector4 &pv1, const DXVector4 &pv2, + const DXVector4 &pv3, float s) { + pout.x = 0.5f * (2.0f * pv1.x + (pv2.x - pv0.x) * s + (2.0f * pv0.x - 5.0f * pv1.x + 4.0f * pv2.x - pv3.x) * s * s + + (pv3.x - 3.0f * pv2.x + 3.0f * pv1.x - pv0.x) * s * s * s); + pout.y = 0.5f * (2.0f * pv1.y + (pv2.y - pv0.y) * s + (2.0f * pv0.y - 5.0f * pv1.y + 4.0f * pv2.y - pv3.y) * s * s + + (pv3.y - 3.0f * pv2.y + 3.0f * pv1.y - pv0.y) * s * s * s); + pout.z = 0.5f * (2.0f * pv1.z + (pv2.z - pv0.z) * s + (2.0f * pv0.z - 5.0f * pv1.z + 4.0f * pv2.z - pv3.z) * s * s + + (pv3.z - 3.0f * pv2.z + 3.0f * pv1.z - pv0.z) * s * s * s); + pout.w = 0.5f * (2.0f * pv1.w + (pv2.w - pv0.w) * s + (2.0f * pv0.w - 5.0f * pv1.w + 4.0f * pv2.w - pv3.w) * s * s + + (pv3.w - 3.0f * pv2.w + 3.0f * pv1.w - pv0.w) * s * s * s); +} +//+------------------------------------------------------------------+ +//| Determines the cross-product in four dimensions. | +//+------------------------------------------------------------------+ +void DXVec4Cross(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2, const DXVector4 &pv3) { + DXVector4 out; + out.x = pv1.y * (pv2.z * pv3.w - pv3.z * pv2.w) - pv1.z * (pv2.y * pv3.w - pv3.y * pv2.w) + + pv1.w * (pv2.y * pv3.z - pv2.z * pv3.y); + out.y = -(pv1.x * (pv2.z * pv3.w - pv3.z * pv2.w) - pv1.z * (pv2.x * pv3.w - pv3.x * pv2.w) + + pv1.w * (pv2.x * pv3.z - pv3.x * pv2.z)); + out.z = pv1.x * (pv2.y * pv3.w - pv3.y * pv2.w) - pv1.y * (pv2.x * pv3.w - pv3.x * pv2.w) + + pv1.w * (pv2.x * pv3.y - pv3.x * pv2.y); + out.w = -(pv1.x * (pv2.y * pv3.z - pv3.y * pv2.z) - pv1.y * (pv2.x * pv3.z - pv3.x * pv2.z) + + pv1.z * (pv2.x * pv3.y - pv3.x * pv2.y)); + pout = out; +} +//+------------------------------------------------------------------+ +//| Determines the dot product of two 4D vectors. | +//+------------------------------------------------------------------+ +float DXVec4Dot(const DXVector4 &pv1, const DXVector4 &pv2) { + return (pv1.x * pv2.x + pv1.y * pv2.y + pv1.z * pv2.z + pv1.w * pv2.w); +} +//+------------------------------------------------------------------+ +//| Performs a Hermite spline interpolation, | +//| using the specified 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Hermite(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pt1, const DXVector4 &pv2, + const DXVector4 &pt2, float s) { + float h1 = 2.0f * s * s * s - 3.0f * s * s + 1.0f; + float h2 = s * s * s - 2.0f * s * s + s; + float h3 = -2.0f * s * s * s + 3.0f * s * s; + float h4 = s * s * s - s * s; + pout.x = h1 * pv1.x + h2 * pt1.x + h3 * pv2.x + h4 * pt2.x; + pout.y = h1 * pv1.y + h2 * pt1.y + h3 * pv2.y + h4 * pt2.y; + pout.z = h1 * pv1.z + h2 * pt1.z + h3 * pv2.z + h4 * pt2.z; + pout.w = h1 * pv1.w + h2 * pt1.w + h3 * pv2.w + h4 * pt2.w; +} +//+------------------------------------------------------------------+ +//| Returns the length of a 4D vector. | +//+------------------------------------------------------------------+ +float DXVec4Length(const DXVector4 &pv) { return ((float)sqrt(pv.x * pv.x + pv.y * pv.y + pv.z * pv.z + pv.w * pv.w)); } +//+------------------------------------------------------------------+ +//| Returns the square of the length of a 4D vector. | +//+------------------------------------------------------------------+ +float DXVec4LengthSq(const DXVector4 &pv) { return ((float)(pv.x * pv.x + pv.y * pv.y + pv.z * pv.z)); } +//+------------------------------------------------------------------+ +//| Performs a linear interpolation between two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Lerp(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2, float s) { + pout.x = (1.0f - s) * pv1.x + s * pv2.x; + pout.y = (1.0f - s) * pv1.y + s * pv2.y; + pout.z = (1.0f - s) * pv1.z + s * pv2.z; + pout.w = (1.0f - s) * pv1.w + s * pv2.w; +} +//+------------------------------------------------------------------+ +//| Returns a 4D vector that is made up of the largest components | +//| of two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Maximize(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2) { + pout.x = (float)fmax(pv1.x, pv2.x); + pout.y = (float)fmax(pv1.y, pv2.y); + pout.z = (float)fmax(pv1.z, pv2.z); + pout.w = (float)fmax(pv1.w, pv2.w); +} +//+------------------------------------------------------------------+ +//| Returns a 4D vector that is made up of the smallest components | +//| of two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Minimize(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2) { + pout.x = (float)fmin(pv1.x, pv2.x); + pout.y = (float)fmin(pv1.y, pv2.y); + pout.z = (float)fmin(pv1.z, pv2.z); + pout.w = (float)fmin(pv1.w, pv2.w); +} +//+------------------------------------------------------------------+ +//| Returns the normalized version of a 4D vector. | +//+------------------------------------------------------------------+ +void DXVec4Normalize(DXVector4 &pout, const DXVector4 &pv) { + //--- calculate length + float norm = DXVec4Length(pv); + if (!norm) { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + pout.w = 0.0f; + } else { + pout.x = pv.x / norm; + pout.y = pv.y / norm; + pout.z = pv.z / norm; + pout.w = pv.w / norm; + } +} +//+------------------------------------------------------------------+ +//| Scales a 4D vector. | +//+------------------------------------------------------------------+ +void DXVec4Scale(DXVector4 &pout, const DXVector4 &pv, float s) { + pout.x = s * pv.x; + pout.y = s * pv.y; + pout.z = s * pv.z; + pout.w = s * pv.w; +} +//+------------------------------------------------------------------+ +//| Subtracts two 4D vectors. | +//+------------------------------------------------------------------+ +void DXVec4Subtract(DXVector4 &pout, const DXVector4 &pv1, const DXVector4 &pv2) { + pout.x = pv1.x - pv2.x; + pout.y = pv1.y - pv2.y; + pout.z = pv1.z - pv2.z; + pout.w = pv1.w - pv2.w; +} +//+------------------------------------------------------------------+ +//| Transforms a 4D vector by a given _matrix. | +//+------------------------------------------------------------------+ +void DXVec4Transform(DXVector4 &pout, const DXVector4 &pv, const DXMatrix &pm) { + DXVector4 temp; + temp.x = pm.m[0][0] * pv.x + pm.m[1][0] * pv.y + pm.m[2][0] * pv.z + pm.m[3][0] * pv.w; + temp.y = pm.m[0][1] * pv.x + pm.m[1][1] * pv.y + pm.m[2][1] * pv.z + pm.m[3][1] * pv.w; + temp.z = pm.m[0][2] * pv.x + pm.m[1][2] * pv.y + pm.m[2][2] * pv.z + pm.m[3][2] * pv.w; + temp.w = pm.m[0][3] * pv.x + pm.m[1][3] * pv.y + pm.m[2][3] * pv.z + pm.m[3][3] * pv.w; + pout = temp; +} +//+------------------------------------------------------------------+ +//| Returns a quaternion in barycentric coordinates. | +//+------------------------------------------------------------------+ +void DXQuaternionBaryCentric(DXQuaternion &pout, DXQuaternion &pq1, DXQuaternion &pq2, DXQuaternion &pq3, float f, + float g) { + DXQuaternion temp1, temp2; + DXQuaternionSlerp(temp1, pq1, pq2, f + g); + DXQuaternionSlerp(temp2, pq1, pq3, f + g); + DXQuaternionSlerp(pout, temp1, temp2, g / (f + g)); +} +//+------------------------------------------------------------------+ +//| Returns the conjugate of a quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionConjugate(DXQuaternion &pout, const DXQuaternion &pq) { + pout.x = -pq.x; + pout.y = -pq.y; + pout.z = -pq.z; + pout.w = pq.w; +} +//+------------------------------------------------------------------+ +//| Returns the dot product of two quaternions. | +//+------------------------------------------------------------------+ +float DXQuaternionDot(DXQuaternion &a, DXQuaternion &b) { return (a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w); } +//+------------------------------------------------------------------+ +//| Calculates the exponential. | +//| This method converts a pure quaternion to a unit quaternion. | +//| DXQuaternionExp expects a pure quaternion, where w is ignored | +//| in the calculation (w == 0). | +//+------------------------------------------------------------------+ +void DXQuaternionExp(DXQuaternion &out, const DXQuaternion &q) { + float norm = (float)sqrt(q.x * q.x + q.y * q.y + q.z * q.z); + if (norm) { + out.x = (float)sin(norm) * q.x / norm; + out.y = (float)sin(norm) * q.y / norm; + out.z = (float)sin(norm) * q.z / norm; + out.w = (float)cos(norm); + } else { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 1.0f; + } +} +//+------------------------------------------------------------------+ +//| Returns the identity quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionIdentity(DXQuaternion &out) { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 1.0f; +} +//+------------------------------------------------------------------+ +//| Determines if a quaternion is an identity quaternion. | +//+------------------------------------------------------------------+ +bool DXQuaternionIsIdentity(DXQuaternion &pq) { + return ((pq.x == 0.0f) && (pq.y == 0.0f) && (pq.z == 0.0f) && (pq.w == 1.0f)); +} +//+------------------------------------------------------------------+ +//| Returns the length of a quaternion. | +//+------------------------------------------------------------------+ +float DXQuaternionLength(const DXQuaternion &pq) { + return ((float)sqrt(pq.x * pq.x + pq.y * pq.y + pq.z * pq.z + pq.w * pq.w)); +} +//+------------------------------------------------------------------+ +//| Returns the square of the length of a quaternion. | +//+------------------------------------------------------------------+ +float DXQuaternionLengthSq(const DXQuaternion &pq) { + return ((float)(pq.x * pq.x + pq.y * pq.y + pq.z * pq.z + pq.w * pq.w)); +} +//+------------------------------------------------------------------+ +//| Conjugates and renormalizes a quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionInverse(DXQuaternion &pout, const DXQuaternion &pq) { + float norm = DXQuaternionLengthSq(pq); + pout.x = -pq.x / norm; + pout.y = -pq.y / norm; + pout.z = -pq.z / norm; + pout.w = pq.w / norm; +} +//+------------------------------------------------------------------+ +//| Calculates the natural logarithm. | +//| The DXQuaternionLn function works only for unit quaternions. | +//+------------------------------------------------------------------+ +void DXQuaternionLn(DXQuaternion &out, const DXQuaternion &q) { + float t; + if ((q.w >= 1.0f) || (q.w == -1.0f)) + t = 1.0f; + else + t = (float)(acos(q.w) / sqrt(1.0f - q.w * q.w)); + out.x = t * q.x; + out.y = t * q.y; + out.z = t * q.z; + out.w = 0.0f; +} +//+------------------------------------------------------------------+ +//| Multiplies two quaternions. | +//+------------------------------------------------------------------+ +void DXQuaternionMultiply(DXQuaternion &pout, const DXQuaternion &pq1, const DXQuaternion &pq2) { + DXQuaternion out; + out.x = pq2.w * pq1.x + pq2.x * pq1.w + pq2.y * pq1.z - pq2.z * pq1.y; + out.y = pq2.w * pq1.y - pq2.x * pq1.z + pq2.y * pq1.w + pq2.z * pq1.x; + out.z = pq2.w * pq1.z + pq2.x * pq1.y - pq2.y * pq1.x + pq2.z * pq1.w; + out.w = pq2.w * pq1.w - pq2.x * pq1.x - pq2.y * pq1.y - pq2.z * pq1.z; + pout = out; +} +//+------------------------------------------------------------------+ +//| Computes a unit length quaternion. | +//+------------------------------------------------------------------+ +void DXQuaternionNormalize(DXQuaternion &out, const DXQuaternion &q) { + float norm = DXQuaternionLength(q); + if (!norm) { + out.x = 0.0f; + out.y = 0.0f; + out.z = 0.0f; + out.w = 0.0f; + } else { + out.x = q.x / norm; + out.y = q.y / norm; + out.z = q.z / norm; + out.w = q.w / norm; + } +} +//+------------------------------------------------------------------+ +//| Rotates a quaternion about an arbitrary axis. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationAxis(DXQuaternion &out, const DXVector3 &v, float angle) { + DXVector3 temp; + DXVec3Normalize(temp, v); + out.x = (float)sin(angle / 2.0f) * temp.x; + out.y = (float)sin(angle / 2.0f) * temp.y; + out.z = (float)sin(angle / 2.0f) * temp.z; + out.w = (float)cos(angle / 2.0f); +} +//+------------------------------------------------------------------+ +//| Builds a quaternion from a rotation _matrix. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationMatrix(DXQuaternion &out, const DXMatrix &m) { + float s; + float trace = m.m[0][0] + m.m[1][1] + m.m[2][2] + 1.0f; + if (trace > 1.0f) { + s = 2.0f * (float)sqrt(trace); + out.x = (m.m[1][2] - m.m[2][1]) / s; + out.y = (m.m[2][0] - m.m[0][2]) / s; + out.z = (m.m[0][1] - m.m[1][0]) / s; + out.w = 0.25f * s; + } else { + int maxi = 0; + for (int i = 1; i < 3; i++) { + if (m.m[i][i] > m.m[maxi][maxi]) maxi = i; + } + switch (maxi) { + case 0: + s = 2.0f * (float)sqrt(1.0f + m.m[0][0] - m.m[1][1] - m.m[2][2]); + out.x = 0.25f * s; + out.y = (m.m[0][1] + m.m[1][0]) / s; + out.z = (m.m[0][2] + m.m[2][0]) / s; + out.w = (m.m[1][2] - m.m[2][1]) / s; + break; + + case 1: + s = 2.0f * (float)sqrt(1.0f + m.m[1][1] - m.m[0][0] - m.m[2][2]); + out.x = (m.m[0][1] + m.m[1][0]) / s; + out.y = 0.25f * s; + out.z = (m.m[1][2] + m.m[2][1]) / s; + out.w = (m.m[2][0] - m.m[0][2]) / s; + break; + + case 2: + s = 2.0f * (float)sqrt(1.0f + m.m[2][2] - m.m[0][0] - m.m[1][1]); + out.x = (m.m[0][2] + m.m[2][0]) / s; + out.y = (m.m[1][2] + m.m[2][1]) / s; + out.z = 0.25f * s; + out.w = (m.m[0][1] - m.m[1][0]) / s; + break; + } + } +} +//+------------------------------------------------------------------+ +//| Builds a quaternion with the given yaw, pitch, and roll. | +//+------------------------------------------------------------------+ +void DXQuaternionRotationYawPitchRoll(DXQuaternion &out, float yaw, float pitch, float roll) { + float syaw = (float)sin(yaw / 2.0f); + float cyaw = (float)cos(yaw / 2.0f); + float spitch = (float)sin(pitch / 2.0f); + float cpitch = (float)cos(pitch / 2.0f); + float sroll = (float)sin(roll / 2.0f); + float croll = (float)cos(roll / 2.0f); + //--- + out.x = syaw * cpitch * sroll + cyaw * spitch * croll; + out.y = syaw * cpitch * croll - cyaw * spitch * sroll; + out.z = cyaw * cpitch * sroll - syaw * spitch * croll; + out.w = cyaw * cpitch * croll + syaw * spitch * sroll; +} +//+------------------------------------------------------------------+ +//| Interpolates between two quaternions, using spherical linear | +//| interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSlerp(DXQuaternion &out, DXQuaternion &q1, DXQuaternion &q2, float t) { + float temp = 1.0f - t; + float dot = DXQuaternionDot(q1, q2); + if (dot < 0.0f) { + t = -t; + dot = -dot; + } + if (1.0f - dot > 0.001f) { + float theta = (float)acos(dot); + temp = (float)sin(theta * temp) / (float)sin(theta); + t = (float)sin(theta * t) / (float)sin(theta); + } + out.x = temp * q1.x + t * q2.x; + out.y = temp * q1.y + t * q2.y; + out.z = temp * q1.z + t * q2.z; + out.w = temp * q1.w + t * q2.w; +} +//+------------------------------------------------------------------+ +//| Interpolates between quaternions, using spherical quadrangle | +//| interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSquad(DXQuaternion &pout, DXQuaternion &pq1, DXQuaternion &pq2, DXQuaternion &pq3, DXQuaternion &pq4, + float t) { + DXQuaternion temp1, temp2; + DXQuaternionSlerp(temp1, pq1, pq4, t); + DXQuaternionSlerp(temp2, pq2, pq3, t); + DXQuaternionSlerp(pout, temp1, temp2, 2.0f * t * (1.0f - t)); +} +//+------------------------------------------------------------------+ +//| add_diff | +//+------------------------------------------------------------------+ +DXQuaternion add_diff(const DXQuaternion &q1, const DXQuaternion &q2, const float add) { + DXQuaternion temp; + temp.x = q1.x + add * q2.x; + temp.y = q1.y + add * q2.y; + temp.z = q1.z + add * q2.z; + temp.w = q1.w + add * q2.w; + //--- + return (temp); +} +//+------------------------------------------------------------------+ +//| Sets up control points for spherical quadrangle interpolation. | +//+------------------------------------------------------------------+ +void DXQuaternionSquadSetup(DXQuaternion &paout, DXQuaternion &pbout, DXQuaternion &pcout, DXQuaternion &pq0, + DXQuaternion &pq1, DXQuaternion &pq2, DXQuaternion &pq3) { + DXQuaternion q, temp1, temp2, temp3, zero; + DXQuaternion aout, cout; + zero.x = 0.0f; + zero.y = 0.0f; + zero.z = 0.0f; + zero.w = 0.0f; + //--- + if (DXQuaternionDot(pq0, pq1) < 0.0f) + temp2 = add_diff(zero, pq0, -1.0f); + else + temp2 = pq0; + //--- + if (DXQuaternionDot(pq1, pq2) < 0.0f) + cout = add_diff(zero, pq2, -1.0f); + else + cout = pq2; + //--- + if (DXQuaternionDot(cout, pq3) < 0.0f) + temp3 = add_diff(zero, pq3, -1.0f); + else + temp3 = pq3; + //--- + DXQuaternionInverse(temp1, pq1); + DXQuaternionMultiply(temp2, temp1, temp2); + DXQuaternionLn(temp2, temp2); + DXQuaternionMultiply(q, temp1, cout); + DXQuaternionLn(q, q); + temp1 = add_diff(temp2, q, 1.0f); + temp1.x *= -0.25f; + temp1.y *= -0.25f; + temp1.z *= -0.25f; + temp1.w *= -0.25f; + DXQuaternionExp(temp1, temp1); + DXQuaternionMultiply(aout, pq1, temp1); + //--- + DXQuaternionInverse(temp1, cout); + DXQuaternionMultiply(temp2, temp1, pq1); + DXQuaternionLn(temp2, temp2); + DXQuaternionMultiply(q, temp1, temp3); + DXQuaternionLn(q, q); + temp1 = add_diff(temp2, q, 1.0f); + temp1.x *= -0.25f; + temp1.y *= -0.25f; + temp1.z *= -0.25f; + temp1.w *= -0.25f; + DXQuaternionExp(temp1, temp1); + DXQuaternionMultiply(pbout, cout, temp1); + paout = aout; + pcout = cout; +} +//+------------------------------------------------------------------+ +//| Computes a quaternion's axis and angle of rotation. | +//+------------------------------------------------------------------+ +void DXQuaternionToAxisAngle(const DXQuaternion &pq, DXVector3 &paxis, float &pangle) { + //--- paxis + paxis.x = pq.x; + paxis.y = pq.y; + paxis.z = pq.z; + //--- pangle + pangle = 2.0f * (float)acos(pq.w); +} +//+------------------------------------------------------------------+ +//| DXMatrixIdentity creates an identity _matrix | +//+------------------------------------------------------------------+ +void DXMatrixIdentity(DXMatrix &out) { + for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) { + if (i == j) + out.m[j, i] = 1.0f; + else + out.m[j, i] = 0.0f; + } +} +//+------------------------------------------------------------------+ +//| Determines if a _matrix is an identity _matrix. | +//+------------------------------------------------------------------+ +bool DXMatrixIsIdentity(DXMatrix &pm) { + for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) { + if (i == j) { + if (fabs(pm.m[j, i] - 1.0f) > 1e-6f) return (false); + } else if (fabs(pm.m[j, i]) > 1e-6f) + return (false); + } + //--- + return (true); +} +//+------------------------------------------------------------------+ +//| Builds a 3D affine transformation _matrix. | +//+------------------------------------------------------------------+ +//| This function calculates the affine transformation _matrix | +//| with the following formula, with _matrix concatenation | +//| evaluated in left-to-right order: | +//| Mout = Ms * (Mrc)-1 * Mr * Mrc * Mt | +//| where: | +//| Mout = output _matrix (pOut) | +//| Ms = scaling _matrix (Scaling) | +//| Mrc = center of rotation _matrix (pRotationCenter) | +//| Mr = rotation _matrix (pRotation) | +//| Mt = translation _matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixAffineTransformation(DXMatrix &out, float scaling, const DXVector3 &rotationcenter, + const DXQuaternion &rotation, const DXVector3 &translation) { + DXMatrixIdentity(out); + //--- rotation + float temp00 = 1.0f - 2.0f * (rotation.y * rotation.y + rotation.z * rotation.z); + float temp01 = 2.0f * (rotation.x * rotation.y + rotation.z * rotation.w); + float temp02 = 2.0f * (rotation.x * rotation.z - rotation.y * rotation.w); + float temp10 = 2.0f * (rotation.x * rotation.y - rotation.z * rotation.w); + float temp11 = 1.0f - 2.0f * (rotation.x * rotation.x + rotation.z * rotation.z); + float temp12 = 2.0f * (rotation.y * rotation.z + rotation.x * rotation.w); + float temp20 = 2.0f * (rotation.x * rotation.z + rotation.y * rotation.w); + float temp21 = 2.0f * (rotation.y * rotation.z - rotation.x * rotation.w); + float temp22 = 1.0f - 2.0f * (rotation.x * rotation.x + rotation.y * rotation.y); + //--- scaling + out.m[0][0] = scaling * temp00; + out.m[0][1] = scaling * temp01; + out.m[0][2] = scaling * temp02; + out.m[1][0] = scaling * temp10; + out.m[1][1] = scaling * temp11; + out.m[1][2] = scaling * temp12; + out.m[2][0] = scaling * temp20; + out.m[2][1] = scaling * temp21; + out.m[2][2] = scaling * temp22; + //--- rotationcenter + out.m[3][0] = rotationcenter.x * (1.0f - temp00) - rotationcenter.y * temp10 - rotationcenter.z * temp20; + out.m[3][1] = rotationcenter.y * (1.0f - temp11) - rotationcenter.x * temp01 - rotationcenter.z * temp21; + out.m[3][2] = rotationcenter.z * (1.0f - temp22) - rotationcenter.x * temp02 - rotationcenter.y * temp12; + //--- translation + out.m[3][0] += translation.x; + out.m[3][1] += translation.y; + out.m[3][2] += translation.z; +} +//+------------------------------------------------------------------+ +//| Builds a 2D affine transformation _matrix in the xy plane. | +//+------------------------------------------------------------------+ +//| This function calculates the affine transformation _matrix | +//| with the following formula, with _matrix concatenation evaluated | +//| in left-to-right order: | +//| Mout = Ms * (Mrc)^(-1) * Mr * Mrc * Mt | +//| where: | +//| Mout = output _matrix (pOut) | +//| Ms = scaling _matrix (Scaling) | +//| Mrc = center of rotation _matrix (pRotationCenter) | +//| Mr = rotation _matrix (Rotation) | +//| Mt = translation _matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixAffineTransformation2D(DXMatrix &out, float scaling, const DXVector2 &rotationcenter, float rotation, + const DXVector2 &translation) { + float s = (float)sin(rotation / 2.0f); + float tmp1 = 1.0f - 2.0f * s * s; + float tmp2 = 2.0f * s * (float)cos(rotation / 2.0f); + //--- + DXMatrixIdentity(out); + out.m[0][0] = scaling * tmp1; + out.m[0][1] = scaling * tmp2; + out.m[1][0] = -scaling * tmp2; + out.m[1][1] = scaling * tmp1; + //--- rotationcenter + float x = rotationcenter.x; + float y = rotationcenter.y; + out.m[3][0] = y * tmp2 - x * tmp1 + x; + out.m[3][1] = -x * tmp2 - y * tmp1 + y; + //--- translation + out.m[3][0] += translation.x; + out.m[3][1] += translation.y; +} +#define D3DERR_INVALIDCALL -2005530516 +//#define S_OK 0; +//+------------------------------------------------------------------+ +//| Breaks down a general 3D transformation _matrix into its scalar, | +//| rotational, and translational components. | +//+------------------------------------------------------------------+ +int DXMatrixDecompose(DXVector3 &poutscale, DXQuaternion &poutrotation, DXVector3 &pouttranslation, + const DXMatrix &pm) { + DXMatrix normalized; + DXVector3 vec; + //--- Compute the scaling part + vec.x = pm.m[0][0]; + vec.y = pm.m[0][1]; + vec.z = pm.m[0][2]; + poutscale.x = DXVec3Length(vec); + vec.x = pm.m[1][0]; + vec.y = pm.m[1][1]; + vec.z = pm.m[1][2]; + poutscale.y = DXVec3Length(vec); + vec.x = pm.m[2][0]; + vec.y = pm.m[2][1]; + vec.z = pm.m[2][2]; + poutscale.z = DXVec3Length(vec); + //--- compute the translation part + pouttranslation.x = pm.m[3][0]; + pouttranslation.y = pm.m[3][1]; + pouttranslation.z = pm.m[3][2]; + //--- let's calculate the rotation now + if ((poutscale.x == 0.0f) || (poutscale.y == 0.0f) || (poutscale.z == 0.0f)) return (D3DERR_INVALIDCALL); + //--- + normalized.m[0][0] = pm.m[0][0] / poutscale.x; + normalized.m[0][1] = pm.m[0][1] / poutscale.x; + normalized.m[0][2] = pm.m[0][2] / poutscale.x; + normalized.m[1][0] = pm.m[1][0] / poutscale.y; + normalized.m[1][1] = pm.m[1][1] / poutscale.y; + normalized.m[1][2] = pm.m[1][2] / poutscale.y; + normalized.m[2][0] = pm.m[2][0] / poutscale.z; + normalized.m[2][1] = pm.m[2][1] / poutscale.z; + normalized.m[2][2] = pm.m[2][2] / poutscale.z; + DXQuaternionRotationMatrix(poutrotation, normalized); + //--- + return (0); +} +//+------------------------------------------------------------------+ +//| Returns the determinant of a _matrix. | +//+------------------------------------------------------------------+ +float DXMatrixDeterminant(const DXMatrix &pm) { + float t[3], v[4]; + t[0] = pm.m[2][2] * pm.m[3][3] - pm.m[2][3] * pm.m[3][2]; + t[1] = pm.m[1][2] * pm.m[3][3] - pm.m[1][3] * pm.m[3][2]; + t[2] = pm.m[1][2] * pm.m[2][3] - pm.m[1][3] * pm.m[2][2]; + v[0] = pm.m[1][1] * t[0] - pm.m[2][1] * t[1] + pm.m[3][1] * t[2]; + v[1] = -pm.m[1][0] * t[0] + pm.m[2][0] * t[1] - pm.m[3][0] * t[2]; + //--- + t[0] = pm.m[1][0] * pm.m[2][1] - pm.m[2][0] * pm.m[1][1]; + t[1] = pm.m[1][0] * pm.m[3][1] - pm.m[3][0] * pm.m[1][1]; + t[2] = pm.m[2][0] * pm.m[3][1] - pm.m[3][0] * pm.m[2][1]; + v[2] = pm.m[3][3] * t[0] - pm.m[2][3] * t[1] + pm.m[1][3] * t[2]; + v[3] = -pm.m[3][2] * t[0] + pm.m[2][2] * t[1] - pm.m[1][2] * t[2]; + //--- + return (pm.m[0][0] * v[0] + pm.m[0][1] * v[1] + pm.m[0][2] * v[2] + pm.m[0][3] * v[3]); +} +//+------------------------------------------------------------------+ +//| Calculates the inverse of a _matrix. | +//+------------------------------------------------------------------+ +void DXMatrixInverse(DXMatrix &pout, float &pdeterminant, const DXMatrix &pm) { + float t[3], v[16]; + t[0] = pm.m[2][2] * pm.m[3][3] - pm.m[2][3] * pm.m[3][2]; + t[1] = pm.m[1][2] * pm.m[3][3] - pm.m[1][3] * pm.m[3][2]; + t[2] = pm.m[1][2] * pm.m[2][3] - pm.m[1][3] * pm.m[2][2]; + v[0] = pm.m[1][1] * t[0] - pm.m[2][1] * t[1] + pm.m[3][1] * t[2]; + v[4] = -pm.m[1][0] * t[0] + pm.m[2][0] * t[1] - pm.m[3][0] * t[2]; + //--- + t[0] = pm.m[1][0] * pm.m[2][1] - pm.m[2][0] * pm.m[1][1]; + t[1] = pm.m[1][0] * pm.m[3][1] - pm.m[3][0] * pm.m[1][1]; + t[2] = pm.m[2][0] * pm.m[3][1] - pm.m[3][0] * pm.m[2][1]; + v[8] = pm.m[3][3] * t[0] - pm.m[2][3] * t[1] + pm.m[1][3] * t[2]; + v[12] = -pm.m[3][2] * t[0] + pm.m[2][2] * t[1] - pm.m[1][2] * t[2]; + //--- + float det = pm.m[0][0] * v[0] + pm.m[0][1] * v[4] + pm.m[0][2] * v[8] + pm.m[0][3] * v[12]; + if (det == 0.0f) { + for (int j = 0; j < 4; j++) + for (int i = 0; i < 4; i++) { + pout.m[j, i] = 0.0; + } + //--- + return; + } + if (pdeterminant) pdeterminant = det; + //--- + t[0] = pm.m[2][2] * pm.m[3][3] - pm.m[2][3] * pm.m[3][2]; + t[1] = pm.m[0][2] * pm.m[3][3] - pm.m[0][3] * pm.m[3][2]; + t[2] = pm.m[0][2] * pm.m[2][3] - pm.m[0][3] * pm.m[2][2]; + v[1] = -pm.m[0][1] * t[0] + pm.m[2][1] * t[1] - pm.m[3][1] * t[2]; + v[5] = pm.m[0][0] * t[0] - pm.m[2][0] * t[1] + pm.m[3][0] * t[2]; + //--- + t[0] = pm.m[0][0] * pm.m[2][1] - pm.m[2][0] * pm.m[0][1]; + t[1] = pm.m[3][0] * pm.m[0][1] - pm.m[0][0] * pm.m[3][1]; + t[2] = pm.m[2][0] * pm.m[3][1] - pm.m[3][0] * pm.m[2][1]; + v[9] = -pm.m[3][3] * t[0] - pm.m[2][3] * t[1] - pm.m[0][3] * t[2]; + v[13] = pm.m[3][2] * t[0] + pm.m[2][2] * t[1] + pm.m[0][2] * t[2]; + //--- + t[0] = pm.m[1][2] * pm.m[3][3] - pm.m[1][3] * pm.m[3][2]; + t[1] = pm.m[0][2] * pm.m[3][3] - pm.m[0][3] * pm.m[3][2]; + t[2] = pm.m[0][2] * pm.m[1][3] - pm.m[0][3] * pm.m[1][2]; + v[2] = pm.m[0][1] * t[0] - pm.m[1][1] * t[1] + pm.m[3][1] * t[2]; + v[6] = -pm.m[0][0] * t[0] + pm.m[1][0] * t[1] - pm.m[3][0] * t[2]; + //--- + t[0] = pm.m[0][0] * pm.m[1][1] - pm.m[1][0] * pm.m[0][1]; + t[1] = pm.m[3][0] * pm.m[0][1] - pm.m[0][0] * pm.m[3][1]; + t[2] = pm.m[1][0] * pm.m[3][1] - pm.m[3][0] * pm.m[1][1]; + v[10] = pm.m[3][3] * t[0] + pm.m[1][3] * t[1] + pm.m[0][3] * t[2]; + v[14] = -pm.m[3][2] * t[0] - pm.m[1][2] * t[1] - pm.m[0][2] * t[2]; + //--- + t[0] = pm.m[1][2] * pm.m[2][3] - pm.m[1][3] * pm.m[2][2]; + t[1] = pm.m[0][2] * pm.m[2][3] - pm.m[0][3] * pm.m[2][2]; + t[2] = pm.m[0][2] * pm.m[1][3] - pm.m[0][3] * pm.m[1][2]; + v[3] = -pm.m[0][1] * t[0] + pm.m[1][1] * t[1] - pm.m[2][1] * t[2]; + v[7] = pm.m[0][0] * t[0] - pm.m[1][0] * t[1] + pm.m[2][0] * t[2]; + //--- + v[11] = -pm.m[0][0] * (pm.m[1][1] * pm.m[2][3] - pm.m[1][3] * pm.m[2][1]) + + pm.m[1][0] * (pm.m[0][1] * pm.m[2][3] - pm.m[0][3] * pm.m[2][1]) - + pm.m[2][0] * (pm.m[0][1] * pm.m[1][3] - pm.m[0][3] * pm.m[1][1]); + //--- + v[15] = pm.m[0][0] * (pm.m[1][1] * pm.m[2][2] - pm.m[1][2] * pm.m[2][1]) - + pm.m[1][0] * (pm.m[0][1] * pm.m[2][2] - pm.m[0][2] * pm.m[2][1]) + + pm.m[2][0] * (pm.m[0][1] * pm.m[1][2] - pm.m[0][2] * pm.m[1][1]); + //--- + det = 1.0f / det; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) pout.m[i][j] = v[4 * i + j] * det; +} +//+------------------------------------------------------------------+ +//| Builds a left-handed,look-at _matrix. | +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| | +//| zaxis = normal(At - Eye) | +//| xaxis = normal(cross(Up,zaxis)) | +//| yaxis = cross(zaxis,xaxis) | +//| | +//| xaxis.x yaxis.x zaxis.x 0 | +//| xaxis.y yaxis.y zaxis.y 0 | +//| xaxis.z yaxis.z zaxis.z 0 | +//| -dot(xaxis,eye) -dot(yaxis,eye) -dot(zaxis,eye) 1 | +//+------------------------------------------------------------------+ +void DXMatrixLookAtLH(DXMatrix &out, const DXVector3 &eye, const DXVector3 &at, const DXVector3 &up) { + DXVector3 right, upn, vec; + DXVec3Subtract(vec, at, eye); + DXVec3Normalize(vec, vec); + DXVec3Cross(right, up, vec); + DXVec3Cross(upn, vec, right); + DXVec3Normalize(right, right); + DXVec3Normalize(upn, upn); + //--- + out.m[0][0] = right.x; + out.m[1][0] = right.y; + out.m[2][0] = right.z; + out.m[3][0] = -DXVec3Dot(right, eye); + out.m[0][1] = upn.x; + out.m[1][1] = upn.y; + out.m[2][1] = upn.z; + out.m[3][1] = -DXVec3Dot(upn, eye); + out.m[0][2] = vec.x; + out.m[1][2] = vec.y; + out.m[2][2] = vec.z; + out.m[3][2] = -DXVec3Dot(vec, eye); + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; +} +//+------------------------------------------------------------------+ +//| Builds a right-handed, look-at _matrix. | +//+------------------------------------------------------------------+ +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| | +//| zaxis = normal(Eye - At) | +//| xaxis = normal(cross(Up,zaxis)) | +//| yaxis = cross(zaxis,xaxis) | +//| | +//| xaxis.x yaxis.x zaxis.x 0 | +//| xaxis.y yaxis.y zaxis.y 0 | +//| xaxis.z yaxis.z zaxis.z 0 | +//| dot(xaxis,eye) dot(yaxis,eye) dot(zaxis,eye) 1 | +//+------------------------------------------------------------------+ +void DXMatrixLookAtRH(DXMatrix &out, const DXVector3 &eye, const DXVector3 &at, const DXVector3 &up) { + DXVector3 right, upn, vec; + DXVec3Subtract(vec, at, eye); + DXVec3Normalize(vec, vec); + DXVec3Cross(right, up, vec); + DXVec3Cross(upn, vec, right); + DXVec3Normalize(right, right); + DXVec3Normalize(upn, upn); + //--- + out.m[0][0] = -right.x; + out.m[1][0] = -right.y; + out.m[2][0] = -right.z; + out.m[3][0] = DXVec3Dot(right, eye); + out.m[0][1] = upn.x; + out.m[1][1] = upn.y; + out.m[2][1] = upn.z; + out.m[3][1] = -DXVec3Dot(upn, eye); + out.m[0][2] = -vec.x; + out.m[1][2] = -vec.y; + out.m[2][2] = -vec.z; + out.m[3][2] = DXVec3Dot(vec, eye); + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; +} +//+------------------------------------------------------------------+ +//| Determines the product of two matrices. | +//+------------------------------------------------------------------+ +void DXMatrixMultiply(DXMatrix &pout, const DXMatrix &pm1, const DXMatrix &pm2) { + DXMatrix out = {}; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + out.m[i][j] = + pm1.m[i][0] * pm2.m[0][j] + pm1.m[i][1] * pm2.m[1][j] + pm1.m[i][2] * pm2.m[2][j] + pm1.m[i][3] * pm2.m[3][j]; + } + } + pout = out; +} +//+------------------------------------------------------------------+ +//| Calculates the transposed product of two matrices. | +//+------------------------------------------------------------------+ +void DXMatrixMultiplyTranspose(DXMatrix &pout, const DXMatrix &pm1, const DXMatrix &pm2) { + DXMatrix temp = {}; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + temp.m[j][i] = + pm1.m[i][0] * pm2.m[0][j] + pm1.m[i][1] * pm2.m[1][j] + pm1.m[i][2] * pm2.m[2][j] + pm1.m[i][3] * pm2.m[3][j]; + pout = temp; +} +//+------------------------------------------------------------------+ +//| Builds a left-handed orthographic projection _matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoLH(DXMatrix &pout, float w, float h, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f / w; + pout.m[1][1] = 2.0f / h; + pout.m[2][2] = 1.0f / (zf - zn); + pout.m[3][2] = zn / (zn - zf); +} +//+------------------------------------------------------------------+ +//| Builds a customized,left-handed orthographic projection _matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoOffCenterLH(DXMatrix &pout, float l, float r, float b, float t, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f / (r - l); + pout.m[1][1] = 2.0f / (t - b); + pout.m[2][2] = 1.0f / (zf - zn); + pout.m[3][0] = -1.0f - 2.0f * l / (r - l); + pout.m[3][1] = 1.0f + 2.0f * t / (b - t); + pout.m[3][2] = zn / (zn - zf); +} +//+------------------------------------------------------------------+ +//| Builds a customized,right-handed orthographic projection _matrix. | +//+------------------------------------------------------------------+ +void DXMatrixOrthoOffCenterRH(DXMatrix &pout, float l, float r, float b, float t, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f / (r - l); + pout.m[1][1] = 2.0f / (t - b); + pout.m[2][2] = 1.0f / (zn - zf); + pout.m[3][0] = -1.0f - 2.0f * l / (r - l); + pout.m[3][1] = 1.0f + 2.0f * t / (b - t); + pout.m[3][2] = zn / (zn - zf); +} +//+------------------------------------------------------------------+ +//| Builds a right-handed orthographic projection _matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixOrthoRH function | +//| are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned _matrix: | +//| 2/w 0 0 0 | +//| 0 2/h 0 0 | +//| 0 0 1/(zn-zf) 0 | +//| 0 0 zn/(zn-zf) 1 | +//+------------------------------------------------------------------+ +void DXMatrixOrthoRH(DXMatrix &pout, float w, float h, float zn, float zf) { + DXMatrixIdentity(pout); + pout.m[0][0] = 2.0f / w; + pout.m[1][1] = 2.0f / h; + pout.m[2][2] = 1.0f / (zn - zf); + pout.m[3][2] = zn / (zn - zf); +} +//+------------------------------------------------------------------+ +//| Builds a left-handed perspective projection _matrix | +//| based on a field of view. | +//+------------------------------------------------------------------+ +//| This function computes the returned _matrix as shown: | +//| xScale 0 0 0 | +//| 0 yScale 0 0 | +//| 0 0 zf/(zf-zn) 1 | +//| 0 0 -zn*zf/(zf-zn) 0 | +//| where: | +//| yScale = cot(fovY/2) | +//| xScale = yScale / aspect ratio | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveFovLH(DXMatrix &pout, float fovy, float aspect, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 1.0f / (aspect * (float)tan(fovy / 2.0f)); + pout.m[1][1] = 1.0f / (float)tan(fovy / 2.0f); + pout.m[2][2] = zf / (zf - zn); + pout.m[2][3] = 1.0f; + pout.m[3][2] = (zf * zn) / (zn - zf); + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a right-handed perspective projection _matrix | +//| based on a field of view. | +//+------------------------------------------------------------------+ +//| This function computes the returned _matrix as shown. | +//| xScale 0 0 0 | +//| 0 yScale 0 0 | +//| 0 0 zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//| where: | +//| yScale = cot(fovY/2) | +//| xScale = yScale / aspect ratio | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveFovRH(DXMatrix &pout, float fovy, float aspect, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 1.0f / (aspect * (float)tan(fovy / 2.0f)); + pout.m[1][1] = 1.0f / (float)tan(fovy / 2.0f); + pout.m[2][2] = zf / (zn - zf); + pout.m[2][3] = -1.0f; + pout.m[3][2] = (zf * zn) / (zn - zf); + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a left-handed perspective projection _matrix | +//+------------------------------------------------------------------+ +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| 2*zn/w 0 0 0 | +//| 0 2*zn/h 0 0 | +//| 0 0 zf/(zf-zn) 1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveLH(DXMatrix &pout, float w, float h, float zn, float zf) { + DXMatrixIdentity(pout); + pout.m[0][0] = 2.0f * zn / w; + pout.m[1][1] = 2.0f * zn / h; + pout.m[2][2] = zf / (zf - zn); + pout.m[3][2] = (zn * zf) / (zn - zf); + pout.m[2][3] = 1.0f; + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a customized, left-handed perspective projection _matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveOffCenterLH | +//| function are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| 2*zn/(r-l) 0 0 0 | +//| 0 2*zn/(t-b) 0 0 | +//| (l+r)/(l-r) (t+b)/(b-t) zf/(zf-zn) 1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveOffCenterLH(DXMatrix &pout, float l, float r, float b, float t, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f * zn / (r - l); + pout.m[1][1] = -2.0f * zn / (b - t); + pout.m[2][0] = -1.0f - 2.0f * l / (r - l); + pout.m[2][1] = 1.0f + 2.0f * t / (b - t); + pout.m[2][2] = -zf / (zn - zf); + pout.m[3][2] = (zn * zf) / (zn - zf); + pout.m[2][3] = 1.0f; + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a customized, right-handed perspective projection _matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveOffCenterRH | +//| function are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| 2*zn/(r-l) 0 0 0 | +//| 0 2*zn/(t-b) 0 0 | +//| (l+r)/(r-l) (t+b)/(t-b) zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveOffCenterRH(DXMatrix &pout, float l, float r, float b, float t, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f * zn / (r - l); + pout.m[1][1] = -2.0f * zn / (b - t); + pout.m[2][0] = 1.0f + 2.0f * l / (r - l); + pout.m[2][1] = -1.0f - 2.0f * t / (b - t); + pout.m[2][2] = zf / (zn - zf); + pout.m[3][2] = (zn * zf) / (zn - zf); + pout.m[2][3] = -1.0f; + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a right-handed perspective projection _matrix. | +//+------------------------------------------------------------------+ +//| All the parameters of the DXMatrixPerspectiveRH function | +//| are distances in camera space. The parameters describe | +//| the dimensions of the view volume. | +//| | +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| 2*zn/w 0 0 0 | +//| 0 2*zn/h 0 0 | +//| 0 0 zf/(zn-zf) -1 | +//| 0 0 zn*zf/(zn-zf) 0 | +//+------------------------------------------------------------------+ +void DXMatrixPerspectiveRH(DXMatrix &pout, float w, float h, float zn, float zf) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 2.0f * zn / w; + pout.m[1][1] = 2.0f * zn / h; + pout.m[2][2] = zf / (zn - zf); + pout.m[3][2] = (zn * zf) / (zn - zf); + pout.m[2][3] = -1.0f; + pout.m[3][3] = 0.0f; +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that reflects the coordinate system about a plane| +//| This function normalizes the plane equation before it creates | +//| the reflected _matrix. | +//| | +//| This function uses the following formula to compute | +//| the returned _matrix. | +//| P = normalize(Plane); | +//| -2 * P.a * P.a + 1 -2 * P.b * P.a -2 * P.c * P.a 0 | +//| -2 * P.a * P.b -2 * P.b * P.b + 1 -2 * P.c * P.b 0 | +//| -2 * P.a * P.c -2 * P.b * P.c -2 * P.c * P.c + 1 0 | +//| -2 * P.a * P.d -2 * P.b * P.d -2 * P.c * P.d 1 | +//+------------------------------------------------------------------+ +void DXMatrixReflect(DXMatrix &pout, const DXPlane &pplane) { + DXPlane Nplane; + DXPlaneNormalize(Nplane, pplane); + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 1.0f - 2.0f * Nplane.a * Nplane.a; + pout.m[0][1] = -2.0f * Nplane.a * Nplane.b; + pout.m[0][2] = -2.0f * Nplane.a * Nplane.c; + pout.m[1][0] = -2.0f * Nplane.a * Nplane.b; + pout.m[1][1] = 1.0f - 2.0f * Nplane.b * Nplane.b; + pout.m[1][2] = -2.0f * Nplane.b * Nplane.c; + pout.m[2][0] = -2.0f * Nplane.c * Nplane.a; + pout.m[2][1] = -2.0f * Nplane.c * Nplane.b; + pout.m[2][2] = 1.0f - 2.0f * Nplane.c * Nplane.c; + pout.m[3][0] = -2.0f * Nplane.d * Nplane.a; + pout.m[3][1] = -2.0f * Nplane.d * Nplane.b; + pout.m[3][2] = -2.0f * Nplane.d * Nplane.c; +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that rotates around an arbitrary axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationAxis(DXMatrix &out, const DXVector3 &v, float angle) { + DXVector3 nv; + DXVec3Normalize(nv, v); + //--- + float sangle = (float)sin(angle); + float cangle = (float)cos(angle); + float cdiff = 1.0f - cangle; + //--- + out.m[0][0] = cdiff * nv.x * nv.x + cangle; + out.m[1][0] = cdiff * nv.x * nv.y - sangle * nv.z; + out.m[2][0] = cdiff * nv.x * nv.z + sangle * nv.y; + out.m[3][0] = 0.0f; + out.m[0][1] = cdiff * nv.y * nv.x + sangle * nv.z; + out.m[1][1] = cdiff * nv.y * nv.y + cangle; + out.m[2][1] = cdiff * nv.y * nv.z - sangle * nv.x; + out.m[3][1] = 0.0f; + out.m[0][2] = cdiff * nv.z * nv.x - sangle * nv.y; + out.m[1][2] = cdiff * nv.z * nv.y + sangle * nv.x; + out.m[2][2] = cdiff * nv.z * nv.z + cangle; + out.m[3][2] = 0.0f; + out.m[0][3] = 0.0f; + out.m[1][3] = 0.0f; + out.m[2][3] = 0.0f; + out.m[3][3] = 1.0f; +} +//+------------------------------------------------------------------+ +//| Builds a rotation _matrix from a quaternion. | +//+------------------------------------------------------------------+ +void DXMatrixRotationQuaternion(DXMatrix &pout, const DXQuaternion &pq) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = 1.0f - 2.0f * (pq.y * pq.y + pq.z * pq.z); + pout.m[0][1] = 2.0f * (pq.x * pq.y + pq.z * pq.w); + pout.m[0][2] = 2.0f * (pq.x * pq.z - pq.y * pq.w); + pout.m[1][0] = 2.0f * (pq.x * pq.y - pq.z * pq.w); + pout.m[1][1] = 1.0f - 2.0f * (pq.x * pq.x + pq.z * pq.z); + pout.m[1][2] = 2.0f * (pq.y * pq.z + pq.x * pq.w); + pout.m[2][0] = 2.0f * (pq.x * pq.z + pq.y * pq.w); + pout.m[2][1] = 2.0f * (pq.y * pq.z - pq.x * pq.w); + pout.m[2][2] = 1.0f - 2.0f * (pq.x * pq.x + pq.y * pq.y); +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that rotates around the x-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationX(DXMatrix &pout, float angle) { + DXMatrixIdentity(pout); + //--- + pout.m[1][1] = (float)cos(angle); + pout.m[2][2] = (float)cos(angle); + pout.m[1][2] = (float)sin(angle); + pout.m[2][1] = -(float)sin(angle); +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that rotates around the y-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationY(DXMatrix &pout, float angle) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = (float)cos(angle); + pout.m[2][2] = (float)cos(angle); + pout.m[0][2] = -(float)sin(angle); + pout.m[2][0] = (float)sin(angle); +} +//+------------------------------------------------------------------+ +//| Builds a _matrix with a specified yaw, pitch, and roll. | +//+------------------------------------------------------------------+ +//| The order of transformations is roll first, then pitch, then yaw.| +//| Relative to the object's local coordinate axis, this is | +//| equivalent to rotation around the z-axis, followed by rotation | +//| around the x-axis, followed by rotation around the y-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationYawPitchRoll(DXMatrix &out, float yaw, float pitch, float roll) { + float sroll = (float)sin(roll); + float croll = (float)cos(roll); + float spitch = (float)sin(pitch); + float cpitch = (float)cos(pitch); + float syaw = (float)sin(yaw); + float cyaw = (float)cos(yaw); + //--- + out.m[0][0] = sroll * spitch * syaw + croll * cyaw; + out.m[0][1] = sroll * cpitch; + out.m[0][2] = sroll * spitch * cyaw - croll * syaw; + out.m[0][3] = 0.0f; + out.m[1][0] = croll * spitch * syaw - sroll * cyaw; + out.m[1][1] = croll * cpitch; + out.m[1][2] = croll * spitch * cyaw + sroll * syaw; + out.m[1][3] = 0.0f; + out.m[2][0] = cpitch * syaw; + out.m[2][1] = -spitch; + out.m[2][2] = cpitch * cyaw; + out.m[2][3] = 0.0f; + out.m[3][0] = 0.0f; + out.m[3][1] = 0.0f; + out.m[3][2] = 0.0f; + out.m[3][3] = 1.0f; +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that rotates around the z-axis. | +//+------------------------------------------------------------------+ +void DXMatrixRotationZ(DXMatrix &pout, float angle) { + DXMatrixIdentity(pout); + //--- + pout.m[0][0] = (float)cos(angle); + pout.m[1][1] = (float)cos(angle); + pout.m[0][1] = (float)sin(angle); + pout.m[1][0] = -(float)sin(angle); +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that scales along the x-axis, | +//| the y-axis,and the z-axis. | +//+------------------------------------------------------------------+ +void DXMatrixScaling(DXMatrix &pout, float sx, float sy, float sz) { + DXMatrixIdentity(pout); + pout.m[0][0] = sx; + pout.m[1][1] = sy; + pout.m[2][2] = sz; +} +//+------------------------------------------------------------------+ +//| Builds a _matrix that flattens geometry into a plane. | +//+------------------------------------------------------------------+ +//| The DXMatrixShadow function flattens geometry into a plane, as | +//| if casting a shadow from a light. | +//| This function uses the following formula to compute the returned | +//| _matrix. | +//| | +//| P = normalize(Plane); | +//| L = Light; | +//| d = -dot(P,L) | +//| | +//| P.a * L.x + d P.a * L.y P.a * L.z P.a * L.w | +//| P.b * L.x P.b * L.y + d P.b * L.z P.b * L.w | +//| P.c * L.x P.c * L.y P.c * L.z + d P.c * L.w | +//| P.d * L.x P.d * L.y P.d * L.z P.d * L.w + d | +//| | +//| If the light's w-component is 0, the ray from the origin to the | +//| light represents a directional light. If it is 1,the light is | +//| a point light. | +//+------------------------------------------------------------------+ +void DXMatrixShadow(DXMatrix &pout, const DXVector4 &plight, const DXPlane &pplane) { + DXPlane Nplane; + DXPlaneNormalize(Nplane, pplane); + float dot = DXPlaneDot(Nplane, plight); + //--- + pout.m[0][0] = dot - Nplane.a * plight.x; + pout.m[0][1] = -Nplane.a * plight.y; + pout.m[0][2] = -Nplane.a * plight.z; + pout.m[0][3] = -Nplane.a * plight.w; + pout.m[1][0] = -Nplane.b * plight.x; + pout.m[1][1] = dot - Nplane.b * plight.y; + pout.m[1][2] = -Nplane.b * plight.z; + pout.m[1][3] = -Nplane.b * plight.w; + pout.m[2][0] = -Nplane.c * plight.x; + pout.m[2][1] = -Nplane.c * plight.y; + pout.m[2][2] = dot - Nplane.c * plight.z; + pout.m[2][3] = -Nplane.c * plight.w; + pout.m[3][0] = -Nplane.d * plight.x; + pout.m[3][1] = -Nplane.d * plight.y; + pout.m[3][2] = -Nplane.d * plight.z; + pout.m[3][3] = dot - Nplane.d * plight.w; +} +//+------------------------------------------------------------------+ +//| Builds a transformation _matrix. | +//+------------------------------------------------------------------+ +//| This function calculates the transformation _matrix with the | +//| following formula, with _matrix concatenation evaluated | +//| in left-to-right order: | +//| | +//| Mout = (Msc)^(-1)*(Msr)^(-1)*Ms*Msr*Msc*(Mrc)^(-1)*Mr*Mrc*Mt | +//| | +//| where: | +//| Mout = output _matrix (pOut) | +//| Msc = scaling center _matrix (pScalingCenter) | +//| Msr = scaling rotation _matrix (pScalingRotation) | +//| Ms = scaling _matrix (pScaling) | +//| Mrc = center of rotation _matrix (pRotationCenter) | +//| Mr = rotation _matrix (pRotation) | +//| Mt = translation _matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixTransformation(DXMatrix &pout, const DXVector3 &pscalingcenter, const DXQuaternion &pscalingrotation, + const DXVector3 &pscaling, const DXVector3 &protationcenter, const DXQuaternion &protation, + const DXVector3 &ptranslation) { + DXMatrix m1, m2, m3, m4, m5, m6, m7; + DXQuaternion prc; + DXVector3 psc, pt; + //--- pscalingcenter + psc.x = pscalingcenter.x; + psc.y = pscalingcenter.y; + psc.z = pscalingcenter.z; + //--- protationcenter + prc.x = protationcenter.x; + prc.y = protationcenter.y; + prc.z = protationcenter.z; + //--- ptranslation + pt.x = ptranslation.x; + pt.y = ptranslation.y; + pt.z = ptranslation.z; + DXMatrixTranslation(m1, -psc.x, -psc.y, -psc.z); + //--- + DXQuaternion temp; + DXMatrixRotationQuaternion(m4, pscalingrotation); + temp.w = pscalingrotation.w; + temp.x = -pscalingrotation.x; + temp.y = -pscalingrotation.y; + temp.z = -pscalingrotation.z; + DXMatrixRotationQuaternion(m2, temp); + //--- pscaling + DXMatrixScaling(m3, pscaling.x, pscaling.y, pscaling.z); + //--- protation + DXMatrixRotationQuaternion(m6, protation); + //--- + DXMatrixTranslation(m5, psc.x - prc.x, psc.y - prc.y, psc.z - prc.z); + DXMatrixTranslation(m7, prc.x + pt.x, prc.y + pt.y, prc.z + pt.z); + DXMatrixMultiply(m1, m1, m2); + DXMatrixMultiply(m1, m1, m3); + DXMatrixMultiply(m1, m1, m4); + DXMatrixMultiply(m1, m1, m5); + DXMatrixMultiply(m1, m1, m6); + DXMatrixMultiply(pout, m1, m7); +} +//+------------------------------------------------------------------+ +//| Builds a 2D transformation _matrix that represents | +//| transformations in the xy plane. | +//+------------------------------------------------------------------+ +//| This function calculates the transformation _matrix with the | +//| following formula, with _matrix concatenation evaluated | +//| in left-to-right order: | +//| | +//| Mout = (Msc)^(-1)*(Msr)^(-1)*Ms*Msr*Msc*(Mrc)^(-1)*Mr*Mrc*Mt | +//| | +//| where: | +//| Mout = output _matrix (pOut) | +//| Msc = scaling center _matrix (pScalingCenter) | +//| Msr = scaling rotation _matrix (pScalingRotation) | +//| Ms = scaling _matrix (pScaling) | +//| Mrc = center of rotation _matrix (pRotationCenter) | +//| Mr = rotation _matrix (Rotation) | +//| Mt = translation _matrix (pTranslation) | +//+------------------------------------------------------------------+ +void DXMatrixTransformation2D(DXMatrix &pout, const DXVector2 &pscalingcenter, float scalingrotation, + const DXVector2 &pscaling, const DXVector2 &protationcenter, float rotation, + const DXVector2 &ptranslation) { + DXQuaternion rot, sca_rot; + DXVector3 rot_center, sca, sca_center, trans; + //--- pscalingcenter + sca_center.x = pscalingcenter.x; + sca_center.y = pscalingcenter.y; + sca_center.z = 0.0f; + //--- pscaling + sca.x = pscaling.x; + sca.y = pscaling.y; + sca.z = 1.0f; + //--- protationcenter + rot_center.x = protationcenter.x; + rot_center.y = protationcenter.y; + rot_center.z = 0.0f; + //--- ptranslation + trans.x = ptranslation.x; + trans.y = ptranslation.y; + trans.z = 0.0f; + //--- + rot.w = (float)cos(rotation / 2.0f); + rot.x = 0.0f; + rot.y = 0.0f; + rot.z = (float)sin(rotation / 2.0f); + //--- + sca_rot.w = (float)cos(scalingrotation / 2.0f); + sca_rot.x = 0.0f; + sca_rot.y = 0.0f; + sca_rot.z = (float)sin(scalingrotation / 2.0f); + DXMatrixTransformation(pout, sca_center, sca_rot, sca, rot_center, rot, trans); +} +//+------------------------------------------------------------------+ +//| Builds a _matrix using the specified offsets. | +//+------------------------------------------------------------------+ +void DXMatrixTranslation(DXMatrix &pout, float x, float y, float z) { + DXMatrixIdentity(pout); + //--- + pout.m[3][0] = x; + pout.m[3][1] = y; + pout.m[3][2] = z; +} +//+------------------------------------------------------------------+ +//| Returns the _matrix transpose of a _matrix. | +//+------------------------------------------------------------------+ +void DXMatrixTranspose(DXMatrix &pout, const DXMatrix &pm) { + const DXMatrix m = pm; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) pout.m[i][j] = m.m[j][i]; +} +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 4D vector. | +//+------------------------------------------------------------------+ +float DXPlaneDot(const DXPlane &p1, const DXVector4 &p2) { + return (p1.a * p2.x + p1.b * p2.y + p1.c * p2.z + p1.d * p2.w); +} +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 3D vector. | +//| The w parameter of the vector is assumed to be 1. | +//+------------------------------------------------------------------+ +float DXPlaneDotCoord(const DXPlane &pp, const DXVector4 &pv) { + return (pp.a * pv.x + pp.b * pv.y + pp.c * pv.z + pp.d); +} +//+------------------------------------------------------------------+ +//| Computes the dot product of a plane and a 3D vector. | +//| The w parameter of the vector is assumed to be 0. | +//+------------------------------------------------------------------+ +float DXPlaneDotNormal(const DXPlane &pp, const DXVector4 &pv) { return (pp.a * pv.x + pp.b * pv.y + pp.c * pv.z); } +//+------------------------------------------------------------------+ +//| Constructs a plane from a point and a normal. | +//+------------------------------------------------------------------+ +void DXPlaneFromPointNormal(DXPlane &pout, const DXVector3 &pvpoint, const DXVector3 &pvnormal) { + pout.a = pvnormal.x; + pout.b = pvnormal.y; + pout.c = pvnormal.z; + pout.d = -DXVec3Dot(pvpoint, pvnormal); +} +//+------------------------------------------------------------------+ +//| Constructs a plane from three points. | +//+------------------------------------------------------------------+ +void DXPlaneFromPoints(DXPlane &pout, const DXVector3 &pv1, const DXVector3 &pv2, const DXVector3 &pv3) { + DXVector3 edge1, edge2, normal, Nnormal; + //--- + edge1.x = 0.0f; + edge1.y = 0.0f; + edge1.z = 0.0f; + edge2.x = 0.0f; + edge2.y = 0.0f; + edge2.z = 0.0f; + //--- + DXVec3Subtract(edge1, pv2, pv1); + DXVec3Subtract(edge2, pv3, pv1); + DXVec3Cross(normal, edge1, edge2); + DXVec3Normalize(Nnormal, normal); + DXPlaneFromPointNormal(pout, pv1, Nnormal); +} +//+------------------------------------------------------------------+ +//| Finds the intersection between a plane and a line. | +//| If the line is parallel to the plane, null vector is returned. | +//+------------------------------------------------------------------+ +void DXPlaneIntersectLine(DXVector3 &pout, const DXPlane &pp, const DXVector3 &pv1, const DXVector3 &pv2) { + DXVector3 direction, normal; + normal.x = pp.a; + normal.y = pp.b; + normal.z = pp.c; + direction.x = pv2.x - pv1.x; + direction.y = pv2.y - pv1.y; + direction.z = pv2.z - pv1.z; + //--- + float dot = DXVec3Dot(normal, direction); + if (!dot) { + pout.x = 0.0f; + pout.y = 0.0f; + pout.z = 0.0f; + } + float temp = (pp.d + DXVec3Dot(normal, pv1)) / dot; + pout.x = pv1.x - temp * direction.x; + pout.y = pv1.y - temp * direction.y; + pout.z = pv1.z - temp * direction.z; +} +//+------------------------------------------------------------------+ +//| Normalizes the plane coefficients so that the plane normal | +//| has unit length. | +//| This function normalizes a plane so that |a,b,c| == 1. | +//+------------------------------------------------------------------+ +void DXPlaneNormalize(DXPlane &out, const DXPlane &p) { + float norm = (float)sqrt(p.a * p.a + p.b * p.b + p.c * p.c); + if (norm) { + out.a = p.a / norm; + out.b = p.b / norm; + out.c = p.c / norm; + out.d = p.d / norm; + } else { + out.a = 0.0f; + out.b = 0.0f; + out.c = 0.0f; + out.d = 0.0f; + } +} +//+------------------------------------------------------------------+ +//| Scale the plane with the given scaling factor. | +//+------------------------------------------------------------------+ +void DXPlaneScale(DXPlane &pout, const DXPlane &p, float s) { + pout.a = p.a * s; + pout.b = p.b * s; + pout.c = p.c * s; + pout.d = p.d * s; +}; +//+------------------------------------------------------------------+ +//| Transforms a plane by a _matrix. | +//| The input _matrix is the inverse transpose of the actual | +//| transformation. | +//+------------------------------------------------------------------+ +void DXPlaneTransform(DXPlane &pout, const DXPlane &pplane, const DXMatrix &pm) { + DXPlane plane = pplane; + //--- + pout.a = pm.m[0][0] * plane.a + pm.m[1][0] * plane.b + pm.m[2][0] * plane.c + pm.m[3][0] * plane.d; + pout.b = pm.m[0][1] * plane.a + pm.m[1][1] * plane.b + pm.m[2][1] * plane.c + pm.m[3][1] * plane.d; + pout.c = pm.m[0][2] * plane.a + pm.m[1][2] * plane.b + pm.m[2][2] * plane.c + pm.m[3][2] * plane.d; + pout.d = pm.m[0][3] * plane.a + pm.m[1][3] * plane.b + pm.m[2][3] * plane.c + pm.m[3][3] * plane.d; +} +//+------------------------------------------------------------------+ +//| Adds two spherical harmonic (SH) vectors; in other words, | +//| out[i] = a[i] + b[i]. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) is stored | +//| at memory location l^2 + m + l,where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l, inclusive. | +//+------------------------------------------------------------------+ +void DXSHAdd(float &out[], int order, const float &a[], const float &b[]) { + for (int i = 0; i < order * order; i++) out[i] = a[i] + b[i]; +} +//+------------------------------------------------------------------+ +//| Computes the dot product of two spherical harmonic (SH) vectors. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) is stored | +//| at memory location l^2 + m + l,where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l,inclusive. | +//+------------------------------------------------------------------+ +float DXSHDot(int order, const float &a[], const float &b[]) { + float s = a[0] * b[0]; + for (int i = 1; i < order * order; i++) s += a[i] * b[i]; + //--- + return (s); +} +//+------------------------------------------------------------------+ +//| weightedcapintegrale | +//+------------------------------------------------------------------+ +void weightedcapintegrale(float &out[], uint order, float angle) { + float coeff[3]; + coeff[0] = (float)cos(angle); + + out[0] = 2.0f * DX_PI * (1.0f - coeff[0]); + out[1] = DX_PI * (float)sin(angle) * (float)sin(angle); + if (order <= 2) return; + + out[2] = coeff[0] * out[1]; + if (order == 3) return; + + coeff[1] = coeff[0] * coeff[0]; + coeff[2] = coeff[1] * coeff[1]; + + out[3] = DX_PI * (-1.25f * coeff[2] + 1.5f * coeff[1] - 0.25f); + if (order == 4) return; + + out[4] = -0.25f * DX_PI * coeff[0] * (7.0f * coeff[2] - 10.0f * coeff[1] + 3.0f); + if (order == 5) return; + + out[5] = DX_PI * (-2.625f * coeff[2] * coeff[1] + 4.375f * coeff[2] - 1.875f * coeff[1] + 0.125f); +} +//+------------------------------------------------------------------+ +//| Evaluates a light that is a cone of constant intensity | +//| and returns spectral spherical harmonic (SH) data. | +//+------------------------------------------------------------------+ +//| Evaluates a light that is a cone of constant intensity and | +//| returns spectral SH data. | +//| The output vector is computed so that if the intensity ratio | +//| R/G/B is equal to 1, the exit radiance of a point | +//| directly under the light (oriented in the cone direction | +//| on a diffuse object with an albedo of 1) would be 1.0. | +//| This will compute three spectral samples; | +//| rout[], gout[] and bout[] will be computed. | +//+------------------------------------------------------------------+ +int DXSHEvalConeLight(int order, const DXVector3 &dir, float radius, float Rintensity, float Gintensity, + float Bintensity, float &rout[], float &gout[], float &bout[]) { + float cap[6]; + //--- + if (radius <= 0.0f) + return (DXSHEvalDirectionalLight(order, dir, Rintensity, Gintensity, Bintensity, rout, gout, bout)); + //--- + float clamped_angle = (radius > DX_PI / 2.0f) ? (DX_PI / 2.0f) : radius; + float norm = (float)sin(clamped_angle) * (float)sin(clamped_angle); + if (order > DXSH_MAXORDER) { + //--- order clamped at DXSH_MAXORDER + order = DXSH_MAXORDER; + } + //--- + weightedcapintegrale(cap, order, radius); + DXSHEvalDirection(rout, order, dir); + //--- + for (int i = 0; i < order; i++) { + float scale = cap[i] / norm; + for (int j = 0; j < 2 * i + 1; j++) { + int index = i * i + j; + float temp = rout[index] * scale; + rout[index] = temp * Rintensity; + gout[index] = temp * Gintensity; + bout[index] = temp * Bintensity; + } + } + return (0); +} +//+------------------------------------------------------------------+ +//| Evaluates the spherical harmonic (SH) basis functions | +//| from an input direction vector. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) is stored | +//| at memory location l^2 + m + l, where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l, inclusive. | +//+------------------------------------------------------------------+ +void DXSHEvalDirection(float &out[], int order, const DXVector3 &dir) { + const float dirxx = dir.x * dir.x; + const float dirxy = dir.x * dir.y; + const float dirxz = dir.x * dir.z; + const float diryy = dir.y * dir.y; + const float diryz = dir.y * dir.z; + const float dirzz = dir.z * dir.z; + const float dirxxxx = dirxx * dirxx; + const float diryyyy = diryy * diryy; + const float dirzzzz = dirzz * dirzz; + const float dirxyxy = dirxy * dirxy; + //--- + if ((order < DXSH_MINORDER) || (order > DXSH_MAXORDER)) return; + + out[0] = 0.5f / (float)sqrt(DX_PI); + out[1] = -0.5f / (float)sqrt(DX_PI / 3.0f) * dir.y; + out[2] = 0.5f / (float)sqrt(DX_PI / 3.0f) * dir.z; + out[3] = -0.5f / (float)sqrt(DX_PI / 3.0f) * dir.x; + if (order == 2) return; + + out[4] = 0.5f / (float)sqrt(DX_PI / 15.0f) * dirxy; + out[5] = -0.5f / (float)sqrt(DX_PI / 15.0f) * diryz; + out[6] = 0.25f / (float)sqrt(DX_PI / 5.0f) * (3.0f * dirzz - 1.0f); + out[7] = -0.5f / (float)sqrt(DX_PI / 15.0f) * dirxz; + out[8] = 0.25f / (float)sqrt(DX_PI / 15.0f) * (dirxx - diryy); + if (order == 3) return; + + out[9] = -(float)sqrt(70.0f / DX_PI) / 8.0f * dir.y * (3.0f * dirxx - diryy); + out[10] = (float)sqrt(105.0f / DX_PI) / 2.0f * dirxy * dir.z; + out[11] = -(float)sqrt(42.0f / DX_PI) / 8.0f * dir.y * (-1.0f + 5.0f * dirzz); + out[12] = (float)sqrt(7.0f / DX_PI) / 4.0f * dir.z * (5.0f * dirzz - 3.0f); + out[13] = (float)sqrt(42.0f / DX_PI) / 8.0f * dir.x * (1.0f - 5.0f * dirzz); + out[14] = (float)sqrt(105.0f / DX_PI) / 4.0f * dir.z * (dirxx - diryy); + out[15] = -(float)sqrt(70.0f / DX_PI) / 8.0f * dir.x * (dirxx - 3.0f * diryy); + if (order == 4) return; + + out[16] = 0.75f * float(sqrt(35.0f / DX_PI)) * dirxy * (dirxx - diryy); + out[17] = 3.0f * dir.z * out[9]; + out[18] = 0.75f * (float)sqrt(5.0f / DX_PI) * dirxy * (7.0f * dirzz - 1.0f); + out[19] = 0.375f * (float)sqrt(10.0f / DX_PI) * diryz * (3.0f - 7.0f * dirzz); + out[20] = 3.0f / (16.0f * (float)sqrt(DX_PI)) * (35.0f * dirzzzz - 30.f * dirzz + 3.0f); + out[21] = 0.375f * (float)sqrt(10.0f / DX_PI) * dirxz * (3.0f - 7.0f * dirzz); + out[22] = 0.375f * (float)sqrt(5.0f / DX_PI) * (dirxx - diryy) * (7.0f * dirzz - 1.0f); + out[23] = 3.0f * dir.z * out[15]; + out[24] = 3.0f / 16.0f * float(sqrt(35.0f / DX_PI)) * (dirxxxx - 6.0f * dirxyxy + diryyyy); + if (order == 5) return; + + out[25] = -3.0f / 32.0f * (float)sqrt(154.0f / DX_PI) * dir.y * (5.0f * dirxxxx - 10.0f * dirxyxy + diryyyy); + out[26] = 0.75f * (float)sqrt(385.0f / DX_PI) * dirxy * dir.z * (dirxx - diryy); + out[27] = (float)sqrt(770.0f / DX_PI) / 32.0f * dir.y * (3.0f * dirxx - diryy) * (1.0f - 9.0f * dirzz); + out[28] = (float)sqrt(1155.0f / DX_PI) / 4.0f * dirxy * dir.z * (3.0f * dirzz - 1.0f); + out[29] = (float)sqrt(165.0f / DX_PI) / 16.0f * dir.y * (14.0f * dirzz - 21.0f * dirzzzz - 1.0f); + out[30] = (float)sqrt(11.0f / DX_PI) / 16.0f * dir.z * (63.0f * dirzzzz - 70.0f * dirzz + 15.0f); + out[31] = (float)sqrt(165.0f / DX_PI) / 16.0f * dir.x * (14.0f * dirzz - 21.0f * dirzzzz - 1.0f); + out[32] = (float)sqrt(1155.0f / DX_PI) / 8.0f * dir.z * (dirxx - diryy) * (3.0f * dirzz - 1.0f); + out[33] = (float)sqrt(770.0f / DX_PI) / 32.0f * dir.x * (dirxx - 3.0f * diryy) * (1.0f - 9.0f * dirzz); + out[34] = 3.0f / 16.0f * (float)sqrt(385.0f / DX_PI) * dir.z * (dirxxxx - 6.0f * dirxyxy + diryyyy); + out[35] = -3.0f / 32.0f * (float)sqrt(154.0f / DX_PI) * dir.x * (dirxxxx - 10.0f * dirxyxy + 5.0f * diryyyy); +} +//+------------------------------------------------------------------+ +//| Evaluates a directional light and | +//| returns spectral spherical harmonic (SH) data. | +//+------------------------------------------------------------------+ +//| The output vector is computed so that if the intensity ratio | +//| R/G/B is equal to 1 ,the resulting exit radiance of a point | +//| directly under the light on a diffuse object with an albedo | +//| of 1 would be 1.0. This will compute three spectral samples; | +//| rout[], gout[] and bout[] will be returned. | +//+------------------------------------------------------------------+ +int DXSHEvalDirectionalLight(int order, const DXVector3 &dir, float Rintensity, float Gintensity, float Bintensity, + float &rout[], float &gout[], float &bout[]) { + float s = 0.75f; + if (order > 2) s += 5.0f / 16.0f; + if (order > 4) s -= 3.0f / 32.0f; + s /= DX_PI; + + DXSHEvalDirection(rout, order, dir); + for (int j = 0; j < order * order; j++) { + float temp = rout[j] / s; + rout[j] = Rintensity * temp; + gout[j] = Gintensity * temp; + bout[j] = Bintensity * temp; + } + //--- + return (0); +} +//+------------------------------------------------------------------+ +//| Evaluates a light that is a linear interpolation | +//| between two colors over the sphere. | +//+------------------------------------------------------------------+ +//| The interpolation is done linearly between the two points, | +//| not over the surface of the sphere (that is, if the axis was | +//| (0,0,1) it is linear in Z, not in the azimuthal angle). | +//| The resulting spherical lighting function is normalized so that | +//| a point on a perfectly diffuse surface with no shadowing | +//| and a normal pointed in the direction pDir would result in exit | +//| radiance with a value of 1 (if the top color was white | +//| and the bottom color was black). This is a very simple model | +//| where Top represents the intensity of the "sky" | +//| and Bottom represents the intensity of the "ground". | +//+------------------------------------------------------------------+ +int DXSHEvalHemisphereLight(int order, const DXVector3 &dir, DXColor &top, DXColor &bottom, float &rout[], + float &gout[], float &bout[]) { + float a[2], temp[4]; + DXSHEvalDirection(temp, 2, dir); + //--- rout + a[0] = (top.r + bottom.r) * 3.0f * DX_PI; + a[1] = (top.r - bottom.r) * DX_PI; + for (int i = 0; i < order; i++) + for (int j = 0; j < 2 * i + 1; j++) + if (i < 2) + rout[i * i + j] = temp[i * i + j] * a[i]; + else + rout[i * i + j] = 0.0f; + //--- gout + a[0] = (top.g + bottom.g) * 3.0f * DX_PI; + a[1] = (top.g - bottom.g) * DX_PI; + for (int i = 0; i < order; i++) + for (int j = 0; j < 2 * i + 1; j++) + if (i < 2) + gout[i * i + j] = temp[i * i + j] * a[i]; + else + gout[i * i + j] = 0.0f; + //--- bout + a[0] = (top.b + bottom.b) * 3.0f * DX_PI; + a[1] = (top.b - bottom.b) * DX_PI; + for (int i = 0; i < order; i++) + for (int j = 0; j < 2 * i + 1; j++) + if (i < 2) + bout[i * i + j] = temp[i * i + j] * a[i]; + else + bout[i * i + j] = 0.0f; + //--- + return (0); +} +//+------------------------------------------------------------------+ +//| Evaluates a spherical light and returns | +//| spectral spherical harmonic (SH) data. | +//+------------------------------------------------------------------+ +//| There is no normalization of the intensity of the light like | +//| there is for directional lights, so care has to be taken when | +//| specifying the intensities. | +//| This will compute three spectral samples; | +//| rout[], gout[], bout[] will be returned. | +//+------------------------------------------------------------------+ +int DXSHEvalSphericalLight(int order, const DXVector3 &dir, float radius, float Rintensity, float Gintensity, + float Bintensity, float &rout[], float &gout[], float &bout[]) { + DXVector3 normal; + float cap[6]; + //--- check order + if (order > DXSH_MAXORDER) order = DXSH_MAXORDER; + //--- check radius + if (radius < 0.0f) radius = -radius; + + float dist = DXVec3Length(dir); + float clamped_angle = (dist <= radius) ? DX_PI / 2.0f : (float)asin(radius / dist); + + weightedcapintegrale(cap, order, clamped_angle); + DXVec3Normalize(normal, dir); + DXSHEvalDirection(rout, order, normal); + + for (int i = 0; i < order; i++) + for (int j = 0; j < 2 * i + 1; j++) { + int index = i * i + j; + float temp = rout[index] * cap[i]; + rout[index] = temp * Rintensity; + gout[index] = temp * Gintensity; + bout[index] = temp * Bintensity; + } + //--- + return (0); +} +//+------------------------------------------------------------------+ +//| Computes the product of two functions represented | +//| using Spherical Harmonics (f and g). | +//+------------------------------------------------------------------+ +//| The order is a number between 2 and 6 inclusive. | +//| So it's the same for the several functions: | +//| DXSHMultiply2, DXSHMultiply3, ... DXSHMultiply6. | +//| | +//| Computes the product of two functions represented | +//| using SH (f and g),where | +//| out[i] = int(y_i(s) * f(s) * g(s)), | +//| where | +//| y_i(s) is the ith SH basis function, | +//| f(s) and g(s) are SH functions (sum_i(y_i(s)*c_i)). | +//| The order determines the lengths of the arrays, where there | +//| should always be l^2 coefficients. | +//| | +//| In general the product of two SH functions of order l generates | +//| an SH function of order 2*l - 1, but the results are truncated. | +//| | +//| This means that the product commutes (f*g == g*f) | +//| but doesn't associate (f*(g*h) != (f*g)*h. | +//+------------------------------------------------------------------+ +void DXSHMultiply2(float &out[], const float &a[], const float &b[]) { + float ta = 0.28209479f * a[0]; + float tb = 0.28209479f * b[0]; + out[0] = 0.28209479f * DXSHDot(2, a, b); + out[1] = ta * b[1] + tb * a[1]; + out[2] = ta * b[2] + tb * a[2]; + out[3] = ta * b[3] + tb * a[3]; +} +//+------------------------------------------------------------------+ +//| Computes the product of two functions represented using | +//| Spherical Harmonics (f and g). Both functions are of order N=3. | +//+------------------------------------------------------------------+ +//| The order is a number between 2 and 6 inclusive. | +//| So it's the same for the several functions: | +//| DXSHMultiply2, DXSHMultiply3, ... DXSHMultiply6. | +//| | +//| Computes the product of two functions represented | +//| using SH (f and g),where | +//| out[i] = int(y_i(s) * f(s) * g(s)), | +//| where | +//| y_i(s) is the ith SH basis function, | +//| f(s) and g(s) are SH functions (sum_i(y_i(s)*c_i)). | +//| The order determines the lengths of the arrays,where there | +//| should always be l^2 coefficients. | +//| | +//| In general the product of two SH functions of order l generates | +//| an SH function of order 2*l - 1, but the results are truncated. | +//| | +//| This means that the product commutes (f*g == g*f) | +//| but doesn't associate (f*(g*h) != (f*g)*h. | +//+------------------------------------------------------------------+ +void DXSHMultiply3(float &out[], const float &a[], const float &b[]) { + out[0] = 0.28209479f * a[0] * b[0]; + float ta = 0.28209479f * a[0] - 0.12615663f * a[6] - 0.21850969f * a[8]; + float tb = 0.28209479f * b[0] - 0.12615663f * b[6] - 0.21850969f * b[8]; + out[1] = ta * b[1] + tb * a[1]; + float t = a[1] * b[1]; + out[0] += 0.28209479f * t; + out[6] = -0.12615663f * t; + out[8] = -0.21850969f * t; + + ta = 0.21850969f * a[5]; + tb = 0.21850969f * b[5]; + out[1] += ta * b[2] + tb * a[2]; + out[2] = ta * b[1] + tb * a[1]; + t = a[1] * b[2] + a[2] * b[1]; + out[5] = 0.21850969f * t; + + ta = 0.21850969f * a[4]; + tb = 0.21850969f * b[4]; + out[1] += ta * b[3] + tb * a[3]; + out[3] = ta * b[1] + tb * a[1]; + t = a[1] * b[3] + a[3] * b[1]; + out[4] = 0.21850969f * t; + + ta = 0.28209480f * a[0] + 0.25231326f * a[6]; + tb = 0.28209480f * b[0] + 0.25231326f * b[6]; + out[2] += ta * b[2] + tb * a[2]; + t = a[2] * b[2]; + out[0] += 0.28209480f * t; + out[6] += 0.25231326f * t; + + ta = 0.21850969f * a[7]; + tb = 0.21850969f * b[7]; + out[2] += ta * b[3] + tb * a[3]; + out[3] += ta * b[2] + tb * a[2]; + t = a[2] * b[3] + a[3] * b[2]; + out[7] = 0.21850969f * t; + + ta = 0.28209479f * a[0] - 0.12615663f * a[6] + 0.21850969f * a[8]; + tb = 0.28209479f * b[0] - 0.12615663f * b[6] + 0.21850969f * b[8]; + out[3] += ta * b[3] + tb * a[3]; + t = a[3] * b[3]; + out[0] += 0.28209479f * t; + out[6] -= 0.12615663f * t; + out[8] += 0.21850969f * t; + + ta = 0.28209479f * a[0] - 0.18022375f * a[6]; + tb = 0.28209479f * b[0] - 0.18022375f * b[6]; + out[4] += ta * b[4] + tb * a[4]; + t = a[4] * b[4]; + out[0] += 0.28209479f * t; + out[6] -= 0.18022375f * t; + + ta = 0.15607835f * a[7]; + tb = 0.15607835f * b[7]; + out[4] += ta * b[5] + tb * a[5]; + out[5] += ta * b[4] + tb * a[4]; + t = a[4] * b[5] + a[5] * b[4]; + out[7] += 0.15607835f * t; + + ta = 0.28209479f * a[0] + 0.09011188f * a[6] - 0.15607835f * a[8]; + tb = 0.28209479f * b[0] + 0.09011188f * b[6] - 0.15607835f * b[8]; + out[5] += ta * b[5] + tb * a[5]; + t = a[5] * b[5]; + out[0] += 0.28209479f * t; + out[6] += 0.09011188f * t; + out[8] -= 0.15607835f * t; + + ta = 0.28209480f * a[0]; + tb = 0.28209480f * b[0]; + out[6] += ta * b[6] + tb * a[6]; + t = a[6] * b[6]; + out[0] += 0.28209480f * t; + out[6] += 0.18022376f * t; + + ta = 0.28209479f * a[0] + 0.09011188f * a[6] + 0.15607835f * a[8]; + tb = 0.28209479f * b[0] + 0.09011188f * b[6] + 0.15607835f * b[8]; + out[7] += ta * b[7] + tb * a[7]; + t = a[7] * b[7]; + out[0] += 0.28209479f * t; + out[6] += 0.09011188f * t; + out[8] += 0.15607835f * t; + + ta = 0.28209479f * a[0] - 0.18022375f * a[6]; + tb = 0.28209479f * b[0] - 0.18022375f * b[6]; + out[8] += ta * b[8] + tb * a[8]; + t = a[8] * b[8]; + out[0] += 0.28209479f * t; + out[6] -= 0.18022375f * t; +} +//+------------------------------------------------------------------+ +//| Computes the product of two functions represented using | +//| Spherical Harmonics (f and g). Both functions are of order N=4. | +//+------------------------------------------------------------------+ +//| The order is a number between 2 and 6 inclusive. | +//| So it's the same for the several functions: | +//| DXSHMultiply2, DXSHMultiply3, ... DXSHMultiply6. | +//| | +//| Computes the product of two functions represented | +//| using SH (f and g), where | +//| out[i] = int(y_i(s) * f(s) * g(s)), | +//| where | +//| y_i(s) is the ith SH basis function, | +//| f(s) and g(s) are SH functions (sum_i(y_i(s)*c_i)). | +//| The order determines the lengths of the arrays,where there | +//| should always be l^2 coefficients. | +//| | +//| In general the product of two SH functions of order l generates | +//| an SH function of order 2*l - 1, but the results are truncated. | +//| | +//| This means that the product commutes (f*g == g*f) | +//| but doesn't associate (f*(g*h) != (f*g)*h. | +//+------------------------------------------------------------------+ +void DXSHMultiply4(float &out[], const float &a[], const float &b[]) { + out[0] = 0.28209479f * a[0] * b[0]; + float ta = 0.28209479f * a[0] - 0.12615663f * a[6] - 0.21850969f * a[8]; + float tb = 0.28209479f * b[0] - 0.12615663f * b[6] - 0.21850969f * b[8]; + out[1] = ta * b[1] + tb * a[1]; + float t = a[1] * b[1]; + out[0] += 0.28209479f * t; + out[6] = -0.12615663f * t; + out[8] = -0.21850969f * t; + + ta = 0.21850969f * a[3] - 0.05839917f * a[13] - 0.22617901f * a[15]; + tb = 0.21850969f * b[3] - 0.05839917f * b[13] - 0.22617901f * b[15]; + out[1] += ta * b[4] + tb * a[4]; + out[4] = ta * b[1] + tb * a[1]; + t = a[1] * b[4] + a[4] * b[1]; + out[3] = 0.21850969f * t; + out[13] = -0.05839917f * t; + out[15] = -0.22617901f * t; + + ta = 0.21850969f * a[2] - 0.14304817f * a[12] - 0.18467439f * a[14]; + tb = 0.21850969f * b[2] - 0.14304817f * b[12] - 0.18467439f * b[14]; + out[1] += ta * b[5] + tb * a[5]; + out[5] = ta * b[1] + tb * a[1]; + t = a[1] * b[5] + a[5] * b[1]; + out[2] = 0.21850969f * t; + out[12] = -0.14304817f * t; + out[14] = -0.18467439f * t; + + ta = 0.20230066f * a[11]; + tb = 0.20230066f * b[11]; + out[1] += ta * b[6] + tb * a[6]; + out[6] += ta * b[1] + tb * a[1]; + t = a[1] * b[6] + a[6] * b[1]; + out[11] = 0.20230066f * t; + + ta = 0.22617901f * a[9] + 0.05839917f * a[11]; + tb = 0.22617901f * b[9] + 0.05839917f * b[11]; + out[1] += ta * b[8] + tb * a[8]; + out[8] += ta * b[1] + tb * a[1]; + t = a[1] * b[8] + a[8] * b[1]; + out[9] = 0.22617901f * t; + out[11] += 0.05839917f * t; + + ta = 0.28209480f * a[0] + 0.25231326f * a[6]; + tb = 0.28209480f * b[0] + 0.25231326f * b[6]; + out[2] += ta * b[2] + tb * a[2]; + t = a[2] * b[2]; + out[0] += 0.28209480f * t; + out[6] += 0.25231326f * t; + + ta = 0.24776671f * a[12]; + tb = 0.24776671f * b[12]; + out[2] += ta * b[6] + tb * a[6]; + out[6] += ta * b[2] + tb * a[2]; + t = a[2] * b[6] + a[6] * b[2]; + out[12] += 0.24776671f * t; + + ta = 0.28209480f * a[0] - 0.12615663f * a[6] + 0.21850969f * a[8]; + tb = 0.28209480f * b[0] - 0.12615663f * b[6] + 0.21850969f * b[8]; + out[3] += ta * b[3] + tb * a[3]; + t = a[3] * b[3]; + out[0] += 0.28209480f * t; + out[6] -= 0.12615663f * t; + out[8] += 0.21850969f * t; + + ta = 0.20230066f * a[13]; + tb = 0.20230066f * b[13]; + out[3] += ta * b[6] + tb * a[6]; + out[6] += ta * b[3] + tb * a[3]; + t = a[3] * b[6] + a[6] * b[3]; + out[13] += 0.20230066f * t; + + ta = 0.21850969f * a[2] - 0.14304817f * a[12] + 0.18467439f * a[14]; + tb = 0.21850969f * b[2] - 0.14304817f * b[12] + 0.18467439f * b[14]; + out[3] += ta * b[7] + tb * a[7]; + out[7] = ta * b[3] + tb * a[3]; + t = a[3] * b[7] + a[7] * b[3]; + out[2] += 0.21850969f * t; + out[12] -= 0.14304817f * t; + out[14] += 0.18467439f * t; + + ta = -0.05839917f * a[13] + 0.22617901f * a[15]; + tb = -0.05839917f * b[13] + 0.22617901f * b[15]; + out[3] += ta * b[8] + tb * a[8]; + out[8] += ta * b[3] + tb * a[3]; + t = a[3] * b[8] + a[8] * b[3]; + out[13] -= 0.05839917f * t; + out[15] += 0.22617901f * t; + + ta = 0.28209479f * a[0] - 0.18022375f * a[6]; + tb = 0.28209479f * b[0] - 0.18022375f * b[6]; + out[4] += ta * b[4] + tb * a[4]; + t = a[4] * b[4]; + out[0] += 0.28209479f * t; + out[6] -= 0.18022375f * t; + + ta = 0.15607835f * a[7]; + tb = 0.15607835f * b[7]; + out[4] += ta * b[5] + tb * a[5]; + out[5] += ta * b[4] + tb * a[4]; + t = a[4] * b[5] + a[5] * b[4]; + out[7] += 0.15607835f * t; + + ta = 0.22617901f * a[3] - 0.09403160f * a[13]; + tb = 0.22617901f * b[3] - 0.09403160f * b[13]; + out[4] += ta * b[9] + tb * a[9]; + out[9] += ta * b[4] + tb * a[4]; + t = a[4] * b[9] + a[9] * b[4]; + out[3] += 0.22617901f * t; + out[13] -= 0.09403160f * t; + + ta = 0.18467439f * a[2] - 0.18806319f * a[12]; + tb = 0.18467439f * b[2] - 0.18806319f * b[12]; + out[4] += ta * b[10] + tb * a[10]; + out[10] = ta * b[4] + tb * a[4]; + t = a[4] * b[10] + a[10] * b[4]; + out[2] += 0.18467439f * t; + out[12] -= 0.18806319f * t; + + ta = -0.05839917f * a[3] + 0.14567312f * a[13] + 0.09403160f * a[15]; + tb = -0.05839917f * b[3] + 0.14567312f * b[13] + 0.09403160f * b[15]; + out[4] += ta * b[11] + tb * a[11]; + out[11] += ta * b[4] + tb * a[4]; + t = a[4] * b[11] + a[11] * b[4]; + out[3] -= 0.05839917f * t; + out[13] += 0.14567312f * t; + out[15] += 0.09403160f * t; + + ta = 0.28209479f * a[0] + 0.09011186f * a[6] - 0.15607835f * a[8]; + tb = 0.28209479f * b[0] + 0.09011186f * b[6] - 0.15607835f * b[8]; + out[5] += ta * b[5] + tb * a[5]; + t = a[5] * b[5]; + out[0] += 0.28209479f * t; + out[6] += 0.09011186f * t; + out[8] -= 0.15607835f * t; + + ta = 0.14867701f * a[14]; + tb = 0.14867701f * b[14]; + out[5] += ta * b[9] + tb * a[9]; + out[9] += ta * b[5] + tb * a[5]; + t = a[5] * b[9] + a[9] * b[5]; + out[14] += 0.14867701f * t; + + ta = 0.18467439f * a[3] + 0.11516472f * a[13] - 0.14867701f * a[15]; + tb = 0.18467439f * b[3] + 0.11516472f * b[13] - 0.14867701f * b[15]; + out[5] += ta * b[10] + tb * a[10]; + out[10] += ta * b[5] + tb * a[5]; + t = a[5] * b[10] + a[10] * b[5]; + out[3] += 0.18467439f * t; + out[13] += 0.11516472f * t; + out[15] -= 0.14867701f * t; + + ta = 0.23359668f * a[2] + 0.05947080f * a[12] - 0.11516472f * a[14]; + tb = 0.23359668f * b[2] + 0.05947080f * b[12] - 0.11516472f * b[14]; + out[5] += ta * b[11] + tb * a[11]; + out[11] += ta * b[5] + tb * a[5]; + t = a[5] * b[11] + a[11] * b[5]; + out[2] += 0.23359668f * t; + out[12] += 0.05947080f * t; + out[14] -= 0.11516472f * t; + + ta = 0.28209479f * a[0]; + tb = 0.28209479f * b[0]; + out[6] += ta * b[6] + tb * a[6]; + t = a[6] * b[6]; + out[0] += 0.28209479f * t; + out[6] += 0.18022376f * t; + + ta = 0.09011186f * a[6] + 0.28209479f * a[0] + 0.15607835f * a[8]; + tb = 0.09011186f * b[6] + 0.28209479f * b[0] + 0.15607835f * b[8]; + out[7] += ta * b[7] + tb * a[7]; + t = a[7] * b[7]; + out[6] += 0.09011186f * t; + out[0] += 0.28209479f * t; + out[8] += 0.15607835f * t; + + ta = 0.14867701f * a[9] + 0.18467439f * a[1] + 0.11516472f * a[11]; + tb = 0.14867701f * b[9] + 0.18467439f * b[1] + 0.11516472f * b[11]; + out[7] += ta * b[10] + tb * a[10]; + out[10] += ta * b[7] + tb * a[7]; + t = a[7] * b[10] + a[10] * b[7]; + out[9] += 0.14867701f * t; + out[1] += 0.18467439f * t; + out[11] += 0.11516472f * t; + + ta = 0.05947080f * a[12] + 0.23359668f * a[2] + 0.11516472f * a[14]; + tb = 0.05947080f * b[12] + 0.23359668f * b[2] + 0.11516472f * b[14]; + out[7] += ta * b[13] + tb * a[13]; + out[13] += ta * b[7] + tb * a[7]; + t = a[7] * b[13] + a[13] * b[7]; + out[12] += 0.05947080f * t; + out[2] += 0.23359668f * t; + out[14] += 0.11516472f * t; + + ta = 0.14867701f * a[15]; + tb = 0.14867701f * b[15]; + out[7] += ta * b[14] + tb * a[14]; + out[14] += ta * b[7] + tb * a[7]; + t = a[7] * b[14] + a[14] * b[7]; + out[15] += 0.14867701f * t; + + ta = 0.28209479f * a[0] - 0.18022375f * a[6]; + tb = 0.28209479f * b[0] - 0.18022375f * b[6]; + out[8] += ta * b[8] + tb * a[8]; + t = a[8] * b[8]; + out[0] += 0.28209479f * t; + out[6] -= 0.18022375f * t; + + ta = -0.09403160f * a[11]; + tb = -0.09403160f * b[11]; + out[8] += ta * b[9] + tb * a[9]; + out[9] += ta * b[8] + tb * a[8]; + t = a[8] * b[9] + a[9] * b[8]; + out[11] -= 0.09403160f * t; + + ta = -0.09403160f * a[15]; + tb = -0.09403160f * b[15]; + out[8] += ta * b[13] + tb * a[13]; + out[13] += ta * b[8] + tb * a[8]; + t = a[8] * b[13] + a[13] * b[8]; + out[15] -= 0.09403160f * t; + + ta = 0.18467439f * a[2] - 0.18806319f * a[12]; + tb = 0.18467439f * b[2] - 0.18806319f * b[12]; + out[8] += ta * b[14] + tb * a[14]; + out[14] += ta * b[8] + tb * a[8]; + t = a[8] * b[14] + a[14] * b[8]; + out[2] += 0.18467439f * t; + out[12] -= 0.18806319f * t; + + ta = -0.21026104f * a[6] + 0.28209479f * a[0]; + tb = -0.21026104f * b[6] + 0.28209479f * b[0]; + out[9] += ta * b[9] + tb * a[9]; + t = a[9] * b[9]; + out[6] -= 0.21026104f * t; + out[0] += 0.28209479f * t; + + ta = 0.28209479f * a[0]; + tb = 0.28209479f * b[0]; + out[10] += ta * b[10] + tb * a[10]; + t = a[10] * b[10]; + out[0] += 0.28209479f * t; + + ta = 0.28209479f * a[0] + 0.12615663f * a[6] - 0.14567312f * a[8]; + tb = 0.28209479f * b[0] + 0.12615663f * b[6] - 0.14567312f * b[8]; + out[11] += ta * b[11] + tb * a[11]; + t = a[11] * b[11]; + out[0] += 0.28209479f * t; + out[6] += 0.12615663f * t; + out[8] -= 0.14567312f * t; + + ta = 0.28209479f * a[0] + 0.16820885f * a[6]; + tb = 0.28209479f * b[0] + 0.16820885f * b[6]; + out[12] += ta * b[12] + tb * a[12]; + t = a[12] * b[12]; + out[0] += 0.28209479f * t; + out[6] += 0.16820885f * t; + + ta = 0.28209479f * a[0] + 0.14567312f * a[8] + 0.12615663f * a[6]; + tb = 0.28209479f * b[0] + 0.14567312f * b[8] + 0.12615663f * b[6]; + out[13] += ta * b[13] + tb * a[13]; + t = a[13] * b[13]; + out[0] += 0.28209479f * t; + out[8] += 0.14567312f * t; + out[6] += 0.12615663f * t; + + ta = 0.28209479f * a[0]; + tb = 0.28209479f * b[0]; + out[14] += ta * b[14] + tb * a[14]; + t = a[14] * b[14]; + out[0] += 0.28209479f * t; + + ta = 0.28209479f * a[0] - 0.21026104f * a[6]; + tb = 0.28209479f * b[0] - 0.21026104f * b[6]; + out[15] += ta * b[15] + tb * a[15]; + t = a[15] * b[15]; + out[0] += 0.28209479f * t; + out[6] -= 0.21026104f * t; +} +//+------------------------------------------------------------------+ +//| rotate_X | +//+------------------------------------------------------------------+ +void rotate_X(float &out[], uint order, float a, float &in[]) { + out[0] = in[0]; + out[1] = a * in[2]; + out[2] = -a * in[1]; + out[3] = in[3]; + out[4] = a * in[7]; + out[5] = -in[5]; + out[6] = -0.5f * in[6] - 0.8660253882f * in[8]; + out[7] = -a * in[4]; + out[8] = -0.8660253882f * in[6] + 0.5f * in[8]; + out[9] = -a * 0.7905694842f * in[12] + a * 0.6123724580f * in[14]; + out[10] = -in[10]; + out[11] = -a * 0.6123724580f * in[12] - a * 0.7905694842f * in[14]; + out[12] = a * 0.7905694842f * in[9] + a * 0.6123724580f * in[11]; + out[13] = -0.25f * in[13] - 0.9682458639f * in[15]; + out[14] = -a * 0.6123724580f * in[9] + a * 0.7905694842f * in[11]; + out[15] = -0.9682458639f * in[13] + 0.25f * in[15]; + if (order == 4) return; + + out[16] = -a * 0.9354143739f * in[21] + a * 0.3535533845f * in[23]; + out[17] = -0.75f * in[17] + 0.6614378095f * in[19]; + out[18] = -a * 0.3535533845f * in[21] - a * 0.9354143739f * in[23]; + out[19] = 0.6614378095f * in[17] + 0.75f * in[19]; + out[20] = 0.375f * in[20] + 0.5590170026f * in[22] + 0.7395099998f * in[24]; + out[21] = a * 0.9354143739f * in[16] + a * 0.3535533845f * in[18]; + out[22] = 0.5590170026f * in[20] + 0.5f * in[22] - 0.6614378691f * in[24]; + out[23] = -a * 0.3535533845f * in[16] + a * 0.9354143739f * in[18]; + out[24] = 0.7395099998f * in[20] - 0.6614378691f * in[22] + 0.125f * in[24]; + if (order == 5) return; + + out[25] = a * 0.7015607357f * in[30] - a * 0.6846531630f * in[32] + a * 0.1976423711f * in[34]; + out[26] = -0.5f * in[26] + 0.8660253882f * in[28]; + out[27] = a * 0.5229125023f * in[30] + a * 0.3061861992f * in[32] - a * 0.7954951525f * in[34]; + out[28] = 0.8660253882f * in[26] + 0.5f * in[28]; + out[29] = a * 0.4841229022f * in[30] + a * 0.6614378691f * in[32] + a * 0.5728219748f * in[34]; + out[30] = -a * 0.7015607357f * in[25] - a * 0.5229125023f * in[27] - a * 0.4841229022f * in[29]; + out[31] = 0.125f * in[31] + 0.4050463140f * in[33] + 0.9057110548f * in[35]; + out[32] = a * 0.6846531630f * in[25] - a * 0.3061861992f * in[27] - a * 0.6614378691f * in[29]; + out[33] = 0.4050463140f * in[31] + 0.8125f * in[33] - 0.4192627370f * in[35]; + out[34] = -a * 0.1976423711f * in[25] + a * 0.7954951525f * in[27] - a * 0.5728219748f * in[29]; + out[35] = 0.9057110548f * in[31] - 0.4192627370f * in[33] + 0.0624999329f * in[35]; +} +//+------------------------------------------------------------------+ +//| Rotates the spherical harmonic (SH) vector by the given _matrix. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) | +//| is stored at memory location l^2 + m + l, where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l, inclusive. | +//+------------------------------------------------------------------+ +void DXSHRotate(float &out[], int order, const DXMatrix &_matrix, const float &in[]) { + float alpha, beta, gamma, sinb, temp[36], temp1[36]; + out[0] = in[0]; + + if ((order > DXSH_MAXORDER) || (order < DXSH_MINORDER)) return; + + if (order <= 3) { + out[1] = _matrix.m[1][1] * in[1] - _matrix.m[2][1] * in[2] + _matrix.m[0][1] * in[3]; + out[2] = -_matrix.m[1][2] * in[1] + _matrix.m[2][2] * in[2] - _matrix.m[0][2] * in[3]; + out[3] = _matrix.m[1][0] * in[1] - _matrix.m[2][0] * in[2] + _matrix.m[0][0] * in[3]; + + if (order == 3) { + float coeff[12] = {}; + coeff[0] = _matrix.m[1][0] * _matrix.m[0][0]; + coeff[1] = _matrix.m[1][1] * _matrix.m[0][1]; + coeff[2] = _matrix.m[1][1] * _matrix.m[2][1]; + coeff[3] = _matrix.m[1][0] * _matrix.m[2][0]; + coeff[4] = _matrix.m[2][0] * _matrix.m[2][0]; + coeff[5] = _matrix.m[2][1] * _matrix.m[2][1]; + coeff[6] = _matrix.m[0][0] * _matrix.m[2][0]; + coeff[7] = _matrix.m[0][1] * _matrix.m[2][1]; + coeff[8] = _matrix.m[0][1] * _matrix.m[0][1]; + coeff[9] = _matrix.m[1][0] * _matrix.m[1][0]; + coeff[10] = _matrix.m[1][1] * _matrix.m[1][1]; + coeff[11] = _matrix.m[0][0] * _matrix.m[0][0]; + + out[4] = (_matrix.m[1][1] * _matrix.m[0][0] + _matrix.m[0][1] * _matrix.m[1][0]) * in[4]; + out[4] -= (_matrix.m[1][0] * _matrix.m[2][1] + _matrix.m[1][1] * _matrix.m[2][0]) * in[5]; + out[4] += 1.7320508076f * _matrix.m[2][0] * _matrix.m[2][1] * in[6]; + out[4] -= (_matrix.m[0][1] * _matrix.m[2][0] + _matrix.m[0][0] * _matrix.m[2][1]) * in[7]; + out[4] += (_matrix.m[0][0] * _matrix.m[0][1] - _matrix.m[1][0] * _matrix.m[1][1]) * in[8]; + + out[5] = (_matrix.m[1][1] * _matrix.m[2][2] + _matrix.m[1][2] * _matrix.m[2][1]) * in[5]; + out[5] -= (_matrix.m[1][1] * _matrix.m[0][2] + _matrix.m[1][2] * _matrix.m[0][1]) * in[4]; + out[5] -= 1.7320508076f * _matrix.m[2][2] * _matrix.m[2][1] * in[6]; + out[5] += (_matrix.m[0][2] * _matrix.m[2][1] + _matrix.m[0][1] * _matrix.m[2][2]) * in[7]; + out[5] -= (_matrix.m[0][1] * _matrix.m[0][2] - _matrix.m[1][1] * _matrix.m[1][2]) * in[8]; + + out[6] = (_matrix.m[2][2] * _matrix.m[2][2] - 0.5f * (coeff[4] + coeff[5])) * in[6]; + out[6] -= (0.5773502692f * (coeff[0] + coeff[1]) - 1.1547005384f * _matrix.m[1][2] * _matrix.m[0][2]) * in[4]; + out[6] += (0.5773502692f * (coeff[2] + coeff[3]) - 1.1547005384f * _matrix.m[1][2] * _matrix.m[2][2]) * in[5]; + out[6] += (0.5773502692f * (coeff[6] + coeff[7]) - 1.1547005384f * _matrix.m[0][2] * _matrix.m[2][2]) * in[7]; + out[6] += (0.2886751347f * (coeff[9] - coeff[8] + coeff[10] - coeff[11]) - + 0.5773502692f * (_matrix.m[1][2] * _matrix.m[1][2] - _matrix.m[0][2] * _matrix.m[0][2])) * + in[8]; + + out[7] = (_matrix.m[0][0] * _matrix.m[2][2] + _matrix.m[0][2] * _matrix.m[2][0]) * in[7]; + out[7] -= (_matrix.m[1][0] * _matrix.m[0][2] + _matrix.m[1][2] * _matrix.m[0][0]) * in[4]; + out[7] += (_matrix.m[1][0] * _matrix.m[2][2] + _matrix.m[1][2] * _matrix.m[2][0]) * in[5]; + out[7] -= 1.7320508076f * _matrix.m[2][2] * _matrix.m[2][0] * in[6]; + out[7] -= (_matrix.m[0][0] * _matrix.m[0][2] - _matrix.m[1][0] * _matrix.m[1][2]) * in[8]; + + out[8] = 0.5f * (coeff[11] - coeff[8] - coeff[9] + coeff[10]) * in[8]; + out[8] += (coeff[0] - coeff[1]) * in[4]; + out[8] += (coeff[2] - coeff[3]) * in[5]; + out[8] += 0.86602540f * (coeff[4] - coeff[5]) * in[6]; + out[8] += (coeff[7] - coeff[6]) * in[7]; + } + return; + } + +#ifdef __MQL5__ + if ((float)fabs(_matrix.m[2][2]) != 1.0f) { + sinb = (float)sqrt(1.0f - _matrix.m[2][2] * _matrix.m[2][2]); + alpha = (float)atan2(_matrix.m[2][1] / sinb, _matrix.m[2][0] / sinb); + beta = (float)atan2(sinb, _matrix.m[2][2]); + gamma = (float)atan2(_matrix.m[1][2] / sinb, -_matrix.m[0][2] / sinb); + } else { + alpha = (float)atan2(_matrix.m[0][1], _matrix.m[0][0]); + beta = 0.0f; + gamma = 0.0f; + } +#else + alpha = 0.0f; + beta = 0.0f; + gamma = 0.0f; + sinb = 0.0f; +#endif + + //--- + DXSHRotateZ(temp, order, gamma, in); + rotate_X(temp1, order, 1.0f, temp); + DXSHRotateZ(temp, order, beta, temp1); + rotate_X(temp1, order, -1.0f, temp); + DXSHRotateZ(out, order, alpha, temp1); +} +//+------------------------------------------------------------------+ +//| Rotates the spherical harmonic (SH) vector | +//| in the z-axis by the given angle. | +//+------------------------------------------------------------------+ +//| Each coefficient of the basis function Y(l,m) | +//| is stored at memory location l^2 + m + l, where: | +//| l is the degree of the basis function. | +//| m is the basis function index for the given l value | +//| and ranges from -l to l, inclusive. | +//+------------------------------------------------------------------+ +void DXSHRotateZ(float &out[], int order, float angle, const float &in[]) { + int sum = 0; + float c[5], s[5]; + order = (int)fmin(fmax(order, DXSH_MINORDER), DXSH_MAXORDER); + out[0] = in[0]; + //--- + for (int i = 1; i < order; i++) { + c[i - 1] = (float)cos(i * angle); + s[i - 1] = (float)sin(i * angle); + sum += i * 2; + //--- + out[sum - i] = c[i - 1] * in[sum - i]; + out[sum - i] += s[i - 1] * in[sum + i]; + for (int j = i - 1; j > 0; j--) { + out[sum - j] = 0.0f; + out[sum - j] = c[j - 1] * in[sum - j]; + out[sum - j] += s[j - 1] * in[sum + j]; + } + out[sum] = in[sum]; + //--- + for (int j = 1; j < i; j++) { + out[sum + j] = 0.0f; + out[sum + j] = -s[j - 1] * in[sum - j]; + out[sum + j] += c[j - 1] * in[sum + j]; + } + out[sum + i] = -s[i - 1] * in[sum - i]; + out[sum + i] += c[i - 1] * in[sum + i]; + } +} +//+------------------------------------------------------------------+ +//| Scales a spherical harmonic (SH) vector; | +//| in other words, out[i] = a[i]*scale. | +//+------------------------------------------------------------------+ +void DXSHScale(float &out[], int order, const float &a[], const float scale) { + for (int i = 0; i < order * order; i++) out[i] = a[i] * scale; +} +//+---------------------------------------------------------------------+ +//| Interpolates y0 to y1 according to value from 0.0 to 1.0 | +//+---------------------------------------------------------------------+ +float DXScalarLerp(const float val1, const float val2, float s) { return ((1 - s) * val1 + s * val2); } +//+---------------------------------------------------------------------+ +//| Interpolates y0 to y1 according to value from 0.0 to 1.0 | +//+---------------------------------------------------------------------+ +float DXScalarBiasScale(const float val, const float bias, const float scale) { return ((val + bias) * scale); } +//+------------------------------------------------------------------+ diff --git a/3D/Mesh.h b/3D/Mesh.h new file mode 100644 index 000000000..0ee82c0dc --- /dev/null +++ b/3D/Mesh.h @@ -0,0 +1,256 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics mesh. + */ + +#include "../Dict.mqh" +#include "../Refs.mqh" +#include "../Util.h" +#include "Face.h" +#include "IndexBuffer.h" +#include "Material.h" +#include "Math.h" +#include "TSR.h" +#include "VertexBuffer.h" + +class Device; + +#define GFX_MESH_LOOKUP_PRECISION 0.001f + +// Point with a key for faster vertices' lookup by their position. +template +struct PointEntry { + T point; + long key; + + PointEntry() {} + + PointEntry(const T& _point) { + point = _point; + key = MakeKey(_point.Position.x, _point.Position.y, _point.Position.z); + } + + bool operator==(const PointEntry& _r) { + return key == MakeKey(_r.point.Position.x, _r.point.Position.y, _r.point.Position.z); + } + + static long MakeKey(float x, float y, float z) { + return long(x / GFX_MESH_LOOKUP_PRECISION) + 4194304 * long(y / GFX_MESH_LOOKUP_PRECISION) + + 17592186044416 * long(z / GFX_MESH_LOOKUP_PRECISION); + } +}; + +// Mesh points type. +enum ENUM_MESH_TYPE { MESH_TYPE_CONNECTED_POINTS, MESH_TYPE_SEPARATE_POINTS }; + +template +class Mesh : public Dynamic { + Ref vbuff; + Ref ibuff; + Ref shader_ps; + Ref shader_vs; + Face faces[]; + TSR tsr; + ENUM_MESH_TYPE type; + bool initialized; + Material material; + + public: + /** + * Constructor. + */ + Mesh(ENUM_MESH_TYPE _type = MESH_TYPE_SEPARATE_POINTS) { + type = _type; + initialized = false; + } + + /** + * Initializes graphics device-related things. + */ + virtual void Initialize(Device* _device) {} + + TSR* GetTSR() { return &tsr; } + + void SetTSR(const TSR& _tsr) { tsr = _tsr; } + + /** + * Adds a single 3 or 4-vertex face. + */ + void AddFace(Face& face) { + face.UpdateNormal(); + Util::ArrayPush(faces, face, 16); + } + + /** + * Returns material assigned to mesh. + */ + Material* GetMaterial() { return &material; } + + /** + * Assigns material to mesh. + */ + void SetMaterial(Material& _material) { material = _material; } + + /** + * Returns vertex shader for mesh rendering. + */ + Shader* GetShaderVS() { return shader_vs.Ptr(); } + + /** + * Sets pixel shader for mesh rendering. + */ + void SetShaderVS(Shader* _shader_vs) { shader_vs = _shader_vs; } + + /** + * Returns pixel shader for mesh rendering. + */ + Shader* GetShaderPS() { return shader_ps.Ptr(); } + + /** + * Sets pixel shader for mesh rendering. + */ + void SetShaderPS(Shader* _shader_ps) { shader_ps = _shader_ps; } + + /** + * Returns vertex and index buffers for this mesh. + * + * @todo Buffers should be invalidated if mesh has changed. + */ + bool GetBuffers(Device* _device, VertexBuffer*& _vbuff, IndexBuffer*& _ibuff) { + if (!initialized) { + Initialize(_device); + initialized = true; + } + +#ifdef __debug__ + Print("Getting buffers. Mesh type = ", EnumToString(type)); +#endif + if (vbuff.IsSet() && ibuff.IsSet()) { + _vbuff = vbuff.Ptr(); + _ibuff = ibuff.Ptr(); + return true; + } + + DictStruct> _points; + T _vertices[]; + unsigned int _indices[]; + int i, k; + + for (i = 0; i < ArraySize(faces); ++i) { + Face _face = faces[i]; + int _face_indices[4]; + + // Adding first triangle. + for (k = 0; k < 3; ++k) { + PointEntry _point1(_face.points[k]); + _face_indices[k] = type == MESH_TYPE_SEPARATE_POINTS ? -1 : _points.IndexOf(_point1); + + if (_face_indices[k] == -1) { + // Point not yet added. + _points.Push(_point1); + _face_indices[k] = (int)_points.Size() - 1; + } + + Util::ArrayPush(_indices, _face_indices[k]); + } + + // Adding second triangle if needed. + if ((_face.flags & FACE_FLAGS_QUAD) == FACE_FLAGS_QUAD) { + if (type == MESH_TYPE_CONNECTED_POINTS) { + PointEntry _point3(_face.points[3]); + _face_indices[3] = _points.IndexOf(_point3); + + if (_face_indices[3] == -1) { + // Point not yet added. + _points.Push(_point3); + _face_indices[3] = (int)_points.Size() - 1; + } + + Util::ArrayPush(_indices, _face_indices[0]); + Util::ArrayPush(_indices, _face_indices[2]); + Util::ArrayPush(_indices, _face_indices[3]); + } else { + int _i1 = ArraySize(_indices) + 0; + int _i2 = ArraySize(_indices) + 1; + int _i3 = ArraySize(_indices) + 2; + + Util::ArrayPush(_indices, _i1); + Util::ArrayPush(_indices, _i2); + Util::ArrayPush(_indices, _i3); + + PointEntry _point0(_face.points[0]); + PointEntry _point2(_face.points[2]); + PointEntry _point3(_face.points[3]); + + _points.Push(_point0); + _points.Push(_point2); + _points.Push(_point3); + } + } + } + + ArrayResize(_vertices, _points.Size()); + + for (DictIteratorBase> iter(_points.Begin()); iter.IsValid(); ++iter) { + _vertices[iter.Index()] = iter.Value().point; + } + + string _s_vertices = "["; + + for (i = 0; i < ArraySize(_vertices); ++i) { + _s_vertices += "["; + _s_vertices += " Pos = " + DoubleToString(_vertices[i].Position.x) + ", " + + DoubleToString(_vertices[i].Position.y) + "," + DoubleToString(_vertices[i].Position.z) + " | "; + _s_vertices += " Clr = " + DoubleToString(_vertices[i].Color.r) + ", " + DoubleToString(_vertices[i].Color.g) + + "," + DoubleToString(_vertices[i].Color.b) + "," + DoubleToString(_vertices[i].Color.a); + _s_vertices += "]"; + if (i != ArraySize(_vertices) - 1) { + _s_vertices += ", "; + } + } + + _s_vertices += "]"; + + string _s_indices = "["; + + for (i = 0; i < ArraySize(_indices); ++i) { + _s_indices += DoubleToString(_indices[i]); + if (i != ArraySize(_indices) - 1) { + _s_indices += ", "; + } + } + + _s_indices += "]"; + +#ifdef __debug__ + Print("Vertices: ", _s_vertices); + Print("Indices: ", _s_indices); +#endif + + vbuff = _vbuff = _device.VertexBuffer(_vertices); + ibuff = _ibuff = _device.IndexBuffer(_indices); + return true; + } +}; diff --git a/3D/Shader.h b/3D/Shader.h new file mode 100644 index 000000000..51ae50504 --- /dev/null +++ b/3D/Shader.h @@ -0,0 +1,108 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics shader. + */ + +#include "../Refs.mqh" + +// Shader type. +enum ENUM_SHADER_TYPE { + SHADER_TYPE_VS, + SHADER_TYPE_PS, +}; + +class Device; +class MTDXShader; + +enum ENUM_GFX_VAR_TYPE_FLOAT { GFX_VAR_TYPE_INT32, GFX_VAR_TYPE_FLOAT }; + +// Note that shader buffers's size must be multiple of 4! +struct MVPBuffer { + DXMatrix world; + DXMatrix view; + DXMatrix projection; + DXVector3 lightdir; + + private: + float _unused1; + + public: + DXColor mat_color; + + private: + // char _unused2[1]; +}; + +// Vertex layout used for Vertex Shaders. +struct ShaderVertexLayout { + string name; + unsigned int index; + ENUM_GFX_VAR_TYPE_FLOAT type; + unsigned int num_components; + bool clamped; + unsigned int stride; + unsigned int offset; +}; + +/** + * Unified vertex/pixel shader. + */ +class Shader : public Dynamic { + // Reference to graphics device. + WeakRef device; + + public: + /** + * Constructor. + */ + Shader(Device* _device) { device = _device; } + + /** + * Returns base graphics device. + */ + Device* GetDevice() { return device.Ptr(); } + + /** + * Sets custom input buffer for shader. + */ + template + void SetCBuffer(const X& data) { + // Unfortunately we can't make this method virtual. + if (dynamic_cast(&this) != NULL) { +// MT5's DirectX. +#ifdef __debug__ + Print("Setting CBuffer data for MT5"); +#endif + ((MTDXShader*)&this).SetCBuffer(data); + } else { + Alert("Unsupported cbuffer device target"); + } + } + + /** + * Selectes shader to be used by graphics device for rendering. + */ + virtual void Select() = NULL; +}; diff --git a/3D/Shaders/chart3d_ps.hlsl b/3D/Shaders/chart3d_ps.hlsl new file mode 100644 index 000000000..e69de29bb diff --git a/3D/Shaders/chart3d_vs.hlsl b/3D/Shaders/chart3d_vs.hlsl new file mode 100644 index 000000000..e69de29bb diff --git a/3D/Shaders/cube_ps.hlsl b/3D/Shaders/cube_ps.hlsl new file mode 100644 index 000000000..6218a4748 --- /dev/null +++ b/3D/Shaders/cube_ps.hlsl @@ -0,0 +1,17 @@ +struct INPUT { + float4 position : SV_POSITION; + float4 normal : TEXCOORD0; + float3 lightdir : TEXCOORD1; + float4 color : COLOR; +}; + +float4 main(INPUT input) : SV_TARGET { + float4 ambient = {0.0, 0.1, 0.3, 1.0}; + input.color.a = 1.0f; + float ambient_contribution = 0.1f; + float diffuse_contribution = 0.6f; + float lighting_contribution = 1.0f - diffuse_contribution - ambient_contribution; + + return ambient_contribution * ambient + diffuse_contribution * input.color + + lighting_contribution * input.color * saturate(dot(input.lightdir, input.normal)); +} diff --git a/3D/Shaders/cube_vs.hlsl b/3D/Shaders/cube_vs.hlsl new file mode 100644 index 000000000..d9c651d7d --- /dev/null +++ b/3D/Shaders/cube_vs.hlsl @@ -0,0 +1,33 @@ +cbuffer MVP : register(b0) { + matrix world; + matrix view; + matrix projection; + float3 lightdir; + float4 mat_color; +}; + +struct INPUT { + float4 position : POSITION; + float3 normal : NORMAL; + float4 color : COLOR; +}; + +struct OUTPUT { + float4 position : SV_POSITION; + float3 normal : TEXCOORD0; + float3 lightdir : TEXCOORD1; + float4 color : COLOR; +}; + +OUTPUT main(INPUT input) { + OUTPUT output; + + matrix mvp = mul(mul(view, world), projection); + output.position = mul(input.position, mvp); + + output.normal = normalize(mul(input.normal, world)); + output.lightdir = lightdir; + output.color = input.color * mat_color; + + return output; +} diff --git a/3D/TSR.h b/3D/TSR.h new file mode 100644 index 000000000..ee015a01f --- /dev/null +++ b/3D/TSR.h @@ -0,0 +1,62 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Translations, scale and rotation matrices. + */ + +#include "Math.h" + +class TSR { + public: + DXVector3 translation; + DXVector3 scale; + DXVector3 rotation; + + TSR() { + translation.x = translation.y = translation.z = 0.0f; + scale.x = scale.y = scale.z = 1.0f; + rotation.x = rotation.y = rotation.z = 0.0f; + } + + DXMatrix ToMatrix() const { + DXMatrix _mtx_translation = {}; + DXMatrix _mtx_scale = {}; + DXMatrix _mtx_rotation_1 = {}; + DXMatrix _mtx_rotation_2 = {}; + DXMatrix _mtx_result = {}; + + DXMatrixTranslation(_mtx_translation, translation.x, translation.y, translation.z); + DXMatrixScaling(_mtx_scale, scale.x, scale.y, scale.z); + DXMatrixRotationYawPitchRoll(_mtx_rotation_1, rotation.x, 0, rotation.z); + DXMatrixRotationYawPitchRoll(_mtx_rotation_2, 0, rotation.y, 0); + + DXMatrixIdentity(_mtx_result); + DXMatrixMultiply(_mtx_result, _mtx_result, _mtx_rotation_1); + DXMatrixMultiply(_mtx_result, _mtx_result, _mtx_rotation_2); + DXMatrixMultiply(_mtx_result, _mtx_result, _mtx_scale); + DXMatrixMultiply(_mtx_result, _mtx_result, _mtx_translation); + + return _mtx_result; + } +}; diff --git a/3D/Vertex.h b/3D/Vertex.h new file mode 100644 index 000000000..8466fa312 --- /dev/null +++ b/3D/Vertex.h @@ -0,0 +1,24 @@ +#include "../Refs.mqh" + +/** + * Generic vertex to be used by meshes. + */ +struct Vertex { + DXVector3 Position; + DXVector3 Normal; + DXColor Color; + + Vertex() { + Color.r = 1.0f; + Color.g = 1.0f; + Color.b = 1.0f; + Color.a = 1.0f; + } + + static const ShaderVertexLayout Layout[3]; +}; + +const ShaderVertexLayout Vertex::Layout[3] = { + {"POSITION", 0, GFX_VAR_TYPE_FLOAT, 3, false, sizeof(Vertex), 0}, + {"NORMAL", 0, GFX_VAR_TYPE_FLOAT, 3, false, sizeof(Vertex), sizeof(float) * 3}, + {"COLOR", 0, GFX_VAR_TYPE_FLOAT, 4, false, sizeof(Vertex), sizeof(float) * 6}}; diff --git a/3D/VertexBuffer.h b/3D/VertexBuffer.h new file mode 100644 index 000000000..822c33fb2 --- /dev/null +++ b/3D/VertexBuffer.h @@ -0,0 +1,46 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Generic graphics vertex buffer. + */ + +#include "../Refs.mqh" + +class VertexBuffer : public Dynamic { + // Reference to graphics device. + WeakRef device; + + public: + /** + * Constructor. + */ + VertexBuffer(Device* _device) { device = _device; } + + /** + * Returns base graphics device. + */ + Device* GetDevice() { return device.Ptr(); } + + virtual void Select() = NULL; +}; diff --git a/Action.mqh b/Action.mqh deleted file mode 100644 index 5ed379560..000000000 --- a/Action.mqh +++ /dev/null @@ -1,373 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -/** - * @file - * Provides integration with actions. - */ - -// Prevents processing this includes file for the second time. -#ifndef ACTION_MQH -#define ACTION_MQH - -// Forward class declaration. -class Action; - -// Includes. -#include "Action.enum.h" -#include "Action.struct.h" -#include "Condition.enum.h" -#include "EA.mqh" - -/** - * Action class. - */ -class Action { - public: - protected: - // Class variables. - Ref logger; - - public: - // Class variables. - DictStruct actions; - - /* Special methods */ - - /** - * Class constructor. - */ - Action() {} - Action(ActionEntry &_entry) { actions.Push(_entry); } - Action(long _action_id, ENUM_ACTION_TYPE _type) { - ActionEntry _entry(_action_id, _type); - actions.Push(_entry); - } - template - Action(T _action_id, void *_obj = NULL) { - ActionEntry _entry(_action_id); - if (_obj != NULL) { - _entry.SetObject(_obj); - } - actions.Push(_entry); - } - template - Action(T _action_id, MqlParam &_args[], void *_obj = NULL) { - ActionEntry _entry(_action_id); - _entry.SetArgs(_args); - if (_obj != NULL) { - _entry.SetObject(_obj); - } - actions.Push(_entry); - } - - /** - * Class copy constructor. - */ - Action(Action &_cond) { actions = _cond.GetActions(); } - - /* Main methods */ - - /** - * Execute actions. - */ - bool Execute() { - bool _result = true, _executed = false; - for (DictStructIterator iter = actions.Begin(); iter.IsValid(); ++iter) { - bool _curr_result = false; - ActionEntry _entry = iter.Value(); - if (!_entry.IsValid()) { - // Ignore invalid entries. - continue; - } - if (_entry.IsActive()) { - _executed = _result &= Execute(_entry); - } - } - return _result && _executed; - } - - /** - * Execute specific action. - */ - static bool Execute(ActionEntry &_entry) { - bool _result = false; - switch (_entry.type) { - case ACTION_TYPE_ACTION: - if (Object::IsValid(_entry.obj)) { - _result = ((Action *)_entry.obj).ExecuteAction((ENUM_ACTION_ACTION)_entry.action_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; - case ACTION_TYPE_EA: - if (Object::IsValid(_entry.obj)) { - _result = ((EA *)_entry.obj).ExecuteAction((ENUM_EA_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#ifdef ORDER_MQH - case ACTION_TYPE_ORDER: - if (Object::IsValid(_entry.obj)) { - _result = ((Order *)_entry.obj).ExecuteAction((ENUM_ORDER_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#endif -#ifdef INDICATOR_MQH - case ACTION_TYPE_INDICATOR: - if (Object::IsValid(_entry.obj)) { - _result = ((IndicatorBase *)_entry.obj).ExecuteAction((ENUM_INDICATOR_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#endif -#ifdef STRATEGY_MQH - case ACTION_TYPE_STRATEGY: - if (Object::IsValid(_entry.obj)) { - _result = ((Strategy *)_entry.obj).ExecuteAction((ENUM_STRATEGY_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#endif -#ifdef TASK_MQH - case ACTION_TYPE_TASK: - if (Object::IsValid(_entry.obj)) { - _result = ((Task *)_entry.obj).ExecuteAction((ENUM_TASK_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#endif - case ACTION_TYPE_TRADE: - if (Object::IsValid(_entry.obj)) { - _result = ((Trade *)_entry.obj).ExecuteAction((ENUM_TRADE_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#ifdef TERMINAL_MQH - case ACTION_TYPE_TERMINAL: - if (Object::IsValid(_entry.obj)) { - _result = ((Terminal *)_entry.obj).ExecuteAction((ENUM_TERMINAL_ACTION)_entry.action_id); - } else { - _result = false; - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - } - break; -#endif - } - if (_result) { - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_DONE); - _entry.RemoveFlags(ACTION_ENTRY_FLAG_IS_ACTIVE); - _entry.last_success = TimeCurrent(); - } else { - if (--_entry.tries <= 0) { - _entry.AddFlags(ACTION_ENTRY_FLAG_IS_INVALID); - _entry.RemoveFlags(ACTION_ENTRY_FLAG_IS_ACTIVE); - } - } - return _result; - } - - /* State methods */ - - /** - * Check if action is active. - */ - bool IsActive() { - // The whole action is active when at least one action is active. - return GetFlagCount(ACTION_ENTRY_FLAG_IS_ACTIVE) > 0; - } - - /** - * Check if action is done. - */ - bool IsDone() { - // The whole action is done when all actions has been executed successfully. - return GetFlagCount(ACTION_ENTRY_FLAG_IS_DONE) == actions.Size(); - } - - /** - * Check if action has failed. - */ - bool IsFailed() { - // The whole action is failed when at least one action failed. - return GetFlagCount(ACTION_ENTRY_FLAG_IS_FAILED) > 0; - } - - /** - * Check if action is finished. - */ - bool IsFinished() { - // The whole action is finished when there are no more active actions. - return GetFlagCount(ACTION_ENTRY_FLAG_IS_ACTIVE) == 0; - } - - /** - * Check if action is invalid. - */ - bool IsInvalid() { - // The whole action is invalid when at least one action is invalid. - return GetFlagCount(ACTION_ENTRY_FLAG_IS_INVALID) > 0; - } - - /* Getters */ - - /** - * Returns actions. - */ - DictStruct *GetActions() { return &actions; } - - /** - * Count entry flags. - */ - unsigned int GetFlagCount(ENUM_ACTION_ENTRY_FLAGS _flag) { - unsigned int _counter = 0; - for (DictStructIterator iter = actions.Begin(); iter.IsValid(); ++iter) { - ActionEntry _entry = iter.Value(); - if (_entry.HasFlag(_flag)) { - _counter++; - } - } - return _counter; - } - - /* Setters */ - - /** - * Sets entry flags. - */ - bool SetFlags(ENUM_ACTION_ENTRY_FLAGS _flag, bool _value = true) { - unsigned int _counter = 0; - for (DictStructIterator iter = actions.Begin(); iter.IsValid(); ++iter) { - ActionEntry _entry = iter.Value(); - switch (_value) { - case false: - if (_entry.HasFlag(_flag)) { - _entry.SetFlag(_flag, _value); - _counter++; - } - break; - case true: - if (!_entry.HasFlag(_flag)) { - _entry.SetFlag(_flag, _value); - _counter++; - } - break; - } - } - return _counter > 0; - } - - /* Conditions and actions */ - - /** - * Checks for Task condition. - * - * @param ENUM_ACTION_CONDITION _cond - * Action condition. - * @return - * Returns true when the condition is met. - */ - bool CheckCondition(ENUM_ACTION_CONDITION _cond, DataParamEntry &_args[]) { - switch (_cond) { - case ACTION_COND_IS_ACTIVE: - // Is active; - return IsActive(); - case ACTION_COND_IS_DONE: - // Is done. - return IsDone(); - case ACTION_COND_IS_FAILED: - // Is failed. - return IsFailed(); - case ACTION_COND_IS_FINISHED: - // Is finished. - return IsFinished(); - case ACTION_COND_IS_INVALID: - // Is invalid. - return IsInvalid(); - default: - logger.Ptr().Error(StringFormat("Invalid Action condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); - return false; - } - } - bool CheckCondition(ENUM_ACTION_CONDITION _cond) { - ARRAY(DataParamEntry, _args); - return Action::CheckCondition(_cond, _args); - } - - /** - * Execute action of action. - * - * @param ENUM_ACTION_ACTION _action - * Action of action to execute. - * @return - * Returns true when the action has been executed successfully. - */ - bool ExecuteAction(ENUM_ACTION_ACTION _action, DataParamEntry &_args[]) { - bool _result = true; - switch (_action) { - case ACTION_ACTION_DISABLE: - // Disable action. - return SetFlags(ACTION_ENTRY_FLAG_IS_ACTIVE, false); - case ACTION_ACTION_EXECUTE: - // Execute action. - return Execute(); - case ACTION_ACTION_MARK_AS_DONE: - // Marks as done. - return SetFlags(ACTION_ENTRY_FLAG_IS_DONE); - case ACTION_ACTION_MARK_AS_FAILED: - // Mark as failed. - return SetFlags(ACTION_ENTRY_FLAG_IS_FAILED); - case ACTION_ACTION_MARK_AS_FINISHED: - // Mark as finished. - return SetFlags(ACTION_ENTRY_FLAG_IS_ACTIVE, false); - case ACTION_ACTION_MARK_AS_INVALID: - // Mark as invalid. - return SetFlags(ACTION_ENTRY_FLAG_IS_INVALID); - default: - logger.Ptr().Error(StringFormat("Invalid action of action: %s!", EnumToString(_action), __FUNCTION_LINE__)); - return false; - } - return _result; - } - bool ExecuteAction(ENUM_ACTION_ACTION _action) { - ARRAY(DataParamEntry, _args); - return Action::ExecuteAction(_action, _args); - } - - /* Other methods */ -}; - -#endif // ACTION_MQH diff --git a/Action.struct.h b/Action.struct.h deleted file mode 100644 index 1f5481f14..000000000 --- a/Action.struct.h +++ /dev/null @@ -1,120 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * @file - * Includes Action's structs. - */ - -#ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once -#endif - -// Includes. -#include "Account.enum.h" -#include "Action.enum.h" -#include "Chart.enum.h" -#include "Data.struct.h" -#include "EA.enum.h" -#include "Indicator.enum.h" -//#include "Market.enum.h" -#include "Order.enum.h" -#include "Serializer.mqh" -#include "Strategy.enum.h" -#include "Task.enum.h" -#include "Trade.enum.h" - -/* Entry for Action class. */ -struct ActionEntry { - unsigned char flags; /* Action flags. */ - datetime last_success; /* Time of the previous check. */ - int frequency; /* How often to check. */ - long action_id; /* Action ID. */ - short tries; /* Number of retries left. */ - void *obj; /* Reference to associated object. */ - ENUM_ACTION_TYPE type; /* Action type. */ - DataParamEntry args[]; /* Action arguments. */ - // Constructors. - ActionEntry() : type(FINAL_ACTION_TYPE_ENTRY), action_id(WRONG_VALUE) { Init(); } - ActionEntry(long _action_id, ENUM_ACTION_TYPE _type) : type(_type), action_id(_action_id) { Init(); } - ActionEntry(ActionEntry &_ae) { this = _ae; } - ActionEntry(ENUM_EA_ACTION _action_id) : type(ACTION_TYPE_EA), action_id(_action_id) { Init(); } - ActionEntry(ENUM_ORDER_ACTION _action_id) : type(ACTION_TYPE_ORDER), action_id(_action_id) { Init(); } - ActionEntry(ENUM_INDICATOR_ACTION _action_id) : type(ACTION_TYPE_INDICATOR), action_id(_action_id) { Init(); } - ActionEntry(ENUM_STRATEGY_ACTION _action_id) : type(ACTION_TYPE_STRATEGY), action_id(_action_id) { Init(); } - ActionEntry(ENUM_TASK_ACTION _action_id) : type(ACTION_TYPE_TASK), action_id(_action_id) { Init(); } - ActionEntry(ENUM_TRADE_ACTION _action_id) : type(ACTION_TYPE_TRADE), action_id(_action_id) { Init(); } - // Deconstructor. - ~ActionEntry() { - // Object::Delete(obj); - } - // Flag methods. - bool HasFlag(unsigned char _flag) { return bool(flags & _flag); } - void AddFlags(unsigned char _flags) { flags |= _flags; } - void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } - void SetFlag(ENUM_ACTION_ENTRY_FLAGS _flag, bool _value) { - if (_value) - AddFlags(_flag); - else - RemoveFlags(_flag); - } - void SetFlags(unsigned char _flags) { flags = _flags; } - // State methods. - bool IsActive() { return HasFlag(ACTION_ENTRY_FLAG_IS_ACTIVE); } - bool IsDone() { return HasFlag(ACTION_ENTRY_FLAG_IS_DONE); } - bool IsFailed() { return HasFlag(ACTION_ENTRY_FLAG_IS_FAILED); } - bool IsInvalid() { return HasFlag(ACTION_ENTRY_FLAG_IS_INVALID); } - bool IsValid() { return !IsInvalid(); } - // Getters. - long GetId() { return action_id; } - ENUM_ACTION_TYPE GetType() { return type; } - // Setter methods. - void AddArg(MqlParam &_arg) { - // @todo: Add another value to args[]. - } - void Init() { - flags = ACTION_ENTRY_FLAG_NONE; - frequency = 60; - SetFlag(ACTION_ENTRY_FLAG_IS_ACTIVE, action_id != WRONG_VALUE); - SetFlag(ACTION_ENTRY_FLAG_IS_INVALID, action_id == WRONG_VALUE); - last_success = 0; - tries = 1; - } - void SetArgs(ARRAY_REF(MqlParam, _args)) { - // @todo: for(). - } - void SetObject(void *_obj) { obj = _obj; } - void SetTries(short _count) { tries = _count; } - - SerializerNodeType Serialize(Serializer &s) { - s.Pass(THIS_REF, "flags", flags); - s.Pass(THIS_REF, "last_success", last_success); - s.Pass(THIS_REF, "action_id", action_id); - // s.Pass(THIS_REF, "tries", tries); - s.PassEnum(THIS_REF, "type", type); - s.PassEnum(THIS_REF, "frequency", frequency); - s.PassArray(this, "args", args); - return SerializerNodeObject; - } - - SERIALIZER_EMPTY_STUB; -}; diff --git a/Chart.mqh b/Chart.mqh index a568d8ae5..6a98e0644 100644 --- a/Chart.mqh +++ b/Chart.mqh @@ -42,10 +42,10 @@ class Market; #include "Chart.enum.h" #include "Chart.struct.h" #include "Chart.struct.serialize.h" -#include "Condition.enum.h" #include "Convert.mqh" #include "Market.mqh" #include "Serializer.mqh" +#include "Task/TaskCondition.enum.h" #ifndef __MQL4__ // Defines structs (for MQL4 backward compatibility). diff --git a/Chart.struct.static.h b/Chart.struct.static.h index 0c728bcc4..f892cf2a8 100644 --- a/Chart.struct.static.h +++ b/Chart.struct.static.h @@ -186,15 +186,15 @@ struct ChartStatic { case MODE_VOLUME: ArraySetAsSeries(arr_l, true); CopyTickVolume(_symbol, _tf, _start, _count, arr_l); - return (ArrayMinimum(arr_l, 0, _count) + _start); + return ArrayMinimum(arr_l, 0, _count) + _start; case MODE_TIME: ArraySetAsSeries(arr_dt, true); CopyTime(_symbol, _tf, _start, _count, arr_dt); - return (ArrayMinimum(arr_dt, 0, _count) + _start); + return ArrayMinimum(arr_dt, 0, _count) + _start; default: break; } - return (ArrayMinimum(arr_d, 0, _count) + _start); + return ArrayMinimum(arr_d, 0, _count) + _start; #endif } diff --git a/Condition.mqh b/Condition.mqh deleted file mode 100644 index 915cbcd4e..000000000 --- a/Condition.mqh +++ /dev/null @@ -1,267 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -/** - * @file - * Provides integration with conditions. - */ - -// Prevents processing this includes file for the second time. -#ifndef CONDITION_MQH -#define CONDITION_MQH - -// Includes. -#include "Account.mqh" -#include "Chart.mqh" -#include "DateTime.mqh" -#include "DictStruct.mqh" -#include "EA.mqh" -#include "Indicator.mqh" -#include "Market.mqh" -#include "Object.mqh" -#include "Order.mqh" - -// Includes class enum and structs. -#include "Condition.enum.h" -#include "Condition.struct.h" - -/** - * Condition class. - */ -class Condition { - public: - protected: - // Class variables. - Ref logger; - - public: - // Class variables. - DictStruct conds; - - /* Special methods */ - - /** - * Class constructor. - */ - Condition() {} - Condition(ConditionEntry &_entry) { conds.Push(_entry); } - Condition(long _cond_id, ENUM_CONDITION_TYPE _type) { - ConditionEntry _entry(_cond_id, _type); - conds.Push(_entry); - } - template - Condition(T _cond_id, void *_obj = NULL) { - ConditionEntry _entry(_cond_id); - if (_obj != NULL) { - _entry.SetObject(_obj); - } - conds.Push(_entry); - } - template - Condition(T _cond_id, MqlParam &_args[], void *_obj = NULL) { - Init(); - ConditionEntry _entry(_cond_id); - _entry.SetArgs(_args); - if (_obj != NULL) { - _entry.SetObject(_obj); - } - conds.Push(_entry); - } - - /** - * Class copy constructor. - */ - Condition(Condition &_cond) { conds = _cond.GetConditions(); } - - /* Main methods */ - - /** - * Test conditions. - */ - bool Test() { - bool _result = false, _prev_result = true; - for (DictStructIterator iter = conds.Begin(); iter.IsValid(); ++iter) { - bool _curr_result = false; - ConditionEntry _entry = iter.Value(); - if (!_entry.IsValid()) { - // Ignore invalid entries. - continue; - } - if (_entry.IsActive()) { - switch (_entry.next_statement) { - case COND_AND: - _curr_result = _prev_result && this.Test(_entry); - break; - case COND_OR: - _curr_result = _prev_result || this.Test(_entry); - break; - case COND_SEQ: - _curr_result = this.Test(_entry); - if (!_curr_result) { - // Do not check further conditions when the current condition is false. - return false; - } - } - _result = _prev_result = _curr_result; - } - } - return _result; - } - - /** - * Test specific condition. - */ - static bool Test(ConditionEntry &_entry) { - bool _result = false; - switch (_entry.type) { - case COND_TYPE_ACCOUNT: - if (Object::IsValid(_entry.obj)) { - _result = ((Account *)_entry.obj).CheckCondition((ENUM_ACCOUNT_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; - case COND_TYPE_CHART: - if (Object::IsValid(_entry.obj)) { - _result = ((Chart *)_entry.obj).CheckCondition((ENUM_CHART_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; - case COND_TYPE_DATETIME: - if (Object::IsValid(_entry.obj)) { - _result = ((DateTime *)_entry.obj).CheckCondition((ENUM_DATETIME_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = DateTime::CheckCondition((ENUM_DATETIME_CONDITION)_entry.cond_id, _entry.args); - } - break; - case COND_TYPE_EA: - if (Object::IsValid(_entry.obj)) { - _result = ((EA *)_entry.obj).CheckCondition((ENUM_EA_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#ifdef INDICATOR_MQH - case COND_TYPE_INDICATOR: - if (Object::IsValid(_entry.obj)) { - _result = ((IndicatorBase *)_entry.obj).CheckCondition((ENUM_INDICATOR_CONDITION)_entry.cond_id, _entry.args); - } else { - // Static method not supported. - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#endif - case COND_TYPE_MARKET: - if (Object::IsValid(_entry.obj)) { - _result = ((Market *)_entry.obj).CheckCondition((ENUM_MARKET_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#ifdef MATH_H - case COND_TYPE_MATH: - /* - if (Object::IsValid(_entry.obj)) { - _result = ((Math *)_entry.obj).CheckCondition((ENUM_MATH_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - */ - return false; - break; -#endif // MATH_M -#ifdef ORDER_MQH - case COND_TYPE_ORDER: - if (Object::IsValid(_entry.obj)) { - _result = ((Order *)_entry.obj).CheckCondition((ENUM_ORDER_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#endif -#ifdef STRATEGY_MQH - case COND_TYPE_STRATEGY: - if (Object::IsValid(_entry.obj)) { - _result = ((Strategy *)_entry.obj).CheckCondition((ENUM_STRATEGY_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#endif -#ifdef TASK_MQH - case COND_TYPE_TASK: - if (Object::IsValid(_entry.obj)) { - _result = ((Task *)_entry.obj).CheckCondition((ENUM_TASK_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#endif - case COND_TYPE_TRADE: - if (Object::IsValid(_entry.obj)) { - _result = ((Trade *)_entry.obj).CheckCondition((ENUM_TRADE_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#ifdef TERMINAL_MQH - case COND_TYPE_TERMINAL: - if (Object::IsValid(_entry.obj)) { - _result = ((Terminal *)_entry.obj).CheckCondition((ENUM_TERMINAL_CONDITION)_entry.cond_id, _entry.args); - } else { - _result = false; - _entry.AddFlags(COND_ENTRY_FLAG_IS_INVALID); - } - break; -#endif - } - if (_result) { - _entry.last_success = TimeCurrent(); - _entry.tries--; - } - _entry.last_check = TimeCurrent(); - return _result; - } - - /* Other methods */ - - /* Getters */ - - /** - * Returns conditions. - */ - DictStruct *GetConditions() { return &conds; } - - /* Setters */ -}; -#endif // CONDITION_MQH diff --git a/Condition.struct.h b/Condition.struct.h deleted file mode 100644 index 6e3376812..000000000 --- a/Condition.struct.h +++ /dev/null @@ -1,111 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * @file - * Includes Condition's structs. - */ - -#ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once -#endif - -// Includes. -#include "Account.enum.h" -#include "Chart.enum.h" -#include "DateTime.enum.h" -#include "EA.enum.h" -#include "Indicator.enum.h" -//#include "Market.enum.h" -#include "Order.enum.h" -#include "Strategy.enum.h" -#include "Task.enum.h" -#include "Trade.enum.h" - -struct ConditionEntry { - unsigned char flags; // Condition flags. - datetime last_check; // Time of the latest check. - datetime last_success; // Time of the last success. - int frequency; // How often to check. - long cond_id; // Condition ID. - short tries; // Number of successful tries left. - void *obj; // Reference to associated object. - ENUM_CONDITION_STATEMENT next_statement; // Statement type of the next condition. - ENUM_CONDITION_TYPE type; // Condition type. - DataParamEntry args[]; // Condition arguments. - // Constructors. - void ConditionEntry() : type(FINAL_CONDITION_TYPE_ENTRY), cond_id(WRONG_VALUE) { Init(); } - void ConditionEntry(long _cond_id, ENUM_CONDITION_TYPE _type) : type(_type), cond_id(_cond_id) { Init(); } - void ConditionEntry(ConditionEntry &_ce) { this = _ce; } - void ConditionEntry(ENUM_ACCOUNT_CONDITION _cond_id) : type(COND_TYPE_ACCOUNT), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_CHART_CONDITION _cond_id) : type(COND_TYPE_CHART), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_DATETIME_CONDITION _cond_id) : type(COND_TYPE_DATETIME), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_EA_CONDITION _cond_id) : type(COND_TYPE_EA), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_INDICATOR_CONDITION _cond_id) : type(COND_TYPE_INDICATOR), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_MARKET_CONDITION _cond_id) : type(COND_TYPE_MARKET), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_ORDER_CONDITION _cond_id) : type(COND_TYPE_ORDER), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_STRATEGY_CONDITION _cond_id) : type(COND_TYPE_STRATEGY), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_TASK_CONDITION _cond_id) : type(COND_TYPE_TASK), cond_id(_cond_id) { Init(); } - void ConditionEntry(ENUM_TRADE_CONDITION _cond_id) : type(COND_TYPE_TRADE), cond_id(_cond_id) { Init(); } - // Deconstructor. - void ~ConditionEntry() { - // Object::Delete(obj); - } - // Flag methods. - bool HasFlag(unsigned char _flag) { return bool(flags & _flag); } - void AddFlags(unsigned char _flags) { flags |= _flags; } - void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } - void SetFlag(ENUM_CONDITION_ENTRY_FLAGS _flag, bool _value) { - if (_value) - AddFlags(_flag); - else - RemoveFlags(_flag); - } - void SetFlags(unsigned char _flags) { flags = _flags; } - // State methods. - bool IsActive() { return HasFlag(COND_ENTRY_FLAG_IS_ACTIVE); } - bool IsExpired() { return HasFlag(COND_ENTRY_FLAG_IS_EXPIRED); } - bool IsReady() { return HasFlag(COND_ENTRY_FLAG_IS_READY); } - bool IsInvalid() { return HasFlag(COND_ENTRY_FLAG_IS_INVALID); } - bool IsValid() { return !IsInvalid(); } - // Getters. - long GetId() { return cond_id; } - ENUM_CONDITION_TYPE GetType() { return type; } - // Setters. - void AddArg(MqlParam &_arg) { - // @todo: Add another value to args[]. - } - void Init() { - flags = COND_ENTRY_FLAG_NONE; - frequency = 60; - SetFlag(COND_ENTRY_FLAG_IS_ACTIVE, cond_id != WRONG_VALUE); - SetFlag(COND_ENTRY_FLAG_IS_INVALID, cond_id == WRONG_VALUE); - last_check = last_success = 0; - next_statement = COND_AND; - tries = 1; - } - void SetArgs(MqlParam &_args[]) { - // @todo: for(). - } - void SetObject(void *_obj) { obj = _obj; } - void SetTries(short _count) { tries = _count; } -}; diff --git a/Dict.mqh b/Dict.mqh index c5275b985..cd55d66ba 100644 --- a/Dict.mqh +++ b/Dict.mqh @@ -173,6 +173,20 @@ class Dict : public DictBase { return slot.value == value; } + /** + * Returns index of dictionary's value or -1 if value doesn't exist. + */ + template <> + int IndexOf(V& value) { + for (DictIteratorBase i(Begin()); i.IsValid(); ++i) { + if (i.Value() == value) { + return (int)i.Index(); + } + } + + return -1; + } + /** * Checks whether dictionary contains given value. */ @@ -191,24 +205,30 @@ class Dict : public DictBase { * Inserts value into given array of DictSlots. */ bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V value, bool allow_resize) { - // Will resize dict if there were performance problems before. - if (allow_resize && !dictSlotsRef.IsPerformant()) { - if (!GrowUp()) { - return false; - } - } - if (_mode == DictModeUnknown) _mode = DictModeDict; else if (_mode != DictModeDict) { Alert("Warning: Dict already operates as a list, not a dictionary!"); - DebugBreak(); return false; } unsigned int position; DictSlot* keySlot = GetSlotByKey(dictSlotsRef, key, position); + if (keySlot == NULL && !IsGrowUpAllowed()) { + // Resize is prohibited. + return false; + } + + // Will resize dict if there were performance problems before. + if (allow_resize && IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + if (!GrowUp()) { + return false; + } + // We now have new positions of slots, so we have to take the corrent slot again. + keySlot = GetSlotByKey(dictSlotsRef, key, position); + } + if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { // No DictSlotsRef.DictSlots available. if (overflow_listener != NULL) { diff --git a/DictBase.mqh b/DictBase.mqh index 534f91e8a..daeaaae7a 100644 --- a/DictBase.mqh +++ b/DictBase.mqh @@ -257,6 +257,18 @@ class DictBase { // No key found. } + /** + * Checks whether overflow listener allows dict to grow up. + */ + bool IsGrowUpAllowed() { + if (overflow_listener == NULL) { + return true; + } + + // Checking if overflow listener allows resize from current to higher number of slots. + return overflow_listener(DICT_OVERFLOW_REASON_FULL, Size(), 0); + } + /** * Moves last slot to given one to fill the hole after removing the value. */ @@ -280,6 +292,11 @@ class DictBase { */ const unsigned int Size() { return _DictSlots_ref._num_used; } + /** + * Returns number of all (reserved) DictSlots. + */ + const unsigned int ReservedSize() { return ArraySize(_DictSlots_ref.DictSlots); } + /** * Checks whether given key exists in the dictionary. */ diff --git a/DictObject.mqh b/DictObject.mqh index 3585c1972..8bcc9d412 100644 --- a/DictObject.mqh +++ b/DictObject.mqh @@ -173,29 +173,49 @@ class DictObject : public DictBase { return slot.value == value; } - protected: /** - * Inserts value into given array of DictSlots. + * Returns index of dictionary's value or -1 if value doesn't exist. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { - // Will resize dict if there were performance problems before. - if (allow_resize && !dictSlotsRef.IsPerformant()) { - if (!GrowUp()) { - return false; + template <> + int IndexOf(V& value) { + for (DictIteratorBase i(Begin()); i.IsValid(); ++i) { + if (i.Value() == value) { + return (int)i.Index(); } } + return -1; + } + + protected: + /** + * Inserts value into given array of DictSlots. + */ + bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { if (_mode == DictModeUnknown) _mode = DictModeDict; else if (_mode != DictModeDict) { Alert("Warning: Dict already operates as a list, not a dictionary!"); - DebugBreak(); return false; } unsigned int position; DictSlot* keySlot = GetSlotByKey(dictSlotsRef, key, position); + if (keySlot == NULL && !IsGrowUpAllowed()) { + // Resize is prohibited. + return false; + } + + // Will resize dict if there were performance problems before. + if (allow_resize && IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + if (!GrowUp()) { + return false; + } + // We now have new positions of slots, so we have to take the corrent slot again. + keySlot = GetSlotByKey(dictSlotsRef, key, position); + } + if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { // No DictSlotsRef.DictSlots available. if (overflow_listener != NULL) { diff --git a/DictStruct.mqh b/DictStruct.mqh index b2cd3d5c9..b4cb0d747 100644 --- a/DictStruct.mqh +++ b/DictStruct.mqh @@ -233,18 +233,25 @@ class DictStruct : public DictBase { return slot.value == value; } - protected: /** - * Inserts value into given array of DictSlots. + * Returns index of dictionary's value or -1 if value doesn't exist. */ - bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { - // Will resize dict if there were performance problems before. - if (allow_resize && !dictSlotsRef.IsPerformant()) { - if (!GrowUp()) { - return false; + template <> + int IndexOf(const V& value) { + for (DictIteratorBase i(Begin()); i.IsValid(); ++i) { + if (i.Value() == value) { + return (int)i.Index(); } } + return -1; + } + + protected: + /** + * Inserts value into given array of DictSlots. + */ + bool InsertInto(DictSlotsRef& dictSlotsRef, const K key, V& value, bool allow_resize) { if (_mode == DictModeUnknown) _mode = DictModeDict; else if (_mode != DictModeDict) { @@ -255,6 +262,20 @@ class DictStruct : public DictBase { unsigned int position; DictSlot* keySlot = GetSlotByKey(dictSlotsRef, key, position); + if (keySlot == NULL && !IsGrowUpAllowed()) { + // Resize is prohibited. + return false; + } + + // Will resize dict if there were performance problems before. + if (allow_resize && IsGrowUpAllowed() && !dictSlotsRef.IsPerformant()) { + if (!GrowUp()) { + return false; + } + // We now have new positions of slots, so we have to take the corrent slot again. + keySlot = GetSlotByKey(dictSlotsRef, key, position); + } + if (keySlot == NULL && dictSlotsRef._num_used == ArraySize(dictSlotsRef.DictSlots)) { // No DictSlotsRef.DictSlots available. if (overflow_listener != NULL) { diff --git a/EA.mqh b/EA.mqh index f499717d7..080e78c38 100644 --- a/EA.mqh +++ b/EA.mqh @@ -30,9 +30,7 @@ #define EA_MQH // Includes. -#include "Action.enum.h" #include "Chart.mqh" -#include "Condition.enum.h" #include "Data.struct.h" #include "Dict.mqh" #include "DictObject.mqh" @@ -46,13 +44,14 @@ #include "SerializerSqlite.mqh" #include "Strategy.mqh" #include "SummaryReport.mqh" -#include "Task.mqh" +#include "Task/TaskManager.h" +#include "Task/Taskable.h" #include "Terminal.mqh" #include "Trade.mqh" #include "Trade/TradeSignal.h" #include "Trade/TradeSignalManager.h" -class EA { +class EA : public Taskable { protected: // Class variables. Account *account; @@ -68,24 +67,41 @@ class EA { DictObject trade; DictObject> data_indi; DictObject> data_stg; - DictStruct tasks; EAParams eparams; EAProcessResult eresults; EAState estate; + TaskManager tasks; TradeSignalManager tsm; + protected: + /* Protected methods */ + + /** + * Init code (called on constructor). + */ + void Init() { InitTask(); } + + /** + * Process initial task (called on constructor). + */ + void InitTask() { + // Add and process init task. + TaskObject _taskobj_init(eparams.GetStruct(STRUCT_ENUM(EAParams, EA_PARAM_STRUCT_TASK_ENTRY)), + THIS_PTR, THIS_PTR); + estate.Set(STRUCT_ENUM(EAState, EA_STATE_FLAG_ON_INIT), true); + _taskobj_init.Process(); + estate.Set(STRUCT_ENUM(EAState, EA_STATE_FLAG_ON_INIT), false); + } + public: /** * Class constructor. */ EA(EAParams &_params) : account(new Account) { eparams = _params; - estate.Set(STRUCT_ENUM(EAState, EA_STATE_FLAG_ON_INIT), true); UpdateStateFlags(); // Add and process tasks. - TaskAdd(eparams.GetStruct(STRUCT_ENUM(EAParams, EA_PARAM_STRUCT_TASK_ENTRY))); - ProcessTasks(); - estate.Set(STRUCT_ENUM(EAState, EA_STATE_FLAG_ON_INIT), false); + Init(); // Initialize a trade instance for the current chart and symbol. ChartParams _cparams((ENUM_TIMEFRAMES)_Period, _Symbol); TradeParams _tparams; @@ -375,8 +391,6 @@ class EA { } if (_strat.TickFilter(_tick)) { _can_trade &= !_strat.IsSuspended(); - _can_trade &= - !_strat.CheckCondition(STRAT_COND_TRADE_COND, TRADE_COND_HAS_STATE, TRADE_STATE_TRADE_CANNOT); TradeSignalEntry _sentry = GetStrategySignalEntry(_strat, _can_trade, _strat.Get(STRAT_PARAM_SHIFT)); if (_sentry.Get(STRUCT_ENUM(TradeSignalEntry, TRADE_SIGNAL_PROP_SIGNALS)) > 0) { TradeSignal _signal(_sentry); @@ -658,53 +672,6 @@ class EA { } */ - /* Tasks */ - - /** - * Add task. - */ - bool TaskAdd(TaskEntry &_entry) { - bool _result = false; - if (_entry.IsValid()) { - switch (_entry.GetConditionType()) { - case COND_TYPE_ACCOUNT: - _entry.SetConditionObject(account); - break; - case COND_TYPE_EA: - _entry.SetConditionObject(THIS_PTR); - break; - case COND_TYPE_TRADE: - _entry.SetConditionObject(trade.GetByKey(_Symbol)); - break; - } - switch (_entry.GetActionType()) { - case ACTION_TYPE_EA: - _entry.SetActionObject(THIS_PTR); - break; - case ACTION_TYPE_TRADE: - _entry.SetActionObject(trade.GetByKey(_Symbol)); - break; - } - _result |= tasks.Push(_entry); - } - return _result; - } - - /** - * Process EA tasks. - */ - unsigned int ProcessTasks() { - unsigned int _counter = 0; - for (DictStructIterator iter = tasks.Begin(); iter.IsValid(); ++iter) { - bool _is_processed = false; - TaskEntry _entry = iter.Value(); - _is_processed = _is_processed || Task::Process(_entry); - // _entry.last_process = TimeCurrent(); - _counter += (unsigned short)_is_processed; - } - return _counter; - } - /* Strategy methods */ /** @@ -865,24 +832,39 @@ class EA { if (eparams.CheckFlag(EA_PARAM_FLAG_LOTSIZE_AUTO)) { // Auto calculate lot size for all strategies. Trade *_trade = trade.GetByKey(_Symbol); - _result &= _trade.ExecuteAction(TRADE_ACTION_CALC_LOT_SIZE); + _result &= _trade.Run(TRADE_ACTION_CALC_LOT_SIZE); Set(STRAT_PARAM_LS, _trade.Get(TRADE_PARAM_LOT_SIZE)); } return _result; } - /* Conditions and actions */ + /* Tasks methods */ /** - * Checks for EA condition. - * - * @param ENUM_EA_CONDITION _cond - * EA condition. - * @return - * Returns true when the condition is met. + * Add task. */ - bool CheckCondition(ENUM_EA_CONDITION _cond, DataParamEntry &_args[]) { - switch (_cond) { + bool AddTask(TaskEntry &_tentry) { + bool _is_valid = _tentry.IsValid(); + if (_is_valid) { + TaskObject _taskobj(_tentry, THIS_PTR, THIS_PTR); + tasks.Add(&_taskobj); + } + return _is_valid; + } + + /** + * Process tasks. + */ + void ProcessTasks() { tasks.Process(); } + + /* Tasks */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { case EA_COND_IS_ACTIVE: return estate.IsActive(); case EA_COND_IS_ENABLED: @@ -907,27 +889,31 @@ class EA { case EA_COND_ON_QUIT: return estate.IsOnQuit(); default: - logger.Error(StringFormat("Invalid EA condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); - return false; + GetLogger().Error(StringFormat("Invalid EA condition: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); + break; } + return _result; } - bool CheckCondition(ENUM_EA_CONDITION _cond) { - ARRAY(DataParamEntry, _args); - return EA::CheckCondition(_cond, _args); + + /** + * Gets a copy of structure. + */ + virtual DataParamEntry Get(const TaskGetterEntry &_entry) { + DataParamEntry _result; + switch (_entry.GetId()) { + default: + break; + } + return _result; } /** - * Execute EA action. - * - * @param ENUM_EA_ACTION _action - * EA action to execute. - * @return - * Returns true when the action has been executed successfully. + * Runs an action. */ - bool ExecuteAction(ENUM_EA_ACTION _action, DataParamEntry &_args[]) { - bool _result = true; - long arg_size = ArraySize(_args); - switch (_action) { + virtual bool Run(const TaskActionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { case EA_ACTION_DISABLE: estate.Enable(false); return true; @@ -937,46 +923,42 @@ class EA { case EA_ACTION_EXPORT_DATA: DataExport(); return true; - case EA_ACTION_STRATS_EXE_ACTION: + case EA_ACTION_STRATS_EXE_ACTION: { // Args: // 1st (i:0) - Strategy's enum action to execute. // 2nd (i:1) - Strategy's argument to pass. + TaskActionEntry _entry_strat = _entry; + _entry_strat.ArgRemove(0); for (DictStructIterator> iter_strat = strats.Begin(); iter_strat.IsValid(); ++iter_strat) { - DataParamEntry _sargs[]; - ArrayResize(_sargs, ArraySize(_args) - 1); - for (int i = 0; i < ArraySize(_sargs); i++) { - _sargs[i] = _args[i + 1]; - } Strategy *_strat = iter_strat.Value().Ptr(); - _result &= _strat.ExecuteAction((ENUM_STRATEGY_ACTION)_args[0].integer_value, _sargs); + + _result &= _strat.Run(_entry_strat); } return _result; + } case EA_ACTION_TASKS_CLEAN: // @todo - return tasks.Size() == 0; - default: - logger.Error(StringFormat("Invalid EA action: %s!", EnumToString(_action), __FUNCTION_LINE__)); + // return tasks.Size() == 0; + SetUserError(ERR_INVALID_PARAMETER); return false; + default: + GetLogger().Error(StringFormat("Invalid EA action: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); } return _result; } - bool ExecuteAction(ENUM_EA_ACTION _action) { - ARRAY(DataParamEntry, _args); - return EA::ExecuteAction(_action, _args); - } - bool ExecuteAction(ENUM_EA_ACTION _action, long _arg1) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - ArrayPushObject(_args, _param1); - return EA::ExecuteAction(_action, _args); - } - bool ExecuteAction(ENUM_EA_ACTION _action, long _arg1, long _arg2) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - DataParamEntry _param2 = _arg2; - ArrayPushObject(_args, _param1); - ArrayPushObject(_args, _param2); - return EA::ExecuteAction(_action, _args); + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const DataParamEntry &_entry_value) { + bool _result = false; + switch (_entry.GetId()) { + // _entry_value.GetValue() + default: + break; + } + return _result; } /* Getters */ diff --git a/EA.struct.h b/EA.struct.h index dbbe64063..5bed4845d 100644 --- a/EA.struct.h +++ b/EA.struct.h @@ -32,7 +32,7 @@ // Includes. #include "DateTime.mqh" -#include "Task.struct.h" +#include "Task/Task.struct.h" /* Defines EA config parameters. */ struct EAParams { @@ -225,7 +225,7 @@ struct EAState { public: // @todo: Move to protected. DateTime last_updated; // Last updated. protected: - unsigned int flags; // Action flags. + unsigned int flags; // TaskAction flags. unsigned int new_periods; // Started periods. public: /* Struct's enumerations */ diff --git a/Indicator.define.h b/Indicator.define.h index 6743cba67..d5056e91f 100644 --- a/Indicator.define.h +++ b/Indicator.define.h @@ -43,12 +43,14 @@ } \ SET_HANDLE; \ } \ - int _bars_calc = ::BarsCalculated(_handle); \ - if (GetLastError() > 0) { \ - return EMPTY_VALUE; \ - } else if (_bars_calc <= 2) { \ - SetUserError(ERR_USER_INVALID_BUFF_NUM); \ - return EMPTY_VALUE; \ + if (Terminal::IsVisualMode()) { \ + int _bars_calc = BarsCalculated(_handle); \ + if (GetLastError() > 0) { \ + return EMPTY_VALUE; \ + } else if (_bars_calc <= 2) { \ + SetUserError(ERR_USER_INVALID_BUFF_NUM); \ + return EMPTY_VALUE; \ + } \ } \ if (::CopyBuffer(_handle, _mode, _shift, 1, _res) < 0) { \ return ArraySize(_res) > 0 ? _res[0] : EMPTY_VALUE; \ diff --git a/Indicator.enum.h b/Indicator.enum.h index 1587ce083..375064b34 100644 --- a/Indicator.enum.h +++ b/Indicator.enum.h @@ -96,7 +96,7 @@ enum ENUM_INDICATOR_TYPE { INDI_OSMA, // OsMA INDI_PATTERN, // Pattern Detector INDI_PIVOT, // Pivot Detector - INDI_PRICE, // Price Indicator + INDI_PRICE, // Price INDI_PRICE_CHANNEL, // Price Channel INDI_PRICE_FEEDER, // Indicator which returns prices from custom array INDI_PRICE_VOLUME_TREND, // Price and Volume Trend @@ -114,7 +114,8 @@ enum ENUM_INDICATOR_TYPE { INDI_STOCHASTIC, // Stochastic Oscillator INDI_SVE_BB, // SVE Bollinger Bands INDI_TEMA, // Triple Exponential Moving Average - INDI_TMA_TRUE, /// Triangular Moving Average True + INDI_TICK, // Tick + INDI_TMA_TRUE, // Triangular Moving Average True INDI_TRIX, // Triple Exponential Moving Averages Oscillator INDI_ULTIMATE_OSCILLATOR, // Ultimate Oscillator INDI_VIDYA, // Variable Index Dynamic Average @@ -218,12 +219,11 @@ enum ENUM_APPLIED_VOLUME { VOLUME_TICK = 0, VOLUME_REAL = 1 }; enum INDICATOR_ENTRY_FLAGS { INDI_ENTRY_FLAG_NONE = 0 << 0, INDI_ENTRY_FLAG_IS_BITWISE = 1 << 0, - INDI_ENTRY_FLAG_IS_DOUBLE = 1 << 1, + INDI_ENTRY_FLAG_IS_DOUBLED = 1 << 1, // Type is doubled in size (e.g. double or long). INDI_ENTRY_FLAG_IS_EXPIRED = 1 << 2, - INDI_ENTRY_FLAG_IS_FLOAT = 1 << 3, - INDI_ENTRY_FLAG_IS_INT = 1 << 4, - INDI_ENTRY_FLAG_IS_LONG = 1 << 5, - INDI_ENTRY_FLAG_IS_PRICE = 1 << 6, - INDI_ENTRY_FLAG_IS_VALID = 1 << 7, - INDI_ENTRY_FLAG_INSUFFICIENT_DATA = 1 << 8, // Entry has missing value for that shift and probably won't ever have. + INDI_ENTRY_FLAG_IS_REAL = 1 << 3, // Type is real (float or double). + INDI_ENTRY_FLAG_IS_PRICE = 1 << 4, + INDI_ENTRY_FLAG_IS_UNSIGNED = 1 << 5, // Type is unsigned (uint or ulong). + INDI_ENTRY_FLAG_IS_VALID = 1 << 6, + INDI_ENTRY_FLAG_INSUFFICIENT_DATA = 1 << 7, // Entry has missing value for that shift and probably won't ever have. }; diff --git a/Indicator.mqh b/Indicator.mqh index 065234ef9..53103b7cf 100644 --- a/Indicator.mqh +++ b/Indicator.mqh @@ -59,6 +59,29 @@ class Indicator : public IndicatorBase { // Structs. TS iparams; + protected: + /* Protected methods */ + + /** + * It's called on class initialization. + */ + bool Init() { + ArrayResize(value_storages, iparams.GetMaxModes()); + switch (iparams.GetDataSourceType()) { + case IDATA_BUILTIN: + break; + case IDATA_ICUSTOM: + break; + case IDATA_INDICATOR: + if (!indi_src.IsSet()) { + // Indi_Price* _indi_price = Indi_Price::GetCached(GetSymbol(), GetTf(), iparams.GetShift()); + // SetDataSource(_indi_price, true, PRICE_OPEN); + } + break; + } + return InitDraw(); + } + public: /* Indicator enumerations */ @@ -77,7 +100,8 @@ class Indicator : public IndicatorBase { /** * Class constructor. */ - Indicator(const TS& _iparams, IndicatorBase* _indi_src = NULL, int _indi_mode = 0) : IndicatorBase(_indi_src) { + Indicator(const TS& _iparams, IndicatorBase* _indi_src = NULL, int _indi_mode = 0) + : IndicatorBase(_iparams.GetTf(), NULL) { iparams = _iparams; SetName(_iparams.name != "" ? _iparams.name : EnumToString(iparams.itype)); if (_indi_src != NULL) { @@ -85,7 +109,13 @@ class Indicator : public IndicatorBase { } Init(); } - Indicator(ENUM_INDICATOR_TYPE _itype, int _shift = 0, string _name = "") { + Indicator(const TS& _iparams, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT) : IndicatorBase(_tf) { + iparams = _iparams; + SetName(_iparams.name != "" ? _iparams.name : EnumToString(iparams.itype)); + Init(); + } + Indicator(ENUM_INDICATOR_TYPE _itype, ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0, string _name = "") + : IndicatorBase(_tf) { iparams.SetIndicatorType(_itype); iparams.SetShift(_shift); SetName(_name != "" ? _name : EnumToString(iparams.itype)); @@ -97,28 +127,6 @@ class Indicator : public IndicatorBase { */ ~Indicator() { DeinitDraw(); } - /* Init methods */ - - /** - * It's called on class initialization. - */ - bool Init() { - ArrayResize(value_storages, iparams.GetMaxModes()); - switch (iparams.GetDataSourceType()) { - case IDATA_BUILTIN: - break; - case IDATA_ICUSTOM: - break; - case IDATA_INDICATOR: - if (!indi_src.IsSet()) { - // Indi_Price* _indi_price = Indi_Price::GetCached(GetSymbol(), GetTf(), iparams.GetShift()); - // SetDataSource(_indi_price, true, PRICE_OPEN); - } - break; - } - return InitDraw(); - } - /** * Initialize indicator data drawing on custom data. */ @@ -155,8 +163,10 @@ class Indicator : public IndicatorBase { * Gets an indicator property flag. */ bool GetFlag(INDICATOR_ENTRY_FLAGS _prop, datetime _bar_time) { - IndicatorDataEntry _entry = GetEntry(_bar_time); - return _entry.CheckFlag(_prop); + // @fixme + // IndicatorDataEntry _entry = GetEntry(_bar_time); + // return _entry.CheckFlag(_prop); + return false; } /* Buffer methods */ @@ -881,15 +891,32 @@ class Indicator : public IndicatorBase { bool _result = true; _result &= _entry.timestamp > 0; _result &= _entry.GetSize() > 0; - if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLE)) { - _result &= !_entry.HasValue(DBL_MAX); - _result &= !_entry.HasValue(NULL); - } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_FLOAT)) { - _result &= !_entry.HasValue(FLT_MAX); - _result &= !_entry.HasValue(NULL); - } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_INT)) { - _result &= !_entry.HasValue(INT_MAX); - _result &= !_entry.HasValue(NULL); + if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_REAL)) { + if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLED)) { + _result &= !_entry.HasValue(DBL_MAX); + _result &= !_entry.HasValue(NULL); + } else { + _result &= !_entry.HasValue(FLT_MAX); + _result &= !_entry.HasValue(NULL); + } + } else { + if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_UNSIGNED)) { + if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLED)) { + _result &= !_entry.HasValue(ULONG_MAX); + _result &= !_entry.HasValue(NULL); + } else { + _result &= !_entry.HasValue(UINT_MAX); + _result &= !_entry.HasValue(NULL); + } + } else { + if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLED)) { + _result &= !_entry.HasValue(LONG_MAX); + _result &= !_entry.HasValue(NULL); + } else { + _result &= !_entry.HasValue(INT_MAX); + _result &= !_entry.HasValue(NULL); + } + } } return _result; } @@ -910,15 +937,44 @@ class Indicator : public IndicatorBase { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - virtual IndicatorDataEntry GetEntry(datetime _bar_time = 0) { - IndicatorDataEntry _entry = idata.GetByKey(_bar_time); + virtual IndicatorDataEntry GetEntry(int _shift = -1) { + ResetLastError(); + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + long _bar_time = 0; // GetBarTime(_ishift); // @fixme + IndicatorDataEntry _entry; // = idata.GetByKey(_bar_time); // @fixme if (_bar_time > 0 && !_entry.IsValid() && !_entry.CheckFlag(INDI_ENTRY_FLAG_INSUFFICIENT_DATA)) { _entry.Resize(iparams.GetMaxModes()); _entry.timestamp = _bar_time; for (int _mode = 0; _mode < (int)iparams.GetMaxModes(); _mode++) { - // _entry.values[_mode] = GetValue(_mode, _ishift); // @todo + switch (iparams.GetDataValueType()) { + case TYPE_BOOL: + case TYPE_CHAR: + case TYPE_INT: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_LONG: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_UINT: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_ULONG: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_DOUBLE: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_FLOAT: + _entry.values[_mode] = GetValue(_mode, _ishift); + break; + case TYPE_STRING: + case TYPE_UCHAR: + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } } - GetEntryAlter(_entry, _bar_time); + GetEntryAlter(_entry, _ishift); _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); if (_entry.IsValid()) { idata.Add(_entry, _bar_time); @@ -941,40 +997,9 @@ class Indicator : public IndicatorBase { * This method allows user to modify the struct entry before it's added to cache. * This method is called on GetEntry() right after values are set. */ - virtual void GetEntryAlter(IndicatorDataEntry& _entry, datetime _bar_time = 0) { - _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType())); + virtual void GetEntryAlter(IndicatorDataEntry& _entry, int _shift = -1) { + _entry.AddFlags(_entry.GetDataTypeFlags(iparams.GetDataValueType())); }; - - /** - * Returns the indicator's entry value for the given shift and mode. - * - * @see: DataParamEntry. - * - * @return - * Returns DataParamEntry struct filled with a single value. - */ - virtual DataParamEntry GetEntryValue(datetime _bar_time = 0, int _mode = 0) { - IndicatorDataEntry _entry = GetEntry(_bar_time); - DataParamEntry _value_entry; - if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLE)) { - _value_entry = _entry.GetValue(_mode); - } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_FLOAT)) { - _value_entry = _entry.GetValue(_mode); - } else if (_entry.CheckFlags(INDI_ENTRY_FLAG_IS_INT)) { - _value_entry = _entry.GetValue(_mode); - } - return _value_entry; - } - - /** - * Returns the indicator's value. - */ - virtual double GetValue(int _shift = -1, int _mode = 0) { - _shift = _shift >= 0 ? _shift : iparams.GetShift(); - istate.is_changed = false; - istate.is_ready = false; - return EMPTY_VALUE; - } }; #endif diff --git a/Indicator.struct.h b/Indicator.struct.h index b84968357..8891b8871 100644 --- a/Indicator.struct.h +++ b/Indicator.struct.h @@ -45,109 +45,149 @@ struct ChartParams; #include "SerializerNode.enum.h" #include "Storage/ValueStorage.indicator.h" +// Type-less value for IndicatorDataEntryValue structure. +union IndicatorDataEntryTypelessValue { + double vdbl; + float vflt; + int vint; + long vlong; +}; + +// Type-aware value for IndicatorDataEntry class. +struct IndicatorDataEntryValue { + unsigned char flags; + IndicatorDataEntryTypelessValue value; + + // Returns type of the value. + ENUM_DATATYPE GetDataType() { return (ENUM_DATATYPE)((flags & 0xF0) >> 4); } + + // Sets type of the value. + void SetDataType(ENUM_DATATYPE _type) { + // Clearing type. + flags &= 0x0F; + + // Setting type. + flags |= (unsigned char)_type << 4; + } + + // Union operators. + template + T operator*(const T _value) { + return Get() * _value; + } + template + T operator+(const T _value) { + return Get() + _value; + } + template + T operator-(const T _value) { + return Get() - _value; + } + template + T operator/(const T _value) { + return Get() / _value; + } + template + bool operator!=(const T _value) { + return Get() != _value; + } + template + bool operator<(const T _value) { + return Get() < _value; + } + template + bool operator<=(const T _value) { + return Get() <= _value; + } + template + bool operator==(const T _value) { + return Get() == _value; + } + template + bool operator>(const T _value) { + return Get() > _value; + } + template + bool operator>=(const T _value) { + return Get() >= _value; + } + template + void operator=(const T _value) { + Set(_value); + } + // Checkers. + template + bool IsGt(T _value) { + return Get() > _value; + } + template + bool IsLt(T _value) { + return Get() < _value; + } + // Getters. + double GetDbl() { return value.vdbl; } + float GetFloat() { return value.vflt; } + int GetInt() { return value.vint; } + long GetLong() { return value.vlong; } + template + void Get(T &_out) { + _out = Get(); + } + template + T Get() { + T _v; + Get(_v); + return _v; + } + void Get(double &_out) { _out = value.vdbl; } + void Get(float &_out) { _out = value.vflt; } + void Get(int &_out) { _out = value.vint; } + void Get(unsigned int &_out) { _out = (unsigned int)value.vint; } + void Get(long &_out) { _out = value.vlong; } + void Get(unsigned long &_out) { _out = (unsigned long)value.vint; } + // Setters. + template + void Set(T _value) { + Set(_value); + } + void Set(double _value) { + value.vdbl = _value; + SetDataType(TYPE_DOUBLE); + } + void Set(float _value) { + value.vflt = _value; + SetDataType(TYPE_FLOAT); + } + void Set(int _value) { + value.vint = _value; + SetDataType(TYPE_INT); + } + void Set(unsigned int _value) { + value.vint = (int)_value; + SetDataType(TYPE_UINT); + } + void Set(long _value) { + value.vlong = _value; + SetDataType(TYPE_LONG); + } + void Set(unsigned long _value) { + value.vlong = (long)_value; + SetDataType(TYPE_ULONG); + } + // Serializers. + // SERIALIZER_EMPTY_STUB + SerializerNodeType Serialize(Serializer &_s); + // To string + template + string ToString() { + return (string)Get(); + } +}; + /* Structure for indicator data entry. */ struct IndicatorDataEntry { long timestamp; // Timestamp of the entry's bar. unsigned short flags; // Indicator entry flags. - union IndicatorDataEntryValue { - double vdbl; - float vflt; - int vint; - long vlong; - // Union operators. - template - T operator*(const T _value) { - return Get() * _value; - } - template - T operator+(const T _value) { - return Get() + _value; - } - template - T operator-(const T _value) { - return Get() - _value; - } - template - T operator/(const T _value) { - return Get() / _value; - } - template - bool operator!=(const T _value) { - return Get() != _value; - } - template - bool operator<(const T _value) { - return Get() < _value; - } - template - bool operator<=(const T _value) { - return Get() <= _value; - } - template - bool operator==(const T _value) { - return Get() == _value; - } - template - bool operator>(const T _value) { - return Get() > _value; - } - template - bool operator>=(const T _value) { - return Get() >= _value; - } - template - void operator=(const T _value) { - Set(_value); - } - // Checkers. - template - bool IsGt(T _value) { - return Get() > _value; - } - template - bool IsLt(T _value) { - return Get() < _value; - } - // Getters. - double GetDbl() { return vdbl; } - float GetFloat() { return vflt; } - int GetInt() { return vint; } - long GetLong() { return vlong; } - template - void Get(T &_out) { - _out = Get(); - } - template - T Get() { - T _v; - Get(_v); - return _v; - } - void Get(double &_out) { _out = vdbl; } - void Get(float &_out) { _out = vflt; } - void Get(int &_out) { _out = vint; } - void Get(long &_out) { _out = vlong; } - // Setters. - template - void Set(T _value) { - Set(_value); - } - void Set(double _value) { vdbl = _value; } - void Set(float _value) { vflt = _value; } - void Set(int _value) { vint = _value; } - void Set(unsigned int _value) { vint = (int)_value; } - void Set(long _value) { vlong = _value; } - void Set(unsigned long _value) { vlong = (long)_value; } - // Serializers. - // SERIALIZER_EMPTY_STUB - SerializerNodeType Serialize(Serializer &_s); - // To string - template - string ToString() { - return (string)Get(); - } - }; - ARRAY(IndicatorDataEntryValue, values); // Constructors. @@ -288,30 +328,35 @@ struct IndicatorDataEntry { int GetMonth() { return DateTimeStatic::Month(timestamp); } int GetYear() { return DateTimeStatic::Year(timestamp); } long GetTime() { return timestamp; }; - ENUM_DATATYPE GetDataType() { - if (CheckFlags(INDI_ENTRY_FLAG_IS_FLOAT)) { - return TYPE_FLOAT; - } else if (CheckFlags(INDI_ENTRY_FLAG_IS_INT)) { - return TYPE_INT; - } else if (CheckFlags(INDI_ENTRY_FLAG_IS_LONG)) { - return TYPE_LONG; - } - return TYPE_DOUBLE; - } - INDICATOR_ENTRY_FLAGS GetDataTypeFlag(ENUM_DATATYPE _dt) { + ENUM_DATATYPE GetDataType(int _mode) { return values[_mode].GetDataType(); } + ushort GetDataTypeFlags(ENUM_DATATYPE _dt) { switch (_dt) { - case TYPE_DOUBLE: - return INDI_ENTRY_FLAG_IS_DOUBLE; - case TYPE_FLOAT: - return INDI_ENTRY_FLAG_IS_FLOAT; + case TYPE_BOOL: + case TYPE_CHAR: + SetUserError(ERR_INVALID_PARAMETER); + break; case TYPE_INT: - return INDI_ENTRY_FLAG_IS_INT; + return INDI_ENTRY_FLAG_NONE; case TYPE_LONG: - return INDI_ENTRY_FLAG_IS_LONG; + return INDI_ENTRY_FLAG_IS_DOUBLED; + case TYPE_UINT: + return INDI_ENTRY_FLAG_IS_UNSIGNED; + case TYPE_ULONG: + return INDI_ENTRY_FLAG_IS_UNSIGNED | INDI_ENTRY_FLAG_IS_DOUBLED; + case TYPE_DOUBLE: + return INDI_ENTRY_FLAG_IS_REAL | INDI_ENTRY_FLAG_IS_DOUBLED; + case TYPE_FLOAT: + return INDI_ENTRY_FLAG_IS_REAL; + case TYPE_STRING: + case TYPE_UCHAR: + SetUserError(ERR_INVALID_PARAMETER); + break; default: + SetUserError(ERR_INVALID_PARAMETER); break; } - return (INDICATOR_ENTRY_FLAGS)0; + SetUserError(ERR_INVALID_PARAMETER); + return INDI_ENTRY_FLAG_NONE; } // Setters. bool Resize(int _size = 0) { return _size > 0 ? ArrayResize(values, _size) > 0 : true; } @@ -334,7 +379,12 @@ struct IndicatorDataEntry { // State checkers. bool IsValid() { return CheckFlags(INDI_ENTRY_FLAG_IS_VALID); } // Serializers. - void SerializeStub(int _n1 = 1, int _n2 = 1, int _n3 = 1, int _n4 = 1, int _n5 = 1) { ArrayResize(values, _n1); } + void SerializeStub(int _n1 = 1, int _n2 = 1, int _n3 = 1, int _n4 = 1, int _n5 = 1) { + ArrayResize(values, _n1); + for (int i = 0; i < _n1; ++i) { + values[i] = (int)1; + } + } SerializerNodeType Serialize(Serializer &_s); template string ToCSV() { @@ -471,7 +521,11 @@ struct IndicatorParams { case TYPE_STRING: case TYPE_UCHAR: return (T)param.string_value; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; } + SetUserError(ERR_INVALID_PARAMETER); return (T)WRONG_VALUE; } /* Setters */ diff --git a/Indicator.struct.serialize.h b/Indicator.struct.serialize.h index d7bd36a23..98e262c60 100644 --- a/Indicator.struct.serialize.h +++ b/Indicator.struct.serialize.h @@ -40,43 +40,59 @@ SerializerNodeType IndicatorDataEntry::Serialize(Serializer &_s) { // this work? _s.Pass(THIS_REF, (string)i, GetEntry(i), SERIALIZER_FIELD_FLAG_DYNAMIC | // SERIALIZER_FIELD_FLAG_FEATURE); // Can this work? - if (CheckFlags(INDI_ENTRY_FLAG_IS_DOUBLE)) { - _s.Pass(THIS_REF, (string)i, values[i].vdbl, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); - } else if (CheckFlags(INDI_ENTRY_FLAG_IS_FLOAT)) { - _s.Pass(THIS_REF, (string)i, values[i].vflt, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); - } else if (CheckFlags(INDI_ENTRY_FLAG_IS_INT)) { - if (!CheckFlags(INDI_ENTRY_FLAG_IS_BITWISE)) { - _s.Pass(THIS_REF, (string)i, values[i].vint, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); - } else { - // Split for each bit and pass 0 or 1. - for (int j = 0; j < sizeof(int) * 8; ++j) { - int _value = (values[i].vint & (1 << j)) != 0; - _s.Pass(THIS_REF, StringFormat("%d@%d", i, j), _value, SERIALIZER_FIELD_FLAG_FEATURE); + switch (values[i].GetDataType()) { + case TYPE_DOUBLE: + _s.Pass(THIS_REF, (string)i, values[i].value.vdbl, + SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); + break; + case TYPE_FLOAT: + _s.Pass(THIS_REF, (string)i, values[i].value.vflt, + SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); + break; + case TYPE_INT: + case TYPE_UINT: + if (CheckFlags(INDI_ENTRY_FLAG_IS_BITWISE)) { + // Split for each bit and pass 0 or 1. + for (int j = 0; j < sizeof(int) * 8; ++j) { + int _value = (values[i].value.vint & (1 << j)) != 0; + _s.Pass(THIS_REF, StringFormat("%d@%d", i, j), _value, SERIALIZER_FIELD_FLAG_FEATURE); + } + } else { + _s.Pass(THIS_REF, (string)i, values[i].value.vint, + SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); } - } - } else if (CheckFlags(INDI_ENTRY_FLAG_IS_LONG)) { - if (!CheckFlags(INDI_ENTRY_FLAG_IS_BITWISE)) { - _s.Pass(THIS_REF, (string)i, values[i].vlong, SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); - } else { - // Split for each bit and pass 0 or 1. - /* @fixme: j, j already defined. - for (int j = 0; j < sizeof(int) * 8; ++j) { - int _value = (values[i].vlong & (1 << j)) != 0; - _s.Pass(THIS_REF, StringFormat("%d@%d", i, j), _value, SERIALIZER_FIELD_FLAG_FEATURE); + break; + case TYPE_LONG: + case TYPE_ULONG: + if (CheckFlags(INDI_ENTRY_FLAG_IS_BITWISE)) { + // Split for each bit and pass 0 or 1. + /* @fixme: j, j already defined. + for (int j = 0; j < sizeof(int) * 8; ++j) { + int _value = (values[i].vlong & (1 << j)) != 0; + _s.Pass(THIS_REF, StringFormat("%d@%d", i, j), _value, SERIALIZER_FIELD_FLAG_FEATURE); + } + */ + SetUserError(ERR_INVALID_PARAMETER); + } else { + _s.Pass(THIS_REF, (string)i, values[i].value.vlong, + SERIALIZER_FIELD_FLAG_DYNAMIC | SERIALIZER_FIELD_FLAG_FEATURE); } - */ - } + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; } } return SerializerNodeObject; } /* Method to serialize IndicatorDataEntry's IndicatorDataEntryValue union. */ -SerializerNodeType IndicatorDataEntry::IndicatorDataEntryValue::Serialize(Serializer &_s) { - _s.Pass(THIS_REF, "vdbl", vdbl); - _s.Pass(THIS_REF, "vflt", vflt); - _s.Pass(THIS_REF, "vint", vint); - _s.Pass(THIS_REF, "vlong", vlong); +SerializerNodeType IndicatorDataEntryValue::Serialize(Serializer &_s) { + _s.Pass(THIS_REF, "flags", flags); + _s.Pass(THIS_REF, "vdbl", value.vdbl); + _s.Pass(THIS_REF, "vflt", value.vflt); + _s.Pass(THIS_REF, "vint", value.vint); + _s.Pass(THIS_REF, "vlong", value.vlong); return SerializerNodeObject; }; diff --git a/IndicatorBase.h b/IndicatorBase.h index df0cb4388..6361d3a8b 100644 --- a/IndicatorBase.h +++ b/IndicatorBase.h @@ -45,7 +45,6 @@ class Chart; #include "Indicator.struct.h" #include "Indicator.struct.serialize.h" #include "Indicator.struct.signal.h" -#include "Math.h" #include "Object.mqh" #include "Refs.mqh" #include "Serializer.mqh" @@ -80,6 +79,17 @@ double iCustom5(string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C ICUSTOM_DEF(_handlers.Set(_key, _handle), COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h COMMA _i COMMA _j); } +template +double iCustom5(string _symbol, ENUM_TIMEFRAMES _tf, string _name, A _a, B _b, C _c, D _d, E _e, F _f, G _g, H _h, I _i, + J _j, K _k, L _l, M _m, int _mode, int _shift) { + ResetLastError(); + static Dict _handlers; + string _key = Util::MakeKey(_symbol, (string)_tf, _name, _a, _b, _c, _d, _e, _f, _g, _h, _i, _j); + int _handle = _handlers.GetByKey(_key); + ICUSTOM_DEF(_handlers.Set(_key, _handle), COMMA _a COMMA _b COMMA _c COMMA _d COMMA _e COMMA _f COMMA _g COMMA _h + COMMA _i COMMA _j COMMA _k COMMA _l COMMA _m); +} #endif /** @@ -118,7 +128,36 @@ class IndicatorBase : public Object { /** * Class constructor. */ - IndicatorBase(IndicatorBase* _indi_src = NULL) : indi_src(_indi_src), is_fed(false) {} + IndicatorBase() : indi_src(NULL) { + calc_start_bar = 0; + is_fed = false; + } + + /** + * Class constructor. + */ + IndicatorBase(ChartParams& _cparams) : indi_src(NULL) { + calc_start_bar = 0; + is_fed = false; + } + + /** + * Class constructor. + */ + IndicatorBase(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, string _symbol = NULL) { + calc_start_bar = 0; + is_fed = false; + indi_src = NULL; + } + + /** + * Class constructor. + */ + IndicatorBase(ENUM_TIMEFRAMES_INDEX _tfi, string _symbol = NULL) { + calc_start_bar = 0; + is_fed = false; + indi_src = NULL; + } /** * Class deconstructor. @@ -813,16 +852,11 @@ class IndicatorBase : public Object { return value_storages[_mode]; } - /** - * Returns indicator value for a given shift and mode. - */ template - T GetValue(int _shift = 0, int _mode = -1) { - T _result; - int _index = _mode != -1 ? _mode : GetDataSourceMode(); - GetEntry(_shift).values[_index].Get(_result); - ResetLastError(); - return _result; + T GetValue(int _shift = 0, int _mode = 0) { + T _out; + GetEntryValue(_shift, _mode).Get(_out); + return _out; } /** @@ -914,7 +948,7 @@ class IndicatorBase : public Object { /** * Returns the indicator's entry value. */ - virtual DataParamEntry GetEntryValue(datetime _bar_time = 0, int _mode = 0) = NULL; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) = NULL; /** * Returns indicator value for a given shift and mode. @@ -958,7 +992,6 @@ class IndicatorBase : public Object { IndicatorDataEntry _stub_entry; _stub_entry.AddFlags(_entry.GetFlags()); SerializerConverter _stub = SerializerConverter::MakeStubObject(_stub_entry, _serializer_flags, _entry.GetSize()); - return SerializerConverter::FromObject(_entry, _serializer_flags).ToString(0, &_stub); } @@ -967,23 +1000,24 @@ class IndicatorBase : public Object { int _bars = Bars(GetSymbol(), GetTf()); if (!is_fed) { - calc_start_bar = 0; - // Calculating start_bar. - for (int i = 0; i < _bars; ++i) { - // Iterating from the oldest. - IndicatorDataEntry _entry = GetEntry(_bars - i - 1); + for (; calc_start_bar < _bars; ++calc_start_bar) { + // Iterating from the oldest or previously iterated. + IndicatorDataEntry _entry = GetEntry(_bars - calc_start_bar - 1); if (_entry.IsValid()) { // From this point we assume that future entries will be all valid. - calc_start_bar = i; is_fed = true; - return _bars - calc_start_bar; } } } + if (!is_fed) { + Print("Can't find valid bars for ", GetFullName()); + return 0; + } + // Assuming all entries are calculated (even if have invalid values). return _bars; } diff --git a/Indicators/Bitwise/Indi_Candle.mqh b/Indicators/Bitwise/Indi_Candle.mqh index d1bc29ff2..a9b94aea1 100644 --- a/Indicators/Bitwise/Indi_Candle.mqh +++ b/Indicators/Bitwise/Indi_Candle.mqh @@ -66,14 +66,15 @@ class Indi_Candle : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); BarOHLC _ohlcs[1]; switch (iparams.idstype) { case IDATA_BUILTIN: // In this mode, price is fetched from chart. - _ohlcs[0] = Chart::GetOHLC(_shift); + _ohlcs[0] = Chart::GetOHLC(_ishift); break; case IDATA_INDICATOR: // In this mode, price is fetched from given indicator. Such indicator @@ -92,10 +93,10 @@ class Indi_Candle : public Indicator { break; } - _ohlcs[0].open = GetDataSource().GetValue(_shift, PRICE_OPEN); - _ohlcs[0].high = GetDataSource().GetValue(_shift, PRICE_HIGH); - _ohlcs[0].low = GetDataSource().GetValue(_shift, PRICE_LOW); - _ohlcs[0].close = GetDataSource().GetValue(_shift, PRICE_CLOSE); + _ohlcs[0].open = GetDataSource().GetValue(_ishift, PRICE_OPEN); + _ohlcs[0].high = GetDataSource().GetValue(_ishift, PRICE_HIGH); + _ohlcs[0].low = GetDataSource().GetValue(_ishift, PRICE_LOW); + _ohlcs[0].close = GetDataSource().GetValue(_ishift, PRICE_CLOSE); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Bitwise/Indi_Pattern.mqh b/Indicators/Bitwise/Indi_Pattern.mqh index 33000b0d4..f9e014ec0 100644 --- a/Indicators/Bitwise/Indi_Pattern.mqh +++ b/Indicators/Bitwise/Indi_Pattern.mqh @@ -32,8 +32,8 @@ // Structs. struct IndiPatternParams : IndicatorParams { // Struct constructor. - IndiPatternParams(int _shift = 0) : IndicatorParams(INDI_PATTERN, 5, TYPE_INT) { - SetDataValueType(TYPE_INT); + IndiPatternParams(int _shift = 0) : IndicatorParams(INDI_PATTERN, 5, TYPE_UINT) { + SetDataValueType(TYPE_UINT); SetDataValueRange(IDATA_RANGE_BITWISE); shift = _shift; }; @@ -57,15 +57,16 @@ class Indi_Pattern : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { int i; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); BarOHLC _ohlcs[8]; switch (iparams.idstype) { case IDATA_BUILTIN: // In this mode, price is fetched from chart. for (i = 0; i < iparams.GetMaxModes(); ++i) { - _ohlcs[i] = Chart::GetOHLC(_shift + i); + _ohlcs[i] = Chart::GetOHLC(_ishift + i); if (!_ohlcs[i].IsValid()) { // Return empty entry on invalid candles. return WRONG_VALUE; @@ -90,10 +91,10 @@ class Indi_Pattern : public Indicator { } for (i = 0; i < iparams.GetMaxModes(); ++i) { - _ohlcs[i].open = GetDataSource().GetValue(_shift + i, PRICE_OPEN); - _ohlcs[i].high = GetDataSource().GetValue(_shift + i, PRICE_HIGH); - _ohlcs[i].low = GetDataSource().GetValue(_shift + i, PRICE_LOW); - _ohlcs[i].close = GetDataSource().GetValue(_shift + i, PRICE_CLOSE); + _ohlcs[i].open = GetDataSource().GetValue(_ishift + i, PRICE_OPEN); + _ohlcs[i].high = GetDataSource().GetValue(_ishift + i, PRICE_HIGH); + _ohlcs[i].low = GetDataSource().GetValue(_ishift + i, PRICE_LOW); + _ohlcs[i].close = GetDataSource().GetValue(_ishift + i, PRICE_CLOSE); if (!_ohlcs[i].IsValid()) { // Return empty entry on invalid candles. return WRONG_VALUE; diff --git a/Indicators/Indi_AC.mqh b/Indicators/Indi_AC.mqh index 96befe0cf..b6e10fca1 100644 --- a/Indicators/Indi_AC.mqh +++ b/Indicators/Indi_AC.mqh @@ -39,6 +39,11 @@ struct IndiACParams : IndicatorParams { SetDataValueRange(IDATA_RANGE_MIXED); SetCustomIndicatorName("Examples\\Accelerator"); shift = _shift; + switch (idstype) { + case IDATA_ICUSTOM: + SetMaxModes(2); + break; + } }; IndiACParams(IndiACParams &_params, ENUM_TIMEFRAMES _tf) { THIS_REF = _params; @@ -100,15 +105,16 @@ class Indi_AC : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { - double _value = EMPTY_VALUE; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + IndicatorDataEntryValue _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_AC::iAC(_Symbol, GetTf(), _shift, THIS_PTR); + _value = Indi_AC::iAC(GetSymbol(), GetTf(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AD.mqh b/Indicators/Indi_AD.mqh index aaa0c8658..51ea677cf 100644 --- a/Indicators/Indi_AD.mqh +++ b/Indicators/Indi_AD.mqh @@ -101,15 +101,16 @@ class Indi_AD : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_AD::iAD(_Symbol, GetTf(), _shift, THIS_PTR); + _value = Indi_AD::iAD(GetSymbol(), GetTf(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ADX.mqh b/Indicators/Indi_ADX.mqh index d02d11225..752805800 100644 --- a/Indicators/Indi_ADX.mqh +++ b/Indicators/Indi_ADX.mqh @@ -115,19 +115,21 @@ class Indi_ADX : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_ADX::iADX(_Symbol, GetTf(), GetPeriod(), GetAppliedPrice(), _mode, _shift, THIS_PTR); + _value = Indi_ADX::iADX(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); + break; } return _value; } diff --git a/Indicators/Indi_ADXW.mqh b/Indicators/Indi_ADXW.mqh index 0a4be9716..efd22ce2e 100644 --- a/Indicators/Indi_ADXW.mqh +++ b/Indicators/Indi_ADXW.mqh @@ -215,16 +215,17 @@ class Indi_ADXW : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = LINE_MAIN_ADX, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN_ADX, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_ADXW::iADXWilder(_Symbol, GetTf(), GetPeriod(), _mode, _shift, THIS_PTR); + _value = Indi_ADXW::iADXWilder(GetSymbol(), GetTf(), GetPeriod(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AMA.mqh b/Indicators/Indi_AMA.mqh index 0e48422d2..58a8bee10 100644 --- a/Indicators/Indi_AMA.mqh +++ b/Indicators/Indi_AMA.mqh @@ -199,16 +199,17 @@ class Indi_AMA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_AMA::iAMA(_Symbol, GetTf(), /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(), GetAMAShift(), - GetAppliedPrice() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_AMA::iAMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetFastPeriod(), GetSlowPeriod(), + GetAMAShift(), GetAppliedPrice() /*]*/, _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetFastPeriod(), GetSlowPeriod(), GetAMAShift() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + GetFastPeriod(), GetSlowPeriod(), GetAMAShift() /*]*/, _mode, _ishift); break; case IDATA_INDICATOR: diff --git a/Indicators/Indi_AO.mqh b/Indicators/Indi_AO.mqh index cbda37d66..a88552202 100644 --- a/Indicators/Indi_AO.mqh +++ b/Indicators/Indi_AO.mqh @@ -103,15 +103,16 @@ class Indi_AO : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_AO::iAO(_Symbol, GetTf(), _shift, _mode, THIS_PTR); + _value = Indi_AO::iAO(GetSymbol(), GetTf(), _ishift, _mode, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ASI.mqh b/Indicators/Indi_ASI.mqh index b15a0505b..2e0cf341b 100644 --- a/Indicators/Indi_ASI.mqh +++ b/Indicators/Indi_ASI.mqh @@ -155,18 +155,19 @@ class Indi_ASI : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetMaximumPriceChanging() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), + /*[*/ GetMaximumPriceChanging() /*]*/, 0, _ishift); break; case IDATA_ONCALCULATE: { - INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(_Symbol, GetTf(), + INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_LONG(GetSymbol(), GetTf(), Util::MakeKey("Indi_ASI", GetMaximumPriceChanging())); _value = - iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, GetMaximumPriceChanging(), _mode, _shift, _cache); + iASIOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_LONG, GetMaximumPriceChanging(), _mode, _ishift, _cache); break; } default: diff --git a/Indicators/Indi_ATR.mqh b/Indicators/Indi_ATR.mqh index 304edb113..c74f1edd6 100644 --- a/Indicators/Indi_ATR.mqh +++ b/Indicators/Indi_ATR.mqh @@ -103,15 +103,16 @@ class Indi_ATR : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_ATR::iATR(_Symbol, GetTf(), GetPeriod(), _shift, THIS_PTR); + _value = Indi_ATR::iATR(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Alligator.mqh b/Indicators/Indi_Alligator.mqh index 22fe7b7de..3cef8086f 100644 --- a/Indicators/Indi_Alligator.mqh +++ b/Indicators/Indi_Alligator.mqh @@ -162,20 +162,21 @@ class Indi_Alligator : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { + double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); #ifdef __MQL4__ if (_mode == 0) { // In MQL4 mode 0 should be treated as mode 1 as Alligator buffers starts from index 1. - return GetValue((ENUM_ALLIGATOR_LINE)1, _shift); + return GetEntryValue((ENUM_ALLIGATOR_LINE)1, _ishift); } #endif - double _value = EMPTY_VALUE; switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; _value = Indi_Alligator::iAlligator(_Symbol, GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), - GetAppliedPrice(), (ENUM_ALLIGATOR_LINE)_mode, _shift, THIS_PTR); + GetAppliedPrice(), (ENUM_ALLIGATOR_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ @@ -183,7 +184,7 @@ class Indi_Alligator : public Indicator { GetLipsShift(), GetMAMethod(), GetAppliedPrice() /*]*/, - _mode, _shift); + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_AppliedPrice.mqh b/Indicators/Indi_AppliedPrice.mqh index 4d6d1e443..379596331 100644 --- a/Indicators/Indi_AppliedPrice.mqh +++ b/Indicators/Indi_AppliedPrice.mqh @@ -74,15 +74,16 @@ class Indi_AppliedPrice : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_INDICATOR: if (HasDataSource()) { // Future validation of indi_src will check if we set mode for source indicator // (e.g. for applied price of Indi_Price). iparams.SetDataSourceMode(GetAppliedPrice()); - _value = Indi_AppliedPrice::iAppliedPriceOnIndicator(GetDataSource(), GetAppliedPrice(), _shift); + _value = Indi_AppliedPrice::iAppliedPriceOnIndicator(GetDataSource(), GetAppliedPrice(), _ishift); } break; default: diff --git a/Indicators/Indi_BWMFI.mqh b/Indicators/Indi_BWMFI.mqh index 4b0318366..cc8b55e08 100644 --- a/Indicators/Indi_BWMFI.mqh +++ b/Indicators/Indi_BWMFI.mqh @@ -114,16 +114,17 @@ class Indi_BWMFI : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = BWMFI_BUFFER, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = BWMFI_BUFFER, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_BWMFI::iBWMFI(_Symbol, GetTf(), _shift, (ENUM_BWMFI_BUFFER)_mode, THIS_PTR); + _value = Indi_BWMFI::iBWMFI(GetSymbol(), GetTf(), _ishift, (ENUM_BWMFI_BUFFER)_mode, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); @@ -140,7 +141,7 @@ class Indi_BWMFI : public Indicator { #ifdef __MQL4__ // @see: https://en.wikipedia.org/wiki/Market_facilitation_index bool _vol_up = GetVolume(_shift) > GetVolume(_shift); - bool _val_up = GetValue(BWMFI_BUFFER, _shift) > GetValue(BWMFI_BUFFER, _shift); + bool _val_up = GetValue(BWMFI_BUFFER, _shift) > GetValue(BWMFI_BUFFER, _shift); double _histcolor = EMPTY_VALUE; switch (_vol_up) { case true: diff --git a/Indicators/Indi_BWZT.mqh b/Indicators/Indi_BWZT.mqh index cbfed6264..65d7cb881 100644 --- a/Indicators/Indi_BWZT.mqh +++ b/Indicators/Indi_BWZT.mqh @@ -172,14 +172,15 @@ class Indi_BWZT : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_BWZT::iBWZT(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_BWZT::iBWZT(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Bands.mqh b/Indicators/Indi_Bands.mqh index 6e15f6d9e..0ff6a92e1 100644 --- a/Indicators/Indi_Bands.mqh +++ b/Indicators/Indi_Bands.mqh @@ -239,22 +239,23 @@ class Indi_Bands : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual double GetValue(int _mode = BAND_BASE, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = BAND_BASE, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Bands::iBands(_Symbol, GetTf(), GetPeriod(), GetDeviation(), GetBandsShift(), GetAppliedPrice(), - (ENUM_BANDS_LINE)_mode, _shift, THIS_PTR); + _value = Indi_Bands::iBands(GetSymbol(), GetTf(), GetPeriod(), GetDeviation(), GetBandsShift(), + GetAppliedPrice(), (ENUM_BANDS_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), - GetBandsShift(), GetDeviation(), GetAppliedPrice() /* ] */, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), + GetBandsShift(), GetDeviation(), GetAppliedPrice() /* ] */, _mode, _ishift); break; case IDATA_INDICATOR: // Calculating bands value from specified indicator. - _value = Indi_Bands::iBandsOnIndicator(GetDataSource(), _Symbol, GetTf(), GetPeriod(), GetDeviation(), - GetBandsShift(), (ENUM_BANDS_LINE)_mode, _shift, THIS_PTR); + _value = Indi_Bands::iBandsOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetDeviation(), + GetBandsShift(), (ENUM_BANDS_LINE)_mode, _ishift, THIS_PTR); break; } return _value; diff --git a/Indicators/Indi_BearsPower.mqh b/Indicators/Indi_BearsPower.mqh index 57eae631e..1b9721606 100644 --- a/Indicators/Indi_BearsPower.mqh +++ b/Indicators/Indi_BearsPower.mqh @@ -104,16 +104,17 @@ class Indi_BearsPower : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = _value = iBearsPower(_Symbol, GetTf(), GetPeriod(), GetAppliedPrice(), _shift, THIS_PTR); + _value = _value = iBearsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_BullsPower.mqh b/Indicators/Indi_BullsPower.mqh index 79244878d..1741f8da7 100644 --- a/Indicators/Indi_BullsPower.mqh +++ b/Indicators/Indi_BullsPower.mqh @@ -104,16 +104,17 @@ class Indi_BullsPower : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = iBullsPower(_Symbol, GetTf(), GetPeriod(), GetAppliedPrice(), _shift, THIS_PTR); + _value = iBullsPower(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /**/ GetPeriod() /**/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetPeriod() /**/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CCI.mqh b/Indicators/Indi_CCI.mqh index 721b034f9..c32d4c191 100644 --- a/Indicators/Indi_CCI.mqh +++ b/Indicators/Indi_CCI.mqh @@ -145,25 +145,26 @@ class Indi_CCI : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); double _value = EMPTY_VALUE; switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = - Indi_CCI::iCCI(_Symbol, GetTf(), GetPeriod(), GetAppliedPrice(), _shift /* + iparams.shift*/, THIS_PTR); + _value = Indi_CCI::iCCI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), _ishift /* + iparams.shift*/, + THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), - GetAppliedPrice() /* ] */, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), + GetAppliedPrice() /* ] */, 0, _ishift); break; case IDATA_INDICATOR: ValidateSelectedDataSource(); // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = Indi_CCI::iCCIOnIndicator(GetDataSource(), _Symbol, GetTf(), GetPeriod(), GetDataSourceMode(), - _shift /* + iparams.shift*/); + _value = Indi_CCI::iCCIOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetDataSourceMode(), + _ishift /* + iparams.shift*/); break; } return _value; diff --git a/Indicators/Indi_CHO.mqh b/Indicators/Indi_CHO.mqh index 4145f26cf..1cf24699e 100644 --- a/Indicators/Indi_CHO.mqh +++ b/Indicators/Indi_CHO.mqh @@ -166,16 +166,17 @@ class Indi_CHO : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_CHO::iChaikin(_Symbol, GetTf(), /*[*/ GetSlowMA(), GetFastMA(), GetSmoothMethod(), - GetInputVolume() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_CHO::iChaikin(GetSymbol(), GetTf(), /*[*/ GetSlowMA(), GetFastMA(), GetSmoothMethod(), + GetInputVolume() /*]*/, _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetFastMA(), - GetSlowMA(), GetSmoothMethod(), GetInputVolume() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetFastMA(), + GetSlowMA(), GetSmoothMethod(), GetInputVolume() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CHV.mqh b/Indicators/Indi_CHV.mqh index d31127d65..d3f97fd42 100644 --- a/Indicators/Indi_CHV.mqh +++ b/Indicators/Indi_CHV.mqh @@ -162,16 +162,17 @@ class Indi_CHV : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_CHV::iCHV(_Symbol, GetTf(), /*[*/ GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod() /*]*/, - _mode, _shift, THIS_PTR); + _value = Indi_CHV::iCHV(GetSymbol(), GetTf(), /*[*/ GetSmoothPeriod(), GetCHVPeriod(), GetSmoothMethod() /*]*/, + _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), - GetCHVPeriod(), GetSmoothMethod() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), + GetCHVPeriod(), GetSmoothMethod() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorBars.mqh b/Indicators/Indi_ColorBars.mqh index f9a82dfa5..f89773c19 100644 --- a/Indicators/Indi_ColorBars.mqh +++ b/Indicators/Indi_ColorBars.mqh @@ -113,14 +113,15 @@ class Indi_ColorBars : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_ColorBars::iColorBars(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_ColorBars::iColorBars(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorCandlesDaily.mqh b/Indicators/Indi_ColorCandlesDaily.mqh index bfc44cefc..b8c4b851c 100644 --- a/Indicators/Indi_ColorCandlesDaily.mqh +++ b/Indicators/Indi_ColorCandlesDaily.mqh @@ -110,14 +110,15 @@ class Indi_ColorCandlesDaily : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_ColorCandlesDaily::iCCD(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_ColorCandlesDaily::iCCD(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ColorLine.mqh b/Indicators/Indi_ColorLine.mqh index 25b17781c..d3dd60ed7 100644 --- a/Indicators/Indi_ColorLine.mqh +++ b/Indicators/Indi_ColorLine.mqh @@ -172,14 +172,15 @@ class Indi_ColorLine : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_ColorLine::iColorLine(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_ColorLine::iColorLine(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_CustomMovingAverage.mqh b/Indicators/Indi_CustomMovingAverage.mqh index 22aefae8b..7d83ee7dc 100644 --- a/Indicators/Indi_CustomMovingAverage.mqh +++ b/Indicators/Indi_CustomMovingAverage.mqh @@ -67,12 +67,13 @@ class Indi_CustomMovingAverage : public Indicator /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), - GetSmoothShift(), GetSmoothMethod() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetSmoothPeriod(), + GetSmoothShift(), GetSmoothMethod() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_DEMA.mqh b/Indicators/Indi_DEMA.mqh index 291bf8bee..420034607 100644 --- a/Indicators/Indi_DEMA.mqh +++ b/Indicators/Indi_DEMA.mqh @@ -166,26 +166,27 @@ class Indi_DEMA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: // We're getting DEMA from Price indicator. istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_DEMA::iDEMA(_Symbol, GetTf(), GetPeriod(), GetMAShift(), GetAppliedPrice(), _shift, _mode, + _value = Indi_DEMA::iDEMA(GetSymbol(), GetTf(), GetPeriod(), GetMAShift(), GetAppliedPrice(), _ishift, _mode, GetPointer(this)); break; case IDATA_ICUSTOM: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /*[*/ GetPeriod(), GetMAShift(), - GetAppliedPrice() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /*[*/ GetPeriod(), GetMAShift(), + GetAppliedPrice() /*]*/, _mode, _ishift); break; case IDATA_INDICATOR: // Calculating DEMA value from specified indicator. _value = Indi_DEMA::iDEMAOnIndicator(GetCache(), GetDataSource(), GetDataSourceMode(), GetPeriod(), - GetMAShift(), _shift); + GetMAShift(), _ishift); break; } return _value; diff --git a/Indicators/Indi_DeMarker.mqh b/Indicators/Indi_DeMarker.mqh index 299687be0..98524dcdb 100644 --- a/Indicators/Indi_DeMarker.mqh +++ b/Indicators/Indi_DeMarker.mqh @@ -102,16 +102,17 @@ class Indi_DeMarker : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = _value = Indi_DeMarker::iDeMarker(_Symbol, GetTf(), GetPeriod(), _shift, THIS_PTR); + _value = _value = Indi_DeMarker::iDeMarker(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Demo.mqh b/Indicators/Indi_Demo.mqh index 9d36aa8c9..a5bf323d5 100644 --- a/Indicators/Indi_Demo.mqh +++ b/Indicators/Indi_Demo.mqh @@ -72,10 +72,11 @@ class Indi_Demo : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { - double _value = Indi_Demo::iDemo(_Symbol, GetTf(), _shift, THIS_PTR); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + double _value = Indi_Demo::iDemo(GetSymbol(), GetTf(), _ishift, THIS_PTR); if (iparams.is_draw) { - draw.DrawLineTo(GetName(), GetBarTime(_shift), _value); + draw.DrawLineTo(GetName(), GetBarTime(_ishift), _value); } return _value; } diff --git a/Indicators/Indi_DetrendedPrice.mqh b/Indicators/Indi_DetrendedPrice.mqh index 4af92b96b..b8c4fe1cd 100644 --- a/Indicators/Indi_DetrendedPrice.mqh +++ b/Indicators/Indi_DetrendedPrice.mqh @@ -116,16 +116,17 @@ class Indi_DetrendedPrice : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_DetrendedPrice::iDPO(_Symbol, GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, _shift, - THIS_PTR); + _value = Indi_DetrendedPrice::iDPO(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Drawer.mqh b/Indicators/Indi_Drawer.mqh index 957b25106..9cf22635d 100644 --- a/Indicators/Indi_Drawer.mqh +++ b/Indicators/Indi_Drawer.mqh @@ -24,10 +24,10 @@ struct IndicatorParams; // Includes. -#include "../Action.mqh" #include "../DictStruct.mqh" #include "../Indicator.mqh" #include "../Redis.mqh" +#include "../Task/TaskAction.h" #include "Indi_Drawer.struct.h" #include "Price/Indi_Price.mqh" @@ -72,8 +72,6 @@ class Indi_Drawer : public Indicator { int num_args = ArraySize(_args), i; IndicatorDataEntry entry(num_args - 1); - // @fixit Not sure if we should enforce double. - entry.AddFlags(INDI_ENTRY_FLAG_IS_DOUBLE); if (_action == INDI_ACTION_SET_VALUE) { iparams.SetMaxModes(num_args - 1); @@ -100,7 +98,8 @@ class Indi_Drawer : public Indicator { virtual void OnTick() { Indicator::OnTick(); - ActionEntry action(INDI_ACTION_SET_VALUE); + /* @fixme + TaskActionEntry action(INDI_ACTION_SET_VALUE); ArrayResize(action.args, 3); action.args[0].type = TYPE_LONG; action.args[0].integer_value = GetBarTime(); @@ -110,9 +109,11 @@ class Indi_Drawer : public Indicator { action.args[2].type = TYPE_DOUBLE; action.args[2].double_value = 1.25; + */ - string json = SerializerConverter::FromObject(action).ToString(/*SERIALIZER_JSON_NO_WHITESPACES*/); + //string json = SerializerConverter::FromObject(action).ToString(/*SERIALIZER_JSON_NO_WHITESPACES*/); + /* @fixme RedisMessage msg; msg.Add("message"); msg.Add("INDICATOR_DRAW"); @@ -127,7 +128,7 @@ class Indi_Drawer : public Indicator { Print("Got: ", message.Message); #endif if (message.Command == "message" && message.Channel == "INDICATOR_DRAW") { - ActionEntry action_entry; + TaskActionEntry action_entry; SerializerConverter::FromString(message.Message).ToObject(action_entry); ExecuteAction((ENUM_INDICATOR_ACTION)action_entry.action_id, action_entry.args); #ifdef __debug__ @@ -137,6 +138,7 @@ class Indi_Drawer : public Indicator { // Drawing on the buffer. } } + */ } Redis *Redis() { return &redis; } @@ -172,15 +174,16 @@ class Indi_Drawer : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Drawer::iDrawer(_Symbol, GetTf(), _shift, THIS_PTR); + _value = Indi_Drawer::iDrawer(GetSymbol(), GetTf(), _ishift, THIS_PTR); break; case IDATA_INDICATOR: - _value = Indi_Drawer::iDrawerOnIndicator(GetDataSource(), THIS_PTR, _Symbol, GetTf(), _shift); + _value = Indi_Drawer::iDrawerOnIndicator(GetDataSource(), THIS_PTR, GetSymbol(), GetTf(), _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Envelopes.mqh b/Indicators/Indi_Envelopes.mqh index 7f81e699d..4761ab8ce 100644 --- a/Indicators/Indi_Envelopes.mqh +++ b/Indicators/Indi_Envelopes.mqh @@ -197,25 +197,27 @@ class Indi_Envelopes : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Envelopes::iEnvelopes(_Symbol, GetTf(), GetMAPeriod(), GetMAMethod(), GetMAShift(), - GetAppliedPrice(), GetDeviation(), _mode, _shift, THIS_PTR); + _value = Indi_Envelopes::iEnvelopes(GetSymbol(), GetTf(), GetMAPeriod(), GetMAMethod(), GetMAShift(), + GetAppliedPrice(), GetDeviation(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /**/ GetMAPeriod(), - GetMAMethod(), GetMAShift(), GetAppliedPrice(), GetDeviation() /**/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetMAPeriod(), + GetMAMethod(), GetMAShift(), GetAppliedPrice(), GetDeviation() /**/, _mode, _ishift); break; case IDATA_INDICATOR: - _value = Indi_Envelopes::iEnvelopesOnIndicator(GetCache(), GetDataSource(), _Symbol, GetTf(), GetMAPeriod(), + _value = Indi_Envelopes::iEnvelopesOnIndicator(GetCache(), GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), GetMAMethod(), GetDataSourceMode(), GetMAShift(), GetDeviation(), - _mode, _shift); + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); + break; } return _value; } @@ -227,7 +229,7 @@ class Indi_Envelopes : public Indicator { Indicator::GetEntryAlter(_entry); #ifdef __MQL4__ // The LINE_MAIN only exists in MQL4 for Envelopes. - _entry.values[LINE_MAIN] = GetValue((ENUM_LO_UP_LINE)LINE_MAIN, _shift); + _entry.values[LINE_MAIN] = GetValue((ENUM_LO_UP_LINE)LINE_MAIN, _shift); #endif } diff --git a/Indicators/Indi_Force.mqh b/Indicators/Indi_Force.mqh index 24c6fd029..9e047338b 100644 --- a/Indicators/Indi_Force.mqh +++ b/Indicators/Indi_Force.mqh @@ -117,16 +117,18 @@ class Indi_Force : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Force::iForce(_Symbol, GetTf(), GetPeriod(), GetMAMethod(), GetAppliedPrice(), _shift, THIS_PTR); + _value = + Indi_Force::iForce(GetSymbol(), GetTf(), GetPeriod(), GetMAMethod(), GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetMAMethod(), GetAppliedPrice(), VOLUME_TICK /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + GetMAMethod(), GetAppliedPrice(), VOLUME_TICK /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_FractalAdaptiveMA.mqh b/Indicators/Indi_FractalAdaptiveMA.mqh index 34074a17c..7640170c0 100644 --- a/Indicators/Indi_FractalAdaptiveMA.mqh +++ b/Indicators/Indi_FractalAdaptiveMA.mqh @@ -134,16 +134,17 @@ class Indi_FrAMA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_FrAMA::iFrAMA(_Symbol, GetTf(), /*[*/ GetPeriod(), GetFRAMAShift(), GetAppliedPrice() /*]*/, - _mode, _shift, THIS_PTR); + _value = Indi_FrAMA::iFrAMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetFRAMAShift(), GetAppliedPrice() /*]*/, + _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetFRAMAShift() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + GetFRAMAShift() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Fractals.mqh b/Indicators/Indi_Fractals.mqh index e19757578..49d395311 100644 --- a/Indicators/Indi_Fractals.mqh +++ b/Indicators/Indi_Fractals.mqh @@ -102,15 +102,16 @@ class Indi_Fractals : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = _value = Indi_Fractals::iFractals(_Symbol, GetTf(), (ENUM_LO_UP_LINE)_mode, _shift, THIS_PTR); + _value = _value = Indi_Fractals::iFractals(GetSymbol(), GetTf(), (ENUM_LO_UP_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Gator.mqh b/Indicators/Indi_Gator.mqh index 3a5d336f5..9dca4b882 100644 --- a/Indicators/Indi_Gator.mqh +++ b/Indicators/Indi_Gator.mqh @@ -169,22 +169,23 @@ class Indi_Gator : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Gator::iGator(_Symbol, GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), - GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice(), - (ENUM_GATOR_HISTOGRAM)_mode, _shift, THIS_PTR); + _value = Indi_Gator::iGator(GetSymbol(), GetTf(), GetJawPeriod(), GetJawShift(), GetTeethPeriod(), + GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice(), + (ENUM_GATOR_HISTOGRAM)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /**/ + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /**/ GetJawPeriod(), GetJawShift(), GetTeethPeriod(), GetTeethShift(), GetLipsPeriod(), GetLipsShift(), GetMAMethod(), GetAppliedPrice() /**/, - _mode, _shift); + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_HeikenAshi.mqh b/Indicators/Indi_HeikenAshi.mqh index 32881967c..789478852 100644 --- a/Indicators/Indi_HeikenAshi.mqh +++ b/Indicators/Indi_HeikenAshi.mqh @@ -195,8 +195,9 @@ class Indi_HeikenAshi : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = HA_OPEN, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = HA_OPEN, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: #ifdef __MQL4__ @@ -216,15 +217,15 @@ class Indi_HeikenAshi : public Indicator { break; } #endif - _value = Indi_HeikenAshi::iHeikenAshi(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_HeikenAshi::iHeikenAshi(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, _ishift); break; case IDATA_ICUSTOM_LEGACY: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_HeikenAshi::iCustomLegacyHeikenAshi(_Symbol, GetTf(), iparams.GetCustomIndicatorName(), _mode, - _shift, THIS_PTR); + _value = Indi_HeikenAshi::iCustomLegacyHeikenAshi(GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), _mode, + _ishift, THIS_PTR); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Ichimoku.mqh b/Indicators/Indi_Ichimoku.mqh index 9996abab1..0f381d8d5 100644 --- a/Indicators/Indi_Ichimoku.mqh +++ b/Indicators/Indi_Ichimoku.mqh @@ -142,17 +142,18 @@ class Indi_Ichimoku : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Ichimoku::iIchimoku(_Symbol, GetTf(), GetTenkanSen(), GetKijunSen(), GetSenkouSpanB(), _mode, - _shift, THIS_PTR); + _value = Indi_Ichimoku::iIchimoku(GetSymbol(), GetTf(), GetTenkanSen(), GetKijunSen(), GetSenkouSpanB(), _mode, + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetTenkanSen(), - GetKijunSen(), GetSenkouSpanB() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetTenkanSen(), + GetKijunSen(), GetSenkouSpanB() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); @@ -168,9 +169,9 @@ class Indi_Ichimoku : public Indicator { #ifdef __MQL4__ // In MQL4 value of LINE_TENKANSEN is 1 (not 0 as in MQL5), // so we are duplicating it. - _entry.values[0] = GetValue(LINE_TENKANSEN, _shift); + _entry.values[0] = GetEntryValue(LINE_TENKANSEN, _shift); #endif - _entry.values[LINE_CHIKOUSPAN] = GetValue(LINE_CHIKOUSPAN, _shift + 26); + _entry.values[LINE_CHIKOUSPAN] = GetEntryValue(LINE_CHIKOUSPAN, _shift + 26); } /** diff --git a/Indicators/Indi_Killzones.mqh b/Indicators/Indi_Killzones.mqh index c24af70dd..70016b6db 100644 --- a/Indicators/Indi_Killzones.mqh +++ b/Indicators/Indi_Killzones.mqh @@ -106,9 +106,10 @@ class Indi_Killzones : public Indicator { /** * Returns the indicator's value. */ - float GetValue(unsigned int _mode, int _shift = 0) { + IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { float _value = FLT_MAX; int _index = (int)_mode / 2; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: // Builtin mode not supported. @@ -118,7 +119,7 @@ class Indi_Killzones : public Indicator { ikt.Set(::TimeGMT()); if (ikt.CheckHours(_index)) { // Pass values to check for new highs or lows. - ikt.Update(_mode % 2 == 0 ? (float)GetHigh(_shift) : (float)GetLow(_shift), _index); + ikt.Update(_mode % 2 == 0 ? (float)GetHigh(_ishift) : (float)GetLow(_ishift), _index); } // Set a final value. _value = _mode % 2 == 0 ? ikt.GetHigh(_index) : ikt.GetLow(_index); @@ -134,30 +135,6 @@ class Indi_Killzones : public Indicator { */ bool IsValidValue(float _value, unsigned int _mode = 0, int _shift = 0) { return _value > 0.0f; } - /** - * Returns the indicator's struct value. - */ - IndicatorDataEntry GetEntry(int _shift = 0) { - long _bar_time = GetBarTime(_shift); - IndicatorDataEntry _entry = idata.GetByKey(_bar_time); - if (!_entry.IsValid() && !_entry.CheckFlag(INDI_ENTRY_FLAG_INSUFFICIENT_DATA)) { - _entry.Resize(iparams.GetMaxModes()); - _entry.timestamp = GetBarTime(_shift); - for (unsigned int _mode = 0; _mode < (uint)iparams.GetMaxModes(); _mode++) { - float _value = GetValue(_mode, _shift); - _entry.values[_mode] = IsValidValue(_value, _mode, _shift) ? _value : 0.0f; - } - _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); - if (_entry.IsValid()) { - _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType())); - idata.Add(_entry, _bar_time); - } else { - _entry.AddFlags(INDI_ENTRY_FLAG_INSUFFICIENT_DATA); - } - } - return _entry; - } - /** * Checks if indicator entry values are valid. */ diff --git a/Indicators/Indi_MA.mqh b/Indicators/Indi_MA.mqh index 98862a147..f4ad4a28e 100644 --- a/Indicators/Indi_MA.mqh +++ b/Indicators/Indi_MA.mqh @@ -627,23 +627,24 @@ class Indi_MA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_MA::iMA(_Symbol, GetTf(), GetPeriod(), GetMAShift(), GetMAMethod(), GetAppliedPrice(), _shift, - THIS_PTR); + _value = Indi_MA::iMA(_Symbol, GetTf(), GetPeriod(), GetMAShift(), GetMAMethod(), GetAppliedPrice(), + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), GetMAShift(), - GetMAMethod(), GetAppliedPrice() /* ] */, 0, _shift); + _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ GetPeriod(), + GetMAShift(), GetMAMethod(), GetAppliedPrice() /* ] */, 0, _ishift); break; case IDATA_INDICATOR: // Calculating MA value from specified indicator. _value = Indi_MA::iMAOnIndicator(GetCache(), GetDataSource(), GetDataSourceMode(), _Symbol, GetTf(), - GetPeriod(), GetMAShift(), GetMAMethod(), _shift); + GetPeriod(), GetMAShift(), GetMAMethod(), _ishift); break; } return _value; @@ -658,8 +659,8 @@ class Indi_MA : public Indicator { string _key = Util::MakeKey(_symbol, (int)_tf, _period, _ma_shift, (int)_ma_method, (int)_ap); if (!Objects::TryGet(_key, _ptr)) { IndiMAParams _p(_period, _ma_shift, _ma_method, _ap); - _ptr = Objects::Set(_key, new Indi_MA(_p)); - _ptr.SetSymbol(_symbol); + // _ptr = Objects::Set(_key, new Indi_MA(_p)); // @fixme + // _ptr.SetSymbol(_symbol); // @fixme } return _ptr; } diff --git a/Indicators/Indi_MACD.mqh b/Indicators/Indi_MACD.mqh index 1a3227f4f..64570f79b 100644 --- a/Indicators/Indi_MACD.mqh +++ b/Indicators/Indi_MACD.mqh @@ -114,17 +114,19 @@ class Indi_MACD : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = LINE_MAIN, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_MACD::iMACD(_Symbol, GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), - GetAppliedPrice(), (ENUM_SIGNAL_LINE)_mode, _shift, THIS_PTR); + _value = Indi_MACD::iMACD(GetSymbol(), GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), + GetAppliedPrice(), (ENUM_SIGNAL_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), - GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, _mode, _shift); + _value = + iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), + GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_MFI.mqh b/Indicators/Indi_MFI.mqh index a416e7179..902d6d08b 100644 --- a/Indicators/Indi_MFI.mqh +++ b/Indicators/Indi_MFI.mqh @@ -111,20 +111,21 @@ class Indi_MFI : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; #ifdef __MQL4__ - _value = Indi_MFI::iMFI(_Symbol, GetTf(), GetPeriod(), _shift); + _value = Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), _ishift); #else // __MQL5__ - _value = Indi_MFI::iMFI(_Symbol, GetTf(), GetPeriod(), GetAppliedVolume(), _shift, THIS_PTR); + _value = Indi_MFI::iMFI(GetSymbol(), GetTf(), GetPeriod(), GetAppliedVolume(), _ishift, THIS_PTR); #endif break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - VOLUME_TICK /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + VOLUME_TICK /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_MassIndex.mqh b/Indicators/Indi_MassIndex.mqh index 4a72deeb4..817b81cd8 100644 --- a/Indicators/Indi_MassIndex.mqh +++ b/Indicators/Indi_MassIndex.mqh @@ -153,19 +153,21 @@ class Indi_MassIndex : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_MassIndex::iMI(_Symbol, GetTf(), /*[*/ GetPeriod(), GetSecondPeriod(), GetSumPeriod() /*]*/, - _mode, _shift, THIS_PTR); + _value = Indi_MassIndex::iMI(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetSecondPeriod(), GetSumPeriod() /*]*/, + _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetSecondPeriod(), GetSumPeriod() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + GetSecondPeriod(), GetSumPeriod() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); + break; } return _value; } diff --git a/Indicators/Indi_Momentum.mqh b/Indicators/Indi_Momentum.mqh index ed4b19f4d..5f4266dd0 100644 --- a/Indicators/Indi_Momentum.mqh +++ b/Indicators/Indi_Momentum.mqh @@ -141,24 +141,25 @@ class Indi_Momentum : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = Indi_Momentum::iMomentum(_Symbol, GetTf(), GetPeriod(), GetAppliedPrice(), iparams.shift + _shift, + _value = Indi_Momentum::iMomentum(GetSymbol(), GetTf(), GetPeriod(), GetAppliedPrice(), iparams.shift + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; case IDATA_INDICATOR: ValidateSelectedDataSource(); // @fixit Somehow shift isn't used neither in MT4 nor MT5. - _value = Indi_Momentum::iMomentumOnIndicator(GetDataSource(), _Symbol, GetTf(), GetPeriod(), + _value = Indi_Momentum::iMomentumOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetPeriod(), GetDataSourceMode(), iparams.shift + _shift); if (iparams.is_draw) { draw.DrawLineTo(StringFormat("%s", GetName()), GetBarTime(iparams.shift + _shift), _value, 1); diff --git a/Indicators/Indi_OBV.mqh b/Indicators/Indi_OBV.mqh index 61a93b422..cefa485e9 100644 --- a/Indicators/Indi_OBV.mqh +++ b/Indicators/Indi_OBV.mqh @@ -118,20 +118,21 @@ class Indi_OBV : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; #ifdef __MQL4__ - _value = Indi_OBV::iOBV(_Symbol, GetTf(), GetAppliedPrice(), _shift); + _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedPrice(), _ishift); #else // __MQL5__ - _value = Indi_OBV::iOBV(_Symbol, GetTf(), GetAppliedVolume(), _shift, THIS_PTR); + _value = Indi_OBV::iOBV(GetSymbol(), GetTf(), GetAppliedVolume(), _ishift, THIS_PTR); #endif break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ VOLUME_TICK /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_OsMA.mqh b/Indicators/Indi_OsMA.mqh index 959d0fbfa..ac0a608a7 100644 --- a/Indicators/Indi_OsMA.mqh +++ b/Indicators/Indi_OsMA.mqh @@ -111,17 +111,19 @@ class Indi_OsMA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); double _value = EMPTY_VALUE; switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_OsMA::iOsMA(_Symbol, GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), - GetAppliedPrice(), _shift, THIS_PTR); + _value = Indi_OsMA::iOsMA(GetSymbol(), GetTf(), GetEmaFastPeriod(), GetEmaSlowPeriod(), GetSignalPeriod(), + GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), - GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, 0, _shift); + _value = + iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetEmaFastPeriod(), + GetEmaSlowPeriod(), GetSignalPeriod(), GetAppliedPrice() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Pivot.mqh b/Indicators/Indi_Pivot.mqh index 3299c0cb9..f501ee846 100644 --- a/Indicators/Indi_Pivot.mqh +++ b/Indicators/Indi_Pivot.mqh @@ -72,9 +72,13 @@ class Indi_Pivot : public Indicator { _entry.timestamp = GetBarTime(_ishift); if (_ohlc.IsValid()) { _entry.Resize(iparams.GetMaxModes()); - _ohlc.GetPivots(GetMethod(), _entry.values[0].vflt, _entry.values[1].vflt, _entry.values[2].vflt, - _entry.values[3].vflt, _entry.values[4].vflt, _entry.values[5].vflt, _entry.values[6].vflt, - _entry.values[7].vflt, _entry.values[8].vflt); + _ohlc.GetPivots(GetMethod(), _entry.values[0].value.vflt, _entry.values[1].value.vflt, + _entry.values[2].value.vflt, _entry.values[3].value.vflt, _entry.values[4].value.vflt, + _entry.values[5].value.vflt, _entry.values[6].value.vflt, _entry.values[7].value.vflt, + _entry.values[8].value.vflt); + for (int i = 0; i <= 8; ++i) { + _entry.values[i].SetDataType(TYPE_FLOAT); + } } GetEntryAlter(_entry, _ishift); _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); @@ -96,7 +100,10 @@ class Indi_Pivot : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { return GetEntry(_shift)[_mode]; } + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + return GetEntry(_ishift)[_mode]; + } /** * Checks if indicator entry values are valid. diff --git a/Indicators/Indi_PriceChannel.mqh b/Indicators/Indi_PriceChannel.mqh index f202df67a..0afc2392e 100644 --- a/Indicators/Indi_PriceChannel.mqh +++ b/Indicators/Indi_PriceChannel.mqh @@ -106,22 +106,20 @@ class Indi_PriceChannel : public Indicator { /** * Returns the indicator's value. */ - double GetValue(int _mode = 0, int _shift = 0) { - ResetLastError(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_PriceChannel::iPriceChannel(_Symbol, GetTf(), GetPeriod(), _mode, _shift, THIS_PTR); + _value = Indi_PriceChannel::iPriceChannel(GetSymbol(), GetTf(), GetPeriod(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); } - istate.is_ready = _LastError == ERR_NO_ERROR; - istate.is_changed = false; return _value; } diff --git a/Indicators/Indi_PriceFeeder.mqh b/Indicators/Indi_PriceFeeder.mqh index 4f6793150..c1d286d88 100644 --- a/Indicators/Indi_PriceFeeder.mqh +++ b/Indicators/Indi_PriceFeeder.mqh @@ -75,12 +75,13 @@ class Indi_PriceFeeder : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(ENUM_APPLIED_PRICE _ap, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { int data_size = ArraySize(iparams.price_data); + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); - if (_shift >= data_size || _shift < 0) return DBL_MIN; + if (_ishift >= data_size || _ishift < 0) return DBL_MIN; - double _value = iparams.price_data[data_size - _shift - 1]; + double _value = iparams.price_data[data_size - _ishift - 1]; return _value; } @@ -94,22 +95,4 @@ class Indi_PriceFeeder : public Indicator { } } } - - /** - * Returns the indicator's struct value. - */ - IndicatorDataEntry GetEntry(datetime _bar_time = 0) { - unsigned int _position; - IndicatorDataEntry _entry(iparams.GetMaxModes()); - if (idata.KeyExists(_bar_time, _position)) { - _entry = idata.GetByPos(_position); - } else { - _entry.timestamp = _bar_time; - _entry.values[0].Set(GetValue(PRICE_OPEN, iparams.GetShift())); - _entry.AddFlags(INDI_ENTRY_FLAG_IS_VALID); - _entry.AddFlags(_entry.GetDataTypeFlag(iparams.GetDataValueType())); - idata.Add(_entry, _bar_time); - } - return _entry; - } }; diff --git a/Indicators/Indi_PriceVolumeTrend.mqh b/Indicators/Indi_PriceVolumeTrend.mqh index 84e6c8307..17505eff1 100644 --- a/Indicators/Indi_PriceVolumeTrend.mqh +++ b/Indicators/Indi_PriceVolumeTrend.mqh @@ -121,15 +121,17 @@ class Indi_PriceVolumeTrend : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_PriceVolumeTrend::iPVT(_Symbol, GetTf(), /*[*/ GetAppliedVolume() /*]*/, _mode, _shift, THIS_PTR); + _value = + Indi_PriceVolumeTrend::iPVT(GetSymbol(), GetTf(), /*[*/ GetAppliedVolume() /*]*/, _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetAppliedVolume() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), + /*[*/ GetAppliedVolume() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_RS.mqh b/Indicators/Indi_RS.mqh index 8e7e7ffd4..40085d885 100644 --- a/Indicators/Indi_RS.mqh +++ b/Indicators/Indi_RS.mqh @@ -78,17 +78,17 @@ class Indi_RS : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { - double _value = EMPTY_VALUE; + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_MATH: - _value = imath[_mode].Ptr().GetValue(); + return imath[_mode].Ptr().GetEntryValue(); break; default: SetUserError(ERR_INVALID_PARAMETER); break; } - return _value; + return EMPTY_VALUE; } /** diff --git a/Indicators/Indi_RSI.mqh b/Indicators/Indi_RSI.mqh index 2f9c4c612..41b7339a9 100644 --- a/Indicators/Indi_RSI.mqh +++ b/Indicators/Indi_RSI.mqh @@ -54,6 +54,7 @@ struct IndiRSIParams : IndicatorParams { : applied_price(_ap), IndicatorParams(INDI_RSI, 1, TYPE_DOUBLE) { shift = _shift; SetDataValueRange(IDATA_RANGE_RANGE); + // SetDataSourceType(IDATA_ICUSTOM); SetCustomIndicatorName("Examples\\RSI"); SetPeriod(_period); }; @@ -288,21 +289,25 @@ class Indi_RSI : public Indicator { * Note that in MQL5 Applied Price must be passed as the last parameter * (before mode and shift). */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + double _res[]; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_RSI::iRSI(_Symbol, GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(), _shift, THIS_PTR); + _value = + Indi_RSI::iRSI(GetSymbol(), GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.custom_indi_name, /* [ */ iparams.GetPeriod(), - iparams.GetAppliedPrice() /* ] */, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.custom_indi_name, /* [ */ iparams.GetPeriod(), + iparams.GetAppliedPrice() /* ] */, 0, _ishift); + Print(_value); break; case IDATA_INDICATOR: - _value = Indi_RSI::iRSIOnIndicator(GetDataSource(), THIS_PTR, _Symbol, GetTf(), iparams.GetPeriod(), - iparams.GetAppliedPrice(), _shift); + _value = Indi_RSI::iRSIOnIndicator(GetDataSource(), THIS_PTR, GetSymbol(), GetTf(), iparams.GetPeriod(), + iparams.GetAppliedPrice(), _ishift); break; } return _value; diff --git a/Indicators/Indi_RVI.mqh b/Indicators/Indi_RVI.mqh index e9ea90041..18402acad 100644 --- a/Indicators/Indi_RVI.mqh +++ b/Indicators/Indi_RVI.mqh @@ -103,16 +103,17 @@ class Indi_RVI : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = LINE_MAIN, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_RVI::iRVI(_Symbol, GetTf(), GetPeriod(), (ENUM_SIGNAL_LINE)_mode, _shift, THIS_PTR); + _value = Indi_RVI::iRVI(GetSymbol(), GetTf(), GetPeriod(), (ENUM_SIGNAL_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, - _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_RateOfChange.mqh b/Indicators/Indi_RateOfChange.mqh index 77b33a436..076f4798a 100644 --- a/Indicators/Indi_RateOfChange.mqh +++ b/Indicators/Indi_RateOfChange.mqh @@ -110,16 +110,17 @@ class Indi_RateOfChange : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_RateOfChange::iROC(_Symbol, GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, _shift, - THIS_PTR); + _value = Indi_RateOfChange::iROC(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_SAR.mqh b/Indicators/Indi_SAR.mqh index 0410e3335..2381c9cf3 100644 --- a/Indicators/Indi_SAR.mqh +++ b/Indicators/Indi_SAR.mqh @@ -102,16 +102,17 @@ class Indi_SAR : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_SAR::iSAR(_Symbol, GetTf(), GetStep(), GetMax(), _shift, THIS_PTR); + _value = Indi_SAR::iSAR(GetSymbol(), GetTf(), GetStep(), GetMax(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetStep(), - GetMax() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetStep(), + GetMax() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_StdDev.mqh b/Indicators/Indi_StdDev.mqh index 4d8f42dff..4ef932b03 100644 --- a/Indicators/Indi_StdDev.mqh +++ b/Indicators/Indi_StdDev.mqh @@ -226,21 +226,23 @@ class Indi_StdDev : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_StdDev::iStdDev(_Symbol, GetTf(), GetMAPeriod(), GetMAShift(), GetMAMethod(), GetAppliedPrice(), - _shift, THIS_PTR); + _value = Indi_StdDev::iStdDev(GetSymbol(), GetTf(), GetMAPeriod(), GetMAShift(), GetMAMethod(), + GetAppliedPrice(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetMAPeriod(), - GetMAShift(), GetMAMethod() /*]*/, 0, _shift); + _value = iCustom(istate.handle, Get(CHART_PARAM_SYMBOL), Get(CHART_PARAM_TF), + iparams.GetCustomIndicatorName(), /*[*/ GetMAPeriod(), GetMAShift(), GetMAMethod() /*]*/, 0, + _ishift); break; case IDATA_INDICATOR: - _value = Indi_StdDev::iStdDevOnIndicator(GetDataSource(), _Symbol, GetTf(), GetMAPeriod(), GetMAShift(), - GetAppliedPrice(), _shift, THIS_PTR); + _value = Indi_StdDev::iStdDevOnIndicator(GetDataSource(), GetSymbol(), GetTf(), GetMAPeriod(), GetMAShift(), + GetAppliedPrice(), _ishift, THIS_PTR); break; } return _value; diff --git a/Indicators/Indi_Stochastic.mqh b/Indicators/Indi_Stochastic.mqh index b23472ac9..d60246205 100644 --- a/Indicators/Indi_Stochastic.mqh +++ b/Indicators/Indi_Stochastic.mqh @@ -119,17 +119,18 @@ class Indi_Stochastic : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = LINE_MAIN, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = LINE_MAIN, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_Stochastic::iStochastic(_Symbol, GetTf(), GetKPeriod(), GetDPeriod(), GetSlowing(), GetMAMethod(), - GetPriceField(), _mode, _shift, THIS_PTR); + _value = Indi_Stochastic::iStochastic(GetSymbol(), GetTf(), GetKPeriod(), GetDPeriod(), GetSlowing(), + GetMAMethod(), GetPriceField(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetKPeriod(), - GetDPeriod(), GetSlowing() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetKPeriod(), + GetDPeriod(), GetSlowing() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_TEMA.mqh b/Indicators/Indi_TEMA.mqh index 953a5dd5e..3cc9e949b 100644 --- a/Indicators/Indi_TEMA.mqh +++ b/Indicators/Indi_TEMA.mqh @@ -126,16 +126,17 @@ class Indi_TEMA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_TEMA::iTEMA(_Symbol, GetTf(), /*[*/ GetPeriod(), GetTEMAShift(), GetAppliedPrice() /*]*/, 0, - _shift, THIS_PTR); + _value = Indi_TEMA::iTEMA(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetTEMAShift(), GetAppliedPrice() /*]*/, 0, + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), - GetTEMAShift() /*]*/, 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod(), + GetTEMAShift() /*]*/, 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_TRIX.mqh b/Indicators/Indi_TRIX.mqh index b47c09615..a8086bd27 100644 --- a/Indicators/Indi_TRIX.mqh +++ b/Indicators/Indi_TRIX.mqh @@ -127,16 +127,17 @@ class Indi_TRIX : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = - Indi_TRIX::iTriX(_Symbol, GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_TRIX::iTriX(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedPrice() /*]*/, _mode, _ishift, + THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_UltimateOscillator.mqh b/Indicators/Indi_UltimateOscillator.mqh index 442265abf..7788bd77b 100644 --- a/Indicators/Indi_UltimateOscillator.mqh +++ b/Indicators/Indi_UltimateOscillator.mqh @@ -209,20 +209,21 @@ class Indi_UltimateOscillator : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = - Indi_UltimateOscillator::iUO(_Symbol, GetTf(), /*[*/ GetFastPeriod(), GetMiddlePeriod(), GetSlowPeriod(), - GetFastK(), GetMiddleK(), GetSlowK() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_UltimateOscillator::iUO(GetSymbol(), GetTf(), /*[*/ GetFastPeriod(), GetMiddlePeriod(), + GetSlowPeriod(), GetFastK(), GetMiddleK(), GetSlowK() /*]*/, _mode, + _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetFastPeriod(), GetMiddlePeriod(), GetSlowPeriod(), GetFastK(), GetMiddleK(), GetSlowK() /*]*/, - 0, _shift); + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_VIDYA.mqh b/Indicators/Indi_VIDYA.mqh index 90f0d4b4e..a5647974c 100644 --- a/Indicators/Indi_VIDYA.mqh +++ b/Indicators/Indi_VIDYA.mqh @@ -147,19 +147,20 @@ class Indi_VIDYA : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_VIDYA::iVIDyA(_Symbol, GetTf(), /*[*/ GetCMOPeriod(), GetMAPeriod(), GetVIDYAShift(), - GetAppliedPrice() /*]*/, 0, _shift, THIS_PTR); + _value = Indi_VIDYA::iVIDyA(GetSymbol(), GetTf(), /*[*/ GetCMOPeriod(), GetMAPeriod(), GetVIDYAShift(), + GetAppliedPrice() /*]*/, 0, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetCMOPeriod(), GetMAPeriod(), GetVIDYAShift() /*]*/, - 0, _shift); + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_VROC.mqh b/Indicators/Indi_VROC.mqh index 278aff2d8..2170fa831 100644 --- a/Indicators/Indi_VROC.mqh +++ b/Indicators/Indi_VROC.mqh @@ -130,16 +130,17 @@ class Indi_VROC : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = - Indi_VROC::iVROC(_Symbol, GetTf(), /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_VROC::iVROC(GetSymbol(), GetTf(), /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, _ishift, + THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), + /*[*/ GetPeriod(), GetAppliedVolume() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_Volumes.mqh b/Indicators/Indi_Volumes.mqh index cea9be8d4..786f9fbbe 100644 --- a/Indicators/Indi_Volumes.mqh +++ b/Indicators/Indi_Volumes.mqh @@ -124,15 +124,16 @@ class Indi_Volumes : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_Volumes::iVolumes(_Symbol, GetTf(), /*[*/ GetAppliedVolume() /*]*/, _mode, _shift, THIS_PTR); + _value = Indi_Volumes::iVolumes(GetSymbol(), GetTf(), /*[*/ GetAppliedVolume() /*]*/, _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetAppliedVolume() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), + /*[*/ GetAppliedVolume() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_WPR.mqh b/Indicators/Indi_WPR.mqh index f47d322c7..8dcc258aa 100644 --- a/Indicators/Indi_WPR.mqh +++ b/Indicators/Indi_WPR.mqh @@ -101,16 +101,17 @@ class Indi_WPR : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_WPR::iWPR(_Symbol, GetTf(), GetPeriod(), _shift, THIS_PTR); + _value = Indi_WPR::iWPR(GetSymbol(), GetTf(), GetPeriod(), _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, 0, - _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), /*[*/ GetPeriod() /*]*/, + 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_WilliamsAD.mqh b/Indicators/Indi_WilliamsAD.mqh index f2581141c..754cff808 100644 --- a/Indicators/Indi_WilliamsAD.mqh +++ b/Indicators/Indi_WilliamsAD.mqh @@ -124,14 +124,15 @@ class Indi_WilliamsAD : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_WilliamsAD::iWAD(_Symbol, GetTf(), _mode, _shift, THIS_PTR); + _value = Indi_WilliamsAD::iWAD(GetSymbol(), GetTf(), _mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), 0, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), 0, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ZigZag.mqh b/Indicators/Indi_ZigZag.mqh index 7d7ea0c0e..94fa26e33 100644 --- a/Indicators/Indi_ZigZag.mqh +++ b/Indicators/Indi_ZigZag.mqh @@ -334,17 +334,18 @@ class Indi_ZigZag : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_ZigZag::iZigZag(_Symbol, GetTf(), GetDepth(), GetDeviation(), GetBackstep(), - (ENUM_ZIGZAG_LINE)_mode, _shift, THIS_PTR); + _value = Indi_ZigZag::iZigZag(GetSymbol(), GetTf(), GetDepth(), GetDeviation(), GetBackstep(), + (ENUM_ZIGZAG_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: istate.handle = istate.is_changed ? INVALID_HANDLE : istate.handle; - _value = Indi_ZigZag::iCustomZigZag(_Symbol, GetTf(), iparams.GetCustomIndicatorName(), GetDepth(), - GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, _shift, THIS_PTR); + _value = Indi_ZigZag::iCustomZigZag(GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), GetDepth(), + GetDeviation(), GetBackstep(), (ENUM_ZIGZAG_LINE)_mode, _ishift, THIS_PTR); break; default: SetUserError(ERR_INVALID_PARAMETER); diff --git a/Indicators/Indi_ZigZagColor.mqh b/Indicators/Indi_ZigZagColor.mqh index ca9300fed..26489fd8f 100644 --- a/Indicators/Indi_ZigZagColor.mqh +++ b/Indicators/Indi_ZigZagColor.mqh @@ -268,23 +268,22 @@ class Indi_ZigZagColor : public Indicator { /** * Returns the indicator's value. */ - double GetValue(int _mode = 0, int _shift = 0) { - ResetLastError(); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_BUILTIN: - _value = Indi_ZigZagColor::iZigZagColor(_Symbol, GetTf(), GetDepth(), GetDeviation(), GetBackstep(), - (ENUM_ZIGZAG_LINE)_mode, _shift, THIS_PTR); + _value = Indi_ZigZagColor::iZigZagColor(GetSymbol(), GetTf(), GetDepth(), GetDeviation(), GetBackstep(), + (ENUM_ZIGZAG_LINE)_mode, _ishift, THIS_PTR); break; case IDATA_ICUSTOM: - _value = iCustom(istate.handle, _Symbol, GetTf(), iparams.GetCustomIndicatorName(), - /*[*/ GetDepth(), GetDeviation(), GetBackstep() /*]*/, _mode, _shift); + _value = iCustom(istate.handle, GetSymbol(), GetTf(), iparams.GetCustomIndicatorName(), + /*[*/ GetDepth(), GetDeviation(), GetBackstep() /*]*/, _mode, _ishift); break; default: SetUserError(ERR_INVALID_PARAMETER); + break; } - istate.is_ready = _LastError == ERR_NO_ERROR; - istate.is_changed = false; return _value; } diff --git a/Indicators/OHLC/Indi_OHLC.mqh b/Indicators/OHLC/Indi_OHLC.mqh index 9db2150e9..7d7d26d93 100644 --- a/Indicators/OHLC/Indi_OHLC.mqh +++ b/Indicators/OHLC/Indi_OHLC.mqh @@ -60,7 +60,8 @@ class Indi_OHLC : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); ENUM_APPLIED_PRICE _ap = PRICE_OPEN; switch (_mode) { case INDI_OHLC_CLOSE: @@ -76,7 +77,7 @@ class Indi_OHLC : public Indicator { _ap = PRICE_LOW; break; } - return ChartStatic::iPrice(_ap, _Symbol, GetTf(), _shift); + return ChartStatic::iPrice(_ap, GetSymbol(), GetTf(), _ishift); } /** diff --git a/Indicators/Price/Indi_Price.mqh b/Indicators/Price/Indi_Price.mqh index 13bb44d9d..74cd43db8 100644 --- a/Indicators/Price/Indi_Price.mqh +++ b/Indicators/Price/Indi_Price.mqh @@ -57,13 +57,14 @@ class Indi_Price : public Indicator { /** * Checks whether indicator has a valid value for a given shift. */ - virtual bool HasValidEntry(datetime _bar_time = 0) { return _bar_time != 0; } + virtual bool HasValidEntry(int _shift = 0) { return GetBarTime(_shift) != 0; } /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { - return ChartStatic::iPrice(iparams.GetAppliedPrice(), _Symbol, GetTf(), _shift); + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + return ChartStatic::iPrice(iparams.GetAppliedPrice(), GetSymbol(), GetTf(), _ishift); } /** diff --git a/Indicators/Special/Indi_Math.mqh b/Indicators/Special/Indi_Math.mqh index 0c8fd4698..ca9c8e22c 100644 --- a/Indicators/Special/Indi_Math.mqh +++ b/Indicators/Special/Indi_Math.mqh @@ -93,8 +93,9 @@ class Indi_Math : public Indicator { /** * Returns the indicator's value. */ - virtual double GetValue(int _mode = 0, int _shift = 0) { + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { double _value = EMPTY_VALUE; + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); switch (iparams.idstype) { case IDATA_INDICATOR: if (!indi_src.IsSet()) { @@ -112,12 +113,12 @@ class Indi_Math : public Indicator { case MATH_OP_MODE_BUILTIN: _value = Indi_Math::iMathOnIndicator(GetDataSource(), _Symbol, GetTf(), /*[*/ GetOpBuiltIn(), GetMode1(), GetMode2(), GetShift1(), - GetShift2() /*]*/, 0, _shift, &this); + GetShift2() /*]*/, 0, _ishift, &this); break; case MATH_OP_MODE_CUSTOM_FUNCTION: _value = Indi_Math::iMathOnIndicator(GetDataSource(), _Symbol, GetTf(), /*[*/ GetOpFunction(), GetMode1(), GetMode2(), GetShift1(), - GetShift2() /*]*/, 0, _shift, &this); + GetShift2() /*]*/, 0, _ishift, &this); break; } break; diff --git a/Indicators/Tick/Indi_TickMt.mqh b/Indicators/Tick/Indi_TickMt.mqh new file mode 100644 index 000000000..fd83cfdc7 --- /dev/null +++ b/Indicators/Tick/Indi_TickMt.mqh @@ -0,0 +1,77 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +// Includes. +#include "../../BufferStruct.mqh" +#include "../../Indicator.mqh" +#include "../../Storage/Objects.h" + +// Structs. +struct IndiTickMtParams : IndicatorParams { + string symbol; + // Struct constructor. + IndiTickMtParams(string _symbol = NULL, int _shift = 0) : IndicatorParams(INDI_TICK, 3, TYPE_DOUBLE) { + SetShift(_shift); + }; + IndiTickMtParams(IndiTickMtParams &_params, ENUM_TIMEFRAMES _tf) { + THIS_REF = _params; + tf = _tf; + }; + // Getters. + string GetSymbol() { return symbol; } + // Setters. + void SetSymbol(string _symbol) { symbol = _symbol; } +}; + +/** + * Price Indicator. + */ +class Indi_TickMt : public Indicator { + public: + /** + * Class constructor. + */ + Indi_TickMt(IndiTickMtParams &_p, IndicatorBase *_indi_src = NULL) : Indicator(_p, _indi_src){}; + Indi_TickMt(ENUM_TIMEFRAMES _tf = PERIOD_CURRENT, int _shift = 0) : Indicator(INDI_TICK, _tf, _shift){}; + + /** + * Returns the indicator's value. + */ + virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _shift = -1) { + int _ishift = _shift >= 0 ? _shift : iparams.GetShift(); + MqlTick _tick = SymbolInfoStatic::GetTick(_Symbol); + switch (_mode) { + case 0: + return _tick.ask; + case 1: + return _tick.bid; + case 2: +#ifdef __MQL4__ + return _tick.volume; +#else + return _tick.volume_real; +#endif + } + SetUserError(ERR_INVALID_PARAMETER); + return DBL_MAX; + } +}; diff --git a/Indicators/Tick/tests/Indi_TickMt.test.mq4 b/Indicators/Tick/tests/Indi_TickMt.test.mq4 new file mode 100644 index 000000000..091a1ba01 --- /dev/null +++ b/Indicators/Tick/tests/Indi_TickMt.test.mq4 @@ -0,0 +1,27 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of Indi_Tick indicator class. + */ + +#include "Indi_TickMt.test.mq5" diff --git a/Indicators/Tick/tests/Indi_TickMt.test.mq5 b/Indicators/Tick/tests/Indi_TickMt.test.mq5 new file mode 100644 index 000000000..4c9572767 --- /dev/null +++ b/Indicators/Tick/tests/Indi_TickMt.test.mq5 @@ -0,0 +1,60 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Includes. +#include "../../../Test.mqh" +#include "../Indi_TickMt.mqh" + +/** + * @file + * Test functionality of Indi_TickMt indicator class. + */ + +Indi_TickMt indi(PERIOD_CURRENT); + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + assertTrueOrFail(indi.IsValid(), "Error on IsValid!"); + // assertTrueOrFail(indi.IsValidEntry(), "Error on IsValidEntry!"); + return (_result && _LastError == ERR_NO_ERROR ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() { + static MqlTick _tick_last; + MqlTick _tick_new = SymbolInfoStatic::GetTick(_Symbol); + if (_tick_new.time % 60 < _tick_last.time % 60) { + // Process ticks each minute. + if (_tick_new.time % 3600 < _tick_last.time % 3600) { + // Print indicator values every hour. + Print(indi.ToString()); + if (indi.Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY))) { + assertTrueOrExit(indi.GetEntry().IsValid(), "Invalid entry!"); + } + } + } + _tick_last = _tick_new; +} diff --git a/Indicators/indicators.h b/Indicators/indicators.h index 7a7ac1d72..027140284 100644 --- a/Indicators/indicators.h +++ b/Indicators/indicators.h @@ -60,6 +60,7 @@ #include "Indi_Gator.mqh" #include "Indi_HeikenAshi.mqh" #include "Indi_Ichimoku.mqh" +#include "Indi_Killzones.mqh" #include "Indi_MA.mqh" #include "Indi_MACD.mqh" #include "Indi_MFI.mqh" diff --git a/Instances.h b/Instances.h new file mode 100644 index 000000000..fee38cb80 --- /dev/null +++ b/Instances.h @@ -0,0 +1,51 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Collects information about class instances. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +#include "Dict.mqh" +#include "Util.h" + +template +class Instances { +public: + + static T* instances[]; + Instances(T* _self) { + Util::ArrayPush(instances, _self); + } + + ~Instances() { + //Util::ArrayRemove(instances, &this); + } +}; + +template +T* Instances::instances[]; diff --git a/Market.mqh b/Market.mqh index b9a2bf9bc..c5945335b 100644 --- a/Market.mqh +++ b/Market.mqh @@ -29,12 +29,12 @@ class Market; class SymbolInfo; // Includes. -#include "Condition.enum.h" #include "Market.struct.h" #include "Math.h" #include "Order.mqh" #include "Serializer.mqh" #include "SymbolInfo.mqh" +#include "Task/TaskCondition.enum.h" // Structs. // Market info. diff --git a/Order.mqh b/Order.mqh index 7cbba164b..38ad8d33f 100644 --- a/Order.mqh +++ b/Order.mqh @@ -30,7 +30,6 @@ #define ORDER_MQH // Includes. -#include "Action.enum.h" #include "Convert.mqh" #include "Data.define.h" #include "Data.struct.h" @@ -43,6 +42,7 @@ #include "Std.h" #include "String.mqh" #include "SymbolInfo.mqh" +#include "Task/TaskAction.enum.h" /* Defines for backward compatibility. */ diff --git a/Refs.struct.h b/Refs.struct.h index 0af59cfc5..60ad53306 100644 --- a/Refs.struct.h +++ b/Refs.struct.h @@ -146,7 +146,7 @@ struct Ref { } // Dropping strong reference. if (!--ptr_object.ptr_ref_counter.num_strong_refs) { -#ifdef __debug__ +#ifdef __debug_ref__ Print(ptr_object.ptr_ref_counter.Debug()); #endif @@ -185,6 +185,10 @@ struct Ref { ptr_object.ptr_ref_counter = NULL; ptr_object = NULL; +#ifdef __debug__ + Print("Refs: Deleting object ", ptr_to_delete); +#endif + delete ptr_to_delete; } @@ -211,7 +215,7 @@ struct Ref { return Ptr(); } ++ptr_object.ptr_ref_counter.num_strong_refs; -#ifdef __debug__ +#ifdef __debug_ref__ Print(ptr_object.ptr_ref_counter.Debug()); #endif } @@ -300,7 +304,7 @@ struct WeakRef { ptr_ref_counter = _ptr.ptr_ref_counter; -#ifdef __debug__ +#ifdef __debug_ref__ Print(ptr_ref_counter.Debug()); #endif @@ -338,7 +342,7 @@ struct WeakRef { // Dropping weak reference. if (!--ptr_ref_counter.num_weak_refs) { // No more weak references. -#ifdef __debug__ +#ifdef __debug_ref__ Print(ptr_ref_counter.Debug()); #endif @@ -350,7 +354,7 @@ struct WeakRef { // Avoiding double deletion in Dynamic's destructor. ptr_ref_counter.ptr_object.ptr_ref_counter = NULL; -#ifdef __debug__ +#ifdef __debug_ref__ Print("Refs: Deleting object ", ptr_ref_counter.ptr_object); #endif diff --git a/SerializerConverter.mqh b/SerializerConverter.mqh index 37753e76d..02fa9cf3d 100644 --- a/SerializerConverter.mqh +++ b/SerializerConverter.mqh @@ -47,6 +47,14 @@ class SerializerConverter { SerializerNode* Node() { return root_node; } + string ToDebugString(int _json_flags = 0) { + if (root_node == NULL) { + return ""; + } + + return root_node.ToString(_json_flags); + } + template static SerializerConverter FromObject(X& _value, int serializer_flags = SERIALIZER_FLAG_INCLUDE_ALL) { Serializer _serializer(NULL, Serialize, serializer_flags); @@ -84,6 +92,10 @@ class SerializerConverter { template static SerializerConverter FromString(string arg) { SerializerConverter _converter(((C*)NULL).Parse(arg), 0); +#ifdef __debug__ + Print("FromString(): result: ", + _converter.Node() != NULL ? _converter.Node().ToString(SERIALIZER_JSON_NO_WHITESPACES) : "NULL"); +#endif return _converter; } diff --git a/Strategy.enum.h b/Strategy.enum.h index b3f60b197..defb9d4e2 100644 --- a/Strategy.enum.h +++ b/Strategy.enum.h @@ -51,7 +51,6 @@ enum ENUM_STRATEGY_ACTION { STRAT_ACTION_DISABLE = 0, // Disables strategy. STRAT_ACTION_ENABLE, // Enables strategy. STRAT_ACTION_SUSPEND, // Suspend Strategy. - STRAT_ACTION_TRADE_EXE, // Execute trade action. STRAT_ACTION_UNSUSPEND, // Unsuspend Strategy. FINAL_STRATEGY_ACTION_ENTRY }; @@ -62,7 +61,6 @@ enum ENUM_STRATEGY_CONDITION { STRAT_COND_IS_SUSPENDED, // Strategy is suspended. STRAT_COND_IS_TREND, // Strategy is in trend. STRAT_COND_SIGNALOPEN, // On strategy's signal to open. - STRAT_COND_TRADE_COND, // On strategy's trade condition (args). FINAL_STRATEGY_CONDITION_ENTRY }; diff --git a/Strategy.mqh b/Strategy.mqh index b391942e5..5d3243cb6 100644 --- a/Strategy.mqh +++ b/Strategy.mqh @@ -36,7 +36,8 @@ class Trade; #include "Strategy.enum.h" #include "Strategy.struct.h" #include "String.mqh" -#include "Task.mqh" +#include "Task/TaskManager.h" +#include "Task/Taskable.h" #include "Trade.mqh" // Defines. @@ -85,7 +86,7 @@ class Trade; /** * Implements strategy class. */ -class Strategy : public Object { +class Strategy : public Taskable { public: StgParams sparams; @@ -94,11 +95,11 @@ class Strategy : public Object { Dict fdata; Dict idata; DictStruct> indicators; // Indicators list. - DictStruct tasks; - Log logger; // Log instance. + Log logger; // Log instance. MqlTick last_tick; StgProcessResult sresult; Strategy *strat_sl, *strat_tp; // Strategy pointers for stop-loss and profit-take. + TaskManager tasks; // Tasks. Trade trade; // Trade instance. // TradeSignalEntry last_signal; // Last signals. @@ -122,7 +123,7 @@ class Strategy : public Object { * Class constructor. */ Strategy(StgParams &_sparams, TradeParams &_tparams, ChartParams &_cparams, string _name = "") - : sparams(_sparams), trade(_tparams, _cparams), Object(GetPointer(this), __LINE__) { + : sparams(_sparams), trade(_tparams, _cparams) { // Initialize variables. name = _name; MqlTick _tick = {0}; @@ -162,44 +163,11 @@ class Strategy : public Object { StgProcessResult Process(unsigned short _periods_started = DATETIME_NONE) { sresult.last_error = ERR_NO_ERROR; if (_periods_started > 0) { - ProcessTasks(); + tasks.Process(); } return sresult; } - /* Tasks */ - - /** - * Add task. - */ - void AddTask(TaskEntry &_entry) { - if (_entry.IsValid()) { - if (_entry.GetAction().GetType() == ACTION_TYPE_STRATEGY) { - _entry.SetActionObject(GetPointer(this)); - } - if (_entry.GetCondition().GetType() == COND_TYPE_STRATEGY) { - _entry.SetConditionObject(GetPointer(this)); - } - tasks.Push(_entry); - } - } - - /** - * Process strategy's tasks. - * - * @return - * Returns StgProcessResult struct. - */ - void ProcessTasks() { - for (DictStructIterator iter = tasks.Begin(); iter.IsValid(); ++iter) { - bool _is_processed = false; - TaskEntry _entry = iter.Value(); - _is_processed = Task::Process(_entry); - sresult.tasks_processed += (unsigned short)_is_processed; - sresult.tasks_processed_not += (unsigned short)!_is_processed; - } - } - /* State checkers */ /** @@ -671,179 +639,6 @@ class Strategy : public Object { return true; } - /* Conditions and actions */ - - /** - * Checks for Strategy condition. - * - * @param ENUM_STRATEGY_CONDITION _cond - * Strategy condition. - * @return - * Returns true when the condition is met. - */ - bool CheckCondition(ENUM_STRATEGY_CONDITION _cond, DataParamEntry &_args[]) { - bool _result = true; - long arg_size = ArraySize(_args); - long _arg1l = ArraySize(_args) > 0 ? DataParamEntry::ToInteger(_args[0]) : WRONG_VALUE; - long _arg2l = ArraySize(_args) > 1 ? DataParamEntry::ToInteger(_args[1]) : WRONG_VALUE; - long _arg3l = ArraySize(_args) > 2 ? DataParamEntry::ToInteger(_args[2]) : WRONG_VALUE; - switch (_cond) { - case STRAT_COND_IS_ENABLED: - return sparams.IsEnabled(); - case STRAT_COND_IS_SUSPENDED: - return sparams.IsSuspended(); - case STRAT_COND_IS_TREND: - _arg1l = _arg1l != WRONG_VALUE ? _arg1l : 0; - return IsTrend((ENUM_ORDER_TYPE)_arg1l); - case STRAT_COND_SIGNALOPEN: { - ENUM_ORDER_TYPE _cmd = ArraySize(_args) > 1 ? (ENUM_ORDER_TYPE)_args[0].integer_value : ORDER_TYPE_BUY; - int _method = ArraySize(_args) > 1 ? (int)_args[1].integer_value : 0; - float _level = ArraySize(_args) > 2 ? (float)_args[2].double_value : 0; - return SignalOpen(_cmd, _method, _level); - } - case STRAT_COND_TRADE_COND: - // Args: - // 1st (i:0) - Trade's enum condition to check. - // 2rd... (i:1) - Optionally trade's arguments to pass. - if (arg_size > 0) { - DataParamEntry _sargs[]; - ArrayResize(_sargs, ArraySize(_args) - 1); - for (int i = 0; i < ArraySize(_sargs); i++) { - _sargs[i] = _args[i + 1]; - } - _result = trade.CheckCondition((ENUM_TRADE_CONDITION)_arg1l, _sargs); - } - return _result; - default: - GetLogger().Error(StringFormat("Invalid EA condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); - return false; - } - } - bool CheckCondition(ENUM_STRATEGY_CONDITION _cond, long _arg1) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - ArrayPushObject(_args, _param1); - return Strategy::CheckCondition(_cond, _args); - } - bool CheckCondition(ENUM_STRATEGY_CONDITION _cond, long _arg1, long _arg2) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - DataParamEntry _param2 = _arg2; - ArrayPushObject(_args, _param1); - ArrayPushObject(_args, _param2); - return Strategy::CheckCondition(_cond, _args); - } - bool CheckCondition(ENUM_STRATEGY_CONDITION _cond) { - ARRAY(DataParamEntry, _args); - return CheckCondition(_cond, _args); - } - - /** - * Execute Strategy action. - * - * @param ENUM_STRATEGY_ACTION _action - * Strategy action to execute. - * @param MqlParam _args - * Strategy action arguments. - * @return - * Returns true when the action has been executed successfully. - */ - bool ExecuteAction(ENUM_STRATEGY_ACTION _action, DataParamEntry &_args[]) { - bool _result = true; - double arg1d = EMPTY_VALUE; - double arg2d = EMPTY_VALUE; - double arg3d = EMPTY_VALUE; - long arg1i = EMPTY; - long arg2i = EMPTY; - long arg3i = EMPTY; - long arg_size = ArraySize(_args); - if (arg_size > 0) { - arg1d = _args[0].type == TYPE_DOUBLE ? _args[0].double_value : EMPTY_VALUE; - arg1i = _args[0].type == TYPE_INT ? _args[0].integer_value : EMPTY; - if (arg_size > 1) { - arg2d = _args[1].type == TYPE_DOUBLE ? _args[1].double_value : EMPTY_VALUE; - arg2i = _args[1].type == TYPE_INT ? _args[1].integer_value : EMPTY; - } - if (arg_size > 2) { - arg3d = _args[2].type == TYPE_DOUBLE ? _args[2].double_value : EMPTY_VALUE; - arg3i = _args[2].type == TYPE_INT ? _args[2].integer_value : EMPTY; - } - } - switch (_action) { - case STRAT_ACTION_DISABLE: - sparams.Enabled(false); - return true; - case STRAT_ACTION_ENABLE: - sparams.Enabled(true); - return true; - case STRAT_ACTION_SUSPEND: - sparams.Suspended(true); - return true; - case STRAT_ACTION_TRADE_EXE: - // Args: - // 1st (i:0) - Trade's enum action to execute. - // 2rd (i:1) - Trade's argument to pass. - if (arg_size > 0) { - DataParamEntry _sargs[]; - ArrayResize(_sargs, ArraySize(_args) - 1); - for (int i = 0; i < ArraySize(_sargs); i++) { - _sargs[i] = _args[i + 1]; - } - _result = trade.ExecuteAction((ENUM_TRADE_ACTION)_args[0].integer_value, _sargs); - /* @fixme - if (_result) { - Order *_order = trade.GetOrderLast(); - switch ((ENUM_TRADE_ACTION)_args[0].integer_value) { - case TRADE_ACTION_ORDERS_CLOSE_BY_TYPE: - // OnOrderClose();// @todo - break; - case TRADE_ACTION_ORDER_OPEN: - // @fixme: Operation on the structure copy. - OnOrderOpen(_order.GetParams()); - break; - } - } - */ - } - return _result; - case STRAT_ACTION_UNSUSPEND: - sparams.Suspended(false); - return true; - default: - GetLogger().Error(StringFormat("Invalid Strategy action: %s!", EnumToString(_action), __FUNCTION_LINE__)); - return false; - } - return _result; - } - bool ExecuteAction(ENUM_STRATEGY_ACTION _action, long _arg1) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - ArrayPushObject(_args, _param1); - return Strategy::ExecuteAction(_action, _args); - } - bool ExecuteAction(ENUM_STRATEGY_ACTION _action, long _arg1, long _arg2) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - DataParamEntry _param2 = _arg2; - ArrayPushObject(_args, _param1); - ArrayPushObject(_args, _param2); - return Strategy::ExecuteAction(_action, _args); - } - bool ExecuteAction(ENUM_STRATEGY_ACTION _action, long _arg1, long _arg2, long _arg3) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - DataParamEntry _param2 = _arg2; - DataParamEntry _param3 = _arg3; - ArrayPushObject(_args, _param1); - ArrayPushObject(_args, _param2); - ArrayPushObject(_args, _param3); - return Strategy::ExecuteAction(_action, _args); - } - bool ExecuteAction(ENUM_STRATEGY_ACTION _action) { - ARRAY(DataParamEntry, _args); - return Strategy::ExecuteAction(_action, _args); - } - /* Printers methods */ /** @@ -1060,9 +855,11 @@ class Strategy : public Object { if (METHOD(_method, 3)) _result &= !trade.HasOrderOppositeType(_cmd); // 8 if (METHOD(_method, 4)) _result &= trade.IsPeak(_cmd); // 16 if (METHOD(_method, 5)) _result &= !trade.HasOrderBetter(_cmd); // 32 + /* if (METHOD(_method, 6)) - _result &= !trade.CheckCondition( + _result &= !trade.Check( TRADE_COND_ACCOUNT, _method > 0 ? ACCOUNT_COND_EQUITY_01PC_LOW : ACCOUNT_COND_EQUITY_01PC_HIGH); // 64 + */ // if (METHOD(_method, 5)) _result &= Trade().IsRoundNumber(_cmd); // if (METHOD(_method, 6)) _result &= Trade().IsHedging(_cmd); _method = _method > 0 ? _method : !_method; @@ -1167,10 +964,12 @@ class Strategy : public Object { _result |= _result || Open[_shift] > High[_shift + 1] || Open[_shift] < Low[_shift + 1]; // 8 if (METHOD(_method, 4)) _result |= _result || trade.IsPeak(_cmd); // 16 if (METHOD(_method, 5)) _result |= _result || trade.HasOrderBetter(_cmd); // 32 + /* if (METHOD(_method, 6)) _result |= - _result || trade.CheckCondition(TRADE_COND_ACCOUNT, _method > 0 ? ACCOUNT_COND_EQUITY_01PC_HIGH + _result || trade.Check(TRADE_COND_ACCOUNT, _method > 0 ? ACCOUNT_COND_EQUITY_01PC_HIGH : ACCOUNT_COND_EQUITY_01PC_LOW); // 64 + */ // if (METHOD(_method, 7)) _result |= _result || Trade().IsRoundNumber(_cmd); // if (METHOD(_method, 8)) _result |= _result || Trade().IsHedging(_cmd); _method = _method > 0 ? _method : !_method; @@ -1252,6 +1051,105 @@ class Strategy : public Object { return _result; }; + /* Tasks methods */ + + /** + * Add task. + */ + bool AddTask(TaskEntry &_tentry) { + bool _is_valid = _tentry.IsValid(); + if (_is_valid) { + TaskObject _taskobj(_tentry, THIS_PTR, THIS_PTR); + tasks.Add(&_taskobj); + } + return _is_valid; + } + + /** + * Process tasks. + */ + void ProcessTasks() { tasks.Process(); } + + /* Tasks */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { + case STRAT_COND_IS_ENABLED: + return sparams.IsEnabled(); + case STRAT_COND_IS_SUSPENDED: + return sparams.IsSuspended(); + case STRAT_COND_IS_TREND: + return IsTrend(_entry.GetArg(0).ToValue()); + case STRAT_COND_SIGNALOPEN: + return SignalOpen(_entry.GetArg(0).ToValue(), _entry.GetArg(1).ToValue(), + _entry.GetArg(2).ToValue()); + default: + GetLogger().Error(StringFormat("Invalid EA condition: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return _result; + } + bool Check(int _id) { + TaskConditionEntry _entry(_id); + return Check(_entry); + } + + /** + * Gets a copy of structure. + */ + virtual DataParamEntry Get(const TaskGetterEntry &_entry) { + DataParamEntry _result; + switch (_entry.GetId()) { + default: + break; + } + return _result; + } + + /** + * Runs an action. + */ + virtual bool Run(const TaskActionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { + case STRAT_ACTION_DISABLE: + sparams.Enabled(false); + return true; + case STRAT_ACTION_ENABLE: + sparams.Enabled(true); + return true; + case STRAT_ACTION_SUSPEND: + sparams.Suspended(true); + return true; + case STRAT_ACTION_UNSUSPEND: + sparams.Suspended(false); + return true; + default: + GetLogger().Error(StringFormat("Invalid Strategy action: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return _result; + } + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const DataParamEntry &_entry_value) { + bool _result = false; + switch (_entry.GetId()) { + // _entry_value.GetValue() + default: + break; + } + return _result; + } + /* Serializers */ /** diff --git a/Strategy.struct.h b/Strategy.struct.h index d0dd82d4f..a29f778a6 100644 --- a/Strategy.struct.h +++ b/Strategy.struct.h @@ -34,7 +34,7 @@ #include "Serializer.mqh" #include "Strategy.enum.h" #include "Strategy.struct.pricestop.h" -#include "Task.struct.h" +#include "Task/Task.struct.h" // Forward class declaration. class Strategy; diff --git a/Task.struct.h b/Task.struct.h deleted file mode 100644 index d1b7487ce..000000000 --- a/Task.struct.h +++ /dev/null @@ -1,89 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -/** - * @file - * Includes Task's structs. - */ - -#ifndef __MQL__ -// Allows the preprocessor to include a header file when it is needed. -#pragma once -#endif - -// Includes. -#include "Action.struct.h" -#include "Condition.struct.h" -#include "Task.enum.h" - -struct TaskEntry { - ActionEntry action; // Action of the task. - ConditionEntry cond; // Condition of the task. - datetime expires; // Time of expiration. - datetime last_process; // Time of the last process. - datetime last_success; // Time of the last success. - unsigned char flags; // Action flags. - // Constructors. - void TaskEntry() { Init(); } - void TaskEntry(ActionEntry &_action, ConditionEntry &_cond) : action(_action), cond(_cond) { Init(); } - void TaskEntry(long _aid, ENUM_ACTION_TYPE _atype, long _cid, ENUM_CONDITION_TYPE _ctype) - : action(_aid, _atype), cond(_cid, _ctype) { - Init(); - } - template - void TaskEntry(AE _aid, CE _cid) : action(_aid), cond(_cid) { - Init(); - } - // Main methods. - void Init() { - flags = TASK_ENTRY_FLAG_NONE; - SetFlag(TASK_ENTRY_FLAG_IS_ACTIVE, action.IsActive() && cond.IsActive()); - SetFlag(TASK_ENTRY_FLAG_IS_INVALID, action.IsInvalid() || cond.IsInvalid()); - expires = last_process = last_success = 0; - } - // Flag methods. - bool HasFlag(unsigned char _flag) { return bool(flags & _flag); } - void AddFlags(unsigned char _flags) { flags |= _flags; } - void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } - void SetFlag(ENUM_TASK_ENTRY_FLAGS _flag, bool _value) { - if (_value) - AddFlags(_flag); - else - RemoveFlags(_flag); - } - void SetFlags(unsigned char _flags) { flags = _flags; } - // State methods. - bool IsActive() { return HasFlag(ACTION_ENTRY_FLAG_IS_ACTIVE); } - bool IsDone() { return HasFlag(ACTION_ENTRY_FLAG_IS_DONE); } - bool IsFailed() { return HasFlag(ACTION_ENTRY_FLAG_IS_FAILED); } - bool IsValid() { return !HasFlag(ACTION_ENTRY_FLAG_IS_INVALID); } - // Getters. - long GetActionId() { return action.GetId(); } - long GetConditionId() { return cond.GetId(); } - ActionEntry GetAction() { return action; } - ConditionEntry GetCondition() { return cond; } - ENUM_ACTION_TYPE GetActionType() { return action.GetType(); } - ENUM_CONDITION_TYPE GetConditionType() { return cond.GetType(); } - // Setters. - void SetActionObject(void *_obj) { action.SetObject(_obj); } - void SetConditionObject(void *_obj) { cond.SetObject(_obj); } -}; diff --git a/Task.enum.h b/Task/Task.enum.h similarity index 94% rename from Task.enum.h rename to Task/Task.enum.h index 0fcbc5b48..66b180b1f 100644 --- a/Task.enum.h +++ b/Task/Task.enum.h @@ -30,14 +30,14 @@ #pragma once #endif -/* Structure for task actions for Action class. */ +/* Structure for task actions for TaskAction class. */ enum ENUM_TASK_ACTION { TASK_ACTION_NONE = 0, // Does nothing. TASK_ACTION_PROCESS, // Process tasks. FINAL_TASK_ACTION_ENTRY }; -/* Structure for task conditions for Action class. */ +/* Structure for task conditions for TaskAction class. */ enum ENUM_TASK_CONDITION { TASK_COND_NONE = 0, // Empty condition. TASK_COND_IS_ACTIVE, // Is active. diff --git a/Task.mqh b/Task/Task.h similarity index 73% rename from Task.mqh rename to Task/Task.h index 2db50c5e4..8bba0c7e1 100644 --- a/Task.mqh +++ b/Task/Task.h @@ -25,23 +25,24 @@ * Provides integration with tasks (manages conditions and actions). */ +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + // Prevents processing this includes file for the second time. -#ifndef TASK_MQH -#define TASK_MQH +#ifndef TASK_H +#define TASK_H // Includes. -#include "Action.mqh" -#include "Condition.mqh" -#include "DictStruct.mqh" -#include "Refs.mqh" +#include "../DictStruct.mqh" #include "Task.enum.h" #include "Task.struct.h" +#include "TaskAction.h" +#include "TaskCondition.h" +#include "Taskable.h" -class Task { - protected: - // Class variables. - Ref logger; - +class Task : public Taskable { public: // Class variables. DictStruct tasks; @@ -64,8 +65,6 @@ class Task { */ ~Task() {} - Log *Logger() { return logger.Ptr(); } - /* Main methods */ /** @@ -73,18 +72,19 @@ class Task { */ void Add(TaskEntry &_entry) { tasks.Push(_entry); } + /* Virtual methods */ + /** * Process tasks. * * @return * Returns true when tasks has been processed. */ - bool Process() { - bool _result = false; + virtual bool Process() { + bool _result = true; for (DictStructIterator iter = tasks.Begin(); iter.IsValid(); ++iter) { - bool _curr_result = false; TaskEntry _entry = iter.Value(); - Process(_entry); + _result &= Process(_entry); } return _result; } @@ -95,25 +95,77 @@ class Task { * @return * Returns true when tasks has been processed. */ - static bool Process(TaskEntry &_entry) { + virtual bool Process(TaskEntry &_entry) { bool _result = false; if (_entry.IsActive()) { - if (Condition::Test(_entry.GetCondition())) { - ActionEntry _action = _entry.GetAction(); - Action::Execute(_action); - if (_action.IsDone()) { - _entry.SetFlag(TASK_ENTRY_FLAG_IS_DONE, _action.IsDone()); - _entry.SetFlag(TASK_ENTRY_FLAG_IS_FAILED, _action.IsFailed()); - _entry.SetFlag(TASK_ENTRY_FLAG_IS_INVALID, _action.IsInvalid()); - _entry.RemoveFlags(TASK_ENTRY_FLAG_IS_ACTIVE); - } + _entry.Set(STRUCT_ENUM(TaskEntry, TASK_ENTRY_PROP_LAST_PROCESS), TimeCurrent()); + if (_entry.IsDone()) { + _entry.SetFlag(TASK_ENTRY_FLAG_IS_DONE, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_DONE))); + _entry.SetFlag(TASK_ENTRY_FLAG_IS_FAILED, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_FAILED))); + _entry.SetFlag(TASK_ENTRY_FLAG_IS_INVALID, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID))); + _entry.RemoveFlags(TASK_ENTRY_FLAG_IS_ACTIVE); } - _entry.last_process = TimeCurrent(); _result = true; } return _result; } + /* Task methods */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) { + bool _result = true; + switch (_entry.GetId()) { + default: + _result = false; + break; + } + return _result; + } + + /** + * Gets a copy of structure. + */ + virtual TaskEntry Get(const TaskGetterEntry &_entry) { + TaskEntry _result; + switch (_entry.GetId()) { + default: + break; + } + return _result; + } + + /** + * Runs an action. + */ + virtual bool Run(const TaskActionEntry &_entry) { + bool _result = true; + switch (_entry.GetId()) { + default: + _result = false; + break; + } + return _result; + } + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const TaskEntry &_entry_value) { + bool _result = true; + switch (_entry.GetId()) { + default: + _result = false; + break; + } + return _result; + } + /* State methods */ /** @@ -232,7 +284,6 @@ class Task { // Is invalid. return IsInvalid(); default: - Logger().Error(StringFormat("Invalid Task condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); return false; } } @@ -256,7 +307,7 @@ class Task { // Process tasks. return Process(); default: - Logger().Error(StringFormat("Invalid Task action: %s!", EnumToString(_action), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); return false; } return _result; @@ -268,4 +319,4 @@ class Task { /* Other methods */ }; -#endif // TASK_MQH +#endif // TASK_H diff --git a/Task/Task.struct.h b/Task/Task.struct.h new file mode 100644 index 000000000..d9ab789c5 --- /dev/null +++ b/Task/Task.struct.h @@ -0,0 +1,144 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Includes Task's structs. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "Task.enum.h" +#include "TaskAction.struct.h" +#include "TaskCondition.struct.h" + +struct TaskEntry { + public: + /* Enumerations */ + enum ENUM_TASK_ENTRY_PROP { + TASK_ENTRY_PROP_NONE = 0, // None + TASK_ENTRY_PROP_EXPIRES, // Expires + TASK_ENTRY_PROP_LAST_PROCESS, // Last process + TASK_ENTRY_PROP_LAST_SUCCESS, // Last success + }; + + protected: + TaskActionEntry action; // TaskAction of the task. + TaskConditionEntry cond; // TaskCondition of the task. + datetime expires; // Time of expiration. + datetime last_process; // Time of the last process. + datetime last_success; // Time of the last success. + unsigned char flags; // TaskAction flags. + protected: + // Protected methods. + void Init() { + flags = TASK_ENTRY_FLAG_NONE; + SetFlag(TASK_ENTRY_FLAG_IS_ACTIVE, action.IsActive() && cond.IsActive()); + SetFlag(TASK_ENTRY_FLAG_IS_INVALID, action.IsInvalid() || cond.IsInvalid()); + expires = last_process = last_success = 0; + } + + public: + // Constructors. + void TaskEntry() { Init(); } + void TaskEntry(TaskActionEntry &_action, TaskConditionEntry &_cond) : action(_action), cond(_cond) { Init(); } + template + void TaskEntry(AE _aid, CE _cid) : action(_aid), cond(_cid) { + Init(); + }; + // Getters. + template + T Get(ENUM_TASK_ENTRY_PROP _prop) { + switch (_prop) { + case TASK_ENTRY_PROP_EXPIRES: // Expires + return (T)expires; + case TASK_ENTRY_PROP_LAST_PROCESS: // Last process + return (T)last_process; + case TASK_ENTRY_PROP_LAST_SUCCESS: // Last success + return (T)last_success; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return (T) false; + }; + bool Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag) { return action.Get(_flag); }; + template + bool Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_PROP) _prop) { + return action.Get(_prop); + }; + // bool Get(ENUM_TASK_ENTRY_FLAGS _flag) { return HasFlag(_flag); } + TaskActionEntry GetActionEntry() { return action; } + TaskConditionEntry GetConditionEntry() { return cond; } + // Setters. + template + void Set(ENUM_TASK_ENTRY_PROP _prop, T _value) { + switch (_prop) { + case TASK_ENTRY_PROP_EXPIRES: // Expires + expires = (T)_value; + break; + case TASK_ENTRY_PROP_LAST_PROCESS: // Last process + last_process = (T)_value; + break; + case TASK_ENTRY_PROP_LAST_SUCCESS: // Last success + last_success = (T)_value; + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + }; + // Flag methods. + bool HasFlag(unsigned char _flag) { return bool(flags & _flag); } + void AddFlags(unsigned char _flags) { flags |= _flags; } + void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } + void SetFlag(ENUM_TASK_ENTRY_FLAGS _flag, bool _value) { + if (_value) + AddFlags(_flag); + else + RemoveFlags(_flag); + } + void SetFlags(unsigned char _flags) { flags = _flags; } + // State methods. + bool IsActive() { return HasFlag(TASK_ENTRY_FLAG_IS_ACTIVE); } + bool IsDone() { return HasFlag(TASK_ENTRY_FLAG_IS_DONE); } + bool IsFailed() { return HasFlag(TASK_ENTRY_FLAG_IS_FAILED); } + bool IsValid() { return action.IsValid() && cond.IsValid(); } + // Getters. + int GetActionId() { return action.GetId(); } + int GetConditionId() { return cond.GetId(); } + TaskActionEntry GetAction() { return action; } + TaskConditionEntry GetCondition() { return cond; } + + public: + SerializerNodeType Serialize(Serializer &s) { + s.PassStruct(THIS_REF, "aentry", action); + s.PassStruct(THIS_REF, "centry", cond); + return SerializerNodeObject; + } + + SERIALIZER_EMPTY_STUB; +}; diff --git a/Action.enum.h b/Task/TaskAction.enum.h similarity index 87% rename from Action.enum.h rename to Task/TaskAction.enum.h index 551ae44f7..3756722ea 100644 --- a/Action.enum.h +++ b/Task/TaskAction.enum.h @@ -21,7 +21,7 @@ /** * @file - * Includes Action's enums. + * Includes TaskAction's enums. */ #ifndef __MQL__ @@ -33,19 +33,10 @@ #ifndef ACTION_ENUM_H #define ACTION_ENUM_H -/* Defines action entry flags. */ -enum ENUM_ACTION_ENTRY_FLAGS { - ACTION_ENTRY_FLAG_NONE = 0, - ACTION_ENTRY_FLAG_IS_ACTIVE = 1, - ACTION_ENTRY_FLAG_IS_DONE = 2, - ACTION_ENTRY_FLAG_IS_FAILED = 4, - ACTION_ENTRY_FLAG_IS_INVALID = 8 -}; - /* Defines action types. */ enum ENUM_ACTION_TYPE { ACTION_TYPE_NONE = 0, // None. - ACTION_TYPE_ACTION, // Action of action. + ACTION_TYPE_ACTION, // TaskAction of action. ACTION_TYPE_EA, // EA action. ACTION_TYPE_INDICATOR, // Order action. ACTION_TYPE_ORDER, // Order action. @@ -56,7 +47,7 @@ enum ENUM_ACTION_TYPE { FINAL_ACTION_TYPE_ENTRY }; -/* Defines action types for Action class. */ +/* Defines action types for TaskAction class. */ enum ENUM_ACTION_ACTION { ACTION_ACTION_NONE = 0, // Does nothing. ACTION_ACTION_DISABLE, // Disables action. diff --git a/Task/TaskAction.h b/Task/TaskAction.h new file mode 100644 index 000000000..8fdf06693 --- /dev/null +++ b/Task/TaskAction.h @@ -0,0 +1,139 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides integration with task's actions. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_ACTION_H +#define TASK_ACTION_H + +// Includes. +#include "TaskAction.enum.h" +#include "TaskAction.struct.h" +#include "TaskActionBase.h" + +/** + * TaskAction class. + */ +template +class TaskAction : public TaskActionBase { + protected: + // Protected class variables. + TaskActionEntry entry; // Action entry. + TO *obj; // Object to run the action on. + + public: + /* Special methods */ + + /** + * Default class constructor. + */ + TaskAction() {} + + /** + * Class constructor with an entry as argument. + */ + TaskAction(TaskActionEntry &_entry, TO *_obj = NULL) : entry(_entry), obj(_obj) {} + + /* Main methods */ + + /** + * Runs a current stored action. + */ + bool Run() { + bool _result = entry.IsValid() && entry.HasTriesLeft(); + _result &= obj.Run(entry); + if (_result) { + entry.AddFlags(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_DONE)); + entry.RemoveFlags(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_ACTIVE)); + entry.Set(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_TIME_LAST_RUN), TimeCurrent()); + } else { + entry.AddFlags(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID)); + entry.RemoveFlags(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_ACTIVE)); + } + entry.TriesDec(); + return _result; + } + + /* Getters */ + + /** + * Gets an entry's flag. + */ + bool Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag) const { return entry.Get(_flag); } + + /** + * Gets an entry's property value. + */ + template + T Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_PROP) _prop) const { + entry.Get(_prop); + } + + /** + * Gets s reference to the object. + */ + TO *GetObject() { return GetPointer(obj); } + + /* Setters */ + + /** + * Sets an entry's flag. + */ + void Set(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag, bool _value = true) { + entry.Set(_flag, _value); + } + + /** + * Sets an entry's property value. + */ + template + void Set(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_PROP) _prop, T _value) { + entry.Set(_prop, _value); + } + + /* TaskActionBase methods */ + + /** + * Runs an action. + */ + bool Run(const TaskActionEntry &_entry) { + switch (_entry.GetId()) { + case 0: + return Run(); + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return false; + } +}; + +#endif // TASK_ACTION_H diff --git a/Task/TaskAction.struct.h b/Task/TaskAction.struct.h new file mode 100644 index 000000000..e82723fab --- /dev/null +++ b/Task/TaskAction.struct.h @@ -0,0 +1,191 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Includes TaskAction's structures. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Data.struct.h" +#include "../Std.h" +#include "Task.enum.h" + +/* Entry for TaskAction class. */ +struct TaskActionEntry { + public: + /* Enumerations */ + + // Defines action entry properties. + enum ENUM_TASK_ACTION_ENTRY_PROP { + TASK_ACTION_ENTRY_FLAGS, + TASK_ACTION_ENTRY_FREQUENCY, + TASK_ACTION_ENTRY_ID, + TASK_ACTION_ENTRY_TRIES, + TASK_ACTION_ENTRY_TIME_LAST_RUN, + }; + // Defines action entry flags. + enum ENUM_TASK_ACTION_ENTRY_FLAG { + TASK_ACTION_ENTRY_FLAG_NONE = 0 << 0, + TASK_ACTION_ENTRY_FLAG_IS_ACTIVE = 1 << 0, + TASK_ACTION_ENTRY_FLAG_IS_DONE = 1 << 1, + TASK_ACTION_ENTRY_FLAG_IS_FAILED = 1 << 2, + TASK_ACTION_ENTRY_FLAG_IS_INVALID = 1 << 3, + }; + + protected: + unsigned char flags; /* TaskAction flags. */ + datetime time_last_run; /* Time of the successful run. */ + int freq; /* How often to run (0 for no limit). */ + int id; /* TaskAction's enum ID. */ + short tries; /* Number of retries left (-1 for unlimited). */ + DataParamEntry args[]; /* TaskAction arguments. */ + protected: + // Protected methods. + void Init() { SetFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); } + + public: + // Constructors. + TaskActionEntry() : flags(0), freq(60), id(WRONG_VALUE), time_last_run(0), tries(-1) { Init(); } + TaskActionEntry(int _id) + : flags(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_ACTIVE)), + id(_id), + freq(60), + time_last_run(0), + tries(-1) { + Init(); + } + TaskActionEntry(TaskActionEntry &_ae) { THIS_REF = _ae; } + // Flag methods. + bool HasFlag(unsigned char _flag) const { return bool(flags & _flag); } + void AddFlags(unsigned char _flags) { flags |= _flags; } + void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } + void SetFlag(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag, bool _value) { + if (_value) { + AddFlags(_flag); + } else { + RemoveFlags(_flag); + } + } + void SetFlags(unsigned char _flags) { flags = _flags; } + // State methods. + bool HasTriesLeft() const { return tries > 0 || tries == -1; } + bool IsActive() const { return HasFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_ACTIVE)); } + bool IsDone() const { return HasFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_DONE)); } + bool IsFailed() const { return HasFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_FAILED)); } + bool IsInvalid() const { return HasFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID)); } + bool IsValid() const { return !IsInvalid(); } + // Getters. + bool Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag) const { return HasFlag(_flag); } + template + T Get(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_PROP) _prop) const { + switch (_prop) { + case TASK_ACTION_ENTRY_FLAGS: + return (T)flags; + case TASK_ACTION_ENTRY_FREQUENCY: + return (T)freq; + case TASK_ACTION_ENTRY_ID: + return (T)id; + case TASK_ACTION_ENTRY_TRIES: + return (T)tries; + case TASK_ACTION_ENTRY_TIME_LAST_RUN: + return (T)time_last_run; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + return WRONG_VALUE; + } + DataParamEntry GetArg(int _index) const { return args[_index]; } + int GetId() const { return id; } + // Setters. + void TriesDec() { tries -= tries > 0 ? 1 : 0; } + void Set(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_FLAG) _flag, bool _value = true) { + SetFlag(_flag, _value); + } + template + void Set(STRUCT_ENUM(TaskActionEntry, ENUM_TASK_ACTION_ENTRY_PROP) _prop, T _value) { + switch (_prop) { + case TASK_ACTION_ENTRY_FLAGS: // ID (magic number). + flags = (unsigned char)_value; + return; + case TASK_ACTION_ENTRY_FREQUENCY: + freq = (int)_value; + return; + case TASK_ACTION_ENTRY_ID: + id = (int)_value; + SetFlag(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); + return; + case TASK_ACTION_ENTRY_TRIES: + tries = (short)_value; + return; + case TASK_ACTION_ENTRY_TIME_LAST_RUN: + time_last_run = (datetime)_value; + return; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + } + // Methods for arguments. + void ArgAdd(DataParamEntry &_arg) { ArgSet(_arg, ::ArraySize(args)); } + void ArgsGet(ARRAY_REF(DataParamEntry, _args)) { + ::ArrayResize(_args, ::ArraySize(args)); + for (int i = 0; i < ::ArraySize(_args); i++) { + _args[i] = args[i]; + } + } + void ArgSet(DataParamEntry &_arg, int _index = 0) { + if (::ArraySize(args) <= _index) { + ::ArrayResize(args, _index + 1); + } + args[_index] = _arg; + } + void ArgsSet(ARRAY_REF(DataParamEntry, _args)) { + ::ArrayResize(args, ::ArraySize(_args)); + for (int i = 0; i < ::ArraySize(_args); i++) { + args[i] = _args[i]; + } + } + void ArgRemove(int _index) { + for (int i = 1; i < ::ArraySize(args); i++) { + ArgSet(args[i], i - 1); + } + ::ArrayResize(args, _index - 1); + } + // Serializers + SerializerNodeType Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "time_last_run", time_last_run); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(this, "args", args); + return SerializerNodeObject; + } + + SERIALIZER_EMPTY_STUB; +}; diff --git a/Task/TaskActionBase.h b/Task/TaskActionBase.h new file mode 100644 index 000000000..55c99c1e8 --- /dev/null +++ b/Task/TaskActionBase.h @@ -0,0 +1,57 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides a base class for a task's action. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_ACTION_BASE_H +#define TASK_ACTION_BASE_H + +/** + * TaskActionBase class. + */ +class TaskActionBase { + public: + /* Special methods */ + + /** + * Class constructor. + */ + TaskActionBase() {} + + /* Main methods */ + + /** + * Runs an action. + */ + virtual bool Run(const TaskActionEntry &_entry) = NULL; +}; + +#endif // TASK_ACTION_BASE_H diff --git a/Condition.enum.h b/Task/TaskCondition.enum.h similarity index 96% rename from Condition.enum.h rename to Task/TaskCondition.enum.h index 3f7a8e9a4..3a8a9fbee 100644 --- a/Condition.enum.h +++ b/Task/TaskCondition.enum.h @@ -21,7 +21,7 @@ /** * @file - * Includes Condition's enums. + * Includes TaskCondition's enums. */ #ifndef __MQL__ @@ -84,17 +84,8 @@ enum ENUM_MARKET_EVENT { }; #endif -/* Defines condition entry flags. */ -enum ENUM_CONDITION_ENTRY_FLAGS { - COND_ENTRY_FLAG_NONE = 0, - COND_ENTRY_FLAG_IS_ACTIVE = 1, - COND_ENTRY_FLAG_IS_EXPIRED = 2, - COND_ENTRY_FLAG_IS_INVALID = 4, - COND_ENTRY_FLAG_IS_READY = 8 -}; - /* Defines condition statements (operators). */ -enum ENUM_CONDITION_STATEMENT { +enum ENUM_TASK_CONDITION_STATEMENT { COND_AND = 1, // Use AND statement. COND_OR, // Use OR statement. COND_SEQ, // Use sequential checks. @@ -102,9 +93,9 @@ enum ENUM_CONDITION_STATEMENT { }; /* Defines condition types. */ -enum ENUM_CONDITION_TYPE { +enum ENUM_TASK_CONDITION_TYPE { COND_TYPE_ACCOUNT = 1, // Account condition. - COND_TYPE_ACTION, // Action condition. + COND_TYPE_ACTION, // TaskAction condition. COND_TYPE_CHART, // Chart condition. COND_TYPE_DATETIME, // Datetime condition. COND_TYPE_EA, // EA condition. @@ -164,7 +155,7 @@ enum ENUM_ACCOUNT_CONDITION { FINAL_ACCOUNT_CONDITION_ENTRY }; -/* Action conditions. */ +/* TaskAction conditions. */ enum ENUM_ACTION_CONDITION { ACTION_COND_NONE = 0, // Empty condition. ACTION_COND_IS_ACTIVE, // Is active. diff --git a/Task/TaskCondition.h b/Task/TaskCondition.h new file mode 100644 index 000000000..0ae7e995b --- /dev/null +++ b/Task/TaskCondition.h @@ -0,0 +1,140 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides integration with task's conditions. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_CONDITION_H +#define TASK_CONDITION_H + +// Includes. +#include "TaskCondition.enum.h" +#include "TaskCondition.struct.h" +#include "TaskConditionBase.h" + +/** + * TaskCondition class. + */ +template +class TaskCondition : public TaskConditionBase { + public: + protected: + // Protected class variables. + TaskConditionEntry entry; // Condition entry. + TO *obj; // Object to run the action on. + + public: + /* Special methods */ + + /** + * Default class constructor. + */ + TaskCondition() {} + + /** + * Class constructor with an entry as argument. + */ + TaskCondition(TaskConditionEntry &_entry, TO *_obj = NULL) : entry(_entry), obj(_obj) {} + + /* Main methods */ + + /** + * Checks a current stored condition. + */ + bool Check() { + bool _result = entry.IsValid() && entry.HasTriesLeft(); + _result &= obj.Check(entry); + if (_result) { + entry.RemoveFlags(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE)); + entry.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_TIME_LAST_CHECK), TimeCurrent()); + entry.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_TIME_LAST_SUCCESS), TimeCurrent()); + } else { + entry.AddFlags(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_INVALID)); + entry.RemoveFlags(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE)); + entry.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_TIME_LAST_CHECK), TimeCurrent()); + } + entry.TriesDec(); + return _result; + } + + /* Getters */ + + /** + * Gets an entry's flag. + */ + bool Get(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_FLAGS) _flag) const { return entry.Get(_flag); } + + /** + * Gets an entry's property value. + */ + template + T Get(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_PROP) _prop) const { + entry.Get(_prop); + } + + /** + * Gets a reference to the object. + */ + TO *GetObject() { return GetPointer(obj); } + + /* Setters */ + + /** + * Sets an entry's flag. + */ + void Set(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_FLAGS) _flag, bool _value = true) { + entry.Set(_flag, _value); + } + + /** + * Sets an entry's property value. + */ + template + void Set(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_PROP) _prop, T _value) { + entry.Set(_prop, _value); + } + + /* TaskConditionBase methods */ + + /** + * Checks a condition. + */ + bool Check(const TaskConditionEntry &_entry) { + switch (_entry.GetId()) { + case 0: + return Check(); + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return false; + } +}; +#endif // TASK_CONDITION_H diff --git a/Task/TaskCondition.struct.h b/Task/TaskCondition.struct.h new file mode 100644 index 000000000..24df55dc7 --- /dev/null +++ b/Task/TaskCondition.struct.h @@ -0,0 +1,206 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Includes TaskCondition's structures. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Data.struct.h" +#include "../Std.h" +#include "Task.enum.h" + +struct TaskConditionEntry { + public: + /* Enumerations */ + + // Defines condition entry properties. + enum ENUM_TASK_CONDITION_ENTRY_PROP { + TASK_CONDITION_ENTRY_FLAGS, + TASK_CONDITION_ENTRY_FREQUENCY, + TASK_CONDITION_ENTRY_ID, + TASK_CONDITION_ENTRY_TRIES, + TASK_CONDITION_ENTRY_TIME_LAST_CHECK, + TASK_CONDITION_ENTRY_TIME_LAST_SUCCESS, + }; + + // Defines condition entry flags.. + enum ENUM_TASK_CONDITION_ENTRY_FLAGS { + TASK_CONDITION_ENTRY_FLAG_NONE = 0 << 0, + TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE = 1 << 0, + TASK_CONDITION_ENTRY_FLAG_IS_EXPIRED = 1 << 1, + TASK_CONDITION_ENTRY_FLAG_IS_INVALID = 1 << 2, + TASK_CONDITION_ENTRY_FLAG_IS_READY = 1 << 3, + }; + + protected: + unsigned char flags; // Condition flags. + datetime last_check; // Time of the latest check. + datetime last_success; // Time of the last success. + int freq; // How often to run (0 for no limit). + int id; // Condition ID. + short tries; // Number of successful tries left (-1 for unlimited). + // ENUM_TASK_CONDITION_STATEMENT next_statement; // Statement type of the next condition. + // ENUM_TASK_CONDITION_TYPE type; // Task's condition type. + DataParamEntry args[]; // Task's condition arguments. + protected: + // Protected methods. + void Init() { SetFlag(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); } + + public: + // Constructors. + TaskConditionEntry() : flags(0), freq(60), id(WRONG_VALUE), tries(-1) { Init(); } + TaskConditionEntry(int _id) + : flags(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE)), + freq(60), + id(_id), + last_check(0), + last_success(0), + tries(-1) { + Init(); + } + TaskConditionEntry(TaskConditionEntry &_ae) { THIS_REF = _ae; } + // Deconstructor. + void ~TaskConditionEntry() {} + // Getters. + bool Get(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_FLAGS) _flag) const { return HasFlag(_flag); } + template + T Get(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_PROP) _prop) const { + switch (_prop) { + case TASK_CONDITION_ENTRY_FLAGS: + return (T)flags; + case TASK_CONDITION_ENTRY_FREQUENCY: + return (T)freq; + case TASK_CONDITION_ENTRY_ID: + return (T)id; + case TASK_CONDITION_ENTRY_TRIES: + return (T)tries; + case TASK_CONDITION_ENTRY_TIME_LAST_CHECK: + return (T)last_check; + case TASK_CONDITION_ENTRY_TIME_LAST_SUCCESS: + return (T)last_success; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + return WRONG_VALUE; + } + DataParamEntry GetArg(int _index) const { return args[_index]; } + int GetId() const { return id; } + // Setters. + void TriesDec() { tries -= tries > 0 ? 1 : 0; } + void Set(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_FLAGS) _flag, bool _value = true) { + SetFlag(_flag, _value); + } + template + void Set(STRUCT_ENUM(TaskConditionEntry, ENUM_TASK_CONDITION_ENTRY_PROP) _prop, T _value) { + switch (_prop) { + case TASK_CONDITION_ENTRY_FLAGS: // ID (magic number). + flags = (unsigned char)_value; + return; + case TASK_CONDITION_ENTRY_FREQUENCY: + freq = (int)_value; + return; + case TASK_CONDITION_ENTRY_ID: + id = (int)_value; + SetFlag(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); + return; + case TASK_CONDITION_ENTRY_TRIES: + tries = (short)_value; + return; + case TASK_CONDITION_ENTRY_TIME_LAST_CHECK: + last_check = (datetime)_value; + return; + case TASK_CONDITION_ENTRY_TIME_LAST_SUCCESS: + last_success = (datetime)_value; + return; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + } + void SetTries(short _count) { tries = _count; } + // Flag methods. + bool HasFlag(unsigned char _flag) const { return bool(flags & _flag); } + void AddFlags(unsigned char _flags) { flags |= _flags; } + void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } + void SetFlag(ENUM_TASK_CONDITION_ENTRY_FLAGS _flag, bool _value) { + if (_value) + AddFlags(_flag); + else + RemoveFlags(_flag); + } + void SetFlags(unsigned char _flags) { flags = _flags; } + // State methods. + bool HasTriesLeft() const { return tries > 0 || tries == -1; } + bool IsActive() const { return HasFlag(TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE); } + bool IsExpired() const { return HasFlag(TASK_CONDITION_ENTRY_FLAG_IS_EXPIRED); } + bool IsReady() const { return HasFlag(TASK_CONDITION_ENTRY_FLAG_IS_READY); } + bool IsInvalid() const { return HasFlag(TASK_CONDITION_ENTRY_FLAG_IS_INVALID); } + bool IsValid() const { return !IsInvalid(); } + // Methods for arguments. + void ArgAdd(DataParamEntry &_arg) { ArgSet(_arg, ::ArraySize(args)); } + void ArgsGet(ARRAY_REF(DataParamEntry, _args)) { + ::ArrayResize(_args, ::ArraySize(args)); + for (int i = 0; i < ::ArraySize(_args); i++) { + _args[i] = args[i]; + } + } + void ArgSet(DataParamEntry &_arg, int _index = 0) { + if (::ArraySize(args) <= _index) { + ::ArrayResize(args, _index + 1); + } + args[_index] = _arg; + } + void ArgsSet(ARRAY_REF(DataParamEntry, _args)) { + ::ArrayResize(args, ::ArraySize(_args)); + for (int i = 0; i < ::ArraySize(_args); i++) { + args[i] = _args[i]; + } + } + void ArgRemove(int _index) { + for (int i = 1; i < ::ArraySize(args); i++) { + ArgSet(args[i], i - 1); + } + ::ArrayResize(args, _index - 1); + } + + public: + // Serializers + SerializerNodeType Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "last_check", last_check); + s.Pass(THIS_REF, "last_success", last_success); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(this, "args", args); + return SerializerNodeObject; + } + + SERIALIZER_EMPTY_STUB; +}; diff --git a/Task/TaskConditionBase.h b/Task/TaskConditionBase.h new file mode 100644 index 000000000..0e8e12d5b --- /dev/null +++ b/Task/TaskConditionBase.h @@ -0,0 +1,57 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides a base class for a task's condition. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_CONDITION_BASE_H +#define TASK_CONDITION_BASE_H + +/** + * TaskConditionBase class. + */ +class TaskConditionBase { + public: + /* Special methods */ + + /** + * Class constructor. + */ + TaskConditionBase() {} + + /* Main methods */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) = NULL; +}; + +#endif // TASK_CONDITION_BASE_H diff --git a/Task/TaskGetter.h b/Task/TaskGetter.h new file mode 100644 index 000000000..6d28ee9f8 --- /dev/null +++ b/Task/TaskGetter.h @@ -0,0 +1,127 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides integration with task's actions. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_GETTER_H +#define TASK_GETTER_H + +// Includes. +//#include "TaskGetter.enum.h" +#include "TaskGetter.struct.h" +#include "TaskGetterBase.h" + +/** + * TaskGetter class. + */ +template +class TaskGetter : public TaskGetterBase { + protected: + // Protected class variables. + TaskGetterEntry entry; // Getter entry. + + public: + /* Special methods */ + + /** + * Default class constructor. + */ + TaskGetter() {} + + /** + * Class constructor with an entry as argument. + */ + TaskGetter(TaskGetterEntry &_entry) : entry(_entry) {} + + /* Main methods */ + + /** + * Runs a current stored action. + */ + TS Get() { + TS _result = Get(entry); + entry.Set(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_TIME_LAST_GET), TimeCurrent()); + entry.TriesDec(); + return _result; + } + + /* Getters */ + + /** + * Gets an entry's flag. + */ + bool Get(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_FLAG) _flag) const { return entry.Get(_flag); } + + /** + * Gets an entry's property value. + */ + template + T Get(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_PROP) _prop) const { + entry.Get(_prop); + } + + /* Setters */ + + /** + * Sets an entry's flag. + */ + void Set(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_FLAG) _flag, bool _value = true) { + entry.Set(_flag, _value); + } + + /** + * Sets an entry's property value. + */ + template + void Set(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_PROP) _prop, T _value) { + entry.Set(_prop, _value); + } + + /* TaskGetterBase methods */ + + /** + * Gets a copy of structure. + */ + TS Get(const TaskGetterEntry &_entry) { + TS _result; + switch (_entry.GetId()) { + case 0: + _result = Get(); + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return _result; + } +}; + +#endif // TASK_GETTER_H diff --git a/Task/TaskGetter.struct.h b/Task/TaskGetter.struct.h new file mode 100644 index 000000000..2abe464da --- /dev/null +++ b/Task/TaskGetter.struct.h @@ -0,0 +1,170 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Includes TaskGetter's structures. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Data.struct.h" +#include "../Std.h" +#include "Task.enum.h" + +/* Entry for TaskGetter class. */ +struct TaskGetterEntry { + public: + /* Enumerations */ + + // Defines action entry properties. + enum ENUM_TASK_GETTER_ENTRY_PROP { + TASK_GETTER_ENTRY_FLAGS, + TASK_GETTER_ENTRY_FREQUENCY, + TASK_GETTER_ENTRY_ID, + TASK_GETTER_ENTRY_TRIES, + TASK_GETTER_ENTRY_TIME_LAST_GET, + }; + // Defines action entry flags. + enum ENUM_TASK_GETTER_ENTRY_FLAG { + TASK_GETTER_ENTRY_FLAG_NONE = 0 << 0, + TASK_GETTER_ENTRY_FLAG_IS_ACTIVE = 1 << 0, + TASK_GETTER_ENTRY_FLAG_IS_DONE = 1 << 1, + TASK_GETTER_ENTRY_FLAG_IS_FAILED = 1 << 2, + TASK_GETTER_ENTRY_FLAG_IS_INVALID = 1 << 3, + }; + + protected: + unsigned char flags; /* TaskGetter flags. */ + datetime time_last_get; /* Time of the successful get. */ + int freq; /* How often to run (0 for no limit). */ + int id; /* TaskGetter's enum ID. */ + short tries; /* Number of retries left (-1 for unlimited). */ + DataParamEntry args[]; /* TaskGetter arguments. */ + protected: + // Protected methods. + void Init() { SetFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); } + + public: + // Constructors. + TaskGetterEntry() : flags(0), freq(60), id(WRONG_VALUE), time_last_get(0), tries(-1) { Init(); } + TaskGetterEntry(int _id) + : flags(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_ACTIVE)), + id(_id), + freq(60), + time_last_get(0), + tries(-1) { + Init(); + } + TaskGetterEntry(TaskGetterEntry &_ae) { this = _ae; } + // Flag methods. + bool HasFlag(unsigned char _flag) const { return bool(flags & _flag); } + void AddFlags(unsigned char _flags) { flags |= _flags; } + void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } + void SetFlag(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_FLAG) _flag, bool _value) { + if (_value) { + AddFlags(_flag); + } else { + RemoveFlags(_flag); + } + } + void SetFlags(unsigned char _flags) { flags = _flags; } + // State methods. + bool HasTriesLeft() const { return tries > 0 || tries == -1; } + bool IsActive() const { return HasFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_ACTIVE)); } + bool IsDone() const { return HasFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_DONE)); } + bool IsFailed() const { return HasFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_FAILED)); } + bool IsInvalid() const { return HasFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_INVALID)); } + bool IsValid() const { return !IsInvalid(); } + // Getters. + bool Get(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_FLAG) _flag) const { return HasFlag(_flag); } + template + T Get(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_PROP) _prop) const { + switch (_prop) { + case TASK_GETTER_ENTRY_FLAGS: + return (T)flags; + case TASK_GETTER_ENTRY_FREQUENCY: + return (T)freq; + case TASK_GETTER_ENTRY_ID: + return (T)id; + case TASK_GETTER_ENTRY_TRIES: + return (T)tries; + case TASK_GETTER_ENTRY_TIME_LAST_RUN: + return (T)time_last_run; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + return WRONG_VALUE; + } + int GetId() const { return id; } + // Setters. + void TriesDec() { tries -= tries > 0 ? 1 : 0; } + void Set(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_FLAG) _flag, bool _value = true) { + SetFlag(_flag, _value); + } + template + void Set(STRUCT_ENUM(TaskGetterEntry, ENUM_TASK_GETTER_ENTRY_PROP) _prop, T _value) { + switch (_prop) { + case TASK_GETTER_ENTRY_FLAGS: // ID (magic number). + flags = (unsigned char)_value; + return; + case TASK_GETTER_ENTRY_FREQUENCY: + freq = (int)_value; + return; + case TASK_GETTER_ENTRY_ID: + id = (int)_value; + SetFlag(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); + return; + case TASK_GETTER_ENTRY_TRIES: + tries = (short)_value; + return; + case TASK_GETTER_ENTRY_TIME_LAST_GET: + time_last_get = (datetime)_value; + return; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + } + void AddArg(MqlParam &_arg) { + // @todo: Add another value to args[]. + } + void SetArgs(ARRAY_REF(MqlParam, _args)) { + // @todo: for(). + } + // Serializers + SerializerNodeType Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "time_last_get", time_last_get); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(this, "args", args); + return SerializerNodeObject; + } + + SERIALIZER_EMPTY_STUB; +}; diff --git a/Task/TaskGetterBase.h b/Task/TaskGetterBase.h new file mode 100644 index 000000000..6261d2ce6 --- /dev/null +++ b/Task/TaskGetterBase.h @@ -0,0 +1,58 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides a base class for a task's getter. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_GETTER_BASE_H +#define TASK_GETTER_BASE_H + +/** + * TaskGetterBase class. + */ +template +class TaskGetterBase { + public: + /* Special methods */ + + /** + * Class constructor. + */ + TaskGetterBase() {} + + /* Main methods */ + + /** + * Gets a copy of structure. + */ + virtual TS Get(const TaskGetterEntry &_entry) = NULL; +}; + +#endif // TASK_GETTER_BASE_H diff --git a/Task/TaskManager.h b/Task/TaskManager.h new file mode 100644 index 000000000..2ae9dd44f --- /dev/null +++ b/Task/TaskManager.h @@ -0,0 +1,113 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides task management. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_MANAGER_H +#define TASK_MANAGER_H + +// Includes. +#include "../DictObject.mqh" +#include "../SerializerConverter.mqh" +#include "../SerializerJson.mqh" +#include "Task.struct.h" +#include "TaskObject.h" + +class TaskManager { + protected: + DictStruct> tasks; + // DictObject> tasks; + // DictObject> tasks; // @todo: Which one? + + /* Protected methods */ + + /** + * Init code (called on constructor). + */ + void Init() {} + + public: + /* Special methods */ + + /** + * Class constructor. + */ + TaskManager() { Init(); } + + /** + * Class deconstructor. + */ + ~TaskManager() {} + + /* Adder methods */ + + /** + * Adds new task. + */ + bool Add(Task *_task) { + Ref _ref = _task; + return tasks.Push(_ref); + } + + /** + * Adds new task. + */ + bool Add(string _entry_str) { + TaskEntry _entry; + SerializerConverter::FromString(_entry_str).ToObject(_entry); + Ref _task = new Task(_entry); + return Add(_task.Ptr()); + } + + /** + * Adds new object task. + */ + template + bool Add(TaskObject *_task_obj) { + return Add((Task *)_task_obj); + } + + /* Processing methods */ + + /** + * Process tasks. + */ + bool Process() { + bool _result = true; + for (DictStructIterator> _iter = tasks.Begin(); _iter.IsValid(); ++_iter) { + Task *_task = _iter.Value().Ptr(); + _result &= _task.Process(); + } + return _result; + } +}; + +#endif // TASK_MANAGER_H diff --git a/Task/TaskObject.h b/Task/TaskObject.h new file mode 100644 index 000000000..f673f7945 --- /dev/null +++ b/Task/TaskObject.h @@ -0,0 +1,110 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides integration with tasks (manages conditions and actions). + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_OBJECT_H +#define TASK_OBJECT_H + +// Includes. +#include "Task.h" + +template +class TaskObject : public Task { + protected: + TA *obja; + TC *objc; + + public: + /* Special methods */ + + /** + * Default class constructor. + */ + TaskObject() {} + + /** + * Class constructor with task entry as argument. + */ + TaskObject(TaskEntry &_tentry, TA *_obja = NULL, TC *_objc = NULL) : obja(_obja), objc(_objc), Task(_tentry) {} + + /** + * Class deconstructor. + */ + ~TaskObject() {} + + /* Virtual methods */ + + /** + * Process tasks. + * + * @return + * Returns true when tasks has been processed. + */ + virtual bool Process() { + bool _result = true; + for (DictStructIterator iter = tasks.Begin(); iter.IsValid(); ++iter) { + TaskEntry _entry = iter.Value(); + _result &= Process(_entry); + } + return _result; + } + + /** + * Process task entry. + * + * @return + * Returns true when tasks has been processed. + */ + virtual bool Process(TaskEntry &_entry) { + bool _result = false; + if (_entry.IsActive()) { + if (Object::IsValid(objc) && objc.Check(_entry.GetCondition()) && Object::IsValid(obja)) { + obja.Run(_entry.GetAction()); + _entry.Set(STRUCT_ENUM(TaskEntry, TASK_ENTRY_PROP_LAST_PROCESS), TimeCurrent()); + if (_entry.IsDone()) { + _entry.SetFlag(TASK_ENTRY_FLAG_IS_DONE, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_DONE))); + _entry.SetFlag(TASK_ENTRY_FLAG_IS_FAILED, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_FAILED))); + _entry.SetFlag(TASK_ENTRY_FLAG_IS_INVALID, + _entry.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_INVALID))); + _entry.RemoveFlags(TASK_ENTRY_FLAG_IS_ACTIVE); + } + _result = true; + } + } + return _result; + } + + /* Other methods */ +}; +#endif // TASK_OBJECT_H diff --git a/Task/TaskSetter.h b/Task/TaskSetter.h new file mode 100644 index 000000000..a08f01957 --- /dev/null +++ b/Task/TaskSetter.h @@ -0,0 +1,133 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides integration with task's actions. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_SETTER_H +#define TASK_SETTER_H + +// Includes. +//#include "TaskSetter.enum.h" +#include "TaskSetter.struct.h" +#include "TaskSetterBase.h" + +/** + * TaskSetter class. + */ +template +class TaskSetter : protected TaskSetterBase { + protected: + // Protected class variables. + TaskSetterEntry entry; // Setter entry. + TO obj; // Object to run the action on. + + public: + /* Special methods */ + + /** + * Default class constructor. + */ + TaskSetter() {} + + /** + * Class constructor with an entry as argument. + */ + TaskSetter(TaskSetterEntry &_entry) : entry(_entry) {} + + /* Main methods */ + + /** + * Runs a current stored action. + */ + bool Set(const TS &_entry_value) { + bool _result = obj.Set(entry, _entry_value); + entry.Set(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_TIME_LAST_GET), TimeCurrent()); + entry.TriesDec(); + return _result; + } + + /* Setters */ + + /** + * Gets an entry's flag. + */ + bool Get(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_FLAG) _flag) const { return entry.Get(_flag); } + + /** + * Gets an entry's property value. + */ + template + T Get(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_PROP) _prop) const { + entry.Get(_prop); + } + + /** + * Gets a reference to the object. + */ + TO *GetObject() { return GetPointer(obj); } + + /* Setters */ + + /** + * Sets an entry's flag. + */ + void Set(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_FLAG) _flag, bool _value = true) { + entry.Set(_flag, _value); + } + + /** + * Sets an entry's property value. + */ + template + void Set(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_PROP) _prop, T _value) { + entry.Set(_prop, _value); + } + + /* TaskSetterBase methods */ + + /** + * Sets an entry value. + */ + bool Set(const TaskSetterEntry &_entry, const TS &_entry_value) { + bool _result = false; + switch (_entry.GetId()) { + case 0: + _result = Set(_entry_value); + break; + default: + SetUserError(ERR_INVALID_PARAMETER); + break; + } + return _result; + } +}; + +#endif // TASK_SETTER_H diff --git a/Task/TaskSetter.struct.h b/Task/TaskSetter.struct.h new file mode 100644 index 000000000..d33715ca3 --- /dev/null +++ b/Task/TaskSetter.struct.h @@ -0,0 +1,170 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Includes TaskSetter's structures. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Includes. +#include "../Data.struct.h" +#include "../Std.h" +#include "Task.enum.h" + +/* Entry for TaskSetter class. */ +struct TaskSetterEntry { + public: + /* Enumerations */ + + // Defines action entry properties. + enum ENUM_TASK_SETTER_ENTRY_PROP { + TASK_SETTER_ENTRY_FLAGS, + TASK_SETTER_ENTRY_FREQUENCY, + TASK_SETTER_ENTRY_ID, + TASK_SETTER_ENTRY_TRIES, + TASK_SETTER_ENTRY_TIME_LAST_GET, + }; + // Defines action entry flags. + enum ENUM_TASK_SETTER_ENTRY_FLAG { + TASK_SETTER_ENTRY_FLAG_NONE = 0 << 0, + TASK_SETTER_ENTRY_FLAG_IS_ACTIVE = 1 << 0, + TASK_SETTER_ENTRY_FLAG_IS_DONE = 1 << 1, + TASK_SETTER_ENTRY_FLAG_IS_FAILED = 1 << 2, + TASK_SETTER_ENTRY_FLAG_IS_INVALID = 1 << 3, + }; + + protected: + unsigned char flags; /* TaskSetter flags. */ + datetime time_last_get; /* Time of the successful get. */ + int freq; /* How often to run (0 for no limit). */ + int id; /* TaskSetter's enum ID. */ + short tries; /* Number of retries left (-1 for unlimited). */ + DataParamEntry args[]; /* TaskSetter arguments. */ + protected: + // Protected methods. + void Init() { SetFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); } + + public: + // Constructors. + TaskSetterEntry() : flags(0), freq(60), id(WRONG_VALUE), time_last_get(0), tries(-1) { Init(); } + TaskSetterEntry(int _id) + : flags(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_ACTIVE)), + id(_id), + freq(60), + time_last_get(0), + tries(-1) { + Init(); + } + TaskSetterEntry(TaskSetterEntry &_ae) { this = _ae; } + // Flag methods. + bool HasFlag(unsigned char _flag) const { return bool(flags & _flag); } + void AddFlags(unsigned char _flags) { flags |= _flags; } + void RemoveFlags(unsigned char _flags) { flags &= ~_flags; } + void SetFlag(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_FLAG) _flag, bool _value) { + if (_value) { + AddFlags(_flag); + } else { + RemoveFlags(_flag); + } + } + void SetFlags(unsigned char _flags) { flags = _flags; } + // State methods. + bool HasTriesLeft() const { return tries > 0 || tries == -1; } + bool IsActive() const { return HasFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_ACTIVE)); } + bool IsDone() const { return HasFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_DONE)); } + bool IsFailed() const { return HasFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_FAILED)); } + bool IsInvalid() const { return HasFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_INVALID)); } + bool IsValid() const { return !IsInvalid(); } + // Setters. + bool Get(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_FLAG) _flag) const { return HasFlag(_flag); } + template + T Get(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_PROP) _prop) const { + switch (_prop) { + case TASK_SETTER_ENTRY_FLAGS: + return (T)flags; + case TASK_SETTER_ENTRY_FREQUENCY: + return (T)freq; + case TASK_SETTER_ENTRY_ID: + return (T)id; + case TASK_SETTER_ENTRY_TRIES: + return (T)tries; + case TASK_SETTER_ENTRY_TIME_LAST_RUN: + return (T)time_last_run; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + return WRONG_VALUE; + } + int GetId() const { return id; } + // Setters. + void TriesDec() { tries -= tries > 0 ? 1 : 0; } + void Set(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_FLAG) _flag, bool _value = true) { + SetFlag(_flag, _value); + } + template + void Set(STRUCT_ENUM(TaskSetterEntry, ENUM_TASK_SETTER_ENTRY_PROP) _prop, T _value) { + switch (_prop) { + case TASK_SETTER_ENTRY_FLAGS: // ID (magic number). + flags = (unsigned char)_value; + return; + case TASK_SETTER_ENTRY_FREQUENCY: + freq = (int)_value; + return; + case TASK_SETTER_ENTRY_ID: + id = (int)_value; + SetFlag(STRUCT_ENUM(TaskSetterEntry, TASK_SETTER_ENTRY_FLAG_IS_INVALID), id == WRONG_VALUE); + return; + case TASK_SETTER_ENTRY_TRIES: + tries = (short)_value; + return; + case TASK_SETTER_ENTRY_TIME_LAST_GET: + time_last_get = (datetime)_value; + return; + default: + break; + } + SetUserError(ERR_INVALID_PARAMETER); + } + void AddArg(MqlParam &_arg) { + // @todo: Add another value to args[]. + } + void SetArgs(ARRAY_REF(MqlParam, _args)) { + // @todo: for(). + } + // Serializers + SerializerNodeType Serialize(Serializer &s) { + s.Pass(THIS_REF, "flags", flags); + s.Pass(THIS_REF, "id", id); + s.Pass(THIS_REF, "time_last_get", time_last_get); + s.Pass(THIS_REF, "tries", tries); + s.PassEnum(THIS_REF, "freq", freq); + s.PassArray(this, "args", args); + return SerializerNodeObject; + } + + SERIALIZER_EMPTY_STUB; +}; diff --git a/Task/TaskSetterBase.h b/Task/TaskSetterBase.h new file mode 100644 index 000000000..2b6f0ca84 --- /dev/null +++ b/Task/TaskSetterBase.h @@ -0,0 +1,58 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Provides a base class for a task's getter. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASK_SETTER_BASE_H +#define TASK_SETTER_BASE_H + +/** + * TaskSetterBase class. + */ +template +class TaskSetterBase { + public: + /* Special methods */ + + /** + * Class constructor. + */ + TaskSetterBase() {} + + /* Main methods */ + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const TS &_entry_value) = NULL; +}; + +#endif // TASK_SETTER_BASE_H diff --git a/Task/Taskable.h b/Task/Taskable.h new file mode 100644 index 000000000..f6adcae25 --- /dev/null +++ b/Task/Taskable.h @@ -0,0 +1,80 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Defines Taskable class. + */ + +#ifndef __MQL__ +// Allows the preprocessor to include a header file when it is needed. +#pragma once +#endif + +// Prevents processing this includes file for the second time. +#ifndef TASKABLE_H +#define TASKABLE_H + +// Includes. +#include "../Object.mqh" +#include "TaskAction.h" +#include "TaskCondition.h" +#include "TaskGetter.h" +#include "TaskSetter.h" + +/** + * Taskable class. + */ +template +class Taskable : public Object { + public: + /* Special methods */ + + /** + * Class constructor with default arguments. + */ + Taskable() : Object(GetPointer(this), __LINE__) {} + + /* Virtual methods */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) = NULL; + + /** + * Gets a copy of structure. + */ + virtual TS Get(const TaskGetterEntry &_entry) = NULL; + + /** + * Runs an action. + */ + virtual bool Run(const TaskActionEntry &_entry) = NULL; + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const TS &_entry_value) = NULL; +}; + +#endif // TASKABLE_H diff --git a/tests/TaskTest.mq4 b/Task/tests/Task.test.mq4 similarity index 97% rename from tests/TaskTest.mq4 rename to Task/tests/Task.test.mq4 index 9dd81f28c..3ab46d4b9 100644 --- a/tests/TaskTest.mq4 +++ b/Task/tests/Task.test.mq4 @@ -25,4 +25,4 @@ */ // Includes. -#include "TaskTest.mq5" +#include "Task.test.mq5" diff --git a/Task/tests/Task.test.mq5 b/Task/tests/Task.test.mq5 new file mode 100644 index 000000000..24191b6f3 --- /dev/null +++ b/Task/tests/Task.test.mq5 @@ -0,0 +1,90 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of Task class. + */ + +// Forward declaration. +struct DataParamEntry; + +// Includes. +#include "../../Test.mqh" +#include "../Task.h" + +// Define test classes. +class ConditionType1 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ConditionType2 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ActionType1 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class ActionType2 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class TaskType1 : public Taskable { + bool Check(const TaskConditionEntry &_entry) { return true; } + MqlParam Get(const TaskGetterEntry &_entry) { + MqlParam _result; + return _result; + } + bool Run(const TaskActionEntry &_entry) { return true; } + bool Set(const TaskSetterEntry &_entry, const MqlParam &_entry_value) { return true; } +}; + +// Test 1. +bool TestTask01() { + bool _result = true; + Task _task1; + TaskAction _taction1; + TaskAction _taction2; + TaskCondition _tcond1; + TaskCondition _tcond2; + return _result; +} + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + _result &= TestTask01(); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/tests/ConditionTest.mq4 b/Task/tests/TaskAction.test.mq4 similarity index 93% rename from tests/ConditionTest.mq4 rename to Task/tests/TaskAction.test.mq4 index 89bff6caa..080ea3c49 100644 --- a/tests/ConditionTest.mq4 +++ b/Task/tests/TaskAction.test.mq4 @@ -21,8 +21,8 @@ /** * @file - * Test functionality of Condition class. + * Test functionality of TaskAction class. */ // Includes. -#include "ConditionTest.mq5" +#include "TaskAction.test.mq5" diff --git a/Task/tests/TaskAction.test.mq5 b/Task/tests/TaskAction.test.mq5 new file mode 100644 index 000000000..981d8c760 --- /dev/null +++ b/Task/tests/TaskAction.test.mq5 @@ -0,0 +1,102 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskAction class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../TaskAction.h" + +enum ENUM_TASK_ACTION_TEST { + TASK_ACTION_TEST01 = 1, + TASK_ACTION_TEST02 = 2, + TASK_ACTION_TEST03 = 3, +}; + +class TaskActionTest01 : public TaskActionBase { + protected: + long sum; + + public: + TaskActionTest01() : sum(0){}; + long GetSum() { return sum; } + bool Run(const TaskActionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return true; + } +}; + +class TaskActionTest02 : public TaskActionBase { + protected: + long sum; + + public: + TaskActionTest02() : sum(0){}; + long GetSum() { return sum; } + bool Run(const TaskActionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return true; + } +}; + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + // Test01 + TaskActionTest01 _atest01; + TaskActionEntry _entry01(TASK_ACTION_TEST01); + TaskAction _action01(_entry01, &_atest01); + _action01.Run(); + _action01.Set(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_ID), TASK_ACTION_TEST02); + _action01.Run(); + _action01.Set(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_ID), TASK_ACTION_TEST03); + _action01.Run(); + assertTrueOrFail(_result && _action01.GetObject().GetSum() == 6, "Fail!"); + // Test02. + TaskActionTest02 _atest02; + TaskActionEntry _entry02(TASK_ACTION_TEST02); + TaskAction _action02(_entry02, &_atest02); + _action02.Run(); + _action02.Set(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_ID), TASK_ACTION_TEST02); + _action02.Run(); + _action02.Set(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_ID), TASK_ACTION_TEST03); + _action02.Run(); + assertTrueOrFail(_result && _action02.GetObject().GetSum() == 7, "Fail!"); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/TaskCondition.test.mq4 b/Task/tests/TaskCondition.test.mq4 new file mode 100644 index 000000000..961feb86a --- /dev/null +++ b/Task/tests/TaskCondition.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskCondition class. + */ + +// Includes. +#include "TaskCondition.test.mq5" diff --git a/Task/tests/TaskCondition.test.mq5 b/Task/tests/TaskCondition.test.mq5 new file mode 100644 index 000000000..30d96f5e4 --- /dev/null +++ b/Task/tests/TaskCondition.test.mq5 @@ -0,0 +1,102 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskCondition class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../TaskCondition.h" + +enum ENUM_TASK_CONDITION_TEST { + TASK_CONDITION_TEST01 = 1, + TASK_CONDITION_TEST02 = 2, + TASK_CONDITION_TEST03 = 3, +}; + +class TaskConditionTest01 : public TaskConditionBase { + protected: + long sum; + + public: + TaskConditionTest01() : sum(0){}; + long GetSum() { return sum; } + bool Check(const TaskConditionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return true; + } +}; + +class TaskConditionTest02 : public TaskConditionBase { + protected: + long sum; + + public: + TaskConditionTest02() : sum(0){}; + long GetSum() { return sum; } + bool Check(const TaskConditionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return true; + } +}; + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + // Test01 + TaskConditionTest01 _test01; + TaskConditionEntry _entry01(TASK_CONDITION_TEST01); + TaskCondition _cond01(_entry01, &_test01); + _result &= _cond01.Check(); + _cond01.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_ID), TASK_CONDITION_TEST02); + _result &= _cond01.Check(); + _cond01.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_ID), TASK_CONDITION_TEST03); + _result &= _cond01.Check(); + assertTrueOrFail(_result && _cond01.GetObject().GetSum() == 6, "Fail!"); + // Test02 + TaskConditionTest02 _test02; + TaskConditionEntry _entry02(TASK_CONDITION_TEST02); + TaskCondition _cond02(_entry02, &_test02); + _result &= _cond02.Check(); + _cond02.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_ID), TASK_CONDITION_TEST02); + _result &= _cond02.Check(); + _cond02.Set(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_ID), TASK_CONDITION_TEST03); + _result &= _cond02.Check(); + assertTrueOrFail(_result && _cond01.GetObject().GetSum() == 6, "Fail!"); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/TaskGetter.test.mq4 b/Task/tests/TaskGetter.test.mq4 new file mode 100644 index 000000000..688c4942e --- /dev/null +++ b/Task/tests/TaskGetter.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskGetter class. + */ + +// Includes. +#include "TaskGetter.test.mq5" diff --git a/Task/tests/TaskGetter.test.mq5 b/Task/tests/TaskGetter.test.mq5 new file mode 100644 index 000000000..8a8761597 --- /dev/null +++ b/Task/tests/TaskGetter.test.mq5 @@ -0,0 +1,91 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskGetter class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../TaskGetter.h" + +enum ENUM_TASK_GETTER_TEST { + TASK_GETTER_TEST01 = 1, + TASK_GETTER_TEST02 = 2, + TASK_GETTER_TEST03 = 3, +}; + +struct TaskGetterTest01Data { + int value; + TaskGetterTest01Data() : value(0) {} + int GetValue() { return value; } + void SetValue(int _value) { value = _value; } +}; + +class TaskGetterTest01 : public TaskGetter { + protected: + TaskGetterTest01Data data; + + public: + TaskGetterTest01(){}; + // long GetSum() { return sum; } + TaskGetterTest01Data Get() { return TaskGetter::Get(); } + TaskGetterTest01Data Get(const TaskGetterEntry &_entry) { + data.SetValue(_entry.GetId()); + PrintFormat("Get: %s; value=%d", __FUNCSIG__, data.GetValue()); + return data; + } +}; + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + int _sum = 0; + // Test01 + TaskGetterTest01 _test01; + TaskGetterEntry _entry01(TASK_GETTER_TEST01); + _result &= _entry01.Get(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_ACTIVE)); + _test01.Set(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_ID), TASK_GETTER_TEST01); + _sum += _test01.Get().GetValue(); + _sum += _test01.Get(_entry01).GetValue(); + _test01.Set(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_ID), TASK_GETTER_TEST02); + _sum += _test01.Get().GetValue(); + _sum += _test01.Get(_entry01).GetValue(); + _test01.Set(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_ID), TASK_GETTER_TEST03); + _sum += _test01.Get().GetValue(); + _sum += _test01.Get(_entry01).GetValue(); + assertTrueOrFail(_result && _sum == 9, "Fail!"); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/TaskManager.test.mq4 b/Task/tests/TaskManager.test.mq4 new file mode 100644 index 000000000..1092ba332 --- /dev/null +++ b/Task/tests/TaskManager.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskManager class. + */ + +// Includes. +#include "TaskManager.test.mq5" diff --git a/Task/tests/TaskManager.test.mq5 b/Task/tests/TaskManager.test.mq5 new file mode 100644 index 000000000..4ade617b8 --- /dev/null +++ b/Task/tests/TaskManager.test.mq5 @@ -0,0 +1,111 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskManager class. + */ + +// Forward declaration. +struct DataParamEntry; + +// Includes. +#include "../../Test.mqh" +#include "../TaskManager.h" + +// Define test classes. +class ConditionType1 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ConditionType2 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ActionType1 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class ActionType2 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class TaskType1 : public Taskable { + bool Check(const TaskConditionEntry &_entry) { return true; } + MqlParam Get(const TaskGetterEntry &_entry) { + MqlParam _result; + return _result; + } + bool Run(const TaskActionEntry &_entry) { return true; } + bool Set(const TaskSetterEntry &_entry, const MqlParam &_entry_value) { return true; } +}; + +// Test 1. +bool TestTaskManager01() { + bool _result = true; + ActionType1 _actionobj1; + ActionType2 _actionobj2; + ConditionType1 _condobj1; + ConditionType2 _condobj2; + TaskManager _tsm; + TaskActionEntry _aentry(1); + TaskConditionEntry _centry(1); + TaskEntry _tentry(_aentry, _centry); + Ref> _taskobj01 = + new TaskObject(_tentry, &_actionobj1, &_condobj1); + Ref> _taskobj02 = + new TaskObject(_tentry, &_actionobj2, &_condobj1); + Ref> _taskobj03 = + new TaskObject(_tentry, &_actionobj1, &_condobj2); + Ref> _taskobj04 = + new TaskObject(_tentry, &_actionobj2, &_condobj2); + _tsm.Add(_taskobj01.Ptr()); + _tsm.Add(_taskobj02.Ptr()); + _tsm.Add(_taskobj03.Ptr()); + _tsm.Add(_taskobj04.Ptr()); + + // @todo: Need a way to test if object was added properly. + _tsm.Add("{\"aentry\": {\"id\": 1}, \"centry\": {\"id\": 2}}"); + + _tsm.Process(); + // @todo: Print via ToString(). + return _result; +} + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + _result &= TestTaskManager01(); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/TaskObject.test.mq4 b/Task/tests/TaskObject.test.mq4 new file mode 100644 index 000000000..a6d856946 --- /dev/null +++ b/Task/tests/TaskObject.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskObject class. + */ + +// Includes. +#include "TaskObject.test.mq5" diff --git a/Task/tests/TaskObject.test.mq5 b/Task/tests/TaskObject.test.mq5 new file mode 100644 index 000000000..9cbe84c1a --- /dev/null +++ b/Task/tests/TaskObject.test.mq5 @@ -0,0 +1,96 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2022, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskObject class. + */ + +// Forward declaration. +struct DataParamEntry; + +// Includes. +#include "../../Test.mqh" +#include "../TaskObject.h" + +// Define test classes. +class ConditionType1 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ConditionType2 : public TaskConditionBase { + public: + bool Check(const TaskConditionEntry &_entry) { return true; } +}; +class ActionType1 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class ActionType2 : public TaskActionBase { + public: + bool Run(const TaskActionEntry &_entry) { return true; } +}; +class TaskType1 : public Taskable { + bool Check(const TaskConditionEntry &_entry) { return true; } + MqlParam Get(const TaskGetterEntry &_entry) { + MqlParam _result; + return _result; + } + bool Run(const TaskActionEntry &_entry) { return true; } + bool Set(const TaskSetterEntry &_entry, const MqlParam &_entry_value) { return true; } +}; + +// Test 1. +bool TestTaskObject01() { + bool _result = true; + ActionType1 _actionobj1; + ActionType2 _actionobj2; + ConditionType1 _condobj1; + ConditionType2 _condobj2; + TaskActionEntry _aentry(1); + TaskConditionEntry _centry(1); + TaskEntry _tentry(_aentry, _centry); + TaskObject _taskobj01(_tentry, &_actionobj1, &_condobj1); + TaskObject _taskobj02(_tentry, &_actionobj2, &_condobj1); + TaskObject _taskobj03(_tentry, &_actionobj1, &_condobj2); + TaskObject _taskobj04(_tentry, &_actionobj2, &_condobj2); + return _result; +} + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + _result &= TestTaskObject01(); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/TaskSetter.test.mq4 b/Task/tests/TaskSetter.test.mq4 new file mode 100644 index 000000000..8c3ebc2c1 --- /dev/null +++ b/Task/tests/TaskSetter.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskSetter class. + */ + +// Includes. +#include "TaskSetter.test.mq5" diff --git a/Task/tests/TaskSetter.test.mq5 b/Task/tests/TaskSetter.test.mq5 new file mode 100644 index 000000000..50fb1688e --- /dev/null +++ b/Task/tests/TaskSetter.test.mq5 @@ -0,0 +1,85 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TaskSetter class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../TaskSetter.h" + +enum ENUM_TASK_SETTER_TEST { + TASK_SETTER_TEST01 = 1, + TASK_SETTER_TEST02 = 2, + TASK_SETTER_TEST03 = 3, +}; + +struct TaskSetterTest01Data { + int value; + TaskSetterTest01Data(int _value) : value(_value) {} + int GetValue() const { return value; } + void SetValue(int _value) { value = _value; } +}; + +class TaskSetterTest01 : protected TaskSetterBase { + protected: + TaskSetterTest01Data data; + + public: + TaskSetterTest01() : data(0){}; + int GetValue() const { return data.GetValue(); } + bool Set(const TaskSetterEntry &_entry, const TaskSetterTest01Data &_entry_value) { + data.SetValue(data.GetValue() + _entry_value.GetValue()); + PrintFormat("Set: %s; value=%d", __FUNCSIG__, data.GetValue()); + return true; + } +}; + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + // Test01 + TaskSetterEntry _entry01(TASK_SETTER_TEST01); + TaskSetterTest01 _test01; + TaskSetterTest01Data _data_entry(1); + _result &= _test01.Set(_entry01, _data_entry); + _data_entry.SetValue(2); + _result &= _test01.Set(_entry01, _data_entry); + _data_entry.SetValue(3); + _result &= _test01.Set(_entry01, _data_entry); + assertTrueOrFail(_result && _test01.GetValue() == 6, "Fail!"); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Task/tests/Taskable.car.test.mq4 b/Task/tests/Taskable.car.test.mq4 new file mode 100644 index 000000000..ad723ca6d --- /dev/null +++ b/Task/tests/Taskable.car.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of Taskable class. + */ + +// Includes. +#include "Taskable.car.test.mq5" diff --git a/Task/tests/Taskable.car.test.mq5 b/Task/tests/Taskable.car.test.mq5 new file mode 100644 index 000000000..db002a564 --- /dev/null +++ b/Task/tests/Taskable.car.test.mq5 @@ -0,0 +1,203 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of Task class. + */ + +// Forward declaration. +struct DataParamEntry; + +// Includes. +#include "../../Data.struct.h" +#include "../../Test.mqh" +#include "../Task.h" + +// Implements test classes. +class Car : public Taskable { + protected: + enum ENUM_CAR_ACTION { + CAR_ACTION_NONE = 0, + CAR_ACTION_CONTINUE, + CAR_ACTION_SPEED_DEC_BY, + CAR_ACTION_SPEED_INC_BY, + CAR_ACTION_SPEED_SET_BY, + CAR_ACTION_STOP, + }; + enum ENUM_CAR_COND { + CAR_COND_NONE = 0, + CAR_COND_NEEDS_SERVICE, + CAR_COND_IS_MOVING_BACKWARD, + CAR_COND_IS_MOVING_FORWARD, + CAR_COND_IS_SPEED_MAX, + CAR_COND_IS_STOPPED, + }; + enum ENUM_CAR_PROP { + CAR_PROP_NONE = 0, + CAR_PROP_MILEAGE_CURR, + CAR_PROP_MILEAGE_MAX, + CAR_PROP_SPEED, + }; + int mileage_curr, mileage_max, speed_curr, speed_max; + + public: + Car(int _speed_max = 100, int _mileage_max = 10000) : mileage_curr(0), speed_max(_speed_max) {} + + /* Tasks */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { + case CAR_COND_NEEDS_SERVICE: + _result = mileage_curr > mileage_max; + break; + case CAR_COND_IS_MOVING_BACKWARD: + _result = speed_curr < 0; + break; + case CAR_COND_IS_MOVING_FORWARD: + _result = speed_curr > 0; + break; + case CAR_COND_IS_SPEED_MAX: + _result = speed_curr >= speed_max; + break; + case CAR_COND_IS_STOPPED: + _result = speed_curr == 0; + break; + default: + break; + } + return _result; + } + + /** + * Gets a copy of structure. + */ + virtual DataParamEntry Get(const TaskGetterEntry &_entry) { + DataParamEntry _result; + switch (_entry.GetId()) { + case CAR_PROP_MILEAGE_CURR: + _result = mileage_curr; + break; + case CAR_PROP_MILEAGE_MAX: + _result = mileage_max; + break; + case CAR_PROP_SPEED: + _result = speed_curr; + break; + default: + break; + } + return _result; + } + template + DataParamEntry Get(E _id) { + TaskGetterEntry _entry(_id); + return Get(_entry); + }; + + /** + * Runs an action. + */ + virtual bool Run(const TaskActionEntry &_entry) { + bool _result = false; + switch (_entry.GetId()) { + case CAR_ACTION_CONTINUE: + mileage_curr += speed_curr; + break; + case CAR_ACTION_SPEED_DEC_BY: + // speed_curr -= _entry.GetArg(); // @fixme + break; + case CAR_ACTION_SPEED_INC_BY: + // speed_curr += _entry.GetArg(); // @fixme + break; + case CAR_ACTION_SPEED_SET_BY: + // speed_curr = _entry.GetArg(); // @fixme + break; + case CAR_ACTION_STOP: + speed_curr = 0; + break; + default: + break; + } + return _result; + } + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const DataParamEntry &_entry_value) { + bool _result = true; + switch (_entry.GetId()) { + case CAR_PROP_MILEAGE_CURR: + // mileage_curr = _entry_value.ToValue(); // @fixme + break; + case CAR_PROP_MILEAGE_MAX: + // mileage_max = _entry_value.ToValue(); // @fixme + break; + case CAR_PROP_SPEED: + // speed_curr = _entry_value.ToValue(); // @fixme + break; + default: + _result = false; + break; + } + return _result; + } + template + DataParamEntry Set(E _id) { + TaskSetterEntry _entry(_id); + return Set(_entry); + }; +}; + +// Test if car can drive. +bool TestCarCanDrive() { + bool _result = true; + Car *_car = new Car(); + _result &= _car.Get(STRUCT_ENUM(Car, CAR_PROP_SPEED)).ToValue() == 0; + delete _car; + return _result; +} + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + // @todo + _result &= TestCarCanDrive(); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/tests/ActionTest.mq4 b/Task/tests/Taskable.test.mq4 similarity index 93% rename from tests/ActionTest.mq4 rename to Task/tests/Taskable.test.mq4 index 5aba4100d..47e309712 100644 --- a/tests/ActionTest.mq4 +++ b/Task/tests/Taskable.test.mq4 @@ -21,8 +21,8 @@ /** * @file - * Test functionality of Action class. + * Test functionality of Taskable class. */ // Includes. -#include "ActionTest.mq5" +#include "Taskable.test.mq5" diff --git a/Task/tests/Taskable.test.mq5 b/Task/tests/Taskable.test.mq5 new file mode 100644 index 000000000..16c01a8de --- /dev/null +++ b/Task/tests/Taskable.test.mq5 @@ -0,0 +1,128 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of Taskable class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../Taskable.h" + +// Defines structure. +struct TaskableIntValue { + protected: + int value; // Field to store a double type. + public: + TaskableIntValue() : value(0) {} + int GetValue() { return value; } + void SetValue(int _value) { value = _value; } +}; + +// Defines class. +class TaskTest01 : public Taskable { + protected: + int sum; + TaskableIntValue data; + + public: + TaskTest01() : sum(0){}; + int GetSum() { return sum; } + + /* Taskable methods */ + + /** + * Checks a condition. + */ + bool Check(const TaskConditionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return sum > 0; + } + + /** + * Gets a copy of structure. + */ + TaskableIntValue Get(const TaskGetterEntry &_entry) { + sum += _entry.GetId(); + data.SetValue(sum); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return data; + } + + /** + * Runs an action. + */ + bool Run(const TaskActionEntry &_entry) { + sum += _entry.GetId(); + PrintFormat("%s; sum=%d", __FUNCSIG__, sum); + return sum > 0; + } + + /** + * Sets an entry value. + */ + bool Set(const TaskSetterEntry &_entry, const TaskableIntValue &_entry_value) { + sum += _entry.GetId(); + data.SetValue(sum); + PrintFormat("%s; sum=%d", __FUNCSIG__, data.GetValue()); + return data.GetValue() > 0; + } +}; + +/** + * Implements Init event handler. + */ +int OnInit() { + bool _result = true; + TaskTest01 _test01; + // Runs a dummy action. + TaskActionEntry _entry_action(2); + _result &= _test01.Run(_entry_action); + _result &= _entry_action.Get(STRUCT_ENUM(TaskActionEntry, TASK_ACTION_ENTRY_FLAG_IS_ACTIVE)); + // Checks a dummy condition. + TaskConditionEntry _entry_cond(2); + _result &= _test01.Check(_entry_cond); + _result &= _entry_cond.Get(STRUCT_ENUM(TaskConditionEntry, TASK_CONDITION_ENTRY_FLAG_IS_ACTIVE)); + // Gets a dummy value. + TaskGetterEntry _entry_get(2); + TaskableIntValue _value = _test01.Get(_entry_get); + _result &= _entry_get.Get(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_ACTIVE)); + // Sets a dummy value. + TaskSetterEntry _entry_set(2); + _result &= _test01.Set(_entry_set, _value); + _result &= _entry_get.Get(STRUCT_ENUM(TaskGetterEntry, TASK_GETTER_ENTRY_FLAG_IS_ACTIVE)); + // Checks the results. + assertTrueOrFail(_result && _test01.GetSum() == 8, "Fail!"); + _result &= GetLastError() == ERR_NO_ERROR; + return (_result ? INIT_SUCCEEDED : INIT_FAILED); +} + +/** + * Implements Tick event handler. + */ +void OnTick() {} + +/** + * Implements Deinit event handler. + */ +void OnDeinit(const int reason) {} diff --git a/Tick/TickManager.h b/Tick/TickManager.h new file mode 100644 index 000000000..befa1d4a9 --- /dev/null +++ b/Tick/TickManager.h @@ -0,0 +1,94 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file + * Implements TickManager class. + */ + +// Includes. +#include "../BufferStruct.mqh" +#include "TickManager.h" +//#include "TickManager.struct.h" + +/** + * Class to store and manage tick data. + */ +class TickManager : public BufferStruct { + protected: + /* Protected methods */ + + /** + * Init code (called on constructor). + */ + void Init() { + AddFlags(DICT_FLAG_FILL_HOLES_UNSORTED); + SetOverflowListener(TickManagerOverflowListener, 10); + } + + public: + /** + * Default class constructor. + */ + TickManager() { Init(); } + + /** + * Class constructor with parameters. + */ + // TickManager(TickManagerParams &_params) : params(_params) { Init(); } + + /* Getters */ + + /** + * Gets a property value. + */ + // template + // T Get(STRUCT_ENUM(TickManagerParams, ENUM_TSM_PARAMS_PROP) _prop) + // { return params.Get(_prop); } + + /* Setters */ + + /** + * Sets a property value. + */ + // template + // void Set(STRUCT_ENUM(TickManagerParams, ENUM_TSM_PARAMS_PROP) _prop, T _value) + // { params.Set(_prop, _value); } + + /* Other methods */ + + /** + * Function should return true if resize can be made, or false to overwrite current slot. + */ + static bool TickManagerOverflowListener(ENUM_DICT_OVERFLOW_REASON _reason, int _size, int _num_conflicts) { + switch (_reason) { + case DICT_OVERFLOW_REASON_FULL: + // We allow resize if dictionary size is less than 86400 slots. + return _size < 86400; + case DICT_OVERFLOW_REASON_TOO_MANY_CONFLICTS: + default: + // When there is too many conflicts, we just reject doing resize, so first conflicting slot will be reused. + break; + } + return false; + } +}; diff --git a/Tick/tests/TickManager.test.mq4 b/Tick/tests/TickManager.test.mq4 new file mode 100644 index 000000000..0956d46bf --- /dev/null +++ b/Tick/tests/TickManager.test.mq4 @@ -0,0 +1,28 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TickManager class. + */ + +// Includes. +#include "TickManager.test.mq5" diff --git a/Tick/tests/TickManager.test.mq5 b/Tick/tests/TickManager.test.mq5 new file mode 100644 index 000000000..a2b36637a --- /dev/null +++ b/Tick/tests/TickManager.test.mq5 @@ -0,0 +1,53 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, EA31337 Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of TickManager class. + */ + +// Includes. +#include "../../Test.mqh" +#include "../TickManager.h" + +TickManager tm; + +/** + * Implements OnInit(). + */ +int OnInit() { + bool _result = true; + // ... + return _result && GetLastError() == ERR_NO_ERROR ? INIT_SUCCEEDED : INIT_FAILED; +} + +/** + * Implements Tick event handler. + */ +void OnTick() { + MqlTick tick; + ::SymbolInfoTick(_Symbol, tick); + tm.Add(tick, tick.time); + if (tick.time % 3600 == 0) { + PrintFormat("Ticks: %d (reserved %d); Memory %d/%d", tm.Size(), tm.ReservedSize(), + ::TerminalInfoInteger(TERMINAL_MEMORY_USED), ::TerminalInfoInteger(TERMINAL_MEMORY_TOTAL)); + } +} diff --git a/Trade.mqh b/Trade.mqh index 254f6bbff..021f29203 100644 --- a/Trade.mqh +++ b/Trade.mqh @@ -31,19 +31,19 @@ class Trade; // Includes. #include "Account.mqh" -#include "Action.enum.h" #include "Chart.mqh" -#include "Condition.enum.h" #include "Convert.mqh" #include "DictStruct.mqh" #include "Math.h" #include "Object.mqh" #include "Order.mqh" #include "OrderQuery.h" +#include "Task/TaskManager.h" +#include "Task/Taskable.h" #include "Trade.enum.h" #include "Trade.struct.h" -class Trade { +class Trade : public Taskable { public: Account account; Ref chart; @@ -51,6 +51,7 @@ class Trade { DictStruct> orders_history; DictStruct> orders_pending; Log logger; // Trade logger. + TaskManager tasks; // Tasks. TradeParams tparams; // Trade parameters. TradeStates tstates; // Trade states. TradeStats tstats; // Trade statistics. @@ -1716,44 +1717,49 @@ HistorySelect(0, TimeCurrent()); // Select history for access. } } - /* Conditions */ + /* Tasks methods */ /** - * Checks for trade condition. - * - * @param ENUM_TRADE_CONDITION _cond - * Trade condition. - * @param MqlParam[] _args - * Condition arguments. - * @return - * Returns true when the condition is met. + * Add task. */ - bool CheckCondition(ENUM_TRADE_CONDITION _cond, DataParamEntry &_args[]) { - bool _result = true; - long _arg1l = ArraySize(_args) > 0 ? DataParamEntry::ToInteger(_args[0]) : WRONG_VALUE; - long _arg2l = ArraySize(_args) > 1 ? DataParamEntry::ToInteger(_args[1]) : WRONG_VALUE; + bool AddTask(TaskEntry &_tentry) { + bool _is_valid = _tentry.IsValid(); + if (_is_valid) { + TaskObject _taskobj(_tentry, THIS_PTR, THIS_PTR); + tasks.Add(&_taskobj); + } + return _is_valid; + } + + /** + * Process tasks. + */ + void ProcessTasks() { tasks.Process(); } + + /* Tasks */ + + /** + * Checks a condition. + */ + virtual bool Check(const TaskConditionEntry &_entry) { + bool _result = false; Ref _oquery_ref; if (Get(TRADE_STATE_ORDERS_ACTIVE)) { _oquery_ref = OrderQuery::GetInstance(orders_active); } - switch (_cond) { + switch (_entry.GetId()) { case TRADE_COND_ACCOUNT: - return account.CheckCondition((ENUM_ACCOUNT_CONDITION)_args[0].integer_value); + return account.CheckCondition(_entry.GetArg(0).ToValue()); case TRADE_COND_ALLOWED_NOT: return !IsTradeAllowed(); case TRADE_COND_HAS_STATE: - _arg1l = _arg1l != WRONG_VALUE ? _arg1l : 0; - return HasState((ENUM_TRADE_STATE)_arg1l); + return HasState(_entry.GetArg(0).ToValue()); case TRADE_COND_IS_ORDER_LIMIT: return tparams.IsLimitGe(tstats); case TRADE_COND_IS_PEAK: - _arg1l = _arg1l != WRONG_VALUE ? _arg1l : 0; - _arg2l = _arg2l != WRONG_VALUE ? _arg2l : 0; - return IsPeak((ENUM_ORDER_TYPE)_arg1l, (int)_arg2l); + return IsPeak(_entry.GetArg(0).ToValue(), _entry.GetArg(1).ToValue()); case TRADE_COND_IS_PIVOT: - _arg1l = _arg1l != WRONG_VALUE ? _arg1l : 0; - _arg2l = _arg2l != WRONG_VALUE ? _arg2l : 0; - return IsPivot((ENUM_ORDER_TYPE)_arg1l, (int)_arg2l); + return IsPivot(_entry.GetArg(0).ToValue(), _entry.GetArg(1).ToValue()); case TRADE_COND_ORDERS_PROFIT_GT_01PC: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { return CalcActiveEquityInPct() >= 1; @@ -1797,42 +1803,39 @@ HistorySelect(0, TimeCurrent()); // Select history for access. // case TRADE_ORDER_CONDS_IN_TREND: // case TRADE_ORDER_CONDS_IN_TREND_NOT: default: - logger.Error(StringFormat("Invalid trade condition: %s!", EnumToString(_cond), __FUNCTION_LINE__)); - _result = false; + GetLogger().Error(StringFormat("Invalid Trade condition: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); break; } return _result; } - bool CheckCondition(ENUM_TRADE_CONDITION _cond, long _arg1) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - ArrayPushObject(_args, _param1); - return Trade::CheckCondition(_cond, _args); - } - bool CheckCondition(ENUM_TRADE_CONDITION _cond) { - ARRAY(DataParamEntry, _args); - return Trade::CheckCondition(_cond, _args); + bool Check(int _id) { + TaskConditionEntry _entry(_id); + return Check(_entry); } - /* Actions */ + /** + * Gets a copy of structure. + */ + virtual DataParamEntry Get(const TaskGetterEntry &_entry) { + DataParamEntry _result; + switch (_entry.GetId()) { + default: + break; + } + return _result; + } /** - * Execute trade action. - * - * @param ENUM_TRADE_ACTION _action - * Trade action to execute. - * @param MqlParam _args - * Trade action arguments. - * @return - * Returns true when the condition is met. + * Runs an action. */ - bool ExecuteAction(ENUM_TRADE_ACTION _action, DataParamEntry &_args[]) { - bool _result = true; + virtual bool Run(const TaskActionEntry &_entry) { + bool _result = false; Ref _oquery_ref; if (Get(TRADE_STATE_ORDERS_ACTIVE)) { _oquery_ref = OrderQuery::GetInstance(orders_active); } - switch (_action) { + switch (_entry.GetId()) { case TRADE_ACTION_CALC_LOT_SIZE: tparams.Set(TRADE_PARAM_LOT_SIZE, CalcLotSize(tparams.Get(TRADE_PARAM_RISK_MARGIN))); return tparams.Get(TRADE_PARAM_LOT_SIZE) > 0; @@ -1852,55 +1855,55 @@ HistorySelect(0, TimeCurrent()); // Select history for access. break; case TRADE_ACTION_ORDER_CLOSE_MOST_LOSS: if (Get(TRADE_STATE_ORDERS_ACTIVE) && orders_active.Size() > 0) { - _result &= _oquery_ref.Ptr() - .FindByPropViaOp(ORDER_PROP_PROFIT, - STRUCT_ENUM(OrderQuery, ORDER_QUERY_OP_LT)) - .Ptr() - .OrderClose(ORDER_REASON_CLOSED_BY_ACTION); + _result = _oquery_ref.Ptr() + .FindByPropViaOp(ORDER_PROP_PROFIT, + STRUCT_ENUM(OrderQuery, ORDER_QUERY_OP_LT)) + .Ptr() + .OrderClose(ORDER_REASON_CLOSED_BY_ACTION); RefreshActiveOrders(true, true); } break; case TRADE_ACTION_ORDER_CLOSE_MOST_PROFIT: if (Get(TRADE_STATE_ORDERS_ACTIVE) && orders_active.Size() > 0) { - _result &= _oquery_ref.Ptr() - .FindByPropViaOp(ORDER_PROP_PROFIT, - STRUCT_ENUM(OrderQuery, ORDER_QUERY_OP_GT)) - .Ptr() - .OrderClose(ORDER_REASON_CLOSED_BY_ACTION); + _result = _oquery_ref.Ptr() + .FindByPropViaOp(ORDER_PROP_PROFIT, + STRUCT_ENUM(OrderQuery, ORDER_QUERY_OP_GT)) + .Ptr() + .OrderClose(ORDER_REASON_CLOSED_BY_ACTION); RefreshActiveOrders(true, true); } break; case TRADE_ACTION_ORDER_OPEN: - return RequestSend(GetTradeOpenRequest((ENUM_ORDER_TYPE)_args[0].integer_value)); + return RequestSend(GetTradeOpenRequest(_entry.GetArg(0).ToValue())); case TRADE_ACTION_ORDERS_CLOSE_ALL: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { - _result &= OrdersCloseAll(ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseAll(ORDER_REASON_CLOSED_BY_ACTION) >= 0; RefreshActiveOrders(true); } break; case TRADE_ACTION_ORDERS_CLOSE_IN_PROFIT: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { - _result &= OrdersCloseViaProp( - ORDER_PROP_PROFIT_PIPS, (int)chart.Ptr().GetSpreadInPips(), MATH_COND_GT, - ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseViaProp( + ORDER_PROP_PROFIT_PIPS, (int)chart.Ptr().GetSpreadInPips(), MATH_COND_GT, + ORDER_REASON_CLOSED_BY_ACTION) >= 0; RefreshActiveOrders(true); } break; case TRADE_ACTION_ORDERS_CLOSE_IN_TREND: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { - _result &= OrdersCloseViaCmd(GetTrendOp(0), ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseViaCmd(GetTrendOp(0), ORDER_REASON_CLOSED_BY_ACTION) >= 0; RefreshActiveOrders(true); } break; case TRADE_ACTION_ORDERS_CLOSE_IN_TREND_NOT: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { - _result &= OrdersCloseViaCmd(Order::NegateOrderType(GetTrendOp(0)), ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseViaCmd(Order::NegateOrderType(GetTrendOp(0)), ORDER_REASON_CLOSED_BY_ACTION) >= 0; RefreshActiveOrders(true); } break; case TRADE_ACTION_ORDERS_CLOSE_BY_TYPE: if (Get(TRADE_STATE_ORDERS_ACTIVE)) { - _result &= OrdersCloseViaCmd((ENUM_ORDER_TYPE)_args[0].integer_value, ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseViaCmd(_entry.GetArg(0).ToValue(), ORDER_REASON_CLOSED_BY_ACTION) >= 0; RefreshActiveOrders(true); } break; @@ -1911,7 +1914,7 @@ HistorySelect(0, TimeCurrent()); // Select history for access. _oquery_ref.Ptr() .FindPropBySum( _order_types1, ORDER_PROP_PROFIT, ORDER_TYPE); - _result &= + _result = OrdersCloseViaCmd(Order::NegateOrderType(_order_type_profitable), ORDER_REASON_CLOSED_BY_ACTION) >= 0; } break; @@ -1922,42 +1925,41 @@ HistorySelect(0, TimeCurrent()); // Select history for access. _oquery_ref.Ptr() .FindPropBySum( _order_types2, ORDER_PROP_PROFIT, ORDER_TYPE); - _result &= OrdersCloseViaCmd(_order_type_profitable2, ORDER_REASON_CLOSED_BY_ACTION) >= 0; + _result = OrdersCloseViaCmd(_order_type_profitable2, ORDER_REASON_CLOSED_BY_ACTION) >= 0; } break; case TRADE_ACTION_ORDERS_LIMIT_SET: // Sets the new limits. - tparams.SetLimits((ENUM_TRADE_STAT_TYPE)_args[0].integer_value, (ENUM_TRADE_STAT_PERIOD)_args[1].integer_value, - (int)_args[2].integer_value); + tparams.SetLimits(_entry.GetArg(0).ToValue(), + _entry.GetArg(1).ToValue(), _entry.GetArg(2).ToValue()); // Verify the new limits. - return tparams.GetLimits((ENUM_TRADE_STAT_TYPE)_args[0].integer_value, - (ENUM_TRADE_STAT_PERIOD)_args[1].integer_value) == _args[2].integer_value; + return tparams.GetLimits(_entry.GetArg(0).ToValue(), + _entry.GetArg(1).ToValue()) == _entry.GetArg(2).ToValue(); case TRADE_ACTION_STATE_ADD: - tstates.AddState((unsigned int)_args[0].integer_value); + tstates.AddState(_entry.GetArg(0).ToValue()); default: - logger.Error(StringFormat("Invalid trade action: %s!", EnumToString(_action), __FUNCTION_LINE__)); - _result = false; + GetLogger().Error(StringFormat("Invalid Trade action: %d!", _entry.GetId(), __FUNCTION_LINE__)); + SetUserError(ERR_INVALID_PARAMETER); break; } - return _result && GetLastError() == ERR_NO_ERROR; - } - bool ExecuteAction(ENUM_TRADE_ACTION _action) { - DataParamEntry _args[]; - return Trade::ExecuteAction(_action, _args); + return _result; } - bool ExecuteAction(ENUM_TRADE_ACTION _action, long _arg1) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - ArrayPushObject(_args, _param1); - return Trade::ExecuteAction(_action, _args); + bool Run(int _id) { + TaskActionEntry _entry(_id); + return Run(_entry); } - bool ExecuteAction(ENUM_TRADE_ACTION _action, long _arg1, long _arg2) { - ARRAY(DataParamEntry, _args); - DataParamEntry _param1 = _arg1; - DataParamEntry _param2 = _arg2; - ArrayPushObject(_args, _param1); - ArrayPushObject(_args, _param2); - return Trade::ExecuteAction(_action, _args); + + /** + * Sets an entry value. + */ + virtual bool Set(const TaskSetterEntry &_entry, const DataParamEntry &_entry_value) { + bool _result = false; + switch (_entry.GetId()) { + // _entry_value.GetValue() + default: + break; + } + return _result; } /* Printer methods */ @@ -1965,7 +1967,11 @@ HistorySelect(0, TimeCurrent()); // Select history for access. /** * Returns textual representation of the Trade class. */ - string ToString() { return StringFormat("Margin required: %g/lot", GetMarginRequired()); } + string ToString() const { + // @todo + // return StringFormat("Margin required: %g/lot", GetMarginRequired()); + return ""; + } /* Class handlers */ diff --git a/Trade/TradeSignalManager.h b/Trade/TradeSignalManager.h index 4871e145c..e3e9060a1 100644 --- a/Trade/TradeSignalManager.h +++ b/Trade/TradeSignalManager.h @@ -177,7 +177,7 @@ class TradeSignalManager : Dynamic { */ string ToString() { // SerializerConverter _stub = SerializerConverter::MakeStubObject(SERIALIZER_FLAG_SKIP_HIDDEN); - return SerializerConverter::FromObject(THIS_REF, SERIALIZER_FLAG_SKIP_HIDDEN) + return SerializerConverter::FromObject(THIS_REF, SERIALIZER_FLAG_INCLUDE_ALL | SERIALIZER_FLAG_SKIP_HIDDEN) .ToString(SERIALIZER_JSON_NO_WHITESPACES); } }; diff --git a/Util.h b/Util.h index 77d7665cc..12ccb0c28 100644 --- a/Util.h +++ b/Util.h @@ -37,6 +37,57 @@ */ class Util { public: + /** + * Resizes native array and reserves space for further items by some fixed step. + */ + template + static void ArrayResize(T& _array[], int _new_size, int _resize_pool = 32) { + ::ArrayResize(_array, _new_size, (_new_size / _resize_pool + 1) * _resize_pool); + } + + /** + * Pushes item into the native array and reserves space for further items by some fixed step. + */ + template + static int ArrayPush(T& _array[], V& _value, int _resize_pool = 32) { + Util::ArrayResize(_array, ArraySize(_array) + 1, _resize_pool); + _array[ArraySize(_array) - 1] = _value; + return ArraySize(_array) - 1; + } + + /** + * Resizes native array and reserves space for further items by some fixed step. + */ + template + static T ArrayPop(T& _array[]) { + T _result = _array[ArraySize(_array) - 1]; + ::ArrayResize(_array, ArraySize(_array) - 1); + return _result; + } + + template + static T Print(T& _array[]) { + string _result; + for (int i = 0; i < ArraySize(_array); ++i) { + _result += IntegerToString(i) + ": " + (string)_array[i]; + } + return _result; + } + + /** + * Checks whether array has given value. + */ + template + static bool ArrayContains(T& _array[], const V& _value) { + for (int i = 0; i < ArraySize(_array); ++i) { + if (_array[i] == _value) { + return true; + } + } + + return false; + } + /** * Creates string-based key using given variables. */ diff --git a/tests/3D/Shaders/pixel.hlsl b/tests/3D/Shaders/pixel.hlsl new file mode 100644 index 000000000..9d94f4069 --- /dev/null +++ b/tests/3D/Shaders/pixel.hlsl @@ -0,0 +1,12 @@ +struct INPUT { + float4 position : SV_POSITION; + float4 normal : TEXCOORD0; + float3 lightdir : TEXCOORD1; + float4 color : COLOR; +}; + +float4 main(INPUT input) : SV_TARGET { + float4 ambient = {0.0, 0.2, 0.4, 1.0}; + return ambient + input.color * saturate(dot(input.lightdir, input.normal)); +} +//+------------------------------------------------------------------+ diff --git a/tests/3D/Shaders/vertex.hlsl b/tests/3D/Shaders/vertex.hlsl new file mode 100644 index 000000000..7df27058a --- /dev/null +++ b/tests/3D/Shaders/vertex.hlsl @@ -0,0 +1,39 @@ +cbuffer MVP : register(b0) +{ + matrix world; + matrix view; + matrix projection; + float3 lightdir; +}; + +struct INPUT +{ + float4 position : POSITION; + float3 normal : NORMAL; + float4 color : COLOR; +}; + +struct OUTPUT +{ + float4 position : SV_POSITION; + float3 normal : TEXCOORD0; + float3 lightdir : TEXCOORD1; + float4 color : COLOR; +}; + +OUTPUT main(INPUT input) +{ + OUTPUT output; + + input.position.w = 1.0f; + + matrix mvp = mul(mul(projection, view), world); + + output.position = mul(mvp, input.position); + + output.normal = normalize(mul(world, input.normal)); + output.lightdir = lightdir; + output.color = input.color; + + return output; +} diff --git a/tests/3DTest.mq5 b/tests/3DTest.mq5 new file mode 100644 index 000000000..63390f3da --- /dev/null +++ b/tests/3DTest.mq5 @@ -0,0 +1,133 @@ +//+------------------------------------------------------------------+ +//| EA31337 framework | +//| Copyright 2016-2021, 31337 Investments Ltd | +//| https://github.com/EA31337 | +//+------------------------------------------------------------------+ + +/* + * This file is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * Test functionality of 3D visualization classes. + */ + +#ifdef __MQL5__ + +//#define __debug__ + +// Resources. +#resource "3D/Shaders/vertex.hlsl" as string ShaderSourceVS; +#resource "3D/Shaders/pixel.hlsl" as string ShaderSourcePS; + +// Includes. +#include "../3D/Chart3D.h" +#include "../3D/Cube.h" +#include "../3D/Devices/MTDX/MTDXDevice.h" +#include "../3D/Devices/MTDX/MTDXIndexBuffer.h" +#include "../3D/Devices/MTDX/MTDXShader.h" +#include "../3D/Devices/MTDX/MTDXVertexBuffer.h" +#include "../3D/Frontends/MT5Frontend.h" +#include "../BufferStruct.mqh" +#include "../Chart.mqh" +#include "../Serializer.mqh" +#include "../Test.mqh" + +BarOHLC ChartPriceFeeder(ENUM_TIMEFRAMES _tf, int _shift) { + static Chart _chart(); + static BufferStruct idata; + + long _bar_time = _chart.GetBarTime(_shift); + unsigned int _position; + IndicatorDataEntry _entry(4); + if (idata.KeyExists(_bar_time, _position)) { + _entry = idata.GetByPos(_position); + } else { + _entry.timestamp = _chart.GetBarTime(_shift); + _entry.values[0] = (float)_chart.GetOpen(_shift); + _entry.values[1] = (float)_chart.GetHigh(_shift); + _entry.values[2] = (float)_chart.GetLow(_shift); + _entry.values[3] = (float)_chart.GetClose(_shift); + _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, true); + idata.Add(_entry, _bar_time); + } + + return BarOHLC(_entry.GetValue(0), _entry.GetValue(1), _entry.GetValue(2), + _entry.GetValue(3)); +} + +/** + * Implements OnInit(). + */ +int OnInit() { + Ref gfx_ptr = new MTDXDevice(); + + // Making a scope to ensure graphics device will be destructed as last. + { + Device* gfx = gfx_ptr.Ptr(); + + gfx.Start(new MT5Frontend()); + + Ref _shader_v = gfx.VertexShader(ShaderSourceVS, Vertex::Layout); + Ref _shader_p = gfx.PixelShader(ShaderSourcePS); + + Ref> _mesh = new Cube(1.0f, 1.0f, 1.0f); + _mesh.Ptr().SetShaderVS(_shader_v.Ptr()); + _mesh.Ptr().SetShaderPS(_shader_p.Ptr()); + + Ref _chart = new Chart3D(ChartPriceFeeder, CHART3D_TYPE_CANDLES); + + unsigned int _rand_color = rand() * 1256; + + gfx.SetCameraOrtho3D(0.0f, 0.0f, 100.0f); + gfx.SetLightDirection(0.0f, 0.0f, -1.0f); + + while (!IsStopped()) { + if ((TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) & 0x8000) != 0) { + break; + } + + gfx.Begin(0x00FFFFFF); + + static float x = 0; + x += 0.025f; + + TSR tsr; + // tsr.rotation.y = (float)sin(x) / 4.0f; + tsr.rotation.x = (float)sin(x / 2); + + gfx.PushTransform(tsr); + _chart.Ptr().Render(gfx); + gfx.PopTransform(); + + gfx.End(); + + // break; + } + } + + gfx_ptr.Ptr().Stop(); + + return (INIT_SUCCEEDED); +} +#else +/** + * Implements OnInit(). + */ +int OnInit() { + // Nothing to test in non-MT5 environment. + return (INIT_SUCCEEDED); +} +#endif diff --git a/tests/ActionTest.mq5 b/tests/ActionTest.mq5 deleted file mode 100644 index f5a8ece2c..000000000 --- a/tests/ActionTest.mq5 +++ /dev/null @@ -1,123 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * @file - * Test functionality of Action class. - */ - -// Defines. -#define ACTION_EA_ENABLED - -// Forward declaration. -struct DataParamEntry; - -// Includes. -#include "../Action.mqh" -#include "../DictObject.mqh" -#include "../EA.mqh" -#include "../Test.mqh" - -// Global variables. -Chart *chart; -EA *ea; -DictObject actions; - -// Define strategy classes. -class Stg1 : public Strategy { - public: - void Stg1(StgParams &_params, TradeParams &_tparams, ChartParams &_cparams, string _name = "Stg1") - : Strategy(_params, _tparams, _cparams, _name) {} - static Stg1 *Init(ENUM_TIMEFRAMES _tf = NULL, unsigned long _magic_no = 0, ENUM_LOG_LEVEL _log_level = V_INFO) { - ChartParams _cparams(_tf); - TradeParams _tparams(_magic_no, _log_level); - Strategy *_strat = new Stg1(stg_params_defaults, _tparams, _cparams, __FUNCTION__); - return _strat; - } - bool SignalOpen(ENUM_ORDER_TYPE _cmd, int _method, float _level, int _shift) { return true; } - bool SignalOpenFilterMethod(ENUM_ORDER_TYPE _cmd, int _method = 0) { return true; } - float SignalOpenBoost(ENUM_ORDER_TYPE _cmd, int _method = 0) { return 1.0; } - bool SignalClose(ENUM_ORDER_TYPE _cmd, int _method, float _level, int _shift) { return true; } - float PriceStop(ENUM_ORDER_TYPE _cmd, ENUM_ORDER_TYPE_VALUE _mode, int _method = 0, float _level = 0.0f) { - return _level; - } -}; - -/** - * Implements Init event handler. - */ -int OnInit() { - bool _result = true; - // Initializes chart. - chart = new Chart(); - // Initializes EA. - EAParams ea_params(__FILE__); - ea = new EA(ea_params); - _result &= ea.StrategyAdd(127); - // Check asserts. - // Confirm EA is active. - assertTrueOrReturnFalse(ea.CheckCondition(EA_COND_IS_ACTIVE), "Wrong condition: EA_COND_IS_ACTIVE!"); - // Confirm EA is enabled. - assertTrueOrReturnFalse(ea.CheckCondition(EA_COND_IS_ENABLED), "Wrong condition: EA_COND_IS_ENABLED!"); -#ifdef ACTION_EA_ENABLED - // Disables EA and confirm it's disabled. - Action *action1 = new Action(EA_ACTION_DISABLE, ea); - action1.Execute(); - assertTrueOrReturnFalse(!ea.CheckCondition(EA_COND_IS_ENABLED), "Wrong condition: EA_COND_IS_ENABLED!"); - delete action1; - // Re-enables EA and confirm it's enabled. - Action *action2 = new Action(EA_ACTION_ENABLE, ea); - action2.Execute(); - assertTrueOrReturnFalse(ea.CheckCondition(EA_COND_IS_ENABLED), "Wrong condition: EA_COND_IS_ENABLED!"); - delete action2; -#endif - _result &= GetLastError() == ERR_NO_ERROR; - - return (_result ? INIT_SUCCEEDED : INIT_FAILED); -} - -/** - * Implements Tick event handler. - */ -void OnTick() { - chart.OnTick(); - if (chart.IsNewBar()) { - unsigned int _bar_index = chart.GetBarIndex(); - switch (_bar_index) { - case 1: - break; - case 2: - break; - case 3: - break; - case 4: - break; - } - } -} - -/** - * Implements Deinit event handler. - */ -void OnDeinit(const int reason) { - delete chart; - delete ea; -} diff --git a/tests/CompileTest.mq5 b/tests/CompileTest.mq5 index 0ec9766b0..aea976adb 100644 --- a/tests/CompileTest.mq5 +++ b/tests/CompileTest.mq5 @@ -24,16 +24,26 @@ * Test compilation of all class files. */ +// 3D includes (MQL5 only). +#ifdef __MQL5__ +#include "../3D/Chart3D.h" +#include "../3D/Cube.h" +#include "../3D/Devices/MTDX/MTDXDevice.h" +#include "../3D/Devices/MTDX/MTDXIndexBuffer.h" +#include "../3D/Devices/MTDX/MTDXShader.h" +#include "../3D/Devices/MTDX/MTDXVertexBuffer.h" +#include "../3D/Frontends/MT5Frontend.h" +#endif + // Includes. #include "../Account.mqh" -#include "../Action.mqh" #include "../Array.mqh" +#include "../Task/TaskAction.h" //#include "../BasicTrade.mqh" // @removeme #include "../Buffer.mqh" #include "../BufferStruct.mqh" #include "../Chart.mqh" #include "../Collection.mqh" -#include "../Condition.mqh" #include "../Config.mqh" #include "../Convert.mqh" #include "../Database.mqh" @@ -56,6 +66,7 @@ #include "../Log.mqh" #include "../MD5.mqh" #include "../Storage/IValueStorage.h" +#include "../Task/TaskCondition.h" //#include "../MQL4.mqh" // @removeme //#include "../MQL5.mqh" // @removeme #include "../Mail.mqh" @@ -99,7 +110,14 @@ #include "../String.mqh" #include "../SummaryReport.mqh" #include "../SymbolInfo.mqh" -#include "../Task.mqh" +#include "../Task/Task.h" +#include "../Task/TaskAction.h" +#include "../Task/TaskCondition.h" +#include "../Task/TaskGetter.h" +#include "../Task/TaskManager.h" +#include "../Task/TaskObject.h" +#include "../Task/TaskSetter.h" +#include "../Task/Taskable.h" #include "../Terminal.mqh" // #include "../Tester.mqh" // @removeme #include "../Storage/ValueStorage.h" diff --git a/tests/ConditionTest.mq5 b/tests/ConditionTest.mq5 deleted file mode 100644 index bb92cf89b..000000000 --- a/tests/ConditionTest.mq5 +++ /dev/null @@ -1,195 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * @file - * Test functionality of Condition class. - */ - -// Forward declaration. -struct DataParamEntry; - -// Includes. -#include "../Condition.mqh" -#include "../DictObject.mqh" -#include "../Indicators/Indi_Demo.mqh" -#include "../Test.mqh" - -// Global variables. -Chart *chart; -DictObject conds; -int bar_processed; - -/** - * Implements Init event handler. - */ -int OnInit() { - bool _result = true; - bar_processed = 0; - chart = new Chart(PERIOD_M1); - _result &= TestAccountConditions(); - _result &= TestChartConditions(); - _result &= TestDateTimeConditions(); - _result &= TestIndicatorConditions(); - _result &= TestMarketConditions(); - _result &= TestMathConditions(); - _result &= TestOrderConditions(); - _result &= TestTradeConditions(); - _result &= GetLastError() == ERR_NO_ERROR; - return _result ? INIT_SUCCEEDED : INIT_FAILED; -} - -/** - * Implements Tick event handler. - */ -void OnTick() { - if (chart.IsNewBar()) { - // ... - bar_processed++; - } -} - -/** - * Implements Deinit event handler. - */ -void OnDeinit(const int reason) { delete chart; } - -/** - * Test account conditions. - */ -bool TestAccountConditions() { - bool _result = true; - Account *_acc = new Account(); - Condition *_cond = new Condition(ACCOUNT_COND_BAL_IN_LOSS); - assertTrueOrReturnFalse(_cond.Test() == _acc.CheckCondition(ACCOUNT_COND_BAL_IN_LOSS), - "Wrong condition: ACCOUNT_COND_BAL_IN_LOSS!"); - delete _cond; - delete _acc; - return _result; -} - -/** - * Test chart conditions. - */ -bool TestChartConditions() { - bool _result = true; - Chart *_chart = new Chart(); - Condition *_cond = new Condition(CHART_COND_ASK_BAR_PEAK, _chart); - assertTrueOrReturnFalse(_cond.Test() == _chart.CheckCondition(CHART_COND_ASK_BAR_PEAK), - "Wrong condition: CHART_COND_ASK_BAR_PEAK!"); - delete _cond; - delete _chart; - return _result; -} - -/** - * Test date time conditions. - */ -bool TestDateTimeConditions() { - bool _result = true; - DateTime *_dt = new DateTime(); - Condition *_cond1 = new Condition(DATETIME_COND_NEW_HOUR, _dt); - assertTrueOrReturnFalse(_cond1.Test() == _dt.CheckCondition(DATETIME_COND_NEW_HOUR), - "Wrong condition: DATETIME_COND_NEW_HOUR (dynamic)!"); - delete _cond1; - Condition *_cond2 = new Condition(DATETIME_COND_NEW_HOUR); - assertTrueOrReturnFalse(_cond2.Test() == DateTime::CheckCondition(DATETIME_COND_NEW_HOUR), - "Wrong condition: DATETIME_COND_NEW_HOUR (static)!"); - delete _cond2; - delete _dt; - return _result; -} - -/** - * Test indicator conditions. - */ -bool TestIndicatorConditions() { - bool _result = true; - Indi_Demo *_demo = new Indi_Demo(); - /* @fixme - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_IS_MAX, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_IS_MAX), - "Wrong condition: INDI_COND_ENTRY_IS_MAX!"); - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_IS_MIN, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_IS_MIN), - "Wrong condition: INDI_COND_ENTRY_IS_MIN!"); - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_GT_AVG, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_GT_AVG), - "Wrong condition: INDI_COND_ENTRY_GT_AVG!"); - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_GT_MED, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_GT_MED), - "Wrong condition: INDI_COND_ENTRY_GT_MED!"); - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_LT_AVG, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_LT_AVG), - "Wrong condition: INDI_COND_ENTRY_LT_AVG!"); - assertTrueOrReturnFalse( - (new Condition(INDI_COND_ENTRY_LT_MED, _demo)).Test() == _demo.CheckCondition(INDI_COND_ENTRY_LT_MED), - "Wrong condition: INDI_COND_ENTRY_LT_MED!"); - */ - delete _demo; - return _result; -} - -/** - * Test market conditions. - */ -bool TestMarketConditions() { - bool _result = true; - Market *_market = new Market(); - Condition *_cond = new Condition(MARKET_COND_IN_PEAK_HOURS, _market); - assertTrueOrReturnFalse(_cond.Test() == _market.CheckCondition(MARKET_COND_IN_PEAK_HOURS), - "Wrong condition: MARKET_COND_IN_PEAK_HOURS!"); - delete _cond; - delete _market; - return _result; -} - -/** - * Test math conditions. - */ -bool TestMathConditions() { - bool _result = true; - // @todo - return _result; -} - -/** - * Test order conditions. - */ -bool TestOrderConditions() { - bool _result = true; - // @todo - return _result; -} - -/** - * Test trade conditions. - */ -bool TestTradeConditions() { - bool _result = true; - Trade *_trade = new Trade(); - Condition *_cond = new Condition(TRADE_COND_ALLOWED_NOT, _trade); - assertTrueOrReturnFalse(_cond.Test() == _trade.CheckCondition(TRADE_COND_ALLOWED_NOT), - "Wrong condition: TRADE_COND_ALLOWED_NOT!"); - delete _cond; - delete _trade; - return _result; -} diff --git a/tests/DrawIndicatorTest.mq5 b/tests/DrawIndicatorTest.mq5 index 19786332b..0ec2fc83b 100644 --- a/tests/DrawIndicatorTest.mq5 +++ b/tests/DrawIndicatorTest.mq5 @@ -151,14 +151,9 @@ bool InitIndicators() { * Print indicators. */ bool PrintIndicators(string _prefix = "") { + ResetLastError(); for (DictIterator iter = indis.Begin(); iter.IsValid(); ++iter) { IndicatorBase *_indi = iter.Value(); - MqlParam _value = _indi.GetEntryValue(); - if (GetLastError() == ERR_INDICATOR_DATA_NOT_FOUND || - GetLastError() == ERR_USER_ERROR_FIRST + ERR_USER_INVALID_BUFF_NUM) { - ResetLastError(); - continue; - } if (_indi.Get(STRUCT_ENUM(IndicatorState, INDICATOR_STATE_PROP_IS_READY))) { PrintFormat("%s: %s: %s", _prefix, _indi.GetName(), _indi.ToString()); } diff --git a/tests/EATest.mq5 b/tests/EATest.mq5 index 31d11e524..96eb8238f 100644 --- a/tests/EATest.mq5 +++ b/tests/EATest.mq5 @@ -42,10 +42,15 @@ class EA2 : public EA { EA2(EAParams &_params) : EA(_params) {} }; +class EA3 : public EA { + public: + EA3(EAParams &_params) : EA(_params) {} +}; + // Global variables. -EA *ea; EA1 *ea1; EA2 *ea2; +EA3 *ea3; /** * Implements OnInit(). @@ -54,16 +59,6 @@ int OnInit() { // Task to export to all possible formats once per hour. TaskEntry _task_export_per_hour(EA_ACTION_EXPORT_DATA, EA_COND_ON_NEW_HOUR); - /* Initialize base class EA */ - EAParams ea_params("EA"); - // Exporting to all possible formats once per hour. - ea_params.Set(STRUCT_ENUM(EAParams, EA_PARAM_PROP_DATA_STORE), EA_DATA_STORE_ALL); - ea_params.Set(STRUCT_ENUM(EAParams, EA_PARAM_PROP_DATA_EXPORT), EA_DATA_EXPORT_ALL); - ea_params.SetTaskEntry(_task_export_per_hour); - ea = new EA(ea_params); - assertTrueOrFail(ea.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)) == "EA", - StringFormat("Invalid EA name: %s!", ea.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)))); - /* Initialize 1st custom EA */ EAParams ea_params1("EA1"); // Exporting to all possible formats once per hour. @@ -71,9 +66,11 @@ int OnInit() { ea_params1.Set(STRUCT_ENUM(EAParams, EA_PARAM_PROP_DATA_EXPORT), EA_DATA_EXPORT_ALL); ea_params1.SetTaskEntry(_task_export_per_hour); ea1 = new EA1(ea_params1); - assertTrueOrFail(ea1.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)) == "EA1", "Invalid EA1 name!"); + assertTrueOrFail(ea1.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)) == "EA1", + StringFormat("Invalid EA name: %s!", ea1.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)))); + assertTrueOrFail(ea1.Get(STRUCT_ENUM(EAState, EA_STATE_FLAG_ENABLED)), "EA should be enabled by default!"); - /* Initialize 2st custom EA */ + /* Initialize 2nd custom EA */ EAParams ea_params2("EA2"); // Exporting to all possible formats once per hour. ea_params2.Set(STRUCT_ENUM(EAParams, EA_PARAM_PROP_DATA_STORE), EA_DATA_STORE_ALL); @@ -82,6 +79,14 @@ int OnInit() { ea2 = new EA2(ea_params2); assertTrueOrFail(ea2.Get(STRUCT_ENUM(EAParams, EA_PARAM_PROP_NAME)) == "EA2", "Invalid EA2 name!"); + /* Initialize 3rd custom EA */ + EAParams ea_params3("EA3"); + // Create an init task to disable EA when enabled. + TaskEntry _tentry_ea_disable(EA_ACTION_DISABLE, EA_COND_IS_ENABLED); + ea_params3.SetTaskEntry(_tentry_ea_disable); + ea3 = new EA3(ea_params3); + assertTrueOrFail(!ea3.Get(STRUCT_ENUM(EAState, EA_STATE_FLAG_ENABLED)), "EA should be disabled by a task!"); + return (INIT_SUCCEEDED); } @@ -89,16 +94,16 @@ int OnInit() { * Implements OnTick(). */ void OnTick() { - ea.ProcessTick(); ea1.ProcessTick(); ea2.ProcessTick(); + ea3.ProcessTick(); } /** * Implements OnDeinit(). */ void OnDeinit(const int reason) { - delete ea; delete ea1; delete ea2; + delete ea3; } diff --git a/tests/IndicatorsTest.mq5 b/tests/IndicatorsTest.mq5 index 5d44e708b..3a36f05da 100644 --- a/tests/IndicatorsTest.mq5 +++ b/tests/IndicatorsTest.mq5 @@ -109,8 +109,6 @@ void OnTick() { PrintFormat("%s: bar %d: %s", _indi.GetFullName(), bar_processed, _indi.ToString()); tested.Set(iter.Key(), true); // Mark as tested. indis.Unset(iter.Key()); - } else if (bar_processed > 20) { - DebugBreak(); } } } @@ -390,12 +388,12 @@ bool InitIndicators() { indi_rsi_on_price.Ptr().SetDataSource(indi_price_4_rsi.Ptr()); indis.Push(indi_rsi_on_price.Ptr()); - // Drawer (socket-based) indicator. + // Drawer (socket-based) indicator over RSI over Price. IndiDrawerParams drawer_params(14, /*unused*/ PRICE_OPEN); - // drawer_params.SetIndicatorData(indi_price_4_rsi); - // drawer_params.SetIndicatorMode(INDI_PRICE_MODE_OPEN); drawer_params.SetDraw(clrBisque, 0); - indis.Push(_indi_drawer = new Indi_Drawer(drawer_params)); + Ref indi_drawer_on_rsi = new Indi_Drawer(drawer_params); + indi_drawer_on_rsi.Ptr().SetDataSource(indi_rsi_on_price.Ptr(), PRICE_OPEN); + indis.Push(indi_drawer_on_rsi.Ptr()); // Applied Price over OHCL indicator. IndiAppliedPriceParams applied_price_params(); @@ -519,7 +517,7 @@ bool InitIndicators() { // Pattern Detector. IndiPatternParams pattern_params(); - indis.Push(_indi_test = new Indi_Pattern(pattern_params)); + indis.Push(new Indi_Pattern(pattern_params)); // Pivot. IndiPivotParams pivot_params(); @@ -559,7 +557,7 @@ bool PrintIndicators(string _prefix = "") { } string _indi_name = _indi.GetFullName(); - MqlParam _value = _indi.GetEntryValue(); + IndicatorDataEntryValue _value = _indi.GetEntryValue(); if (GetLastError() == ERR_INDICATOR_DATA_NOT_FOUND || GetLastError() == ERR_USER_ERROR_FIRST + ERR_USER_INVALID_BUFF_NUM) { ResetLastError(); diff --git a/tests/TaskTest.mq5 b/tests/TaskTest.mq5 deleted file mode 100644 index 67ebb58d3..000000000 --- a/tests/TaskTest.mq5 +++ /dev/null @@ -1,114 +0,0 @@ -//+------------------------------------------------------------------+ -//| EA31337 framework | -//| Copyright 2016-2021, EA31337 Ltd | -//| https://github.com/EA31337 | -//+------------------------------------------------------------------+ - -/* - * This file is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** - * @file - * Test functionality of Task class. - */ - -// Forward declaration. -struct DataParamEntry; - -// Includes. -#include "../Action.mqh" -#include "../Chart.mqh" -#include "../DictObject.mqh" -#include "../EA.mqh" -#include "../Task.mqh" -#include "../Test.mqh" - -// Global variables. -Chart *chart; -EA *ea; -DictObject tasks; - -// Define strategy classes. -class Stg1 : public Strategy { - public: - void Stg1(StgParams &_params, TradeParams &_tparams, ChartParams &_cparams, string _name = "Stg1") - : Strategy(_params, _tparams, _cparams, _name) {} - static Stg1 *Init(ENUM_TIMEFRAMES _tf = NULL, unsigned long _magic_no = 0, ENUM_LOG_LEVEL _log_level = V_INFO) { - ChartParams _cparams(_tf); - TradeParams _tparams(_magic_no, _log_level); - Strategy *_strat = new Stg1(stg_params_defaults, _tparams, _cparams, __FUNCTION__); - return _strat; - } - bool SignalOpen(ENUM_ORDER_TYPE _cmd, int _method, float _level, int _shift) { return true; } - bool SignalOpenFilterMethod(ENUM_ORDER_TYPE _cmd, int _method = 0) { return true; } - float SignalOpenBoost(ENUM_ORDER_TYPE _cmd, int _method = 0) { return 1.0f; } - bool SignalClose(ENUM_ORDER_TYPE _cmd, int _method, float _level, int _shift) { return true; } - float PriceStop(ENUM_ORDER_TYPE _cmd, ENUM_ORDER_TYPE_VALUE _mode, int _method = 0, float _level = 0.0f) { - return _level; - } -}; - -/** - * Implements Init event handler. - */ -int OnInit() { - bool _result = true; - // Initializes chart. - chart = new Chart(); - // Initializes EA. - EAParams ea_params(__FILE__); - ea = new EA(ea_params); - //_result &= ea.StrategyAdd(127); - _result &= GetLastError() == ERR_NO_ERROR; - return (_result ? INIT_SUCCEEDED : INIT_FAILED); -} - -/** - * Implements Tick event handler. - */ -void OnTick() { - chart.OnTick(); - if (chart.IsNewBar()) { - unsigned int _bar_index = chart.GetBarIndex(); - switch (_bar_index) { - case 1: - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 5: - break; - case 6: - break; - case 7: - break; - case 8: - break; - case 9: - break; - } - } -} - -/** - * Implements Deinit event handler. - */ -void OnDeinit(const int reason) { - delete chart; - delete ea; -}