Flutter bezier curves automatically deciding the control point - flutter

I want to draw a bezier curve between the given two locations (offsets) with the flutter. I can see the following function available to do so,
https://api.flutter.dev/flutter/dart-ui/Path/quadraticBezierTo.html
Here we have to specify start and end points those I already know. And also have to specify the control point (x1,y1). I there a way we can generate this control point? May be to have more control of the curve we can have a factor (that decides how far away this point from the baseline) and based on that factor we can generate the control point.
Please find my scretch below

You can use cubicTo function
https://api.flutter.dev/flutter/dart-ui/Path/cubicTo.html
void cubicTo (
double x1,
double y1,
double x2,
double y2,
double x3,
double y3
)
this creates a bezier curve from the baseline (x1,y1) to (x2,y2) with (x3,y3) as control point.
class HalfCirclePainter extends CustomPainter {
HalfCirclePainter({this.scrollOffset});
final double scrollOffset;
#override
void paint(Canvas canvas, Size size) {
Paint circle = new Paint()
..color = Colors.red
..style = PaintingStyle.fill;
Path topCirclePath = new Path()
..moveTo(0, 0)
..lineTo(0, scrollOffset < size.height ? scrollOffset : size.height)
..cubicTo(
20,
size.height,
size.width - 20,
size.height,
size.width,
scrollOffset < size.height ? scrollOffset : size.height,
)
..lineTo(size.width, 0)
..lineTo(0, 0)
..close();
canvas.drawPath(topCirclePath, circle);
}
}
This code draws a semi circle, I hope this will be helpful.

Thank you for #pskink to help me. As you mentioned your answer as a comment and the code is not that readable when displaying in the comments. Here I am listing the code as the answer. So it is easy to grasp for someone who looking into this.
void drawBezierLine(Canvas canvas, Offset start, Offset end){
Offset middle = end/2 + start/2;
Offset baselineVector = end - start;
Offset controlPoint = middle + Offset(-baselineVector.dy, baselineVector.dx);
Paint paint = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 8.0;
var path = Path();
path.moveTo(start.dx, start.dy);
path.quadraticBezierTo(controlPoint.dx, controlPoint.dy, end.dx, end.dy);
canvas.drawPath(path, paint);
}
So here we can give start and end offsets to the function and it will draw the Bezier curve based on it. The hight of the Bezier(factor) curve corresponds to the length of the base line.

Related

How to fill a rectangle that has one side curved in flutter?

I'm trying to fill a curved rectangle, but it is overflowing from the curved part, have used paths to create this shape. Any suggestions for how to achieve this.
Or is there any way to create a rectangle with 4 offset values, whose one side is curved like this?
I have offset values of two straight line (x1,y1), (x2,y2), (x3,y3) and (x4,y4). Any references?
final path = Path();
path.moveTo(87.75, 10.06);
path.lineTo(263.25, 80.06);
path.lineTo(263.25, 359.0);
path.lineTo(87.75, 359.0);
canvasWrapper.drawPath(path, _barAreaPaint..color = Colors.red);
Try drawing an arc
Rect rect = Rect.fromLTWH(0.0, 0.0, size.width, size.height);
path.arcTo(
rect,
0.0,
pi / 2,//adjust radians accordingly
false);
}

flutter) How do I make arrow lines with canvas?

I want to put an arrow at the end (or on both sides) of a line drawn with a drawLine() of canvas.
Like this
Can I know how to do this?
That's actually not that hard to do - there's just a little math involved.
Let's have a line and paint set up like this:
final paint = Paint()
..color = Colors.black
..strokeWidth = 2;
final p1 = Offset(50, 50);
final p2 = Offset(250, 150);
canvas.drawLine(p1, p2, paint);
this will give us:
Now if we think of this line as a vector, we remember a vector has a direction. To get the direction we must find the angle between the start and the end of the line. This can be done using the atan2 function of dart's math library.
So import the math library
import 'dart:math' as math;
and add the following lines
final dX = p2.dx - p1.dx;
final dY = p2.dy - p1.dy;
final angle = math.atan2(dY, dX);
Afterwards let's determine a size and an angle for the arrow we're about to put at the end of the line:
final arrowSize = 15;
final arrowAngle= 25 * math.pi / 180;
Everything that's left is actually drawing the arrow. This is basically done by drawing a triangle at the proper angle the line is facing using a path.
final path = Path();
path.moveTo(p2.dx - arrowSize * math.cos(angle - arrowAngle),
p2.dy - arrowSize * math.sin(angle - arrowAngle));
path.lineTo(p2.dx, p2.dy);
path.lineTo(p2.dx - arrowSize * math.cos(angle + arrowAngle),
p2.dy - arrowSize * math.sin(angle + arrowAngle));
path.close();
canvas.drawPath(path, paint);
Which will finally give us:

Flutter CustomPaint Moving a circle along a path

