diff --git a/src/Catty.xcodeproj/project.pbxproj b/src/Catty.xcodeproj/project.pbxproj index a7b487af9..9ce735310 100644 --- a/src/Catty.xcodeproj/project.pbxproj +++ b/src/Catty.xcodeproj/project.pbxproj @@ -1574,6 +1574,27 @@ 9EE674DA23253A5E0029D153 /* PlaySameSoundTwiceDifferentObjects.xml in Resources */ = {isa = PBXBuildFile; fileRef = 9EE674D923253A5D0029D153 /* PlaySameSoundTwiceDifferentObjects.xml */; }; 9EE674E32325447C0029D153 /* half_seconds_patchwork.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9EE674E12325447C0029D153 /* half_seconds_patchwork.mp3 */; }; 9EE674E42325447C0029D153 /* short_drum_long_sax.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9EE674E22325447C0029D153 /* short_drum_long_sax.mp3 */; }; + A43B471F2CB452BC00D253B7 /* plotter.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B47192CB452BB00D253B7 /* plotter.png */; }; + A43B47202CB452BC00D253B7 /* panda_b.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B471A2CB452BB00D253B7 /* panda_b.png */; }; + A43B47222CB452BC00D253B7 /* apple.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B471C2CB452BB00D253B7 /* apple.png */; }; + A43B47232CB452BC00D253B7 /* lynx_a.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B471D2CB452BB00D253B7 /* lynx_a.png */; }; + A43B47242CB452BC00D253B7 /* panda_a.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B471E2CB452BB00D253B7 /* panda_a.png */; }; + A43B47262CB45F0200D253B7 /* frame.png in Resources */ = {isa = PBXBuildFile; fileRef = A43B47252CB45F0200D253B7 /* frame.png */; }; + A4A1C38C2BA9981F006824D9 /* StartPlotBrickCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A1C38B2BA9981F006824D9 /* StartPlotBrickCell.swift */; }; + A4A1C38F2BA99942006824D9 /* StartPlotBrick+Instruction.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A1C38E2BA99942006824D9 /* StartPlotBrick+Instruction.swift */; }; + A4A1C3932BA99AB9006824D9 /* StartPlotBrick+CBXMLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A1C3922BA99AB9006824D9 /* StartPlotBrick+CBXMLHandler.swift */; }; + A4A1C3972BA99BF2006824D9 /* StartPlotBrick.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A1C3962BA99BF2006824D9 /* StartPlotBrick.swift */; }; + A4A2564B2C31788C00CA8445 /* StartPlotBrickTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A2564A2C31788C00CA8445 /* StartPlotBrickTests.swift */; }; + A4A2564D2C3178FB00CA8445 /* StopPlotBrickTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A2564C2C3178FB00CA8445 /* StopPlotBrickTests.swift */; }; + A4A8B51D2BE7DB37009266FE /* StopPlotBrick+Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B51C2BE7DB37009266FE /* StopPlotBrick+Instructions.swift */; }; + A4A8B51F2BE7DCDD009266FE /* StopPlotBrick.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B51E2BE7DCDD009266FE /* StopPlotBrick.swift */; }; + A4A8B5212BE7DE6D009266FE /* StopPlotBrickCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B5202BE7DE6D009266FE /* StopPlotBrickCell.swift */; }; + A4A8B5232BE7DEDD009266FE /* StopPlotBrick+CBXMLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B5222BE7DEDD009266FE /* StopPlotBrick+CBXMLHandler.swift */; }; + A4A8B5252BE7E579009266FE /* DashedLineShapeNode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B5242BE7E579009266FE /* DashedLineShapeNode.swift */; }; + A4A8B5272BE7EEF3009266FE /* SavePlotSVGBrick.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B5262BE7EEF3009266FE /* SavePlotSVGBrick.swift */; }; + A4A8B5292BE7EF62009266FE /* SavePlotSVGBrickCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B5282BE7EF62009266FE /* SavePlotSVGBrickCell.swift */; }; + A4A8B52B2BE7F538009266FE /* SavePlotSVGBrick+Instructions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B52A2BE7F538009266FE /* SavePlotSVGBrick+Instructions.swift */; }; + A4A8B52F2BE7FEF4009266FE /* SavePlotSVGBrick+CBXMLHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4A8B52E2BE7FEF3009266FE /* SavePlotSVGBrick+CBXMLHandler.swift */; }; AA3BD9091BBF4BFB002B34A0 /* WaitBrickTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3BD9081BBF4BFB002B34A0 /* WaitBrickTests.swift */; }; AA401FE91BBEF6F9001BC49F /* CBInstructionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA401FE81BBEF6F9001BC49F /* CBInstructionProtocol.swift */; }; AA6CC28B1BB4AB9A0007A621 /* CBScriptContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6CC28A1BB4AB9A0007A621 /* CBScriptContext.swift */; }; @@ -4090,7 +4111,29 @@ 9EE674E12325447C0029D153 /* half_seconds_patchwork.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = half_seconds_patchwork.mp3; sourceTree = ""; }; 9EE674E22325447C0029D153 /* short_drum_long_sax.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = short_drum_long_sax.mp3; sourceTree = ""; }; A032C198F02439059E373A5C /* pa */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = pa; path = pa.lproj/Localizable.strings; sourceTree = ""; }; + A43B47192CB452BB00D253B7 /* plotter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = plotter.png; path = Resources/plotter.png; sourceTree = ""; }; + A43B471A2CB452BB00D253B7 /* panda_b.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = panda_b.png; path = Resources/panda_b.png; sourceTree = ""; }; + A43B471C2CB452BB00D253B7 /* apple.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = apple.png; path = Resources/apple.png; sourceTree = ""; }; + A43B471D2CB452BB00D253B7 /* lynx_a.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = lynx_a.png; path = Resources/lynx_a.png; sourceTree = ""; }; + A43B471E2CB452BB00D253B7 /* panda_a.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = panda_a.png; path = Resources/panda_a.png; sourceTree = ""; }; + A43B47252CB45F0200D253B7 /* frame.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = frame.png; path = Resources/frame.png; sourceTree = ""; }; A46D335B661B03F52135C789 /* gu */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = gu; path = gu.lproj/Localizable.strings; sourceTree = ""; }; + A4A1C38B2BA9981F006824D9 /* StartPlotBrickCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartPlotBrickCell.swift; sourceTree = ""; }; + A4A1C38E2BA99942006824D9 /* StartPlotBrick+Instruction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StartPlotBrick+Instruction.swift"; sourceTree = ""; }; + A4A1C3922BA99AB9006824D9 /* StartPlotBrick+CBXMLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StartPlotBrick+CBXMLHandler.swift"; sourceTree = ""; }; + A4A1C3962BA99BF2006824D9 /* StartPlotBrick.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StartPlotBrick.swift; sourceTree = ""; }; + A4A2564A2C31788C00CA8445 /* StartPlotBrickTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartPlotBrickTests.swift; sourceTree = ""; }; + A4A2564C2C3178FB00CA8445 /* StopPlotBrickTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopPlotBrickTests.swift; sourceTree = ""; }; + A4A8B51C2BE7DB37009266FE /* StopPlotBrick+Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StopPlotBrick+Instructions.swift"; sourceTree = ""; }; + A4A8B51E2BE7DCDD009266FE /* StopPlotBrick.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StopPlotBrick.swift; sourceTree = ""; }; + A4A8B5202BE7DE6D009266FE /* StopPlotBrickCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StopPlotBrickCell.swift; sourceTree = ""; }; + A4A8B5222BE7DEDD009266FE /* StopPlotBrick+CBXMLHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "StopPlotBrick+CBXMLHandler.swift"; sourceTree = ""; }; + A4A8B5242BE7E579009266FE /* DashedLineShapeNode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DashedLineShapeNode.swift; path = PlayerEngine/DataModel/DashedLineShapeNode.swift; sourceTree = ""; }; + A4A8B5262BE7EEF3009266FE /* SavePlotSVGBrick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavePlotSVGBrick.swift; sourceTree = ""; }; + A4A8B5282BE7EF62009266FE /* SavePlotSVGBrickCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavePlotSVGBrickCell.swift; sourceTree = ""; }; + A4A8B52A2BE7F538009266FE /* SavePlotSVGBrick+Instructions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SavePlotSVGBrick+Instructions.swift"; sourceTree = ""; }; + A4A8B52E2BE7FEF3009266FE /* SavePlotSVGBrick+CBXMLHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SavePlotSVGBrick+CBXMLHandler.swift"; sourceTree = ""; }; + A4B27D622C206378002177A3 /* CattyDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CattyDebug.entitlements; sourceTree = ""; }; A61F854BCF220F35A21001B8 /* kab */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Localizable.strings; sourceTree = ""; }; A6B80FF78CD540E39740C5D8 /* fi */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; A988832B1BCA86FF005029E8 /* ObjectTVCTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectTVCTests.swift; sourceTree = ""; }; @@ -6860,6 +6903,7 @@ 4C724D9B1B4D3E8C00E27479 /* Bricks */ = { isa = PBXGroup; children = ( + A4A1C3912BA99A90006824D9 /* Plot */, 46B472BE2560950D007972DB /* Embroidery */, 186AA3452482E086001E38FD /* Pen */, 92B46F961BC7BC8C004980C1 /* Arduino */, @@ -7713,12 +7757,14 @@ 9E2C2D9123258F3F004B66C6 /* TurnRightBrickTests.swift */, 590200D123FC03A900F0978B /* BrickExtensionTests.swift */, 4C1D26641E84F12700BAA2D2 /* VibrationBrickTests.swift */, + A4A2564C2C3178FB00CA8445 /* StopPlotBrickTests.swift */, AA3BD9081BBF4BFB002B34A0 /* WaitBrickTests.swift */, BA584CB9219C8803002CC082 /* WaitUntilBrickTests.swift */, 3DA1BED824365A3400133C99 /* BrickMutableCopyTests.swift */, D3AF5C18243F6A4C00B04BC6 /* BrickCategoryTest.swift */, 186AA34824830491001E38FD /* PenDownBrickTests.swift */, 186E991C2488FBDB00627E36 /* PenUpBrickTests.swift */, + A4A2564A2C31788C00CA8445 /* StartPlotBrickTests.swift */, 46B94F9625791507007E5ABE /* StitchBrickTests.swift */, FE9E7F7629811AFB00C9598C /* StitchThreadColorBrickTests.swift */, 18E83F842493C978003295DA /* PenClearBrickTests.swift */, @@ -9049,6 +9095,7 @@ 92FF302B1A24DCAA00093DA7 /* Bricks */ = { isa = PBXGroup; children = ( + A4A1C3952BA99BAF006824D9 /* Plot */, 4614B1CE2560856B004174AD /* Embroidery */, 4C6F413B22F4DAAC00900B3F /* Category */, 92B46F8A1BC7B3E5004980C1 /* Arduino */, @@ -10188,9 +10235,50 @@ path = Sounds; sourceTree = ""; }; + A4A1C38A2BA99807006824D9 /* Plot */ = { + isa = PBXGroup; + children = ( + A4A1C38B2BA9981F006824D9 /* StartPlotBrickCell.swift */, + A4A8B5202BE7DE6D009266FE /* StopPlotBrickCell.swift */, + A4A8B5282BE7EF62009266FE /* SavePlotSVGBrickCell.swift */, + ); + path = Plot; + sourceTree = ""; + }; + A4A1C38D2BA99924006824D9 /* Plot */ = { + isa = PBXGroup; + children = ( + A4A1C38E2BA99942006824D9 /* StartPlotBrick+Instruction.swift */, + A4A8B51C2BE7DB37009266FE /* StopPlotBrick+Instructions.swift */, + A4A8B52A2BE7F538009266FE /* SavePlotSVGBrick+Instructions.swift */, + ); + name = Plot; + sourceTree = ""; + }; + A4A1C3912BA99A90006824D9 /* Plot */ = { + isa = PBXGroup; + children = ( + A4A8B52E2BE7FEF3009266FE /* SavePlotSVGBrick+CBXMLHandler.swift */, + A4A1C3922BA99AB9006824D9 /* StartPlotBrick+CBXMLHandler.swift */, + A4A8B5222BE7DEDD009266FE /* StopPlotBrick+CBXMLHandler.swift */, + ); + path = Plot; + sourceTree = ""; + }; + A4A1C3952BA99BAF006824D9 /* Plot */ = { + isa = PBXGroup; + children = ( + A4A8B51E2BE7DCDD009266FE /* StopPlotBrick.swift */, + A4A1C3962BA99BF2006824D9 /* StartPlotBrick.swift */, + A4A8B5262BE7EEF3009266FE /* SavePlotSVGBrick.swift */, + ); + path = Plot; + sourceTree = ""; + }; AA401FE51BBEF5F5001BC49F /* Instructions */ = { isa = PBXGroup; children = ( + A4A1C38D2BA99924006824D9 /* Plot */, 46B472B92560946E007972DB /* Embroidery */, 186AA3422482DD73001E38FD /* Pen */, 92B46FA41BC7C2A5004980C1 /* Arduino */, @@ -10558,6 +10646,7 @@ AA74F0211BC05FCE00D1E954 /* BrickCells */ = { isa = PBXGroup; children = ( + A4A1C38A2BA99807006824D9 /* Plot */, 4614B1D725608DB3004174AD /* Embroidery */, E577F0492563E8F200368C18 /* BrickCategoryOverviewCollectionViewCell.swift */, 92B46F9D1BC7BDF7004980C1 /* Arduino */, @@ -10835,6 +10924,12 @@ B43498471927A3AB002F9075 /* Resources */ = { isa = PBXGroup; children = ( + A43B471C2CB452BB00D253B7 /* apple.png */, + A43B47252CB45F0200D253B7 /* frame.png */, + A43B471D2CB452BB00D253B7 /* lynx_a.png */, + A43B471E2CB452BB00D253B7 /* panda_a.png */, + A43B471A2CB452BB00D253B7 /* panda_b.png */, + A43B47192CB452BB00D253B7 /* plotter.png */, 4981EDCB289AC2690052AEB1 /* MLModels */, 9EC51F8C24FC03890029CD0E /* Audio Engine */, 4C9E2D1D198EA6B600455B77 /* Licenses */, @@ -11045,6 +11140,7 @@ CAD232181B10F12600570370 /* DataModel */ = { isa = PBXGroup; children = ( + A4A8B5242BE7E579009266FE /* DashedLineShapeNode.swift */, 18385C67247C0EB30050819A /* LineShapeNode.swift */, 46E9E4A325AF53A100027D28 /* CircleShapeNode.swift */, CA02C5E81B14386E00233FB0 /* CBSpriteNode */, @@ -12097,11 +12193,13 @@ 9E5DFCFE250574380049265C /* 8-hand-clap.wv in Resources */, 9EC5208124FC04EA0029CD0E /* pizzicato-60.wv in Resources */, 9E5DFD1C250574380049265C /* choir-60.wv in Resources */, + A43B47202CB452BC00D253B7 /* panda_b.png in Resources */, 9E5DFD19250574380049265C /* choir-48.wv in Resources */, 9EC520AC24FC063D0029CD0E /* wooden-flute-72.wv in Resources */, 9EC520B024FC063D0029CD0E /* basso-48.wv in Resources */, 4CDE4CF7203DBBF500B43034 /* EVCircularProgressView.license in Resources */, 9EC520B624FC063D0029CD0E /* flute.sfz in Resources */, + A43B471F2CB452BC00D253B7 /* plotter.png in Resources */, 9E5DFD16250574380049265C /* synth-pad-60.wv in Resources */, 9E5DFD8C25057ECF0049265C /* ScratchSampleInstruments.license in Resources */, 9EC520B224FC063D0029CD0E /* saxophone-36.wv in Resources */, @@ -12121,6 +12219,7 @@ 4CDE4CED203DBBF500B43034 /* NSString+FastImageSize.license in Resources */, 9EC5206524FC03E80029CD0E /* piano.sfz in Resources */, 9218B1FC1CC4AB75007B4C60 /* nko_brightness_guide.png in Resources */, + A43B47222CB452BC00D253B7 /* apple.png in Resources */, 9E5DFD1F250574380049265C /* synth-lead.sfz in Resources */, 9EC5208924FC04EA0029CD0E /* trombo.sfz in Resources */, 9E5DFD1A250574380049265C /* choir-72.wv in Resources */, @@ -12163,6 +12262,7 @@ 9E5DFD14250574380049265C /* music-box-60.wv in Resources */, 4CDE4CF4203DBBF500B43034 /* HMSegmentedControl.license in Resources */, 9E5DFD06250574380049265C /* 10-wood-block.wv in Resources */, + A43B47242CB452BC00D253B7 /* panda_a.png in Resources */, 9EC5208624FC04EA0029CD0E /* cello.sfz in Resources */, 9EC5208C24FC04EA0029CD0E /* bass-48.wv in Resources */, 2E88D620263161D80063D573 /* TrustedDomains.plist in Resources */, @@ -12175,6 +12275,7 @@ 9EC520B724FC063D0029CD0E /* flute-72.wv in Resources */, 9E5DFD15250574380049265C /* synth-pad.sfz in Resources */, 9E5DFD0E250574380049265C /* 5-open-hi-hat.wv in Resources */, + A43B47232CB452BC00D253B7 /* lynx_a.png in Resources */, 9E5DFD0C250574380049265C /* 11-cowbell.wv in Resources */, 7B6FBD0027F1D8B4006E3BD5 /* LaunchScreen.storyboard in Resources */, 9E5DFD07250574380049265C /* 7-tambourine.wv in Resources */, @@ -12184,6 +12285,7 @@ 4CDE4CE3203DBBF500B43034 /* NKOColorPickerView.license in Resources */, 9E5DFD05250574380049265C /* 1-snare.wv in Resources */, 9EC5206324FC03E80029CD0E /* piano-24.wv in Resources */, + A43B47262CB45F0200D253B7 /* frame.png in Resources */, 9EC5205C24FC03E80029CD0E /* electric-piano-60.wv in Resources */, 9E5DFD03250574380049265C /* 16-guiro.wv in Resources */, 9EC5208724FC04EA0029CD0E /* cello-48.wv in Resources */, @@ -12386,6 +12488,7 @@ 4C7182DC23EEB22B00195BC7 /* PaintViewControllerTests.swift in Sources */, 4C2CBB5825A5AA8400C1C143 /* CBXMLSerializerHelperTests.swift in Sources */, BA38080C21A1D8920075B831 /* ConvertExceptionToError.m in Sources */, + A4A2564B2C31788C00CA8445 /* StartPlotBrickTests.swift in Sources */, 6F7D0C9324EBD19A008C1D4C /* LookTest.swift in Sources */, 9E24D7092326ED6C00608203 /* SetYBrickTests.swift in Sources */, 6F954C6624D1A3E400E10BB3 /* UploadCategoryViewControllerDelegateMock.swift in Sources */, @@ -12669,6 +12772,7 @@ E579F11A253DCD90009107C8 /* ChangeXByNBrickTests.swift in Sources */, 4CEB224F1B95E47500B3BE2F /* BrickMoveManagerForeverTests.m in Sources */, 9742DC5B269A2EC600980DE8 /* TouchesFingerSensorTest.swift in Sources */, + A4A2564D2C3178FB00CA8445 /* StopPlotBrickTests.swift in Sources */, E579F109253D9485009107C8 /* PhiroMotorMoveForwardBrickTests.swift in Sources */, 4C2CBB5525A5AA8400C1C143 /* XMLParserTests098.swift in Sources */, 0502CFAD26763BCC0028CF53 /* RecentlyUsedBricksManagerTests.swift in Sources */, @@ -12996,6 +13100,7 @@ 7B09EB6A29B1DF8200590F3D /* LoginViewController.swift in Sources */, 5EFBD5F92145533B003B3CDC /* ProjectDescriptionViewController.swift in Sources */, 92FF31571A24DEB300093DA7 /* ObjectTableViewController.m in Sources */, + A4A1C38C2BA9981F006824D9 /* StartPlotBrickCell.swift in Sources */, 4CE3D68F2107B68600005629 /* VisualDetectionManagerProtocol.swift in Sources */, BB6FE23C2812BD4E007ABDA9 /* FormCrashReportsSwitchTableViewCell.swift in Sources */, 929CC0EF1BC39B8C0027DEC0 /* PhiroMotorStopBrickCell.m in Sources */, @@ -13047,9 +13152,11 @@ 4C0F9F72204BD1B600E71B2D /* SpeakAndWaitBrick+CBXMLHandler.m in Sources */, 92BCDC871BF1FB1E00F9CB23 /* FirmataProtocol.swift in Sources */, AA74EFE21BC05B5F00D1E954 /* BroadcastWaitBrick.m in Sources */, + A4A8B5292BE7EF62009266FE /* SavePlotSVGBrickCell.swift in Sources */, FEFCFB29297C06B50028CEB2 /* StitchThreadColorBrick+Instruction.swift in Sources */, 4CF0729420D66A7F00F93AB5 /* AltitudeSensor.swift in Sources */, 9218B2131CC4AB75007B4C60 /* UndoManager.m in Sources */, + A4A8B5212BE7DE6D009266FE /* StopPlotBrickCell.swift in Sources */, AA74F0B31BC05FCE00D1E954 /* SetSizeToBrickCell.m in Sources */, F4E6E5C6210E24F900D86FE6 /* TrueFunction.swift in Sources */, 4CB0F9EC20A1731700D1BE2F /* PositionYSensor.swift in Sources */, @@ -13087,8 +13194,10 @@ 49402B9D2811742E009FCBF8 /* LeftMiddleFingerKnuckleXSensor.swift in Sources */, 4691E51D25F2B03F00122891 /* SetBackgroundByIndexBrick.swift in Sources */, AA74F0CD1BC05FCE00D1E954 /* StartScriptCell.m in Sources */, + A4A8B5232BE7DEDD009266FE /* StopPlotBrick+CBXMLHandler.swift in Sources */, 468EE8D9257F8F94003976DD /* SetBackgroundAndWaitBrick.swift in Sources */, 5E21DA7121A5289F00017D2C /* DataExtension.swift in Sources */, + A4A1C3932BA99AB9006824D9 /* StartPlotBrick+CBXMLHandler.swift in Sources */, 92FF31491A24DEB300093DA7 /* BaseCollectionViewController.m in Sources */, 4647D55A23D8AF9F001A67F6 /* PrivacyPolicyViewController.swift in Sources */, 46D0516324F8147F00A79155 /* SetRotationStyleBrick+CBXMLHandler.swift in Sources */, @@ -13238,6 +13347,7 @@ 4981EE3B289ACF1A0052AEB1 /* FaceDetectionManager.swift in Sources */, F4664F9220DD7A1700E1519A /* FormulaEditorViewControllerSectionExtension.swift in Sources */, 4C0F9F65204BD18600E71B2D /* PreviousLookBrick+CBXMLHandler.swift in Sources */, + A4A8B51D2BE7DB37009266FE /* StopPlotBrick+Instructions.swift in Sources */, 49402B9328116FA2009FCBF8 /* LeftIndexKnuckleYSensor.swift in Sources */, 4981EDCD289AC2690052AEB1 /* YOLOv3Tiny.mlmodel in Sources */, 5977B2A620298BD0008753ED /* MediaLibraryDownloader.swift in Sources */, @@ -13600,6 +13710,7 @@ 4C724E601B4D3E8C00E27479 /* Parser.m in Sources */, 92DFB0161A38949E00FA9B0F /* InternFormulaState.m in Sources */, AA74F0C51BC05FCE00D1E954 /* PlaySoundBrickCell.m in Sources */, + A4A8B51F2BE7DCDD009266FE /* StopPlotBrick.swift in Sources */, 498C158728070C6C00B81C8E /* LeftHipXSensor.swift in Sources */, 4981EE36289ACF1A0052AEB1 /* ObjectDetectionManager.swift in Sources */, 639CB8E525C418FE0051CB82 /* Sound.swift in Sources */, @@ -13740,6 +13851,7 @@ AA74F0C21BC05FCE00D1E954 /* TurnLeftBrickCell.m in Sources */, 9E0F741622B55D620030CD89 /* StandardAudioPlayerFactory.swift in Sources */, 92C6318A1BC502CB00486958 /* KnownDevicesTableViewController.swift in Sources */, + A4A1C3972BA99BF2006824D9 /* StartPlotBrick.swift in Sources */, 4C0F9F43204ADC6600E71B2D /* ThinkBubbleBrick.m in Sources */, AAF6D9D81BC0BAF300686849 /* ShowBrick+Instruction.swift in Sources */, 593E4DDE1FE11D410016DCB9 /* CatrobatReorderableCollectionViewFlowLayout.m in Sources */, @@ -13762,6 +13874,7 @@ 922FE8E11B963BDE005A19FA /* BrickSelectionManager.m in Sources */, 4981EE04289ACB2E0052AEB1 /* WidthOfObjectWithIDFunction.swift in Sources */, 4981EE28289ACEF90052AEB1 /* FacePositionYSensor.swift in Sources */, + A4A8B52B2BE7F538009266FE /* SavePlotSVGBrick+Instructions.swift in Sources */, 4CF0729920D66C4800F93AB5 /* BackgroundNameSensor.swift in Sources */, AA74EEE01BC057B900D1E954 /* IfLogicEndBrick+CBXMLHandler.m in Sources */, C808F26E26B04BA0008A349A /* TouchesEdgeSensor.swift in Sources */, @@ -13804,10 +13917,12 @@ AA74EFE11BC05B5F00D1E954 /* BroadcastBrick.m in Sources */, 4C5076D42578DCB000650440 /* Instrument.swift in Sources */, 4CB0F9F620A1741400D1BE2F /* LayerSensor.swift in Sources */, + A4A1C38F2BA99942006824D9 /* StartPlotBrick+Instruction.swift in Sources */, 4C2EE4201B555B55006DE9B8 /* CBXMLPropertyMapping.m in Sources */, 92C631881BC502CB00486958 /* BluetoothPopupVC.swift in Sources */, 4C4F21702314F73C0036E245 /* LXReorderableCollectionViewFlowLayout.m in Sources */, 498C158428070C6C00B81C8E /* RightHipXSensor.swift in Sources */, + A4A8B52F2BE7FEF4009266FE /* SavePlotSVGBrick+CBXMLHandler.swift in Sources */, 49B55F7227E86315000A95B4 /* RightEyeCenterYSensor.swift in Sources */, 4C26506A1A6538710050775B /* CBMutableCopyContext.m in Sources */, 49B55F9727E88702000A95B4 /* LeftEyebrowOuterYSensor.swift in Sources */, @@ -13837,6 +13952,7 @@ 4C4EE29D210901E70045F890 /* FormulaEditorViewControllerInputExtension.swift in Sources */, 6F348C85245499F1000C963B /* AppDelegate.swift in Sources */, 92FF32B41A24E2F400093DA7 /* CellMotionEffect.m in Sources */, + A4A8B5252BE7E579009266FE /* DashedLineShapeNode.swift in Sources */, 92FF314F1A24DEB300093DA7 /* LooksTableViewController.m in Sources */, 4963EB6E2812CFD500A9B3FA /* TextBlockLanguageFromCameraFunction.swift in Sources */, 929CC0D01BC39B0A0027DEC0 /* PhiroRGBLightBrick.m in Sources */, @@ -13862,6 +13978,7 @@ AA74EFFB1BC05B5F00D1E954 /* ChangeSizeByNBrick.m in Sources */, 4C595F5321CAEC2B0097D850 /* SmallerOrEqualOperator.swift in Sources */, 186212382475A1070098F4C5 /* SpriteKitDefines.swift in Sources */, + A4A8B5272BE7EEF3009266FE /* SavePlotSVGBrick.swift in Sources */, E4668B3C20FDCA6D0021FA0B /* FeaturedProjectsStoreTableDataSource.swift in Sources */, 91FC87CEFA4B1008B213EA31 /* AlertControllerBuilder.swift in Sources */, 91FC8B6AB3345BC25B02DF81 /* BaseAlertController.swift in Sources */, diff --git a/src/Catty/DataModel/Bricks/Plot/SavePlotSVGBrick.swift b/src/Catty/DataModel/Bricks/Plot/SavePlotSVGBrick.swift new file mode 100644 index 000000000..a38e2fc48 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Plot/SavePlotSVGBrick.swift @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2010-2024 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(SavePlotSVGBrick) + +@objcMembers class SavePlotSVGBrick: Brick, BrickProtocol, BrickFormulaProtocol { + + var filename: Formula? + + override required init() { + super.init() + } + + func category() -> kBrickCategoryType { + kBrickCategoryType.plotBrick + } + + override class func description() -> String { + "PlotBrick" + } + + override func getRequiredResources() -> Int { + ResourceType.noResources.rawValue + } + + override func brickCell() -> BrickCellProtocol.Type! { + SavePlotSVGBrickCell.self as BrickCellProtocol.Type + } + + func formula(forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) -> Formula! { + self.filename + } + + func setFormula(_ formula: Formula!, forLineNumber lineNumber: Int, andParameterNumber paramNumber: Int) { + self.filename = formula + } + + func getFormulas() -> [Formula]! { + [filename!] + } + + override func setDefaultValuesFor(_ spriteObject: SpriteObject!) { + self.filename = Formula(string: spriteObject.name! + ".svg") ?? Formula(string: kLocalizedSavePlotDefaultFile) + } + + func allowsStringFormula() -> Bool { + true + } + + override func isDisabledForBackground() -> Bool { + false + } +} diff --git a/src/Catty/DataModel/Bricks/Plot/StartPlotBrick.swift b/src/Catty/DataModel/Bricks/Plot/StartPlotBrick.swift new file mode 100644 index 000000000..a435c6d50 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Plot/StartPlotBrick.swift @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2010-2024 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 + +@objc(StartPlotBrick) +@objcMembers class StartPlotBrick: Brick, BrickProtocol { + + override required init() { + super.init() + } + + func category() -> kBrickCategoryType { + kBrickCategoryType.plotBrick + } + + override class func description() -> String { + "PlotBrick" + } + + override func getRequiredResources() -> Int { + ResourceType.noResources.rawValue + } + + override func brickCell() -> BrickCellProtocol.Type! { + StartPlotBrickCell.self as BrickCellProtocol.Type + } + + override func isDisabledForBackground() -> Bool { + true + } +} diff --git a/src/Catty/DataModel/Bricks/Plot/StopPlotBrick.swift b/src/Catty/DataModel/Bricks/Plot/StopPlotBrick.swift new file mode 100644 index 000000000..99937f5e5 --- /dev/null +++ b/src/Catty/DataModel/Bricks/Plot/StopPlotBrick.swift @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2010-2024 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(StopPlotBrick) + +@objcMembers class StopPlotBrick: Brick, BrickProtocol { + + override required init() { + super.init() + } + + func category() -> kBrickCategoryType { + kBrickCategoryType.plotBrick + } + + override class func description() -> String { + "PlotBrick" + } + + override func getRequiredResources() -> Int { + ResourceType.noResources.rawValue + } + + override func brickCell() -> BrickCellProtocol.Type! { + StopPlotBrickCell.self as BrickCellProtocol.Type + } + + override func isDisabledForBackground() -> Bool { + true + } + +} diff --git a/src/Catty/DataModel/Configuration/PenConfiguration.swift b/src/Catty/DataModel/Configuration/PenConfiguration.swift index 38a290c13..38b21d47d 100644 --- a/src/Catty/DataModel/Configuration/PenConfiguration.swift +++ b/src/Catty/DataModel/Configuration/PenConfiguration.swift @@ -22,6 +22,8 @@ struct PenConfiguration { var penDown = false + var isCut = false + static let sizeConversionFactor = CGFloat(0.634) private(set) var size: CGFloat @@ -38,6 +40,11 @@ struct PenConfiguration { var color = SpriteKitDefines.defaultPenColor var previousPositions = SynchronizedArray() + var previousPositionLines = SynchronizedArray>() + + var previousCutPositions = SynchronizedArray() + var drawnCutPoints = 0 + var previousCutPositionLines = SynchronizedArray>() init(projectWidth: CGFloat?, projectHeight: CGFloat?) { diff --git a/src/Catty/Defines/LanguageTranslationDefines.h b/src/Catty/Defines/LanguageTranslationDefines.h index e38e61f3c..f65f1dcfb 100644 --- a/src/Catty/Defines/LanguageTranslationDefines.h +++ b/src/Catty/Defines/LanguageTranslationDefines.h @@ -137,6 +137,7 @@ #define kLocalizedEditScript NSLocalizedString(@"Edit script", @"Action sheet menu title") #define kLocalizedEditBrick NSLocalizedString(@"Edit brick", @"Action sheet menu title") #define kLocalizedAddLook NSLocalizedString(@"Add look", @"Action sheet menu title") +#define kLocalizedAddLocal NSLocalizedString(@"Add local actor or object", @"Action sheet menu title") #define kLocalizedLookFilename NSLocalizedString(@"look", @"LOOK") #define kLocalizedEditProject NSLocalizedString(@"Edit project", nil) #define kLocalizedEditProjects NSLocalizedString(@"Edit projects", nil) @@ -464,6 +465,12 @@ #define kLocalizedBlue NSLocalizedString(@"blue", nil) #define kLocalizedStamp NSLocalizedString(@"Stamp", nil) +// plot bricks +#define kLocalizedStartPlot NSLocalizedString(@"Start to plot", nil) +#define kLocalizedStopPlot NSLocalizedString(@"Stop to plot", nil) +#define kLocalizedSavePlot NSLocalizedString(@"Save plot as SVG", nil) +#define kLocalizedSavePlotDefaultFile NSLocalizedString(@"plot.svg", nil) + // sound bricks #define kLocalizedSound NSLocalizedString(@"Sound", nil) #define kLocalizedPlaySound NSLocalizedString(@"Start sound", nil) @@ -913,6 +920,7 @@ #define kLocalizedCategoryPhiro NSLocalizedString(@"Phiro", nil) #define kLocalizedCategoryPen NSLocalizedString(@"Pen", nil) #define kLocalizedCategoryEmbroidery NSLocalizedString(@"Embroidery", nil) +#define kLocalizedCategoryPlot NSLocalizedString(@"Plot", nil) //************************************************************************************************************ //************************************ PhiroDefines ******************************************** diff --git a/src/Catty/Defines/LanguageTranslationDefinesSwift.swift b/src/Catty/Defines/LanguageTranslationDefinesSwift.swift index 15872b9f1..85f86e305 100644 --- a/src/Catty/Defines/LanguageTranslationDefinesSwift.swift +++ b/src/Catty/Defines/LanguageTranslationDefinesSwift.swift @@ -137,6 +137,7 @@ let kLocalizedEditBackgrounds = NSLocalizedString("Edit backgrounds", comment: " let kLocalizedEditScript = NSLocalizedString("Edit script", comment: "Action sheet menu title") let kLocalizedEditBrick = NSLocalizedString("Edit brick", comment: "Action sheet menu title") let kLocalizedAddLook = NSLocalizedString("Add look", comment: "Action sheet menu title") +let kLocalizedAddLocal = NSLocalizedString("Add local actor or object", comment: "Action sheet menu title") let kLocalizedLookFilename = NSLocalizedString("look", comment: "LOOK") let kLocalizedEditProject = NSLocalizedString("Edit project", comment: "") let kLocalizedEditProjects = NSLocalizedString("Edit projects", comment: "") @@ -464,6 +465,12 @@ let kLocalizedGreen = NSLocalizedString("green", comment: "") let kLocalizedBlue = NSLocalizedString("blue", comment: "") let kLocalizedStamp = NSLocalizedString("Stamp", comment: "") +// plot bricks +let kLocalizedStartPlot = NSLocalizedString("Start to plot", comment: "") +let kLocalizedStopPlot = NSLocalizedString("Stop to plot", comment: "") +let kLocalizedSavePlot = NSLocalizedString("Save plot as SVG", comment: "") +let kLocalizedSavePlotDefaultFile = NSLocalizedString("plot.svg", comment: "") + // sound bricks let kLocalizedSound = NSLocalizedString("Sound", comment: "") let kLocalizedPlaySound = NSLocalizedString("Start sound", comment: "") @@ -913,6 +920,7 @@ let kLocalizedCategoryArduino = NSLocalizedString("Arduino", comment: "") let kLocalizedCategoryPhiro = NSLocalizedString("Phiro", comment: "") let kLocalizedCategoryPen = NSLocalizedString("Pen", comment: "") let kLocalizedCategoryEmbroidery = NSLocalizedString("Embroidery", comment: "") +let kLocalizedCategoryPlot = NSLocalizedString("Plot", comment: "") //************************************************************************************************************ //************************************ PhiroDefines ******************************************** diff --git a/src/Catty/Defines/ProjectDefines.h b/src/Catty/Defines/ProjectDefines.h index ad3652d7b..2a2fd13c7 100644 --- a/src/Catty/Defines/ProjectDefines.h +++ b/src/Catty/Defines/ProjectDefines.h @@ -83,6 +83,14 @@ #define kDefaultProjectBundleName @"My first project" #define kDefaultProjectBundleOtherObjectsNamePrefix @"Mole" +#define kDefaultFrame @"Frame" +#define kDefaultFrameFile @"frame" +#define kDefaultNeedle @"Needle" +#define kDefaultNeedleFile @"plotter" +#define kDefaultPlotter @"Plotter" +#define kDefaultPlotterFile @"plotter" +#define kDefaultLynx @"Lynx" +#define kDefaultLynxFile @"lynx_a" // indexes #define kNumberOfSectionsInSceneTableViewController 2 diff --git a/src/Catty/Defines/UIDefines.h b/src/Catty/Defines/UIDefines.h index ef11ff7e8..dc90c3105 100644 --- a/src/Catty/Defines/UIDefines.h +++ b/src/Catty/Defines/UIDefines.h @@ -35,6 +35,7 @@ typedef NS_ENUM(NSUInteger, kBrickCategoryType) { kPhiroBrick = 8, kPenBrick = 9, kEmbroideryBrick = 10, + kPlotBrick = 11, kInvisible = 99, kRecentlyUsedBricks = 0 }; diff --git a/src/Catty/Extension&Delegate&Protocol/Extensions/SpriteNode/CBSpriteNodePenExtension.swift b/src/Catty/Extension&Delegate&Protocol/Extensions/SpriteNode/CBSpriteNodePenExtension.swift index 4b4c95b6c..c37c0cf37 100644 --- a/src/Catty/Extension&Delegate&Protocol/Extensions/SpriteNode/CBSpriteNodePenExtension.swift +++ b/src/Catty/Extension&Delegate&Protocol/Extensions/SpriteNode/CBSpriteNodePenExtension.swift @@ -23,25 +23,45 @@ extension CBSpriteNode { @objc func drawPenLine() { + //swiftlint:disable:next unused_enumerated + for (_, positionLine) in self.penConfiguration.previousPositionLines.enumerated() { + drawLineFromConfiguration(with: positionLine) + } + self.penConfiguration.previousPositionLines.removeAll() + if self.penConfiguration.previousPositions.last != self.position && penConfiguration.penDown { + self.penConfiguration.previousPositions.append(self.position) + } + let positions = self.penConfiguration.previousPositions + drawLineFromConfiguration(with: positions) + if positions.count > 1 { + self.penConfiguration.previousPositions.removeSubrange(0.., from startIndex: Int = 0) { + let positionCount = positions.count if positionCount > 1 { - for (index, point) in penConfiguration.previousPositions.enumerated() where index > 0 { - guard let lineFrom = penConfiguration.previousPositions[index - 1] else { + for (index, point) in positions.enumerated() where index > startIndex && index > 0 { + guard let lineFrom = positions[index - 1] else { fatalError("This should never happen") } let lineTo = point - self.addLine(from: lineFrom, to: lineTo, withColor: penConfiguration.color, withSize: penConfiguration.size) - } - penConfiguration.previousPositions.removeSubrange(0..() spriteNode.penConfiguration.penDown = false } } diff --git a/src/Catty/Resources/Localization/en.lproj/Localizable.strings b/src/Catty/Resources/Localization/en.lproj/Localizable.strings index 7d963b7a8..bbc59e5a3 100644 --- a/src/Catty/Resources/Localization/en.lproj/Localizable.strings +++ b/src/Catty/Resources/Localization/en.lproj/Localizable.strings @@ -49,6 +49,9 @@ /* No comment provided by engineer. */ "Add image" = "Add image"; +/* Action sheet menu title */ +"Add local actor or object" = "Add local actor or object"; + /* Action sheet menu title */ "Add look" = "Add look"; @@ -1552,6 +1555,12 @@ /* bluetooth */ "Please try resetting the device and try again." = "Please try resetting the device and try again."; +/* No comment provided by engineer. */ +"Plot" = "Plot"; + +/* No comment provided by engineer. */ +"plot.svg" = "plot.svg"; + /* No comment provided by engineer. */ "Pocket Code" = "Pocket Code"; @@ -1828,6 +1837,9 @@ /* No comment provided by engineer. */ "Save changes" = "Save changes"; +/* No comment provided by engineer. */ +"Save plot as SVG" = "Save plot as SVG"; + /* paint */ "Save to Photos" = "Save to Photos"; @@ -2065,6 +2077,9 @@ /* No comment provided by engineer. */ "Start sound and wait" = "Start sound and wait"; +/* No comment provided by engineer. */ +"Start to plot" = "Start to plot"; + /* No comment provided by engineer. */ "Start triple stitch with length" = "Start triple stitch with length"; @@ -2092,6 +2107,9 @@ /* No comment provided by engineer. */ "Stop Phiro motor" = "Stop Phiro motor"; +/* No comment provided by engineer. */ +"Stop to plot" = "Stop to plot"; + /* No comment provided by engineer. */ "Su" = "Su"; diff --git a/src/Catty/Resources/apple.png b/src/Catty/Resources/apple.png new file mode 100644 index 000000000..04b6179d1 Binary files /dev/null and b/src/Catty/Resources/apple.png differ diff --git a/src/Catty/Resources/frame.png b/src/Catty/Resources/frame.png new file mode 100644 index 000000000..52f3087e0 Binary files /dev/null and b/src/Catty/Resources/frame.png differ diff --git a/src/Catty/Resources/lynx_a.png b/src/Catty/Resources/lynx_a.png new file mode 100644 index 000000000..45d21bebf Binary files /dev/null and b/src/Catty/Resources/lynx_a.png differ diff --git a/src/Catty/Resources/panda_a.png b/src/Catty/Resources/panda_a.png new file mode 100644 index 000000000..641aafbfe Binary files /dev/null and b/src/Catty/Resources/panda_a.png differ diff --git a/src/Catty/Resources/panda_b.png b/src/Catty/Resources/panda_b.png new file mode 100644 index 000000000..be3ea2832 Binary files /dev/null and b/src/Catty/Resources/panda_b.png differ diff --git a/src/Catty/Resources/plotter.png b/src/Catty/Resources/plotter.png new file mode 100644 index 000000000..0f8d9d3f7 Binary files /dev/null and b/src/Catty/Resources/plotter.png differ diff --git a/src/Catty/SavePlotSVGBrick+Instructions.swift b/src/Catty/SavePlotSVGBrick+Instructions.swift new file mode 100644 index 000000000..d03b06ede --- /dev/null +++ b/src/Catty/SavePlotSVGBrick+Instructions.swift @@ -0,0 +1,94 @@ +/** + * Copyright (C) 2010-2024 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 SavePlotSVGBrick: CBInstructionProtocol { + + func instruction() -> CBInstruction { + .action { context in SKAction.run(self.actionBlock(context: context)) } + } + + func actionBlock(context: CBScriptContextProtocol) -> () -> Void { + guard let object = self.script?.object, + let spriteNode = object.spriteNode + else { fatalError("This should never happen!") } + + return { + var filename = context.formulaInterpreter.interpretString(self.filename!, for: object) + if let number = Double(filename) { + filename = number.displayString + } + + var paths = "" + //swiftlint:disable:next unused_enumerated + for (_, previousCutPositionLine) in spriteNode.penConfiguration.previousCutPositionLines.enumerated() { + paths += self.getLinePath(with: previousCutPositionLine) + "\n" + } + paths += self.getLinePath(with: spriteNode.penConfiguration.previousCutPositions) + + self.saveSVGPlot(with: paths, to: filename, width: Int(object.scene.width() ?? "0") ?? 0, height: Int(object.scene.height() ?? "0") ?? 0) + } + } + + private func getDocumentsDirectory() -> URL { + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + return paths[0] + } + + private func saveSVGPlot(with paths: String, to filename: String, width: Int, height: Int) { + var filecontent = "\n\n" + filecontent += "\n" + filecontent += "Plotter export\n" + var filename = filename + + filecontent += paths + filecontent += "\n" + + if !filename.hasSuffix(".svg") { + filename += ".svg" + } + let file = self.getDocumentsDirectory().appendingPathComponent(filename) + try? filecontent.write(to: file, atomically: true, encoding: String.Encoding.utf8) + } + + private func getLinePath(with positions: SynchronizedArray) -> String { + var path = "" + + let positionCount = positions.count + if positionCount > 1 { + path = " 0 { + guard positions[index - 1] != nil else { + fatalError("This should never happen") + } + path += " L" + String(format: "%.2f", point.x) + " " + String(format: "%.2f", point.y) + } + path += "\" />" + } + return path + } +} diff --git a/src/Catty/Setup/CatrobatSetup+Bricks.swift b/src/Catty/Setup/CatrobatSetup+Bricks.swift index 1b5dd7094..fdae29077 100644 --- a/src/Catty/Setup/CatrobatSetup+Bricks.swift +++ b/src/Catty/Setup/CatrobatSetup+Bricks.swift @@ -127,7 +127,11 @@ StartZigzagStitchBrick(), StartTripleStitchBrick(), SewUpBrick(), - StopCurrentStitchBrick() + StopCurrentStitchBrick(), + // plot brick + StartPlotBrick(), + StopPlotBrick(), + SavePlotSVGBrick() ] if isPhiroEnabled() { @@ -166,6 +170,12 @@ strokeColor: UIColor.eventBrickStroke, enabled: true), + BrickCategory(type: kBrickCategoryType.plotBrick, + name: kLocalizedCategoryPlot, + color: UIColor.plotBrick, + strokeColor: UIColor.plotBrickStroke, + enabled: true), + BrickCategory(type: kBrickCategoryType.controlBrick, name: kLocalizedCategoryControl, color: UIColor.controlBrickOrange, diff --git a/src/Catty/StartPlotBrick+Instruction.swift b/src/Catty/StartPlotBrick+Instruction.swift new file mode 100644 index 000000000..4d518cc86 --- /dev/null +++ b/src/Catty/StartPlotBrick+Instruction.swift @@ -0,0 +1,40 @@ +/** + * Copyright (C) 2010-2024 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 StartPlotBrick: CBInstructionProtocol { + + func instruction() -> CBInstruction { + .action { _ in SKAction.run(self.actionBlock()) } + } + + func actionBlock() -> () -> Void { + guard let object = self.script?.object, + let spriteNode = object.spriteNode + else { fatalError("This should never happen!") } + + return { + spriteNode.penConfiguration.isCut = true + spriteNode.penConfiguration.previousCutPositions.append(spriteNode.position) + } + } + +} diff --git a/src/Catty/StopPlotBrick+Instructions.swift b/src/Catty/StopPlotBrick+Instructions.swift new file mode 100644 index 000000000..fed0f41d3 --- /dev/null +++ b/src/Catty/StopPlotBrick+Instructions.swift @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2010-2024 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 StopPlotBrick: CBInstructionProtocol { + + func instruction() -> CBInstruction { + .action { _ in SKAction.run(self.actionBlock()) } + } + + func actionBlock() -> () -> Void { + guard let object = self.script?.object, + let spriteNode = object.spriteNode + else { fatalError("This should never happen!") } + + return { + spriteNode.penConfiguration.previousCutPositionLines.append(spriteNode.penConfiguration.previousCutPositions) + spriteNode.penConfiguration.previousCutPositions = SynchronizedArray() + spriteNode.penConfiguration.isCut = false + spriteNode.penConfiguration.drawnCutPoints = 0 + } + } + +} diff --git a/src/Catty/ViewController/Continue&New/MaintainObject/MaintainLooks/LooksTableViewController.m b/src/Catty/ViewController/Continue&New/MaintainObject/MaintainLooks/LooksTableViewController.m index 41e7c1e8b..7fe83a89e 100644 --- a/src/Catty/ViewController/Continue&New/MaintainObject/MaintainLooks/LooksTableViewController.m +++ b/src/Catty/ViewController/Continue&New/MaintainObject/MaintainLooks/LooksTableViewController.m @@ -593,6 +593,148 @@ - (BOOL)textField:(UITextField*)field shouldChangeCharactersInRange:(NSRange)ran return [characters length] <= kMaxNumOfLookNameCharacters; } +#pragma mark - action sheet +- (void)showAddLocalActionSheet +{ + id actionSheet = [[AlertControllerBuilder actionSheetWithTitle:kLocalizedAddLocal] + addCancelActionWithTitle:kLocalizedCancel handler:^{ + if (self.showAddLookActionSheetAtStartForObject || self.showAddLookActionSheetAtStartForScriptEditor) { + SAFE_BLOCK_CALL(self.afterSafeBlock, nil); + } + }]; + [[[[[[actionSheet addDefaultActionWithTitle:kDefaultNeedle handler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIImage* image = [UIImage imageNamed:kDefaultNeedleFile]; + NSData *imageData = UIImagePNGRepresentation(image); + // use temporary filename, will be renamed by user afterwards + NSString *newImageFileName = [NSString stringWithFormat:@"temp_%@.%@", + [[[imageData md5] stringByReplacingOccurrencesOfString:@"-" withString:@""] uppercaseString], + kLocalizedMyImageExtension]; + Look *look = [[Look alloc] initWithName:[Util uniqueName:kDefaultNeedle + existingNames:[self.object allLookNames]] + andPath:newImageFileName]; + + NSString *newImagePath = [look pathForScene:self.object.scene]; + self.filePath = newImagePath; + // leaving the main queue here! + [imageData writeToFile:newImagePath atomically:YES]; + + [self hideLoadingView]; + [self showPlaceHolder:([self.object.lookList count] == 0)]; + if (self.showAddLookActionSheetAtStartForObject) { + [self addLookActionWithName:look.name look:look]; + }}); + }] addDefaultActionWithTitle:kDefaultPlotter handler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIImage* image = [UIImage imageNamed:kDefaultPlotterFile]; + NSData *imageData = UIImagePNGRepresentation(image); + // use temporary filename, will be renamed by user afterwards + NSString *newImageFileName = [NSString stringWithFormat:@"temp_%@.%@", + [[[imageData md5] stringByReplacingOccurrencesOfString:@"-" withString:@""] uppercaseString], + kLocalizedMyImageExtension]; + Look *look = [[Look alloc] initWithName:[Util uniqueName:kDefaultPlotter + existingNames:[self.object allLookNames]] + andPath:newImageFileName]; + + NSString *newImagePath = [look pathForScene:self.object.scene]; + self.filePath = newImagePath; + // leaving the main queue here! + [imageData writeToFile:newImagePath atomically:YES]; + + [self hideLoadingView]; + [self showPlaceHolder:([self.object.lookList count] == 0)]; + if (self.showAddLookActionSheetAtStartForObject) { + [self addLookActionWithName:look.name look:look]; + }}); + }] addDefaultActionWithTitle:kDefaultFrame handler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIImage* image = [UIImage imageNamed:kDefaultFrameFile]; + NSData *imageData = UIImagePNGRepresentation(image); + // use temporary filename, will be renamed by user afterwards + NSString *newImageFileName = [NSString stringWithFormat:@"temp_%@.%@", + [[[imageData md5] stringByReplacingOccurrencesOfString:@"-" withString:@""] uppercaseString], + kLocalizedMyImageExtension]; + Look *look = [[Look alloc] initWithName:[Util uniqueName:kDefaultFrame + existingNames:[self.object allLookNames]] + andPath:newImageFileName]; + + NSString *newImagePath = [look pathForScene:self.object.scene]; + self.filePath = newImagePath; + // leaving the main queue here! + [imageData writeToFile:newImagePath atomically:YES]; + + [self hideLoadingView]; + [self showPlaceHolder:([self.object.lookList count] == 0)]; + if (self.showAddLookActionSheetAtStartForObject) { + [self addLookActionWithName:look.name look:look]; + + StartScript *script = [StartScript new]; + script.object = self.object; + [self.object.scriptList addObject:script]; + + SetTransparencyBrick* transperency100Brick = [[SetTransparencyBrick alloc] init]; + transperency100Brick.script = script; + transperency100Brick.transparency = [[Formula alloc] init]; + transperency100Brick.transparency.formulaTree = [[FormulaElement alloc] initWithInteger:100]; + [script.brickList addObject: transperency100Brick]; + + PlaceAtBrick* placeAtXYBrick = [[PlaceAtBrick alloc] init]; + placeAtXYBrick.script = script; + placeAtXYBrick.yPosition = [[Formula alloc] init]; + placeAtXYBrick.xPosition = [[Formula alloc] init]; + placeAtXYBrick.xPosition.formulaTree = [[FormulaElement alloc] initWithInteger:-250]; + placeAtXYBrick.yPosition.formulaTree = [[FormulaElement alloc] initWithInteger:250]; + + [script.brickList addObject: placeAtXYBrick]; + PenDownBrick* penDownBrick = [[PenDownBrick alloc] init]; + penDownBrick.script = script; + [script.brickList addObject:penDownBrick]; + + MoveNStepsBrick* move500Brick = [[MoveNStepsBrick alloc] init]; + move500Brick.script = script; + move500Brick.steps = [[Formula alloc] init]; + move500Brick.steps.formulaTree = [[FormulaElement alloc] initWithInteger:500]; + + TurnRightBrick* turn90Brick = [[TurnRightBrick alloc] init]; + turn90Brick.script = script; + turn90Brick.degrees = [[Formula alloc] init]; + turn90Brick.degrees.formulaTree = [[FormulaElement alloc] initWithInteger:90]; + + [script.brickList addObject:move500Brick]; + [script.brickList addObject:turn90Brick]; + [script.brickList addObject:move500Brick]; + [script.brickList addObject:turn90Brick]; + [script.brickList addObject:move500Brick]; + [script.brickList addObject:turn90Brick]; + [script.brickList addObject:move500Brick]; + [script.brickList addObject:turn90Brick]; + }}); + }] addDefaultActionWithTitle:kDefaultLynx handler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + UIImage* image = [UIImage imageNamed:kDefaultLynxFile]; + NSData *imageData = UIImagePNGRepresentation(image); + // use temporary filename, will be renamed by user afterwards + NSString *newImageFileName = [NSString stringWithFormat:@"temp_%@.%@", + [[[imageData md5] stringByReplacingOccurrencesOfString:@"-" withString:@""] uppercaseString], + kLocalizedMyImageExtension]; + Look *look = [[Look alloc] initWithName:[Util uniqueName:kDefaultLynx + existingNames:[self.object allLookNames]] + andPath:newImageFileName]; + + NSString *newImagePath = [look pathForScene:self.object.scene]; + self.filePath = newImagePath; + // leaving the main queue here! + [imageData writeToFile:newImagePath atomically:YES]; + + [self hideLoadingView]; + [self showPlaceHolder:([self.object.lookList count] == 0)]; + if (self.showAddLookActionSheetAtStartForObject) { + [self addLookActionWithName:look.name look:look]; + }}); + }] + build] showWithController:self]; +}; + #pragma mark - action sheet - (void)showAddLookActionSheet { @@ -636,6 +778,14 @@ - (void)showAddLookActionSheet }]; } } + if (!self.object.isBackground) { + [actionSheet addDefaultActionWithTitle:kLocalizedAddLocal handler:^{ + dispatch_async(dispatch_get_main_queue(), ^{ + [self showAddLocalActionSheet]; + }); + }]; + } + [[[[actionSheet addDefaultActionWithTitle:kLocalizedDrawNewImage handler:^{ diff --git a/src/Catty/ViewController/Continue&New/SceneTableViewController.m b/src/Catty/ViewController/Continue&New/SceneTableViewController.m index c59827383..ad760e8f8 100644 --- a/src/Catty/ViewController/Continue&New/SceneTableViewController.m +++ b/src/Catty/ViewController/Continue&New/SceneTableViewController.m @@ -106,6 +106,7 @@ - (void)addObjectAction:(id)sender addCancelActionWithTitle:kLocalizedCancel handler:^{ [self cancelAddingObjectFromScriptEditor]; }] + addDefaultActionWithTitle:kLocalizedOK handler:^(NSString *name) { [self addObjectActionWithName:name]; }] diff --git a/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/SavePlotSVGBrickCell.swift b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/SavePlotSVGBrickCell.swift new file mode 100644 index 000000000..1992a3eb2 --- /dev/null +++ b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/SavePlotSVGBrickCell.swift @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2010-2024 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/. + */ + +class SavePlotSVGBrickCell: BrickCell, BrickCellProtocol { + + var leftTextLabel: UILabel? + var fileTextField: UITextField? + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + } + + static func cellHeight() -> CGFloat { + UIDefines.brickHeight1h + } + + override func hookUpSubViews(_ inlineViewSubViews: [Any]!) { + self.leftTextLabel = inlineViewSubViews[0] as? UILabel + self.fileTextField = inlineViewSubViews[1] as? UITextField + } + + func brickTitle(forBackground isBackground: Bool, andInsertionScreen isInsertion: Bool) -> String! { + kLocalizedSavePlot + " %@\n" + } + + override func parameters() -> [String]! { + NSArray.init(objects: "{FLOAT;range=(0,inf)}", "{VARIABLE}") as? [String] + } +} diff --git a/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StartPlotBrickCell.swift b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StartPlotBrickCell.swift new file mode 100644 index 000000000..b13f54d7b --- /dev/null +++ b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StartPlotBrickCell.swift @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2010-2024 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/. + */ + +class StartPlotBrickCell: BrickCell, BrickCellProtocol { + + var textLabel: UILabel? + + static func cellHeight() -> CGFloat { + UIDefines.brickHeight1h + } + + func brickTitle(forBackground isBackground: Bool, andInsertionScreen isInsertion: Bool) -> String! { + kLocalizedStartPlot + } + + override func hookUpSubViews(_ inlineViewSubViews: [Any]!) { + self.textLabel = inlineViewSubViews[0] as? UILabel + } +} diff --git a/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StopPlotBrickCell.swift b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StopPlotBrickCell.swift new file mode 100644 index 000000000..0679b623e --- /dev/null +++ b/src/Catty/Views/Custom/CollectionViewCells/BrickCells/Plot/StopPlotBrickCell.swift @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2010-2024 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/. + */ + +class StopPlotBrickCell: BrickCell, BrickCellProtocol { + + var textLabel: UILabel? + + static func cellHeight() -> CGFloat { + UIDefines.brickHeight1h + } + + func brickTitle(forBackground isBackground: Bool, andInsertionScreen isInsertion: Bool) -> String! { + kLocalizedStopPlot + } + + override func hookUpSubViews(_ inlineViewSubViews: [Any]!) { + self.textLabel = inlineViewSubViews[0] as? UILabel + } +} diff --git a/src/Catty/XML/XMLHandler/Bricks/Plot/SavePlotSVGBrick+CBXMLHandler.swift b/src/Catty/XML/XMLHandler/Bricks/Plot/SavePlotSVGBrick+CBXMLHandler.swift new file mode 100644 index 000000000..2c3b0ac2c --- /dev/null +++ b/src/Catty/XML/XMLHandler/Bricks/Plot/SavePlotSVGBrick+CBXMLHandler.swift @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2010-2024 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 SavePlotSVGBrick: CBXMLNodeProtocol { + static func parse(from xmlElement: GDataXMLElement, with context: CBXMLParserContext) -> Self { + CBXMLParserHelper.validate(xmlElement, forFormulaListWithTotalNumberOfFormulas: 1) + let brick = self.init() + let formula = CBXMLParserHelper.formula(in: xmlElement, forCategoryName: "WRITE_FILENAME", with: context) + brick.filename = formula + return brick + } + + func xmlElement(with context: CBXMLSerializerContext) -> GDataXMLElement? { + let brick = super.xmlElement(for: "SavePlotSVGBrick", with: context) + let formulaList = GDataXMLElement(name: "formulaList", context: context) + let formula = self.filename?.xmlElement(with: context) + formula?.addAttribute(GDataXMLElement(name: "category", stringValue: "WRITE_FILENAME", context: nil)) + formulaList?.addChild(formula, context: context) + brick?.addChild(formulaList, context: context) + return brick + } +} diff --git a/src/Catty/XML/XMLHandler/Bricks/Plot/StartPlotBrick+CBXMLHandler.swift b/src/Catty/XML/XMLHandler/Bricks/Plot/StartPlotBrick+CBXMLHandler.swift new file mode 100644 index 000000000..4818b084e --- /dev/null +++ b/src/Catty/XML/XMLHandler/Bricks/Plot/StartPlotBrick+CBXMLHandler.swift @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2010-2024 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 StartPlotBrick: CBXMLNodeProtocol { + static func parse(from xmlElement: GDataXMLElement, with context: CBXMLParserContext) -> Self { + CBXMLParserHelper.validate(xmlElement, forNumberOfChildNodes: 0) + return self.init() + } + + func xmlElement(with context: CBXMLSerializerContext) -> GDataXMLElement? { + let brick = super.xmlElement(for: "StartPlotBrick", with: context) + return brick + } +} diff --git a/src/Catty/XML/XMLHandler/Bricks/Plot/StopPlotBrick+CBXMLHandler.swift b/src/Catty/XML/XMLHandler/Bricks/Plot/StopPlotBrick+CBXMLHandler.swift new file mode 100644 index 000000000..8f7d63d95 --- /dev/null +++ b/src/Catty/XML/XMLHandler/Bricks/Plot/StopPlotBrick+CBXMLHandler.swift @@ -0,0 +1,33 @@ +/** + * Copyright (C) 2010-2024 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 StopPlotBrick: CBXMLNodeProtocol { + static func parse(from xmlElement: GDataXMLElement, with context: CBXMLParserContext) -> Self { + CBXMLParserHelper.validate(xmlElement, forNumberOfChildNodes: 0) + return self.init() + } + + func xmlElement(with context: CBXMLSerializerContext) -> GDataXMLElement? { + let brick = super.xmlElement(for: "StopPlotBrick", with: context) + return brick + } +} diff --git a/src/CattyTests/Bricks/StartPlotBrickTests.swift b/src/CattyTests/Bricks/StartPlotBrickTests.swift new file mode 100644 index 000000000..d9ba10c85 --- /dev/null +++ b/src/CattyTests/Bricks/StartPlotBrickTests.swift @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2010-2024 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 XCTest + +@testable import Pocket_Code + +final class StartPlotBrickTest: XCTestCase { + + func testStartPlotBrick() { + let object = SpriteObject() + let scene = Scene(name: "testScene") + object.scene = scene + let spriteNode = CBSpriteNode(spriteObject: object) + spriteNode.penConfiguration.isCut = false + object.spriteNode = spriteNode + + let script = Script() + script.object = object + + let brick = StartPlotBrick() + brick.script = script + + let action = brick.actionBlock() + XCTAssertFalse(spriteNode.penConfiguration.isCut) + action() + XCTAssertTrue(spriteNode.penConfiguration.isCut) + XCTAssertEqual(spriteNode.penConfiguration.previousCutPositions.last, spriteNode.position) + } +} diff --git a/src/CattyTests/Bricks/StopPlotBrickTests.swift b/src/CattyTests/Bricks/StopPlotBrickTests.swift new file mode 100644 index 000000000..04dca250e --- /dev/null +++ b/src/CattyTests/Bricks/StopPlotBrickTests.swift @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2010-2024 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 XCTest + +@testable import Pocket_Code + +final class StopPlotBrickTests: XCTestCase { + + func testStopPlotBrick() { + let object = SpriteObject() + let scene = Scene(name: "testScene") + object.scene = scene + let spriteNode = CBSpriteNode(spriteObject: object) + object.spriteNode = spriteNode + spriteNode.penConfiguration.isCut = true + + let script = Script() + script.object = object + + let brick = StopPlotBrick() + brick.script = script + + let action = brick.actionBlock() + XCTAssertTrue(spriteNode.penConfiguration.isCut) + action() + XCTAssertFalse(spriteNode.penConfiguration.isCut) + } +} diff --git a/src/CattyTests/Stage/CBSpriteNodePenExtensionTests.swift b/src/CattyTests/Stage/CBSpriteNodePenExtensionTests.swift index a723418df..b26099280 100644 --- a/src/CattyTests/Stage/CBSpriteNodePenExtensionTests.swift +++ b/src/CattyTests/Stage/CBSpriteNodePenExtensionTests.swift @@ -96,7 +96,47 @@ final class CBSpriteNodePenExtensionTests: XCTestCase { XCTAssertEqual(allShapeNodes.count, 2) XCTAssertEqual(allShapeNodes[1].pathStartPoint, CGPoint(x: 1, y: 1)) XCTAssertEqual(allShapeNodes[1].pathEndPoint, spriteNode.position) + } + + func testWhenSpritePositionChangedStartPlot() { + spriteNode.penConfiguration.isCut = true + spriteNode.penConfiguration.previousCutPositions.append(initialPosition) + spriteNode.position = initialPosition + + XCTAssertEqual(stage.children.count, 1) + + spriteNode.position = CGPoint(x: 1, y: 1) + spriteNode.update(CACurrentMediaTime()) + + XCTAssertEqual(stage.children.count, 2) + + var allShapeNodes = getAllLineShapeNodes() + + XCTAssertEqual(allShapeNodes.count, 1) + XCTAssertEqual(allShapeNodes[0].pathStartPoint, initialPosition) + XCTAssertEqual(allShapeNodes[0].pathEndPoint, spriteNode.position) + + spriteNode.position = CGPoint(x: 2, y: 2) + spriteNode.update(CACurrentMediaTime()) + + XCTAssertEqual(stage.children.count, 3) + + allShapeNodes = getAllLineShapeNodes() + + XCTAssertEqual(allShapeNodes.count, 2) + XCTAssertEqual(allShapeNodes[1].pathStartPoint, CGPoint(x: 1, y: 1)) + XCTAssertEqual(allShapeNodes[1].pathEndPoint, spriteNode.position) + } + + func testWhenSpritePositionChangedStopPlot() { + spriteNode.penConfiguration.isCut = false + spriteNode.position = CGPoint(x: 1, y: 1) + XCTAssertEqual(stage.children.count, 1) + + spriteNode.update(CACurrentMediaTime()) + + XCTAssertEqual(stage.children.count, 1) } func testWhenSpritePositionNotChangedPenUp() { @@ -109,6 +149,16 @@ final class CBSpriteNodePenExtensionTests: XCTestCase { XCTAssertEqual(stage.children.count, 1) } + func testWhenSpritePositionNotChangedStartPlot() { + spriteNode.penConfiguration.isCut = true + spriteNode.position = initialPosition + + XCTAssertEqual(stage.children.count, 1) + + spriteNode.update(CACurrentMediaTime()) + + XCTAssertEqual(stage.children.count, 1) + } func testWhenSpritePositionNotChangedPenDown() { spriteNode.penConfiguration.penDown = true @@ -121,6 +171,17 @@ final class CBSpriteNodePenExtensionTests: XCTestCase { XCTAssertEqual(stage.children.count, 1) } + func testWhenSpritePositionNotChangedStopPlot() { + spriteNode.penConfiguration.isCut = false + spriteNode.position = initialPosition + + XCTAssertEqual(stage.children.count, 1) + + spriteNode.update(CACurrentMediaTime()) + + XCTAssertEqual(stage.children.count, 1) + } + func testDrawnLineAttributes() { spriteNode.penConfiguration.penDown = true spriteNode.penConfiguration.previousPositions.append(initialPosition) diff --git a/src/CattyUITests/Defines/LanguageTranslationDefinesUI.swift b/src/CattyUITests/Defines/LanguageTranslationDefinesUI.swift index 1bff22603..de87c434d 100644 --- a/src/CattyUITests/Defines/LanguageTranslationDefinesUI.swift +++ b/src/CattyUITests/Defines/LanguageTranslationDefinesUI.swift @@ -137,6 +137,7 @@ let kLocalizedEditBackgrounds = NSLocalizedString("Edit backgrounds", bundle: Bu let kLocalizedEditScript = NSLocalizedString("Edit script", bundle: Bundle(for: LanguageTranslation.self), comment: "Action sheet menu title") let kLocalizedEditBrick = NSLocalizedString("Edit brick", bundle: Bundle(for: LanguageTranslation.self), comment: "Action sheet menu title") let kLocalizedAddLook = NSLocalizedString("Add look", bundle: Bundle(for: LanguageTranslation.self), comment: "Action sheet menu title") +let kLocalizedAddLocal = NSLocalizedString("Add local actor or object", bundle: Bundle(for: LanguageTranslation.self), comment: "Action sheet menu title") let kLocalizedLookFilename = NSLocalizedString("look", bundle: Bundle(for: LanguageTranslation.self), comment: "LOOK") let kLocalizedEditProject = NSLocalizedString("Edit project", bundle: Bundle(for: LanguageTranslation.self), comment: "") let kLocalizedEditProjects = NSLocalizedString("Edit projects", bundle: Bundle(for: LanguageTranslation.self), comment: "") @@ -464,6 +465,12 @@ let kLocalizedGreen = NSLocalizedString("green", bundle: Bundle(for: LanguageTra let kLocalizedBlue = NSLocalizedString("blue", bundle: Bundle(for: LanguageTranslation.self), comment: "") let kLocalizedStamp = NSLocalizedString("Stamp", bundle: Bundle(for: LanguageTranslation.self), comment: "") +// plot bricks +let kLocalizedStartPlot = NSLocalizedString("Start to plot", bundle: Bundle(for: LanguageTranslation.self), comment: "") +let kLocalizedStopPlot = NSLocalizedString("Stop to plot", bundle: Bundle(for: LanguageTranslation.self), comment: "") +let kLocalizedSavePlot = NSLocalizedString("Save plot as SVG", bundle: Bundle(for: LanguageTranslation.self), comment: "") +let kLocalizedSavePlotDefaultFile = NSLocalizedString("plot.svg", bundle: Bundle(for: LanguageTranslation.self), comment: "") + // sound bricks let kLocalizedSound = NSLocalizedString("Sound", bundle: Bundle(for: LanguageTranslation.self), comment: "") let kLocalizedPlaySound = NSLocalizedString("Start sound", bundle: Bundle(for: LanguageTranslation.self), comment: "") @@ -913,6 +920,7 @@ let kLocalizedCategoryArduino = NSLocalizedString("Arduino", bundle: Bundle(for: let kLocalizedCategoryPhiro = NSLocalizedString("Phiro", bundle: Bundle(for: LanguageTranslation.self), comment: "") let kLocalizedCategoryPen = NSLocalizedString("Pen", bundle: Bundle(for: LanguageTranslation.self), comment: "") let kLocalizedCategoryEmbroidery = NSLocalizedString("Embroidery", bundle: Bundle(for: LanguageTranslation.self), comment: "") +let kLocalizedCategoryPlot = NSLocalizedString("Plot", bundle: Bundle(for: LanguageTranslation.self), comment: "") //************************************************************************************************************ //************************************ PhiroDefines ********************************************