From 1174623aa973fdd2f5628446c624be6dad54c57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20St=C3=B6glehner?= Date: Tue, 8 Feb 2022 10:46:01 +0100 Subject: [PATCH 1/2] CATTY-369 Implement place visually feature --- src/Catty.xcodeproj/project.pbxproj | 52 ++--- src/Catty/DataModel/Bricks/Brick.h | 2 + src/Catty/DataModel/Bricks/Brick.m | 6 + .../Motion/GlideToBrick+CBXMLHandler.swift | 60 ++++++ .../DataModel/Bricks/Motion/GlideToBrick.h | 35 ---- .../DataModel/Bricks/Motion/GlideToBrick.m | 104 ---------- .../Bricks/Motion/GlideToBrick.swift | 102 ++++++++++ .../Motion/PlaceAtBrick+CBXMLHandler.swift | 54 +++++ .../DataModel/Bricks/Motion/PlaceAtBrick.h | 33 --- .../DataModel/Bricks/Motion/PlaceAtBrick.m | 79 ------- .../Bricks/Motion/PlaceAtBrick.swift | 83 ++++++++ src/Catty/DataModel/Formula/FormulaElement.h | 2 + src/Catty/DataModel/Formula/FormulaElement.m | 12 ++ .../DataModel/SpriteObject/SpriteObject.m | 14 +- .../Defines/LanguageTranslationDefines.h | 1 + .../LanguageTranslationDefinesSwift.swift | 1 + src/Catty/Defines/ProjectDefines.h | 1 + .../BrickVisualPlacementProtocol.swift} | 13 +- .../FormulaEditorViewController.m | 54 ++++- .../VisualPlacementViewController.swift | 192 ++++++++++++++++++ .../menu_icon.png | Bin 5778 -> 0 bytes .../menu_icon@2x.png | Bin 4360 -> 0 bytes .../Contents.json | 13 +- .../PocketCode_small.png | Bin 0 -> 17029 bytes .../PlayerEngine/Helper/CBSceneHelper.swift | 15 +- .../Look/SetSizeToBrick+Instruction.swift | 5 + .../Motion/GlideToBrick+Instruction.swift | 10 +- .../Motion/PlaceAtBrick+Instruction.swift | 2 +- .../Localization/en.lproj/Localizable.strings | 3 + src/Catty/Storyboard+XIB/iPhone.storyboard | 135 +++++++++--- .../Supporting Files/Catty-Bridging-Header.h | 3 - .../ScriptCollectionViewController.m | 57 +++++- .../Stage/StagePresenterViewController.m | 1 + ...terViewControllerScreenshotExtension.swift | 22 +- .../BrickCellData/BrickCellFormulaData.m | 9 +- .../BrickCells/BrickCell.h | 1 + .../BrickCells/Motion/GlideToBrickCell.m | 1 - src/Catty/XML/Old Parser/ProjectParser.m | 9 + .../Bricks/Motion/GlideToBrick+CBXMLHandler.m | 71 ------- .../Bricks/Motion/PlaceAtBrick+CBXMLHandler.h | 30 --- .../Bricks/Motion/PlaceAtBrick+CBXMLHandler.m | 63 ------ .../Abstract/BrickMoveManagerAbstractTest.m | 1 - src/CattyTests/Bricks/GlideToBrickTests.swift | 42 ++-- .../Project/RequiredResourcesTests.swift | 32 +-- .../XMLParserBrickTests093.swift | 18 +- .../XMLParserTests0994.swift | 4 +- .../XMLParserTests0995.swift | 4 +- .../XMLParserTests0996.swift | 4 +- .../XMLParserTests0998.swift | 4 +- .../XMLParserTests0999.swift | 4 +- .../XMLParserTests09993.swift | 4 +- .../LanguageTranslationDefinesUI.swift | 1 + .../Extensions/XCTestCaseExtension.swift | 16 ++ .../ScriptCollectionView/BrickCellTests.swift | 6 + src/CattyUITests/VisualPlacementTest.swift | 127 ++++++++++++ 55 files changed, 1051 insertions(+), 566 deletions(-) create mode 100644 src/Catty/DataModel/Bricks/Motion/GlideToBrick+CBXMLHandler.swift delete mode 100644 src/Catty/DataModel/Bricks/Motion/GlideToBrick.h delete mode 100644 src/Catty/DataModel/Bricks/Motion/GlideToBrick.m create mode 100644 src/Catty/DataModel/Bricks/Motion/GlideToBrick.swift create mode 100644 src/Catty/DataModel/Bricks/Motion/PlaceAtBrick+CBXMLHandler.swift delete mode 100644 src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.h delete mode 100644 src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.m create mode 100644 src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.swift rename src/Catty/{XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.h => Extension&Delegate&Protocol/Protocols/Bricks/BrickData/BrickVisualPlacementProtocol.swift} (76%) create mode 100644 src/Catty/FormulaEditor/ViewController/VisualPlacementViewController.swift delete mode 100644 src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon.png delete mode 100644 src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon@2x.png rename src/Catty/Images.xcassets/{Icons/menu_icon_(deprecated).imageset => PocketCodeSmall.imageset}/Contents.json (53%) create mode 100644 src/Catty/Images.xcassets/PocketCodeSmall.imageset/PocketCode_small.png delete mode 100644 src/Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.m delete mode 100644 src/Catty/XML/XMLHandler/Bricks/Motion/PlaceAtBrick+CBXMLHandler.h delete mode 100644 src/Catty/XML/XMLHandler/Bricks/Motion/PlaceAtBrick+CBXMLHandler.m create mode 100644 src/CattyUITests/VisualPlacementTest.swift diff --git a/src/Catty.xcodeproj/project.pbxproj b/src/Catty.xcodeproj/project.pbxproj index 2dddb92160..3b4aa41a02 100644 --- a/src/Catty.xcodeproj/project.pbxproj +++ b/src/Catty.xcodeproj/project.pbxproj @@ -358,12 +358,15 @@ 49402BBF28118354009FCBF8 /* RightMiddleFingerKnuckleXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49402BBB28118354009FCBF8 /* RightMiddleFingerKnuckleXSensor.swift */; }; 49402BC028118354009FCBF8 /* RightRingFingerKnuckleXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49402BBC28118354009FCBF8 /* RightRingFingerKnuckleXSensor.swift */; }; 49402BC128118354009FCBF8 /* RightRingFingerKnuckleYSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49402BBD28118354009FCBF8 /* RightRingFingerKnuckleYSensor.swift */; }; + 4949720027BE4D4E003D8C7E /* VisualPlacementTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 494971FF27BE4D4E003D8C7E /* VisualPlacementTest.swift */; }; 49519D2F27DCD6B100E32E88 /* SecondFaceDetectedSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49519D2E27DCD6B100E32E88 /* SecondFaceDetectedSensor.swift */; }; 49519D3327DCE0EC00E32E88 /* SecondFacePositionXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49519D3227DCE0EC00E32E88 /* SecondFacePositionXSensor.swift */; }; 49519D3527DCE0FE00E32E88 /* SecondFacePositionYSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49519D3427DCE0FE00E32E88 /* SecondFacePositionYSensor.swift */; }; 49519D3727DCE11700E32E88 /* SecondFaceSizeSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49519D3627DCE11700E32E88 /* SecondFaceSizeSensor.swift */; }; 49530A8227FDDADC003A9E24 /* FaceDetectionSensors.xml in Resources */ = {isa = PBXBuildFile; fileRef = 49530A8127FDDADC003A9E24 /* FaceDetectionSensors.xml */; }; 49530A8427FDDC04003A9E24 /* BackwardsCompatibleFaceDetectionSensors.xml in Resources */ = {isa = PBXBuildFile; fileRef = 49530A8327FDDC03003A9E24 /* BackwardsCompatibleFaceDetectionSensors.xml */; }; + 495A1EC827B0F579009610F5 /* PlaceAtBrick.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495A1EC727B0F579009610F5 /* PlaceAtBrick.swift */; }; + 495A1ECC27B105B0009610F5 /* PlaceAtBrick+CBXMLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 495A1ECB27B105B0009610F5 /* PlaceAtBrick+CBXMLHandler.swift */; }; 4963EB632812C5E100A9B3FA /* TextBlockXFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4963EB622812C5E100A9B3FA /* TextBlockXFunction.swift */; }; 4963EB672812C5EE00A9B3FA /* TextBlockYFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4963EB662812C5EE00A9B3FA /* TextBlockYFunction.swift */; }; 4963EB6C2812CFB800A9B3FA /* TextBlockFromCameraFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4963EB6B2812CFB800A9B3FA /* TextBlockFromCameraFunction.swift */; }; @@ -382,6 +385,7 @@ 4979F41127E85ACA00ABCAD6 /* LeftEyeInnerYSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4979F41027E85ACA00ABCAD6 /* LeftEyeInnerYSensor.swift */; }; 4979F41327E85AD900ABCAD6 /* LeftEyeOuterXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4979F41227E85AD900ABCAD6 /* LeftEyeOuterXSensor.swift */; }; 4979F41527E85AE700ABCAD6 /* LeftEyeOuterYSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4979F41427E85AE700ABCAD6 /* LeftEyeOuterYSensor.swift */; }; + 497B439E27B24B5E0059091F /* GlideToBrick.swift in Sources */ = {isa = PBXBuildFile; fileRef = 497B439D27B24B5D0059091F /* GlideToBrick.swift */; }; 4981EDCD289AC2690052AEB1 /* YOLOv3Tiny.mlmodel in Sources */ = {isa = PBXBuildFile; fileRef = 4981EDCC289AC2690052AEB1 /* YOLOv3Tiny.mlmodel */; }; 4981EDFB289AC98C0052AEB1 /* XOfObjectWithIDFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4981EDF7289AC98C0052AEB1 /* XOfObjectWithIDFunction.swift */; }; 4981EDFC289AC98C0052AEB1 /* YOfObjectWithIDFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4981EDFA289AC98C0052AEB1 /* YOfObjectWithIDFunction.swift */; }; @@ -454,7 +458,10 @@ 49B55F9B27E88702000A95B4 /* RightEyebrowOuterXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B55F8F27E88701000A95B4 /* RightEyebrowOuterXSensor.swift */; }; 49B55F9C27E88702000A95B4 /* RightEyebrowCenterXSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B55F9027E88702000A95B4 /* RightEyebrowCenterXSensor.swift */; }; 49B55F9D27E88702000A95B4 /* LeftEyebrowInnerYSensor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B55F9127E88702000A95B4 /* LeftEyebrowInnerYSensor.swift */; }; + 49D5D7AC27B2589700B80096 /* GlideToBrick+CBXMLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49D5D7AB27B2589700B80096 /* GlideToBrick+CBXMLHandler.swift */; }; 49DB87C228A2BE2100DD1224 /* StagePresenterViewControllerOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49DB87C128A2BE2100DD1224 /* StagePresenterViewControllerOrientation.swift */; }; + 49F0DA1627B3A7A3003E4B3A /* BrickVisualPlacementProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F0DA1527B3A7A2003E4B3A /* BrickVisualPlacementProtocol.swift */; }; + 49F0DA1A27B3B4A3003E4B3A /* VisualPlacementViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49F0DA1927B3B4A3003E4B3A /* VisualPlacementViewController.swift */; }; 4C0076DA25F6AF79002D754D /* StagePresenterSideMenuViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C0076D725F6AF79002D754D /* StagePresenterSideMenuViewTests.swift */; }; 4C03A4BE2133FD05007BFFD2 /* FormulaManager+Resources.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C03A4BD2133FD05007BFFD2 /* FormulaManager+Resources.swift */; }; 4C03A4C22133FDE7007BFFD2 /* FormulaManager+Editor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C03A4C12133FDE7007BFFD2 /* FormulaManager+Editor.swift */; }; @@ -1581,10 +1588,8 @@ AA74EF4D1BC0586F00D1E954 /* ChangeXByNBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF311BC0586F00D1E954 /* ChangeXByNBrick+CBXMLHandler.m */; }; AA74EF4E1BC0586F00D1E954 /* ChangeYByNBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF331BC0586F00D1E954 /* ChangeYByNBrick+CBXMLHandler.m */; }; AA74EF4F1BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF351BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.m */; }; - AA74EF501BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF371BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.m */; }; AA74EF511BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF391BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.m */; }; AA74EF521BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF3B1BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.m */; }; - AA74EF531BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF3D1BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.m */; }; AA74EF541BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF3F1BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.m */; }; AA74EF551BC0586F00D1E954 /* PointToBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF411BC0586F00D1E954 /* PointToBrick+CBXMLHandler.m */; }; AA74EF571BC0586F00D1E954 /* SetXBrick+CBXMLHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EF451BC0586F00D1E954 /* SetXBrick+CBXMLHandler.m */; }; @@ -1624,11 +1629,9 @@ AA74EFFC1BC05B5F00D1E954 /* ChangeXByNBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFBB1BC05B5F00D1E954 /* ChangeXByNBrick.m */; }; AA74EFFD1BC05B5F00D1E954 /* ChangeYByNBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFBD1BC05B5F00D1E954 /* ChangeYByNBrick.m */; }; AA74EFFE1BC05B5F00D1E954 /* ComeToFrontBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFBF1BC05B5F00D1E954 /* ComeToFrontBrick.m */; }; - AA74EFFF1BC05B5F00D1E954 /* GlideToBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFC11BC05B5F00D1E954 /* GlideToBrick.m */; }; AA74F0001BC05B5F00D1E954 /* GoNStepsBackBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFC31BC05B5F00D1E954 /* GoNStepsBackBrick.m */; }; AA74F0011BC05B5F00D1E954 /* IfOnEdgeBounceBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFC51BC05B5F00D1E954 /* IfOnEdgeBounceBrick.m */; }; AA74F0021BC05B5F00D1E954 /* MoveNStepsBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFC71BC05B5F00D1E954 /* MoveNStepsBrick.m */; }; - AA74F0031BC05B5F00D1E954 /* PlaceAtBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFC91BC05B5F00D1E954 /* PlaceAtBrick.m */; }; AA74F0041BC05B5F00D1E954 /* PointInDirectionBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFCB1BC05B5F00D1E954 /* PointInDirectionBrick.m */; }; AA74F0051BC05B5F00D1E954 /* PointToBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFCD1BC05B5F00D1E954 /* PointToBrick.m */; }; AA74F0061BC05B5F00D1E954 /* SetXBrick.m in Sources */ = {isa = PBXBuildFile; fileRef = AA74EFCF1BC05B5F00D1E954 /* SetXBrick.m */; }; @@ -2484,12 +2487,15 @@ 49402BBB28118354009FCBF8 /* RightMiddleFingerKnuckleXSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightMiddleFingerKnuckleXSensor.swift; sourceTree = ""; }; 49402BBC28118354009FCBF8 /* RightRingFingerKnuckleXSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightRingFingerKnuckleXSensor.swift; sourceTree = ""; }; 49402BBD28118354009FCBF8 /* RightRingFingerKnuckleYSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightRingFingerKnuckleYSensor.swift; sourceTree = ""; }; + 494971FF27BE4D4E003D8C7E /* VisualPlacementTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualPlacementTest.swift; sourceTree = ""; }; 49519D2E27DCD6B100E32E88 /* SecondFaceDetectedSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondFaceDetectedSensor.swift; sourceTree = ""; }; 49519D3227DCE0EC00E32E88 /* SecondFacePositionXSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondFacePositionXSensor.swift; sourceTree = ""; }; 49519D3427DCE0FE00E32E88 /* SecondFacePositionYSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondFacePositionYSensor.swift; sourceTree = ""; }; 49519D3627DCE11700E32E88 /* SecondFaceSizeSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondFaceSizeSensor.swift; sourceTree = ""; }; 49530A8127FDDADC003A9E24 /* FaceDetectionSensors.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = FaceDetectionSensors.xml; sourceTree = ""; }; 49530A8327FDDC03003A9E24 /* BackwardsCompatibleFaceDetectionSensors.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = BackwardsCompatibleFaceDetectionSensors.xml; sourceTree = ""; }; + 495A1EC727B0F579009610F5 /* PlaceAtBrick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceAtBrick.swift; sourceTree = ""; }; + 495A1ECB27B105B0009610F5 /* PlaceAtBrick+CBXMLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "PlaceAtBrick+CBXMLHandler.swift"; path = "Catty/DataModel/Bricks/Motion/PlaceAtBrick+CBXMLHandler.swift"; sourceTree = SOURCE_ROOT; }; 4963EB622812C5E100A9B3FA /* TextBlockXFunction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBlockXFunction.swift; sourceTree = ""; }; 4963EB662812C5EE00A9B3FA /* TextBlockYFunction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBlockYFunction.swift; sourceTree = ""; }; 4963EB6B2812CFB800A9B3FA /* TextBlockFromCameraFunction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextBlockFromCameraFunction.swift; sourceTree = ""; }; @@ -2507,6 +2513,7 @@ 4979F41027E85ACA00ABCAD6 /* LeftEyeInnerYSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftEyeInnerYSensor.swift; sourceTree = ""; }; 4979F41227E85AD900ABCAD6 /* LeftEyeOuterXSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftEyeOuterXSensor.swift; sourceTree = ""; }; 4979F41427E85AE700ABCAD6 /* LeftEyeOuterYSensor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeftEyeOuterYSensor.swift; sourceTree = ""; }; + 497B439D27B24B5D0059091F /* GlideToBrick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlideToBrick.swift; sourceTree = ""; }; 4981EDCC289AC2690052AEB1 /* YOLOv3Tiny.mlmodel */ = {isa = PBXFileReference; lastKnownFileType = file.mlmodel; path = YOLOv3Tiny.mlmodel; sourceTree = ""; }; 4981EDF7289AC98C0052AEB1 /* XOfObjectWithIDFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XOfObjectWithIDFunction.swift; path = Catty/Functions/ObjectRecognition/XOfObjectWithIDFunction.swift; sourceTree = SOURCE_ROOT; }; 4981EDFA289AC98C0052AEB1 /* YOfObjectWithIDFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = YOfObjectWithIDFunction.swift; path = Catty/Functions/ObjectRecognition/YOfObjectWithIDFunction.swift; sourceTree = SOURCE_ROOT; }; @@ -2580,8 +2587,11 @@ 49B55F8F27E88701000A95B4 /* RightEyebrowOuterXSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightEyebrowOuterXSensor.swift; sourceTree = ""; }; 49B55F9027E88702000A95B4 /* RightEyebrowCenterXSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RightEyebrowCenterXSensor.swift; sourceTree = ""; }; 49B55F9127E88702000A95B4 /* LeftEyebrowInnerYSensor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LeftEyebrowInnerYSensor.swift; sourceTree = ""; }; + 49D5D7AB27B2589700B80096 /* GlideToBrick+CBXMLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "GlideToBrick+CBXMLHandler.swift"; path = "Catty/DataModel/Bricks/Motion/GlideToBrick+CBXMLHandler.swift"; sourceTree = SOURCE_ROOT; }; 49DB87C128A2BE2100DD1224 /* StagePresenterViewControllerOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StagePresenterViewControllerOrientation.swift; sourceTree = ""; }; 49DDC04327EA26B2007E8B0D /* EyeSensorTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyeSensorTest.swift; sourceTree = ""; }; + 49F0DA1527B3A7A2003E4B3A /* BrickVisualPlacementProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrickVisualPlacementProtocol.swift; sourceTree = ""; }; + 49F0DA1927B3B4A3003E4B3A /* VisualPlacementViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualPlacementViewController.swift; sourceTree = ""; }; 4C0076D725F6AF79002D754D /* StagePresenterSideMenuViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StagePresenterSideMenuViewTests.swift; sourceTree = ""; }; 4C03A4BD2133FD05007BFFD2 /* FormulaManager+Resources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FormulaManager+Resources.swift"; sourceTree = ""; }; 4C03A4C12133FDE7007BFFD2 /* FormulaManager+Editor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FormulaManager+Editor.swift"; sourceTree = ""; }; @@ -4091,14 +4101,10 @@ AA74EF331BC0586F00D1E954 /* ChangeYByNBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ChangeYByNBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/ChangeYByNBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; AA74EF341BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "ComeToFrontBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/ComeToFrontBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; AA74EF351BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "ComeToFrontBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/ComeToFrontBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; - AA74EF361BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "GlideToBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; - AA74EF371BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "GlideToBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; AA74EF381BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "GoNStepsBackBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/GoNStepsBackBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; AA74EF391BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "GoNStepsBackBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/GoNStepsBackBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; AA74EF3A1BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MoveNStepsBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/MoveNStepsBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; AA74EF3B1BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MoveNStepsBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/MoveNStepsBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; - AA74EF3C1BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "PlaceAtBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/PlaceAtBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; - AA74EF3D1BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "PlaceAtBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/PlaceAtBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; AA74EF3E1BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "PointInDirectionBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/PointInDirectionBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; AA74EF3F1BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "PointInDirectionBrick+CBXMLHandler.m"; path = "Catty/XML/XMLHandler/Bricks/Motion/PointInDirectionBrick+CBXMLHandler.m"; sourceTree = SOURCE_ROOT; }; AA74EF401BC0586F00D1E954 /* PointToBrick+CBXMLHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "PointToBrick+CBXMLHandler.h"; path = "Catty/XML/XMLHandler/Bricks/Motion/PointToBrick+CBXMLHandler.h"; sourceTree = SOURCE_ROOT; }; @@ -4177,16 +4183,12 @@ AA74EFBD1BC05B5F00D1E954 /* ChangeYByNBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ChangeYByNBrick.m; sourceTree = ""; }; AA74EFBE1BC05B5F00D1E954 /* ComeToFrontBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComeToFrontBrick.h; sourceTree = ""; }; AA74EFBF1BC05B5F00D1E954 /* ComeToFrontBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComeToFrontBrick.m; sourceTree = ""; }; - AA74EFC01BC05B5F00D1E954 /* GlideToBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GlideToBrick.h; sourceTree = ""; }; - AA74EFC11BC05B5F00D1E954 /* GlideToBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GlideToBrick.m; sourceTree = ""; }; AA74EFC21BC05B5F00D1E954 /* GoNStepsBackBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GoNStepsBackBrick.h; sourceTree = ""; }; AA74EFC31BC05B5F00D1E954 /* GoNStepsBackBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GoNStepsBackBrick.m; sourceTree = ""; }; AA74EFC41BC05B5F00D1E954 /* IfOnEdgeBounceBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IfOnEdgeBounceBrick.h; sourceTree = ""; }; AA74EFC51BC05B5F00D1E954 /* IfOnEdgeBounceBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IfOnEdgeBounceBrick.m; sourceTree = ""; }; AA74EFC61BC05B5F00D1E954 /* MoveNStepsBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MoveNStepsBrick.h; sourceTree = ""; }; AA74EFC71BC05B5F00D1E954 /* MoveNStepsBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MoveNStepsBrick.m; sourceTree = ""; }; - AA74EFC81BC05B5F00D1E954 /* PlaceAtBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlaceAtBrick.h; sourceTree = ""; }; - AA74EFC91BC05B5F00D1E954 /* PlaceAtBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaceAtBrick.m; sourceTree = ""; }; AA74EFCA1BC05B5F00D1E954 /* PointInDirectionBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointInDirectionBrick.h; sourceTree = ""; }; AA74EFCB1BC05B5F00D1E954 /* PointInDirectionBrick.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PointInDirectionBrick.m; sourceTree = ""; }; AA74EFCC1BC05B5F00D1E954 /* PointToBrick.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointToBrick.h; sourceTree = ""; }; @@ -6437,6 +6439,7 @@ 9990BB6C1D89D89A0088357A /* BrickStaticChoiceProtocol.h */, 4C1EB20F1AC19B3D0001F431 /* BrickTextProtocol.h */, 4CC0D51F1B01FB73006193C4 /* BrickVariableProtocol.h */, + 49F0DA1527B3A7A2003E4B3A /* BrickVisualPlacementProtocol.swift */, ); path = BrickData; sourceTree = ""; @@ -6857,16 +6860,12 @@ AA74EF331BC0586F00D1E954 /* ChangeYByNBrick+CBXMLHandler.m */, AA74EF341BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.h */, AA74EF351BC0586F00D1E954 /* ComeToFrontBrick+CBXMLHandler.m */, - AA74EF361BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.h */, - AA74EF371BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.m */, AA74EF381BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.h */, AA74EF391BC0586F00D1E954 /* GoNStepsBackBrick+CBXMLHandler.m */, AA74EF7C1BC05A3400D1E954 /* IfOnEdgeBounceBrick+CBXMLHandler.h */, AA74EF7D1BC05A3400D1E954 /* IfOnEdgeBounceBrick+CBXMLHandler.m */, AA74EF3A1BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.h */, AA74EF3B1BC0586F00D1E954 /* MoveNStepsBrick+CBXMLHandler.m */, - AA74EF3C1BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.h */, - AA74EF3D1BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.m */, AA74EF3E1BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.h */, AA74EF3F1BC0586F00D1E954 /* PointInDirectionBrick+CBXMLHandler.m */, AA74EF401BC0586F00D1E954 /* PointToBrick+CBXMLHandler.h */, @@ -6881,6 +6880,8 @@ AA74EF4B1BC0586F00D1E954 /* TurnRightBrick+CBXMLHandler.m */, D30359E424445A6B006688F5 /* GoToBrick+CBXMLHandler.h */, D30359E524445ABE006688F5 /* GoToBrick+CBXMLHandler.m */, + 495A1ECB27B105B0009610F5 /* PlaceAtBrick+CBXMLHandler.swift */, + 49D5D7AB27B2589700B80096 /* GlideToBrick+CBXMLHandler.swift */, ); name = Motion; path = MotionBricks; @@ -8575,6 +8576,7 @@ 4C4EE29A210901E70045F890 /* FormulaEditorViewControllerInputExtension.swift */, F4664F9120DD7A1700E1519A /* FormulaEditorViewControllerSectionExtension.swift */, 18957DDE25B8B870001A623F /* FormulaEditorSubsection.swift */, + 49F0DA1927B3B4A3003E4B3A /* VisualPlacementViewController.swift */, ); path = ViewController; sourceTree = ""; @@ -10355,16 +10357,12 @@ AA74EFBD1BC05B5F00D1E954 /* ChangeYByNBrick.m */, AA74EFBE1BC05B5F00D1E954 /* ComeToFrontBrick.h */, AA74EFBF1BC05B5F00D1E954 /* ComeToFrontBrick.m */, - AA74EFC01BC05B5F00D1E954 /* GlideToBrick.h */, - AA74EFC11BC05B5F00D1E954 /* GlideToBrick.m */, AA74EFC21BC05B5F00D1E954 /* GoNStepsBackBrick.h */, AA74EFC31BC05B5F00D1E954 /* GoNStepsBackBrick.m */, AA74EFC41BC05B5F00D1E954 /* IfOnEdgeBounceBrick.h */, AA74EFC51BC05B5F00D1E954 /* IfOnEdgeBounceBrick.m */, AA74EFC61BC05B5F00D1E954 /* MoveNStepsBrick.h */, AA74EFC71BC05B5F00D1E954 /* MoveNStepsBrick.m */, - AA74EFC81BC05B5F00D1E954 /* PlaceAtBrick.h */, - AA74EFC91BC05B5F00D1E954 /* PlaceAtBrick.m */, AA74EFCA1BC05B5F00D1E954 /* PointInDirectionBrick.h */, AA74EFCB1BC05B5F00D1E954 /* PointInDirectionBrick.m */, AA74EFCC1BC05B5F00D1E954 /* PointToBrick.h */, @@ -10379,6 +10377,8 @@ AA74EFD51BC05B5F00D1E954 /* TurnRightBrick.m */, D38F58652441B24C0079F6EA /* GoToBrick.h */, D38F58612441B19F0079F6EA /* GoToBrick.m */, + 495A1EC727B0F579009610F5 /* PlaceAtBrick.swift */, + 497B439D27B24B5D0059091F /* GlideToBrick.swift */, ); path = Motion; sourceTree = ""; @@ -10704,6 +10704,7 @@ 9797CB8225AF7A6000A50444 /* SceneTVCTests.swift */, 18E908A72624995900EF57D6 /* FormulaEditorComputeDialogTest.swift */, 2EB9FAF3269967FF00DBC4FB /* TrustedDomainTVCTests.swift */, + 494971FF27BE4D4E003D8C7E /* VisualPlacementTest.swift */, ); path = CattyUITests; sourceTree = ""; @@ -12243,6 +12244,7 @@ 4CB1FB741BC2D74F0000A2FE /* XCTestCaseExtension.swift in Sources */, BC19327F21186504009DE43A /* CreateProjectTests.swift in Sources */, BCFED6ED2111C04000BE57BF /* BackgroundsTests.swift in Sources */, + 4949720027BE4D4E003D8C7E /* VisualPlacementTest.swift in Sources */, 2EB9FAF4269967FF00DBC4FB /* TrustedDomainTVCTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -12784,6 +12786,7 @@ 4C0F9F82204BD20A00E71B2D /* SpeakAndWaitBrick+Instruction.swift in Sources */, AA74EFF21BC05B5F00D1E954 /* ChangeTransparencyByNBrick.m in Sources */, 4979F40F27E85AB800ABCAD6 /* LeftEyeInnerXSensor.swift in Sources */, + 49F0DA1627B3A7A3003E4B3A /* BrickVisualPlacementProtocol.swift in Sources */, 929CC0CD1BC39B0A0027DEC0 /* PhiroMotorMoveForwardBrick.m in Sources */, 4C0F9F8E204BD27200E71B2D /* RepeatUntilBrickCell.m in Sources */, AABCB3DB1BB364B9009F67D2 /* CBBroadcastHandlerProtocol.swift in Sources */, @@ -12861,6 +12864,7 @@ 4979F41327E85AD900ABCAD6 /* LeftEyeOuterXSensor.swift in Sources */, AA74EFEC1BC05B5F00D1E954 /* ChangeVariableBrick.m in Sources */, 92FF31031A24DCAA00093DA7 /* Script.m in Sources */, + 495A1ECC27B105B0009610F5 /* PlaceAtBrick+CBXMLHandler.swift in Sources */, 4C822693213FBC4400F3D750 /* MultiFingerTouchedFunction.swift in Sources */, 4420ACB1250929AE00951328 /* AskBrick+CBXMLHandler.swift in Sources */, 1882475924C84D9C00B01653 /* SetPenColorBrickCell.swift in Sources */, @@ -13081,7 +13085,6 @@ AA74EEDC1BC057B900D1E954 /* BroadcastWaitBrick+CBXMLHandler.m in Sources */, 4C0F9F5A204BD16300E71B2D /* CameraBrick+CBXMLHandler.m in Sources */, AA74EFFA1BC05B5F00D1E954 /* ShowBrick.m in Sources */, - AA74EF501BC0586F00D1E954 /* GlideToBrick+CBXMLHandler.m in Sources */, BACDF3571F1F9BB000346167 /* DeleteItemOfUserListBrick+CBXMLHandler.m in Sources */, 921D468A1BDF698A0086AD20 /* NextLookBrick+Instruction.swift in Sources */, 9218B2141CC4AB75007B4C60 /* BrushPickerViewController.m in Sources */, @@ -13160,6 +13163,7 @@ 4CF072AE20D6753D00F93AB5 /* DateYearSensor.swift in Sources */, 4C994E1C207A55E500C415FD /* CompassDirectionSensor.swift in Sources */, 9200C2D51C8082EF002F5CA4 /* HideTextBrickCell.m in Sources */, + 49D5D7AC27B2589700B80096 /* GlideToBrick+CBXMLHandler.swift in Sources */, 46CF173925E2F53000DADF08 /* SetBackgroundBrickCell.swift in Sources */, AA74EF001BC057C900D1E954 /* ShowBrick+CBXMLHandler.m in Sources */, AA74EFE41BC05B5F00D1E954 /* IfLogicBeginBrick.m in Sources */, @@ -13169,6 +13173,7 @@ 97D015912553392A00B6967D /* RoundedImageView.swift in Sources */, 4C5076D22578DBDC00650440 /* SetInstrumentBrick+CBXMLHandler.swift in Sources */, 181CC10724B750AF004A783E /* CBPosition.swift in Sources */, + 497B439E27B24B5E0059091F /* GlideToBrick.swift in Sources */, AA74EFF91BC05B5F00D1E954 /* SetTransparencyBrick.m in Sources */, 186E99192488F74500627E36 /* PenUpBrick+Instruction.swift in Sources */, 056677F727DEA772006B4477 /* StartZigzagStitchBrick+Instruction.swift in Sources */, @@ -13184,6 +13189,7 @@ 4CF0728C20D659F700F93AB5 /* PhiroSideLeftSensor.swift in Sources */, AA74F0C01BC05FCE00D1E954 /* SetXBrickCell.m in Sources */, 6F1911A124E7CE0D00CF1B4F /* SceneMock.swift in Sources */, + 49F0DA1A27B3B4A3003E4B3A /* VisualPlacementViewController.swift in Sources */, AA74EF6F1BC0588A00D1E954 /* PlaySoundBrick+CBXMLHandler.m in Sources */, 49949B462806EAD20073BF65 /* RightWristXSensor.swift in Sources */, 4981EDFB289AC98C0052AEB1 /* XOfObjectWithIDFunction.swift in Sources */, @@ -13208,7 +13214,6 @@ 49949B402806EAD20073BF65 /* LeftElbowYSensor.swift in Sources */, 9E78D88B22A9B1D400948AC3 /* IterableCache.swift in Sources */, 921D467E1BDF61350086AD20 /* GoNStepsBackBrick+Instruction.swift in Sources */, - AA74EF531BC0586F00D1E954 /* PlaceAtBrick+CBXMLHandler.m in Sources */, BB9EC0E927D62CBA009FF8CE /* FormItem.swift in Sources */, 4C2EE41F1B555B55006DE9B8 /* CBXMLPositionStack.m in Sources */, AAF6D9D41BC0B9EA00686849 /* HideBrick+Instruction.swift in Sources */, @@ -13516,6 +13521,7 @@ 4CF0728420D6581900F93AB5 /* PhiroFrontLeftSensor.swift in Sources */, 6F4B2FBB246670C800B5F0ED /* ProjectMigratorProtocol.swift in Sources */, 492E595F287FDEFB00357F09 /* LabelOfObjectWithIDFunction.swift in Sources */, + 495A1EC827B0F579009610F5 /* PlaceAtBrick.swift in Sources */, 9218B2001CC4AB75007B4C60 /* YKImageCropperOverlayView.m in Sources */, 4C0F9F2E204ADC2300E71B2D /* CameraBrick.m in Sources */, 92C631891BC502CB00486958 /* ConnectedDevicesTableViewController.swift in Sources */, @@ -13676,7 +13682,6 @@ 92FF2E8D1A24C7D800093DA7 /* CatrobatAudioPlayer.m in Sources */, 92D56C341BF202E700A54750 /* FirmataDevice.swift in Sources */, 2DA8790E212B444E00FE0585 /* SearchStoreDataSource.swift in Sources */, - AA74EFFF1BC05B5F00D1E954 /* GlideToBrick.m in Sources */, AA74F0081BC05B5F00D1E954 /* TurnLeftBrick.m in Sources */, AA74F0C71BC05FCE00D1E954 /* SpeakBrickCell.m in Sources */, AA74F0091BC05B5F00D1E954 /* TurnRightBrick.m in Sources */, @@ -13708,7 +13713,6 @@ D30359E624445ABE006688F5 /* GoToBrick+CBXMLHandler.m in Sources */, 92C6318B1BC502CB00486958 /* SearchDevicesTableViewController.swift in Sources */, BA3337372195BC6D00DA3505 /* RepeatUntilBrick+CBXMLHandler.m in Sources */, - AA74F0031BC05B5F00D1E954 /* PlaceAtBrick.m in Sources */, AA74F0001BC05B5F00D1E954 /* GoNStepsBackBrick.m in Sources */, 92FF31071A24DCAA00093DA7 /* SpriteObject.m in Sources */, 4C37A0AA212E7E540074F417 /* FormulaEditorItem.swift in Sources */, diff --git a/src/Catty/DataModel/Bricks/Brick.h b/src/Catty/DataModel/Bricks/Brick.h index 92dbefeae9..4c8d58950f 100644 --- a/src/Catty/DataModel/Bricks/Brick.h +++ b/src/Catty/DataModel/Bricks/Brick.h @@ -44,6 +44,8 @@ - (BOOL)isFormulaBrick; +- (BOOL)isVisualPlacementBrick; + - (BOOL)isIfLogicBrick; - (BOOL)isLoopBrick; diff --git a/src/Catty/DataModel/Bricks/Brick.m b/src/Catty/DataModel/Bricks/Brick.m index 5bdd47b8bd..3b613e9b4a 100644 --- a/src/Catty/DataModel/Bricks/Brick.m +++ b/src/Catty/DataModel/Bricks/Brick.m @@ -27,6 +27,7 @@ #import "BroadcastScript.h" #import "CBMutableCopyContext.h" #import "Util.h" +#import "Pocket_Code-Swift.h" @implementation Brick @@ -52,6 +53,11 @@ - (BOOL)isFormulaBrick return ([self conformsToProtocol:@protocol(BrickFormulaProtocol)]); } +- (BOOL)isVisualPlacementBrick +{ + return ([self conformsToProtocol:@protocol(BrickVisualPlacementProtocol)]); +} + - (BOOL)isIfLogicBrick { return NO; diff --git a/src/Catty/DataModel/Bricks/Motion/GlideToBrick+CBXMLHandler.swift b/src/Catty/DataModel/Bricks/Motion/GlideToBrick+CBXMLHandler.swift new file mode 100644 index 0000000000..7e7f52ea82 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Motion/GlideToBrick+CBXMLHandler.swift @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2010-2022 The Catrobat Team + * (http://developer.catrobat.org/credits) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * (http://developer.catrobat.org/license_additional_term) + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +extension GlideToBrick: CBXMLNodeProtocol { + static func parse(from xmlElement: GDataXMLElement!, with context: CBXMLParserContext!) -> Self! { + CBXMLParserHelper.validate(xmlElement, forNumberOfChildNodes: 1, andFormulaListWithTotalNumberOfFormulas: 3) + + let glideToBrick = self.init() + if let formulaDuration = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "DURATION_IN_SECONDS", with: context) { + glideToBrick.durationInSeconds = formulaDuration + } + if let formulaXDestination = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "X_DESTINATION", with: context) { + glideToBrick.xPosition = formulaXDestination + } + if let formulaYDestination = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "Y_DESTINATION", with: context) { + glideToBrick.yPosition = formulaYDestination + } + + return glideToBrick + } + + func xmlElement(with context: CBXMLSerializerContext!) -> GDataXMLElement! { + let brick = super.xmlElement(for: "GlideToBrick", with: context) + let formulaList = GDataXMLElement(name: "formulaList", context: context) + + var formula = self.durationInSeconds.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement.attribute(withName: "category", escapedStringValue: "DURATION_IN_SECONDS") as? GDataXMLNode) + formulaList?.addChild(formula, context: context) + + formula = self.yPosition.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement.attribute(withName: "category", escapedStringValue: "Y_DESTINATION") as? GDataXMLNode) + formulaList?.addChild(formula, context: context) + + formula = self.xPosition.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement.attribute(withName: "category", escapedStringValue: "X_DESTINATION") as? GDataXMLNode) + formulaList?.addChild(formula, context: context) + + brick?.addChild(formulaList, context: context) + return brick + } +} diff --git a/src/Catty/DataModel/Bricks/Motion/GlideToBrick.h b/src/Catty/DataModel/Bricks/Motion/GlideToBrick.h deleted file mode 100644 index 1c010eeeaf..0000000000 --- a/src/Catty/DataModel/Bricks/Motion/GlideToBrick.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (C) 2010-2022 The Catrobat Team - * (http://developer.catrobat.org/credits) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * (http://developer.catrobat.org/license_additional_term) - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ - -#import "Brick.h" -#import "BrickFormulaProtocol.h" - -@class Formula; - -@interface GlideToBrick : Brick - -@property (nonatomic, strong) Formula *durationInSeconds; -@property (nonatomic, strong) Formula *xDestination; -@property (nonatomic, strong) Formula *yDestination; - - -@end diff --git a/src/Catty/DataModel/Bricks/Motion/GlideToBrick.m b/src/Catty/DataModel/Bricks/Motion/GlideToBrick.m deleted file mode 100644 index 164e0d7dae..0000000000 --- a/src/Catty/DataModel/Bricks/Motion/GlideToBrick.m +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (C) 2010-2022 The Catrobat Team - * (http://developer.catrobat.org/credits) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * (http://developer.catrobat.org/license_additional_term) - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ - -#import "GlideToBrick.h" -#import "Script.h" -#import "CBMutableCopyContext.h" -#import "Pocket_Code-Swift.h" - -@interface GlideToBrick() -@end - -@implementation GlideToBrick - -@synthesize durationInSeconds = _durationInSeconds; -@synthesize xDestination = _xDestination; -@synthesize yDestination = _yDestination; - -- (kBrickCategoryType)category -{ - return kMotionBrick; -} - -- (Formula*)formulaForLineNumber:(NSInteger)lineNumber andParameterNumber:(NSInteger)paramNumber -{ - if(lineNumber == 0 && paramNumber == 0) - return self.durationInSeconds; - else if(lineNumber == 1 && paramNumber == 0) - return self.xDestination; - else if(lineNumber == 1 && paramNumber == 1) - return self.yDestination; - - return nil; -} - -- (void)setFormula:(Formula*)formula forLineNumber:(NSInteger)lineNumber andParameterNumber:(NSInteger)paramNumber -{ - if(lineNumber == 0 && paramNumber == 0) - self.durationInSeconds = formula; - else if(lineNumber == 1 && paramNumber == 0) - self.xDestination = formula; - else if(lineNumber == 1 && paramNumber == 1) - self.yDestination = formula; -} - -- (NSArray*)getFormulas -{ - return @[self.durationInSeconds,self.xDestination,self.yDestination]; -} - -- (BOOL)allowsStringFormula -{ - return NO; -} - -- (void)setDefaultValuesForObject:(SpriteObject*)spriteObject -{ - self.durationInSeconds = [[Formula alloc] initWithInteger:1]; - self.xDestination = [[Formula alloc] initWithInteger:100]; - self.yDestination = [[Formula alloc] initWithInteger:200]; -} - - -#pragma mark - Description -- (NSString*)description -{ - return [NSString stringWithFormat:@"GlideToBrick"]; -} - -- (BOOL)isEqualToBrick:(Brick*)brick -{ - if(![self.durationInSeconds isEqualToFormula:((GlideToBrick*)brick).durationInSeconds]) - return NO; - if(![self.xDestination isEqualToFormula:((GlideToBrick*)brick).xDestination]) - return NO; - if(![self.yDestination isEqualToFormula:((GlideToBrick*)brick).yDestination]) - return NO; - return YES; -} - -#pragma mark - Resources -- (NSInteger)getRequiredResources -{ - return [self.durationInSeconds getRequiredResources]|[self.xDestination getRequiredResources]|[self.yDestination getRequiredResources]; -} -@end diff --git a/src/Catty/DataModel/Bricks/Motion/GlideToBrick.swift b/src/Catty/DataModel/Bricks/Motion/GlideToBrick.swift new file mode 100644 index 0000000000..98b1a76ea4 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Motion/GlideToBrick.swift @@ -0,0 +1,102 @@ +/** + * Copyright (C) 2010-2022 The Catrobat Team + * (http://developer.catrobat.org/credits) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * (http://developer.catrobat.org/license_additional_term) + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +@objc(GlideToBrick) +@objcMembers class GlideToBrick: Brick, BrickVisualPlacementProtocol { + var durationInSeconds: Formula + var xPosition: Formula + var yPosition: Formula + + override required init() { + self.durationInSeconds = Formula(integer: 1) + self.xPosition = Formula(integer: 100) + self.yPosition = Formula(integer: 200) + super.init() + } + + func category() -> kBrickCategoryType { + kBrickCategoryType.motionBrick + } + + func formula(forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) -> Formula? { + if lineNumber == 0 && paramNumber == 0 { + return self.durationInSeconds + } else if lineNumber == 1 && paramNumber == 0 { + return self.xPosition + } else if lineNumber == 1 && paramNumber == 1 { + return self.yPosition + } + + return nil + } + + func setFormula(_ formula: Formula, forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) { + if lineNumber == 0 && paramNumber == 0 { + self.durationInSeconds = formula + } else if lineNumber == 1 && paramNumber == 0 { + self.xPosition = formula + } else if lineNumber == 1 && paramNumber == 1 { + self.yPosition = formula + } + } + + func getFormulas() -> [Formula] { + [self.durationInSeconds, self.xPosition, self.yPosition] + } + + func allowsStringFormula() -> Bool { + false + } + + override func setDefaultValuesFor(_ spriteObject: SpriteObject!) { + self.durationInSeconds = Formula(integer: 1) + self.xPosition = Formula(integer: 100) + self.yPosition = Formula(integer: 200) + } + + override func description() -> String! { + "GlideToBrick" + } + + override func isEqual(to brick: Brick!) -> Bool { + if brick is GlideToBrick { + let glideToBrick = brick as! GlideToBrick + return self.durationInSeconds.isEqual(to: glideToBrick.durationInSeconds) && + self.xPosition.isEqual(to: glideToBrick.xPosition) && + self.yPosition.isEqual(to: glideToBrick.yPosition) + } + return false + } + + func isVisualPlacementFormula(_ formula: Formula) -> Bool { + (formula.isEqual(to: xPosition) || formula.isEqual(to: yPosition)) && + doVisualPlacementBrickCellsContainOnlyValues() + } + + func doVisualPlacementBrickCellsContainOnlyValues() -> Bool { + xPosition.formulaTree.isSingleNumberFormula() && yPosition.formulaTree.isSingleNumberFormula() + } + + override func getRequiredResources() -> Int { + durationInSeconds.getRequiredResources() | xPosition.getRequiredResources() | yPosition.getRequiredResources() + } +} diff --git a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick+CBXMLHandler.swift b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick+CBXMLHandler.swift new file mode 100644 index 0000000000..f2631b8140 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick+CBXMLHandler.swift @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2010-2022 The Catrobat Team + * (http://developer.catrobat.org/credits) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * (http://developer.catrobat.org/license_additional_term) + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +extension PlaceAtBrick: CBXMLNodeProtocol { + static func parse(from xmlElement: GDataXMLElement!, with context: CBXMLParserContext!) -> Self! { + + CBXMLParserHelper.validate(xmlElement, forNumberOfChildNodes: 1, andFormulaListWithTotalNumberOfFormulas: 2) + + let placeAtBrick = self.init() + if let formulaXPosition = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "X_POSITION", with: context) { + placeAtBrick.xPosition = formulaXPosition + } + if let formulaYPostion = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "Y_POSITION", with: context) { + placeAtBrick.yPosition = formulaYPostion + } + return placeAtBrick + } + + func xmlElement(with context: CBXMLSerializerContext) -> GDataXMLElement? { + let brick = super.xmlElement(for: "PlaceAtBrick", with: context) + let formulaList = GDataXMLElement(name: "formulaList", context: context) + + var formula = self.yPosition.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement.attribute(withName: "category", escapedStringValue: "Y_POSITION") as? GDataXMLNode) + formulaList?.addChild(formula, context: context) + + formula = self.xPosition.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement.attribute(withName: "category", escapedStringValue: "X_POSITION") as? GDataXMLNode) + formulaList?.addChild(formula, context: context) + + brick?.addChild(formulaList, context: context) + return brick + } + +} diff --git a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.h b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.h deleted file mode 100644 index 42e6b0edae..0000000000 --- a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (C) 2010-2022 The Catrobat Team - * (http://developer.catrobat.org/credits) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * (http://developer.catrobat.org/license_additional_term) - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ - -#import "Brick.h" -#import "BrickFormulaProtocol.h" - -@class Formula; - -@interface PlaceAtBrick : Brick - -@property (nonatomic, strong) Formula *xPosition; -@property (nonatomic, strong) Formula *yPosition; - -@end diff --git a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.m b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.m deleted file mode 100644 index acb611025a..0000000000 --- a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.m +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright (C) 2010-2022 The Catrobat Team - * (http://developer.catrobat.org/credits) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * An additional term exception under section 7 of the GNU Affero - * General Public License, version 3, is available at - * (http://developer.catrobat.org/license_additional_term) - * - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - */ - -#import "PlaceAtBrick.h" -#import "Script.h" - -@implementation PlaceAtBrick - -- (Formula*)formulaForLineNumber:(NSInteger)lineNumber andParameterNumber:(NSInteger)paramNumber -{ - if(paramNumber == 0) - return self.xPosition; - else if(paramNumber == 1) - return self.yPosition; - - return nil; -} - -- (void)setFormula:(Formula*)formula forLineNumber:(NSInteger)lineNumber andParameterNumber:(NSInteger)paramNumber -{ - if(paramNumber == 0) - self.xPosition = formula; - else if(paramNumber == 1) - self.yPosition = formula; -} - -- (NSArray*)getFormulas -{ - return @[self.xPosition,self.yPosition]; -} - -- (BOOL)allowsStringFormula -{ - return NO; -} - -- (void)setDefaultValuesForObject:(SpriteObject*)spriteObject -{ - self.xPosition = [[Formula alloc] initWithInteger:100]; - self.yPosition = [[Formula alloc] initWithInteger:200]; -} - -- (kBrickCategoryType)category -{ - return kMotionBrick; -} - -#pragma mark - Description -- (NSString*)description -{ - return [NSString stringWithFormat:@"PlaceAtBrick"]; -} - -#pragma mark - Resources -- (NSInteger)getRequiredResources -{ - return [self.xPosition getRequiredResources]|[self.yPosition getRequiredResources]; -} - -@end diff --git a/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.swift b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.swift new file mode 100644 index 0000000000..50bf7d853c --- /dev/null +++ b/src/Catty/DataModel/Bricks/Motion/PlaceAtBrick.swift @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2010-2022 The Catrobat Team + * (http://developer.catrobat.org/credits) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * (http://developer.catrobat.org/license_additional_term) + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +@objc(PlaceAtBrick) +@objcMembers class PlaceAtBrick: Brick, BrickVisualPlacementProtocol { + var xPosition: Formula + var yPosition: Formula + + override required init () { + self.xPosition = Formula(integer: 100) + self.yPosition = Formula(integer: 200) + super.init() + } + + func formula(forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) -> Formula? { + if paramNumber == 0 { + return self.xPosition + } else if paramNumber == 1 { + return self.yPosition + } + return nil + } + + func setFormula(_ formula: Formula, forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) { + if paramNumber == 0 { + self.xPosition = formula + } else if paramNumber == 1 { + self.yPosition = formula + } + } + + func getFormulas() -> [Formula]! { + [self.xPosition, self.yPosition] + } + + func allowsStringFormula() -> Bool { + false + } + + override func setDefaultValuesFor(_ spriteObject: SpriteObject) { + self.xPosition = Formula(integer: 100) + self.yPosition = Formula(integer: 200) + } + + func category() -> kBrickCategoryType { + kBrickCategoryType.motionBrick + } + + override class func description() -> String { + "PlaceAtBrick" + } + + func isVisualPlacementFormula(_ formula: Formula) -> Bool { + doVisualPlacementBrickCellsContainOnlyValues() + } + + func doVisualPlacementBrickCellsContainOnlyValues() -> Bool { + xPosition.formulaTree.isSingleNumberFormula() && yPosition.formulaTree.isSingleNumberFormula() + } + + override func getRequiredResources() -> Int { + xPosition.getRequiredResources() | yPosition.getRequiredResources() + } +} diff --git a/src/Catty/DataModel/Formula/FormulaElement.h b/src/Catty/DataModel/Formula/FormulaElement.h index ffbd94623e..a031d96be0 100644 --- a/src/Catty/DataModel/Formula/FormulaElement.h +++ b/src/Catty/DataModel/Formula/FormulaElement.h @@ -128,6 +128,8 @@ additionalChildren:(NSArray*)additionalChildren - (BOOL)isSingleNumberFormula; +- (double)getSingleNumberFormulaValue; + - (NSInteger)getRequiredResources; @end diff --git a/src/Catty/DataModel/Formula/FormulaElement.m b/src/Catty/DataModel/Formula/FormulaElement.m index 9645b21164..42670645b0 100644 --- a/src/Catty/DataModel/Formula/FormulaElement.m +++ b/src/Catty/DataModel/Formula/FormulaElement.m @@ -321,6 +321,18 @@ - (BOOL)isSingleNumberFormula return false; } +- (double)getSingleNumberFormulaValue +{ + if (self.type == OPERATOR) { + if (self.value == MinusOperator.tag && self.leftChild == nil) { + return -[self.rightChild getSingleNumberFormulaValue]; + } + } else if(self.type == NUMBER) { + return [self.value doubleValue]; + } + return 0.0; +} + #pragma mark - Copy - (id)mutableCopyWithContext:(CBMutableCopyContext*)context { diff --git a/src/Catty/DataModel/SpriteObject/SpriteObject.m b/src/Catty/DataModel/SpriteObject/SpriteObject.m index 4cb0f01dd6..3efcdb63de 100644 --- a/src/Catty/DataModel/SpriteObject/SpriteObject.m +++ b/src/Catty/DataModel/SpriteObject/SpriteObject.m @@ -101,11 +101,15 @@ - (NSString*)projectPath - (NSString*)previewImagePath { - Look* look = [self.lookList objectAtIndex:0]; - if (! look) - return nil; - - return [look pathForScene:self.scene]; + if ([self numberOfLooks] > 0) + { + Look* look = [self.lookList objectAtIndex:0]; + if (! look) + return nil; + + return [look pathForScene:self.scene]; + } + return nil; } - (BOOL)isBackground diff --git a/src/Catty/Defines/LanguageTranslationDefines.h b/src/Catty/Defines/LanguageTranslationDefines.h index 54e44ce2a7..83a05f3091 100644 --- a/src/Catty/Defines/LanguageTranslationDefines.h +++ b/src/Catty/Defines/LanguageTranslationDefines.h @@ -167,6 +167,7 @@ #define kLocalizedDisableCondition NSLocalizedString(@"Disable condition", nil) #define kLocalizedEnableCondition NSLocalizedString(@"Enable condition", nil) #define kLocalizedEditFormula NSLocalizedString(@"Edit formula", nil) +#define kLocalizedPlaceVisually NSLocalizedString(@"Place visually", nil) #define kLocalizedMoveBrick NSLocalizedString(@"Move brick", nil) #define kLocalizedDeleteSounds NSLocalizedString(@"Delete sounds", nil) #define kLocalizedMoveSounds NSLocalizedString(@"Move sounds",nil) diff --git a/src/Catty/Defines/LanguageTranslationDefinesSwift.swift b/src/Catty/Defines/LanguageTranslationDefinesSwift.swift index 049baf9283..6275112f4b 100644 --- a/src/Catty/Defines/LanguageTranslationDefinesSwift.swift +++ b/src/Catty/Defines/LanguageTranslationDefinesSwift.swift @@ -167,6 +167,7 @@ let kLocalizedEnableLoop = NSLocalizedString("Enable loop", comment: "") let kLocalizedDisableCondition = NSLocalizedString("Disable condition", comment: "") let kLocalizedEnableCondition = NSLocalizedString("Enable condition", comment: "") let kLocalizedEditFormula = NSLocalizedString("Edit formula", comment: "") +let kLocalizedPlaceVisually = NSLocalizedString("Place visually", comment: "") let kLocalizedMoveBrick = NSLocalizedString("Move brick", comment: "") let kLocalizedDeleteSounds = NSLocalizedString("Delete sounds", comment: "") let kLocalizedMoveSounds = NSLocalizedString("Move sounds", comment: "") diff --git a/src/Catty/Defines/ProjectDefines.h b/src/Catty/Defines/ProjectDefines.h index 97fdca769a..7908b15925 100644 --- a/src/Catty/Defines/ProjectDefines.h +++ b/src/Catty/Defines/ProjectDefines.h @@ -119,6 +119,7 @@ typedef NS_ENUM(NSInteger, ResourceType) { #define kScreenshotFilename @"screenshot.png" #define kScreenshotManualFilename @"manual_screenshot.png" #define kScreenshotAutoFilename @"automatic_screenshot.png" +#define kScreenshotAutoFullScreenFilename @"automatic_full_screen_screenshot.png" #define kUserDefaultsBrickSelectionStatisticsMap @"BrickSelectionStatisticsMap" diff --git a/src/Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.h b/src/Catty/Extension&Delegate&Protocol/Protocols/Bricks/BrickData/BrickVisualPlacementProtocol.swift similarity index 76% rename from src/Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.h rename to src/Catty/Extension&Delegate&Protocol/Protocols/Bricks/BrickData/BrickVisualPlacementProtocol.swift index d6e2bd226e..2da6ccaa5a 100644 --- a/src/Catty/XML/XMLHandler/Bricks/Motion/GlideToBrick+CBXMLHandler.h +++ b/src/Catty/Extension&Delegate&Protocol/Protocols/Bricks/BrickData/BrickVisualPlacementProtocol.swift @@ -20,11 +20,12 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -#import "GlideToBrick.h" -#import "CBXMLNodeProtocol.h" +import Foundation -@class CBXMLContext; +@objc protocol BrickVisualPlacementProtocol: BrickFormulaProtocol { + var xPosition: Formula { get set } + var yPosition: Formula { get set } -@interface GlideToBrick (CBXMLHandler) - -@end + func isVisualPlacementFormula(_ formula: Formula) -> Bool + func doVisualPlacementBrickCellsContainOnlyValues() -> Bool +} diff --git a/src/Catty/FormulaEditor/ViewController/FormulaEditorViewController.m b/src/Catty/FormulaEditor/ViewController/FormulaEditorViewController.m index 4e8a5f190f..ec8aa75ffe 100644 --- a/src/Catty/FormulaEditor/ViewController/FormulaEditorViewController.m +++ b/src/Catty/FormulaEditor/ViewController/FormulaEditorViewController.m @@ -43,7 +43,7 @@ kButtonIndexCancel = 4 }; -@interface FormulaEditorViewController () +@interface FormulaEditorViewController () @property (weak, nonatomic) Formula *formula; @@ -139,6 +139,58 @@ - (void)openFormulaEditor:(BrickCellFormulaData*)formulaData withEvent:(UIEvent* [self.brickCell setNeedsDisplay]; } +- (void)openFormulaAndVisualPlacementActionSheet:(BrickCellFormulaData *)formulaData withEvent:(UIEvent *)event +{ + [self openFormulaEditor:formulaData withEvent:event]; + + Brick* brick = formulaData.brickCell.scriptOrBrick; + Brick *visualPlacementBrick; + + if ([brick isVisualPlacementBrick]) { + visualPlacementBrick = (Brick *) brick; + if (!([visualPlacementBrick isVisualPlacementFormula:formulaData.formula])) { + return; + } + + [[[[[[AlertControllerBuilder actionSheetWithTitle:nil] + addCancelActionWithTitle:kLocalizedCancel handler:nil] + addDefaultActionWithTitle:kLocalizedEditFormula handler:^{ + [self openFormulaEditor:formulaData withEvent:event]; + }] + addDefaultActionWithTitle:kLocalizedPlaceVisually handler:^{ + [self openVisualPlacementEditor:visualPlacementBrick]; + }] + build] + showWithController:[Util topmostViewController]]; + } +} + +-(void)openVisualPlacementEditor:(Brick *)visualPlacementBrick +{ + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"iPhone" bundle: nil]; + VisualPlacementViewController *visualPlacementViewController = [storyboard instantiateViewControllerWithIdentifier:@"VisualPlacementViewController"]; + [visualPlacementViewController initWithBrick:visualPlacementBrick andObject:self.object]; + visualPlacementViewController.delegate = self; + + visualPlacementViewController.modalPresentationStyle = UIModalPresentationFullScreen; + [self presentViewController:visualPlacementViewController animated:YES completion:NULL]; +} + +- (void)doneVisualPlacement +{ + Brick *brick = (Brick *) self.brickCell.scriptOrBrick; + + self.internFormula = [[brick formulaForLineNumber:self.brickCellData.lineNumber andParameterNumber:self.brickCellData.parameterNumber] getInternFormula]; + + [self changeBrickCellFormulaData:self.brickCellData]; + [self update]; + [self showFormulaEditorTextView]; +} + +- (void)cancelVisualPlacement { + [self showFormulaEditorTextView]; +} + - (BOOL)changeBrickCellFormulaData:(BrickCellFormulaData *)brickCellData { InternFormulaParser *internFormulaParser = [[InternFormulaParser alloc] initWithTokens:[self.internFormula getInternTokenList] andFormulaManager:self.formulaManager]; diff --git a/src/Catty/FormulaEditor/ViewController/VisualPlacementViewController.swift b/src/Catty/FormulaEditor/ViewController/VisualPlacementViewController.swift new file mode 100644 index 0000000000..afa84f7309 --- /dev/null +++ b/src/Catty/FormulaEditor/ViewController/VisualPlacementViewController.swift @@ -0,0 +1,192 @@ +/** + * Copyright (C) 2010-2022 The Catrobat Team + * (http://developer.catrobat.org/credits) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * (http://developer.catrobat.org/license_additional_term) + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +import Foundation +import UIKit + +@objc protocol VisualPlacementViewControllerDelegate { + func doneVisualPlacement() + func cancelVisualPlacement() +} + +@objcMembers class VisualPlacementViewController: UIViewController { + @IBOutlet private weak var objectImageView: UIImageView! + @IBOutlet private weak var oldPositionObjectImageView: UIImageView! + @IBOutlet private weak var backgroundImageView: UIImageView! + @IBOutlet private weak var cancelButton: UIButton! + @IBOutlet private weak var doneButton: UIButton! + @IBOutlet private weak var titleLabel: UILabel! + + weak var delegate: VisualPlacementViewControllerDelegate? + static var lookScalingDictionary = [String: Double] () + var objectImage: UIImage? + var backgroundImage: UIImage? + var isDragging = false + var imageWidth = 100.0 + var imageHeight = 100.0 + var scaleFactor = 1.0 + var oldPositionInView = CGPoint(x: 0.0, y: 0.0) + var centerPositionInView = CGPoint(x: 0.0, y: 0.0) + var centerPositionInStage = CGPoint(x: 0.0, y: 0.0) + var stageSize = CGSize(width: 0.0, height: 0.0) + var brick: BrickVisualPlacementProtocol! + var object: SpriteObject! + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + guard let navigationController = navigationController else { return } + navigationController.setNavigationBarHidden(true, animated: animated) + navigationController.setToolbarHidden(true, animated: animated) + + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.edgesForExtendedLayout = [UIRectEdge.top, UIRectEdge.left, UIRectEdge.right, UIRectEdge.bottom] + self.extendedLayoutIncludesOpaqueBars = true + + backgroundImageView.alpha = 0.5 + oldPositionObjectImageView.alpha = 0.5 + + if let backgroundImage = backgroundImage { + backgroundImageView.image = backgroundImage + } + self.setupObjectImageView(self.objectImageView) + self.setupObjectImageView(self.oldPositionObjectImageView) + self.setupNavigationBar() + + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + guard let navigationController = navigationController else { return } + navigationController.setNavigationBarHidden(false, animated: animated) + navigationController.setToolbarHidden(false, animated: animated) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + .lightContent + } + + func initWith(brick: BrickVisualPlacementProtocol, andObject object: SpriteObject) { + + let screenshotFileName = object.projectPath() + kScreenshotAutoFullScreenFilename + self.backgroundImage = UIImage.init(contentsOfFile: screenshotFileName) + if self.backgroundImage == nil, + let backgroundObject = object.scene.objects().first, + let backgroundImagePath = backgroundObject.previewImagePath() { + self.backgroundImage = UIImage.init(contentsOfFile: backgroundImagePath) + } + + self.brick = brick + self.centerPositionInStage.x = brick.xPosition.formulaTree.getSingleNumberFormulaValue() + self.centerPositionInStage.y = brick.yPosition.formulaTree.getSingleNumberFormulaValue() + self.object = object + + stageSize = CGSize(width: object.scene.project?.header.screenWidth.doubleValue ?? 0, height: object.scene.project?.header.screenHeight.doubleValue ?? 0) + centerPositionInView = CBSceneHelper.convertPointToTouchCoordinate(point: centerPositionInStage, stageSize: stageSize) + + let screenSize = Util.screenSize(false) + scaleFactor = (screenSize.width / stageSize.width) + + if let objectImagePath = object.previewImagePath() { + self.objectImage = UIImage.init(contentsOfFile: objectImagePath) + } + } + + func setupNavigationBar() { + cancelButton.setTitle(kLocalizedCancel, for: .normal) + doneButton.setTitle(kLocalizedDone, for: .normal) + titleLabel.text = kLocalizedPlaceVisually + } + + func setupObjectImageView(_ imageView: UIImageView) { + if let objectImage = objectImage { + var catrobatScaling = 1.0 + if let scaling = VisualPlacementViewController.lookScalingDictionary[object.name] { + catrobatScaling = scaling + } + catrobatScaling = catrobatScaling == 0 ? 1.0 : catrobatScaling + imageWidth = objectImage.size.width * self.scaleFactor * catrobatScaling + imageHeight = objectImage.size.height * self.scaleFactor * catrobatScaling + imageView.image = objectImage + } + imageView.frame.size.width = imageWidth + imageView.frame.size.height = imageHeight + imageView.frame.origin.x = centerPositionInView.x - imageWidth / 2 + imageView.frame.origin.y = centerPositionInView.y - imageHeight / 2 + } + + @IBAction private func cancelTapped(_ sender: Any) { + dismiss(animated: true) { + if let delegate = self.delegate { + delegate.cancelVisualPlacement() + } + } + } + + @IBAction private func doneTapped(_ sender: Any) { + let newPositionInStage = CBSceneHelper.convertTouchCoordinateToPoint(coordinate: self.centerPositionInView, stageSize: self.stageSize) + brick.xPosition = Formula(integer: Int32(newPositionInStage.x)) + brick.yPosition = Formula(integer: Int32(newPositionInStage.y)) + guard let project = object.scene.project else { return } + project.saveToDisk(withNotification: true) { + DispatchQueue.main.async { + self.dismiss(animated: true) { + if let delegate = self.delegate { + delegate.doneVisualPlacement() + } + + } + } + } + } +} + +extension VisualPlacementViewController { + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + guard let touch = touches.first else { + return + } + let touchLocation = touch.location(in: objectImageView) + if objectImageView!.bounds.contains(touchLocation) { + isDragging = true + oldPositionInView = touchLocation + } + } + override func touchesMoved(_ touches: Set, with event: UIEvent?) { + guard isDragging, let touch = touches.first else { + return + } + + let touchLocation = touch.location(in: view) + objectImageView!.frame.origin.x = touchLocation.x - oldPositionInView.x + objectImageView!.frame.origin.y = touchLocation.y - oldPositionInView.y + centerPositionInView.x = objectImageView!.frame.origin.x + imageWidth / 2 + centerPositionInView.y = objectImageView!.frame.origin.y + imageHeight / 2 + } + override func touchesEnded(_ touches: Set, with event: UIEvent?) { + isDragging = false + } +} diff --git a/src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon.png b/src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon.png deleted file mode 100644 index ed147bf19c2cd467e0f91676404e6df8fb285516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5778 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 Qng9R*07*qoM6N<$f;+)BxBvhE diff --git a/src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon@2x.png b/src/Catty/Images.xcassets/Icons/menu_icon_(deprecated).imageset/menu_icon@2x.png deleted file mode 100644 index 84c0e602a1a6feb6ee9e9a1eda029d959389ce56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4360 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 literal 0 HcmV?d00001 diff --git a/src/Catty/PlayerEngine/Helper/CBSceneHelper.swift b/src/Catty/PlayerEngine/Helper/CBSceneHelper.swift index 82d65a6909..236cbb1ec3 100644 --- a/src/Catty/PlayerEngine/Helper/CBSceneHelper.swift +++ b/src/Catty/PlayerEngine/Helper/CBSceneHelper.swift @@ -25,10 +25,17 @@ // MARK: - Operations (Helpers) @objc class func convertTouchCoordinateToPoint(coordinate: CGPoint, stageSize: CGSize) -> CGPoint { let screenSize = Util.screenSize(false) - var x = (coordinate.x - screenSize.width / 2.0) - x *= (stageSize.width / screenSize.width) - var y = (screenSize.height / 2.0 - coordinate.y) - y *= (stageSize.height / screenSize.height) + let scaling = stageSize.width / screenSize.width + let x = (coordinate.x - screenSize.width / 2.0) * scaling + let y = (screenSize.height / 2.0 - coordinate.y) * scaling + return CGPoint(x: x, y: y) + } + + @objc class func convertPointToTouchCoordinate(point: CGPoint, stageSize: CGSize) -> CGPoint { + let screenSize = Util.screenSize(false) + let scaling = screenSize.width / stageSize.width + let x = point.x * scaling + screenSize.width / 2.0 + let y = screenSize.height / 2.0 - point.y * scaling return CGPoint(x: x, y: y) } } diff --git a/src/Catty/PlayerEngine/Instructions/Look/SetSizeToBrick+Instruction.swift b/src/Catty/PlayerEngine/Instructions/Look/SetSizeToBrick+Instruction.swift index 8a218acdb7..7a54555717 100644 --- a/src/Catty/PlayerEngine/Instructions/Look/SetSizeToBrick+Instruction.swift +++ b/src/Catty/PlayerEngine/Instructions/Look/SetSizeToBrick+Instruction.swift @@ -34,6 +34,11 @@ return { let sizeInPercent = formulaInterpreter.interpretDouble(self.size, for: object) spriteNode.catrobatSize = sizeInPercent + if let currentLook = spriteNode.currentLook, let firstLook = object.lookList.firstObject { + if currentLook.isEqual(firstLook) { + VisualPlacementViewController.lookScalingDictionary[object.name] = sizeInPercent / 100 + } + } } } } diff --git a/src/Catty/PlayerEngine/Instructions/Motion/GlideToBrick+Instruction.swift b/src/Catty/PlayerEngine/Instructions/Motion/GlideToBrick+Instruction.swift index bfa674c3b7..8b52871283 100644 --- a/src/Catty/PlayerEngine/Instructions/Motion/GlideToBrick+Instruction.swift +++ b/src/Catty/PlayerEngine/Instructions/Motion/GlideToBrick+Instruction.swift @@ -20,12 +20,10 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -@objc extension GlideToBrick: CBInstructionProtocol { +extension GlideToBrick: CBInstructionProtocol { @nonobjc func instruction() -> CBInstruction { - guard let durationFormula = self.durationInSeconds else { fatalError("This should never happen!") } - - return .longDurationAction(duration: CBDuration.varTime(formula: durationFormula), closure: { duration, context -> SKAction in + CBInstruction.longDurationAction(duration: CBDuration.varTime(formula: self.durationInSeconds), closure: { duration, context -> SKAction in self.action(duration, context.formulaInterpreter) }) } @@ -35,8 +33,8 @@ let spriteNode = object.spriteNode else { fatalError("This should never happen!") } - let xDestination = formulaInterpreter.interpretFloat(self.xDestination, for: object) - let yDestination = formulaInterpreter.interpretFloat(self.yDestination, for: object) + let xDestination = formulaInterpreter.interpretFloat(self.xPosition, for: object) + let yDestination = formulaInterpreter.interpretFloat(self.yPosition, for: object) let duration = formulaInterpreter.interpretDouble(self.durationInSeconds, for: object) guard let scene = spriteNode.scene else { diff --git a/src/Catty/PlayerEngine/Instructions/Motion/PlaceAtBrick+Instruction.swift b/src/Catty/PlayerEngine/Instructions/Motion/PlaceAtBrick+Instruction.swift index ef8f01d50e..4bf8d298e3 100644 --- a/src/Catty/PlayerEngine/Instructions/Motion/PlaceAtBrick+Instruction.swift +++ b/src/Catty/PlayerEngine/Instructions/Motion/PlaceAtBrick+Instruction.swift @@ -20,7 +20,7 @@ * along with this program. If not, see http://www.gnu.org/licenses/. */ -@objc extension PlaceAtBrick: CBInstructionProtocol { +extension PlaceAtBrick: CBInstructionProtocol { @nonobjc func instruction() -> CBInstruction { .action { context in SKAction.run(self.actionBlock(context.formulaInterpreter)) } diff --git a/src/Catty/Resources/Localization/en.lproj/Localizable.strings b/src/Catty/Resources/Localization/en.lproj/Localizable.strings index 1bc22f70f7..3ce987e621 100644 --- a/src/Catty/Resources/Localization/en.lproj/Localizable.strings +++ b/src/Catty/Resources/Localization/en.lproj/Localizable.strings @@ -1528,6 +1528,9 @@ /* No comment provided by engineer. */ "Place at " = "Place at "; +/* No comment provided by engineer. */ +"Place visually" = "Place visually"; + /* No comment provided by engineer. */ "Play" = "Play"; diff --git a/src/Catty/Storyboard+XIB/iPhone.storyboard b/src/Catty/Storyboard+XIB/iPhone.storyboard index 83ae7497f3..eb32abb515 100644 --- a/src/Catty/Storyboard+XIB/iPhone.storyboard +++ b/src/Catty/Storyboard+XIB/iPhone.storyboard @@ -1,9 +1,9 @@ - + - + @@ -21,11 +21,11 @@ - +