I'm learning CustomPaint by making my own line chart. I have already made the chart itself so now i'm making the things around it. One them is a trackball/line whenever you pan. So far i've made the dashed line from top to bottom, and it works perfectly, now i want to make the the ball that's gonna follow along with the line. My problem is getting the ball and dashed line to always be in sync. right now it's the dashed line that follows my finger and the ball that does'nt match up. I found this answer on stack overflow but it's not working quite right for me. This video is how it currently functions with the following code,
where dragX is the current x-axis position of your finger in pixels.
void drawTrackballLine(Canvas canvas, Size size) {
double dashHeight = 5;
double dashSpace = trackBallOptions.dashSpacing;
double y = 0;
final paint = Paint()
..color = trackBallOptions.color
..strokeWidth = trackBallOptions.strokeWidth;
while (y < size.height) {
canvas.drawLine(
Offset(dragX, y),
Offset(dragX, y + dashHeight),
paint,
);
y += dashSpace + dashHeight;
}
}
void drawTrackball(Canvas canvas, Size size, Path path) {
final pathMetric = path.computeMetrics().elementAt(0);
final value = pathMetric.length * (dragX / size.width);
final pos = pathMetric.getTangentForOffset(value);
canvas.drawCircle(pos!.position, 5, Paint());
}

In Flutter, is it possible to draw an image with a replaced color and opacity?

I have a grey image. I want to draw it using a color but with opacity to a canvas. By using Color Filter, I was able to draw it on the canvas by replacing the color. If I adjust the alpha value, it changes image opacity (partial grey/red). The following code snippet shows how to draw a grey image as red onto a canvas. But how can I draw the red image as transparent to the canvas?
static void drawBrushDetail(Canvas canvas, double cx, double cy, double brushSize, double imageAngleRad) {
canvas.translate(cx + brushSize / 2, cy + brushSize / 2);
ui.Image bt = imgBrushTip; //this is a gray image
Color selectedColor = getDrawColor(cx,cy);
final _paint = Paint();
var alphaVal=255;
//if I change alphaVal to 100, the final draw looks more gray instead of red with 100 Alpha
_paint.colorFilter = ColorFilter.mode(selectedColor.withAlpha(alphaVal), BlendMode.srcATop);
if (imageAngleRad != 0.0) canvas.rotate(imageAngleRad);
canvas.translate(-brushSize / 2, -brushSize / 2);
// draw image with specified brushSize
canvas.drawImageRect(bx, Rect.fromLTRB(0, 0, bt.width, bt.height),
Rect.fromLTRB(0, 0, brushSize, brushSize), _paint);
//traslate back, don't want to use save and restore layer since it's expensive
canvas.translate(brushSize / 2, brushSize / 2);
if (imageAngleRad != 0.0) canvas.rotate(-imageAngleRad);
canvas.translate(-cx - brushSize / 2, -cy - brushSize / 2);
}
See the image below. The above code will paint the image with solid red color. (image pointed by the red arrow). But I want the result to be an image with red color and transparent. Since in Flutter, there is no API to set alpha for canvas, don't know if it is possible.
I figured it out but the credit goes to pskink
I only need to draw one image(gray color) but I need to do the followings:
Resize and rotate the image.
Replace the gray image with red color.
Draw the image (now in red) with opacity.
Here is the code snippet.
static void drawBrushDetail1(Canvas canvas, double cx, double cy, double size) {
final _paint = Paint();
const opacity = 0.5;
_paint.color = Color.fromRGBO(0, 0, 0, opacity);
ui.Image bx = uiImg[0];
double scale = size / 60; //size is brush tip size and 60x60 is gray image size
canvas.drawAtlas(
bx,
[
RSTransform.fromComponents(
rotation: imageAngleRad,
scale: scale,
anchorX: 30,
anchorY: 30,
translateX: cx,
translateY: cy)
],
[
/* The size of gray image is 60 x 60 */
const Rect.fromLTWH(0, 0, 60, 60)
],
[Colors.red],
BlendMode.dstIn, /*This is replace the gray image wiht red color
null /* No need for cullRect */,
_paint); //_paint will paint the red image in 0.5 opacity
}

How to give a shadow to a Custom Path arc in Flutter?

As the question says ...... I need to add a shadow to an arc drawn by custom paint. I tried our the drawShadow() method like this :
Path a = Path()..arcTo(Rect.fromCircle(center: center, radius: pieRadius), -(pi / 2) + val1 + val3 + val4 ,val2, false);
canvas.drawCircle(center,pieRadius/* * 0.4*/, outerArc);
canvas.drawPath(a, innerArc1);
canvas.drawShadow(a, Colors.black, 3, true);
But this results in a shadow like this :
But I want a shadow equally surrounding the arc just like the boxShadow property of Container.
In this manner (The area between blue marking and arc should be under shadow).
Thankyou in advance for help.
I had the same issue. After some experimentation, the best solution I found was to draw two arcs, one for the arc itself and another underneath for the shadow. The shadow arc has a small offset, and can have its own paint, with a MaskFilter.blur.
Paint arcPaint = Paint()
..color = Colors.red,
..style = PaintingStyle.stroke
..strokeWidth = 15;
Paint shadowPaint = Paint()
..color = Colors.grey[600]
..style = PaintingStyle.stroke
..strokeWidth = 15
..maskFilter = MaskFilter.blur(BlurStyle.normal, 5);
Path path = Path();
path.addArc(Rect.fromCircle(center: center, radius: pieRadius), -(pi / 2) + val1 + val3 + val4, val2);
canvas.drawPath(path.shift(Offset(2, 2)), shadowPaint);
canvas.drawPath(path, arcPaint);