Warning, /webapps/qmlonline/qml/examples/canvasadvanced.qml is written in an unsupported language. File is not indexed.

0001 import QtQuick 2.13
0002 import QtQuick.Controls 2.5
0003 
0004 Item {
0005     id: root
0006     anchors.fill: parent
0007     focus: true
0008 
0009     MouseArea {
0010         id: globalMouse
0011         hoverEnabled: true
0012         anchors.fill: parent
0013     }
0014 
0015     ListModel {
0016         id: listModel
0017     }
0018 
0019     Canvas {
0020         id: canvas
0021         anchors.fill: parent
0022         property var path: listModel
0023 
0024         function getArea() {
0025             // Shoelace algorithm
0026             // Ref: https://en.wikipedia.org/wiki/Shoelace_formula
0027             var sum = 0
0028 
0029             for(var i = 0; i < path.count; i++) {
0030                 var point = path.get(i)
0031                 var nextPoint = i + 1 != path.count ? path.get(i + 1) : path.get(0)
0032 
0033                 sum += point.xPos*nextPoint.yPos - nextPoint.xPos*point.yPos
0034             }
0035 
0036             return Math.abs(sum) / 2
0037         }
0038 
0039         function getCentroid() {
0040             // Calculate the centroid of a non-self-intersecting closed polygon defined by n vertices
0041             // Ref: https://en.wikipedia.org/wiki/Centroid
0042             var areaMultiplied = 6*getArea()
0043             var centroid = {xPos: 0, yPos: 0}
0044 
0045             for(var i = 0; i < path.count; i++) {
0046                 var point = path.get(i)
0047                 var nextPoint = i + 1 != path.count ? path.get(i + 1) : path.get(0)
0048 
0049                 var multiplier = point.xPos*nextPoint.yPos - nextPoint.xPos*point.yPos
0050                 centroid.xPos += (point.xPos + nextPoint.xPos)*multiplier
0051                 centroid.yPos += (point.yPos + nextPoint.yPos)*multiplier
0052             }
0053 
0054             centroid.xPos = Math.abs(centroid.xPos/areaMultiplied)
0055             centroid.yPos = Math.abs(centroid.yPos/areaMultiplied)
0056             return centroid
0057         }
0058 
0059         onPaint: {
0060             var context = getContext("2d");
0061             context.reset();
0062 
0063             // Do not paint if there isn't enough points to create a line
0064             if(path.count < 2){
0065                 return;
0066             }
0067 
0068             var firstPoint = path.get(0)
0069             context.beginPath()
0070             context.moveTo(firstPoint.xPos, firstPoint.yPos)
0071             context.lineWidth = 2
0072             context.strokeStyle = "red"
0073             context.textAlign = "center"
0074 
0075             for(var i = 1; i < path.count; i++) {
0076                 // Create line with 2 points
0077                 var previousPoint = path.get(i - 1)
0078                 var point = path.get(i)
0079 
0080                 // Calculate line middle point
0081                 var middlePoint = {xPos: (point.xPos + previousPoint.xPos)/2, yPos: (point.yPos + previousPoint.yPos)/2}
0082                 // Translated line to the origin
0083                 var translatedLine = {xPos: point.xPos - previousPoint.xPos, yPos: point.yPos - previousPoint.yPos}
0084                 // Calculate relative distance between the line points
0085                 var relativeDistance = Math.hypot(translatedLine.yPos, translatedLine.xPos)
0086                 // Calculate line angle
0087                 var angle = Math.atan(translatedLine.yPos/translatedLine.xPos)
0088 
0089                 // Translate and rotate context to draw distance text
0090                 context.save();
0091                 context.translate(middlePoint.xPos, middlePoint.yPos);
0092                 context.rotate(angle);
0093                 context.fillStyle = "blue"
0094                 context.fillText(relativeDistance.toFixed(2), 0, -5);
0095                 // Restore from translation and rotation
0096                 context.restore()
0097 
0098                 // Draw line to the next point
0099                 context.lineTo(point.xPos, point.yPos)
0100             }
0101 
0102             // If there is enough points to create a polygon, fill it and add area information
0103             if(path.count > 2) {
0104                 var centerPoint = getCentroid()
0105                 context.fillStyle = "green"
0106                 context.fillText(getArea().toFixed(2), centerPoint.xPos, centerPoint.yPos)
0107                 context.fillStyle = "#22000033"
0108                 context.fill()
0109             }
0110             context.stroke()
0111         }
0112     }
0113 
0114     Repeater {
0115         id: repeater
0116         model: canvas.path
0117         delegate: Rectangle {
0118             width: 10
0119             height: width
0120             color: "black"
0121             radius: width
0122             x: xPos - width/2
0123             y: yPos - height/2
0124             MouseArea {
0125                 anchors.fill: parent
0126                 drag.target: parent
0127                 onPositionChanged: {
0128                     canvas.path.get(index).xPos = parent.x + width/2
0129                     canvas.path.get(index).yPos = parent.y + height/2
0130                     canvas.requestPaint()
0131                 }
0132             }
0133         }
0134     }
0135 
0136     Keys.onPressed: {
0137         switch(event.key) {
0138             case Qt.Key_N:
0139                 var newPoint = {xPos: globalMouse.mouseX, yPos: globalMouse.mouseY}
0140                 print("New point:", JSON.stringify(newPoint))
0141                 event.accepted = true
0142 
0143                 canvas.path.append({xPos: globalMouse.mouseX, yPos: globalMouse.mouseY})
0144                 canvas.requestPaint()
0145                 break;
0146 
0147             case Qt.Key_D:
0148                 var lastIndex = canvas.path.count - 1
0149                 print("Delete last point:", JSON.stringify(canvas.path.get(lastIndex)))
0150                 event.accepted = true
0151 
0152                 canvas.path.remove(lastIndex, 1)
0153                 canvas.requestPaint()
0154                 break;
0155 
0156             default:
0157                 print("We don't handle it.")
0158                 event.accepted = false
0159         }
0160     }
0161 
0162     Label {
0163         anchors.top: parent.top
0164         text: "New Point: 'N', Delete Point: 'D'"
0165         font.pixelSize: 30
0166     }
0167 }