本文基于、、、、
界面用于机器人JOG界面,功能详情按设计图设计。指令通讯采用GRPC从服务端获取
RotaryCartesianInterface.kt
package com.hxrobot.ui.Jog
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.times
import androidx.lifecycle.viewmodel.compose.viewModel
import com.hxrobot.ui.teach.PointTeach.JogCurSwitchAxis
@Composable
fun RotaryCartesianInterface() {
var valueIndex by remember { mutableStateOf(4) }//默认值与默认轴对应
val curAxisViewModel: JogCurSwitchAxis = viewModel()//默认值 0
//初始化轴的方法,防止切换模式后轴被保留
if(valueIndex == 4){
curAxisViewModel.currentAxis = 4
}
Box(modifier = Modifier.size(250.dp,250.dp)){
//val size =(200f/density).dp
val size = 250.dp
val centerXY = size*0.5f
val multiple = size/100f.dp
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 1))
.background(if (valueIndex == 1) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 1
curAxisViewModel.currentAxis = 2
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY+(20*multiple).dp).toPx())
translationY = translationY.plus((centerXY+(27*multiple).dp).toPx())
},text = "Y", color = (if(valueIndex == 1) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 2))
.background(if (valueIndex == 2) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 2
curAxisViewModel.currentAxis = 0
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(29*multiple).dp).toPx())
translationY = translationY.plus((centerXY+(25*multiple).dp).toPx())
},text = "Z", color = (if(valueIndex == 2) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 3))
.background(if (valueIndex == 3) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 3
curAxisViewModel.currentAxis = 3
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(45*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(19*multiple).dp).toPx())
},text = "Rz", color = (if(valueIndex == 3) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 4))
.background(if (valueIndex == 4) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 4
curAxisViewModel.currentAxis = 4
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(7*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(48*multiple).dp).toPx())
},text = "Flip", color = (if(valueIndex == 4) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 5))
.background(if (valueIndex == 5) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 5
curAxisViewModel.currentAxis = 1
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY+(35*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(19*multiple).dp).toPx())
},text = "X", color = (if(valueIndex == 5) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(modifier = Modifier.size((0.64f)*size).absoluteOffset(centerXY-((0.64f*size.value)/2f).dp,centerXY-((0.64f*size.value)/2f).dp)){
InnerCircleInRotaryMax()
}
Box(modifier = Modifier.size((0.55f)*size).absoluteOffset(centerXY-((0.55f*size.value)/2f).dp,centerXY-((0.55f*size.value)/2f).dp)){
InnerCircleInRotaryMaxGrayBottom()
}
Box(modifier = Modifier.absoluteOffset(centerXY-((0.49f*size.value)/2f).dp,centerXY-((0.49f*size.value)/2f).dp)){
InnerCircleInRotary(startAngle = (valueIndex)*72f-18f-5f,sizeX = (0.49f)*size.value, sizeY = (0.49f)*size.value)
}
}
}
RotaryJointInterface.kt
package com.hxrobot.ui.Jog
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.unit.*
import androidx.lifecycle.viewmodel.compose.viewModel
import com.hxrobot.ui.teach.PointTeach.JogCurSwitchAxis
@Composable
fun RotaryJointInterface() {
var valueIndex by remember { mutableStateOf(4) }//默认值与默认轴对应
val curAxisViewModel: JogCurSwitchAxis = viewModel()//默认值 0
//初始化轴的方法,防止切换模式后轴被保留
if(valueIndex == 4){
curAxisViewModel.currentAxis = 4
}
Box(modifier = Modifier.size(250.dp,250.dp)){
//val size =(200f/density).dp
val size = 250.dp
val centerXY = size*0.5f
val multiple = size/100f.dp
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 1))
.background(if (valueIndex == 1) Color(0xFF3153CE) else Color(0xFFF1F4F4))
// brush = Brush.verticalGradient(listOf(Color(0xFFECF3F9), Color(0xFFECF3F9)))
.clickable {
valueIndex = 1
curAxisViewModel.currentAxis = 1
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY+(17*multiple).dp).toPx())
translationY = translationY.plus((centerXY+(27*multiple).dp).toPx())
},text = "J2", color = (if(valueIndex == 1) Color.White else Color(0xFF8A8C93)), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 2))
.background(if (valueIndex == 2) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 2
curAxisViewModel.currentAxis =2
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(30*multiple).dp).toPx())
translationY = translationY.plus((centerXY+(25*multiple).dp).toPx())
},text = "J3", color = (if(valueIndex == 2) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 3))
.background(if (valueIndex == 3) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 3
curAxisViewModel.currentAxis = 3
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(45*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(19*multiple).dp).toPx())
},text = "J4", color = (if(valueIndex == 3) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 4))
.background(if (valueIndex == 4) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 4
curAxisViewModel.currentAxis = 4
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(7*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(48*multiple).dp).toPx())
},text = "Flip", color = (if(valueIndex == 4) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShape(5, 5))
.background(if (valueIndex == 5) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 5
curAxisViewModel.currentAxis = 0
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY+(35*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(19*multiple).dp).toPx())
},text = "Z", color = (if(valueIndex == 5) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
//128/200
Box(modifier = Modifier.size((0.64f)*size).absoluteOffset(centerXY-((0.64f*size.value)/2f).dp,centerXY-((0.64f*size.value)/2f).dp)){
InnerCircleInRotaryMax()
}
//110/200
Box(modifier = Modifier.size((0.55f)*size).absoluteOffset(centerXY-((0.55f*size.value)/2f).dp,centerXY-((0.55f*size.value)/2f).dp)){
InnerCircleInRotaryMaxGrayBottom()
}
//110/200 98/200
Box(modifier = Modifier.absoluteOffset(centerXY-((0.49f*size.value)/2f).dp,centerXY-((0.49f*size.value)/2f).dp)){
//InnerCircleLineOutSide(sizeX = (128f/size.value)*size.value, sizeY = (128f/size.value)*size.value)
InnerCircleInRotary(startAngle = (valueIndex)*72f-18f-5f,sizeX = (0.49f)*size.value, sizeY = (0.49f)*size.value)
}
// //60/200
// Box(modifier = Modifier.size((0.3f)*size).absoluteOffset(centerXY-((0.3f*size.value)/2f).dp,centerXY-((0.3f*size.value)/2f).dp)){
// InnerCircleInRotaryMin()
// }
}
}
class SectorShape(val parts: Int,val currentPart:Int) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density, ): Outline {
val rect = Rect(0f, 0f, size.width, size.height)
val angelOfEachSection = 360f/parts
//定义画笔的路径
val path = Path().apply {
// 将坐标系原点移动到画布中央
moveTo(size.width / 2f, size.height / 2f)//移动到起点
addArc(rect, (currentPart-1)*angelOfEachSection+18f+0.5f, angelOfEachSection-0.5f)
lineTo(size.width / 2f, size.height / 2f)//移动到终点
}
//返回路径的轮廓
return Outline.Generic(path)
}
}
fun DrawScope.backgroundIndicator(
componentSize: Size,
indicatorColor: Color,
indicatorStrokeWidth: Float,
){
drawArc(
size = componentSize,
color = indicatorColor,
startAngle = 0f,
sweepAngle = 360f,
useCenter = true,
style = Stroke(
width = indicatorStrokeWidth,
cap = StrokeCap.Square
),
topLeft = Offset(
x = (size.width - componentSize.width) / 2f,
y = (size.height - componentSize.height) / 2f
)
)
}
fun DrawScope.foregroundIndicator(
componentSize: Size,
indicatorColor: Color,
indicatorStrokeWidth: Float,
startAngle :Float
) {
drawArc(
size = componentSize,
color = indicatorColor,
startAngle = startAngle,
sweepAngle = 10f,
useCenter = false,
style = Stroke(
width = indicatorStrokeWidth,
cap = StrokeCap.Butt
),
topLeft = Offset(
x = (size.width - componentSize.width) / 2f,
y = (size.height - componentSize.height) / 2f
)
)
}
@Composable
fun InnerCircleInRotaryMin() {
Box(modifier = Modifier
.fillMaxSize()
.drawBehind {
drawArc(
color = Color(0xFFF1F4F4),
startAngle = 0f,
sweepAngle = 360f,
useCenter = true,
)
}
)
}
@Composable
fun InnerCircleInRotaryMax() {
Box(modifier = Modifier
.fillMaxSize()
.drawBehind {
drawArc(
color = Color.White,
startAngle = 0f,
sweepAngle = 360f,
useCenter = true,
)
}
)
}
@Composable
fun InnerCircleInRotaryMaxGrayBottom(){
Box(modifier = Modifier
.fillMaxSize()
.drawBehind {
drawArc(
color = Color(0xFFF1F4F4),
startAngle = 0f,
sweepAngle = 360f,
useCenter = true,
)
}
)
}
@Composable
fun InnerCircleInRotary(
canvasSize: Dp = 110.dp,
indicatorValue: Int = 0,
maxIndicatorValue: Int = 100,
backgroundIndicatorColor: Color =Color(0xFFF1F4F4),
backgroundIndicatorStrokeWidth: Float = 100f,
foregroundIndicatorColor: Color = Color(0xFF4D86FF),
foregroundIndicatorStrokeWidth: Float = 28f,
startAngle: Float = 14f,
sizeX: Float = 128f,
sizeY: Float = 128f
){
Box(
modifier = Modifier
.size(sizeX.dp, sizeY.dp)
.drawBehind {
val componentSize = size
// backgroundIndicator(
// componentSize = componentSize,
// indicatorColor = backgroundIndicatorColor,
// indicatorStrokeWidth = backgroundIndicatorStrokeWidth,
// )
foregroundIndicator(
componentSize = componentSize,
indicatorColor = foregroundIndicatorColor,
indicatorStrokeWidth = foregroundIndicatorStrokeWidth,
startAngle = startAngle
)
}
) {
}
}
RotaryToolInterface.kt
package com.hxrobot.ui.Jog
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.absoluteOffset
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.unit.*
import androidx.lifecycle.viewmodel.compose.viewModel
import com.hxrobot.ui.teach.PointTeach.JogCurSwitchAxis
@Composable
fun RotaryToolInterface() {
var valueIndex by remember { mutableStateOf(1) }//默认值与默认轴对应
val curAxisViewModel: JogCurSwitchAxis = viewModel()//默认值 0
//初始化轴的方法,防止切换模式后轴被保留
if(valueIndex == 1){
curAxisViewModel.currentAxis = 1
}
Box(modifier = Modifier.size(250.dp,250.dp)){
//val size =(200f/density).dp
val size = 250.dp
val centerXY = size*0.5f
val multiple = size/100f.dp
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShapeForTool(2, 1))
.background(if (valueIndex == 1) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 1
curAxisViewModel.currentAxis = 1
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY+(39*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(7*multiple).dp).toPx())
},text = "X", color = (if(valueIndex == 1) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
Box(
modifier = Modifier
.fillMaxSize()
//.zIndex(2f)
.clip(SectorShapeForTool(2, 2))
.background(if (valueIndex == 2) Color(0xFF3153CE) else Color(0xFFF1F4F4))
.clickable {
valueIndex = 2
curAxisViewModel.currentAxis = 2
}
) {
Text(
modifier = Modifier
.graphicsLayer {
translationX = translationX.plus((centerXY-(43*multiple).dp).toPx())
translationY = translationY.plus((centerXY-(8*multiple).dp).toPx())
},text = "Y", color = (if(valueIndex == 2) Color.White else Color(0xFF8A8C93 )), fontSize = 24.sp,)
}
// Box(modifier = Modifier.size((128/size.value)*size).absoluteOffset(centerXY-(((128f/size.value)*size.value)/2f).dp,centerXY-(((128f/size.value)*size.value)/2f).dp)){
// InnerCircleInRotaryMax()
// }
// Box(modifier = Modifier.absoluteOffset(centerXY-(((110/size.value)*size.value)/2f).dp,centerXY-(((110/size.value)*size.value)/2f).dp)){
// InnerCircleInRotary(startAngle = (valueIndex-1)*180f-5f,sizeX = (110/size.value)*size.value, sizeY = (110/size.value)*size.value)
// }
// Box(modifier = Modifier.size((60/size.value)*size).absoluteOffset(centerXY-(((60f/size.value)*size.value)/2f).dp,centerXY-(((60f/size.value)*size.value)/2f).dp)){
// InnerCircleInRotaryMin()
// }
Box(modifier = Modifier.size((0.64f)*size).absoluteOffset(centerXY-((0.64f*size.value)/2f).dp,centerXY-((0.64f*size.value)/2f).dp)){
InnerCircleInRotaryMax()
}
Box(modifier = Modifier.size((0.55f)*size).absoluteOffset(centerXY-((0.55f*size.value)/2f).dp,centerXY-((0.55f*size.value)/2f).dp)){
InnerCircleInRotaryMaxGrayBottom()
}
Box(modifier = Modifier.absoluteOffset(centerXY-((0.49f*size.value)/2f).dp,centerXY-((0.49f*size.value)/2f).dp)){
InnerCircleInRotary(startAngle = (valueIndex-1)*180f-5f,sizeX = (0.49f)*size.value, sizeY = (0.49f)*size.value)
}
}
}
class SectorShapeForTool(val parts: Int,val currentPart:Int) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density, ): Outline {
val rect = Rect(0f, 0f, size.width, size.height)
val angelOfEachSection = 360f/parts
//定义画笔的路径
val path = Path().apply {
// 将坐标系原点移动到画布中央
moveTo(size.width / 2f, size.height / 2f)//移动到起点
addArc(rect, (currentPart-1)*angelOfEachSection+270f+0.5f, angelOfEachSection-0.5f)
lineTo(size.width / 2f, size.height / 2f)//移动到终点
}
//返回路径的轮廓
return Outline.Generic(path)
}
}