├── .gitignore ├── Sources ├── AddOnResources │ ├── RFIX.win │ │ └── AddOnMain.rc2 │ ├── ResourceMDIDIds.hpp.in │ ├── RFIX │ │ ├── AddOnFix.grc │ │ └── Images │ │ │ ├── AddOnIcon_18x18.svg │ │ │ └── DialogDrawing_220x160.svg │ ├── RFIX.mac │ │ └── Info.plist │ ├── RINT │ │ └── AddOn.grc │ └── Tools │ │ └── CompileResources.py └── AddOn │ ├── ResourceIds.hpp │ ├── APIEnvir.h │ ├── MazeSettings.hpp │ ├── MigrationUtils.hpp │ ├── MazeSettingsDialog.hpp │ ├── MazeSettings.cpp │ ├── MigrationUtils.cpp │ ├── MazeSettingsDialog.cpp │ ├── MazeGenerator.hpp │ ├── AddOnMain.cpp │ └── MazeGenerator.cpp ├── conanfile.py ├── README.md ├── CMakePresets.json ├── LICENSE.md └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | Build/* 2 | .DS_Store 3 | .vscode 4 | conan/* 5 | out/* 6 | .vsconan 7 | CMakeUserPresets.json 8 | -------------------------------------------------------------------------------- /Sources/AddOnResources/RFIX.win/AddOnMain.rc2: -------------------------------------------------------------------------------- 1 | #include "AddOn.grc.rc2" 2 | #include "AddOnFix.grc.rc2" 3 | 4 | 1 ICON LOADONCALL MOVEABLE IMPURE ACAP.ico 5 | -------------------------------------------------------------------------------- /Sources/AddOnResources/ResourceMDIDIds.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef RESOURCEMDIDIDS_HPP 2 | #define RESOURCEMDIDIDS_HPP 3 | 4 | #define AC_MDID_DEV @AC_MDID_DEV@ 5 | #define AC_MDID_LOC @AC_MDID_LOC@ 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Sources/AddOn/ResourceIds.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RESOURCEIDS_HPP 2 | #define RESOURCEIDS_HPP 3 | 4 | #define ID_ADDON_INFO 32000 5 | #define ID_ADDON_MENU 32500 6 | #define ID_ADDON_STRINGS 32501 7 | #define ID_ADDON_DLG 32502 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /Sources/AddOnResources/RFIX/AddOnFix.grc: -------------------------------------------------------------------------------- 1 | #include "ResourceMDIDIds.hpp" 2 | 3 | 'MDID' 32500 "Add-On Identifier" { 4 | AC_MDID_DEV /* Set AC_MDID_DEV value as your developer id. */ 5 | AC_MDID_LOC /* Set AC_MDID_LOC value as your local id. */ 6 | } 7 | 8 | 'GICN' 10001 "AddOnIcon" { 9 | "AddOnIcon" 10 | } 11 | 12 | 'GICN' 10002 "DialogDrawing" { 13 | "DialogDrawing" 14 | } 15 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | 3 | 4 | class Recipe(ConanFile): 5 | settings = "os", "compiler", "build_type", "arch" 6 | generators = "CMakeToolchain", "CMakeDeps", "VirtualBuildEnv", "cmake_paths" 7 | 8 | def layout(self): 9 | self.folders.generators = "conan" 10 | 11 | def requirements(self): 12 | self.requires("archicad-apidevkit/26") 13 | -------------------------------------------------------------------------------- /Sources/AddOn/APIEnvir.h: -------------------------------------------------------------------------------- 1 | #ifndef _APIENVIR_H_ 2 | #define _APIENVIR_H_ 3 | 4 | #if defined (_MSC_VER) 5 | #if !defined (WINDOWS) 6 | #define WINDOWS 7 | #endif 8 | #endif 9 | 10 | #if defined (WINDOWS) 11 | #include "Win32Interface.hpp" 12 | #endif 13 | 14 | #if defined (macintosh) 15 | #include 16 | #endif 17 | 18 | #if !defined (ACExtension) 19 | #define ACExtension 20 | #endif 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeSettings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAZESETTINGS_HPP 2 | #define MAZESETTINGS_HPP 3 | 4 | #include "Object.hpp" 5 | 6 | class MazeSettings : public GS::Object 7 | { 8 | DECLARE_CLASS_INFO; 9 | 10 | public: 11 | MazeSettings (); 12 | MazeSettings (UInt32 rowCount, UInt32 columnCount, double cellSize, bool createGroup, bool createSlab); 13 | 14 | virtual GSErrCode Read (GS::IChannel& ic) override; 15 | virtual GSErrCode Write (GS::OChannel& oc) const override; 16 | 17 | UInt32 rowCount; 18 | UInt32 columnCount; 19 | double cellSize; 20 | bool createGroup; 21 | bool createSlab; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /Sources/AddOn/MigrationUtils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MIGRATIONUTILS_HPP 2 | #define MIGRATIONUTILS_HPP 3 | 4 | #include "ACAPinc.h" 5 | 6 | #include "MemoryIChannel.hpp" 7 | #include "MemoryOChannel.hpp" 8 | 9 | #if defined (ServerMainVers_2500) 10 | using MemoryIChannel = GS::MemoryIChannel; 11 | using MemoryOChannel = GS::MemoryOChannel; 12 | #else 13 | using MemoryIChannel = IO::MemoryIChannel; 14 | using MemoryOChannel = IO::MemoryOChannel; 15 | #endif 16 | 17 | void SetAPIElementType (API_Element& element, API_ElemTypeID elemTypeId); 18 | GSErrCode Register_Menu (short menuStrResID, short promptStrResID, APIMenuCodeID menuPosCode, GSFlags menuFlags); 19 | GSErrCode Install_MenuHandler (short menuStrResID, APIMenuCommandProc* handlerProc); 20 | GSErrCode ElementGroup_Create (const GS::Array& elemGuids, API_Guid* groupGuid = nullptr, const API_Guid* parentGroupGuid = nullptr); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Archicad Maze Generator Add-On 2 | 3 | [![Build](https://github.com/GRAPHISOFT/archicad-maze-generator/actions/workflows/build.yml/badge.svg)](https://github.com/GRAPHISOFT/archicad-maze-generator/actions/workflows/build.yml) 4 | 5 | This repository contains the source code for the [Archicad](https://graphisoft.com/solutions/products/archicad) Maze Generator Add-On Tutorial: 6 | - [Part 1: Create elements, handle undo scope.](https://archicadapi.graphisoft.com/archicad-maze-generator-add-on-tutorial-part-1) 7 | - [Part 2: Create a dialog to manipulate the functionality.](https://archicadapi.graphisoft.com/archicad-maze-generator-add-on-tutorial-part-2) 8 | - [Part 3: Store dialog data in preferences.](https://archicadapi.graphisoft.com/archicad-maze-generator-add-on-tutorial-part-3) 9 | 10 | !!! Disclaimer: Please note that this example targets Archicad 26 and may not work with newer Development Kits. Check out the [official Add-On template](https://github.com/GRAPHISOFT/archicad-addon-cmake) repository for a detailed build guide. -------------------------------------------------------------------------------- /Sources/AddOnResources/RFIX.mac/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleInfoDictionaryVersion 6 | 6.0 7 | CFBundleIdentifier 8 | com.graphisoft.AddOn 9 | CFBundleName 10 | AddOn 11 | CFBundleGetInfoString 12 | AddOn 13 | CFBundleShortVersionString 14 | AddOn 15 | CFBundlePackageType 16 | .APX 17 | CFBundleSignature 18 | GSAP 19 | CFBundleVersion 20 | 1.0 21 | CFBundleDevelopmentRegion 22 | English 23 | CFBundleIconFile 24 | ArchiCADPlugin.icns 25 | LSMinimumSystemVersion 26 | 10.11.0 27 | LSRequiresCarbon 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "conan", 6 | "hidden": true, 7 | "cacheVariables": { 8 | "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/conan/conan_toolchain.cmake", 9 | "CMAKE_POLICY_DEFAULT_CMP0091": "NEW" 10 | } 11 | }, 12 | { 13 | "name": "default" 14 | }, 15 | { 16 | "name": "default-conan", 17 | "inherits": ["conan"] 18 | } 19 | ], 20 | "buildPresets": [ 21 | { 22 | "name": "default-release", 23 | "configurePreset": "default", 24 | "configuration": "Release" 25 | }, 26 | { 27 | "name": "default-debug", 28 | "configurePreset": "default", 29 | "configuration": "Debug" 30 | }, 31 | { 32 | "name": "default-conan-release", 33 | "configurePreset": "default-conan", 34 | "configuration": "Release" 35 | }, 36 | { 37 | "name": "default-conan-debug", 38 | "configurePreset": "default-conan", 39 | "configuration": "Debug" 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeSettingsDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAZESETTINGSDIALOG_HPP 2 | #define MAZESETTINGSDIALOG_HPP 3 | 4 | #include "DGModule.hpp" 5 | #include "MazeSettings.hpp" 6 | 7 | class MazeSettingsDialog : public DG::ModalDialog, 8 | public DG::PanelObserver, 9 | public DG::ButtonItemObserver, 10 | public DG::CompoundItemObserver 11 | { 12 | public: 13 | MazeSettingsDialog (const MazeSettings& mazeSettings); 14 | ~MazeSettingsDialog (); 15 | 16 | const MazeSettings& GetMazeSettings () const; 17 | 18 | private: 19 | virtual void PanelOpened (const DG::PanelOpenEvent& ev) override; 20 | virtual void PanelCloseRequested (const DG::PanelCloseRequestEvent& ev, bool* accepted) override; 21 | virtual void ButtonClicked (const DG::ButtonClickEvent& ev) override; 22 | 23 | DG::Button okButton; 24 | DG::Button cancelButton; 25 | DG::PosIntEdit rowEdit; 26 | DG::PosIntEdit columnEdit; 27 | DG::LengthEdit cellSizeEdit; 28 | DG::CheckBox groupElementsCheck; 29 | DG::CheckBox placeSlabCheck; 30 | 31 | MazeSettings mazeSettings; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 GRAPHISOFT 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeSettings.cpp: -------------------------------------------------------------------------------- 1 | #include "MazeSettings.hpp" 2 | 3 | GS::ClassInfo MazeSettings::classInfo ("MazeSettings", GS::Guid ("B45089A9-B372-460B-B145-80E6EBF107C3"), GS::ClassVersion (1, 0)); 4 | 5 | MazeSettings::MazeSettings () : 6 | MazeSettings (0, 0, 0.0, false, false) 7 | { 8 | 9 | } 10 | 11 | MazeSettings::MazeSettings (UInt32 rowCount, UInt32 columnCount, double cellSize, bool createGroup, bool createSlab) : 12 | rowCount (rowCount), 13 | columnCount (columnCount), 14 | cellSize (cellSize), 15 | createGroup (createGroup), 16 | createSlab (createSlab) 17 | { 18 | 19 | } 20 | 21 | GSErrCode MazeSettings::Read (GS::IChannel& ic) 22 | { 23 | GS::InputFrame frame (ic, classInfo); 24 | ic.Read (rowCount); 25 | ic.Read (columnCount); 26 | ic.Read (cellSize); 27 | ic.Read (createGroup); 28 | ic.Read (createSlab); 29 | return ic.GetInputStatus (); 30 | } 31 | 32 | GSErrCode MazeSettings::Write (GS::OChannel& oc) const 33 | { 34 | GS::OutputFrame frame (oc, classInfo); 35 | oc.Write (rowCount); 36 | oc.Write (columnCount); 37 | oc.Write (cellSize); 38 | oc.Write (createGroup); 39 | oc.Write (createSlab); 40 | return oc.GetOutputStatus (); 41 | } 42 | -------------------------------------------------------------------------------- /Sources/AddOn/MigrationUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "MigrationUtils.hpp" 2 | 3 | void SetAPIElementType (API_Element& element, API_ElemTypeID elemTypeId) 4 | { 5 | #ifdef ServerMainVers_2600 6 | element.header.type = API_ElemType (elemTypeId); 7 | #else 8 | element.header.typeID = elemTypeId; 9 | #endif 10 | } 11 | 12 | 13 | GSErrCode Register_Menu (short menuStrResID, short promptStrResID, APIMenuCodeID menuPosCode, GSFlags menuFlags) 14 | { 15 | #if defined(ServerMainVers_2700) 16 | return ACAPI_MenuItem_RegisterMenu (menuStrResID, promptStrResID, menuPosCode, menuFlags); 17 | #else 18 | return ACAPI_Register_Menu (menuStrResID, promptStrResID, menuPosCode, menuFlags); 19 | #endif 20 | } 21 | 22 | 23 | GSErrCode Install_MenuHandler (short menuStrResID, APIMenuCommandProc* handlerProc) 24 | { 25 | #if defined(ServerMainVers_2700) 26 | return ACAPI_MenuItem_InstallMenuHandler (menuStrResID, handlerProc); 27 | #else 28 | return ACAPI_Install_MenuHandler (menuStrResID, handlerProc); 29 | #endif 30 | } 31 | 32 | 33 | GSErrCode ElementGroup_Create (const GS::Array& elemGuids, API_Guid* groupGuid, const API_Guid* parentGroupGuid) 34 | { 35 | #if defined(ServerMainVers_2700) 36 | return ACAPI_Grouping_CreateGroup (elemGuids, groupGuid, parentGroupGuid); 37 | #else 38 | return ACAPI_ElementGroup_Create (elemGuids, groupGuid, parentGroupGuid); 39 | #endif 40 | } -------------------------------------------------------------------------------- /Sources/AddOnResources/RINT/AddOn.grc: -------------------------------------------------------------------------------- 1 | #include "ResourceIds.hpp" 2 | 3 | 'STR#' ID_ADDON_INFO "Add-on Name and Description" { 4 | /* [ 1] */ "Maze Generator" 5 | /* [ 2] */ "Maze generator Add-On for training purposes." 6 | } 7 | 8 | 'STR#' ID_ADDON_MENU "Strings for the Menu" { 9 | /* [ 1] */ "Generate Maze... ^E3 ^ES ^EE ^EI ^ED ^ET ^10001" 10 | } 11 | 12 | 'STR#' ID_ADDON_STRINGS "Add-On Strings" { 13 | /* [ 1] */ "Generate Maze" 14 | } 15 | 16 | 'GDLG' ID_ADDON_DLG Modal 40 40 250 452 "Maze Settings" { 17 | /* [ 1] */ Button 150 419 90 23 LargePlain "OK" 18 | /* [ 2] */ Button 50 419 90 23 LargePlain "Cancel" 19 | /* [ 3] */ Icon 15 10 220 160 10002 20 | /* [ 4] */ LeftText 10 180 230 23 LargeBold vCenter "Grid Settings" 21 | /* [ 5] */ LeftText 10 210 130 23 LargePlain vCenter "Number of Rows" 22 | /* [ 6] */ PosIntEdit 150 210 90 23 LargePlain "1" "50" 23 | /* [ 7] */ LeftText 10 240 130 23 LargePlain vCenter "Number of Columns" 24 | /* [ 8] */ PosIntEdit 150 240 90 23 LargePlain "1" "50" 25 | /* [ 9] */ LeftText 10 270 130 23 LargePlain vCenter "Cell Dimension" 26 | /* [ 10] */ LengthEdit 150 270 90 23 LargePlain "1.00" "50.0" 27 | /* [ 11] */ Separator 10 305 230 2 28 | /* [ 12] */ LeftText 10 317 230 23 LargeBold vCenter "Options" 29 | /* [ 13] */ CheckBox 10 347 230 23 LargePlain "Group placed elements" 30 | /* [ 14] */ CheckBox 10 372 230 23 LargePlain "Place slab under walls" 31 | /* [ 15] */ Separator 10 407 230 2 32 | } 33 | 34 | 'DLGH' ID_ADDON_DLG DLG_Maze_Settings { 35 | 1 "" Button_0 36 | 2 "" Button_1 37 | 3 "" Icon_0 38 | 4 "" LeftText_0 39 | 5 "" LeftText_1 40 | 6 "" PosIntEdit_0 41 | 7 "" LeftText_2 42 | 8 "" PosIntEdit_1 43 | 9 "" LeftText_3 44 | 10 "" LengthEdit_0 45 | 11 "" Separator_0 46 | 12 "" LeftText_4 47 | 13 "" CheckBox_0 48 | 14 "" CheckBox_1 49 | 15 "" Separator_1 50 | } 51 | -------------------------------------------------------------------------------- /Sources/AddOnResources/RFIX/Images/AddOnIcon_18x18.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeSettingsDialog.cpp: -------------------------------------------------------------------------------- 1 | #include "MazeSettingsDialog.hpp" 2 | #include "ResourceIds.hpp" 3 | #include "ACAPinc.h" 4 | 5 | enum DialogResourceIds 6 | { 7 | MazeDialogResourceId = ID_ADDON_DLG, 8 | OKButtonId = 1, 9 | CancelButtonId = 2, 10 | DrawingId = 3, 11 | GridSettingsTextId = 4, 12 | RowTextId = 5, 13 | RowEditId = 6, 14 | ColumnTextId = 7, 15 | ColumnEditId = 8, 16 | CellSizeTextId = 9, 17 | CellSizeEditId = 10, 18 | Separator1Id = 11, 19 | OptionsTextId = 12, 20 | GroupElementsCheckId = 13, 21 | PlaceSlabCheckId = 14, 22 | Separator2Id = 15 23 | }; 24 | 25 | MazeSettingsDialog::MazeSettingsDialog (const MazeSettings& mazeSettings) : 26 | DG::ModalDialog (ACAPI_GetOwnResModule (), MazeDialogResourceId, ACAPI_GetOwnResModule ()), 27 | okButton (GetReference (), OKButtonId), 28 | cancelButton (GetReference (), CancelButtonId), 29 | rowEdit (GetReference (), RowEditId), 30 | columnEdit (GetReference (), ColumnEditId), 31 | cellSizeEdit (GetReference (), CellSizeEditId), 32 | groupElementsCheck (GetReference (), GroupElementsCheckId), 33 | placeSlabCheck (GetReference (), PlaceSlabCheckId), 34 | mazeSettings (mazeSettings) 35 | { 36 | AttachToAllItems (*this); 37 | Attach (*this); 38 | } 39 | 40 | MazeSettingsDialog::~MazeSettingsDialog () 41 | { 42 | Detach (*this); 43 | DetachFromAllItems (*this); 44 | } 45 | 46 | const MazeSettings& MazeSettingsDialog::GetMazeSettings () const 47 | { 48 | return mazeSettings; 49 | } 50 | 51 | void MazeSettingsDialog::PanelOpened (const DG::PanelOpenEvent&) 52 | { 53 | rowEdit.SetValue (mazeSettings.rowCount); 54 | columnEdit.SetValue (mazeSettings.columnCount); 55 | cellSizeEdit.SetValue (mazeSettings.cellSize); 56 | groupElementsCheck.SetState (mazeSettings.createGroup); 57 | placeSlabCheck.SetState (mazeSettings.createSlab); 58 | } 59 | 60 | void MazeSettingsDialog::PanelCloseRequested (const DG::PanelCloseRequestEvent& ev, bool*) 61 | { 62 | if (ev.IsAccepted ()) { 63 | mazeSettings.rowCount = rowEdit.GetValue (); 64 | mazeSettings.columnCount = columnEdit.GetValue (); 65 | mazeSettings.cellSize = cellSizeEdit.GetValue (); 66 | mazeSettings.createGroup = groupElementsCheck.IsChecked (); 67 | mazeSettings.createSlab = placeSlabCheck.IsChecked (); 68 | } 69 | } 70 | 71 | void MazeSettingsDialog::ButtonClicked (const DG::ButtonClickEvent& ev) 72 | { 73 | if (ev.GetSource () == &okButton) { 74 | PostCloseRequest (DG::ModalDialog::Accept); 75 | } else if (ev.GetSource () == &cancelButton) { 76 | PostCloseRequest (DG::ModalDialog::Cancel); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeGenerator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MAZEGENERATOR_HPP 2 | #define MAZEGENERATOR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace MG 10 | { 11 | 12 | using CellId = int; 13 | using WallId = int; 14 | extern const CellId InvalidCellId; 15 | extern const WallId InvalidWallId; 16 | 17 | enum class Direction 18 | { 19 | Left, 20 | Right, 21 | Top, 22 | Bottom, 23 | Invalid 24 | }; 25 | 26 | class Cell 27 | { 28 | public: 29 | Cell (); 30 | 31 | bool HasWall (Direction dir) const; 32 | WallId GetWall (Direction dir) const; 33 | void EnumerateWalls (const std::function& processor) const; 34 | void AddWall (Direction dir, WallId wallId); 35 | void RemoveWall (WallId wallId); 36 | 37 | private: 38 | std::vector walls; 39 | }; 40 | 41 | class Wall 42 | { 43 | public: 44 | Wall (); 45 | Wall (CellId cellId1, CellId cellId2); 46 | 47 | CellId GetCellId1 () const; 48 | CellId GetCellId2 () const; 49 | CellId GetOtherCellId (CellId cellId) const; 50 | 51 | private: 52 | CellId cellId1; 53 | CellId cellId2; 54 | }; 55 | 56 | class WallGeometry 57 | { 58 | public: 59 | WallGeometry (double begX, double begY, double endX, double endY); 60 | 61 | double begX; 62 | double begY; 63 | double endX; 64 | double endY; 65 | }; 66 | 67 | class Maze 68 | { 69 | public: 70 | Maze (); 71 | Maze (int rowCount, int colCount); 72 | 73 | void Reset (int rowCount, int colCount); 74 | 75 | CellId GetCellId (int row, int col) const; 76 | WallId GetWallId (int row, int col, Direction dir) const; 77 | 78 | const Cell& GetCell (CellId cellId) const; 79 | const Wall& GetWall (WallId wallId) const; 80 | 81 | WallId AddWall (int row, int col, Direction dir); 82 | void RemoveWall (WallId wallId); 83 | 84 | std::vector GetWallGeometries (double cellSize) const; 85 | 86 | private: 87 | int rows; 88 | int cols; 89 | std::vector cells; 90 | std::unordered_map walls; 91 | WallId nextWallId; 92 | }; 93 | 94 | class MazeGenerator 95 | { 96 | public: 97 | MazeGenerator (int rowCount, int colCount); 98 | 99 | bool Generate (); 100 | const Maze& GetMaze () const; 101 | 102 | private: 103 | void VisitCell (CellId cellId); 104 | WallId SelectRandomWall (); 105 | 106 | Maze maze; 107 | int rowCount; 108 | int colCount; 109 | 110 | std::unordered_set visited; 111 | std::unordered_set walls; 112 | }; 113 | 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /Sources/AddOn/AddOnMain.cpp: -------------------------------------------------------------------------------- 1 | #include "APIEnvir.h" 2 | #include "ACAPinc.h" 3 | 4 | #include "ResourceIds.hpp" 5 | #include "MazeGenerator.hpp" 6 | #include "MazeSettings.hpp" 7 | #include "MazeSettingsDialog.hpp" 8 | 9 | #include "MigrationUtils.hpp" 10 | 11 | static const GSResID AddOnInfoID = ID_ADDON_INFO; 12 | static const Int32 AddOnNameID = 1; 13 | static const Int32 AddOnDescriptionID = 2; 14 | 15 | static const short AddOnMenuID = ID_ADDON_MENU; 16 | static const Int32 AddOnCommandID = 1; 17 | 18 | static const GSResID AddOnStringsID = ID_ADDON_STRINGS; 19 | static const Int32 UndoStringID = 1; 20 | 21 | static const Int32 PreferencesVersion = 1; 22 | 23 | static void GenerateMazeWallGeometries (int rowCount, int colCount, double cellSize, std::vector& mazeWalls) 24 | { 25 | MG::MazeGenerator generator (rowCount, colCount); 26 | if (!generator.Generate ()) { 27 | return; 28 | } 29 | 30 | const MG::Maze& maze = generator.GetMaze (); 31 | mazeWalls = maze.GetWallGeometries (cellSize); 32 | } 33 | 34 | static GSErrCode CreateWallElement (double begX, double begY, double endX, double endY, API_Guid& placedWallGuid) 35 | { 36 | GSErrCode err = NoError; 37 | 38 | API_Element wallElement = {}; 39 | SetAPIElementType (wallElement, API_WallID); 40 | err = ACAPI_Element_GetDefaults (&wallElement, nullptr); 41 | if (err != NoError) { 42 | return err; 43 | } 44 | 45 | wallElement.wall.begC = { begX, begY }; 46 | wallElement.wall.endC = { endX, endY }; 47 | wallElement.wall.referenceLineLocation = APIWallRefLine_Center; 48 | err = ACAPI_Element_Create (&wallElement, nullptr); 49 | if (err != NoError) { 50 | return err; 51 | } 52 | 53 | placedWallGuid = wallElement.header.guid; 54 | return NoError; 55 | } 56 | 57 | static GSErrCode CreateSlabElement (double begX, double begY, double endX, double endY, API_Guid& placedSlabGuid) 58 | { 59 | GSErrCode err = NoError; 60 | 61 | API_Element slabElement = {}; 62 | SetAPIElementType (slabElement, API_SlabID); 63 | err = ACAPI_Element_GetDefaults (&slabElement, nullptr); 64 | if (err != NoError) { 65 | return err; 66 | } 67 | 68 | slabElement.slab.poly.nCoords = 5; 69 | slabElement.slab.poly.nSubPolys = 1; 70 | 71 | API_ElementMemo slabMemo = {}; 72 | 73 | slabMemo.coords = (API_Coord**) BMhAllClear ((slabElement.slab.poly.nCoords + 1) * sizeof (API_Coord)); 74 | (*slabMemo.coords)[1] = { begX, begY }; 75 | (*slabMemo.coords)[2] = { endX, begY }; 76 | (*slabMemo.coords)[3] = { endX, endY }; 77 | (*slabMemo.coords)[4] = { begX, endY }; 78 | (*slabMemo.coords)[5] = (*slabMemo.coords)[1]; 79 | 80 | slabMemo.pends = (Int32**) BMhAllClear ((slabElement.slab.poly.nSubPolys + 1) * sizeof (Int32)); 81 | (*slabMemo.pends)[1] = slabElement.slab.poly.nCoords; 82 | 83 | err = ACAPI_Element_Create (&slabElement, &slabMemo); 84 | ACAPI_DisposeElemMemoHdls (&slabMemo); 85 | if (err != NoError) { 86 | return err; 87 | } 88 | 89 | placedSlabGuid = slabElement.header.guid; 90 | return NoError; 91 | } 92 | 93 | static bool LoadMazeSettingsFromPreferences (MazeSettings& mazeSettings) 94 | { 95 | GSErrCode err = NoError; 96 | 97 | Int32 version = 0; 98 | GSSize bytes = 0; 99 | err = ACAPI_GetPreferences (&version, &bytes, nullptr); 100 | if (err != NoError || version == 0 || bytes == 0) { 101 | return false; 102 | } 103 | 104 | char* data = new char[bytes]; 105 | err = ACAPI_GetPreferences (&version, &bytes, data); 106 | if (err != NoError) { 107 | delete[] data; 108 | return false; 109 | } 110 | 111 | MazeSettings tempMazeSettings; 112 | MemoryIChannel inputChannel (data, bytes); 113 | err = tempMazeSettings.Read (inputChannel); 114 | if (err != NoError) { 115 | delete[] data; 116 | return false; 117 | } 118 | 119 | mazeSettings = tempMazeSettings; 120 | 121 | delete[] data; 122 | return true; 123 | } 124 | 125 | static bool WriteMazeSettingsToPreferences (const MazeSettings& mazeSettings) 126 | { 127 | GSErrCode err = NoError; 128 | 129 | MemoryOChannel outputChannel; 130 | err = mazeSettings.Write (outputChannel); 131 | if (err != NoError) { 132 | return false; 133 | } 134 | 135 | UInt64 bytes = outputChannel.GetDataSize (); 136 | const char* data = outputChannel.GetDestination (); 137 | 138 | err = ACAPI_SetPreferences (PreferencesVersion, (GSSize) bytes, data); 139 | if (err != NoError) { 140 | return false; 141 | } 142 | 143 | return true; 144 | } 145 | 146 | static bool GetMazeSettingsFromDialog (MazeSettings& mazeSettings) 147 | { 148 | MazeSettings initialMazeSettings (10, 20, 1.0, true, true); 149 | LoadMazeSettingsFromPreferences (initialMazeSettings); 150 | 151 | MazeSettingsDialog mazeSettingsDialog (initialMazeSettings); 152 | if (mazeSettingsDialog.Invoke ()) { 153 | mazeSettings = mazeSettingsDialog.GetMazeSettings (); 154 | WriteMazeSettingsToPreferences (mazeSettings); 155 | return true; 156 | } else { 157 | return false; 158 | } 159 | } 160 | 161 | static void GenerateMaze () 162 | { 163 | MazeSettings mazeSettings; 164 | if (!GetMazeSettingsFromDialog (mazeSettings)) { 165 | return; 166 | } 167 | 168 | std::vector mazeWalls; 169 | GenerateMazeWallGeometries (mazeSettings.rowCount, mazeSettings.columnCount, mazeSettings.cellSize, mazeWalls); 170 | 171 | static const double SlabPadding = 2.0; 172 | double slabBegX = -SlabPadding; 173 | double slabBegY = -SlabPadding; 174 | double slabEndX = mazeSettings.cellSize * mazeSettings.columnCount + SlabPadding; 175 | double slabEndY = mazeSettings.cellSize * mazeSettings.rowCount + SlabPadding; 176 | 177 | GS::UniString undoString = RSGetIndString (AddOnStringsID, UndoStringID, ACAPI_GetOwnResModule ()); 178 | ACAPI_CallUndoableCommand (undoString, [&] () -> GSErrCode { 179 | GS::Array placedElementGuids; 180 | GSErrCode err = NoError; 181 | for (const MG::WallGeometry& mazeWall : mazeWalls) { 182 | API_Guid placedWallGuid; 183 | err = CreateWallElement (mazeWall.begX, mazeWall.begY, mazeWall.endX, mazeWall.endY, placedWallGuid); 184 | if (err != NoError) { 185 | return APIERR_CANCEL; 186 | } 187 | placedElementGuids.Push (placedWallGuid); 188 | } 189 | if (mazeSettings.createSlab) { 190 | API_Guid placedSlabGuid; 191 | err = CreateSlabElement (slabBegX, slabBegY, slabEndX, slabEndY, placedSlabGuid); 192 | if (err != NoError) { 193 | return APIERR_CANCEL; 194 | } 195 | placedElementGuids.Push (placedSlabGuid); 196 | } 197 | if (mazeSettings.createGroup) { 198 | err = ElementGroup_Create (placedElementGuids); 199 | if (err != NoError) { 200 | return APIERR_CANCEL; 201 | } 202 | } 203 | return NoError; 204 | }); 205 | } 206 | 207 | static GSErrCode MenuCommandHandler (const API_MenuParams *menuParams) 208 | { 209 | switch (menuParams->menuItemRef.menuResID) { 210 | case AddOnMenuID: 211 | switch (menuParams->menuItemRef.itemIndex) { 212 | case AddOnCommandID: 213 | GenerateMaze (); 214 | break; 215 | } 216 | break; 217 | } 218 | return NoError; 219 | } 220 | 221 | API_AddonType CheckEnvironment (API_EnvirParams* envir) 222 | { 223 | RSGetIndString (&envir->addOnInfo.name, AddOnInfoID, AddOnNameID, ACAPI_GetOwnResModule ()); 224 | RSGetIndString (&envir->addOnInfo.description, AddOnInfoID, AddOnDescriptionID, ACAPI_GetOwnResModule ()); 225 | 226 | return APIAddon_Normal; 227 | } 228 | 229 | GSErrCode RegisterInterface (void) 230 | { 231 | return Register_Menu (AddOnMenuID, 0, MenuCode_Extras, MenuFlag_Default); 232 | } 233 | 234 | GSErrCode Initialize (void) 235 | { 236 | return Install_MenuHandler (AddOnMenuID, MenuCommandHandler); 237 | } 238 | 239 | GSErrCode FreeData (void) 240 | { 241 | return NoError; 242 | } 243 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.16) 2 | 3 | function (SetCompilerOptions target ac_version) 4 | if (${ac_version} GREATER_EQUAL "27") 5 | target_compile_features (${target} PUBLIC cxx_std_17) 6 | else () 7 | target_compile_features (${target} PUBLIC cxx_std_14) 8 | endif () 9 | target_compile_options (${target} PUBLIC "$<$:-DDEBUG>") 10 | if (WIN32) 11 | target_compile_options (${target} PUBLIC /W4 /WX 12 | /Zc:wchar_t- 13 | /wd4499 14 | /EHsc 15 | ) 16 | else () 17 | target_compile_options (${target} PUBLIC -Wall -Wextra -Werror 18 | -fvisibility=hidden 19 | -Wno-multichar 20 | -Wno-ctor-dtor-privacy 21 | -Wno-invalid-offsetof 22 | -Wno-ignored-qualifiers 23 | -Wno-reorder 24 | -Wno-overloaded-virtual 25 | -Wno-unused-parameter 26 | -Wno-deprecated) 27 | if (${ac_version} LESS_EQUAL "24") 28 | target_compile_options (${target} PUBLIC -Wno-non-c-typedef-for-linkage) 29 | endif () 30 | endif () 31 | endfunction () 32 | 33 | function (LinkACAP_STATLibraries target ac_version) 34 | if (${ac_version} GREATER_EQUAL "27") 35 | if (WIN32) 36 | target_link_libraries (AddOn 37 | "$<$:${AC_API_DEVKIT_DIR}/Lib/ACAP_STATD.lib>" 38 | "$<$:${AC_API_DEVKIT_DIR}/Lib/ACAP_STAT.lib>" 39 | "$<$:${AC_API_DEVKIT_DIR}/Lib/ACAP_STAT.lib>" 40 | ) 41 | else () 42 | target_link_libraries (AddOn 43 | "${AC_API_DEVKIT_DIR}/Lib/libACAP_STAT.a" 44 | ) 45 | endif () 46 | else () 47 | if (WIN32) 48 | target_link_libraries (AddOn 49 | "$<$:${AC_API_DEVKIT_DIR}/Lib/Win/ACAP_STATD.lib>" 50 | "$<$:${AC_API_DEVKIT_DIR}/Lib/Win/ACAP_STAT.lib>" 51 | "$<$:${AC_API_DEVKIT_DIR}/Lib/Win/ACAP_STAT.lib>" 52 | ) 53 | else () 54 | target_link_libraries (AddOn 55 | "${AC_API_DEVKIT_DIR}/Lib/Mactel/libACAP_STAT.a" 56 | ) 57 | endif () 58 | endif () 59 | endfunction () 60 | 61 | set (API_DEVKIT_DIR $ENV{AC_API_DEVKIT_DIR}) 62 | 63 | if (GITHUB_BUILD) 64 | include(${CMAKE_BINARY_DIR}/conan_paths.cmake) 65 | set (API_DEVKIT_DIR ${CONAN_ARCHICAD-APIDEVKIT_ROOT}/bin) 66 | endif () 67 | 68 | set_property (GLOBAL PROPERTY USE_FOLDERS ON) 69 | 70 | set (CMAKE_SUPPRESS_REGENERATION 1) 71 | set (CMAKE_CONFIGURATION_TYPES Debug;Release;RelWithDebInfo) 72 | set (AC_API_DEVKIT_DIR ${API_DEVKIT_DIR} CACHE PATH "API DevKit directory.") 73 | set (AC_ADDON_NAME "ExampleAddOn" CACHE STRING "Add-On name.") 74 | set (AC_ADDON_LANGUAGE "INT" CACHE STRING "Add-On language code.") 75 | set (AC_MDID_DEV "1" CACHE STRING "Your Developer ID") 76 | set (AC_MDID_LOC "1" CACHE STRING "Add-On Local ID") 77 | 78 | set (ACAPINC_FILE_LOCATION ${AC_API_DEVKIT_DIR}/Inc/ACAPinc.h) 79 | if (EXISTS ${ACAPINC_FILE_LOCATION}) 80 | file (READ ${ACAPINC_FILE_LOCATION} ACAPIncContent) 81 | string (REGEX MATCHALL "#define[ \t]+ServerMainVers_([0-9][0-9])" VersionList ${ACAPIncContent}) 82 | set (ARCHICAD_VERSION ${CMAKE_MATCH_1}) 83 | message (STATUS "Archicad Version: ${ARCHICAD_VERSION}") 84 | else () 85 | message (FATAL_ERROR "Failed to detect Archicad version, please check the value of the AC_API_DEVKIT_DIR variable.") 86 | endif () 87 | 88 | if (WIN32) 89 | add_definitions (-DUNICODE -D_UNICODE) 90 | else () 91 | add_definitions (-Dmacintosh=1) 92 | endif () 93 | add_definitions (-DACExtension) 94 | 95 | project (${AC_ADDON_NAME}) 96 | 97 | set (AddOnSourcesFolder Sources/AddOn) 98 | set (AddOnResourcesFolder Sources/AddOnResources) 99 | 100 | # AddOnResources 101 | 102 | set (ResourceObjectsDir ${CMAKE_BINARY_DIR}/ResourceObjects) 103 | 104 | configure_file (${AddOnResourcesFolder}/ResourceMDIDIds.hpp.in ${ResourceObjectsDir}/ResourceMDIDIds.hpp) 105 | 106 | file (GLOB AddOnImageFiles 107 | ${AddOnResourcesFolder}/RFIX/Images/*.svg 108 | ) 109 | if (WIN32) 110 | file (GLOB AddOnResourceFiles 111 | ${AddOnResourcesFolder}/R${AC_ADDON_LANGUAGE}/*.grc 112 | ${AddOnResourcesFolder}/RFIX/*.grc 113 | ${AddOnResourcesFolder}/RFIX/*.grc 114 | ${AddOnResourcesFolder}/RFIX.win/*.rc2 115 | ${AddOnResourcesFolder}/Tools/*.py 116 | ) 117 | else () 118 | file (GLOB AddOnResourceFiles 119 | ${AddOnResourcesFolder}/R${AC_ADDON_LANGUAGE}/*.grc 120 | ${AddOnResourcesFolder}/RFIX/*.grc 121 | ${AddOnResourcesFolder}/RFIX.mac/*.plist 122 | ${AddOnResourcesFolder}/Tools/*.py 123 | ) 124 | endif () 125 | 126 | source_group ("Images" FILES ${AddOnImageFiles}) 127 | source_group ("Resources" FILES ${AddOnResourceFiles}) 128 | add_custom_target ( 129 | AddOnResources ALL 130 | DEPENDS "${ResourceObjectsDir}/AddOnResources.stamp" 131 | SOURCES ${AddOnResourceFiles} ${AddOnImageFiles} 132 | ) 133 | 134 | find_package(Python COMPONENTS Interpreter) 135 | 136 | get_filename_component (AddOnSourcesFolderAbsolute "${CMAKE_CURRENT_LIST_DIR}/${AddOnSourcesFolder}" ABSOLUTE) 137 | get_filename_component (AddOnResourcesFolderAbsolute "${CMAKE_CURRENT_LIST_DIR}/${AddOnResourcesFolder}" ABSOLUTE) 138 | if (WIN32) 139 | add_custom_command ( 140 | OUTPUT "${ResourceObjectsDir}/AddOnResources.stamp" 141 | DEPENDS ${AddOnResourceFiles} ${AddOnImageFiles} 142 | COMMENT "Compiling resources..." 143 | COMMAND ${CMAKE_COMMAND} -E make_directory "${ResourceObjectsDir}" 144 | COMMAND ${Python_EXECUTABLE} "${AddOnResourcesFolderAbsolute}/Tools/CompileResources.py" "${AC_ADDON_LANGUAGE}" "${AC_API_DEVKIT_DIR}" "${AddOnSourcesFolderAbsolute}" "${AddOnResourcesFolderAbsolute}" "${ResourceObjectsDir}" "${ResourceObjectsDir}/${AC_ADDON_NAME}.res" 145 | COMMAND ${CMAKE_COMMAND} -E touch "${ResourceObjectsDir}/AddOnResources.stamp" 146 | ) 147 | else () 148 | add_custom_command ( 149 | OUTPUT "${ResourceObjectsDir}/AddOnResources.stamp" 150 | DEPENDS ${AddOnResourceFiles} ${AddOnImageFiles} 151 | COMMENT "Compiling resources..." 152 | COMMAND ${CMAKE_COMMAND} -E make_directory "${ResourceObjectsDir}" 153 | COMMAND ${Python_EXECUTABLE} "${AddOnResourcesFolderAbsolute}/Tools/CompileResources.py" "${AC_ADDON_LANGUAGE}" "${AC_API_DEVKIT_DIR}" "${AddOnSourcesFolderAbsolute}" "${AddOnResourcesFolderAbsolute}" "${ResourceObjectsDir}" "${CMAKE_BINARY_DIR}/$/${AC_ADDON_NAME}.bundle/Contents/Resources" 154 | COMMAND ${CMAKE_COMMAND} -E copy "${AC_API_DEVKIT_DIR}/Inc/PkgInfo" "${CMAKE_BINARY_DIR}/$/${AC_ADDON_NAME}.bundle/Contents/PkgInfo" 155 | COMMAND ${CMAKE_COMMAND} -E touch "${ResourceObjectsDir}/AddOnResources.stamp" 156 | ) 157 | endif () 158 | 159 | # AddOn 160 | 161 | file (GLOB AddOnHeaderFiles 162 | ${AddOnSourcesFolder}/*.h 163 | ${AddOnSourcesFolder}/*.hpp 164 | ) 165 | file (GLOB AddOnSourceFiles 166 | ${AddOnSourcesFolder}/*.c 167 | ${AddOnSourcesFolder}/*.cpp 168 | ) 169 | set ( 170 | AddOnFiles 171 | ${AddOnHeaderFiles} 172 | ${AddOnSourceFiles} 173 | ) 174 | source_group ("Sources" FILES ${AddOnFiles}) 175 | if (WIN32) 176 | add_library (AddOn SHARED ${AddOnFiles}) 177 | else () 178 | add_library (AddOn MODULE ${AddOnFiles}) 179 | endif () 180 | 181 | set_target_properties (AddOn PROPERTIES OUTPUT_NAME ${AC_ADDON_NAME}) 182 | if (WIN32) 183 | set_target_properties (AddOn PROPERTIES SUFFIX ".apx") 184 | set_target_properties (AddOn PROPERTIES RUNTIME_OUTPUT_DIRECTORY_$ "${CMAKE_BINARY_DIR}/$") 185 | else () 186 | set_target_properties (AddOn PROPERTIES BUNDLE TRUE) 187 | set_target_properties (AddOn PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_LIST_DIR}/${AddOnResourcesFolder}/RFIX.mac/Info.plist") 188 | set_target_properties (AddOn PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/$") 189 | endif () 190 | 191 | if (WIN32) 192 | target_link_options (AddOn PUBLIC "${ResourceObjectsDir}/${AC_ADDON_NAME}.res") 193 | target_link_options (AddOn PUBLIC /export:GetExportedFuncAddrs,@1 /export:SetImportedFuncAddrs,@2) 194 | endif () 195 | 196 | target_include_directories (AddOn PUBLIC 197 | ${AddOnSourcesFolder} 198 | ${AC_API_DEVKIT_DIR}/Inc 199 | ) 200 | 201 | LinkACAP_STATLibraries (AddOn ARCHICAD_VERSION) 202 | 203 | if (NOT WIN32) 204 | find_library (CocoaFramework Cocoa) 205 | target_link_libraries (AddOn ${CocoaFramework}) 206 | endif () 207 | 208 | SetCompilerOptions (AddOn ARCHICAD_VERSION) 209 | set_source_files_properties (${AddOnSourceFiles} PROPERTIES LANGUAGE CXX) 210 | 211 | file (GLOB ModuleFolders ${AC_API_DEVKIT_DIR}/Modules/*) 212 | target_include_directories (AddOn PUBLIC ${ModuleFolders}) 213 | if (WIN32) 214 | file (GLOB LibFilesInFolder ${AC_API_DEVKIT_DIR}/Modules/*/*/*.lib) 215 | target_link_libraries (AddOn ${LibFilesInFolder}) 216 | else () 217 | file (GLOB FrameworkFilesInFolder ${AC_API_DEVKIT_DIR}/Frameworks/*.framework) 218 | target_link_libraries (AddOn ${FrameworkFilesInFolder}) 219 | endif () 220 | add_dependencies (AddOn AddOnResources) 221 | -------------------------------------------------------------------------------- /Sources/AddOnResources/Tools/CompileResources.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import platform 4 | import subprocess 5 | import shutil 6 | import codecs 7 | import argparse 8 | 9 | class ResourceCompiler (object): 10 | def __init__ (self, devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath): 11 | self.devKitPath = devKitPath 12 | self.languageCode = languageCode 13 | self.sourcesPath = sourcesPath 14 | self.resourcesPath = resourcesPath 15 | self.resourceObjectsPath = resourceObjectsPath 16 | self.resConvPath = None 17 | 18 | def IsValid (self): 19 | if self.resConvPath == None: 20 | return False 21 | if not os.path.exists (self.resConvPath): 22 | return False 23 | return True 24 | 25 | def GetPrecompiledResourceFilePath (self, grcFilePath): 26 | grcFileName = os.path.split (grcFilePath)[1] 27 | return os.path.join (self.resourceObjectsPath, grcFileName + '.i') 28 | 29 | def CompileLocalizedResources (self): 30 | locResourcesFolder = os.path.join (self.resourcesPath, 'R' + self.languageCode) 31 | grcFiles = self.CollectFilesFromFolderWithExtension (locResourcesFolder, '.grc') 32 | for grcFilePath in grcFiles: 33 | assert self.CompileResourceFile (grcFilePath), 'Failed to compile resource: ' + grcFilePath 34 | 35 | def CompileFixResources (self): 36 | fixResourcesFolder = os.path.join (self.resourcesPath, 'RFIX') 37 | grcFiles = self.CollectFilesFromFolderWithExtension (fixResourcesFolder, '.grc') 38 | for grcFilePath in grcFiles: 39 | assert self.CompileResourceFile (grcFilePath), 'Failed to compile resource: ' + grcFilePath 40 | 41 | def RunResConv (self, platformSign, codepage, inputFilePath, nativeResourceFileExtenion): 42 | imageResourcesFolder = os.path.join (self.resourcesPath, 'RFIX', 'Images') 43 | inputFileBaseName = os.path.splitext (os.path.split (inputFilePath)[1])[0] 44 | nativeResourceFilePath = os.path.join (self.resourceObjectsPath, inputFileBaseName + nativeResourceFileExtenion) 45 | result = subprocess.call ([ 46 | self.resConvPath, 47 | '-m', 'r', # resource compile mode 48 | '-T', platformSign, # target platform 49 | '-q', 'utf8', codepage, # code page conversion 50 | '-w', '2', # HiDPI image size list 51 | '-p', imageResourcesFolder, # image search path 52 | '-i', inputFilePath, # input path 53 | '-o', nativeResourceFilePath # output path 54 | ]) 55 | if result != 0: 56 | return False 57 | return True 58 | 59 | def CollectFilesFromFolderWithExtension (self, folderPath, extension): 60 | result = [] 61 | for fileName in os.listdir (folderPath): 62 | fileExtension = os.path.splitext (fileName)[1] 63 | if fileExtension.lower () == extension.lower (): 64 | fullPath = os.path.join (folderPath, fileName) 65 | result.append (fullPath) 66 | return result 67 | 68 | class WinResourceCompiler (ResourceCompiler): 69 | def __init__ (self, devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath): 70 | super (WinResourceCompiler, self).__init__ (devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath) 71 | self.resConvPath = os.path.join (devKitPath, 'Tools', 'Win', 'ResConv.exe') 72 | 73 | def PrecompileResourceFile (self, grcFilePath): 74 | precompiledGrcFilePath = self.GetPrecompiledResourceFilePath (grcFilePath) 75 | result = subprocess.call ([ 76 | 'cl', 77 | '/nologo', 78 | '/X', 79 | '/EP', 80 | '/P', 81 | '/I', os.path.join (self.devKitPath, 'Inc'), 82 | '/I', os.path.join (self.devKitPath, 'Modules', 'DGLib'), 83 | '/I', self.sourcesPath, 84 | '/I', self.resourceObjectsPath, 85 | '/DWINDOWS', 86 | '/execution-charset:utf-8', 87 | '/Fi{}'.format (precompiledGrcFilePath), 88 | grcFilePath, 89 | ]) 90 | assert result == 0, 'Failed to precompile resource ' + grcFilePath 91 | return precompiledGrcFilePath 92 | 93 | def CompileResourceFile (self, grcFilePath): 94 | precompiledGrcFilePath = self.PrecompileResourceFile (grcFilePath) 95 | return self.RunResConv ('W', '1252', precompiledGrcFilePath, '.rc2') 96 | 97 | def GetNativeResourceFile (self): 98 | defaultNativeResourceFile = os.path.join (self.resourcesPath, 'RFIX.win', 'AddOnMain.rc2') 99 | if os.path.exists (defaultNativeResourceFile): 100 | return defaultNativeResourceFile 101 | 102 | existingNativeResourceFiles = self.CollectFilesFromFolderWithExtension (os.path.join (self.resourcesPath, 'RFIX.win'), '.rc2') 103 | assert existingNativeResourceFiles, 'Native resource file was not found at RFIX.win folder' 104 | 105 | return existingNativeResourceFiles[0] 106 | 107 | def CompileNativeResource (self, resultResourcePath): 108 | nativeResourceFile = self.GetNativeResourceFile () 109 | result = subprocess.call ([ 110 | 'rc', 111 | '/i', os.path.join (self.devKitPath, 'Inc'), 112 | '/i', os.path.join (self.devKitPath, 'Modules', 'DGLib'), 113 | '/i', self.sourcesPath, 114 | '/i', self.resourceObjectsPath, 115 | '/fo', resultResourcePath, 116 | nativeResourceFile 117 | ]) 118 | assert result == 0, 'Failed to compile native resource ' + nativeResourceFile 119 | 120 | class MacResourceCompiler (ResourceCompiler): 121 | def __init__ (self, devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath): 122 | super (MacResourceCompiler, self).__init__ (devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath) 123 | self.resConvPath = os.path.join (devKitPath, 'Tools', 'OSX', 'ResConv') 124 | 125 | def PrecompileResourceFile (self, grcFilePath): 126 | precompiledGrcFilePath = self.GetPrecompiledResourceFilePath (grcFilePath) 127 | result = subprocess.call ([ 128 | 'clang', 129 | '-x', 'c++', 130 | '-E', 131 | '-P', 132 | '-Dmacintosh', 133 | '-I', os.path.join (self.devKitPath, 'Inc'), 134 | '-I', os.path.join (self.devKitPath, 'Modules', 'DGLib'), 135 | '-I', self.sourcesPath, 136 | '-I', self.resourceObjectsPath, 137 | '-o', precompiledGrcFilePath, 138 | grcFilePath, 139 | ]) 140 | assert result == 0, 'Failed to precompile resource ' + grcFilePath 141 | return precompiledGrcFilePath 142 | 143 | def CompileResourceFile (self, grcFilePath): 144 | precompiledGrcFilePath = self.PrecompileResourceFile (grcFilePath) 145 | return self.RunResConv ('M', 'utf16', precompiledGrcFilePath, '.ro') 146 | 147 | def CompileNativeResource (self, resultResourcePath): 148 | resultLocalizedResourcePath = os.path.join (resultResourcePath, 'English.lproj') 149 | if not os.path.exists (resultLocalizedResourcePath): 150 | os.makedirs (resultLocalizedResourcePath) 151 | resultLocalizableStringsPath = os.path.join (resultLocalizedResourcePath, 'Localizable.strings') 152 | resultLocalizableStringsFile = codecs.open (resultLocalizableStringsPath, 'w', 'utf-16') 153 | for fileName in os.listdir (self.resourceObjectsPath): 154 | filePath = os.path.join (self.resourceObjectsPath, fileName) 155 | extension = os.path.splitext (fileName)[1].lower () 156 | if extension == '.tif': 157 | shutil.copy (filePath, resultResourcePath) 158 | elif extension == '.rsrd': 159 | shutil.copy (filePath, resultLocalizedResourcePath) 160 | elif extension == '.strings': 161 | stringsFile = codecs.open (filePath, 'r', 'utf-16') 162 | resultLocalizableStringsFile.write (stringsFile.read ()) 163 | stringsFile.close () 164 | resultLocalizableStringsFile.close () 165 | 166 | def Main (argv): 167 | parser = argparse.ArgumentParser (description = 'Archicad Add-On Resource Compiler.') 168 | parser.add_argument ('languageCode', help = 'Language code of the Add-On.') 169 | parser.add_argument ('devKitPath', help = 'Path of the Archicad Development Kit.') 170 | parser.add_argument ('sourcesPath', help = 'Path of the sources folder of the Add-On.') 171 | parser.add_argument ('resourcesPath', help = 'Path of the resources folder of the Add-On.') 172 | parser.add_argument ('resourceObjectsPath', help = 'Path of the folder to build resource objects.') 173 | parser.add_argument ('resultResourcePath', help = 'Path of the resulting resource.') 174 | args = parser.parse_args () 175 | 176 | currentDir = os.path.dirname (os.path.abspath (__file__)) 177 | os.chdir (currentDir) 178 | 179 | languageCode = args.languageCode 180 | devKitPath = os.path.abspath (args.devKitPath) 181 | sourcesPath = os.path.abspath (args.sourcesPath) 182 | resourcesPath = os.path.abspath (args.resourcesPath) 183 | resourceObjectsPath = os.path.abspath (args.resourceObjectsPath) 184 | resultResourcePath = os.path.abspath (args.resultResourcePath) 185 | 186 | resourceCompiler = None 187 | system = platform.system () 188 | if system == 'Windows': 189 | resourceCompiler = WinResourceCompiler (devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath) 190 | elif system == 'Darwin': 191 | resourceCompiler = MacResourceCompiler (devKitPath, languageCode, sourcesPath, resourcesPath, resourceObjectsPath) 192 | 193 | assert resourceCompiler, 'Platform is not supported' 194 | assert resourceCompiler.IsValid (), 'Invalid resource compiler' 195 | 196 | resourceCompiler.CompileLocalizedResources () 197 | resourceCompiler.CompileFixResources () 198 | resourceCompiler.CompileNativeResource (resultResourcePath) 199 | 200 | return 0 201 | 202 | sys.exit (Main (sys.argv)) 203 | -------------------------------------------------------------------------------- /Sources/AddOn/MazeGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "MazeGenerator.hpp" 2 | 3 | #include 4 | 5 | namespace MG 6 | { 7 | 8 | static const double Eps = 0.00005; 9 | 10 | const CellId InvalidCellId = -1; 11 | const WallId InvalidWallId = -1; 12 | 13 | class WallCollector 14 | { 15 | public: 16 | enum class Direction 17 | { 18 | Horizontal, 19 | Vertical 20 | }; 21 | 22 | WallCollector (std::vector& wallGeometries, Direction direction, double elevation) : 23 | wallGeometries (wallGeometries), 24 | direction (direction), 25 | elevation (elevation), 26 | begPosition (0.0), 27 | endPosition (0.0), 28 | hasWall (false) 29 | { 30 | 31 | } 32 | 33 | void AddWall (double beg, double end) 34 | { 35 | if (hasWall && std::fabs (endPosition - beg) > Eps) { 36 | Flush (); 37 | } 38 | if (!hasWall) { 39 | begPosition = beg; 40 | } 41 | endPosition = end; 42 | hasWall = true; 43 | } 44 | 45 | void Flush () 46 | { 47 | if (hasWall) { 48 | if (direction == Direction::Horizontal) { 49 | wallGeometries.push_back (WallGeometry (begPosition, elevation, endPosition, elevation)); 50 | } else if (direction == Direction::Vertical) { 51 | wallGeometries.push_back (WallGeometry (elevation, begPosition, elevation, endPosition)); 52 | } 53 | hasWall = false; 54 | } 55 | } 56 | 57 | private: 58 | std::vector& wallGeometries; 59 | Direction direction; 60 | double elevation; 61 | double begPosition; 62 | double endPosition; 63 | bool hasWall; 64 | }; 65 | 66 | static size_t GetDirectionIndex (Direction dir) 67 | { 68 | switch (dir) { 69 | case Direction::Left: 70 | return 0; 71 | case Direction::Right: 72 | return 1; 73 | case Direction::Top: 74 | return 2; 75 | case Direction::Bottom: 76 | return 3; 77 | default: 78 | return (size_t) -1; 79 | } 80 | } 81 | 82 | static Direction GetOppositeDirection (Direction dir) 83 | { 84 | if (dir == Direction::Left) { 85 | return Direction::Right; 86 | } else if (dir == Direction::Right) { 87 | return Direction::Left; 88 | } else if (dir == Direction::Top) { 89 | return Direction::Bottom; 90 | } else if (dir == Direction::Bottom) { 91 | return Direction::Top; 92 | } 93 | return Direction::Invalid; 94 | } 95 | 96 | Cell::Cell () : 97 | walls (4, InvalidWallId) 98 | { 99 | 100 | } 101 | 102 | bool Cell::HasWall (Direction dir) const 103 | { 104 | size_t index = GetDirectionIndex (dir); 105 | return walls[index] != InvalidWallId; 106 | } 107 | 108 | WallId Cell::GetWall (Direction dir) const 109 | { 110 | size_t index = GetDirectionIndex (dir); 111 | return walls[index]; 112 | } 113 | 114 | void Cell::EnumerateWalls (const std::function& processor) const 115 | { 116 | for (WallId wallId : walls) { 117 | if (wallId != InvalidWallId) { 118 | processor (wallId); 119 | } 120 | } 121 | } 122 | 123 | void Cell::AddWall (Direction dir, WallId wallId) 124 | { 125 | size_t index = GetDirectionIndex (dir); 126 | walls[index] = wallId; 127 | } 128 | 129 | void Cell::RemoveWall (WallId wallId) 130 | { 131 | for (size_t i = 0; i < walls.size (); i++) { 132 | if (walls[i] == wallId) { 133 | walls[i] = InvalidWallId; 134 | } 135 | } 136 | } 137 | 138 | Wall::Wall () : 139 | Wall (InvalidCellId, InvalidCellId) 140 | { 141 | 142 | } 143 | 144 | Wall::Wall (CellId cellId1, CellId cellId2) : 145 | cellId1 (cellId1), 146 | cellId2 (cellId2) 147 | { 148 | 149 | } 150 | 151 | CellId Wall::GetCellId1 () const 152 | { 153 | return cellId1; 154 | } 155 | 156 | CellId Wall::GetCellId2 () const 157 | { 158 | return cellId2; 159 | } 160 | 161 | CellId Wall::GetOtherCellId (CellId cellId) const 162 | { 163 | if (cellId == cellId1) { 164 | return cellId2; 165 | } else if (cellId == cellId2) { 166 | return cellId1; 167 | } 168 | return InvalidCellId; 169 | } 170 | 171 | WallGeometry::WallGeometry (double begX, double begY, double endX, double endY) : 172 | begX (begX), 173 | begY (begY), 174 | endX (endX), 175 | endY (endY) 176 | { 177 | 178 | } 179 | 180 | Maze::Maze () : 181 | rows (0), 182 | cols (0), 183 | cells (), 184 | walls (), 185 | nextWallId (0) 186 | { 187 | 188 | } 189 | 190 | Maze::Maze (int rowCount, int colCount) : 191 | rows (0), 192 | cols (0), 193 | cells (), 194 | walls (), 195 | nextWallId (0) 196 | { 197 | Reset (rowCount, colCount); 198 | } 199 | 200 | void Maze::Reset (int rowCount, int colCount) 201 | { 202 | rows = rowCount; 203 | cols = colCount; 204 | cells.clear (); 205 | walls.clear (); 206 | nextWallId = 0; 207 | 208 | cells.assign (rows * cols, Cell ()); 209 | for (int row = 0; row < rows; row++) { 210 | for (int col = 0; col < cols; col++) { 211 | AddWall (row, col, Direction::Left); 212 | AddWall (row, col, Direction::Top); 213 | if (row == rows - 1) { 214 | AddWall (row, col, Direction::Bottom); 215 | } 216 | if (col == cols - 1) { 217 | AddWall (row, col, Direction::Right); 218 | } 219 | } 220 | } 221 | } 222 | 223 | CellId Maze::GetCellId (int row, int col) const 224 | { 225 | if (row < 0 || row >= rows) { 226 | return InvalidCellId; 227 | } 228 | if (col < 0 || col >= cols) { 229 | return InvalidCellId; 230 | } 231 | return (row * cols + col); 232 | } 233 | 234 | WallId Maze::GetWallId (int row, int col, Direction dir) const 235 | { 236 | CellId cellId = GetCellId (row, col); 237 | if (cellId == InvalidCellId) { 238 | return InvalidWallId; 239 | } 240 | const Cell& cell = cells[cellId]; 241 | return cell.GetWall (dir); 242 | } 243 | 244 | const Cell& Maze::GetCell (CellId cellId) const 245 | { 246 | return cells[cellId]; 247 | } 248 | 249 | const Wall& Maze::GetWall (WallId wallId) const 250 | { 251 | return walls.at (wallId); 252 | } 253 | 254 | WallId Maze::AddWall (int row, int col, Direction dir) 255 | { 256 | CellId currCellId = GetCellId (row, col); 257 | if (currCellId == InvalidCellId) { 258 | return InvalidWallId; 259 | } 260 | 261 | Cell& currCell = cells[currCellId]; 262 | if (currCell.HasWall (dir)) { 263 | return InvalidWallId; 264 | } 265 | 266 | CellId nextCellId = InvalidCellId; 267 | Direction nextDirection = GetOppositeDirection (dir); 268 | if (dir == Direction::Left) { 269 | nextCellId = GetCellId (row, col - 1); 270 | } else if (dir == Direction::Right) { 271 | nextCellId = GetCellId (row, col + 1); 272 | } else if (dir == Direction::Top) { 273 | nextCellId = GetCellId (row - 1, col); 274 | } else if (dir == Direction::Bottom) { 275 | nextCellId = GetCellId (row + 1, col); 276 | } 277 | 278 | if (nextCellId != InvalidCellId && cells[nextCellId].HasWall (nextDirection)) { 279 | return InvalidWallId; 280 | } 281 | 282 | WallId wallId = nextWallId++; 283 | walls.insert ({ wallId, Wall (currCellId, nextCellId) }); 284 | 285 | currCell.AddWall (dir, wallId); 286 | if (nextCellId != InvalidCellId) { 287 | Cell& nextCell = cells[nextCellId]; 288 | if (!nextCell.HasWall (nextDirection)) { 289 | nextCell.AddWall (nextDirection, wallId); 290 | } 291 | } 292 | 293 | return wallId; 294 | } 295 | 296 | void Maze::RemoveWall (WallId wallId) 297 | { 298 | const Wall& wall = walls[wallId]; 299 | CellId cellId1 = wall.GetCellId1 (); 300 | if (cellId1 != InvalidCellId) { 301 | cells[cellId1].RemoveWall (wallId); 302 | } 303 | CellId cellId2 = wall.GetCellId2 (); 304 | if (cellId2 != InvalidCellId) { 305 | cells[cellId2].RemoveWall (wallId); 306 | } 307 | walls.erase (wallId); 308 | } 309 | 310 | std::vector Maze::GetWallGeometries (double cellSize) const 311 | { 312 | std::vector wallGeometries; 313 | std::vector horizontalCollectors; 314 | std::vector verticalCollectors; 315 | 316 | for (int row = 0; row <= rows; row++) { 317 | double top = row * cellSize; 318 | horizontalCollectors.push_back (WallCollector (wallGeometries, WallCollector::Direction::Horizontal, top)); 319 | } 320 | 321 | for (int col = 0; col <= cols; col++) { 322 | double left = col * cellSize; 323 | verticalCollectors.push_back (WallCollector (wallGeometries, WallCollector::Direction::Vertical, left)); 324 | } 325 | 326 | for (int row = 0; row < rows; row++) { 327 | double top = row * cellSize; 328 | double bottom = (row + 1) * cellSize; 329 | for (int col = 0; col < cols; col++) { 330 | CellId cellId = GetCellId (row, col); 331 | double left = col * cellSize; 332 | double right = (col + 1) * cellSize; 333 | const Cell& cell = cells[cellId]; 334 | if (cell.HasWall (Direction::Top)) { 335 | horizontalCollectors[row].AddWall (left, right); 336 | } 337 | if (cell.HasWall (Direction::Left)) { 338 | verticalCollectors[col].AddWall (top, bottom); 339 | } 340 | if (row == rows - 1 && cell.HasWall (Direction::Bottom)) { 341 | horizontalCollectors[row + 1].AddWall (left, right); 342 | } 343 | if (col == cols - 1 && cell.HasWall (Direction::Right)) { 344 | verticalCollectors[col + 1].AddWall (top, bottom); 345 | } 346 | } 347 | } 348 | for (WallCollector& collector : horizontalCollectors) { 349 | collector.Flush (); 350 | } 351 | for (WallCollector& collector : verticalCollectors) { 352 | collector.Flush (); 353 | } 354 | 355 | return wallGeometries; 356 | } 357 | 358 | MazeGenerator::MazeGenerator (int rowCount, int colCount) : 359 | maze (), 360 | rowCount (rowCount), 361 | colCount (colCount) 362 | { 363 | 364 | } 365 | 366 | bool MazeGenerator::Generate () 367 | { 368 | std::srand ((unsigned int) std::time (nullptr)); 369 | 370 | maze.Reset (rowCount, colCount); 371 | visited.clear (); 372 | walls.clear (); 373 | 374 | CellId firstCellId = maze.GetCellId (0, 0); 375 | VisitCell (firstCellId); 376 | 377 | while (!walls.empty ()) { 378 | WallId wallId = SelectRandomWall (); 379 | const Wall& wall = maze.GetWall (wallId); 380 | CellId cellId1 = wall.GetCellId1 (); 381 | CellId cellId2 = wall.GetCellId2 (); 382 | bool cellVisited1 = (visited.find (cellId1) != visited.end ()); 383 | bool cellVisited2 = (visited.find (cellId2) != visited.end ()); 384 | if (cellVisited1 != cellVisited2) { 385 | CellId newCellId = (cellVisited1 ? cellId2 : cellId1); 386 | maze.RemoveWall (wallId); 387 | VisitCell (newCellId); 388 | } 389 | walls.erase (wallId); 390 | } 391 | 392 | WallId entrance = maze.GetWallId (0, 0, Direction::Top); 393 | WallId exit = maze.GetWallId (rowCount - 1, colCount - 1, Direction::Bottom); 394 | maze.RemoveWall (entrance); 395 | maze.RemoveWall (exit); 396 | 397 | return true; 398 | } 399 | 400 | const Maze& MazeGenerator::GetMaze () const 401 | { 402 | return maze; 403 | } 404 | 405 | void MazeGenerator::VisitCell (CellId cellId) 406 | { 407 | const Cell& cell = maze.GetCell (cellId); 408 | cell.EnumerateWalls ([&] (WallId wallId) { 409 | const Wall& wall = maze.GetWall (wallId); 410 | CellId otherCellId = wall.GetOtherCellId (cellId); 411 | if (otherCellId == InvalidCellId || visited.find (otherCellId) != visited.end ()) { 412 | return; 413 | } 414 | walls.insert (wallId); 415 | }); 416 | visited.insert (cellId); 417 | } 418 | 419 | WallId MazeGenerator::SelectRandomWall () 420 | { 421 | if (walls.empty ()) { 422 | return InvalidWallId; 423 | } 424 | size_t random = std::rand () % walls.size (); 425 | auto it = walls.begin (); 426 | std::advance (it, random); 427 | return *it; 428 | } 429 | 430 | } 431 | -------------------------------------------------------------------------------- /Sources/AddOnResources/RFIX/Images/DialogDrawing_220x160.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | --------------------------------------------------------------------------------