MPAndroidChart - make xaxis spacing relative - mpandroidchart
I have an MPAndroidChart line chart that shows dates on the xaxis & weights on the yaxis. As it is, the data points along the xaxis are all evenly spaced, but I'd like to make them relative to the time period between each weight entry. Try as I might, I can't find anything in the MPAndroidChart documentation that describes how to do this, or if it's even supported. Can anyone point me in the right direction here?
Here is the code to create the chart (with the result being even spacing):
private fun loadChart(weightList: List<FormattedWeight>) {
if (weightList.isEmpty()) { return }
val weights = ArrayList<Entry>()
val dates = ArrayList<Date>()
for (i in weightList.indices) {
val weight = weightList[i]
weights.add(Entry(i.toFloat(), weight.Weight!!.toFloat()))
dates.add(weight.WeightDate!!)
}
val dataSet = LineDataSet(weights, "Weights")
dataSet.mode = LineDataSet.Mode.LINEAR
val xaxis = binding.weightChart.xAxis
xaxis.granularity=2f
dataSet.color = Color.BLUE
dataSet.setCircleColor(Color.BLUE)
xaxis.position = XAxis.XAxisPosition.BOTTOM
xaxis.valueFormatter = object : ValueFormatter() {
private val mFormat = SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT)
override fun getFormattedValue(value: Float): String {
return try {
mFormat.format(dates[value.toInt()])
} catch (e: Exception) {
""
}
}
}
binding.weightChart.data = LineData(dataSet)
binding.weightChart.description.isEnabled = false
binding.weightChart.legend.isEnabled = false
binding.weightChart.invalidate()
binding.weightChart.moveViewToX(weights[weights.size - 1].y)
}
Even spacing
Relative spacing
I figured out that the xaxis spacing is set in
Entry(float x, float y)
If you set the x parameter to evenly incremented numbers (eg: 1, 2, 3...) you will get evenly spaced data points. However if you set them to varying increments (eg: 1, 1.3, 2.2...), you'll get relative spacing. So here's the code I used to achieve this:
val spacing = calcRelativeSpacing(weightList)
for (i in weightList.indices) {
val weight = weightList[i]
weights.add(Entry(spacing[i], weight.Weight!!.toFloat()))
dates.add(weight.WeightDate!!)
}
Note the call to calcRelativeSpacing(weightList):
private fun calcRelativeSpacing(entries: List<FormattedWeight>): ArrayList<Float> {
val startDate = Instant(entries.get(0).WeightDate)
val endDate = Instant(entries.get(entries.lastIndex).WeightDate)
val days = Days.daysBetween(startDate, endDate).days
val dayInterval = (days / entries.size).toFloat()
val spacing = ArrayList<Float>()
spacing.add(1f)
for (i in 1 until entries.size) {
spacing.add(spacing[i-1] + (Days.daysBetween(Instant(entries.get(i-1).WeightDate), Instant(entries.get(i).WeightDate)).days / dayInterval))
}
return spacing
}
The result:
Related
MPhiljay Barchart in android
In Barchart show x values from column belongs to one label more than 2 two but should be one. viewModel.reportMounthData.observe(this, Observer { reportModelList-> val barChart = binding.idBarChartMounth barChart.setScaleEnabled(false) val entries: ArrayList<BarEntry> = ArrayList() var i=0 reportModelList.forEach { reportModel-> entries.add(BarEntry(reportModel.YigilganBonus.toFloat(), i)) i++ } val bardataset = BarDataSet(entries, "Oylik maoshlar miqdori") val labels = ArrayList<String>() reportModelList.sortedBy { it.oy }.forEach { reportModel -> labels.add(mounthSalary(reportModel.oy)) } val data = BarData(labels, bardataset) barChart.data = data // set the data and list of labels into chart barChart.setDescription("\uD83D\uDEE0 Oylik maosh statistikasi") // set the description // bardataset.setColors(ColorTemplate.COLORFUL_COLORS) barChart.animateY(2500) }) enter image description here
it works perfectly until the values reach 6. If there are more than 6, a defect may occur naturally. they said that the main reason for this is that it will not fit on the screen
How can I get the width of a String when I use TextPaint() in Compose?
I use Code A to draw a text with Compose, and I can use either Code B or Code C to get the height of the string. I hope to get the width of the String var label="Hello, world!", how can I do ? Code A val paintText = TextPaint().also { it.isAntiAlias = true it.color = Color.Red.toArgb() it.textSize = 30f } var label="Hello, world!" it.nativeCanvas.drawText( label, x, y, paintText ) Code B val fontHeight = paintText.textSize Code C val fm= paintText.fontMetrics val fontHeight: Float = fm.descent - fm.ascent
How to try every possible permutation in Kotlin
fun main () { var integers = mutableListOf(0) for (x in 1..9) { integers.add(x) } //for or while could be used in this instance var lowerCase = listOf("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") var upperCase = listOf('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') println(integers) println(lowerCase) println(upperCase) //Note that for the actual program, it is also vital that I use potential punctuation val passwordGeneratorKey1 = Math.random()*999 val passwordGeneratorKey2 = passwordGeneratorKey1.toInt() var passwordGeneratorL1 = lowerCase[(Math.random()*lowerCase.size).toInt()] var passwordGeneratorL2 = lowerCase[(Math.random()*lowerCase.size).toInt()] var passwordGeneratorL3 = lowerCase[(Math.random()*lowerCase.size).toInt()] var passwordGeneratorU1 = upperCase[(Math.random()*upperCase.size).toInt()] var passwordGeneratorU2 = upperCase[(Math.random()*upperCase.size).toInt()] var passwordGeneratorU3 = upperCase[(Math.random()*upperCase.size).toInt()] val password = passwordGeneratorKey2.toString()+passwordGeneratorL1+passwordGeneratorL2+passwordGeneratorL3+passwordGeneratorU1+passwordGeneratorU2+passwordGeneratorU3 println(password) //No, this isn't random, but it's pretty close to it //How do I now run through every possible combination of the lists //lowerCase, integers, and upperCase? } How do I run through every possible permutation to eventually solve for the randomly generated password? This is in Kotlin.
I think you should append all the lists together and then draw from it by random index, this way you ensure that position of numbers, lower cases and uppercases is random too. Also you don't need to write all the characters, you can use Range which generates them for you. fun main() { val allChars = mutableListOf<Any>().apply { addAll(0..9) // creates range from 0 to 9 and adds it to a list addAll('a'..'z') // creates range from a to z and adds it to a list addAll('A'..'Z') // creates range from A to Z and adds it to a list } val passwordLength = 9 val password = StringBuilder().apply { for (i in 0 until passwordLength) { val randomCharIndex = Random.nextInt(allChars.lastIndex) // generate random index from 0 to lastIndex of list val randomChar = allChars[randomCharIndex] // select character from list append(randomChar) // append char to password string builder } }.toString() println(password) } Even shorter solution can be achieved using list methods fun main() { val password = mutableListOf<Any>() .apply { addAll(0..9) // creates range from 0 to 9 and adds it to a list addAll('a'..'z') // creates range from a to z and adds it to a list addAll('A'..'Z') // creates range from A to Z and adds it to a list } .shuffled() // shuffle the list .take(9) // take first 9 elements from list .joinToString("") // join them to string println(password) }
As others pointed out there are less painful ways to generate the initial password in the format of: 1 to 3 digits followed by 3 lowercase characters followed by 3 uppercase characters. To brute force this password, you will need to consider all 3-permutations of "a..z" and all 3-permitations of "A..Z". In both cases the number of such 3-permutations is 15600 = 26! / (26-3)!. In worst case you will have to examine 1000 * 15600 * 15600 combination, half of this on the average. Probably doable in a few hours with the code below: import kotlin.random.Random import kotlin.system.exitProcess val lowercaseList = ('a'..'z').toList() val uppercaseList = ('A'..'Z').toList() val lowercase = lowercaseList.joinToString(separator = "") val uppercase = uppercaseList.joinToString(separator = "") fun genPassword(): String { val lowercase = lowercaseList.shuffled().take(3) val uppercase = uppercaseList.shuffled().take(3) return (listOf(Random.nextInt(0, 1000)) + lowercase + uppercase).joinToString(separator = "") } /** * Generate all K-sized permutations of str of length N. The number of such permutations is: * N! / (N-K)! * * For example: perm(2, "abc") = [ab, ac, ba, bc, ca, cb] */ fun perm(k: Int, str: String): List<String> { val nk = str.length - k fun perm(str: String, accumulate: String): List<String> { return when (str.length == nk) { true -> listOf(accumulate) false -> { str.flatMapIndexed { i, c -> perm(str.removeRange(i, i + 1), accumulate + c) } } } } return perm(str, "") } fun main() { val password = genPassword().also { println(it) } val all3LowercasePermutations = perm(3, lowercase).also { println(it) }.also { println(it.size) } val all3UppercasePermutations = perm(3, uppercase).also { println(it) }.also { println(it.size) } for (i in 0..999) { println("trying $i") for (l in all3LowercasePermutations) { for (u in all3UppercasePermutations) { if ("$i$l$u" == password) { println("found: $i$l$u") exitProcess(0) } } } } }
Kotlin decomposing numbers into powers of 2
Hi I am writing an app in kotlin and need to decompose a number into powers of 2. I have already done this in c#, PHP and swift but kotlin works differently somehow. having researched this I believe it is something to do with the numbers in my code going negative somewhere and that the solution lies in declaring one or more of the variable as "Long" to prevent this from happening but i have not been able to figure out how to do this. here is my code: var salads = StringBuilder() var value = 127 var j=0 while (j < 256) { var mask = 1 shl j if(value != 0 && mask != 0) { salads.append(mask) salads.append(",") } j += 1 } // salads = (salads.dropLast()) // removes the final "," println("Salads = $salads") This shoud output the following: 1,2,4,8,16,32,64 What I actually get is: 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216,33554432,67108864,134217728,268435456,536870912,1073741824,-2147483648, Any ideas?
This works for the one input that you specified, at the very least: fun powersOfTwo(value :Long): String { val result = ArrayList<String>() var i = 0 var lastMask = 0 while (lastMask < value) { val mask = 1 shl i if (value != 0.toLong() && mask < value) { result.add(mask.toString()) } lastMask = mask i += 1 } return result.joinToString(",") } Ran it in a unit test: #Test fun addition_isCorrect() { val result = powersOfTwo(127) assertEquals("1,2,4,8,16,32,64", result) } Test passed.
You can get a list of all powers of two that fit in Int and test each of them for whether the value contains it with the infix function and: val value = 126 val powersOfTwo = (0 until Int.SIZE_BITS).map { n -> 1 shl n } println(powersOfTwo.filter { p -> value and p != 0}.joinToString(",")) // prints: 2,4,8,16,32,64 See the entire code in Kotlin playground: https://pl.kotl.in/f4CZtmCyI
Hi I finally managed to get this working properly: fun decomposeByTwo(value :Int): String { val result = ArrayList<String>() var value = value var j = 0 while (j < 256) { var mask = 1 shl j if ((value and mask) != 0) { value -= mask result.add(mask.toString()) } j += 1 } return result.toString() } I hope this helps someone trying to get a handle on bitwise options!
Somehow you want to do the "bitwise AND" of "value" and "mask" to determine if the j-th bit of "value" is set. I think you just forgot that test in your kotlin implementation.
Fragment BarChart in Modal is not updating - TornadoFX
I've created a Fragment to hold a BarChart and a ScrollPane - the end result will be a scrollable histogram. I'm creating these new fragments in a seperate modal using the openModal method. The problem that i'm having is that the BarChart doesn't seem to be updating when I call my loadData method, as shown below: class Histogram : Fragment() { override val root = vbox{ hgrow = Priority.ALWAYS vgrow = Priority.ALWAYS style{ minWidth = 1280.px minHeight = 180.px } hbox{ hgrow = Priority.ALWAYS } } private val bar = ScrollBar() private var barChart = barchart("bar", CategoryAxis(), NumberAxis()){ barGap = 0.0 categoryGap = -1.0 hgrow = Priority.ALWAYS vgrow = Priority.ALWAYS style{ minWidth = 640.px minHeight = 240.px maxHeight = 480.px } isLegendVisible = false } private val s = XYChart.Series<String, Number>() init{ root.add(barChart) root.add(bar) } fun loadData(h: AlignmentHistogram){ s.data.add(XYChart.Data<String, Number>("asd", 2)) barChart.title = h.rname /* for(i in 0..MAX_DATASET_SIZE){ if(i > h.histogram.size){ break } val data = XYChart.Data<String, Int>((h.firstPosition + i + 1).toString(), (h.histogram[i])) println(data) s.data.add(data) }*/ s.data.add(XYChart.Data<String, Number>("asasdd", 5)) s.name = h.rname barChart.data.add(s) } } AlignmentHistogram is just a POJO with the data for the histogram and a few other details. I'm calling the fragment with the following code: val stage = Stage() stage.title = "${pointer.rname} - ${selectedFile.file.name}" val view = Histogram() view.loadData(h) val parent = StackPane() stage.scene = Scene(parent, 1280.0, 360.0) view.openModal(StageStyle.UTILITY, Modality.NONE, false, stage, false, true) The result of this is just an empty histogram in a new modal, despite calling barChart.data.add(s) Any ideas? Thanks in advance!
Nevermind, I solved it by passing the POJO in as the scope and putting the set-up code into the init block. This article helped with the scope part: Tornadofx - How to pass parameter to Fragment on every instance