diff --git a/src/activities/programmingMaze/ProgrammingMaze.qml b/src/activities/programmingMaze/ProgrammingMaze.qml index cde5ba30d..10afea8c8 100644 --- a/src/activities/programmingMaze/ProgrammingMaze.qml +++ b/src/activities/programmingMaze/ProgrammingMaze.qml @@ -1,530 +1,498 @@ /* GCompris - programmingMaze.qml * * Copyright (C) 2014 Siddhesh Suthar * * Authors: * Siddhesh Suthar (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.2 import GCompris 1.0 import "../../core" import "qrc:/gcompris/src/core/core.js" as Core import "programmingMaze.js" as Activity import QtQuick.Controls 1.0 import QtQuick.Layouts 1.0 ActivityBase { id: activity onStart: focus = true onStop: {} property int oldWidth: width onWidthChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } pageComponent: Rectangle { id: background anchors.fill: parent color: "#8C8984" property bool keyNavigation: false signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property GCAudio audioEffects: activity.audioEffects property alias mazeModel: mazeModel property alias instructionModel: instructionModel property alias answerModel: answerModel property alias player: player property alias fish: fish } onStart: { Activity.start(items) keyNavigation = false } onStop: { Activity.stop() } Keys.onRightPressed: { keyNavigation = true - instruction.incrementCurrentIndex() + instruction.moveCurrentIndexRight() } Keys.onLeftPressed: { keyNavigation = true - instruction.decrementCurrentIndex() + instruction.moveCurrentIndexLeft() } Keys.onDownPressed: { keyNavigation = true - instruction.incrementCurrentIndex() + instruction.moveCurrentIndexDown() } Keys.onUpPressed: { keyNavigation = true - instruction.decrementCurrentIndex() + instruction.moveCurrentIndexUp() } Keys.onSpacePressed: { keyNavigation = true - instruction.currentItem.children[3].clicked() + instruction.currentItem.mouseAreaInstruction.clicked() } Keys.onEnterPressed: { keyNavigation = true - instruction.currentItem.children[3].clicked() + instruction.currentItem.mouseAreaInstruction.clicked() } Keys.onReturnPressed: { keyNavigation = true - instruction.currentItem.children[3].clicked() + instruction.currentItem.mouseAreaInstruction.clicked() } ListModel { id: instructionModel - ListElement { - name: qsTr("Move Forward") - } - ListElement { - name: qsTr("Turn Left") - } - ListElement { - name: qsTr("Turn Right") - } } ListModel { id: answerModel } Repeater { id: mazeModel model: Activity.mazeBlocks[0] anchors.left: parent.left anchors.top: parent.top anchors.bottom: instruction.top Image { x: modelData[0] * background.width / 8 y: modelData[1] * (background.height - background.height/8) / 8 width: background.width / 8 height: background.height / 8 source: Activity.reverseCountUrl + "iceblock.svg" } } Image { id: player source: "qrc:/gcompris/src/activities/maze/resource/" + "tux_top_south.svg" sourceSize.width: background.width / 10 x: 0; y: 0; z: 11 property int duration: 1000 property bool tuxIsBusy: false signal init onInit: { player.rotation = -90 } onTuxIsBusyChanged: { Activity.playerRunningChanged() } Behavior on x { SmoothedAnimation { duration: player.duration reversingMode: SmoothedAnimation.Immediate onRunningChanged: { player.tuxIsBusy = !player.tuxIsBusy - // Activity.busy = !Activity.busy - // Activity.playerRunningChanged() } } } Behavior on y { SmoothedAnimation { duration: player.duration reversingMode: SmoothedAnimation.Immediate onRunningChanged: { player.tuxIsBusy = !player.tuxIsBusy - // Activity.busy = !Activity.busy - // Activity.playerRunningChanged() } } } Behavior on rotation { RotationAnimation { duration: player.duration / 2 direction: RotationAnimation.Shortest onRunningChanged: { player.tuxIsBusy = !player.tuxIsBusy - // Activity.busy = !Activity.busy - // Activity.playerRunningChanged() } } } } Image { id: fish source: Activity.reverseCountUrl + "blue-fish.svg" sourceSize.width: background.width / 10 // anchors.leftMargin: 20 * ApplicationInfo.ratio x: 0; y: 0; z: 5 } - ListView { + property int buttonWidth: background.width / 10 + property int buttonHeight: background.height / 10 + property int answerButtonWidth: background.width / 10 + property int answerButtonHeight: background.height / 10 + + GridView { id: instruction width: parent.width * 0.625 height: parent.height * 0.375 - bar.height / 2 + cellWidth: background.buttonWidth + cellHeight: background.buttonHeight anchors.left: parent.left anchors.bottom: bar.top anchors.margins: 10 * ApplicationInfo.ratio anchors.bottomMargin: bar.height / 2 - orientation: Qt.Vertical - verticalLayoutDirection: ListView.TopToBottom - spacing: 5 * ApplicationInfo.ratio interactive: false model: instructionModel - clip: true header: instructionHeaderComponent highlight: Rectangle { - width: instruction.width - height: instruction.buttonHeight + width: buttonWidth + height: buttonHeight color: "lightsteelblue" border.width: 3 border.color: "black" visible: background.keyNavigation - y: instruction.currentItem.y - Behavior on y { SpringAnimation { spring: 3; damping: 0.2 } } + x: instruction.currentItem.x + Behavior on x { SpringAnimation { spring: 3; damping: 0.2 } } } highlightFollowsCurrentItem: false focus: true keyNavigationWraps: true - property int buttonHeight: instruction.height / instructionModel.count - instruction.spacing + delegate: Column { + spacing: 10 * ApplicationInfo.ratio + property alias mouseAreaInstruction: mouseAreaInstruction - delegate: Item { - width: instruction.width - instruction.spacing - height: instruction.buttonHeight + Item { + width: background.buttonWidth + height: background.buttonHeight - Rectangle { - id: rect - anchors.fill: parent - gradient: Gradient { - GradientStop { position: 0.0; color: "#0191C8" } - GradientStop { position: 1.0; color: "#005B9A" } - } - opacity: 0.5 + Rectangle { + id: rect + width: parent.width + height: parent.height + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#0191C8" } + GradientStop { position: 1.0; color: "#005B9A" } + } + opacity: 0.5 - } + } - Image { - source: "qrc:/gcompris/src/core/resource/button.svg" - sourceSize { height: parent.height; width: parent.width } - width: sourceSize.width - height: sourceSize.height - smooth: false - } + Image { + id: icon + source: Activity.url + name + ".svg" + sourceSize {width: parent.width; height: parent.height} + width: sourceSize.width + height: sourceSize.height + anchors.horizontalCenter: parent.horizontalCenter + smooth: false + } - GCText { - id: instructionText - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignHCenter - width: instruction.width - fontSizeMode: Text.Fit - minimumPointSize: 7 - fontSize: regularSize - wrapMode: Text.WordWrap - color: "white" - text: name - } - MouseArea { - id: mouseArea - anchors.fill: parent - signal clicked - onClicked: { - clickedAnim.start() - answerModel.append({"name": instructionText.text}) - } - onPressed: { - clickedAnim.start() - answerModel.append({"name": instructionText.text}) + Image { + source: "qrc:/gcompris/src/core/resource/button.svg" + sourceSize { height: parent.height; width: parent.width } + width: sourceSize.width + height: sourceSize.height + smooth: false } - } - SequentialAnimation { - id: clickedAnim - PropertyAnimation { - target: rect - property: "opacity" - to: "1" - duration: 300 + MouseArea { + id: mouseAreaInstruction + anchors.fill: parent + signal clicked + onClicked: { + clickedAnim.start() + answerModel.append({"name": name}) + } + onPressed: { + clickedAnim.start() + answerModel.append({"name": name}) + } } - PropertyAnimation { - target: rect - property: "opacity" - to: "0.5" - duration: 300 + SequentialAnimation { + id: clickedAnim + PropertyAnimation { + target: rect + property: "opacity" + to: "1" + duration: 300 + } + + PropertyAnimation { + target: rect + property: "opacity" + to: "0.5" + duration: 300 + } } } } } // insert data upon clicking the list items into this answerData // and then process it to run the code - ScrollView { - id: answerSheetScroller + GridView { + id: answerSheet + width: parent.width * 0.350 height: parent.height * 0.80 - bar.height + cellWidth: background.answerButtonWidth + cellHeight: background.answerButtonHeight anchors.right: parent.right anchors.top: parent.top anchors.margins: 10 * ApplicationInfo.ratio - ListView { - id: answerSheet - - width: parent.width - height: parent.height - - orientation: Qt.Vertical - verticalLayoutDirection: ListView.TopToBottom - spacing: 5 * ApplicationInfo.ratio - interactive: false - model: answerModel - clip: true + interactive: false + model: answerModel + clip: true + header: answerHeaderComponent - header: answerHeaderComponent + delegate: Column { - delegate: Item { - width: answerSheet.width - answerSheet.spacing - height: answerSheet.height / 5 - answerSheet.spacing + Item { + width: background.answerButtonWidth + height: background.answerButtonHeight Rectangle { id: answerRect anchors.fill: parent color: "#005B9A" opacity: 1 } Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } - GCText { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - horizontalAlignment: Text.AlignHCenter - width: parent.width - fontSizeMode: Text.Fit - minimumPointSize: 7 - fontSize: regularSize - wrapMode: Text.WordWrap - color: "white" - text: name - } - Image { - source: "qrc:/gcompris/src/core/resource/bar_exit.svg" - sourceSize { height: parent.height ; width: parent.width } - width: sourceSize.width / 8 - height: sourceSize.height / 3 - smooth: false - - anchors.right: parent.right + id: answer + source: Activity.url + name + ".svg" + sourceSize { width: parent.width; height: parent.height } + width: sourceSize.width + height: sourceSize.height + anchors.centerIn: parent anchors.verticalCenter: parent.verticalCenter - anchors.rightMargin: 10 * ApplicationInfo.ratio + anchors.horizontalCenter: parent.horizontalCenter + smooth: false MouseArea { id: answerRemove anchors.fill: parent onPressed: { answerModel.remove(model.index) } } } - } } - } -// Component { -// id: answerFooterComponent -// anchors.top: answerSheet.bottom + Item { width: background.width / 8 height: background.height / 8 - anchors.top: answerSheetScroller.bottom + anchors.top: answerSheet.bottom anchors.topMargin: 10 * ApplicationInfo.ratio - anchors.left: answerSheetScroller.left + anchors.left: answerSheet.left Image { id: runCode width: parent.width height: parent.height source:"qrc:/gcompris/src/core/resource/bar_ok.svg" fillMode: Image.PreserveAspectFit MouseArea { id: runCodeMouseArea anchors.fill: parent hoverEnabled: true onEntered: runCode.scale = 1.1 onClicked: { Activity.runCode() } onExited: runCode.scale = 1 } } } Component { id: instructionHeaderComponent Rectangle { id: headerRect - width: ListView.view.width + width: instruction.width height: 40 * ApplicationInfo.ratio color: "#005B9A" opacity: 1 Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: headerText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: parent.width fontSizeMode: Font.DemiBold minimumPointSize: 7 fontSize: mediumSize wrapMode: Text.WordWrap color: "white" text: qsTr("Choose the instructions") } } } Component { id: answerHeaderComponent Rectangle { id: answerHeaderRect - width: ListView.view.width + width: answerSheet.width height: 40 * ApplicationInfo.ratio color: "#005B9A" opacity: 1 Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: answerHeaderText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignHCenter width: parent.width fontSizeMode: Font.DemiBold minimumPointSize: 7 fontSize: mediumSize wrapMode: Text.WordWrap color: "white" text: qsTr("Your Code") } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.initLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/programmingMaze/programmingMaze.js b/src/activities/programmingMaze/programmingMaze.js index d0aabe615..ff5856363 100644 --- a/src/activities/programmingMaze/programmingMaze.js +++ b/src/activities/programmingMaze/programmingMaze.js @@ -1,233 +1,282 @@ /* GCompris - programmingMaze.js * * Copyright (C) 2014 * * Authors: * "Siddhesh Suthar" (Qt Quick port) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ .pragma library .import QtQuick 2.0 as Quick var currentLevel = 0 var numberOfLevel = 4 var items var reverseCountUrl = "qrc:/gcompris/src/activities/reversecount/resource/" var mazeBlocks = [ - [[1,2],[2,2],[3,2]], - [[1,3],[2,3],[2,2],[2,1],[3,1]], - [[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3]], - [[0,3],[1,3],[1,2],[2,2],[2,1],[3,1]] + //level one + [ + //maze blocks + [[1,2],[2,2],[3,2]], + //fish index + [[3,2]], + //instruction set + [qsTr("move-forward"), + qsTr("turn-left"), + qsTr("turn-right")] + ], + //level two + [ + [[1,3],[2,3],[2,2],[2,1],[3,1]], + //fish index + [[3,1]], + //instruction set + [qsTr("move-forward"), + qsTr("turn-left"), + qsTr("turn-right")] + ], + //level three + [ + [[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3]], + [[1,3]], + //instruction set + [qsTr("move-forward"), + qsTr("turn-left"), + qsTr("turn-right"), + qsTr("call-procedure")] + ], + //level four + [ + [[0,3],[1,3],[1,2],[2,2],[2,1],[3,1]], + [[3,1]], + //instruction set + [qsTr("move-forward"), + qsTr("turn-left"), + qsTr("turn-right"), + qsTr("call-procedure")] + ] ] +//[1,3],[2,3],[2,2],[2,1],[3,1] +//[1,1],[2,1],[3,1],[3,2],[3,3],[2,3],[1,3] +//[0,3],[1,3],[1,2],[2,2],[2,1],[3,1] var countOfMazeBlocks var initialX var initialY var stepX var stepY var playerCode = [] var currentInstruction var tuxIceBlockNumber var currentBlock var nextBlock var currentX var currentY var nextX var nextY var changedX var changedY var currentRotation var changedRotation var flag = 0 var j =0 -//var busy = false +var blocksDataIndex = 0 +var blocksFishIndex = 1 +var blocksInstructionIndex = 2 +var levelInstructions +var url = "qrc:/gcompris/src/activities/programmingMaze/resource/" function start(items_) { items = items_ currentLevel = 0 initLevel() } function stop() { } function initLevel() { items.bar.level = currentLevel + 1 - items.mazeModel.model = mazeBlocks[currentLevel] + items.mazeModel.model = mazeBlocks[currentLevel][blocksDataIndex] items.answerModel.clear() - countOfMazeBlocks = mazeBlocks[currentLevel].length + countOfMazeBlocks = mazeBlocks[currentLevel][blocksDataIndex].length + stepX = items.background.width / 8 stepY = (items.background.height - items.background.height/8) / 8 - initialX = mazeBlocks[currentLevel][0][0] * stepX - initialY = mazeBlocks[currentLevel][0][1] * stepY + initialX = mazeBlocks[currentLevel][blocksDataIndex][0][0] * stepX + initialY = mazeBlocks[currentLevel][blocksDataIndex][0][1] * stepY + + items.instructionModel.clear() + levelInstructions = mazeBlocks[currentLevel][blocksInstructionIndex] + for (var i = 0; i < levelInstructions.length ; i++) { + items.instructionModel.append({"name":levelInstructions[i]}); + } -// busy = false items.player.tuxIsBusy = false j = 0 playerCode = [] items.player.x = initialX items.player.y = initialY - items.fish.x = mazeBlocks[currentLevel][countOfMazeBlocks -1][0] * stepX - items.fish.y = mazeBlocks[currentLevel][countOfMazeBlocks -1][1] * stepY + items.fish.x = mazeBlocks[currentLevel][blocksFishIndex][0][0] * stepX + items.fish.y = mazeBlocks[currentLevel][blocksFishIndex][0][1] * stepY tuxIceBlockNumber = 0 currentRotation = -90 changedRotation = -90 flag = 0 items.player.init() } /* 0= SOUTH * 90= WEST * 180 = NORTH * 270 =EAST */ function getPlayerRotation() { return ((changedRotation % 360) + 360) % 360 } function runCode() { //initiallize back to starting position and code playerCode = [] items.player.tuxIsBusy = false for(var i = 0; i < items.answerModel.count; i++) { playerCode.push(items.answerModel.get([i]).name) } if(!items.player.tuxIsBusy) { executeNextInstruction() } } function playerRunningChanged() { if(!items.player.tuxIsBusy) { if(flag == 1) { deadEnd() } executeNextInstruction() } } function executeNextInstruction() { if(!items.player.tuxIsBusy && j < playerCode.length ) { changedX = items.player.x changedY = items.player.y currentRotation = getPlayerRotation() currentInstruction = playerCode[j] console.log(j + " executing next " +currentInstruction) currentBlock = tuxIceBlockNumber nextBlock = tuxIceBlockNumber + 1 - currentX = mazeBlocks[currentLevel][currentBlock][0] - currentY = mazeBlocks[currentLevel][currentBlock][1] - nextX = mazeBlocks[currentLevel][nextBlock][0] - nextY = mazeBlocks[currentLevel][nextBlock][1] + currentX = mazeBlocks[currentLevel][blocksDataIndex][currentBlock][0] + currentY = mazeBlocks[currentLevel][blocksDataIndex][currentBlock][1] + nextX = mazeBlocks[currentLevel][blocksDataIndex][nextBlock][0] + nextY = mazeBlocks[currentLevel][blocksDataIndex][nextBlock][1] - if ( currentInstruction == "Move Forward") { + if ( currentInstruction == "move-forward") { ++tuxIceBlockNumber; if (nextX - currentX > 0 && currentRotation == 270) { //EAST 270 changedX = currentX * stepX + stepX console.log("moving forward emitting the signal") items.player.x = changedX items.player.y = changedY } else if(nextX - currentX < 0 && currentRotation == 90){ //WEST 90 changedX = currentX * stepX - stepX items.player.x = changedX items.player.y = changedY } else if (nextY - currentY < 0 && currentRotation == 180) { //NORTH 0 changedY = currentY * stepY - stepY console.log("moving forward emitting the signal") items.player.x = changedX items.player.y = changedY } else if (nextY - currentY > 0 && currentRotation == 0) { //SOUTH 180 changedY = currentY * stepY + stepY items.player.x = changedX items.player.y = changedY } else { // add an animation to indicate that its not possible flag = 1 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") console.log("dead end") return } } - else if ( currentInstruction == "Turn Left") { + else if ( currentInstruction == "turn-left") { changedRotation = (currentRotation - 90) % 360 // console.log("turning left") items.player.rotation = changedRotation } - else if ( currentInstruction == "Turn Right") { + else if ( currentInstruction == "turn-right") { changedRotation = (currentRotation + 90) % 360 items.player.rotation = changedRotation } j = j + 1 -// busy = true items.player.tuxIsBusy = true checkSuccess() } } function deadEnd() { playerCode = [] j =0 items.player.tuxIsBusy = false console.log("ending") + initLevel(); } function checkSuccess() { if(changedX === items.fish.x && changedY === items.fish.y){ console.log("success") playerCode = [] j = 0 -// busy = false items.player.tuxIsBusy = false items.bonus.good("smiley") } } function nextLevel() { if(numberOfLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); } function repositionObjectsOnWidthChanged(factor) { if(items) initLevel() } function repositionObjectsOnHeightChanged(factor) { if(items) initLevel() } diff --git a/src/activities/programmingMaze/resource/call-procedure.svg b/src/activities/programmingMaze/resource/call-procedure.svg new file mode 100644 index 000000000..1b02e6e0e --- /dev/null +++ b/src/activities/programmingMaze/resource/call-procedure.svg @@ -0,0 +1,124 @@ + + + + Procedure in purple + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/move-forward.svg b/src/activities/programmingMaze/resource/move-forward.svg new file mode 100644 index 000000000..7d8f4989f --- /dev/null +++ b/src/activities/programmingMaze/resource/move-forward.svg @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Indian road sign - Compulsory ahead only + 2012-09-10T00:52:00 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172253/indian-road-sign---compulsory-ahead-only-by-ksrujana96-172253 + + + ksrujana96 + + + + + Compulsory ahead only + indian road sign + road sign + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-left.svg b/src/activities/programmingMaze/resource/turn-left.svg new file mode 100644 index 000000000..e127f866e --- /dev/null +++ b/src/activities/programmingMaze/resource/turn-left.svg @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Indian road sign - Compulsory turn left + 2012-09-10T00:55:46 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172256/indian-road-sign---compulsory-turn-left-by-ksrujana96-172256 + + + ksrujana96 + + + + + Compulsory turn left + indian road sign + road sign + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-right.svg b/src/activities/programmingMaze/resource/turn-right.svg new file mode 100644 index 000000000..01a8fd3e2 --- /dev/null +++ b/src/activities/programmingMaze/resource/turn-right.svg @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + Indian road sign - Compulsory turn right + 2012-09-10T00:56:48 + created by swecha developer and contributer Srujana. + https://openclipart.org/detail/172257/indian-road-sign---compulsory-turn-right-by-ksrujana96-172257 + + + ksrujana96 + + + + + Compulsory turn right + indian road sign + road sign + + + + + + + + + + +