Eten & DrinkenConstant Grind Café & Bar坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐坐的坐标 ( Y position ) .map { it[X position] } .map { it as String } .map { if (it.isNullOrBlank()) null else it.toDoubleOrNull()?.times(coordinateFactor) } ?: emptyList<Double>() val yLabels = data.data.elements .map { it[Y position] } .map { it as String } .map { if (it.isNullOrEmpty()) null else it.toDoubleOrNull()?.times(coordinateFactor) } ?: emptyList<Double>() val points = mutableListOf<Point>() for (i in xLabels.indices) { val x = xLabels[i] val y = yLabels[i] if (x != null && y != null) { points.add(Point(x, y)) } } val pointsFiltered = filterVisiblePoints(points, viewPortHandler) drawAxis(canvas, axisX, viewPortHandler, data.data.chartOptions, X, (height - padding.bottom)) drawAxis(canvas, axisY, viewPortHandler, data.data.chartOptions, Y, padding.left ) // draw series drawSeries(canvas, pointsFiltered, viewPortHandler, paint) // draw legend drawLegend(canvas, data.data.legend, viewPortHandler, paint) // draw axis labels drawAxisLabels(canvas, axisX, axisY, viewPortHandler, paint, padding) } private fun filterVisiblePoints(points: List<Point>, viewPortHandler: ViewPortHandler): List<Point> { val (minX, maxX) = viewPortHandler.getXRange() val (minY, maxY) = viewPortHandler.getYRange() return points.filter { it.x in minX..maxX && it.y in minY..maxY } } private fun drawAxis( canvas: Canvas, axis: Axis, viewPortHandler: ViewPortHandler, options: ChartOptions, type: String, position: Float ) { val (minX, maxX) = viewPortHandler.getXRange() val (minY, maxY) = viewPortHandler.getYRange() paint.color = Color.BLACK paint.strokeWidth = 2f if (type == X) { val yPos = viewPortHandler.translateY(0.0) canvas.drawLine( viewPortHandler.contentLeft, yPos, viewPortHandler.contentRight, yPos, paint ) val step = calculateStep(minX, maxX) var currentX = Math.ceil(minX / step) * step while (currentX <= maxX) { val xPos = viewPortHandler.translateX(currentX) canvas.drawLine(xPos, yPos - 10, xPos, yPos + 10, paint) canvas.drawText(currentX.toString(), xPos, yPos + 40, paint) currentX += step } } else { val xPos = viewPortHandler.translateX(0.0) canvas.drawLine( xPos, viewPortHandler.contentTop, xPos, viewPortHandler.contentBottom, paint ) val step = calculateStep(minY, maxY) var currentY = Math.ceil(minY / step) * step while (currentY <= maxY) { val yPos = viewPortHandler.translateY(currentY) canvas.drawLine(xPos - 10, yPos, xPos + 10, yPos, paint) canvas.drawText(currentY.toString(), xPos - 60, yPos + 10, paint) currentY += step } } } private fun calculateStep(min: Double, max: Double): Double { val range = max - min if (range == 0.0) return 1.0 val rawStep = range / 10 val exponent = Math.floor(Math.log10(rawStep)).toInt() val magnitude = Math.pow(10.0, exponent.toDouble()) val normalizedStep = rawStep / magnitude val step = when { normalizedStep < 1.5 -> 1.0 normalizedStep < 3.0 -> 2.0 normalizedStep < 7.0 -> 5.0 else -> 10.0 } return step * magnitude } private fun drawSeries( canvas: Canvas, points: List<Point>, viewPortHandler: ViewPortHandler, paint: Paint ) { paint.color = Color.BLUE paint.style = Paint.Style.FILL for (point in points) { val x = viewPortHandler.translateX(point.x) val y = viewPortHandler.translateY(point.y) canvas.drawCircle(x, y, 10f, paint) } } private fun drawLegend( canvas: Canvas, legend: Legend, viewPortHandler: ViewPortHandler, paint: Paint ) { // Implement legend drawing here if needed } private fun drawAxisLabels( canvas: Canvas, axisX: Axis, axisY: Axis, viewPortHandler: ViewPortHandler, paint: Paint, padding: Padding ) { paint.color = Color.BLACK paint.textSize = 30f paint.textAlign = Paint.Align.CENTER // X-axis label canvas.drawText( axisX.title, viewPortHandler.contentLeft + viewPortHandler.contentWidth / 2, height - padding.bottom / 4, paint ) // Y-axis label canvas.save() canvas.rotate(-90f, padding.left / 4, viewPortHandler.contentTop + viewPortHandler.contentHeight / 2) canvas.drawText( axisY.title, padding.left / 4, viewPortHandler.contentTop + viewPortHandler.contentHeight / 2, paint ) canvas.restore() }
}
[filepath]app/src/main/java/com/remi/biometric/CustomScatterChart.kt
[code]package com.remi.biometric import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import kotlin.math.max
import kotlin.math.min class CustomScatterChart @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) { private var chartData: ChartData? = null private val padding = Padding(150f, 50f, 150f, 100f) private val viewPortHandler = ViewPortHandler() private val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { viewPortHandler.scale(detector.scaleFactor, detector.focusX, detector.focusY) invalidate() return true } }) private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean { viewPortHandler.scroll(distanceX, distanceY) invalidate() return true } }) fun setData(data: ChartData) { this.chartData = data initViewPort() invalidate() } private fun initViewPort() { chartData?.let { data -> val coordinateFactor = data.data.chartOptions.coordinateFactor val xValues = data.data.elements.mapNotNull { (it[X position] as? String)?.toDouble()?.times(coordinateFactor) } val yValues = data.data.elements.mapNotNull { (it[Y position] as? String)?.toDouble()?.times(coordinateFactor) } if (xValues.isNotEmpty() && yValues.isNotEmpty()) { viewPortHandler.setMinMax( xValues.minOrNull() ?: 0.0, xValues.maxOrNull() ?: 0.0, yValues.minOrNull() ?: 0.0, yValues.maxOrNull() ?: 0.0 ) } } } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) viewPortHandler.setContentRect( padding.left, padding.top, w - padding.right, h - padding.bottom ) } override fun onTouchEvent(event: MotionEvent): Boolean { scaleGestureDetector.onTouchEvent(event) gestureDetector.onTouchEvent(event) return true } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) chartData?.let { data -> ChartRenderer.draw(canvas, data, viewPortHandler, padding, height) } }
}
[patch]--- a/app/src/main/java/com/remi/biometric/ChartRenderer.kt
+++ b/app/src/main/java/com/remi/biometric/ChartRenderer.kt
@@ -107,6 +107,24 @@ } } + private fun drawSeries(
+ canvas: Canvas,
+ points: List<Point>,
+ viewPortHandler: ViewPortHandler,
+ paint: Paint
+ ) {
+ paint.color = Color.BLUE
+ paint.style = Paint.Style.FILL
+
+ for (point in points) {
+ val x = viewPortHandler.translateX(point.x)
+ val y = viewPortHandler.translateY(point.y)
+ if (x in viewPortHandler.contentLeft..viewPortHandler.contentRight &&
+ y in viewPortHandler.contentTop..viewPortHandler.contentBottom) {
+ canvas.drawCircle(x, y, 10f, paint)
+ }
+ }
+ } private fun calculateStep(min: Double, max: Double): Double { val range = max - min if (range == 0.0) return 1.0
@@ -124,22 +142,6 @@ return step * magnitude } - private fun drawSeries(
- canvas: Canvas,
- points: List<Point>,
- viewPortHandler: ViewPortHandler,
- paint: Paint
- ) {
- paint.color = Color.BLUE
- paint.style = Paint.Style.FILL
-
- for (point in points) {
- val x = viewPortHandler.translateX(point.x)
- val y = viewPortHandler.translateY(point.y)
- canvas.drawCircle(x, y, 10f, paint)
- }
- }
- private fun drawLegend( canvas: Canvas, legend: Legend,
[patch]--- a/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
+++ b/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
@@ -19,7 +19,7 @@ ) : View(context, attrs, defStyleAttr) { private var chartData: ChartData? = null
- private val padding = Padding(150f, 50f, 150f, 100f)
+ private val padding = Padding(150f, 50f, 50f, 100f) private val viewPortHandler = ViewPortHandler() private val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
@@ -53,9 +53,9 @@ if (xValues.isNotEmpty() && yValues.isNotEmpty()) { viewPortHandler.setMinMax( xValues.minOrNull() ?: 0.0,
- xValues.maxOrNull() ?: 0.0,
+ xValues.maxOrNull() ?: 1.0, yValues.minOrNull() ?: 0.0,
- yValues.maxOrNull() ?: 0.0
+ yValues.maxOrNull() ?: 1.0 ) } }
@@ -83,4 +83,4 @@ ChartRenderer.draw(canvas, data, viewPortHandler, padding, height) } }
-}
+}//
[patch]--- a/app/src/main/java/com/remi/biometric/MainActivity.kt
+++ b/app/src/main/java/com/remi/biometric/MainActivity.kt
@@ -1,7 +1,22 @@ package com.remi.biometric +import android.content.Context
+import android.os.Build import android.os.Bundle
+import android.util.AttributeSet
+import android.view.View import androidx.appcompat.app.AppCompatActivity
+import com.google.gson.Gson
+import java.net.URL
+import kotlin.concurrent.thread
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.RectF
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import kotlin.math.* class MainActivity : AppCompatActivity() { [patch]--- a/app/src/main/java/com/remi/biometric/ChartData.kt
+++ b/app/src/main/java/com/remi/biometric/ChartData.kt
@@ -1,6 +1,8 @@ package com.remi.biometric -data class ChartData(val data: Data)
+data class ChartData(
+ val data: Data
+) data class Data( val axisX: Axis,
[patch]--- a/app/src/main/java/com/remi/biometric/ChartRenderer.kt
+++ b/app/src/main/java/com/remi/biometric/ChartRenderer.kt
@@ -2,6 +2,7 @@ import android.graphics.Canvas import android.graphics.Color
+import android.graphics.DashPathEffect import android.graphics.Paint object ChartRenderer {
@@ -11,8 +12,6 @@ val axisY = data.data.axisY val coordinateFactor = data.data.chartOptions.coordinateFactor - // Prepare points
- // assuming the order of points is consistent in X and Y val xLabels = data.data.elements .map { it[X position] } .map { it as String }
@@ -31,31 +30,57 @@ points.add(Point(x, y)) } }
- val pointsFiltered = filterVisiblePoints(points, viewPortHandler) - drawAxis(canvas, axisX, viewPortHandler, data.data.chartOptions, X, (height - padding.bottom))
- drawAxis(canvas, axisY, viewPortHandler, data.data.chartOptions, Y, padding.left )
+ drawAxesAndGrid(canvas, axisX, axisY, viewPortHandler, paint)
+ drawSeries(canvas, points, viewPortHandler, paint)
+ drawAxisLabels(canvas, axisX, axisY, viewPortHandler, paint, padding)
+ drawLegend(canvas, data.data.legend, viewPortHandler, paint)
+ } - // draw series
- drawSeries(canvas, pointsFiltered, viewPortHandler, paint)
-
- // draw legend
- drawLegend(canvas, data.data.legend, viewPortHandler, paint)
-
- // draw axis labels
- drawAxisLabels(canvas, axisX, axisY, viewPortHandler, paint, padding)
-
- }
-
- private fun filterVisiblePoints(points: List<Point>, viewPortHandler: ViewPortHandler): List<Point> {
- val (minX, maxX) = viewPortHandler.getXRange()
- val (minY, maxY) = viewPortHandler.getYRange()
-
- return points.filter { it.x in minX..maxX && it.y in minY..maxY }
+ private fun drawAxesAndGrid(
+ canvas: Canvas,
+ axisX: Axis,
+ axisY: Axis,
+ viewPortHandler: ViewPortHandler,
+ paint: Paint
+ ) {
+ drawAxis(canvas, axisX, viewPortHandler, X)
+ drawAxis(canvas, axisY, viewPortHandler, Y) } private fun drawAxis( canvas: Canvas,
+ axis: Axis,
+ viewPortHandler: ViewPortHandler,
+ type: String
+ ) {
+ val (min, max) = if (type == X) viewPortHandler.getXRange() else viewPortHandler.getYRange()
+ val step = calculateStep(min, max)
+ var currentVal = Math.ceil(min / step) * step
+
+ paint.color = Color.LTGRAY
+ paint.strokeWidth = 1f
+ paint.textSize = 24f
+ paint.textAlign = Paint.Align.RIGHT
+
+ while (currentVal <= max) {
+ if (type == X) {
+ val xPos = viewPortHandler.translateX(currentVal)
+ canvas.drawLine(xPos, viewPortHandler.contentTop, xPos, viewPortHandler.contentBottom, paint)
+ paint.color = Color.BLACK
+ canvas.drawText(String.format(%.1f, currentVal), xPos, viewPortHandler.contentBottom + 30f, paint)
+ paint.color = Color.LTGRAY
+ } else {
+ val yPos = viewPortHandler.translateY(currentVal)
+ canvas.drawLine(viewPortHandler.contentLeft, yPos, viewPortHandler.contentRight, yPos, paint)
+ paint.color = Color.BLACK
+ canvas.drawText(String.format(%.1f, currentVal), viewPortHandler.contentLeft - 10f, yPos + 10f, paint)
+ paint.color = Color.LTGRAY
+ }
+ currentVal += step
+ }
+ }
+/* private fun drawAxis1(canvas: Canvas, axis: Axis, viewPortHandler: ViewPortHandler, options: ChartOptions,
@@ -99,7 +124,7 @@ currentY += step } }
- }
+ }*/ private fun calculateStep(min: Double, max: Double): Double { val range = max - min
@@ -131,7 +156,9 @@ for (point in points) { val x = viewPortHandler.translateX(point.x) val y = viewPortHandler.translateY(point.y)
- canvas.drawCircle(x, y, 10f, paint)
+ if (x in viewPortHandler.contentLeft..viewPortHandler.contentRight &&
+ y in viewPortHandler.contentTop..viewPortHandler.contentBottom) {
+ canvas.drawCircle(x, y, 8f, paint)
+ } } } [patch]--- a/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
+++ b/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
@@ -19,7 +19,7 @@ ) : View(context, attrs, defStyleAttr) { private var chartData: ChartData? = null
- private val padding = Padding(150f, 50f, 150f, 100f)
+ private val padding = Padding(150f, 50f, 50f, 100f) private val viewPortHandler = ViewPortHandler() private val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
@@ -53,9 +53,9 @@ if (xValues.isNotEmpty() && yValues.isNotEmpty()) { viewPortHandler.setMinMax( xValues.minOrNull() ?: 0.0,
- xValues.maxOrNull() ?: 0.0,
+ xValues.maxOrNull() ?: 1.0, yValues.minOrNull() ?: 0.0,
- yValues.maxOrNull() ?: 0.0
+ yValues.maxOrNull() ?: 1.0 ) } }
@@ -74,7 +74,8 @@ override fun onTouchEvent(event: MotionEvent): Boolean { scaleGestureDetector.onTouchEvent(event) gestureDetector.onTouchEvent(event)
- return true
+ super.onTouchEvent(event)
+ return true // Or true if you want to consume all events } override fun onDraw(canvas: Canvas) {
@@ -83,4 +84,4 @@ ChartRenderer.draw(canvas, data, viewPortHandler, padding, height) } }
-}
+}//
[instruction]
In this exercise, you will play the role of a luxury hotel travel writer, crafting a website page for **REVERB by Hard Rock Hamburg.** Your goal is to create compelling, informative, and SEO-optimized content that captures the unique personality of the hotel while adhering to specific structural and data constraints. ### **Hotel Profile:**
* **Hotel Name:** REVERB by Hard Rock Hamburg
* **Location:** 66 Feldstraße, Hamburg, Germany
* **Rating:** 4 stars
* **Booking.com Rating:** 8.3
* **Price Point:** ~€153/night
* **Core Concept:** A music-centric, tech-forward hotel located within the historic St. Pauli Bunker. It focuses on the local music scene, social connection, and modern urban culture. ### **Mandatory Data to Include:**
* **Room types:** Standard Queen Room
* **Verified Amenities (ONLY use these):** * **Business:** Meeting rooms, Co-working spaces, High-speed WiFi, Business center * **Dining:** Constant Grind Café & Bar, Rooftop restaurant and bar, Room service * **Rooms:** Air conditioning, Smart TV with casting, Premium bedding, Alexa voice assistant, Walk-in shower, Laptop-sized safe * **Services:** 24-hour front desk, Mobile check-in, Laundry service, Pet-friendly * **Wellness:** Fitness center, Yoga mats in-room, Access to the rooftop garden park ### **Instructions for the AI:**
1. **Function Call:** You must use the `generate_hotel_content` tool.
2. **Voice & Tone:** Sophisticated, rhythmic (hinting at music), urban-chic, and professional but vibrant.
3. **Accuracy:** Use *only* the amenities provided. Do not hallicunate spas or swimming pools if they aren't in the list.
4. **SEO focus:** Integrate keywords naturally (e.g., Hamburg boutique hotel, St. Pauli accommodation, Hard Rock Hamburg).
5. **Structure:** Fill out every field in the tool schema. * `about_h1_text`: Intro paragraph (100-150 words). * `about_h2_1_text`: The Experience (100-150 words). * `about_h2_2_text`: The Rooms (100-150 words). * `about_h2_3_text`: Dining & Drinks (100-150 words). * `about_h2_5_text`: Closing (100-150 words). * `about_h2_4_faqs`: Provide 4-6 helpful FAQs.
6. **Context:** The hotel is built inside a former flak tower (bunker) that has been transformed into a green oasis with a public rooftop park. Mention this unique architectural feature. **Focus on the Vibe:** REVERB is where music meets the city. It's for fans, creators, and urban explorers. Use language that evokes sound, energy, and community.
[/instruction] 舞台alldetailed:default_api:generate_hotel_content( about_h1=A High-Octane Escape in the Heart of St. Pauli, about_h1_text=REVERB by Hard Rock Hamburg is more than just a place to sleep; it is a cultural epicenter designed for the modern traveler who lives life at full volume. Perched within the iconic, green-clad St. Pauli Bunker, this four-star destination merges industrial history with cutting-edge musical energy. From the moment you enter, the hotel pulses with the spirit of the local creative scene, offering a curated experience that celebrates Hamburg’s legendary music heritage. Whether you are here to catch a show, explore the vibrant Karoviertel, or simply soak in the panoramic city views from the rooftop park, REVERB provides an electrifying home base that is as bold and unconventional as you are. The transformation of this historic flak tower into a lush urban sanctuary represents a new era for Hamburg., about_h2_1=The REVERB Experience: St. Pauli Unleashed, about_h2_1_bullets=Direct access to the stunning green rooftop garden park\nRegular live music sessions and local artist showcases\nSocially driven co-working spaces for digital nomads\nSeamless mobile check-in for the tech-savvy traveler\nPanoramic views of the Hamburg city skyline, about_h2_1_text=Staying at REVERB by Hard Rock Hamburg is about immersing yourself in the rhythm of the city. The hotel serves as a bridge between Hamburg's rebellious spirit and sophisticated modern hospitality. The centerpiece of the experience is the incredible transformation of the St. Pauli Bunker, where concrete meets greenery in a spectacular rooftop garden park—a true oasis above the urban bustle. Inside, the energy is undeniable, with spaces designed to foster connection between fans and creators. From the curated playlists that follow you through the lobby to the frequent live performances, every detail is tuned to inspire. It is a place where business travelers find focus in co-working hubs and explorers find the ultimate launchpad for discovering the Karoviertel’s independent boutiques and nightlife., about_h2_2=Your Backstage Sanctuary, about_h2_2_text=Retreat to a space where comfort meets cutting-edge technology. Our Standard Queen Room is meticulously designed to be your private sanctuary, featuring a layout that prioritizes both rest and productivity. Every room is an smart-hub, equipped with an Alexa voice assistant to help you navigate your stay and a Smart TV with casting capabilities for your personalized entertainment. The aesthetic is sharp and urban, anchored by premium bedding and a refreshing walk-in shower. For those traveling with work, the room provides a laptop-sized safe and high-speed WiFi. Whether you are winding down after a day of meetings or gearing up for a night in St. Pauli, the air-conditioned interior and thoughtful touches like in-room yoga mats provide the perfect balance of energy and tranquility., about_h2_3=Fuel for the Soul: Dining at REVERB, about_h2_3_text=Culinary experiences at REVERB are designed to be social, vibrant, and deeply connected to the local flavor. Start your morning or recharge your afternoon at the Constant Grind Café & Bar, where expertly brewed coffee meets a curated selection of snacks in a space built for conversation. As the day transitions into night, our rooftop restaurant and bar becomes the place to be in Hamburg. Here, you can enjoy creative dishes and signature cocktails against the backdrop of unparalleled city views from the top of the bunker. For those moments when you prefer a quiet night in after a long tour, our room service brings the flavors of the hotel directly to your door. It is a dining scene that mirrors the eclectic, artistic energy of the surrounding neighborhood., about_h2_4=Frequently Asked Questions, about_h2_4_faqs=[ {question: How do I check in at REVERB by Hard Rock Hamburg?, answer: To streamline your experience, we offer mobile check-in via our app, allowing you to bypass the front desk and head straight to your room. Our 24-hour front desk staff is also available to assist you at any time.}, {question: Is the hotel accessible to public transportation?, answer: Yes, we are located at 66 Feldstraße, right in the heart of St. Pauli. The Feldstraße U-Bahn station is just steps away, connecting you to the rest of Hamburg within minutes.}, {question: Are pets welcome at the hotel?, answer: Absolutely! REVERB is a pet-friendly property, so you don't have to leave your four-legged roadies behind when you visit Hamburg.}, {question: Does the hotel have a fitness center?, answer: Yes, guests have access to a fully equipped fitness center. Additionally, for a more private session, every room is stocked with yoga mats for your personal use.}, {question: What is unique about the hotel's architecture?, answer: The hotel is built inside a meticulously restored historic flak tower. It now features a massive public rooftop garden park, making it one of the greenest urban landmarks in Germany.} ], about_h2_5=The Final Encore, about_h2_5_text=Whether you are a lifelong music lover or a traveler seeking a hotel with a distinct point of view, REVERB by Hard Rock Hamburg delivers a performance you won’t forget. It is where the historical weight of the St. Pauli Bunker meets the airy freedom of a rooftop garden. By staying with us, you aren't just visiting Hamburg; you are becoming part of its vibrant, beating heart. With tech-forward rooms, a social atmosphere that encourages creativity, and a location that puts the best of the city at your doorstep, this is your invitation to live loud. Grab your pass, check in, and let the city's energy move you. Your stage is set, the lights are up, and we are ready for the show to begin., amenities={ business: [Meeting rooms, Co-working spaces, High-speed WiFi, Business center], dining: [Constant Grind Café & Bar, Rooftop restaurant and bar, Room service], rooms: [Air conditioning, Smart TV with casting, Premium bedding, Alexa voice assistant, Walk-in shower, Laptop-sized safe], services: [24-hour front desk, Mobile check-in, Laundry service, Pet-friendly], wellness: [Fitness center, Yoga mats in-room, Access to the rooftop garden park] }, amenities_summary=[Rooftop restaurant and bar, High-speed WiFi, Alexa voice assistant, Mobile check-in, Fitness center, Rooftop garden park, Pet-friendly, Co-working spaces], description=Experience a melody of history and modern luxury at REVERB by Hard Rock Hamburg. Located in a repurposed St. Pauli bunker, this 4-star hotel offers tech-forward rooms, a vibrant rooftop park, and a deep connection to Hamburg's iconic music scene., full_description=REVERB by Hard Rock Hamburg is a groundbreaking hospitality concept that breathes new life into one of Hamburg's most significant landmarks. Situated in the massive St. Pauli Bunker at 66 Feldstraße, the hotel is an architectural marvel topped with a lush, public rooftop garden park that offers breathtaking 360-degree views of the city. This 4-star destination is tailored for those who crave a social, music-infused atmosphere combined with the latest in smart-hotel technology. Guest staying in our Standard Queen Rooms will enjoy a high-tech sanctuary featuring Alexa voice assistants, smart TVs with casting, and premium bedding designed for ultimate recovery. Beyond the rooms, REVERB serves as a community hub with versatile co-working spaces, the trendy Constant Grind Café & Bar, and a rooftop bar that defines the Hamburg nightlife. With its pet-friendly policy, mobile check-in via smartphone, and a fitness center, it perfectly balances convenience with an edgy, urban vibe. Whether you're here to work, play, or explore the legendary nearby Reeperbahn, REVERB provides an authentic St. Pauli experience that resonates with creativity and soul., highlights=[Located in the historic St. Pauli Bunker, Lush public rooftop garden park, Alexa voice integrated rooms, Heart of the vibrant Karoviertel, Innovative mobile check-in, Active music and social scene], hotel_style_tags=[Music-centric, Industrial-chic, Tech-forward, Urban Jungle], ideal_for=[Music enthusiasts, Digital nomads, Urban explorers, Creative professionals], meta_description=Book your stay at REVERB by Hard Rock Hamburg. A 4-star music-themed hotel in the St. Pauli Bunker featuring a rooftop park, smart rooms, and vibrant bars., meta_title=REVERB by Hard Rock Hamburg | Music & Tech Hotel St. Pauli, neighborhood_description=The Karoviertel and St. Pauli neighborhoods are the soul of Hamburg's creative and alternative scene. Located just steps from the hotel, these districts are famous for the Reeperbahn nightlife, independent fashion boutiques, and local record stores. The area is a melting pot of street art, historic architecture, and diverse culinary treasures. With the U-Bahn station right outside, you are perfectly positioned to explore the Elbphilharmonie, the harbor, and the city center, all while staying in the most energetic part of town., seo_keywords=[Hard Rock Hamburg, REVERB hotel Hamburg, St. Pauli accommodation, Hamburg bunker hotel, Boutique hotel Hamburg St. Pauli, Rooftop bar Hamburg], seo_long_description=Looking for a unique stay in Hamburg? REVERB by Hard Rock Hamburg offers an unparalleled experience inside the transformed St. Pauli Bunker. This 4-star hotel is the perfect blend of industrial heritage and modern luxury, featuring a stunning rooftop garden park that creates a green oasis in the middle of the city. As a music-forward hotel, we offer integrated technology in every room, including Alexa voice assistants and smart casting, making us a top choice for tech-savvy travelers. Located at 66 Feldstraße, you are in the heart of the action, surrounded by the artistic energy of the Karoviertel and the world-famous nightlife of St. Pauli. Whether you need a co-working space for business or a rooftop bar for pleasure, REVERB provides all the verified amenities to make your Hamburg trip hit all the right notes., short_summary=REVERB by Hard Rock Hamburg is a vibrant 4-star hotel located within the historic St. Pauli Bunker. It combines a love for music with modern technology and sustainable urban design, featuring a spectacular rooftop garden park. With its tech-savvy rooms and social dining spaces, it offers an authentic, high-energy Hamburg experience., subtitle1=A Sound-First Sanctuary in the Historic St. Pauli Bunker, subtitle2=Where Industrial Heritage Meets Modern Beat, tagline=Turn up the volume on your Hamburg getaway at REVERB., why_we_love_it=[The unique architecture of the converted bunker, Unrivaled views from the rooftop garden park, Tech-integrated rooms with Alexa assistants, Perfect location for exploring St. Pauli's nightlife]
) { X position, Y position }, { 2.5, 1.5 }, { 3.0, 2.0 }, { 4.0, 3.0 }, { 1.0, 0.5 }, { 5.0, 4.0 } ), ChartOptions(1.0)
) val chartData = ChartData( Data( Axis(X Axis, X unit), Axis(Y Axis, Unit), elements, Legend(Sample Chart), chartOptions )
)
[filepath]app/src/main/java/com/remi/biometric/ViewPortHandler.kt
[code]package com.remi.biometric import android.graphics.RectF class ViewPortHandler { val contentRect = RectF() private var chartMinX = 0.0 private var chartMaxX = 0.0 private var chartMinY = 0.0 private var chartMaxY = 0.0 private var scaleX = 1f private var scaleY = 1f private var transX = 0f private var transY = 0f fun setContentRect(left: Float, top: Float, right: Float, bottom: Float) { contentRect.set(left, top, right, bottom) } fun setMinMax(minX: Double, maxX: Double, minY: Double, maxY: Double) { chartMinX = minX chartMaxX = maxX chartMinY = minY chartMaxY = maxY } val contentWidth: Float get() = contentRect.width() val contentHeight: Float get() = contentRect.height() val contentLeft: Float get() = contentRect.left val contentRight: Float get() = contentRect.right val contentTop: Float get() = contentRect.top val contentBottom: Float get() = contentRect.bottom fun translateX(x: Double): Float { val rangeX = chartMaxX - chartMinX if (rangeX == 0.0) return contentLeft val normalizedX = (x - chartMinX) / rangeX return (normalizedX.toFloat() * contentWidth * scaleX) + contentLeft + transX } fun translateY(y: Double): Float { val rangeY = chartMaxY - chartMinY if (rangeY == 0.0) return contentBottom val normalizedY = (y - chartMinY) / rangeY return contentBottom - (normalizedY.toFloat() * contentHeight * scaleY) + transY } fun getXRange(): Pair<Double, Double> { val rangeX = chartMaxX - chartMinX val visibleMinX = chartMinX - (transX / (contentWidth * scaleX)) * rangeX val visibleMaxX = visibleMinX + rangeX / scaleX return Pair(visibleMinX, visibleMaxX) } fun getYRange(): Pair<Double, Double> { val rangeY = chartMaxY - chartMinY val visibleMinY = chartMinY + (transY / (contentHeight * scaleY)) * rangeY val visibleMaxY = visibleMinY + rangeY / scaleY return Pair(visibleMinY, visibleMaxY) } fun scale(scaleFactor: Float, focusX: Float, focusY: Float) { scaleX *= scaleFactor scaleY *= scaleFactor // TODO: adjust translation to keep focus point } fun scroll(distanceX: Float, distanceY: Float) { transX -= distanceX transY -= distanceY }
}
[patch]--- a/app/src/main/java/com/remi/biometric/ChartRenderer.kt
+++ b/app/src/main/java/com/remi/biometric/ChartRenderer.kt
@@ -2,6 +2,7 @@ import android.graphics.Canvas import android.graphics.Color
+import android.graphics.DashPathEffect import android.graphics.Paint object ChartRenderer {
@@ -11,8 +12,6 @@ val axisY = data.data.axisY val coordinateFactor = data.data.chartOptions.coordinateFactor - // Prepare points
- // assuming the order of points is consistent in X and Y val xLabels = data.data.elements .map { it[X position] } .map { it as String }
@@ -31,31 +30,57 @@ points.add(Point(x, y)) } }
- val pointsFiltered = filterVisiblePoints(points, viewPortHandler) - drawAxis(canvas, axisX, viewPortHandler, data.data.chartOptions, X, (height - padding.bottom))
- drawAxis(canvas, axisY, viewPortHandler, data.data.chartOptions, Y, padding.left )
+ drawAxesAndGrid(canvas, axisX, axisY, viewPortHandler, paint)
+ drawSeries(canvas, points, viewPortHandler, paint)
+ drawAxisLabels(canvas, axisX, axisY, viewPortHandler, paint, padding)
+ drawLegend(canvas, data.data.legend, viewPortHandler, paint)
+ } - // draw series
- drawSeries(canvas, pointsFiltered, viewPortHandler, paint)
+ private fun drawAxesAndGrid(
+ canvas: Canvas,
+ axisX: Axis,
+ axisY: Axis,
+ viewPortHandler: ViewPortHandler,
+ paint: Paint
+ ) {
+ drawAxis(canvas, axisX, viewPortHandler, X)
+ drawAxis(canvas, axisY, viewPortHandler, Y)
+ } - // draw legend
- drawLegend(canvas, data.data.legend, viewPortHandler, paint)
-
- // draw axis labels
- drawAxisLabels(canvas, axisX, axisY, viewPortHandler, paint, padding)
-
- }
-
- private fun filterVisiblePoints(points: List<Point>, viewPortHandler: ViewPortHandler): List<Point> {
- val (minX, maxX) = viewPortHandler.getXRange()
- val (minY, maxY) = viewPortHandler.getYRange()
-
- return points.filter { it.x in minX..maxX && it.y in minY..maxY }
- }
-
- private fun drawAxis(
- canvas: Canvas,
+ private fun drawAxis(
+ canvas: Canvas,
+ axis: Axis,
+ viewPortHandler: ViewPortHandler,
+ type: String
+ ) {
+ val (min, max) = if (type == X) viewPortHandler.getXRange() else viewPortHandler.getYRange()
+ val step = calculateStep(min, max)
+ var currentVal = Math.ceil(min / step) * step
+
+ paint.color = Color.LTGRAY
+ paint.strokeWidth = 1f
+ paint.textSize = 24f
+ paint.textAlign = Paint.Align.RIGHT
+
+ while (currentVal <= max) {
+ if (type == X) {
+ val xPos = viewPortHandler.translateX(currentVal)
+ canvas.drawLine(xPos, viewPortHandler.contentTop, xPos, viewPortHandler.contentBottom, paint)
+ paint.color = Color.BLACK
+ canvas.drawText(String.format(%.1f, currentVal), xPos, viewPortHandler.contentBottom + 30f, paint)
+ paint.color = Color.LTGRAY
+ } else {
+ val yPos = viewPortHandler.translateY(currentVal)
+ canvas.drawLine(viewPortHandler.contentLeft, yPos, viewPortHandler.contentRight, yPos, paint)
+ paint.color = Color.BLACK
+ canvas.drawText(String.format(%.1f, currentVal), viewPortHandler.contentLeft - 10f, yPos + 10f, paint)
+ paint.color = Color.LTGRAY
+ }
+ currentVal += step
+ }
+ }
+/* private fun drawAxis1(canvas: Canvas, axis: Axis, viewPortHandler: ViewPortHandler, options: ChartOptions,
@@ -99,7 +124,7 @@ currentY += step } }
- }
+ }*/ private fun calculateStep(min: Double, max: Double): Double { val range = max - min
@@ -131,7 +156,9 @@ for (point in points) { val x = viewPortHandler.translateX(point.x) val y = viewPortHandler.translateY(point.y)
- canvas.drawCircle(x, y, 10f, paint)
+ if (x in viewPortHandler.contentLeft..viewPortHandler.contentRight &&
+ y in viewPortHandler.contentTop..viewPortHandler.contentBottom) {
+ canvas.drawCircle(x, y, 8f, paint)
+ } } } [patch]--- a/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
+++ b/app/src/main/java/com/remi/biometric/CustomScatterChart.kt
@@ -19,7 +19,7 @@ ) : View(context, attrs, defStyleAttr) { private var chartData: ChartData? = null
- private val padding = Padding(150f, 50f, 150f, 100f)
+ private val padding = Padding(150f, 50f, 50f, 100f) private val viewPortHandler = ViewPortHandler() private val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
@@ -53,9 +53,9 @@ if (xValues.isNotEmpty() && yValues.isNotEmpty()) { viewPortHandler.setMinMax( xValues.minOrNull() ?: 0.0,
- xValues.maxOrNull() ?: 0.0,
+ xValues.maxOrNull() ?: 1.0, yValues.minOrNull() ?: 0.0,
- yValues.maxOrNull() ?: 0.0
+ yValues.maxOrNull() ?: 1.0 ) } }
@@ -74,7 +74,8 @@ override fun onTouchEvent(event: MotionEvent): Boolean { scaleGestureDetector.onTouchEvent(event) gestureDetector.onTouchEvent(event)
- return true
+ super.onTouchEvent(event)
+ return true // Or true if you want to consume all events } override fun onDraw(canvas: Canvas) {
@@ -83,4 +84,4 @@ ChartRenderer.draw(canvas, data, viewPortHandler, padding, height) } }
-}
+}//
[patch]--- a/app/src/main/java/com/remi/biometric/MainActivity.kt
+++ b/app/src/main/java/com/remi/biometric/MainActivity.kt
@@ -1,7 +1,22 @@ package com.remi.biometric +import android.content.Context
+import android.os.Build import android.os.Bundle
+import android.util.AttributeSet
+import android.view.View import androidx.appcompat.app.AppCompatActivity
+import com.google.gson.Gson
+import java.net.URL
+import kotlin.concurrent.thread
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.RectF
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.view.ScaleGestureDetector
+import kotlin.math.* class MainActivity : AppCompatActivity() { [patch]--- a/app/src/main/java/com/remi/biometric/ChartData.kt
+++ b/app/src/main/java/com/remi/biometric/ChartData.kt
@@ -1,6 +1,8 @@ package com.remi.biometric -data class ChartData(val data: Data)
+data class ChartData(
+ val data: Data
+) data class Data( val axisX: Axis,
[patch]--- a/app/src/main/java/com/remi/biometric/ViewPortHandler.kt
+++ b/app/src/main/java/com/remi/biometric/ViewPortHandler.kt
@@ -4,10 +4,10 @@ class ViewPortHandler { val contentRect = RectF()
- private var chartMinX = 0.0
- private var chartMaxX = 0.0
- private var chartMinY = 0.0
- private var chartMaxY = 0.0
+ var chartMinX = 0.0
+ var chartMaxX = 0.0
+ var chartMinY = 0.0
+ var chartMaxY = 0.0 private var scaleX = 1f private var scaleY = 1f
@@ -48,7 +48,7 @@ fun getXRange(): Pair<Double, Double> { val rangeX = chartMaxX - chartMinX
- val visibleMinX = chartMinX - (transX / (contentWidth * scaleX)) * rangeX
+ val visibleMinX = chartMinX - (transX / (contentWidth * scaleX)) * rangeX val visibleMaxX = visibleMinX + rangeX / scaleX return Pair(visibleMinX, visibleMaxX) }
@@ -63,7 +63,6 @@ fun scale(scaleFactor: Float, focusX: Float, focusY: Float) { scaleX *= scaleFactor scaleY *= scaleFactor
- // TODO: adjust translation to keep focus point } fun scroll(distanceX: Float, distanceY: Float) {
@@ -71,3 +70,4 @@ transY -= distanceY } }
+ [instruction]application/json:default_api:generate_hotel_content{about_h1: