Compose draw autoresizable text that always inside defined rectangle using canvas - android-jetpack-compose

As defined in the image, xAxis's texts are going off from the blue rectangle below the xAxis.
Is it possible to make the text auto resizable to always fit inside the defined rectangle inrespect of defined textSize?
val xPoints: List<String> = listOf(
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
)
Canvas(
modifier = modifier
.fillMaxWidth()
) {
val rectWidth = (size.width - leftPadding - rightPadding) / xPoints.size
//Draw x-Axis Text
xPoints.forEachIndexed { index, text ->
val offset = Offset(
0f + leftPadding + (index * rectWidth) + (rectWidth / 2),
graphHeight - (bottomPadding / 2)
)
val rect = Rect(offset, rectWidth / 2)
drawRect(
color = Color.Red,
Offset(0f + leftPadding + (index * rectWidth), graphHeight - bottomPadding)
, size = Size(rectWidth, bottomPadding)
)
rotate(degrees = -45f, rect.center) {
drawIntoCanvas {
it.nativeCanvas.drawText(
text,
rect.center.x,
rect.center.y,
xAxisTextPaint
)
}
}
}
}

One possibility would be to use SubcomposeLayout to first measure the composable in one slot, then calculate the desired scale factor and use it to wrap the composable in another composable with a scale modifier, and finally, layout the scaled composable in another layout slot.

Related

Gap between two drawings in Jetpack compose Canvas

I am trying to make a rectangle and an arc attached to rectangle's bottom. I have used the size provided by the drawScope to lay the drawings on screen but I am unable to get why there is an unnecessary gap between the two drawings even if the specified topLeft parameter of the arc is equal to the height of the rectangle drawn.
Canvas(
modifier = Modifier.fillMaxWidth()
.height(200.dp)
){
drawRect(
color = Color(0xFFEF3125),
topLeft = Offset(0f, 0f),
size = Size(this.size.width, this.size.height.times(0.75f))
)
drawArc(
color = Color(0xFFEF3125),
startAngle = 0f,
sweepAngle = 180f,
useCenter = false,
topLeft = Offset(
0f, this.size.height.times(0.75f)
)
)
}
There is a gap because when you draw an Arc you use a rectangle as refefrence but you draw arc to half of the this rectangle so you need to offset up as much as the half of the height of the rectangle that you draw arc into
#Composable
private fun ArcSample() {
Canvas(
modifier = Modifier.fillMaxWidth()
.height(200.dp)
){
drawRect(
color = Color(0xFFEF3125),
size = Size(this.size.width, this.size.height.times(0.75f)),
style = Stroke(4.dp.toPx())
)
drawArc(
color = Color(0xFFEF3125),
startAngle = 0f,
sweepAngle = 180f,
useCenter = false,
size = Size(size.width, size.height.times(.25f)),
topLeft = Offset(
0f, this.size.height.times(0.625f)
),
style = Stroke(4.dp.toPx())
)
}
}
I used Stroke style to show bounds, it's for demonstration.
#Composable
private fun ArcSample2() {
Canvas(
modifier = Modifier.fillMaxWidth()
.height(200.dp)
){
drawRect(
color = Color(0xFFEF3125),
size = Size(this.size.width, this.size.height.times(0.75f))
)
val sizeCoefficient = 0.25f
drawArc(
color = Color(0xFFEF3125),
startAngle = 0f,
sweepAngle = 360f,
useCenter = false,
size = Size(size.width, size.height.times(sizeCoefficient)),
topLeft = Offset(
0f, this.size.height.times(0.75f-sizeCoefficient/2f)
)
)
}
}

Swift 3 - Slider's pointer width

I want to make a label for a slider that shows the position of the pointer like this:
This is my code that's moving this label when the slider's pointer moves:
var sliderPointWidth : CGFloat = 32.0
#IBAction func sliderValueChanged( _ slider : UISlider ){
sliderLabel.text = "\( Int( roundf( slider.value ) ) )"
let leftMove = slider.frame.minX
let allRange = ( slider.frame.width - sliderPointWidth ) * CGFloat( slider.value / slider.maximumValue )
let middleOfSliferLabel = sliderLabel.frame.width / 2
let x = leftMove + sliderPointWidth / 2 + allRange - middleOfSliferLabel
sliderLabel.frame.origin = CGPoint( x: CGFloat(x) , y: sliderLabel.frame.minY )
}
But to make the label's middle x and the pointer's middle x the same, I need to know the width of this pointer. Fluently looking in this, I understood that it's about 32 points. But I don't know this value for other screens. Are there any methods in UISlider that can say this value?
You should retreive the size for the thumb with thumbRect:
let thumbSize = slider.thumbRect(forBounds: slider.bounds, trackRect: slider.trackRect(forBounds: slider.bounds), value: 0).size

Getting Pixel value in the image

I am calculating the RGB values of pixels in my captured photo. I have this code
func getPixelColorAtLocation(context: CGContext, point: CGPoint) -> Color {
self.context = createARGBBitmapContext(imgView.image!)
let data = CGBitmapContextGetData(context)
let dataType = UnsafePointer<UInt8>(data)
let offset = 4 * ((Int(imageHeight) * Int(point.x)) + Int(point.y))
var color = Color()
color.blue = dataType[offset]
color.green = dataType[offset + 1]
color.red = dataType[offset + 2]
color.alpha = dataType[offset + 3]
color.point.x = point.x
color.point.y = point.y
But I am not sure what this line means in the code.
let offset = 4 * ((Int(imageHeight) * Int(point.x)) + Int(point.y))
Any help??
Thanks in advance
Image is the set of pixels. In order to get the pixel at (x,y) point, you need to calculate the offset for that set.
If you use dataType[0], it has no offset 'cos it points to the place where the pointer is. If you used dataType[10], it would mean you took 10-th element from the beginning where the pointer is.
Due to the fact, we have RGBA colour model, you should multiply by 4, then you need to get what offset by x (it will be x), and by y (it will be the width of the image multiplied by y, in order to get the necessary column) or:
offset = x + width * y
// offset, offset + 1, offset + 2, offset + 3 <- necessary values for you
Imagine, like you have a long array with values in it.
It will be clear if you imagine the implementation of two-dimensional array in the form of one-dimensional array. It would help you, I hope.

Highcharts - spider web chart questions

I'm working with Highcharts spider web chart at the moment and wanted to see if I can do the following
How do I zoom polar charts? (or is it possible?)
How do I put background-color in each of the segment?
How do I zoom polar charts? (or is it possible?)
If you want a zoom like zoomType then no. zoomType is disabled for polar charts by highcharts-more.js. From source:
// Disable certain features on angular and polar axes
chart.inverted = false;
chartOptions.chart.zoomType = null;
How do I put background-color in each of the segment?
You can use math and Chart.renderer to create and fill paths to color the background of the segments. For example you might do it like this:
var colors = [ "pink", "yellow", "blue", "red", "green", "cyan", "teal", "indigo" ];
var parts = 6;
for(var i = 0; i < parts; i++) {
centerX = chart.plotLeft + chart.yAxis[0].center[0];
centerY = chart.plotTop + chart.yAxis[0].center[1];
axisLength = chart.yAxis[0].height;
angleOffset = -Math.PI/2;
angleSegment = Math.PI/(parts/2);
firstPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * i)));
firstPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * i)));
secondPointX = centerX + (axisLength * Math.cos(angleOffset + (angleSegment * (i+1))));
secondPointY = centerY + (axisLength * Math.sin(angleOffset + (angleSegment * (i+1))));
chart.renderer.path([
'M', centerX, centerY,
'L', firstPointX, firstPointY,
'L', secondPointX, secondPointY,
'Z'
]).attr({
fill: colors[i % colors.length],
'stroke-width': 1,
'opacity': 1
}).add();
}
As seen in this JSFiddle demonstration. You just have to match number of categories with the parts variable.

Highcharts tooltip always on right side of cursor

I want to show the tooltip on the right side of the cursor.
I looked in the documentation/examples but I can't find a way to force the tooltips to stay on the right side of the cursor.
Can anyone tell me how to do it?
With tooltip positioner I only can set a default position.
Tooltip positioner is much more than just default position. The function arguments contain info about your point position & tooltip dimensions, using which it should be fairly simple to position it to the right.
Highchart/stock allows you to define your alternate positioner as follows
tooltip:{
positioner:function(boxWidth, boxHeight, point){
...
}
}
Note that you have three arguments (boxWidth, boxHeight, point) at your disposal, these seem to be sufficient for most of the use cases to calculate a desired tooltip position. boxWidth and boxHeight are the width and height that your tooltip will require, hence you can use them for edge cases to adjust your tooltip and prevent it from spilling out of the chart or even worse getting clipped.
The default tooltip positioner that comes with highstock is as follows (Source)
/**
* Place the tooltip in a chart without spilling over
* and not covering the point it self.
*/
getPosition: function (boxWidth, boxHeight, point) {
// Set up the variables
var chart = this.chart,
plotLeft = chart.plotLeft,
plotTop = chart.plotTop,
plotWidth = chart.plotWidth,
plotHeight = chart.plotHeight,
distance = pick(this.options.distance, 12), // You can use a number directly here, as you may not be able to use pick, as its an internal highchart function
pointX = point.plotX,
pointY = point.plotY,
x = pointX + plotLeft + (chart.inverted ? distance : -boxWidth - distance),
y = pointY - boxHeight + plotTop + 15, // 15 means the point is 15 pixels up from the bottom of the tooltip
alignedRight;
// It is too far to the left, adjust it
if (x < 7) {
x = plotLeft + pointX + distance;
}
// Test to see if the tooltip is too far to the right,
// if it is, move it back to be inside and then up to not cover the point.
if ((x + boxWidth) > (plotLeft + plotWidth)) {
x -= (x + boxWidth) - (plotLeft + plotWidth);
y = pointY - boxHeight + plotTop - distance;
alignedRight = true;
}
// If it is now above the plot area, align it to the top of the plot area
if (y < plotTop + 5) {
y = plotTop + 5;
// If the tooltip is still covering the point, move it below instead
if (alignedRight && pointY >= y && pointY <= (y + boxHeight)) {
y = pointY + plotTop + distance; // below
}
}
// Now if the tooltip is below the chart, move it up. It's better to cover the
// point than to disappear outside the chart. #834.
if (y + boxHeight > plotTop + plotHeight) {
y = mathMax(plotTop, plotTop + plotHeight - boxHeight - distance); // below
}
return {x: x, y: y};
}
With all the above information, I think you have sufficient tools to implement your requirement by simply modifying the function to make float to right instead of the default left.
I will go ahead and give you the simplest implementation of positioning tooltip to right, you should be able to implement the edge cases based on the aftermentioned default tooltip positioner's code
tooltip: {
positioner: function(boxWidth, boxHeight, point) {
return {x:point.plotX + 20,y:point.plotY};
}
}
Read more # Customizing Highcharts - Tooltip positioning
The better solution to get your tooltip always on the right side of the cursor is the following:
function (labelWidth, labelHeight, point) {
return {
x: point.plotX + labelWidth / 2 + 20,
y: point.plotY + labelHeight / 2
};
}

Resources