I am drawing part arc in circle with QML Canvas.
This is my code:
import QtQuick 2.0
import QtQml 2.2
Item {
id: root
property real arcAzimuth: 0
property real arcAngle: 80
property string arcColor: "red"
rotation: - (arcAngle / 4)
onArcColorChanged: canvas.requestPaint()
onArcAngleChanged: canvas.requestPaint()
Canvas {
id: canvas
anchors.fill: parent
rotation: -90 + parent.rotation
onPaint: {
var ctx = getContext("2d")
var x = width / 2
var y = height / 2
var start = Math.PI * (parent.arcAzimuth / 180)
var end = Math.PI * ((parent.arcAzimuth + parent.arcAngle) / 180)
ctx.reset()
ctx.beginPath();
ctx.lineWidth = 8
ctx.arc(x, y, (width / 2) - ctx.lineWidth / 2, start, end, false)
ctx.strokeStyle = root.arcColor
ctx.stroke()
}
}
}
This draws me something like angle of unfilled circle (border of circle). I want to draw exact same thing, but I want to rotate this by something like z coord so it will look like standing and looking on circle that is painted on floor.
How can I do this?
(After imgur will start working with stackoverflow, i will provide images)
Thank for your help
//Edit: Temporaly images links (because of error with uploading)
I have got this
and I want this
If you want to obtain a rotation in several axes you must pass a Rotation to transform:
import QtQuick 2.0
import QtQml 2.2
Item {
id: root
property real arcAzimuth: 0
property real arcAngle: 80
property string arcColor: "red"
rotation: - (arcAngle / 4)
onArcColorChanged: canvas.requestPaint()
onArcAngleChanged: canvas.requestPaint()
Canvas {
id: canvas
anchors.fill: parent
transform: Rotation{
axis { x: 0; y: 0.8; z: 1.0 }
angle: 225 + parent.rotation
}
onPaint: {
var ctx = getContext("2d")
var x = width / 2
var y = height / 2
var start = Math.PI * (parent.arcAzimuth / 180)
var end = 2*Math.PI * ((parent.arcAzimuth + parent.arcAngle) / 180)
ctx.reset()
ctx.beginPath();
ctx.lineWidth = 8
ctx.arc(x, y, (width / 2) - ctx.lineWidth / 2, start, end, false)
ctx.strokeStyle = root.arcColor
ctx.stroke()
}
}
}
Related
I need to draw something like a target, a bunch of concentric circles split into 12 or so sectors (pizza slices). So the number of "layers" inside the outer circle can change anywhere from 2 to 10, the size of the whole target should remain the same, and the distance between inner circles should be equal (bascially OuterCircleRadius / NumberOfLayers)
So far I came up with a code that basically draws a single segment of a circle, then, by putting a Repeater in the main file I get a full circle, split into 12 sectors, which is what I need. Now I need to find a way to add a number of concentric circles inside the outer one. The problem is that the number of concentric circles is not constant, and is set by user. So the "target" should change when a different number of layers is chosen (for example with a SpinBox). So far my idea is to draw a few additional arcs inside of a single sector so I can keep the Repeater. My code below.
// main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts
import QtQuick.Controls
Window {
width: 600
height: 600
visible: true
id: window
property int sectors: 12
Rectangle {
anchors.centerIn: parent
width: 600
height: 600
Repeater {
model: sectors
CircleSector {
anchors.fill: parent
anchors.margins: 10
sectors: window.sectors
sector: index
fillColor: index == spinBox.value ? "orange" : "aliceblue"
}
}
}
Frame {
SpinBox {
id: spinBox
Layout.fillWidth: true
from: 0
to: sectors - 1
value: 1
wrap: true
stepSize: 1
}
}
}
// CircleSector.qml
import QtQuick 2.12
import QtQuick.Shapes 1.12
Shape {
id: circleSector
antialiasing: true
property int sectors: 12
property int sector: 0
property real from: sector * (360 / sectors)
property real to: (sector + 1) * (360 / sectors)
property real centerX: width / 2
property real centerY: height / 2
property alias fillColor: shapePath.fillColor
property alias strokeColor: shapePath.strokeColor
property real fromX: centerX + centerX * Math.cos(from * Math.PI / 180)
property real fromY: centerY + centerY * Math.sin(from * Math.PI / 180)
property real toX: centerX + centerX * Math.cos(to * Math.PI / 180)
property real toY: centerY + centerY * Math.sin(to * Math.PI / 180)
containsMode: Shape.FillContains
ShapePath{
id: shapePath
fillColor: "aliceblue"
strokeColor: "grey"
startX: centerX; startY: centerY
PathLine { x: fromX; y: fromY }
PathArc{
radiusX: centerX; radiusY: centerY
x: toX; y: toY
}
PathLine{ x: centerX; y: centerY }
}
}
Depending on what you want to do with your polar coordinate system you could use PolarChartView. With that you can also easily plot data on it.
import QtQuick
import QtQuick.Controls
import QtCharts
Window {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
PolarChartView {
anchors.fill: parent
legend.visible: false
antialiasing: true
ValueAxis {
id: axisAngular
min: 0
max: 1
labelsVisible: false
lineVisible: false
tickCount: axisAngularSpinBox.value
}
ValueAxis {
id: axisRadial
min: 0
max: 1
labelsVisible: false
lineVisible: false
tickCount: axisRadialSpinBox.value
}
SplineSeries {
id: series
axisAngular: axisAngular
axisRadial: axisRadial
pointsVisible: true
}
}
Column {
SpinBox {
id: axisAngularSpinBox
value: 9
}
SpinBox {
id: axisRadialSpinBox
value: 10
}
}
}
You could use this Canvas solution. It isn't pretty, but it hopefully does what you want.
import QtQuick
import QtQuick.Controls
ApplicationWindow {
id: root
title: "Polar Coordinates Canvas"
width: 640
height: 480
visible: true
color: "white"
property int numCircles: circlesSpinBox.value
property int numSegments: segmentsSpinBox.value
Canvas {
id: canvas
anchors.centerIn: parent
width: 400
height: canvas.width
onPaint: {
var ctx = getContext("2d")
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, canvas.width, canvas.height);
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const radius = canvas.width / 2
const circleDistance = (canvas.width / 2) / root.numCircles
for (let i = 1; i <= root.numCircles; ++i) {
let r = i * circleDistance
ctx.beginPath()
ctx.arc(centerX, centerY, r, 0, 2 * Math.PI, false)
ctx.lineWidth = 1
ctx.strokeStyle = 'black'
ctx.stroke()
}
if (root.numSegments < 2)
return
const segmentAngle = 360 / root.numSegments
if (root.numSegments % 2 === 0) { // even
for (let s = 0; s < (root.numSegments / 2); ++s) {
let a = s * segmentAngle
ctx.beginPath()
let x = centerX + radius * Math.cos(a * (Math.PI / 180))
let y = centerY + radius * Math.sin(a * (Math.PI / 180))
ctx.moveTo(x, y)
x = centerX + radius * Math.cos((a + 180) * (Math.PI / 180))
y = centerY + radius * Math.sin((a + 180) * (Math.PI / 180))
ctx.lineTo(x, y)
ctx.lineWidth = 1
ctx.strokeStyle = 'black'
ctx.stroke()
}
} else { // odd
for (let s = 0; s < root.numSegments; ++s) {
let a = s * segmentAngle
ctx.beginPath()
let x = centerX + radius * Math.cos(a * (Math.PI / 180))
let y = centerY + radius * Math.sin(a * (Math.PI / 180))
ctx.moveTo(x, y)
ctx.lineTo(centerX, centerY)
ctx.lineWidth = 1
ctx.strokeStyle = 'black'
ctx.stroke()
}
}
}
}
Column {
SpinBox {
id: circlesSpinBox
value: 3
onValueChanged: canvas.requestPaint()
}
SpinBox {
id: segmentsSpinBox
value: 7
onValueChanged: canvas.requestPaint()
}
}
}
I need scale and rotate a Rectangle around mouse point. When the Rectangle is not rotated my solution works fine, but if I apply Rotation transform I face the problem - my Rectangle move to an unexpected point. In my solution I use a MouseArea for drag the Rectangle, Scale and Rotation transforms for scale and rotate.
import QtQuick 2.0
Rectangle {
id: root
color: "gray"
Rectangle {
color: "black"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
}
Rectangle {
id: sample
color: "green"
width: 360
height: 200
opacity: 0.5
x: 500
y: 350
property real currX: 0
property real currY: 0
property real currZoom: 1
property real maxZoom: 5
property real minZoom: 0.5
transform: [
Scale {
id: scaler
origin.x: sample.currX
origin.y: sample.currY
xScale: sample.currZoom
yScale: sample.currZoom
},
Rotation{
id: rotation
origin.x: 180
origin.y: 100
angle: 30
}
]
MouseArea{
id: mouseArea
anchors.fill: parent
drag.target: sample
onClicked: mouse => {
zoom(true, mouse.x, mouse.y)
}
onWheel: (wheel) => {
var isIn = wheel.angleDelta.y > 0
zoom(isIn, wheel.x, wheel.y)
}
function zoom(isIn, x, y) {
var prevZoom = sample.currZoom
var prevX = sample.currX
var prevY = sample.currY
sample.currX = x
sample.currY = y
sample.currZoom = calculateZoom(isIn, prevZoom)
sample.x = sample.x + (prevX - sample.currX) * (1 - prevZoom)
sample.y = sample.y + (prevY - sample.currY) * (1 - prevZoom)
printSamplePostion()
}
function calculateZoom(isIn, prevZoom) {
var newZoom = isIn ? prevZoom + 0.1 : prevZoom - 0.1
if (newZoom > mouseArea.maxZoom)
newZoom = mouseArea.maxZoom
if (newZoom < mouseArea.minZoom)
newZoom = mouseArea.minZoom
return newZoom
}
function printSamplePostion() {
console.log("== x: 500 y: 350 ======")
console.log("-- x: " + sample.x)
console.log("-- y: " + sample.y)
console.log("=======================")
}
}
}
}
Thanks in advance
I want to design a ruler as shown in the image below:
Which approach is the best possible way to design a ruler with these small and big lines(scale divisions) as shown in the image.
Also text and numbers will be added with the scale divisions.
There is one knob which i can slide from left to right and vice versa. Can this be achieved using Slider component?
Try using the QtQuick.Extras module it has a Gauge QML Type. For tick marks use the tickmark and minorTickmark properties from the GaugeStyle QML Type. Then add to this what you want.
import QtQuick 2.15
import QtQuick.Window 2.12
import QtQuick.Extras 1.4
Window {
visible: true
width: 640
height: 480
color: "gray"
x: (Screen.width - width) / 2
y: (Screen.height - height) / 2
Gauge {
minimumValue: 0
value: 50
maximumValue: 100
anchors.centerIn: parent
orientation: Qt.Horizontal
}
}
Item {
width: parent.width
height: 8
anchors.bottom: parent.bottom
property real spacing: 33.3
Repeater {
model: parent.width / (parent.spacing + 1) - 1
delegate: Rectangle {
x: index * (rowLayout.spacing + 4)
y: parent.height - height
implicitWidth: major ? 2 : 1
implicitHeight: major ? 18 : 9
color: "grey"
readonly property bool major: index % 6 == 0
}
}
}
You can greatly customize the Slider in QtQuick.Controls 2.x. Here is a start for you
Slider {
id: control
width: parent.width
height: 50
background: Canvas {
x: control.leftPadding + (control.horizontal ? handle.width * 0.5 : (control.availableWidth - width) / 2)
y: control.topPadding + (control.horizontal ? (control.availableHeight - height) / 2 : 0)
implicitWidth: control.horizontal ? 200 : control.width
implicitHeight: control.horizontal ? control.height : 200
width: control.horizontal ? control.availableWidth - handle.width : implicitWidth
height: control.horizontal ? implicitHeight : control.availableHeight - handle.height
onPaint: {
var ctx = getContext("2d")
ctx.clearRect(0, 0, width, height)
ctx.strokeStyle = Qt.rgba(1, 0, 0, 0.4)
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(width, 0)
ctx.lineTo(width, height)
ctx.lineTo(0, height)
ctx.lineTo(0, 0)
for(var x=0;x < width;x += 40) //assuming 40 is unit of measurement
{
ctx.moveTo(x, height)
if(x % 120 == 0)
ctx.lineTo(x, height * 0.3)
else
ctx.lineTo(x, height * 0.6)
}
ctx.stroke();
}
}
handle: Canvas {
id: handle
x: control.leftPadding + (control.horizontal ? control.visualPosition * (control.availableWidth - width) : 0)
y: control.topPadding + (control.horizontal ? 0 : control.visualPosition * (control.availableHeight - height))
implicitWidth: control.height
implicitHeight: 28
onPaint: {
var ctx = getContext("2d")
ctx.clearRect(0, 0, width, height)
ctx.strokeStyle = Qt.rgba(1, 0, 0, 0.4)
ctx.fillStyle = "white"
ctx.lineWidth = 1
ctx.beginPath()
ctx.moveTo(0, 0)
ctx.lineTo(width, 0)
ctx.lineTo(width, height - width * 0.5)
ctx.lineTo(width * 0.5, height)
ctx.lineTo(0, height - width * 0.5)
ctx.lineTo(0, 0)
ctx.fill()
ctx.stroke()
}
}
}
Part of the code was taken from QtQuick.Controls.Material. I only tested the horizontal slider, vertical will fail (especially the handle)
Is there any way to draw half dashed circle in QML? I drawn half circle in this way
var Circle = getContext("2d");
Circle.save();
var CircleGradient =
Circle.createLinearGradient(parent.width/4,parent.height,parent.width/4,0);
CircleGradient.addColorStop(0, firstGradientPoint);
CircleGradient.addColorStop(1, secondGradientPoint);
Circle.clearRect(0, 0, parent.width, parent.height);
Circle.beginPath();
Circle.lineCap = "round";
Circle.lineWidth = 10;
Circle.strokeStyle = CircleGradient
Circle.arc(parent.width/2, parent.height/2, canvas.radius - (Circle.lineWidth / 2), Math.PI/2, canvas.Value);
Circle.stroke();
Circle.restore();
Result
But how can I make it dashed like this.
I need
I know that this question is very outdated, but it might help someone. You can use Qt Quick Shapes (since Qt 5.10) to render what you want. It's not copy-paste code, but more of an approach:
Shape {
ShapePath {
id: shapePath
strokeColor: "black"
strokeStyle: ShapePath.DashLine
dashPattern: [6, 8]
fillColor: "transparent"
PathArc {
x: 0
y: radiusX + radiusY
radiusX: 100
radiusY: 100
useLargeArc: true
}
}
}
PathArc documentation has pretty much everything you need. Here are some more Shape Examples.
I know QML little bit but never coded.
But you can solve your problem by logic.
Here is the logic- Code below is pseudo, will not work but will give you an idea.
Draw the small arcs in loop with spaces in between.
//DECLARE YOUR ANGLES START AND END
startAngle = 0.0;
endAngle = pi/20;// 10 ARCS AND 10 SPACES
while (q++ < 10){
Circle.arc(parent.width/2, parent.height/2, canvas.radius - (Circle.lineWidth / 2), startAngle, endAngle, canvas.Value)
//LEAVE SPACE AND CREATE NEW START AND END ANGLE.
startAngle = endAngle + endAngle;
endAngle = startAngle + endAngle;
}
I'm creating a star field in a three.js scene. The code to generate the random positions of the stars is below. When the stars are rendered and the camera is pulled back enough from the center of the scene, there are a couple of visible "empty" tracks in the placement of the stars.
I'm assuming it has to do with the math in the _addStars method. Can anyone help me to tighten up the placement of the stars throughout the entire canvas?
Note: The canvas I have to work with is somewhere around an 8:1 ratio height:width. So just repositioning the camera is not an option.
UPDATE: I've added a fiddle to demonstrate the issue: https://jsfiddle.net/scottwatkins/5zjoLLpx/5/
/** Method to generate the stars and place them in the particle system */
_addStars: function () {
var starColors = [];
var starGeometry = new THREE.Geometry();
starGeometry.colors = starColors;
for (var i = 0; i < this.totalStars; i++) {
var x = 120 - Math.random() * 1040;
var y = 480 - Math.random() * 1040;
var z = 0 - Math.random() * 1040;
starGeometry.vertices.push( new THREE.Vector3( x, y, z ) );
var starColor = new THREE.Color(0xffffff);
starColor.setRGB(
.8 + Math.random() * .2,
.8 + Math.random() * .2,
.8 + Math.random() * .2);
starColors.push(starColor)
}
var starMaterial = new THREE.PointsMaterial( {
size: 2.0,
map: this.starTexture,
depthTest: false,
depthWrite: false,
blending: THREE.AdditiveBlending,
transparent : true,
vertexColors: true
} );
this.particleSystem = new THREE.Points( starGeometry, starMaterial );
this.scene.add(this.particleSystem);
}
It appears to be caused by Math.random() seems to work with THREE.Math.random16()
var x = 120 - THREE.Math.random16() * 1040;
var y = 480 - THREE.Math.random16() * 1040;
var z = 0 - THREE.Math.random16() * 1040;
Here's what it says in the docs for THREE.Math.random16():
Random float from 0 to 1 with 16 bits of randomness.
Standard Math.random() creates repetitive patterns when applied over larger space.
Updated fiddle: here