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;
}
Related
I'm following a tutorial by George Francis in the tutorial after some initial examples he shows how to use image data to create random layouts.
I'm trying to work out how to get the image data from a canvas created using paper.js, as I need to get the rgb values from each individual pixel on the canvas
Link to codepen
Unknowns:
Do I need to use the rasterize() method on the shape I've created?
Currently I am attempting the following:
// create a white rectangle the size of the view (not sure I need this but doing it so that there are both white and black pixels)
const bg = new paper.Path.Rectangle({
position: [0,0],
size: view.viewSize.multiply(2),
fillColor: 'white'
})
// create a black rectangle smaller than the view size
const shape = new paper.Path.RegularPolygon({
radius: view.viewSize.width * 0.4,
fillColor: 'black',
strokeColor: 'black',
sides: 4,
position: view.center
})
// So far so good shapes render as expected. Next put the shapes in a group
const group = new paper.Group([bg,shape])
// rasterise the group (thinking it needs to be rasterized to get the pixel data, but again , not sure?)
group.rasterize()
// iterate over each pixel on the canvas and get the image data
for(let x = 0; x < width; x++){
for(let y = 0; y < height; y++){
const { data } = view.context.getImageData(x,y,1,1)
console.log(data)
}
}
Expecting: To get an array of buffers where if the pixel is white it would give me
Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
byteLength: 4, byteOffset: 0, length: 4]
0: 255
1: 255
2: 255
//(not sure if the fourth index represents (rgb'a')?
3: 255
buffer:
ArrayBuffer(4)
byteLength: 4
byteOffset: 0
length: 4
Symbol(Symbol.toStringTag): (...)
[[Prototype]]: TypedArray
and if the pixel is black I should get
Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
byteLength: 4, byteOffset: 0, length: 4]
0: 0
1: 0
2: 0
3: 0
buffer:
ArrayBuffer(4)
byteLength: 4
byteOffset: 0
length: 4
Symbol(Symbol.toStringTag): (...)
[[Prototype]]: TypedArray
i.e either 255,255,255 (white) or 0,0,0(black)
Instead, all the values are 0,0,0?
I think that your issue was that at the time where you are getting the image data, your scene is not yet drawn to the canvas.
In order to make sure it's drawn, you just need to call view.update().
Here's a simple sketch demonstrating how it could be used.
Note that you don't need to rasterize your scene if you are using the Canvas API directly to manipulate the image data. But you could also rasterize it and take advantage of Paper.js helper methods like raster.getPixel().
// Draw a white background (you effectively need it otherwise your default
// pixels will be black).
new Path.Rectangle({
rectangle: view.bounds,
fillColor: 'white'
});
// Draw a black rectangle covering most of the canvas.
new Path.Rectangle({
rectangle: view.bounds.scale(0.9),
fillColor: 'black'
});
// Make sure that the scene is drawn into the canvas.
view.update();
// Get the canvas image data.
const { width, height } = view.element;
const imageData = view.context.getImageData(0, 0, width, height);
// Loop over each pixel and store all the different colors to check that this works.
const colors = new Set();
const length = imageData.data.length;
for (let i = 0; i < length; i += 4) {
const [r, g, b, a] = imageData.data.slice(i, i + 4);
const color = JSON.stringify({ r, g, b, a });
colors.add(color);
}
console.log('colors', [...colors]);
I'm trying to calculate position 2 in the illustration below.
I know position 1 from
this._end = new THREE.Vector3()
this._end.copy( this._rectanglePos )
.sub( this._circlePos ).setLength( 1.1 ).add( this._circlePos )
Where the radius of the circle is 2.2
I'm now trying to work out a position on the edge of the rectangle along this intersect.
I've found an equation written in pseudo code which I turned into this function
function positionAtEdge(phi, width, height){
let c = Math.cos(phi)
let s = Math.sin(phi)
let x = width/2
let y = height/2
if (width * Math.abs(s) < height * Math.abs(c)){
x -= Math.sign(c) * width / 2
y -= Math.tan(phi) * x
}
else{
y -= Math.sign(s) * height / 2
x -= cot(phi) * y
}
return {x, y, z: 0}
function cot(aValue){
return 1/Math.tan(aValue);
}
}
And this kind of works for the top of the rectangle but starts throwing crazy values after 90 degrees. Math didn't have a coTan function so I assumed from a little googling they meant this cot function.
Anyone know an easier way of finding this position 2 or how to convert this function into something useable.
This is a general purpose solution, which is independent of their relative position.
Live Example (JSFiddle)
function getIntersection( circle, rectangle, width, height ) {
// offset is a utility Vector3.
// initialized outside the function scope.
offset.copy( circle ).sub( rectangle );
let ratio = Math.min(
width * 0.5 / Math.abs( offset.x ),
height * 0.5 / Math.abs( offset.y )
);
offset.multiplyScalar( ratio ).add( rectangle );
return offset;
}
You don't need any transcendental functions for this.
Vsb = (Spherecenter - rectanglecenter)
P2 = rectanglecenter + ((vsb * rectangleheight * .5) / vsb.y)
I'm writing an Apple Watch app and I want to create a list just like this one in the Settings menu where the rows in the middle of the list are rectangles whereas the top and bottom are rounded rectangles. Is this a type of List style?
It is possible to do, though it requires using GeometryReader to handle drawing the rounded corners.
There is a great post by swiftui-lab.com that explains how to make the certain corners of a view rounded. Here is the code.
struct RoundedCorners: View {
var color: Color = .blue
var tl: CGFloat = 0.0
var tr: CGFloat = 0.0
var bl: CGFloat = 0.0
var br: CGFloat = 0.0
var body: some View {
GeometryReader { geometry in
Path { path in
let w = geometry.size.width
let h = geometry.size.height
// Make sure we do not exceed the size of the rectangle
let tr = min(min(self.tr, h/2), w/2)
let tl = min(min(self.tl, h/2), w/2)
let bl = min(min(self.bl, h/2), w/2)
let br = min(min(self.br, h/2), w/2)
path.move(to: CGPoint(x: w / 2.0, y: 0))
path.addLine(to: CGPoint(x: w - tr, y: 0))
path.addArc(center: CGPoint(x: w - tr, y: tr), radius: tr, startAngle: Angle(degrees: -90), endAngle: Angle(degrees: 0), clockwise: false)
path.addLine(to: CGPoint(x: w, y: h - br))
path.addArc(center: CGPoint(x: w - br, y: h - br), radius: br, startAngle: Angle(degrees: 0), endAngle: Angle(degrees: 90), clockwise: false)
path.addLine(to: CGPoint(x: bl, y: h))
path.addArc(center: CGPoint(x: bl, y: h - bl), radius: bl, startAngle: Angle(degrees: 90), endAngle: Angle(degrees: 180), clockwise: false)
path.addLine(to: CGPoint(x: 0, y: tl))
path.addArc(center: CGPoint(x: tl, y: tl), radius: tl, startAngle: Angle(degrees: 180), endAngle: Angle(degrees: 270), clockwise: false)
}
.fill(self.color)
}
}
}
We can then make a small example. Taking an array of items we can create a List. We will need the indices of the items so that we can tell which item is first (index of 0) and last (index of count - 1). The .listRowBackground modifier allows us to set the background of our rows so we will use that to set the view on it.
We create a helper function createRoundedCorners which will takes the index of the row and the number of rows that exist. This returns a RoundedCorrners view that the top left and right corners of the first row to be rounded and similarly the bottom left and right corners of the end row.
struct ContentView: View {
let items = ["Within 2 Minutes of Last Use",
"Within 1 Hour of Last Use",
"Always"]
var body: some View {
List {
ForEach(0..<items.count) { index in
Text(self.items[index]).font(.system(size: 24))
.listRowBackground(self.createRoundedCorners(at: index, count: self.items.count))
}
}
}
/// This function creates rounded corners for the first and last items in an array
func createRoundedCorners(at index: Int, count: Int) -> RoundedCorners {
switch index {
case 0:
return RoundedCorners(color: .blue, tl: 15, tr: 15, bl: 0, br: 0)
case (count - 1):
return RoundedCorners(color: .blue, tl: 0, tr: 0, bl: 15, br: 15)
default:
return RoundedCorners(color: .blue, tl: 0, tr: 0, bl: 0, br: 0)
}
}
}
This gives the following results
List at the top:
List at the bottom:
No, in SwiftUI Lists are very limited for configurations.
I recommend you to try making your own custom List-like view using ScrollView.
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()
}
}
}
I am trying to use unit/normal vector based gradients in html5 canvas element and transform them afterwards for the desired results. However, I seem to figure troubles which might be because of my lack of math. I am trying to create a simple linear gradient going from 0,0 to 1,0 (i.e. a simple unit gradient going from left to right). Afterwards, I transform the canvas for scaling, rotating and moving the gradient. However, when for example giving a rotation value of 45DEG, the actual gradient gets painted wrong. The right bottom corner has way to much black that is, the gradient seems to be not "big" enough. Here's my code:
var rect = {x: 0, y: 0, w: 500, h: 500};
var rotation = 45 * Math.PI/180;
var sx = 1;
var sy = 1;
var tx = 0;
var ty = 0;
var radial = false;
// Create unit vector 0,0 1,1
var grd = radial ? ctx.createRadialGradient(0, 0, 0, 0, 0, 0.5) : ctx.createLinearGradient(0, 0, 1, 0);
grd.addColorStop(0, 'black');
grd.addColorStop(0.1, 'lime');
grd.addColorStop(0.9, 'yellow');
grd.addColorStop(1, 'black');
// Add our rectangle path before transforming
ctx.beginPath();
ctx.moveTo(rect.x, rect.y);
ctx.lineTo(rect.x + rect.w, rect.y);
ctx.lineTo(rect.x + rect.w, rect.y + rect.h);
ctx.lineTo(rect.x, rect.y + rect.h);
ctx.closePath();
// Rotate and scale unit gradient
ctx.rotate(rotation);
ctx.scale(sx * rect.w, sy * rect.h);
ctx.fillStyle = grd;
// Fill gradient
ctx.fill();
And here's the fiddle to try it out:
http://jsfiddle.net/4GsCE/1/
Curious enough, changing the unit linear gradient vector to a factor of about 1.41 makes the gradient look right:
ctx.createLinearGradient(0, 0, 1.41, 0)
Which can be seen in this fiddle:
http://jsfiddle.net/4GsCE/2/
But I couldn't figure how to calculate that factor?
Since you want to use normalized gradients, you have to decide how to normalize. Here you choose to center the gradient, and to have its (x,y) in the [-0.5, 0.5 ] range.
First issue is that the linear gradient is not centered, it's in the [0, 1.0] range.
Normalize them the same way :
var linGrd = ctx.createLinearGradient(-0.5, 0, 0.5, 0);
Second issue is that you must translate to the center of your figure, then scale, then draw in a normalized way.
Meaning you must use same coordinate system as your gradients.
Since you were both drawing a shape having (w,h) as size AND using a scale of (w,h), you were drawing a ( ww, hh ) sized rect.
Correct draw code is this one :
function drawRect(rect, fill) {
ctx.save();
// translate to the center of the rect (the new (0,0) )
ctx.translate(rect.x + rect.w / 2, rect.y + rect.h / 2);
// Rotate
ctx.rotate(rotation);
// scale to the size of the rect
ctx.scale(rect.w, rect.h);
// ...
ctx.fillStyle = fill;
// draw 'normalized' rect
ctx.fillRect(-0.5, -0.5, 1, 1);
ctx.restore();
}
Notice that by default the radialGradient will end at a distance of 0.5, meaning, if you are filling a rect, that it will fill the corners with the last color of the gradient. Maybe you want the corners to end the gradient.
In that case, you want to have the gradient to reach its value at a distance of :
sqrt ( 0.5*0.5 + 0.5*0.5 ) = 0.7 ( pythagore in the normalized circle)
So you'll define your normalized gradient like :
var fullRadGrd = ctx.createRadialGradient(0, 0, 0, 0, 0, 0.7) ;
http://jsfiddle.net/gamealchemist/4GsCE/4/