I am using InteractiveViewer to display a video. It is working properly.
But I want set initial zoom level.
Is there a way to do this?
return InteractiveViewer(
minScale: 1,
maxScale: 4,
constrained: false,
child: Container(
color: Colors.red,
width: 320,
height: 180,
child: widget.remoteVideoRenderer(),
),
);
I was able to do this, with varying levels of success, like so (credit to #pskink for pointing me in the right direction):
final viewTransformationController = TransformationController();
#override
void initState() {
final zoomFactor = 2.0;
final xTranslate = 300.0;
final yTranslate = 300.0;
viewTransformationController.value.setEntry(0, 0, zoomFactor);
viewTransformationController.value.setEntry(1, 1, zoomFactor);
viewTransformationController.value.setEntry(2, 2, zoomFactor);
viewTransformationController.value.setEntry(0, 3, -xTranslate);
viewTransformationController.value.setEntry(1, 3, -yTranslate);
super.initState();
}
#override
Widget build(BuildContext context) {
return InteractiveViewer(
transformationController: viewTransformationController,
minScale: 0.1,
maxScale: 6,
child: // ....
);
}
viewTransformationController.value is a 4x4 matrix that is applied to the view, it encodes the translations/rotations/scaling. The representation of these transformations in the matrix is probably documented somewhere (and/or just standard from projective/affine geometry), but I couldn't find them, so I just printed them out while zooming/panning until the roles of each entry became clear.
If you just set the zoom factor to 2 and don't translate, you'll be zoomed-in on the top-left corner of the widget.
Note that you cannot access the windows dimensions in the initState() method using MediaQuery.of(context), which is somewhat inconvenient if you want to e.g. zoom in on the middle of the window. I haven't found a good way to do that yet.
Base on Amos Joshua answer, if you want to always zoom center screen. I have this formula:
zoomFactor = 1, xTrans = 0, yTrans = 0
zoomFactor = 2, xTrans = w / 2, yTrans = h / 2
zoomFactor = 2.8, xTrans = (w / 2)(1 + 0.8), yTrans = (h / 2)(1 + 0.8)
Therefore:
zoomFactor = z, xTrans = (w / 2)(z - 1), yTrans = (h / 2)(z - 1)
Related
I am writing an extension / widgets for gnome-shell.So I want to show some information using text. Actually I want to put text in middle of the circle using pangoCairo but I can't figure out how can I put text using pangoCairo?
here is my code of drawing circles with a function to set text middle of the circles but the text is not showing in the middle.
draw_stuff(canvas, cr, width, height) {
cr.setSourceRGBA(1,1,1,0.2);
cr.save ();
cr.setOperator (Cairo.Operator.CLEAR);
cr.paint ();
cr.restore ();
cr.setOperator (Cairo.Operator.OVER);
cr.scale (width, height);
cr.setLineCap (Cairo.LineCap.BUTT);
//test
cr.setLineWidth (0.08);
cr.translate (0.5, 0.5);
cr.arc (0, 0, 0.4, 0, Math.PI * 2);
cr.stroke ();
cr.setSourceRGBA(1,1,1,0.8);
cr.rotate(-Math.PI/2);
cr.save();
cr.arc (0,0, 0.4, 0, this.currentR /100 * 2 * Math.PI);
cr.stroke();
cr.setSourceRGBA(1,1,1,0.9);
cr.save();
cr.arc (0,0, 0.25, 0, 2 * Math.PI);
cr.fill();
cr.moveTo(0, 0);
cr.setSourceRGBA(0,0,0,0.9);
cr.save();
this.text_show(cr, "WoW");
cr.restore();
return true; }
update() { this.currentR = 60;
this._canvas.connect ("draw", this.draw_stuff.bind(this));
this._canvas.invalidate();}
text_show(cr, showtext, font = "DejaVuSerif Bold 16") {
let pl = PangoCairo.create_layout(cr);
pl.set_text(showtext, -1);
pl.set_font_description(Pango.FontDescription.from_string(font));
PangoCairo.update_layout(cr, pl);
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2, 0);
PangoCairo.show_layout(cr, pl);
cr.relMoveTo(w / 2, 0);}
I dont know what's wrong here?
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2, 0);
You are placing your text something like 1000 pixels off the screen. get_pixel_size() is documented as:
pango_layout_get_size() returns the width and height scaled by PANGO_SCALE.
and PANGO_SCALE has the value 1024:
Thus, you need to change the code above to
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2048, 0);
Bonus points for not hardcoding the value of PANGO_SCALE and instead getting it from your Pango API bindings. However, for that I don't know enough about gnome-shell-extensions. Is this code in Javascript? Dunno.
I usually work with matrices and vectors, what I want to do is when I click on an element, zoom to it and it works, however I also want to change the scale, I want it a little closer, but when I change the value from 1.0 to 2.0 for example, it zooms but it moves to the other side of the controller and I don't know why.
I'm probably skipping a step to adjust the zoom but I can't find a way, any ideas?
CODE ZOOM
Future<void> _zoomToTheElement({
required double x,
required double y,
required MiniatureValues miniatureValues,
}) async {
final double itemHalfWidth = (80 / 2);
final double itemHalfHeight = (50 / 2);
/// Calculation of the position depending from the item and also
/// the half of the screen (Device.w(50))
///
/// x : [itemHalfWidth] means that is half the [width] of an element
/// y : [itemHalfHeight] means that is half the [height] of an element
/// y : [56] means that is the [AppBar]'s size
final double positionX = -(x) + (Device.w(50) - itemHalfWidth);
final double positionY = -(y) + (Device.h(50) - (56) - itemHalfHeight);
final Matrix4 _matrixEndPlan = Matrix4.compose(
vector.Vector3(positionX, positionY, 1),
vector.Quaternion.euler(0.0, 0.0, 0.0),
vector.Vector3(1.0, 1.0, 1.0),
)..scale(1.0); // here i want to write 2.0, but now does not respect the positionX and positionY
/// Animated
_matrixPlanAnimation = Matrix4Tween(
begin: planController.value,
end: _matrixEndPlan,
)
.chain(
CurveTween(
curve: Curves.easeInOutCirc,
),
)
.animate(_animationController);
}
IMAGE ZOOM WITH 1.0, but when i changed to 2.0 or to other scale, moves to another location
----- NEW EDIT WITH #pskink CODE -----
/// Calculation of the position depending from the item and also
/// the half of the screen (Device.w(50))
///
/// [56] means that is the [AppBar]'s size
final double positionX = -(x) + (Device.w(50));
final double positionY = -(y) + (Device.h(50) - (56));
final Matrix4 matrix1 = TransformEntry.fromOffsets(
translate: Offset(positionX, positionY),
scale: 1.0, // change this and goes to left side in the InteractiveViewer
rotation: 0,
anchor: Offset(itemHalfWidth, itemHalfHeight),
).matrix;
----- MY SOLUTION CODE -----
/// Calculation of the position depending from the item and also
/// the half of the screen (Device.w(50))
///
/// [itemHalfWidth] means that is half the [width] of an element
/// [itemHalfHeight] means that is half the [height] of an element
/// [56] means that is the [AppBar]'s size
const double scale = 1.0;
final double dx = (-x * scale) + (Device.w(50) - itemHalfWidth);
final double dy = (-y * scale) + (Device.h(50) - (56) - itemHalfHeight);
i have some codes which i need to reimplement with Cairo::RefPtrCairo::Context...It is a bit confusing since i could not find good example which uses pattern while we have Cairo::RefPtrCairo::Context instead of cairo_t..
Cairo::RefPtr<Cairo::Surface> surface =
Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, width, height);
Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface);
cairo_pattern_t *cp = cairo_pattern_create_radial(x_off, y_off, 0, x_off, y_off, cent_point_radius);
cairo_pattern_add_color_stop_rgba(cp, 0.0, 0.7, 0.7, 0.7, 0.8);
cairo_pattern_add_color_stop_rgba(cp, 1.0, 0.1, 0.1, 0.1, 0.8);
cairo_set_source(cr, cp);
How i can change "cp" to something which is recognizable for cr->set_resource().....cr used to be a cairo_t,but then i had to change it to Cairo::RefPtrCairo::Context
best regards
Since you've already decided to do it the C++ way, why not go all the way in?
// Create image surface.
Cairo::RefPtr <Cairo::Surface> refSurface =
Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32,
nWidth,
nHeight);
// Create Cairo context for the image surface.
Cairo::RefPtr <Cairo::Context> refContext =
Cairo::Context::create(refSurface);
// Create a radial gradient (pattern)
Cairo::RefPtr <Cairo::RadialGradient> refPattern =
Cairo::RadialGradient::create(x_off,
y_off,
0,
x_off,
y_off,
cent_point_radius);
// Add color stops to the pattern
refPattern->add_color_stop_rgba(0.0,
0.7,
0.7,
0.7,
0.8);
refPattern->add_color_stop_rgba(1.0,
0.1,
0.1,
0.1,
0.8);
// Set the pattern as the source for the context.
refContext->set_source(refPattern);
// Add a closed path and fill...
I am trying to create a venn diagram of two elements from a scatter plot widget on package fl_chart.
scatterPlot(screenSize) {
return ListView(children: [
SizedBox(
width: 500,
height: 500,
child: ScatterChart(
ScatterChartData(
scatterSpots:
_createSpots(determineCircleLocation(screenSize), screenSize),
minX: 0,
minY: 0,
maxX: screenSize.width,
maxY: screenSize.height,
borderData: FlBorderData(show: false),
...
I have this problem that whenever I resize my window the scatter circles are moving - so if at a full screen it appeared that they had no intersection, when I resize the screen it appears like it has an intersection.
I was trying to wrap my plot in a SizedBox thinking that would stop the resizing side effects but it didn't.
Here's a demo of the problem
Is there a way to prevent this from happening?
While writing this I thought that by changing maxX and maxY to be some constants could help, but it resulted even worst outcome.
because we set a direct size (no matter in which size is it running) this problem happens.
You can check the window size, then update the dot's radius based on the window size.
(I calculated a number, then multiplied with radiuses).
Watch the video below. and check the code here:
LayoutBuilder(
builder: (context, constraints) {
final size = constraints.biggest.shortestSide;
final radiusMultiplier = size / 800;
return Padding(
padding: const EdgeInsets.only(right: 18.0),
child: ScatterChart(
ScatterChartData(
scatterSpots: [
ScatterSpot(5, 7, radius: 60 * radiusMultiplier,),
ScatterSpot(8, 4, radius: 20 * radiusMultiplier,),
ScatterSpot(3, 8, radius: 70 * radiusMultiplier),
],
minX: 1,
maxX: 10,
minY: 1,
maxY: 10,
)
),
);
},
)
Is there any way to find color at a particular stop after creating a gradient.
Gradient g = SweepGradient(
center: FractionalOffset.center,
startAngle: 0.0,
endAngle: math.pi * 2,
colors: const <Color>[
Color(0xFF4285F4), // blue
Color(0xFF34A853), // green
Color(0xFFFBBC05), // yellow
Color(0xFFEA4335), // red
Color(0xFF4285F4), // blue again to seamlessly transition to the start
],
stops: const <double>[0.0, 0.25, 0.5, 0.75, 1.0],
)
Can I do something like g.colorAt(0.95) and get the color?
You can use something like Color.lerp(a, b, t) to linearly interpolate between two colors a and b at value t.
Using this we can make an extension on Gradient and create the method colorAt(double):
extension GradientColorAt on Gradient {
Color colorAt(double stop) {
assert(0 <= stop && stop <= 1);
final nextStopIndex = stops.indexWhere((element) => element >= stop);
if (nextStopIndex == -1) {
// happens when the last stop is not 1, where we return the last given color
return colors.last;
}
if (nextStopIndex == 0) {
// happens when the first stop is not 0, where we return the first given color
return colors.first;
}
return Color.lerp(
colors[nextStopIndex - 1],
colors[nextStopIndex],
(stop - stops[nextStopIndex - 1]) / (stops[nextStopIndex] - stops[nextStopIndex - 1]),
);
}
}
g.colorAt(0.95);
Now returns Color(0xFF6377CD) which is purplish.
Obviously this isn't going to be exact since gradient does not necessarily use lerp, but maybe you find it useful.